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