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