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