ProcessStatsService.java revision 8472e6189cd4e0520c047bdb28457abc728b373f
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 boolean shouldWriteNowLocked(long now) {
175        if (now > (mLastWriteTime+WRITE_PERIOD)) {
176            if (SystemClock.elapsedRealtime()
177                    > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
178                mCommitPending = true;
179            }
180            return true;
181        }
182        return false;
183    }
184
185    public void shutdownLocked() {
186        Slog.w(TAG, "Writing process stats before shutdown...");
187        mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
188        writeStateSyncLocked();
189        mShuttingDown = true;
190    }
191
192    public void writeStateAsyncLocked() {
193        writeStateLocked(false);
194    }
195
196    public void writeStateSyncLocked() {
197        writeStateLocked(true);
198    }
199
200    private void writeStateLocked(boolean sync) {
201        if (mShuttingDown) {
202            return;
203        }
204        boolean commitPending = mCommitPending;
205        mCommitPending = false;
206        writeStateLocked(sync, commitPending);
207    }
208
209    public void writeStateLocked(boolean sync, final boolean commit) {
210        synchronized (mPendingWriteLock) {
211            long now = SystemClock.uptimeMillis();
212            if (mPendingWrite == null || !mPendingWriteCommitted) {
213                mPendingWrite = Parcel.obtain();
214                mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
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<ProcessStats.ProcessState>> procMap
289                            = stats.mProcesses.getMap();
290                    final int NPROC = procMap.size();
291                    for (int ip=0; ip<NPROC; ip++) {
292                        Slog.w(TAG, "Process: " + procMap.keyAt(ip));
293                        SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip);
294                        final int NUID = uids.size();
295                        for (int iu=0; iu<NUID; iu++) {
296                            Slog.w(TAG, "  Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
297                        }
298                    }
299                    ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap
300                            = stats.mPackages.getMap();
301                    final int NPKG = pkgMap.size();
302                    for (int ip=0; ip<NPKG; ip++) {
303                        Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
304                        SparseArray<SparseArray<ProcessStats.PackageState>> uids
305                                = pkgMap.valueAt(ip);
306                        final int NUID = uids.size();
307                        for (int iu=0; iu<NUID; iu++) {
308                            Slog.w(TAG, "  Uid: " + uids.keyAt(iu));
309                            SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
310                            final int NVERS = vers.size();
311                            for (int iv=0; iv<NVERS; iv++) {
312                                Slog.w(TAG, "    Vers: " + vers.keyAt(iv));
313                                ProcessStats.PackageState pkgState = vers.valueAt(iv);
314                                final int NPROCS = pkgState.mProcesses.size();
315                                for (int iproc=0; iproc<NPROCS; iproc++) {
316                                    Slog.w(TAG, "      Process " + pkgState.mProcesses.keyAt(iproc)
317                                            + ": " + pkgState.mProcesses.valueAt(iproc));
318                                }
319                                final int NSRVS = pkgState.mServices.size();
320                                for (int isvc=0; isvc<NSRVS; isvc++) {
321                                    Slog.w(TAG, "      Service " + pkgState.mServices.keyAt(isvc)
322                                            + ": " + pkgState.mServices.valueAt(isvc));
323
324                                }
325                            }
326                        }
327                    }
328                }
329                return false;
330            }
331        } catch (Throwable e) {
332            stats.mReadError = "caught exception: " + e;
333            Slog.e(TAG, "Error reading process statistics", e);
334            return false;
335        }
336        return true;
337    }
338
339    private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
340            boolean inclCheckedIn) {
341        File[] files = mBaseDir.listFiles();
342        if (files == null || files.length <= minNum) {
343            return null;
344        }
345        ArrayList<String> filesArray = new ArrayList<String>(files.length);
346        String currentFile = mFile.getBaseFile().getPath();
347        if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
348        for (int i=0; i<files.length; i++) {
349            File file = files[i];
350            String fileStr = file.getPath();
351            if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
352            if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
353                if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
354                continue;
355            }
356            if (!inclCurrent && fileStr.equals(currentFile)) {
357                if (DEBUG) Slog.d(TAG, "Skipping: current stats");
358                continue;
359            }
360            filesArray.add(fileStr);
361        }
362        Collections.sort(filesArray);
363        return filesArray;
364    }
365
366    public void trimHistoricStatesWriteLocked() {
367        ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
368        if (filesArray == null) {
369            return;
370        }
371        while (filesArray.size() > MAX_HISTORIC_STATES) {
372            String file = filesArray.remove(0);
373            Slog.i(TAG, "Pruning old procstats: " + file);
374            (new File(file)).delete();
375        }
376    }
377
378    boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
379            boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
380            boolean sepProcStates, int[] procStates, long now, String reqPackage) {
381        ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked(
382                screenStates, memStates, procStates, procStates, now, reqPackage, false);
383        if (procs.size() > 0) {
384            if (header != null) {
385                pw.println(header);
386            }
387            ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
388                    sepMemStates, memStates, sepProcStates, procStates, now);
389            return true;
390        }
391        return false;
392    }
393
394    static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
395            String[] outError) {
396        ArrayList<Integer> res = new ArrayList<Integer>();
397        int lastPos = 0;
398        for (int i=0; i<=arg.length(); i++) {
399            char c = i < arg.length() ? arg.charAt(i) : 0;
400            if (c != ',' && c != '+' && c != ' ' && c != 0) {
401                continue;
402            }
403            boolean isSep = c == ',';
404            if (lastPos == 0) {
405                // We now know the type of op.
406                outSep[0] = isSep;
407            } else if (c != 0 && outSep[0] != isSep) {
408                outError[0] = "inconsistent separators (can't mix ',' with '+')";
409                return null;
410            }
411            if (lastPos < (i-1)) {
412                String str = arg.substring(lastPos, i);
413                for (int j=0; j<states.length; j++) {
414                    if (str.equals(states[j])) {
415                        res.add(j);
416                        str = null;
417                        break;
418                    }
419                }
420                if (str != null) {
421                    outError[0] = "invalid word \"" + str + "\"";
422                    return null;
423                }
424            }
425            lastPos = i + 1;
426        }
427
428        int[] finalRes = new int[res.size()];
429        for (int i=0; i<res.size(); i++) {
430            finalRes[i] = res.get(i) * mult;
431        }
432        return finalRes;
433    }
434
435    public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
436        mAm.mContext.enforceCallingOrSelfPermission(
437                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
438        Parcel current = Parcel.obtain();
439        mWriteLock.lock();
440        try {
441            synchronized (mAm) {
442                mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
443                mProcessStats.writeToParcel(current, 0);
444            }
445            if (historic != null) {
446                ArrayList<String> files = getCommittedFiles(0, false, true);
447                if (files != null) {
448                    for (int i=files.size()-1; i>=0; i--) {
449                        try {
450                            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
451                                    new File(files.get(i)), ParcelFileDescriptor.MODE_READ_ONLY);
452                            historic.add(pfd);
453                        } catch (IOException e) {
454                            Slog.w(TAG, "Failure opening procstat file " + files.get(i), e);
455                        }
456                    }
457                }
458            }
459        } finally {
460            mWriteLock.unlock();
461        }
462        return current.marshall();
463    }
464
465    public ParcelFileDescriptor getStatsOverTime(long minTime) {
466        mAm.mContext.enforceCallingOrSelfPermission(
467                android.Manifest.permission.PACKAGE_USAGE_STATS, null);
468        mWriteLock.lock();
469        try {
470            Parcel current = Parcel.obtain();
471            long curTime;
472            synchronized (mAm) {
473                mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
474                mProcessStats.writeToParcel(current, 0);
475                curTime = mProcessStats.mTimePeriodEndRealtime
476                        - mProcessStats.mTimePeriodStartRealtime;
477            }
478            if (curTime < minTime) {
479                // Need to add in older stats to reach desired time.
480                ArrayList<String> files = getCommittedFiles(0, false, true);
481                if (files != null && files.size() > 0) {
482                    current.setDataPosition(0);
483                    ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
484                    current.recycle();
485                    int i = files.size()-1;
486                    while (i >= 0 && (stats.mTimePeriodEndRealtime
487                            - stats.mTimePeriodStartRealtime) < minTime) {
488                        AtomicFile file = new AtomicFile(new File(files.get(i)));
489                        i--;
490                        ProcessStats moreStats = new ProcessStats(false);
491                        readLocked(moreStats, file);
492                        if (moreStats.mReadError == null) {
493                            stats.add(moreStats);
494                            StringBuilder sb = new StringBuilder();
495                            sb.append("Added stats: ");
496                            sb.append(moreStats.mTimePeriodStartClockStr);
497                            sb.append(", over ");
498                            TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime
499                                    - moreStats.mTimePeriodStartRealtime, sb);
500                            Slog.i(TAG, sb.toString());
501                        } else {
502                            Slog.w(TAG, "Failure reading " + files.get(i+1) + "; "
503                                    + moreStats.mReadError);
504                            continue;
505                        }
506                    }
507                    current = Parcel.obtain();
508                    stats.writeToParcel(current, 0);
509                }
510            }
511            final byte[] outData = current.marshall();
512            current.recycle();
513            final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
514            Thread thr = new Thread("ProcessStats pipe output") {
515                public void run() {
516                    FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
517                    try {
518                        fout.write(outData);
519                        fout.close();
520                    } catch (IOException e) {
521                        Slog.w(TAG, "Failure writing pipe", e);
522                    }
523                }
524            };
525            thr.start();
526            return fds[0];
527        } catch (IOException e) {
528            Slog.w(TAG, "Failed building output pipe", e);
529        } finally {
530            mWriteLock.unlock();
531        }
532        return null;
533    }
534
535    public int getCurrentMemoryState() {
536        synchronized (mAm) {
537            return mLastMemOnlyState;
538        }
539    }
540
541    private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
542            String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
543            boolean dumpAll, boolean activeOnly) {
544        ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
545                - (ProcessStats.COMMIT_PERIOD/2));
546        if (pfd == null) {
547            pw.println("Unable to build stats!");
548            return;
549        }
550        ProcessStats stats = new ProcessStats(false);
551        InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
552        stats.read(stream);
553        if (stats.mReadError != null) {
554            pw.print("Failure reading: "); pw.println(stats.mReadError);
555            return;
556        }
557        if (isCompact) {
558            stats.dumpCheckinLocked(pw, reqPackage);
559        } else {
560            if (dumpDetails || dumpFullDetails) {
561                stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
562            } else {
563                stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
564            }
565        }
566    }
567
568    static private void dumpHelp(PrintWriter pw) {
569        pw.println("Process stats (procstats) dump options:");
570        pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
571        pw.println("    [--details] [--full-details] [--current] [--hours] [--active]");
572        pw.println("    [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
573        pw.println("  --checkin: perform a checkin: print and delete old committed states.");
574        pw.println("  --c: print only state in checkin format.");
575        pw.println("  --csv: output data suitable for putting in a spreadsheet.");
576        pw.println("  --csv-screen: on, off.");
577        pw.println("  --csv-mem: norm, mod, low, crit.");
578        pw.println("  --csv-proc: pers, top, fore, vis, precept, backup,");
579        pw.println("    service, home, prev, cached");
580        pw.println("  --details: dump per-package details, not just summary.");
581        pw.println("  --full-details: dump all timing and active state details.");
582        pw.println("  --current: only dump current state.");
583        pw.println("  --hours: aggregate over about N last hours.");
584        pw.println("  --active: only show currently active processes/services.");
585        pw.println("  --commit: commit current stats to disk and reset to start new stats.");
586        pw.println("  --reset: reset current stats, without committing.");
587        pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
588        pw.println("  --write: write current in-memory stats to disk.");
589        pw.println("  --read: replace current stats with last-written stats.");
590        pw.println("  -a: print everything.");
591        pw.println("  -h: print this help text.");
592        pw.println("  <package.name>: optional name of package to filter output by.");
593    }
594
595    @Override
596    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
597        if (mAm.checkCallingPermission(android.Manifest.permission.DUMP)
598                != PackageManager.PERMISSION_GRANTED) {
599            pw.println("Permission Denial: can't dump procstats from from pid="
600                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
601                    + " without permission " + android.Manifest.permission.DUMP);
602            return;
603        }
604
605        long ident = Binder.clearCallingIdentity();
606        try {
607            dumpInner(fd, pw, args);
608        } finally {
609            Binder.restoreCallingIdentity(ident);
610        }
611    }
612
613    private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
614        final long now = SystemClock.uptimeMillis();
615
616        boolean isCheckin = false;
617        boolean isCompact = false;
618        boolean isCsv = false;
619        boolean currentOnly = false;
620        boolean dumpDetails = false;
621        boolean dumpFullDetails = false;
622        boolean dumpAll = false;
623        int aggregateHours = 0;
624        boolean activeOnly = false;
625        String reqPackage = null;
626        boolean csvSepScreenStats = false;
627        int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
628        boolean csvSepMemStats = false;
629        int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
630        boolean csvSepProcStats = true;
631        int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
632        if (args != null) {
633            for (int i=0; i<args.length; i++) {
634                String arg = args[i];
635                if ("--checkin".equals(arg)) {
636                    isCheckin = true;
637                } else if ("-c".equals(arg)) {
638                    isCompact = true;
639                } else if ("--csv".equals(arg)) {
640                    isCsv = true;
641                } else if ("--csv-screen".equals(arg)) {
642                    i++;
643                    if (i >= args.length) {
644                        pw.println("Error: argument required for --csv-screen");
645                        dumpHelp(pw);
646                        return;
647                    }
648                    boolean[] sep = new boolean[1];
649                    String[] error = new String[1];
650                    csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD,
651                            args[i], sep, error);
652                    if (csvScreenStats == null) {
653                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
654                        dumpHelp(pw);
655                        return;
656                    }
657                    csvSepScreenStats = sep[0];
658                } else if ("--csv-mem".equals(arg)) {
659                    i++;
660                    if (i >= args.length) {
661                        pw.println("Error: argument required for --csv-mem");
662                        dumpHelp(pw);
663                        return;
664                    }
665                    boolean[] sep = new boolean[1];
666                    String[] error = new String[1];
667                    csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
668                    if (csvMemStats == null) {
669                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
670                        dumpHelp(pw);
671                        return;
672                    }
673                    csvSepMemStats = sep[0];
674                } else if ("--csv-proc".equals(arg)) {
675                    i++;
676                    if (i >= args.length) {
677                        pw.println("Error: argument required for --csv-proc");
678                        dumpHelp(pw);
679                        return;
680                    }
681                    boolean[] sep = new boolean[1];
682                    String[] error = new String[1];
683                    csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error);
684                    if (csvProcStats == null) {
685                        pw.println("Error in \"" + args[i] + "\": " + error[0]);
686                        dumpHelp(pw);
687                        return;
688                    }
689                    csvSepProcStats = sep[0];
690                } else if ("--details".equals(arg)) {
691                    dumpDetails = true;
692                } else if ("--full-details".equals(arg)) {
693                    dumpFullDetails = true;
694                } else if ("--hours".equals(arg)) {
695                    i++;
696                    if (i >= args.length) {
697                        pw.println("Error: argument required for --hours");
698                        dumpHelp(pw);
699                        return;
700                    }
701                    try {
702                        aggregateHours = Integer.parseInt(args[i]);
703                    } catch (NumberFormatException e) {
704                        pw.println("Error: --hours argument not an int -- " + args[i]);
705                        dumpHelp(pw);
706                        return;
707                    }
708                } else if ("--active".equals(arg)) {
709                    activeOnly = true;
710                    currentOnly = true;
711                } else if ("--current".equals(arg)) {
712                    currentOnly = true;
713                } else if ("--commit".equals(arg)) {
714                    synchronized (mAm) {
715                        mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
716                        writeStateLocked(true, true);
717                        pw.println("Process stats committed.");
718                    }
719                    return;
720                } else if ("--reset".equals(arg)) {
721                    synchronized (mAm) {
722                        mProcessStats.resetSafely();
723                        pw.println("Process stats reset.");
724                    }
725                    return;
726                } else if ("--clear".equals(arg)) {
727                    synchronized (mAm) {
728                        mProcessStats.resetSafely();
729                        ArrayList<String> files = getCommittedFiles(0, true, true);
730                        if (files != null) {
731                            for (int fi=0; fi<files.size(); fi++) {
732                                (new File(files.get(fi))).delete();
733                            }
734                        }
735                        pw.println("All process stats cleared.");
736                    }
737                    return;
738                } else if ("--write".equals(arg)) {
739                    synchronized (mAm) {
740                        writeStateSyncLocked();
741                        pw.println("Process stats written.");
742                    }
743                    return;
744                } else if ("--read".equals(arg)) {
745                    synchronized (mAm) {
746                        readLocked(mProcessStats, mFile);
747                        pw.println("Process stats read.");
748                    }
749                    return;
750                } else if ("-h".equals(arg)) {
751                    dumpHelp(pw);
752                    return;
753                } else if ("-a".equals(arg)) {
754                    dumpDetails = true;
755                    dumpAll = true;
756                } else if (arg.length() > 0 && arg.charAt(0) == '-'){
757                    pw.println("Unknown option: " + arg);
758                    dumpHelp(pw);
759                    return;
760                } else {
761                    // Not an option, last argument must be a package name.
762                    reqPackage = arg;
763                    // Include all details, since we know we are only going to
764                    // be dumping a smaller set of data.  In fact only the details
765                    // container per-package data, so that are needed to be able
766                    // to dump anything at all when filtering by package.
767                    dumpDetails = true;
768                }
769            }
770        }
771
772        if (isCsv) {
773            pw.print("Processes running summed over");
774            if (!csvSepScreenStats) {
775                for (int i=0; i<csvScreenStats.length; i++) {
776                    pw.print(" ");
777                    ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]);
778                }
779            }
780            if (!csvSepMemStats) {
781                for (int i=0; i<csvMemStats.length; i++) {
782                    pw.print(" ");
783                    ProcessStats.printMemLabelCsv(pw, csvMemStats[i]);
784                }
785            }
786            if (!csvSepProcStats) {
787                for (int i=0; i<csvProcStats.length; i++) {
788                    pw.print(" ");
789                    pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]);
790                }
791            }
792            pw.println();
793            synchronized (mAm) {
794                dumpFilteredProcessesCsvLocked(pw, null,
795                        csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
796                        csvSepProcStats, csvProcStats, now, reqPackage);
797                /*
798                dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
799                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
800                        true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
801                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
802                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
803                                STATE_PREVIOUS, STATE_CACHED},
804                        now, reqPackage);
805                dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
806                        false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
807                        false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
808                                ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
809                        true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
810                                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
811                                STATE_PREVIOUS, STATE_CACHED},
812                        now, reqPackage);
813                */
814            }
815            return;
816        } else if (aggregateHours != 0) {
817            pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
818            dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
819                    dumpDetails, dumpFullDetails, dumpAll, activeOnly);
820            return;
821        }
822
823        boolean sepNeeded = false;
824        if (dumpAll || isCheckin) {
825            mWriteLock.lock();
826            try {
827                ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
828                if (files != null) {
829                    for (int i=0; i<files.size(); i++) {
830                        if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
831                        try {
832                            AtomicFile file = new AtomicFile(new File(files.get(i)));
833                            ProcessStats processStats = new ProcessStats(false);
834                            readLocked(processStats, file);
835                            if (processStats.mReadError != null) {
836                                if (isCheckin || isCompact) pw.print("err,");
837                                pw.print("Failure reading "); pw.print(files.get(i));
838                                pw.print("; "); pw.println(processStats.mReadError);
839                                if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
840                                (new File(files.get(i))).delete();
841                                continue;
842                            }
843                            String fileStr = file.getBaseFile().getPath();
844                            boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
845                            if (isCheckin || isCompact) {
846                                // Don't really need to lock because we uniquely own this object.
847                                processStats.dumpCheckinLocked(pw, reqPackage);
848                            } else {
849                                if (sepNeeded) {
850                                    pw.println();
851                                } else {
852                                    sepNeeded = true;
853                                }
854                                pw.print("COMMITTED STATS FROM ");
855                                pw.print(processStats.mTimePeriodStartClockStr);
856                                if (checkedIn) pw.print(" (checked in)");
857                                pw.println(":");
858                                // Don't really need to lock because we uniquely own this object.
859                                // Always dump summary here, dumping all details is just too
860                                // much crud.
861                                if (dumpFullDetails) {
862                                    mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
863                                            activeOnly);
864                                } else {
865                                    processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
866                                }
867                            }
868                            if (isCheckin) {
869                                // Rename file suffix to mark that it has checked in.
870                                file.getBaseFile().renameTo(new File(
871                                        fileStr + STATE_FILE_CHECKIN_SUFFIX));
872                            }
873                        } catch (Throwable e) {
874                            pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
875                            e.printStackTrace(pw);
876                        }
877                    }
878                }
879            } finally {
880                mWriteLock.unlock();
881            }
882        }
883        if (!isCheckin) {
884            if (!currentOnly) {
885                if (sepNeeded) {
886                    pw.println();
887                }
888                pw.println("AGGREGATED OVER LAST 24 HOURS:");
889                dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
890                        dumpDetails, dumpFullDetails, dumpAll, activeOnly);
891                pw.println();
892                pw.println("AGGREGATED OVER LAST 3 HOURS:");
893                dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
894                        dumpDetails, dumpFullDetails, dumpAll, activeOnly);
895                sepNeeded = true;
896            }
897            synchronized (mAm) {
898                if (isCompact) {
899                    mProcessStats.dumpCheckinLocked(pw, reqPackage);
900                } else {
901                    if (sepNeeded) {
902                        pw.println();
903                    }
904                    pw.println("CURRENT STATS:");
905                    if (dumpDetails || dumpFullDetails) {
906                        mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
907                                activeOnly);
908                        if (dumpAll) {
909                            pw.print("  mFile="); pw.println(mFile.getBaseFile());
910                        }
911                    } else {
912                        mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
913                    }
914                }
915            }
916        }
917    }
918}
919