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