ProcStatsData.java revision 30bbd902dd98197fd39b4e43bf8cb5027c49984b
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.applications; 18 19import android.app.ActivityManager; 20import android.content.Context; 21import android.content.pm.PackageManager; 22import android.os.ParcelFileDescriptor; 23import android.os.RemoteException; 24import android.os.ServiceManager; 25import android.os.SystemClock; 26import android.text.format.Formatter; 27import android.util.ArrayMap; 28import android.util.Log; 29import android.util.SparseArray; 30 31import com.android.internal.app.IProcessStats; 32import com.android.internal.app.ProcessMap; 33import com.android.internal.app.ProcessStats; 34import com.android.internal.app.ProcessStats.ProcessDataCollection; 35import com.android.internal.app.ProcessStats.TotalMemoryUseCollection; 36import com.android.internal.util.MemInfoReader; 37import com.android.settings.R; 38import com.android.settings.Utils; 39 40import java.io.IOException; 41import java.io.InputStream; 42import java.util.ArrayList; 43import java.util.Comparator; 44import java.util.List; 45 46public class ProcStatsData { 47 48 private static final String TAG = "ProcStatsManager"; 49 50 private static final boolean DEBUG = ProcessStatsUi.DEBUG; 51 52 private static ProcessStats sStatsXfer; 53 54 private PackageManager mPm; 55 private Context mContext; 56 private long memTotalTime; 57 58 private IProcessStats mProcessStats; 59 private ProcessStats mStats; 60 61 private boolean mUseUss; 62 private long mDuration; 63 64 private int[] mMemStates; 65 66 private int[] mStates; 67 68 private MemInfo mMemInfo; 69 70 private ArrayList<ProcStatsPackageEntry> pkgEntries; 71 72 public ProcStatsData(Context context, boolean useXfer) { 73 mContext = context; 74 mPm = context.getPackageManager(); 75 mProcessStats = IProcessStats.Stub.asInterface( 76 ServiceManager.getService(ProcessStats.SERVICE_NAME)); 77 mMemStates = ProcessStats.ALL_MEM_ADJ; 78 mStates = ProcessStats.BACKGROUND_PROC_STATES; 79 if (useXfer) { 80 mStats = sStatsXfer; 81 } 82 } 83 84 public void setTotalTime(int totalTime) { 85 memTotalTime = totalTime; 86 } 87 88 public void xferStats() { 89 sStatsXfer = mStats; 90 } 91 92 public void setMemStates(int[] memStates) { 93 mMemStates = memStates; 94 refreshStats(false); 95 } 96 97 public void setStats(int[] stats) { 98 this.mStates = stats; 99 refreshStats(false); 100 } 101 102 public int getMemState() { 103 int factor = mStats.mMemFactor; 104 if (factor == ProcessStats.ADJ_NOTHING) { 105 return ProcessStats.ADJ_MEM_FACTOR_NORMAL; 106 } 107 if (factor >= ProcessStats.ADJ_SCREEN_ON) { 108 factor -= ProcessStats.ADJ_SCREEN_ON; 109 } 110 return factor; 111 } 112 113 public MemInfo getMemInfo() { 114 return mMemInfo; 115 } 116 117 public long getElapsedTime() { 118 return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime; 119 } 120 121 public void setDuration(long duration) { 122 if (duration != mDuration) { 123 mDuration = duration; 124 refreshStats(true); 125 } 126 } 127 128 public long getDuration() { 129 return mDuration; 130 } 131 132 public List<ProcStatsPackageEntry> getEntries() { 133 return pkgEntries; 134 } 135 136 public void refreshStats(boolean forceLoad) { 137 if (mStats == null || forceLoad) { 138 load(); 139 } 140 141 pkgEntries = new ArrayList<>(); 142 143 long now = SystemClock.uptimeMillis(); 144 145 memTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations, 146 mStats.mMemFactor, mStats.mStartTime, now); 147 148 ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection( 149 ProcessStats.ALL_SCREEN_ADJ, mMemStates); 150 mStats.computeTotalMemoryUse(totalMem, now); 151 152 mMemInfo = new MemInfo(mContext, totalMem, memTotalTime); 153 154 ProcessDataCollection bgTotals = new ProcessDataCollection( 155 ProcessStats.ALL_SCREEN_ADJ, mMemStates, mStates); 156 ProcessDataCollection runTotals = new ProcessDataCollection( 157 ProcessStats.ALL_SCREEN_ADJ, mMemStates, ProcessStats.NON_CACHED_PROC_STATES); 158 159 createPkgMap(getProcs(bgTotals, runTotals), bgTotals, runTotals); 160 if (totalMem.sysMemZRamWeight > 0) { 161 distributeZRam(totalMem.sysMemZRamWeight); 162 } 163 164 ProcStatsPackageEntry osPkg = createOsEntry(bgTotals, runTotals, totalMem, 165 mMemInfo.baseCacheRam); 166 pkgEntries.add(osPkg); 167 } 168 169 private void createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals, 170 ProcessDataCollection runTotals) { 171 // Combine processes into packages. 172 ArrayMap<String, ProcStatsPackageEntry> pkgMap = new ArrayMap<>(); 173 for (int i = procEntries.size() - 1; i >= 0; i--) { 174 ProcStatsEntry proc = procEntries.get(i); 175 proc.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 176 ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage); 177 if (pkg == null) { 178 pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage, memTotalTime); 179 pkgMap.put(proc.mBestTargetPackage, pkg); 180 pkgEntries.add(pkg); 181 } 182 pkg.addEntry(proc); 183 } 184 } 185 186 private void distributeZRam(double zramWeight) { 187 // Distribute kernel's Z-Ram across processes, based on how much they have been running. 188 // The idea is that the memory used by the kernel for this is not really the kernel's 189 // responsibility, but that of whoever got swapped in to it... and we will take how 190 // much a process runs for as a sign of the proportion of Z-Ram it is responsible for. 191 192 long zramMem = (long) (zramWeight / memTotalTime); 193 long totalTime = 0; 194 for (int i = pkgEntries.size() - 1; i >= 0; i--) { 195 ProcStatsPackageEntry entry = pkgEntries.get(i); 196 for (int j = entry.mEntries.size() - 1; j >= 0; j--) { 197 ProcStatsEntry proc = entry.mEntries.get(j); 198 totalTime += proc.mRunDuration; 199 } 200 } 201 for (int i = pkgEntries.size() - 1; i >= 0 && totalTime > 0; i--) { 202 ProcStatsPackageEntry entry = pkgEntries.get(i); 203 long pkgRunTime = 0; 204 for (int j = entry.mEntries.size() - 1; j >= 0; j--) { 205 ProcStatsEntry proc = entry.mEntries.get(j); 206 pkgRunTime += proc.mRunDuration; 207 } 208 long pkgZRam = (zramMem*pkgRunTime)/totalTime; 209 if (pkgZRam > 0) { 210 zramMem -= pkgZRam; 211 totalTime -= pkgRunTime; 212 ProcStatsEntry procEntry = new ProcStatsEntry(entry.mPackage, 0, 213 mContext.getString(R.string.process_stats_os_zram), memTotalTime, 214 pkgZRam); 215 procEntry.evaluateTargetPackage(mPm, mStats, null, null, sEntryCompare, mUseUss); 216 entry.addEntry(procEntry); 217 } 218 } 219 } 220 221 private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals, 222 ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) { 223 // Add in fake entry representing the OS itself. 224 ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime); 225 ProcStatsEntry osEntry; 226 if (totalMem.sysMemNativeWeight > 0) { 227 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 228 mContext.getString(R.string.process_stats_os_native), memTotalTime, 229 (long) (totalMem.sysMemNativeWeight / memTotalTime)); 230 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 231 osPkg.addEntry(osEntry); 232 } 233 if (totalMem.sysMemKernelWeight > 0) { 234 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 235 mContext.getString(R.string.process_stats_os_kernel), memTotalTime, 236 (long) (totalMem.sysMemKernelWeight / memTotalTime)); 237 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 238 osPkg.addEntry(osEntry); 239 } 240 /* Turned off now -- zram is being distributed across running apps. 241 if (totalMem.sysMemZRamWeight > 0) { 242 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 243 mContext.getString(R.string.process_stats_os_zram), memTotalTime, 244 (long) (totalMem.sysMemZRamWeight / memTotalTime)); 245 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 246 osPkg.addEntry(osEntry); 247 } 248 */ 249 if (baseCacheRam > 0) { 250 osEntry = new ProcStatsEntry(Utils.OS_PKG, 0, 251 mContext.getString(R.string.process_stats_os_cache), memTotalTime, 252 baseCacheRam / 1024); 253 osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); 254 osPkg.addEntry(osEntry); 255 } 256 return osPkg; 257 } 258 259 private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals, 260 ProcessDataCollection runTotals) { 261 final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>(); 262 if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES"); 263 264 final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>(); 265 for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) { 266 final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages 267 .getMap().valueAt(ipkg); 268 for (int iu = 0; iu < pkgUids.size(); iu++) { 269 final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu); 270 for (int iv = 0; iv < vpkgs.size(); iv++) { 271 final ProcessStats.PackageState st = vpkgs.valueAt(iv); 272 for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) { 273 final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc); 274 final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName, 275 pkgProc.mUid); 276 if (proc == null) { 277 Log.w(TAG, "No process found for pkg " + st.mPackageName 278 + "/" + st.mUid + " proc name " + pkgProc.mName); 279 continue; 280 } 281 ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid); 282 if (ent == null) { 283 ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals, 284 mUseUss); 285 if (ent.mRunWeight > 0) { 286 if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/" 287 + proc.mUid + ": time=" 288 + ProcessStatsUi.makeDuration(ent.mRunDuration) + " (" 289 + ((((double) ent.mRunDuration) / memTotalTime) * 100) 290 + "%)" 291 + " pss=" + ent.mAvgRunMem); 292 entriesMap.put(proc.mName, proc.mUid, ent); 293 procEntries.add(ent); 294 } 295 } else { 296 ent.addPackage(st.mPackageName); 297 } 298 } 299 } 300 } 301 } 302 303 if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES"); 304 305 // Add in service info. 306 for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) { 307 SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap() 308 .valueAt(ip); 309 for (int iu = 0; iu < uids.size(); iu++) { 310 SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu); 311 for (int iv = 0; iv < vpkgs.size(); iv++) { 312 ProcessStats.PackageState ps = vpkgs.valueAt(iv); 313 for (int is = 0, NS = ps.mServices.size(); is < NS; is++) { 314 ProcessStats.ServiceState ss = ps.mServices.valueAt(is); 315 if (ss.mProcessName != null) { 316 ProcStatsEntry ent = entriesMap.get(ss.mProcessName, 317 uids.keyAt(iu)); 318 if (ent != null) { 319 if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName 320 + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc " 321 + ss.mProcessName); 322 ent.addService(ss); 323 } else { 324 Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu) 325 + " for service " + ss.mName); 326 } 327 } 328 } 329 } 330 } 331 } 332 333 return procEntries; 334 } 335 336 private void load() { 337 try { 338 ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration); 339 mStats = new ProcessStats(false); 340 InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 341 mStats.read(is); 342 try { 343 is.close(); 344 } catch (IOException e) { 345 } 346 if (mStats.mReadError != null) { 347 Log.w(TAG, "Failure reading process stats: " + mStats.mReadError); 348 } 349 } catch (RemoteException e) { 350 Log.e(TAG, "RemoteException:", e); 351 } 352 } 353 354 public static class MemInfo { 355 double realUsedRam; 356 double realFreeRam; 357 double realTotalRam; 358 long baseCacheRam; 359 360 double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT]; 361 double freeWeight; 362 double usedWeight; 363 double weightToRam; 364 double totalRam; 365 double totalScale; 366 long memTotalTime; 367 368 private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem, 369 long memTotalTime) { 370 this.memTotalTime = memTotalTime; 371 calculateWeightInfo(context, totalMem, memTotalTime); 372 373 double usedRam = (usedWeight * 1024) / memTotalTime; 374 double freeRam = (freeWeight * 1024) / memTotalTime; 375 totalRam = usedRam + freeRam; 376 totalScale = realTotalRam / totalRam; 377 weightToRam = totalScale / memTotalTime * 1024; 378 379 realUsedRam = usedRam * totalScale; 380 realFreeRam = freeRam * totalScale; 381 if (DEBUG) { 382 Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context, 383 (long) realUsedRam)); 384 Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context, 385 (long) realFreeRam)); 386 } 387 if (DEBUG) { 388 Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context, 389 (long) realUsedRam)); 390 Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context, 391 (long) realFreeRam)); 392 } 393 394 ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); 395 ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo( 396 memInfo); 397 if (memInfo.hiddenAppThreshold >= realFreeRam) { 398 realUsedRam = freeRam; 399 realFreeRam = 0; 400 baseCacheRam = (long) realFreeRam; 401 } else { 402 realUsedRam += memInfo.hiddenAppThreshold; 403 realFreeRam -= memInfo.hiddenAppThreshold; 404 baseCacheRam = memInfo.hiddenAppThreshold; 405 } 406 } 407 408 private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem, 409 long memTotalTime) { 410 MemInfoReader memReader = new MemInfoReader(); 411 memReader.readMemInfo(); 412 realTotalRam = memReader.getTotalSize(); 413 freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight; 414 usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight 415 + totalMem.sysMemZRamWeight; 416 for (int i = 0; i < ProcessStats.STATE_COUNT; i++) { 417 if (i == ProcessStats.STATE_SERVICE_RESTARTING) { 418 // These don't really run. 419 mMemStateWeights[i] = 0; 420 } else { 421 mMemStateWeights[i] = totalMem.processStateWeight[i]; 422 if (i >= ProcessStats.STATE_HOME) { 423 freeWeight += totalMem.processStateWeight[i]; 424 } else { 425 usedWeight += totalMem.processStateWeight[i]; 426 } 427 } 428 } 429 if (DEBUG) { 430 Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context, 431 (long) ((usedWeight * 1024) / memTotalTime))); 432 Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context, 433 (long) ((freeWeight * 1024) / memTotalTime))); 434 Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context, 435 (long) (((freeWeight + usedWeight) * 1024) / memTotalTime))); 436 } 437 } 438 } 439 440 final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() { 441 @Override 442 public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) { 443 if (lhs.mRunWeight < rhs.mRunWeight) { 444 return 1; 445 } else if (lhs.mRunWeight > rhs.mRunWeight) { 446 return -1; 447 } else if (lhs.mRunDuration < rhs.mRunDuration) { 448 return 1; 449 } else if (lhs.mRunDuration > rhs.mRunDuration) { 450 return -1; 451 } 452 return 0; 453 } 454 }; 455} 456