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