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