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
17package com.android.server.am;
18
19import android.content.pm.PackageManager;
20import android.os.Binder;
21import android.os.Parcel;
22import android.os.ParcelFileDescriptor;
23import android.os.RemoteException;
24import android.os.SystemClock;
25import android.os.SystemProperties;
26import android.util.ArrayMap;
27import android.util.AtomicFile;
28import android.util.Slog;
29import android.util.SparseArray;
30import android.util.TimeUtils;
31
32import com.android.internal.annotations.GuardedBy;
33import com.android.internal.app.procstats.DumpUtils;
34import com.android.internal.app.procstats.IProcessStats;
35import com.android.internal.app.procstats.ProcessState;
36import com.android.internal.app.procstats.ProcessStats;
37import com.android.internal.app.procstats.ServiceState;
38import com.android.internal.os.BackgroundThread;
39
40import java.io.File;
41import java.io.FileDescriptor;
42import java.io.FileInputStream;
43import java.io.FileOutputStream;
44import java.io.IOException;
45import java.io.InputStream;
46import java.io.PrintWriter;
47import java.util.ArrayList;
48import java.util.Collections;
49import java.util.List;
50import java.util.concurrent.locks.ReentrantLock;
51
52public 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
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
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
119    public ProcessState getProcessStateLocked(String packageName,
120            int uid, int versionCode, String processName) {
121        return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
122    }
123
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
130    public boolean isMemFactorLowered() {
131        return mMemFactorLowered;
132    }
133
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
171    public int getMemFactorLocked() {
172        return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
173    }
174
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
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
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
200    public void writeStateAsyncLocked() {
201        writeStateLocked(false);
202    }
203
204    public void writeStateSyncLocked() {
205        writeStateLocked(true);
206    }
207
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
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
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
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
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
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
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
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
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