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