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