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