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