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