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 static private void dumpHelp(PrintWriter pw) { 533 pw.println("Process stats (procstats) dump options:"); 534 pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]"); 535 pw.println(" [--details] [--full-details] [--current] [--hours] [--active]"); 536 pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]"); 537 pw.println(" --checkin: perform a checkin: print and delete old committed states."); 538 pw.println(" --c: print only state in checkin format."); 539 pw.println(" --csv: output data suitable for putting in a spreadsheet."); 540 pw.println(" --csv-screen: on, off."); 541 pw.println(" --csv-mem: norm, mod, low, crit."); 542 pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,"); 543 pw.println(" service, home, prev, cached"); 544 pw.println(" --details: dump per-package details, not just summary."); 545 pw.println(" --full-details: dump all timing and active state details."); 546 pw.println(" --current: only dump current state."); 547 pw.println(" --hours: aggregate over about N last hours."); 548 pw.println(" --active: only show currently active processes/services."); 549 pw.println(" --commit: commit current stats to disk and reset to start new stats."); 550 pw.println(" --reset: reset current stats, without committing."); 551 pw.println(" --clear: clear all stats; does both --reset and deletes old stats."); 552 pw.println(" --write: write current in-memory stats to disk."); 553 pw.println(" --read: replace current stats with last-written stats."); 554 pw.println(" -a: print everything."); 555 pw.println(" -h: print this help text."); 556 pw.println(" <package.name>: optional name of package to filter output by."); 557 } 558 559 @Override 560 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 561 if (mAm.checkCallingPermission(android.Manifest.permission.DUMP) 562 != PackageManager.PERMISSION_GRANTED) { 563 pw.println("Permission Denial: can't dump procstats from from pid=" 564 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 565 + " without permission " + android.Manifest.permission.DUMP); 566 return; 567 } 568 569 long ident = Binder.clearCallingIdentity(); 570 try { 571 dumpInner(fd, pw, args); 572 } finally { 573 Binder.restoreCallingIdentity(ident); 574 } 575 } 576 577 private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) { 578 final long now = SystemClock.uptimeMillis(); 579 580 boolean isCheckin = false; 581 boolean isCompact = false; 582 boolean isCsv = false; 583 boolean currentOnly = false; 584 boolean dumpDetails = false; 585 boolean dumpFullDetails = false; 586 boolean dumpAll = false; 587 int aggregateHours = 0; 588 boolean activeOnly = false; 589 String reqPackage = null; 590 boolean csvSepScreenStats = false; 591 int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON}; 592 boolean csvSepMemStats = false; 593 int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL}; 594 boolean csvSepProcStats = true; 595 int[] csvProcStats = ProcessStats.ALL_PROC_STATES; 596 if (args != null) { 597 for (int i=0; i<args.length; i++) { 598 String arg = args[i]; 599 if ("--checkin".equals(arg)) { 600 isCheckin = true; 601 } else if ("-c".equals(arg)) { 602 isCompact = true; 603 } else if ("--csv".equals(arg)) { 604 isCsv = true; 605 } else if ("--csv-screen".equals(arg)) { 606 i++; 607 if (i >= args.length) { 608 pw.println("Error: argument required for --csv-screen"); 609 dumpHelp(pw); 610 return; 611 } 612 boolean[] sep = new boolean[1]; 613 String[] error = new String[1]; 614 csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD, 615 args[i], sep, error); 616 if (csvScreenStats == null) { 617 pw.println("Error in \"" + args[i] + "\": " + error[0]); 618 dumpHelp(pw); 619 return; 620 } 621 csvSepScreenStats = sep[0]; 622 } else if ("--csv-mem".equals(arg)) { 623 i++; 624 if (i >= args.length) { 625 pw.println("Error: argument required for --csv-mem"); 626 dumpHelp(pw); 627 return; 628 } 629 boolean[] sep = new boolean[1]; 630 String[] error = new String[1]; 631 csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error); 632 if (csvMemStats == null) { 633 pw.println("Error in \"" + args[i] + "\": " + error[0]); 634 dumpHelp(pw); 635 return; 636 } 637 csvSepMemStats = sep[0]; 638 } else if ("--csv-proc".equals(arg)) { 639 i++; 640 if (i >= args.length) { 641 pw.println("Error: argument required for --csv-proc"); 642 dumpHelp(pw); 643 return; 644 } 645 boolean[] sep = new boolean[1]; 646 String[] error = new String[1]; 647 csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error); 648 if (csvProcStats == null) { 649 pw.println("Error in \"" + args[i] + "\": " + error[0]); 650 dumpHelp(pw); 651 return; 652 } 653 csvSepProcStats = sep[0]; 654 } else if ("--details".equals(arg)) { 655 dumpDetails = true; 656 } else if ("--full-details".equals(arg)) { 657 dumpFullDetails = true; 658 } else if ("--hours".equals(arg)) { 659 i++; 660 if (i >= args.length) { 661 pw.println("Error: argument required for --hours"); 662 dumpHelp(pw); 663 return; 664 } 665 try { 666 aggregateHours = Integer.parseInt(args[i]); 667 } catch (NumberFormatException e) { 668 pw.println("Error: --hours argument not an int -- " + args[i]); 669 dumpHelp(pw); 670 return; 671 } 672 } else if ("--active".equals(arg)) { 673 activeOnly = true; 674 currentOnly = true; 675 } else if ("--current".equals(arg)) { 676 currentOnly = true; 677 } else if ("--commit".equals(arg)) { 678 synchronized (mAm) { 679 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE; 680 writeStateLocked(true, true); 681 pw.println("Process stats committed."); 682 } 683 return; 684 } else if ("--reset".equals(arg)) { 685 synchronized (mAm) { 686 mProcessStats.resetSafely(); 687 pw.println("Process stats reset."); 688 } 689 return; 690 } else if ("--clear".equals(arg)) { 691 synchronized (mAm) { 692 mProcessStats.resetSafely(); 693 ArrayList<String> files = getCommittedFiles(0, true, true); 694 if (files != null) { 695 for (int fi=0; fi<files.size(); fi++) { 696 (new File(files.get(fi))).delete(); 697 } 698 } 699 pw.println("All process stats cleared."); 700 } 701 return; 702 } else if ("--write".equals(arg)) { 703 synchronized (mAm) { 704 writeStateSyncLocked(); 705 pw.println("Process stats written."); 706 } 707 return; 708 } else if ("--read".equals(arg)) { 709 synchronized (mAm) { 710 readLocked(mProcessStats, mFile); 711 pw.println("Process stats read."); 712 } 713 return; 714 } else if ("-h".equals(arg)) { 715 dumpHelp(pw); 716 return; 717 } else if ("-a".equals(arg)) { 718 dumpDetails = true; 719 dumpAll = true; 720 } else if (arg.length() > 0 && arg.charAt(0) == '-'){ 721 pw.println("Unknown option: " + arg); 722 dumpHelp(pw); 723 return; 724 } else { 725 // Not an option, last argument must be a package name. 726 try { 727 IPackageManager pm = AppGlobals.getPackageManager(); 728 if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) { 729 reqPackage = arg; 730 // Include all details, since we know we are only going to 731 // be dumping a smaller set of data. In fact only the details 732 // container per-package data, so that are needed to be able 733 // to dump anything at all when filtering by package. 734 dumpDetails = true; 735 } 736 } catch (RemoteException e) { 737 } 738 if (reqPackage == null) { 739 pw.println("Unknown package: " + arg); 740 dumpHelp(pw); 741 return; 742 } 743 } 744 } 745 } 746 747 if (isCsv) { 748 pw.print("Processes running summed over"); 749 if (!csvSepScreenStats) { 750 for (int i=0; i<csvScreenStats.length; i++) { 751 pw.print(" "); 752 ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]); 753 } 754 } 755 if (!csvSepMemStats) { 756 for (int i=0; i<csvMemStats.length; i++) { 757 pw.print(" "); 758 ProcessStats.printMemLabelCsv(pw, csvMemStats[i]); 759 } 760 } 761 if (!csvSepProcStats) { 762 for (int i=0; i<csvProcStats.length; i++) { 763 pw.print(" "); 764 pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]); 765 } 766 } 767 pw.println(); 768 synchronized (mAm) { 769 dumpFilteredProcessesCsvLocked(pw, null, 770 csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, 771 csvSepProcStats, csvProcStats, now, reqPackage); 772 /* 773 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", 774 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 775 true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, 776 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 777 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 778 STATE_PREVIOUS, STATE_CACHED}, 779 now, reqPackage); 780 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", 781 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, 782 false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, 783 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, 784 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, 785 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, 786 STATE_PREVIOUS, STATE_CACHED}, 787 now, reqPackage); 788 */ 789 } 790 return; 791 } else if (aggregateHours != 0) { 792 ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 793 - (ProcessStats.COMMIT_PERIOD/2)); 794 if (pfd == null) { 795 pw.println("Unable to build stats!"); 796 return; 797 } 798 ProcessStats stats = new ProcessStats(false); 799 InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 800 stats.read(stream); 801 if (stats.mReadError != null) { 802 pw.print("Failure reading: "); pw.println(stats.mReadError); 803 return; 804 } 805 if (isCompact) { 806 stats.dumpCheckinLocked(pw, reqPackage); 807 } else { 808 if (dumpDetails || dumpFullDetails) { 809 stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly); 810 } else { 811 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 812 } 813 } 814 return; 815 } 816 817 boolean sepNeeded = false; 818 if (!currentOnly || isCheckin) { 819 mWriteLock.lock(); 820 try { 821 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin); 822 if (files != null) { 823 for (int i=0; i<files.size(); i++) { 824 if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i)); 825 try { 826 AtomicFile file = new AtomicFile(new File(files.get(i))); 827 ProcessStats processStats = new ProcessStats(false); 828 readLocked(processStats, file); 829 if (processStats.mReadError != null) { 830 if (isCheckin || isCompact) pw.print("err,"); 831 pw.print("Failure reading "); pw.print(files.get(i)); 832 pw.print("; "); pw.println(processStats.mReadError); 833 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i)); 834 (new File(files.get(i))).delete(); 835 continue; 836 } 837 String fileStr = file.getBaseFile().getPath(); 838 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX); 839 if (isCheckin || isCompact) { 840 // Don't really need to lock because we uniquely own this object. 841 processStats.dumpCheckinLocked(pw, reqPackage); 842 } else { 843 if (sepNeeded) { 844 pw.println(); 845 } else { 846 sepNeeded = true; 847 } 848 pw.print("COMMITTED STATS FROM "); 849 pw.print(processStats.mTimePeriodStartClockStr); 850 if (checkedIn) pw.print(" (checked in)"); 851 pw.println(":"); 852 // Don't really need to lock because we uniquely own this object. 853 // Always dump summary here, dumping all details is just too 854 // much crud. 855 if (dumpFullDetails) { 856 mProcessStats.dumpLocked(pw, reqPackage, now, false, false, 857 activeOnly); 858 } else { 859 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 860 } 861 } 862 if (isCheckin) { 863 // Rename file suffix to mark that it has checked in. 864 file.getBaseFile().renameTo(new File( 865 fileStr + STATE_FILE_CHECKIN_SUFFIX)); 866 } 867 } catch (Throwable e) { 868 pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i)); 869 e.printStackTrace(pw); 870 } 871 } 872 } 873 } finally { 874 mWriteLock.unlock(); 875 } 876 } 877 if (!isCheckin) { 878 synchronized (mAm) { 879 if (isCompact) { 880 mProcessStats.dumpCheckinLocked(pw, reqPackage); 881 } else { 882 if (sepNeeded) { 883 pw.println(); 884 pw.println("CURRENT STATS:"); 885 } 886 if (dumpDetails || dumpFullDetails) { 887 mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, 888 activeOnly); 889 if (dumpAll) { 890 pw.print(" mFile="); pw.println(mFile.getBaseFile()); 891 } 892 } else { 893 mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly); 894 } 895 } 896 } 897 } 898 } 899 } 900