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