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