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