Home | History | Annotate | Download | only in am
      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 
     17 package com.android.server.am;
     18 
     19 import android.app.AppGlobals;
     20 import android.content.pm.IPackageManager;
     21 import android.content.pm.PackageManager;
     22 import android.os.Binder;
     23 import android.os.Parcel;
     24 import android.os.ParcelFileDescriptor;
     25 import android.os.RemoteException;
     26 import android.os.SystemClock;
     27 import android.os.SystemProperties;
     28 import android.os.UserHandle;
     29 import android.util.ArrayMap;
     30 import android.util.AtomicFile;
     31 import android.util.Slog;
     32 import android.util.SparseArray;
     33 import android.util.TimeUtils;
     34 import com.android.internal.app.IProcessStats;
     35 import com.android.internal.app.ProcessStats;
     36 import com.android.internal.os.BackgroundThread;
     37 
     38 import java.io.File;
     39 import java.io.FileDescriptor;
     40 import java.io.FileInputStream;
     41 import java.io.FileOutputStream;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 import java.io.PrintWriter;
     45 import java.util.ArrayList;
     46 import java.util.Collections;
     47 import java.util.List;
     48 import java.util.concurrent.locks.ReentrantLock;
     49 
     50 public 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     static private void dumpHelp(PrintWriter pw) {
    533         pw.println("Process stats (procstats) dump options:");
    534         pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
    535         pw.println("    [--details] [--full-details] [--current] [--hours] [--active]");
    536         pw.println("    [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
    537         pw.println("  --checkin: perform a checkin: print and delete old committed states.");
    538         pw.println("  --c: print only state in checkin format.");
    539         pw.println("  --csv: output data suitable for putting in a spreadsheet.");
    540         pw.println("  --csv-screen: on, off.");
    541         pw.println("  --csv-mem: norm, mod, low, crit.");
    542         pw.println("  --csv-proc: pers, top, fore, vis, precept, backup,");
    543         pw.println("    service, home, prev, cached");
    544         pw.println("  --details: dump per-package details, not just summary.");
    545         pw.println("  --full-details: dump all timing and active state details.");
    546         pw.println("  --current: only dump current state.");
    547         pw.println("  --hours: aggregate over about N last hours.");
    548         pw.println("  --active: only show currently active processes/services.");
    549         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
    550         pw.println("  --reset: reset current stats, without committing.");
    551         pw.println("  --clear: clear all stats; does both --reset and deletes old stats.");
    552         pw.println("  --write: write current in-memory stats to disk.");
    553         pw.println("  --read: replace current stats with last-written stats.");
    554         pw.println("  -a: print everything.");
    555         pw.println("  -h: print this help text.");
    556         pw.println("  <package.name>: optional name of package to filter output by.");
    557     }
    558 
    559     @Override
    560     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    561         if (mAm.checkCallingPermission(android.Manifest.permission.DUMP)
    562                 != PackageManager.PERMISSION_GRANTED) {
    563             pw.println("Permission Denial: can't dump procstats from from pid="
    564                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
    565                     + " without permission " + android.Manifest.permission.DUMP);
    566             return;
    567         }
    568 
    569         long ident = Binder.clearCallingIdentity();
    570         try {
    571             dumpInner(fd, pw, args);
    572         } finally {
    573             Binder.restoreCallingIdentity(ident);
    574         }
    575     }
    576 
    577     private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
    578         final long now = SystemClock.uptimeMillis();
    579 
    580         boolean isCheckin = false;
    581         boolean isCompact = false;
    582         boolean isCsv = false;
    583         boolean currentOnly = false;
    584         boolean dumpDetails = false;
    585         boolean dumpFullDetails = false;
    586         boolean dumpAll = false;
    587         int aggregateHours = 0;
    588         boolean activeOnly = false;
    589         String reqPackage = null;
    590         boolean csvSepScreenStats = false;
    591         int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
    592         boolean csvSepMemStats = false;
    593         int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
    594         boolean csvSepProcStats = true;
    595         int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
    596         if (args != null) {
    597             for (int i=0; i<args.length; i++) {
    598                 String arg = args[i];
    599                 if ("--checkin".equals(arg)) {
    600                     isCheckin = true;
    601                 } else if ("-c".equals(arg)) {
    602                     isCompact = true;
    603                 } else if ("--csv".equals(arg)) {
    604                     isCsv = true;
    605                 } else if ("--csv-screen".equals(arg)) {
    606                     i++;
    607                     if (i >= args.length) {
    608                         pw.println("Error: argument required for --csv-screen");
    609                         dumpHelp(pw);
    610                         return;
    611                     }
    612                     boolean[] sep = new boolean[1];
    613                     String[] error = new String[1];
    614                     csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD,
    615                             args[i], sep, error);
    616                     if (csvScreenStats == null) {
    617                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
    618                         dumpHelp(pw);
    619                         return;
    620                     }
    621                     csvSepScreenStats = sep[0];
    622                 } else if ("--csv-mem".equals(arg)) {
    623                     i++;
    624                     if (i >= args.length) {
    625                         pw.println("Error: argument required for --csv-mem");
    626                         dumpHelp(pw);
    627                         return;
    628                     }
    629                     boolean[] sep = new boolean[1];
    630                     String[] error = new String[1];
    631                     csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
    632                     if (csvMemStats == null) {
    633                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
    634                         dumpHelp(pw);
    635                         return;
    636                     }
    637                     csvSepMemStats = sep[0];
    638                 } else if ("--csv-proc".equals(arg)) {
    639                     i++;
    640                     if (i >= args.length) {
    641                         pw.println("Error: argument required for --csv-proc");
    642                         dumpHelp(pw);
    643                         return;
    644                     }
    645                     boolean[] sep = new boolean[1];
    646                     String[] error = new String[1];
    647                     csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error);
    648                     if (csvProcStats == null) {
    649                         pw.println("Error in \"" + args[i] + "\": " + error[0]);
    650                         dumpHelp(pw);
    651                         return;
    652                     }
    653                     csvSepProcStats = sep[0];
    654                 } else if ("--details".equals(arg)) {
    655                     dumpDetails = true;
    656                 } else if ("--full-details".equals(arg)) {
    657                     dumpFullDetails = true;
    658                 } else if ("--hours".equals(arg)) {
    659                     i++;
    660                     if (i >= args.length) {
    661                         pw.println("Error: argument required for --hours");
    662                         dumpHelp(pw);
    663                         return;
    664                     }
    665                     try {
    666                         aggregateHours = Integer.parseInt(args[i]);
    667                     } catch (NumberFormatException e) {
    668                         pw.println("Error: --hours argument not an int -- " + args[i]);
    669                         dumpHelp(pw);
    670                         return;
    671                     }
    672                 } else if ("--active".equals(arg)) {
    673                     activeOnly = true;
    674                     currentOnly = true;
    675                 } else if ("--current".equals(arg)) {
    676                     currentOnly = true;
    677                 } else if ("--commit".equals(arg)) {
    678                     synchronized (mAm) {
    679                         mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
    680                         writeStateLocked(true, true);
    681                         pw.println("Process stats committed.");
    682                     }
    683                     return;
    684                 } else if ("--reset".equals(arg)) {
    685                     synchronized (mAm) {
    686                         mProcessStats.resetSafely();
    687                         pw.println("Process stats reset.");
    688                     }
    689                     return;
    690                 } else if ("--clear".equals(arg)) {
    691                     synchronized (mAm) {
    692                         mProcessStats.resetSafely();
    693                         ArrayList<String> files = getCommittedFiles(0, true, true);
    694                         if (files != null) {
    695                             for (int fi=0; fi<files.size(); fi++) {
    696                                 (new File(files.get(fi))).delete();
    697                             }
    698                         }
    699                         pw.println("All process stats cleared.");
    700                     }
    701                     return;
    702                 } else if ("--write".equals(arg)) {
    703                     synchronized (mAm) {
    704                         writeStateSyncLocked();
    705                         pw.println("Process stats written.");
    706                     }
    707                     return;
    708                 } else if ("--read".equals(arg)) {
    709                     synchronized (mAm) {
    710                         readLocked(mProcessStats, mFile);
    711                         pw.println("Process stats read.");
    712                     }
    713                     return;
    714                 } else if ("-h".equals(arg)) {
    715                     dumpHelp(pw);
    716                     return;
    717                 } else if ("-a".equals(arg)) {
    718                     dumpDetails = true;
    719                     dumpAll = true;
    720                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
    721                     pw.println("Unknown option: " + arg);
    722                     dumpHelp(pw);
    723                     return;
    724                 } else {
    725                     // Not an option, last argument must be a package name.
    726                     try {
    727                         IPackageManager pm = AppGlobals.getPackageManager();
    728                         if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
    729                             reqPackage = arg;
    730                             // Include all details, since we know we are only going to
    731                             // be dumping a smaller set of data.  In fact only the details
    732                             // container per-package data, so that are needed to be able
    733                             // to dump anything at all when filtering by package.
    734                             dumpDetails = true;
    735                         }
    736                     } catch (RemoteException e) {
    737                     }
    738                     if (reqPackage == null) {
    739                         pw.println("Unknown package: " + arg);
    740                         dumpHelp(pw);
    741                         return;
    742                     }
    743                 }
    744             }
    745         }
    746 
    747         if (isCsv) {
    748             pw.print("Processes running summed over");
    749             if (!csvSepScreenStats) {
    750                 for (int i=0; i<csvScreenStats.length; i++) {
    751                     pw.print(" ");
    752                     ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]);
    753                 }
    754             }
    755             if (!csvSepMemStats) {
    756                 for (int i=0; i<csvMemStats.length; i++) {
    757                     pw.print(" ");
    758                     ProcessStats.printMemLabelCsv(pw, csvMemStats[i]);
    759                 }
    760             }
    761             if (!csvSepProcStats) {
    762                 for (int i=0; i<csvProcStats.length; i++) {
    763                     pw.print(" ");
    764                     pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]);
    765                 }
    766             }
    767             pw.println();
    768             synchronized (mAm) {
    769                 dumpFilteredProcessesCsvLocked(pw, null,
    770                         csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
    771                         csvSepProcStats, csvProcStats, now, reqPackage);
    772                 /*
    773                 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
    774                         false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
    775                         true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
    776                         true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
    777                                 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
    778                                 STATE_PREVIOUS, STATE_CACHED},
    779                         now, reqPackage);
    780                 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
    781                         false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
    782                         false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
    783                                 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
    784                         true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
    785                                 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
    786                                 STATE_PREVIOUS, STATE_CACHED},
    787                         now, reqPackage);
    788                 */
    789             }
    790             return;
    791         } else if (aggregateHours != 0) {
    792             ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
    793                     - (ProcessStats.COMMIT_PERIOD/2));
    794             if (pfd == null) {
    795                 pw.println("Unable to build stats!");
    796                 return;
    797             }
    798             ProcessStats stats = new ProcessStats(false);
    799             InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
    800             stats.read(stream);
    801             if (stats.mReadError != null) {
    802                 pw.print("Failure reading: "); pw.println(stats.mReadError);
    803                 return;
    804             }
    805             if (isCompact) {
    806                 stats.dumpCheckinLocked(pw, reqPackage);
    807             } else {
    808                 if (dumpDetails || dumpFullDetails) {
    809                     stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
    810                 } else {
    811                     stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
    812                 }
    813             }
    814             return;
    815         }
    816 
    817         boolean sepNeeded = false;
    818         if (!currentOnly || isCheckin) {
    819             mWriteLock.lock();
    820             try {
    821                 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
    822                 if (files != null) {
    823                     for (int i=0; i<files.size(); i++) {
    824                         if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
    825                         try {
    826                             AtomicFile file = new AtomicFile(new File(files.get(i)));
    827                             ProcessStats processStats = new ProcessStats(false);
    828                             readLocked(processStats, file);
    829                             if (processStats.mReadError != null) {
    830                                 if (isCheckin || isCompact) pw.print("err,");
    831                                 pw.print("Failure reading "); pw.print(files.get(i));
    832                                 pw.print("; "); pw.println(processStats.mReadError);
    833                                 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
    834                                 (new File(files.get(i))).delete();
    835                                 continue;
    836                             }
    837                             String fileStr = file.getBaseFile().getPath();
    838                             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
    839                             if (isCheckin || isCompact) {
    840                                 // Don't really need to lock because we uniquely own this object.
    841                                 processStats.dumpCheckinLocked(pw, reqPackage);
    842                             } else {
    843                                 if (sepNeeded) {
    844                                     pw.println();
    845                                 } else {
    846                                     sepNeeded = true;
    847                                 }
    848                                 pw.print("COMMITTED STATS FROM ");
    849                                 pw.print(processStats.mTimePeriodStartClockStr);
    850                                 if (checkedIn) pw.print(" (checked in)");
    851                                 pw.println(":");
    852                                 // Don't really need to lock because we uniquely own this object.
    853                                 // Always dump summary here, dumping all details is just too
    854                                 // much crud.
    855                                 if (dumpFullDetails) {
    856                                     mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
    857                                             activeOnly);
    858                                 } else {
    859                                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
    860                                 }
    861                             }
    862                             if (isCheckin) {
    863                                 // Rename file suffix to mark that it has checked in.
    864                                 file.getBaseFile().renameTo(new File(
    865                                         fileStr + STATE_FILE_CHECKIN_SUFFIX));
    866                             }
    867                         } catch (Throwable e) {
    868                             pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
    869                             e.printStackTrace(pw);
    870                         }
    871                     }
    872                 }
    873             } finally {
    874                 mWriteLock.unlock();
    875             }
    876         }
    877         if (!isCheckin) {
    878             synchronized (mAm) {
    879                 if (isCompact) {
    880                     mProcessStats.dumpCheckinLocked(pw, reqPackage);
    881                 } else {
    882                     if (sepNeeded) {
    883                         pw.println();
    884                         pw.println("CURRENT STATS:");
    885                     }
    886                     if (dumpDetails || dumpFullDetails) {
    887                         mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
    888                                 activeOnly);
    889                         if (dumpAll) {
    890                             pw.print("  mFile="); pw.println(mFile.getBaseFile());
    891                         }
    892                     } else {
    893                         mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
    894                     }
    895                 }
    896             }
    897         }
    898     }
    899 }
    900