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     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