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