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