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 32 import com.android.internal.annotations.GuardedBy; 33 import com.android.internal.app.procstats.DumpUtils; 34 import com.android.internal.app.procstats.IProcessStats; 35 import com.android.internal.app.procstats.ProcessState; 36 import com.android.internal.app.procstats.ProcessStats; 37 import com.android.internal.app.procstats.ServiceState; 38 import com.android.internal.os.BackgroundThread; 39 40 import java.io.File; 41 import java.io.FileDescriptor; 42 import java.io.FileInputStream; 43 import java.io.FileOutputStream; 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.io.PrintWriter; 47 import java.util.ArrayList; 48 import java.util.Collections; 49 import java.util.List; 50 import java.util.concurrent.locks.ReentrantLock; 51 52 public final class ProcessStatsService extends IProcessStats.Stub { 53 static final String TAG = "ProcessStatsService"; 54 static final boolean DEBUG = false; 55 56 // Most data is kept in a sparse data structure: an integer array which integer 57 // holds the type of the entry, and the identifier for a long array that data 58 // exists in and the offset into the array to find it. The constants below 59 // define the encoding of that data in an integer. 60 61 static final int MAX_HISTORIC_STATES = 8; // Maximum number of historic states we will keep. 62 static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames. 63 static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames. 64 static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in. 65 static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so. 66 67 final ActivityManagerService mAm; 68 final File mBaseDir; 69 ProcessStats mProcessStats; 70 AtomicFile mFile; 71 boolean mCommitPending; 72 boolean mShuttingDown; 73 int mLastMemOnlyState = -1; 74 boolean mMemFactorLowered; 75 76 final ReentrantLock mWriteLock = new ReentrantLock(); 77 final Object mPendingWriteLock = new Object(); 78 AtomicFile mPendingWriteFile; 79 Parcel mPendingWrite; 80 boolean mPendingWriteCommitted; 81 long mLastWriteTime; 82 83 /** For CTS to inject the screen state. */ 84 @GuardedBy("mAm") 85 Boolean mInjectedScreenState; 86 ProcessStatsService(ActivityManagerService am, File file)87 public ProcessStatsService(ActivityManagerService am, File file) { 88 mAm = am; 89 mBaseDir = file; 90 mBaseDir.mkdirs(); 91 mProcessStats = new ProcessStats(true); 92 updateFile(); 93 SystemProperties.addChangeCallback(new Runnable() { 94 @Override public void run() { 95 synchronized (mAm) { 96 if (mProcessStats.evaluateSystemProperties(false)) { 97 mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS; 98 writeStateLocked(true, true); 99 mProcessStats.evaluateSystemProperties(true); 100 } 101 } 102 } 103 }); 104 } 105 106 @Override onTransact(int code, Parcel data, Parcel reply, int flags)107 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 108 throws RemoteException { 109 try { 110 return super.onTransact(code, data, reply, flags); 111 } catch (RuntimeException e) { 112 if (!(e instanceof SecurityException)) { 113 Slog.wtf(TAG, "Process Stats Crash", e); 114 } 115 throw e; 116 } 117 } 118 getProcessStateLocked(String packageName, int uid, int versionCode, String processName)119 public ProcessState getProcessStateLocked(String packageName, 120 int uid, int versionCode, String processName) { 121 return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); 122 } 123 getServiceStateLocked(String packageName, int uid, int versionCode, String processName, String className)124 public ServiceState getServiceStateLocked(String packageName, int uid, 125 int versionCode, String processName, String className) { 126 return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, 127 className); 128 } 129 isMemFactorLowered()130 public boolean isMemFactorLowered() { 131 return mMemFactorLowered; 132 } 133 setMemFactorLocked(int memFactor, boolean screenOn, long now)134 public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) { 135 mMemFactorLowered = memFactor < mLastMemOnlyState; 136 mLastMemOnlyState = memFactor; 137 if (mInjectedScreenState != null) { 138 screenOn = mInjectedScreenState; 139 } 140 if (screenOn) { 141 memFactor += ProcessStats.ADJ_SCREEN_ON; 142 } 143 if (memFactor != mProcessStats.mMemFactor) { 144 if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) { 145 mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor] 146 += now - mProcessStats.mStartTime; 147 } 148 mProcessStats.mMemFactor = memFactor; 149 mProcessStats.mStartTime = now; 150 final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap 151 = mProcessStats.mPackages.getMap(); 152 for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) { 153 final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg); 154 for (int iuid=uids.size()-1; iuid>=0; iuid--) { 155 final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid); 156 for (int iver=vers.size()-1; iver>=0; iver--) { 157 final ProcessStats.PackageState pkg = vers.valueAt(iver); 158 final ArrayMap<String, ServiceState> services = pkg.mServices; 159 for (int isvc=services.size()-1; isvc>=0; isvc--) { 160 final ServiceState service = services.valueAt(isvc); 161 service.setMemFactor(memFactor, now); 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 if (DEBUG) Slog.d(TAG, "Prepared write state in " 237 + (SystemClock.uptimeMillis()-now) + "ms"); 238 if (!sync) { 239 BackgroundThread.getHandler().post(new Runnable() { 240 @Override public void run() { 241 performWriteState(); 242 } 243 }); 244 return; 245 } 246 } 247 248 performWriteState(); 249 } 250 updateFile()251 private void updateFile() { 252 mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX 253 + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX)); 254 mLastWriteTime = SystemClock.uptimeMillis(); 255 } 256 performWriteState()257 void performWriteState() { 258 if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()); 259 Parcel data; 260 AtomicFile file; 261 synchronized (mPendingWriteLock) { 262 data = mPendingWrite; 263 file = mPendingWriteFile; 264 mPendingWriteCommitted = false; 265 if (data == null) { 266 return; 267 } 268 mPendingWrite = null; 269 mPendingWriteFile = null; 270 mWriteLock.lock(); 271 } 272 273 FileOutputStream stream = null; 274 try { 275 stream = file.startWrite(); 276 stream.write(data.marshall()); 277 stream.flush(); 278 file.finishWrite(stream); 279 if (DEBUG) Slog.d(TAG, "Write completed successfully!"); 280 } catch (IOException e) { 281 Slog.w(TAG, "Error writing process statistics", e); 282 file.failWrite(stream); 283 } finally { 284 data.recycle(); 285 trimHistoricStatesWriteLocked(); 286 mWriteLock.unlock(); 287 } 288 } 289 readLocked(ProcessStats stats, AtomicFile file)290 boolean readLocked(ProcessStats stats, AtomicFile file) { 291 try { 292 FileInputStream stream = file.openRead(); 293 stats.read(stream); 294 stream.close(); 295 if (stats.mReadError != null) { 296 Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError); 297 if (DEBUG) { 298 ArrayMap<String, SparseArray<ProcessState>> procMap = 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<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<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 DumpUtils.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] "); 587 pw.println(" [--pretend-screen-on] [--pretend-screen-off] [--stop-pretend-screen]"); 588 pw.println(" [<package.name>]"); 589 pw.println(" --checkin: perform a checkin: print and delete old committed states."); 590 pw.println(" -c: print only state in checkin format."); 591 pw.println(" --csv: output data suitable for putting in a spreadsheet."); 592 pw.println(" --csv-screen: on, off."); 593 pw.println(" --csv-mem: norm, mod, low, crit."); 594 pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); 595 pw.println(" service, home, prev, cached"); 596 pw.println(" --details: dump per-package details, not just summary."); 597 pw.println(" --full-details: dump all timing and active state details."); 598 pw.println(" --current: only dump current state."); 599 pw.println(" --hours: aggregate over about N last hours."); 600 pw.println(" --last: only show the last committed stats at index N (starting at 1)."); 601 pw.println(" --max: for -a, max num of historical batches to print."); 602 pw.println(" --active: only show currently active processes/services."); 603 pw.println(" --commit: commit current stats to disk and reset to start new stats."); 604 pw.println(" --reset: reset current stats, without committing."); 605 pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); 606 pw.println(" --write: write current in-memory stats to disk."); 607 pw.println(" --read: replace current stats with last-written stats."); 608 pw.println(" --start-testing: clear all stats and starting high frequency pss sampling."); 609 pw.println(" --stop-testing: stop high frequency pss sampling."); 610 pw.println(" --pretend-screen-on: pretend screen is on."); 611 pw.println(" --pretend-screen-off: pretend screen is off."); 612 pw.println(" --stop-pretend-screen: forget \"pretend screen\" and use the real state."); 613 pw.println(" -a: print everything."); 614 pw.println(" -h: print this help text."); 615 pw.println(" <package.name>: optional name of package to filter output by."); 616 } 617 618 @Override 619 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 620 if (!com.android.internal.util.DumpUtils.checkDumpAndUsageStatsPermission(mAm.mContext, 621 TAG, pw)) return; 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(DumpUtils.ADJ_SCREEN_NAMES_CSV, 672 ProcessStats.ADJ_SCREEN_MOD, 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(DumpUtils.ADJ_MEM_NAMES_CSV, 1, args[i], 689 sep, error); 690 if (csvMemStats == null) { 691 pw.println("Error in \"" + args[i] + "\": " + error[0]); 692 dumpHelp(pw); 693 return; 694 } 695 csvSepMemStats = sep[0]; 696 } else if ("--csv-proc".equals(arg)) { 697 i++; 698 if (i >= args.length) { 699 pw.println("Error: argument required for --csv-proc"); 700 dumpHelp(pw); 701 return; 702 } 703 boolean[] sep = new boolean[1]; 704 String[] error = new String[1]; 705 csvProcStats = parseStateList(DumpUtils.STATE_NAMES_CSV, 1, args[i], 706 sep, error); 707 if (csvProcStats == null) { 708 pw.println("Error in \"" + args[i] + "\": " + error[0]); 709 dumpHelp(pw); 710 return; 711 } 712 csvSepProcStats = sep[0]; 713 } else if ("--details".equals(arg)) { 714 dumpDetails = true; 715 } else if ("--full-details".equals(arg)) { 716 dumpFullDetails = true; 717 } else if ("--hours".equals(arg)) { 718 i++; 719 if (i >= args.length) { 720 pw.println("Error: argument required for --hours"); 721 dumpHelp(pw); 722 return; 723 } 724 try { 725 aggregateHours = Integer.parseInt(args[i]); 726 } catch (NumberFormatException e) { 727 pw.println("Error: --hours argument not an int -- " + args[i]); 728 dumpHelp(pw); 729 return; 730 } 731 } else if ("--last".equals(arg)) { 732 i++; 733 if (i >= args.length) { 734 pw.println("Error: argument required for --last"); 735 dumpHelp(pw); 736 return; 737 } 738 try { 739 lastIndex = Integer.parseInt(args[i]); 740 } catch (NumberFormatException e) { 741 pw.println("Error: --last argument not an int -- " + args[i]); 742 dumpHelp(pw); 743 return; 744 } 745 } else if ("--max".equals(arg)) { 746 i++; 747 if (i >= args.length) { 748 pw.println("Error: argument required for --max"); 749 dumpHelp(pw); 750 return; 751 } 752 try { 753 maxNum = Integer.parseInt(args[i]); 754 } catch (NumberFormatException e) { 755 pw.println("Error: --max argument not an int -- " + args[i]); 756 dumpHelp(pw); 757 return; 758 } 759 } else if ("--active".equals(arg)) { 760 activeOnly = true; 761 currentOnly = true; 762 } else if ("--current".equals(arg)) { 763 currentOnly = true; 764 } else if ("--commit".equals(arg)) { 765 synchronized (mAm) { 766 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; 767 writeStateLocked(true, true); 768 pw.println("Process stats committed."); 769 quit = true; 770 } 771 } else if ("--reset".equals(arg)) { 772 synchronized (mAm) { 773 mProcessStats.resetSafely(); 774 pw.println("Process stats reset."); 775 quit = true; 776 } 777 } else if ("--clear".equals(arg)) { 778 synchronized (mAm) { 779 mProcessStats.resetSafely(); 780 ArrayList<String> files = getCommittedFiles(0, true, true); 781 if (files != null) { 782 for (int fi=0; fi<files.size(); fi++) { 783 (new File(files.get(fi))).delete(); 784 } 785 } 786 pw.println("All process stats cleared."); 787 quit = true; 788 } 789 } else if ("--write".equals(arg)) { 790 synchronized (mAm) { 791 writeStateSyncLocked(); 792 pw.println("Process stats written."); 793 quit = true; 794 } 795 } else if ("--read".equals(arg)) { 796 synchronized (mAm) { 797 readLocked(mProcessStats, mFile); 798 pw.println("Process stats read."); 799 quit = true; 800 } 801 } else if ("--start-testing".equals(arg)) { 802 synchronized (mAm) { 803 mAm.setTestPssMode(true); 804 pw.println("Started high frequency sampling."); 805 quit = true; 806 } 807 } else if ("--stop-testing".equals(arg)) { 808 synchronized (mAm) { 809 mAm.setTestPssMode(false); 810 pw.println("Stopped high frequency sampling."); 811 quit = true; 812 } 813 } else if ("--pretend-screen-on".equals(arg)) { 814 synchronized (mAm) { 815 mInjectedScreenState = true; 816 } 817 quit = true; 818 } else if ("--pretend-screen-off".equals(arg)) { 819 synchronized (mAm) { 820 mInjectedScreenState = false; 821 } 822 quit = true; 823 } else if ("--stop-pretend-screen".equals(arg)) { 824 synchronized (mAm) { 825 mInjectedScreenState = null; 826 } 827 quit = true; 828 } else if ("-h".equals(arg)) { 829 dumpHelp(pw); 830 return; 831 } else if ("-a".equals(arg)) { 832 dumpDetails = true; 833 dumpAll = true; 834 } else if (arg.length() > 0 && arg.charAt(0) == '-'){ 835 pw.println("Unknown option: " + arg); 836 dumpHelp(pw); 837 return; 838 } else { 839 // Not an option, last argument must be a package name. 840 reqPackage = arg; 841 // Include all details, since we know we are only going to 842 // be dumping a smaller set of data. In fact only the details 843 // contain per-package data, so this is needed to be able 844 // to dump anything at all when filtering by package. 845 dumpDetails = true; 846 } 847 } 848 } 849 850 if (quit) { 851 return; 852 } 853 854 if (isCsv) { 855 pw.print("Processes running summed over"); 856 if (!csvSepScreenStats) { 857 for (int i=0; i<csvScreenStats.length; i++) { 858 pw.print(" "); 859 DumpUtils.printScreenLabelCsv(pw, csvScreenStats[i]); 860 } 861 } 862 if (!csvSepMemStats) { 863 for (int i=0; i<csvMemStats.length; i++) { 864 pw.print(" "); 865 DumpUtils.printMemLabelCsv(pw, csvMemStats[i]); 866 } 867 } 868 if (!csvSepProcStats) { 869 for (int i=0; i<csvProcStats.length; i++) { 870 pw.print(" "); 871 pw.print(DumpUtils.STATE_NAMES_CSV[csvProcStats[i]]); 872 } 873 } 874 pw.println(); 875 synchronized (mAm) { 876 dumpFilteredProcessesCsvLocked(pw, null, 877 csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, 878 csvSepProcStats, csvProcStats, now, reqPackage); 879 /* 880 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", 881 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 882 true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, 883 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 884 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 885 STATE_PREVIOUS, STATE_CACHED}, 886 now, reqPackage); 887 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", 888 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 889 false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, 890 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, 891 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 892 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 893 STATE_PREVIOUS, STATE_CACHED}, 894 now, reqPackage); 895 */ 896 } 897 return; 898 } else if (aggregateHours != 0) { 899 pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:"); 900 dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact, 901 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 902 return; 903 } else if (lastIndex > 0) { 904 pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":"); 905 ArrayList<String> files = getCommittedFiles(0, false, true); 906 if (lastIndex >= files.size()) { 907 pw.print("Only have "); pw.print(files.size()); pw.println(" data sets"); 908 return; 909 } 910 AtomicFile file = new AtomicFile(new File(files.get(lastIndex))); 911 ProcessStats processStats = new ProcessStats(false); 912 readLocked(processStats, file); 913 if (processStats.mReadError != null) { 914 if (isCheckin || isCompact) pw.print("err,"); 915 pw.print("Failure reading "); pw.print(files.get(lastIndex)); 916 pw.print("; "); pw.println(processStats.mReadError); 917 return; 918 } 919 String fileStr = file.getBaseFile().getPath(); 920 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); 921 if (isCheckin || isCompact) { 922 // Don't really need to lock because we uniquely own this object. 923 processStats.dumpCheckinLocked(pw, reqPackage); 924 } else { 925 pw.print("COMMITTED STATS FROM "); 926 pw.print(processStats.mTimePeriodStartClockStr); 927 if (checkedIn) pw.print(" (checked in)"); 928 pw.println(":"); 929 if (dumpDetails || dumpFullDetails) { 930 processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, 931 activeOnly); 932 if (dumpAll) { 933 pw.print(" mFile="); pw.println(mFile.getBaseFile()); 934 } 935 } else { 936 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 937 } 938 } 939 return; 940 } 941 942 boolean sepNeeded = false; 943 if (dumpAll || isCheckin) { 944 mWriteLock.lock(); 945 try { 946 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); 947 if (files != null) { 948 int start = isCheckin ? 0 : (files.size() - maxNum); 949 if (start < 0) { 950 start = 0; 951 } 952 for (int i=start; i<files.size(); i++) { 953 if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); 954 try { 955 AtomicFile file = new AtomicFile(new File(files.get(i))); 956 ProcessStats processStats = new ProcessStats(false); 957 readLocked(processStats, file); 958 if (processStats.mReadError != null) { 959 if (isCheckin || isCompact) pw.print("err,"); 960 pw.print("Failure reading "); pw.print(files.get(i)); 961 pw.print("; "); pw.println(processStats.mReadError); 962 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); 963 (new File(files.get(i))).delete(); 964 continue; 965 } 966 String fileStr = file.getBaseFile().getPath(); 967 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); 968 if (isCheckin || isCompact) { 969 // Don't really need to lock because we uniquely own this object. 970 processStats.dumpCheckinLocked(pw, reqPackage); 971 } else { 972 if (sepNeeded) { 973 pw.println(); 974 } else { 975 sepNeeded = true; 976 } 977 pw.print("COMMITTED STATS FROM "); 978 pw.print(processStats.mTimePeriodStartClockStr); 979 if (checkedIn) pw.print(" (checked in)"); 980 pw.println(":"); 981 // Don't really need to lock because we uniquely own this object. 982 // Always dump summary here, dumping all details is just too 983 // much crud. 984 if (dumpFullDetails) { 985 processStats.dumpLocked(pw, reqPackage, now, false, false, 986 activeOnly); 987 } else { 988 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 989 } 990 } 991 if (isCheckin) { 992 // Rename file suffix to mark that it has checked in. 993 file.getBaseFile().renameTo(new File( 994 fileStr + STATE_FILE_CHECKIN_SUFFIX)); 995 } 996 } catch (Throwable e) { 997 pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); 998 e.printStackTrace(pw); 999 } 1000 } 1001 } 1002 } finally { 1003 mWriteLock.unlock(); 1004 } 1005 } 1006 if (!isCheckin) { 1007 synchronized (mAm) { 1008 if (isCompact) { 1009 mProcessStats.dumpCheckinLocked(pw, reqPackage); 1010 } else { 1011 if (sepNeeded) { 1012 pw.println(); 1013 } 1014 pw.println("CURRENT STATS:"); 1015 if (dumpDetails || dumpFullDetails) { 1016 mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, 1017 activeOnly); 1018 if (dumpAll) { 1019 pw.print(" mFile="); pw.println(mFile.getBaseFile()); 1020 } 1021 } else { 1022 mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 1023 } 1024 sepNeeded = true; 1025 } 1026 } 1027 if (!currentOnly) { 1028 if (sepNeeded) { 1029 pw.println(); 1030 } 1031 pw.println("AGGREGATED OVER LAST 24 HOURS:"); 1032 dumpAggregatedStats(pw, 24, now, reqPackage, isCompact, 1033 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 1034 pw.println(); 1035 pw.println("AGGREGATED OVER LAST 3 HOURS:"); 1036 dumpAggregatedStats(pw, 3, now, reqPackage, isCompact, 1037 dumpDetails, dumpFullDetails, dumpAll, activeOnly); 1038 } 1039 } 1040 } 1041 } 1042