1 /* 2 * Copyright (C) 2013 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 17 package com.android.server.am; 18 19 import android.content.pm.PackageManager; 20 import android.os.Binder; 21 import android.os.Parcel; 22 import android.os.ParcelFileDescriptor; 23 import android.os.RemoteException; 24 import android.os.SystemClock; 25 import android.os.SystemProperties; 26 import android.util.ArrayMap; 27 import android.util.AtomicFile; 28 import android.util.Slog; 29 import android.util.SparseArray; 30 import android.util.TimeUtils; 31 import com.android.internal.app.procstats.DumpUtils; 32 import com.android.internal.app.procstats.IProcessStats; 33 import com.android.internal.app.procstats.ProcessState; 34 import com.android.internal.app.procstats.ProcessStats; 35 import com.android.internal.app.procstats.ServiceState; 36 import com.android.internal.os.BackgroundThread; 37 38 import java.io.File; 39 import java.io.FileDescriptor; 40 import java.io.FileInputStream; 41 import java.io.FileOutputStream; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 import java.util.concurrent.locks.ReentrantLock; 49 50 public final class ProcessStatsService extends IProcessStats.Stub { 51 static final String TAG = "ProcessStatsService"; 52 static final boolean DEBUG = false; 53 54 // Most data is kept in a sparse data structure: an integer array which integer 55 // holds the type of the entry, and the identifier for a long array that data 56 // exists in and the offset into the array to find it. The constants below 57 // define the encoding of that data in an integer. 58 59 static final int MAX_HISTORIC_STATES = 8; // Maximum number of historic states we will keep. 60 static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. 61 static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. 62 static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. 63 static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. 64 65 final ActivityManagerService mAm; 66 final File mBaseDir; 67 ProcessStats mProcessStats; 68 AtomicFile mFile; 69 boolean mCommitPending; 70 boolean mShuttingDown; 71 int mLastMemOnlyState = -1; 72 boolean mMemFactorLowered; 73 74 final ReentrantLock mWriteLock = new ReentrantLock(); 75 final Object mPendingWriteLock = new Object(); 76 AtomicFile mPendingWriteFile; 77 Parcel mPendingWrite; 78 boolean mPendingWriteCommitted; 79 long mLastWriteTime; 80 ProcessStatsService(ActivityManagerService am, File file)81 public ProcessStatsService(ActivityManagerService am, File file) { 82 mAm = am; 83 mBaseDir = file; 84 mBaseDir.mkdirs(); 85 mProcessStats = new ProcessStats(true); 86 updateFile(); 87 SystemProperties.addChangeCallback(new Runnable() { 88 @Override public void run() { 89 synchronized (mAm) { 90 if (mProcessStats.evaluateSystemProperties(false)) { 91 mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; 92 writeStateLocked(true, true); 93 mProcessStats.evaluateSystemProperties(true); 94 } 95 } 96 } 97 }); 98 } 99 100 @Override onTransact(int code, Parcel data, Parcel reply, int flags)101 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 102 throws RemoteException { 103 try { 104 return super.onTransact(code, data, reply, flags); 105 } catch (RuntimeException e) { 106 if (!(e instanceof SecurityException)) { 107 Slog.wtf(TAG, "Process Stats Crash", e); 108 } 109 throw e; 110 } 111 } 112 getProcessStateLocked(String packageName, int uid, int versionCode, String processName)113 public ProcessState getProcessStateLocked(String packageName, 114 int uid, int versionCode, String processName) { 115 return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); 116 } 117 getServiceStateLocked(String packageName, int uid, int versionCode, String processName, String className)118 public ServiceState getServiceStateLocked(String packageName, int uid, 119 int versionCode, String processName, String className) { 120 return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, 121 className); 122 } 123 isMemFactorLowered()124 public boolean isMemFactorLowered() { 125 return mMemFactorLowered; 126 } 127 setMemFactorLocked(int memFactor, boolean screenOn, long now)128 public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { 129 mMemFactorLowered = memFactor < mLastMemOnlyState; 130 mLastMemOnlyState = memFactor; 131 if (screenOn) { 132 memFactor += ProcessStats.ADJ_SCREEN_ON; 133 } 134 if (memFactor != mProcessStats.mMemFactor) { 135 if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) { 136 mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor] 137 += now - mProcessStats.mStartTime; 138 } 139 mProcessStats.mMemFactor = memFactor; 140 mProcessStats.mStartTime = now; 141 final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap 142 = mProcessStats.mPackages.getMap(); 143 for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) { 144 final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg); 145 for (int iuid=uids.size()-1; iuid>=0; iuid--) { 146 final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid); 147 for (int iver=vers.size()-1; iver>=0; iver--) { 148 final ProcessStats.PackageState pkg = vers.valueAt(iver); 149 final ArrayMap<String, ServiceState> services = pkg.mServices; 150 for (int isvc=services.size()-1; isvc>=0; isvc--) { 151 final ServiceState service = services.valueAt(isvc); 152 service.setMemFactor(memFactor, now); 153 } 154 } 155 } 156 } 157 return true; 158 } 159 return false; 160 } 161 getMemFactorLocked()162 public int getMemFactorLocked() { 163 return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0; 164 } 165 addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, long nativeMem)166 public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem, 167 long nativeMem) { 168 mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem); 169 } 170 shouldWriteNowLocked(long now)171 public boolean shouldWriteNowLocked(long now) { 172 if (now > (mLastWriteTime+WRITE_PERIOD)) { 173 if (SystemClock.elapsedRealtime() 174 > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) && 175 SystemClock.uptimeMillis() 176 > (mProcessStats.mTimePeriodStartUptime+ProcessStats.COMMIT_UPTIME_PERIOD)) { 177 mCommitPending = true; 178 } 179 return true; 180 } 181 return false; 182 } 183 shutdownLocked()184 public void shutdownLocked() { 185 Slog.w(TAG, "Writing process stats before shutdown..."); 186 mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN; 187 writeStateSyncLocked(); 188 mShuttingDown = true; 189 } 190 writeStateAsyncLocked()191 public void writeStateAsyncLocked() { 192 writeStateLocked(false); 193 } 194 writeStateSyncLocked()195 public void writeStateSyncLocked() { 196 writeStateLocked(true); 197 } 198 writeStateLocked(boolean sync)199 private void writeStateLocked(boolean sync) { 200 if (mShuttingDown) { 201 return; 202 } 203 boolean commitPending = mCommitPending; 204 mCommitPending = false; 205 writeStateLocked(sync, commitPending); 206 } 207 writeStateLocked(boolean sync, final boolean commit)208 public void writeStateLocked(boolean sync, final boolean commit) { 209 synchronized (mPendingWriteLock) { 210 long now = SystemClock.uptimeMillis(); 211 if (mPendingWrite == null || !mPendingWriteCommitted) { 212 mPendingWrite = Parcel.obtain(); 213 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); 214 mProcessStats.mTimePeriodEndUptime = now; 215 if (commit) { 216 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; 217 } 218 mProcessStats.writeToParcel(mPendingWrite, 0); 219 mPendingWriteFile = new AtomicFile(mFile.getBaseFile()); 220 mPendingWriteCommitted = commit; 221 } 222 if (commit) { 223 mProcessStats.resetSafely(); 224 updateFile(); 225 } 226 mLastWriteTime = SystemClock.uptimeMillis(); 227 Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms"); 228 if (!sync) { 229 BackgroundThread.getHandler().post(new Runnable() { 230 @Override public void run() { 231 performWriteState(); 232 } 233 }); 234 return; 235 } 236 } 237 238 performWriteState(); 239 } 240 updateFile()241 private void updateFile() { 242 mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX 243 + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); 244 mLastWriteTime = SystemClock.uptimeMillis(); 245 } 246 performWriteState()247 void performWriteState() { 248 if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); 249 Parcel data; 250 AtomicFile file; 251 synchronized (mPendingWriteLock) { 252 data = mPendingWrite; 253 file = mPendingWriteFile; 254 mPendingWriteCommitted = false; 255 if (data == null) { 256 return; 257 } 258 mPendingWrite = null; 259 mPendingWriteFile = null; 260 mWriteLock.lock(); 261 } 262 263 FileOutputStream stream = null; 264 try { 265 stream = file.startWrite(); 266 stream.write(data.marshall()); 267 stream.flush(); 268 file.finishWrite(stream); 269 if (DEBUG) Slog.d(TAG, "Write completed successfully!"); 270 } catch (IOException e) { 271 Slog.w(TAG, "Error writing process statistics", e); 272 file.failWrite(stream); 273 } finally { 274 data.recycle(); 275 trimHistoricStatesWriteLocked(); 276 mWriteLock.unlock(); 277 } 278 } 279 readLocked(ProcessStats stats, AtomicFile file)280 boolean readLocked(ProcessStats stats, AtomicFile file) { 281 try { 282 FileInputStream stream = file.openRead(); 283 stats.read(stream); 284 stream.close(); 285 if (stats.mReadError != null) { 286 Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError); 287 if (DEBUG) { 288 ArrayMap<String, SparseArray<ProcessState>> procMap = stats.mProcesses.getMap(); 289 final int NPROC = procMap.size(); 290 for (int ip=0; ip<NPROC; ip++) { 291 Slog.w(TAG, "Process: " + procMap.keyAt(ip)); 292 SparseArray<ProcessState> uids = procMap.valueAt(ip); 293 final int NUID = uids.size(); 294 for (int iu=0; iu<NUID; iu++) { 295 Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); 296 } 297 } 298 ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap 299 = stats.mPackages.getMap(); 300 final int NPKG = pkgMap.size(); 301 for (int ip=0; ip<NPKG; ip++) { 302 Slog.w(TAG, "Package: " + pkgMap.keyAt(ip)); 303 SparseArray<SparseArray<ProcessStats.PackageState>> uids 304 = pkgMap.valueAt(ip); 305 final int NUID = uids.size(); 306 for (int iu=0; iu<NUID; iu++) { 307 Slog.w(TAG, " Uid: " + uids.keyAt(iu)); 308 SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu); 309 final int NVERS = vers.size(); 310 for (int iv=0; iv<NVERS; iv++) { 311 Slog.w(TAG, " Vers: " + vers.keyAt(iv)); 312 ProcessStats.PackageState pkgState = vers.valueAt(iv); 313 final int NPROCS = pkgState.mProcesses.size(); 314 for (int iproc=0; iproc<NPROCS; iproc++) { 315 Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc) 316 + ": " + pkgState.mProcesses.valueAt(iproc)); 317 } 318 final int NSRVS = pkgState.mServices.size(); 319 for (int isvc=0; isvc<NSRVS; isvc++) { 320 Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc) 321 + ": " + pkgState.mServices.valueAt(isvc)); 322 323 } 324 } 325 } 326 } 327 } 328 return false; 329 } 330 } catch (Throwable e) { 331 stats.mReadError = "caught exception: " + e; 332 Slog.e(TAG, "Error reading process statistics", e); 333 return false; 334 } 335 return true; 336 } 337 getCommittedFiles(int minNum, boolean inclCurrent, boolean inclCheckedIn)338 private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent, 339 boolean inclCheckedIn) { 340 File[] files = mBaseDir.listFiles(); 341 if (files == null || files.length <= minNum) { 342 return null; 343 } 344 ArrayList<String> filesArray = new ArrayList<String>(files.length); 345 String currentFile = mFile.getBaseFile().getPath(); 346 if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); 347 for (int i=0; i<files.length; i++) { 348 File file = files[i]; 349 String fileStr = file.getPath(); 350 if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); 351 if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { 352 if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); 353 continue; 354 } 355 if (!inclCurrent && fileStr.equals(currentFile)) { 356 if (DEBUG) Slog.d(TAG, "Skipping: current stats"); 357 continue; 358 } 359 filesArray.add(fileStr); 360 } 361 Collections.sort(filesArray); 362 return filesArray; 363 } 364 trimHistoricStatesWriteLocked()365 public void trimHistoricStatesWriteLocked() { 366 ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true); 367 if (filesArray == null) { 368 return; 369 } 370 while (filesArray.size() > MAX_HISTORIC_STATES) { 371 String file = filesArray.remove(0); 372 Slog.i(TAG, "Pruning old procstats: " + file); 373 (new File(file)).delete(); 374 } 375 } 376 dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage)377 boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, 378 boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, 379 boolean sepProcStates, int[] procStates, long now, String reqPackage) { 380 ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked( 381 screenStates, memStates, procStates, procStates, now, reqPackage, false); 382 if (procs.size() > 0) { 383 if (header != null) { 384 pw.println(header); 385 } 386 DumpUtils.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, 387 sepMemStates, memStates, sepProcStates, procStates, now); 388 return true; 389 } 390 return false; 391 } 392 parseStateList(String[] states, int mult, String arg, boolean[] outSep, String[] outError)393 static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep, 394 String[] outError) { 395 ArrayList<Integer> res = new ArrayList<Integer>(); 396 int lastPos = 0; 397 for (int i=0; i<=arg.length(); i++) { 398 char c = i < arg.length() ? arg.charAt(i) : 0; 399 if (c != ',' && c != '+' && c != ' ' && c != 0) { 400 continue; 401 } 402 boolean isSep = c == ','; 403 if (lastPos == 0) { 404 // We now know the type of op. 405 outSep[0] = isSep; 406 } else if (c != 0 && outSep[0] != isSep) { 407 outError[0] = "inconsistent separators (can't mix ',' with '+')"; 408 return null; 409 } 410 if (lastPos < (i-1)) { 411 String str = arg.substring(lastPos, i); 412 for (int j=0; j<states.length; j++) { 413 if (str.equals(states[j])) { 414 res.add(j); 415 str = null; 416 break; 417 } 418 } 419 if (str != null) { 420 outError[0] = "invalid word \"" + str + "\""; 421 return null; 422 } 423 } 424 lastPos = i + 1; 425 } 426 427 int[] finalRes = new int[res.size()]; 428 for (int i=0; i<res.size(); i++) { 429 finalRes[i] = res.get(i) * mult; 430 } 431 return finalRes; 432 } 433 434 public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) { 435 mAm.mContext.enforceCallingOrSelfPermission( 436 android.Manifest.permission.PACKAGE_USAGE_STATS, null); 437 Parcel current = Parcel.obtain(); 438 synchronized (mAm) { 439 long now = SystemClock.uptimeMillis(); 440 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); 441 mProcessStats.mTimePeriodEndUptime = now; 442 mProcessStats.writeToParcel(current, now, 0); 443 } 444 mWriteLock.lock(); 445 try { 446 if (historic != null) { 447 ArrayList<String> files = getCommittedFiles(0, false, true); 448 if (files != null) { 449 for (int i=files.size()-1; i>=0; i--) { 450 try { 451 ParcelFileDescriptor pfd = ParcelFileDescriptor.open( 452 new File(files.get(i)), ParcelFileDescriptor.MODE_READ_ONLY); 453 historic.add(pfd); 454 } catch (IOException e) { 455 Slog.w(TAG, "Failure opening procstat file " + files.get(i), e); 456 } 457 } 458 } 459 } 460 } finally { 461 mWriteLock.unlock(); 462 } 463 return current.marshall(); 464 } 465 466 public ParcelFileDescriptor getStatsOverTime(long minTime) { 467 mAm.mContext.enforceCallingOrSelfPermission( 468 android.Manifest.permission.PACKAGE_USAGE_STATS, null); 469 Parcel current = Parcel.obtain(); 470 long curTime; 471 synchronized (mAm) { 472 long now = SystemClock.uptimeMillis(); 473 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); 474 mProcessStats.mTimePeriodEndUptime = now; 475 mProcessStats.writeToParcel(current, now, 0); 476 curTime = mProcessStats.mTimePeriodEndRealtime 477 - mProcessStats.mTimePeriodStartRealtime; 478 } 479 mWriteLock.lock(); 480 try { 481 if (curTime < minTime) { 482 // Need to add in older stats to reach desired time. 483 ArrayList<String> files = getCommittedFiles(0, false, true); 484 if (files != null && files.size() > 0) { 485 current.setDataPosition(0); 486 ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); 487 current.recycle(); 488 int i = files.size()-1; 489 while (i >= 0 && (stats.mTimePeriodEndRealtime 490 - stats.mTimePeriodStartRealtime) < minTime) { 491 AtomicFile file = new AtomicFile(new File(files.get(i))); 492 i--; 493 ProcessStats moreStats = new ProcessStats(false); 494 readLocked(moreStats, file); 495 if (moreStats.mReadError == null) { 496 stats.add(moreStats); 497 StringBuilder sb = new StringBuilder(); 498 sb.append("Added stats: "); 499 sb.append(moreStats.mTimePeriodStartClockStr); 500 sb.append(", over "); 501 TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime 502 - moreStats.mTimePeriodStartRealtime, sb); 503 Slog.i(TAG, sb.toString()); 504 } else { 505 Slog.w(TAG, "Failure reading " + files.get(i+1) + "; " 506 + moreStats.mReadError); 507 continue; 508 } 509 } 510 current = Parcel.obtain(); 511 stats.writeToParcel(current, 0); 512 } 513 } 514 final byte[] outData = current.marshall(); 515 current.recycle(); 516 final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); 517 Thread thr = new Thread("ProcessStats pipe output") { 518 public void run() { 519 FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]); 520 try { 521 fout.write(outData); 522 fout.close(); 523 } catch (IOException e) { 524 Slog.w(TAG, "Failure writing pipe", e); 525 } 526 } 527 }; 528 thr.start(); 529 return fds[0]; 530 } catch (IOException e) { 531 Slog.w(TAG, "Failed building output pipe", e); 532 } finally { 533 mWriteLock.unlock(); 534 } 535 return null; 536 } 537 538 public int getCurrentMemoryState() { 539 synchronized (mAm) { 540 return mLastMemOnlyState; 541 } 542 } 543 544 private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now, 545 String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails, 546 boolean dumpAll, boolean activeOnly) { 547 ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 548 - (ProcessStats.COMMIT_PERIOD/2)); 549 if (pfd == null) { 550 pw.println("Unable to build stats!"); 551 return; 552 } 553 ProcessStats stats = new ProcessStats(false); 554 InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 555 stats.read(stream); 556 if (stats.mReadError != null) { 557 pw.print("Failure reading: "); pw.println(stats.mReadError); 558 return; 559 } 560 if (isCompact) { 561 stats.dumpCheckinLocked(pw, reqPackage); 562 } else { 563 if (dumpDetails || dumpFullDetails) { 564 stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly); 565 } else { 566 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 567 } 568 } 569 } 570 571 static private void dumpHelp(PrintWriter pw) { 572 pw.println("Process stats (procstats) dump options:"); 573 pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); 574 pw.println(" [--details] [--full-details] [--current] [--hours N] [--last N]"); 575 pw.println(" [--max N] --active] [--commit] [--reset] [--clear] [--write] [-h]"); 576 pw.println(" [--start-testing] [--stop-testing] [<package.name>]"); 577 pw.println(" --checkin: perform a checkin: print and delete old committed states."); 578 pw.println(" -c: print only state in checkin format."); 579 pw.println(" --csv: output data suitable for putting in a spreadsheet."); 580 pw.println(" --csv-screen: on, off."); 581 pw.println(" --csv-mem: norm, mod, low, crit."); 582 pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); 583 pw.println(" service, home, prev, cached"); 584 pw.println(" --details: dump per-package details, not just summary."); 585 pw.println(" --full-details: dump all timing and active state details."); 586 pw.println(" --current: only dump current state."); 587 pw.println(" --hours: aggregate over about N last hours."); 588 pw.println(" --last: only show the last committed stats at index N (starting at 1)."); 589 pw.println(" --max: for -a, max num of historical batches to print."); 590 pw.println(" --active: only show currently active processes/services."); 591 pw.println(" --commit: commit current stats to disk and reset to start new stats."); 592 pw.println(" --reset: reset current stats, without committing."); 593 pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); 594 pw.println(" --write: write current in-memory stats to disk."); 595 pw.println(" --read: replace current stats with last-written stats."); 596 pw.println(" --start-testing: clear all stats and starting high frequency pss sampling."); 597 pw.println(" --stop-testing: stop high frequency pss sampling."); 598 pw.println(" -a: print everything."); 599 pw.println(" -h: print this help text."); 600 pw.println(" <package.name>: optional name of package to filter output by."); 601 } 602 603 @Override 604 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 605 if (mAm.checkCallingPermission(android.Manifest.permission.DUMP) 606 != PackageManager.PERMISSION_GRANTED) { 607 pw.println("Permission Denial: can't dump procstats from from pid=" 608 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 609 + " without permission " + android.Manifest.permission.DUMP); 610 return; 611 } 612 613 long ident = Binder.clearCallingIdentity(); 614 try { 615 dumpInner(fd, pw, args); 616 } finally { 617 Binder.restoreCallingIdentity(ident); 618 } 619 } 620 621 private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) { 622 final long now = SystemClock.uptimeMillis(); 623 624 boolean isCheckin = false; 625 boolean isCompact = false; 626 boolean isCsv = false; 627 boolean currentOnly = false; 628 boolean dumpDetails = false; 629 boolean dumpFullDetails = false; 630 boolean dumpAll = false; 631 boolean quit = false; 632 int aggregateHours = 0; 633 int lastIndex = 0; 634 int maxNum = 2; 635 boolean activeOnly = false; 636 String reqPackage = null; 637 boolean csvSepScreenStats = false; 638 int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON}; 639 boolean csvSepMemStats = false; 640 int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL}; 641 boolean csvSepProcStats = true; 642 int[] csvProcStats = ProcessStats.ALL_PROC_STATES; 643 if (args != null) { 644 for (int i=0; i<args.length; i++) { 645 String arg = args[i]; 646 if ("--checkin".equals(arg)) { 647 isCheckin = true; 648 } else if ("-c".equals(arg)) { 649 isCompact = true; 650 } else if ("--csv".equals(arg)) { 651 isCsv = true; 652 } else if ("--csv-screen".equals(arg)) { 653 i++; 654 if (i >= args.length) { 655 pw.println("Error: argument required for --csv-screen"); 656 dumpHelp(pw); 657 return; 658 } 659 boolean[] sep = new boolean[1]; 660 String[] error = new String[1]; 661 csvScreenStats = parseStateList(DumpUtils.ADJ_SCREEN_NAMES_CSV, 662 ProcessStats.ADJ_SCREEN_MOD, args[i], sep, error); 663 if (csvScreenStats == null) { 664 pw.println("Error in \"" + args[i] + "\": " + error[0]); 665 dumpHelp(pw); 666 return; 667 } 668 csvSepScreenStats = sep[0]; 669 } else if ("--csv-mem".equals(arg)) { 670 i++; 671 if (i >= args.length) { 672 pw.println("Error: argument required for --csv-mem"); 673 dumpHelp(pw); 674 return; 675 } 676 boolean[] sep = new boolean[1]; 677 String[] error = new String[1]; 678 csvMemStats = parseStateList(DumpUtils.ADJ_MEM_NAMES_CSV, 1, args[i], 679 sep, error); 680 if (csvMemStats == null) { 681 pw.println("Error in \"" + args[i] + "\": " + error[0]); 682 dumpHelp(pw); 683 return; 684 } 685 csvSepMemStats = sep[0]; 686 } else if ("--csv-proc".equals(arg)) { 687 i++; 688 if (i >= args.length) { 689 pw.println("Error: argument required for --csv-proc"); 690 dumpHelp(pw); 691 return; 692 } 693 boolean[] sep = new boolean[1]; 694 String[] error = new String[1]; 695 csvProcStats = parseStateList(DumpUtils.STATE_NAMES_CSV, 1, args[i], 696 sep, error); 697 if (csvProcStats == null) { 698 pw.println("Error in \"" + args[i] + "\": " + error[0]); 699 dumpHelp(pw); 700 return; 701 } 702 csvSepProcStats = sep[0]; 703 } else if ("--details".equals(arg)) { 704 dumpDetails = true; 705 } else if ("--full-details".equals(arg)) { 706 dumpFullDetails = true; 707 } else if ("--hours".equals(arg)) { 708 i++; 709 if (i >= args.length) { 710 pw.println("Error: argument required for --hours"); 711 dumpHelp(pw); 712 return; 713 } 714 try { 715 aggregateHours = Integer.parseInt(args[i]); 716 } catch (NumberFormatException e) { 717 pw.println("Error: --hours argument not an int -- " + args[i]); 718 dumpHelp(pw); 719 return; 720 } 721 } else if ("--last".equals(arg)) { 722 i++; 723 if (i >= args.length) { 724 pw.println("Error: argument required for --last"); 725 dumpHelp(pw); 726 return; 727 } 728 try { 729 lastIndex = Integer.parseInt(args[i]); 730 } catch (NumberFormatException e) { 731 pw.println("Error: --last argument not an int -- " + args[i]); 732 dumpHelp(pw); 733 return; 734 } 735 } else if ("--max".equals(arg)) { 736 i++; 737 if (i >= args.length) { 738 pw.println("Error: argument required for --max"); 739 dumpHelp(pw); 740 return; 741 } 742 try { 743 maxNum = Integer.parseInt(args[i]); 744 } catch (NumberFormatException e) { 745 pw.println("Error: --max argument not an int -- " + args[i]); 746 dumpHelp(pw); 747 return; 748 } 749 } else if ("--active".equals(arg)) { 750 activeOnly = true; 751 currentOnly = true; 752 } else if ("--current".equals(arg)) { 753 currentOnly = true; 754 } else if ("--commit".equals(arg)) { 755 synchronized (mAm) { 756 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; 757 writeStateLocked(true, true); 758 pw.println("Process stats committed."); 759 quit = true; 760 } 761 } else if ("--reset".equals(arg)) { 762 synchronized (mAm) { 763 mProcessStats.resetSafely(); 764 pw.println("Process stats reset."); 765 quit = true; 766 } 767 } else if ("--clear".equals(arg)) { 768 synchronized (mAm) { 769 mProcessStats.resetSafely(); 770 ArrayList<String> files = getCommittedFiles(0, true, true); 771 if (files != null) { 772 for (int fi=0; fi<files.size(); fi++) { 773 (new File(files.get(fi))).delete(); 774 } 775 } 776 pw.println("All process stats cleared."); 777 quit = true; 778 } 779 } else if ("--write".equals(arg)) { 780 synchronized (mAm) { 781 writeStateSyncLocked(); 782 pw.println("Process stats written."); 783 quit = true; 784 } 785 } else if ("--read".equals(arg)) { 786 synchronized (mAm) { 787 readLocked(mProcessStats, mFile); 788 pw.println("Process stats read."); 789 quit = true; 790 } 791 } else if ("--start-testing".equals(arg)) { 792 synchronized (mAm) { 793 mAm.setTestPssMode(true); 794 pw.println("Started high frequency sampling."); 795 quit = true; 796 } 797 } else if ("--stop-testing".equals(arg)) { 798 synchronized (mAm) { 799 mAm.setTestPssMode(false); 800 pw.println("Stopped high frequency sampling."); 801 quit = true; 802 } 803 } else if ("-h".equals(arg)) { 804 dumpHelp(pw); 805 return; 806 } else if ("-a".equals(arg)) { 807 dumpDetails = true; 808 dumpAll = true; 809 } else if (arg.length() > 0 && arg.charAt(0) == '-'){ 810 pw.println("Unknown option: " + arg); 811 dumpHelp(pw); 812 return; 813 } else { 814 // Not an option, last argument must be a package name. 815 reqPackage = arg; 816 // Include all details, since we know we are only going to 817 // be dumping a smaller set of data. In fact only the details 818 // contain per-package data, so this is needed to be able 819 // to dump anything at all when filtering by package. 820 dumpDetails = true; 821 } 822 } 823 } 824 825 if (quit) { 826 return; 827 } 828 829 if (isCsv) { 830 pw.print("Processes running summed over"); 831 if (!csvSepScreenStats) { 832 for (int i=0; i<csvScreenStats.length; i++) { 833 pw.print(" "); 834 DumpUtils.printScreenLabelCsv(pw, csvScreenStats[i]); 835 } 836 } 837 if (!csvSepMemStats) { 838 for (int i=0; i<csvMemStats.length; i++) { 839 pw.print(" "); 840 DumpUtils.printMemLabelCsv(pw, csvMemStats[i]); 841 } 842 } 843 if (!csvSepProcStats) { 844 for (int i=0; i<csvProcStats.length; i++) { 845 pw.print(" "); 846 pw.print(DumpUtils.STATE_NAMES_CSV[csvProcStats[i]]); 847 } 848 } 849 pw.println(); 850 synchronized (mAm) { 851 dumpFilteredProcessesCsvLocked(pw, null, 852 csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, 853 csvSepProcStats, csvProcStats, now, reqPackage); 854 /* 855 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", 856 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 857 true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, 858 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 859 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 860 STATE_PREVIOUS, STATE_CACHED}, 861 now, reqPackage); 862 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", 863 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 864 false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, 865 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, 866 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 867 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 868 STATE_PREVIOUS, STATE_CACHED}, 869 now, reqPackage); 870 */ 871 } 872 return; 873 } else if (aggregateHours != 0) { 874 pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:"); 875 dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact, 876 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 877 return; 878 } else if (lastIndex > 0) { 879 pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":"); 880 ArrayList<String> files = getCommittedFiles(0, false, true); 881 if (lastIndex >= files.size()) { 882 pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); 883 return; 884 } 885 AtomicFile file = new AtomicFile(new File(files.get(lastIndex))); 886 ProcessStats processStats = new ProcessStats(false); 887 readLocked(processStats, file); 888 if (processStats.mReadError != null) { 889 if (isCheckin || isCompact) pw.print("err,"); 890 pw.print("Failure reading "); pw.print(files.get(lastIndex)); 891 pw.print("; "); pw.println(processStats.mReadError); 892 return; 893 } 894 String fileStr = file.getBaseFile().getPath(); 895 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); 896 if (isCheckin || isCompact) { 897 // Don't really need to lock because we uniquely own this object. 898 processStats.dumpCheckinLocked(pw, reqPackage); 899 } else { 900 pw.print("COMMITTED STATS FROM "); 901 pw.print(processStats.mTimePeriodStartClockStr); 902 if (checkedIn) pw.print(" (checked in)"); 903 pw.println(":"); 904 if (dumpDetails || dumpFullDetails) { 905 processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, 906 activeOnly); 907 if (dumpAll) { 908 pw.print(" mFile="); pw.println(mFile.getBaseFile()); 909 } 910 } else { 911 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 912 } 913 } 914 return; 915 } 916 917 boolean sepNeeded = false; 918 if (dumpAll || isCheckin) { 919 mWriteLock.lock(); 920 try { 921 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); 922 if (files != null) { 923 int start = isCheckin ? 0 : (files.size() - maxNum); 924 if (start < 0) { 925 start = 0; 926 } 927 for (int i=start; i<files.size(); i++) { 928 if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); 929 try { 930 AtomicFile file = new AtomicFile(new File(files.get(i))); 931 ProcessStats processStats = new ProcessStats(false); 932 readLocked(processStats, file); 933 if (processStats.mReadError != null) { 934 if (isCheckin || isCompact) pw.print("err,"); 935 pw.print("Failure reading "); pw.print(files.get(i)); 936 pw.print("; "); pw.println(processStats.mReadError); 937 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); 938 (new File(files.get(i))).delete(); 939 continue; 940 } 941 String fileStr = file.getBaseFile().getPath(); 942 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); 943 if (isCheckin || isCompact) { 944 // Don't really need to lock because we uniquely own this object. 945 processStats.dumpCheckinLocked(pw, reqPackage); 946 } else { 947 if (sepNeeded) { 948 pw.println(); 949 } else { 950 sepNeeded = true; 951 } 952 pw.print("COMMITTED STATS FROM "); 953 pw.print(processStats.mTimePeriodStartClockStr); 954 if (checkedIn) pw.print(" (checked in)"); 955 pw.println(":"); 956 // Don't really need to lock because we uniquely own this object. 957 // Always dump summary here, dumping all details is just too 958 // much crud. 959 if (dumpFullDetails) { 960 processStats.dumpLocked(pw, reqPackage, now, false, false, 961 activeOnly); 962 } else { 963 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 964 } 965 } 966 if (isCheckin) { 967 // Rename file suffix to mark that it has checked in. 968 file.getBaseFile().renameTo(new File( 969 fileStr + STATE_FILE_CHECKIN_SUFFIX)); 970 } 971 } catch (Throwable e) { 972 pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); 973 e.printStackTrace(pw); 974 } 975 } 976 } 977 } finally { 978 mWriteLock.unlock(); 979 } 980 } 981 if (!isCheckin) { 982 synchronized (mAm) { 983 if (isCompact) { 984 mProcessStats.dumpCheckinLocked(pw, reqPackage); 985 } else { 986 if (sepNeeded) { 987 pw.println(); 988 } 989 pw.println("CURRENT STATS:"); 990 if (dumpDetails || dumpFullDetails) { 991 mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, 992 activeOnly); 993 if (dumpAll) { 994 pw.print(" mFile="); pw.println(mFile.getBaseFile()); 995 } 996 } else { 997 mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 998 } 999 sepNeeded = true; 1000 } 1001 } 1002 if (!currentOnly) { 1003 if (sepNeeded) { 1004 pw.println(); 1005 } 1006 pw.println("AGGREGATED OVER LAST 24 HOURS:"); 1007 dumpAggregatedStats(pw, 24, now, reqPackage, isCompact, 1008 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 1009 pw.println(); 1010 pw.println("AGGREGATED OVER LAST 3 HOURS:"); 1011 dumpAggregatedStats(pw, 3, now, reqPackage, isCompact, 1012 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 1013 } 1014 } 1015 } 1016 } 1017