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