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