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