blob: 14f3ef981375b0b10fd2bbb9ee41f491f454ac86 [file] [log] [blame]
Dianne Hackbornd2932242013-08-05 18:18:42 -07001/*
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
17package com.android.server.am;
18
Dianne Hackborn8a0de582013-08-07 15:22:07 -070019import android.content.pm.PackageManager;
20import android.os.Binder;
Dianne Hackbornd2932242013-08-05 18:18:42 -070021import android.os.Parcel;
Dianne Hackborn23fb6e82013-08-07 10:08:22 -070022import android.os.ParcelFileDescriptor;
Dianne Hackbornd2932242013-08-05 18:18:42 -070023import android.os.RemoteException;
24import android.os.SystemClock;
25import android.os.SystemProperties;
Dianne Hackbornd2932242013-08-05 18:18:42 -070026import android.util.ArrayMap;
27import android.util.AtomicFile;
28import android.util.Slog;
29import android.util.SparseArray;
Dianne Hackborn53459a72013-09-17 17:14:57 -070030import android.util.TimeUtils;
Dianne Hackborn23fb6e82013-08-07 10:08:22 -070031import com.android.internal.app.IProcessStats;
Dianne Hackbornd2932242013-08-05 18:18:42 -070032import com.android.internal.app.ProcessStats;
33import com.android.internal.os.BackgroundThread;
34
35import java.io.File;
36import java.io.FileDescriptor;
37import java.io.FileInputStream;
38import java.io.FileOutputStream;
39import java.io.IOException;
Dianne Hackborn53459a72013-09-17 17:14:57 -070040import java.io.InputStream;
Dianne Hackbornd2932242013-08-05 18:18:42 -070041import java.io.PrintWriter;
42import java.util.ArrayList;
43import java.util.Collections;
Dianne Hackborn23fb6e82013-08-07 10:08:22 -070044import java.util.List;
Dianne Hackbornd2932242013-08-05 18:18:42 -070045import java.util.concurrent.locks.ReentrantLock;
46
Dianne Hackborn23fb6e82013-08-07 10:08:22 -070047public final class ProcessStatsService extends IProcessStats.Stub {
Dianne Hackbornd2932242013-08-05 18:18:42 -070048 static final String TAG = "ProcessStatsService";
49 static final boolean DEBUG = false;
50
51 // Most data is kept in a sparse data structure: an integer array which integer
52 // holds the type of the entry, and the identifier for a long array that data
53 // exists in and the offset into the array to find it. The constants below
54 // define the encoding of that data in an integer.
55
Dianne Hackborn6d9ef382013-09-23 14:39:23 -070056 static final int MAX_HISTORIC_STATES = 8; // Maximum number of historic states we will keep.
Dianne Hackbornd2932242013-08-05 18:18:42 -070057 static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
58 static final String STATE_FILE_SUFFIX = ".bin"; // Suffix to use for state filenames.
59 static final String STATE_FILE_CHECKIN_SUFFIX = ".ci"; // State files that have checked in.
60 static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so.
Dianne Hackbornd2932242013-08-05 18:18:42 -070061
Dianne Hackborn8a0de582013-08-07 15:22:07 -070062 final ActivityManagerService mAm;
Dianne Hackbornd2932242013-08-05 18:18:42 -070063 final File mBaseDir;
64 ProcessStats mProcessStats;
65 AtomicFile mFile;
66 boolean mCommitPending;
67 boolean mShuttingDown;
68 int mLastMemOnlyState = -1;
69 boolean mMemFactorLowered;
70
71 final ReentrantLock mWriteLock = new ReentrantLock();
72 final Object mPendingWriteLock = new Object();
73 AtomicFile mPendingWriteFile;
74 Parcel mPendingWrite;
75 boolean mPendingWriteCommitted;
76 long mLastWriteTime;
77
Dianne Hackborn8a0de582013-08-07 15:22:07 -070078 public ProcessStatsService(ActivityManagerService am, File file) {
79 mAm = am;
Dianne Hackbornd2932242013-08-05 18:18:42 -070080 mBaseDir = file;
81 mBaseDir.mkdirs();
82 mProcessStats = new ProcessStats(true);
83 updateFile();
84 SystemProperties.addChangeCallback(new Runnable() {
85 @Override public void run() {
Dianne Hackborn8a0de582013-08-07 15:22:07 -070086 synchronized (mAm) {
Dianne Hackbornd2932242013-08-05 18:18:42 -070087 if (mProcessStats.evaluateSystemProperties(false)) {
88 mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
89 writeStateLocked(true, true);
90 mProcessStats.evaluateSystemProperties(true);
91 }
92 }
93 }
94 });
95 }
96
Dianne Hackbornd94d5332013-10-03 17:32:19 -070097 @Override
98 public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
99 throws RemoteException {
100 try {
101 return super.onTransact(code, data, reply, flags);
102 } catch (RuntimeException e) {
103 if (!(e instanceof SecurityException)) {
104 Slog.wtf(TAG, "Process Stats Crash", e);
105 }
106 throw e;
107 }
108 }
109
Dianne Hackbornd2932242013-08-05 18:18:42 -0700110 public ProcessStats.ProcessState getProcessStateLocked(String packageName,
Dianne Hackborn8472e612014-01-23 17:57:20 -0800111 int uid, int versionCode, String processName) {
112 return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700113 }
114
115 public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid,
Dianne Hackborn8472e612014-01-23 17:57:20 -0800116 int versionCode, String processName, String className) {
117 return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
118 className);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700119 }
120
121 public boolean isMemFactorLowered() {
122 return mMemFactorLowered;
123 }
124
125 public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
126 mMemFactorLowered = memFactor < mLastMemOnlyState;
127 mLastMemOnlyState = memFactor;
128 if (screenOn) {
129 memFactor += ProcessStats.ADJ_SCREEN_ON;
130 }
131 if (memFactor != mProcessStats.mMemFactor) {
132 if (mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING) {
133 mProcessStats.mMemFactorDurations[mProcessStats.mMemFactor]
134 += now - mProcessStats.mStartTime;
135 }
136 mProcessStats.mMemFactor = memFactor;
137 mProcessStats.mStartTime = now;
Dianne Hackborn8472e612014-01-23 17:57:20 -0800138 final ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pmap
Dianne Hackbornd2932242013-08-05 18:18:42 -0700139 = mProcessStats.mPackages.getMap();
Dianne Hackborn8472e612014-01-23 17:57:20 -0800140 for (int ipkg=pmap.size()-1; ipkg>=0; ipkg--) {
141 final SparseArray<SparseArray<ProcessStats.PackageState>> uids = pmap.valueAt(ipkg);
142 for (int iuid=uids.size()-1; iuid>=0; iuid--) {
143 final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid);
144 for (int iver=vers.size()-1; iver>=0; iver--) {
145 final ProcessStats.PackageState pkg = vers.valueAt(iver);
146 final ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices;
147 for (int isvc=services.size()-1; isvc>=0; isvc--) {
148 final ProcessStats.ServiceState service = services.valueAt(isvc);
149 if (service.isInUse()) {
150 if (service.mStartedState != ProcessStats.STATE_NOTHING) {
151 service.setStarted(true, memFactor, now);
152 }
153 if (service.mBoundState != ProcessStats.STATE_NOTHING) {
154 service.setBound(true, memFactor, now);
155 }
156 if (service.mExecState != ProcessStats.STATE_NOTHING) {
157 service.setExecuting(true, memFactor, now);
158 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700159 }
Dianne Hackborn8472e612014-01-23 17:57:20 -0800160
Dianne Hackbornd2932242013-08-05 18:18:42 -0700161 }
162 }
163 }
164 }
165 return true;
166 }
167 return false;
168 }
169
170 public int getMemFactorLocked() {
171 return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
172 }
173
174 public boolean shouldWriteNowLocked(long now) {
175 if (now > (mLastWriteTime+WRITE_PERIOD)) {
176 if (SystemClock.elapsedRealtime()
Dianne Hackborncb428552013-09-26 11:07:17 -0700177 > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700178 mCommitPending = true;
179 }
180 return true;
181 }
182 return false;
183 }
184
185 public void shutdownLocked() {
186 Slog.w(TAG, "Writing process stats before shutdown...");
187 mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
188 writeStateSyncLocked();
189 mShuttingDown = true;
190 }
191
192 public void writeStateAsyncLocked() {
193 writeStateLocked(false);
194 }
195
196 public void writeStateSyncLocked() {
197 writeStateLocked(true);
198 }
199
200 private void writeStateLocked(boolean sync) {
201 if (mShuttingDown) {
202 return;
203 }
204 boolean commitPending = mCommitPending;
205 mCommitPending = false;
206 writeStateLocked(sync, commitPending);
207 }
208
209 public void writeStateLocked(boolean sync, final boolean commit) {
210 synchronized (mPendingWriteLock) {
211 long now = SystemClock.uptimeMillis();
212 if (mPendingWrite == null || !mPendingWriteCommitted) {
213 mPendingWrite = Parcel.obtain();
214 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
215 if (commit) {
216 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
217 }
Dianne Hackborn23fb6e82013-08-07 10:08:22 -0700218 mProcessStats.writeToParcel(mPendingWrite, 0);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700219 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
Dianne Hackbornd2932242013-08-05 18:18:42 -0700280 boolean readLocked(ProcessStats stats, AtomicFile file) {
281 try {
282 FileInputStream stream = file.openRead();
Dianne Hackborn60444fd2013-08-08 21:57:14 -0700283 stats.read(stream);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700284 stream.close();
Dianne Hackbornd2932242013-08-05 18:18:42 -0700285 if (stats.mReadError != null) {
286 Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError);
287 if (DEBUG) {
288 ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap
289 = stats.mProcesses.getMap();
290 final int NPROC = procMap.size();
291 for (int ip=0; ip<NPROC; ip++) {
292 Slog.w(TAG, "Process: " + procMap.keyAt(ip));
293 SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip);
294 final int NUID = uids.size();
295 for (int iu=0; iu<NUID; iu++) {
296 Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
297 }
298 }
Dianne Hackborn8472e612014-01-23 17:57:20 -0800299 ArrayMap<String, SparseArray<SparseArray<ProcessStats.PackageState>>> pkgMap
Dianne Hackbornd2932242013-08-05 18:18:42 -0700300 = stats.mPackages.getMap();
301 final int NPKG = pkgMap.size();
302 for (int ip=0; ip<NPKG; ip++) {
303 Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
Dianne Hackborn8472e612014-01-23 17:57:20 -0800304 SparseArray<SparseArray<ProcessStats.PackageState>> uids
305 = pkgMap.valueAt(ip);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700306 final int NUID = uids.size();
307 for (int iu=0; iu<NUID; iu++) {
308 Slog.w(TAG, " Uid: " + uids.keyAt(iu));
Dianne Hackborn8472e612014-01-23 17:57:20 -0800309 SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iu);
310 final int NVERS = vers.size();
311 for (int iv=0; iv<NVERS; iv++) {
312 Slog.w(TAG, " Vers: " + vers.keyAt(iv));
313 ProcessStats.PackageState pkgState = vers.valueAt(iv);
314 final int NPROCS = pkgState.mProcesses.size();
315 for (int iproc=0; iproc<NPROCS; iproc++) {
316 Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc)
317 + ": " + pkgState.mProcesses.valueAt(iproc));
318 }
319 final int NSRVS = pkgState.mServices.size();
320 for (int isvc=0; isvc<NSRVS; isvc++) {
321 Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc)
322 + ": " + pkgState.mServices.valueAt(isvc));
323
324 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700325 }
326 }
327 }
328 }
329 return false;
330 }
331 } catch (Throwable e) {
332 stats.mReadError = "caught exception: " + e;
333 Slog.e(TAG, "Error reading process statistics", e);
334 return false;
335 }
336 return true;
337 }
338
Dianne Hackborn53459a72013-09-17 17:14:57 -0700339 private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
340 boolean inclCheckedIn) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700341 File[] files = mBaseDir.listFiles();
342 if (files == null || files.length <= minNum) {
343 return null;
344 }
345 ArrayList<String> filesArray = new ArrayList<String>(files.length);
346 String currentFile = mFile.getBaseFile().getPath();
347 if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
348 for (int i=0; i<files.length; i++) {
349 File file = files[i];
350 String fileStr = file.getPath();
351 if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr);
Dianne Hackborn53459a72013-09-17 17:14:57 -0700352 if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700353 if (DEBUG) Slog.d(TAG, "Skipping: already checked in");
354 continue;
355 }
Dianne Hackborn53459a72013-09-17 17:14:57 -0700356 if (!inclCurrent && fileStr.equals(currentFile)) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700357 if (DEBUG) Slog.d(TAG, "Skipping: current stats");
358 continue;
359 }
360 filesArray.add(fileStr);
361 }
362 Collections.sort(filesArray);
363 return filesArray;
364 }
365
366 public void trimHistoricStatesWriteLocked() {
Dianne Hackborn53459a72013-09-17 17:14:57 -0700367 ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700368 if (filesArray == null) {
369 return;
370 }
371 while (filesArray.size() > MAX_HISTORIC_STATES) {
372 String file = filesArray.remove(0);
373 Slog.i(TAG, "Pruning old procstats: " + file);
374 (new File(file)).delete();
375 }
376 }
377
378 boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
379 boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
380 boolean sepProcStates, int[] procStates, long now, String reqPackage) {
381 ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked(
Dianne Hackborn164371f2013-10-01 19:10:13 -0700382 screenStates, memStates, procStates, procStates, now, reqPackage, false);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700383 if (procs.size() > 0) {
384 if (header != null) {
385 pw.println(header);
386 }
387 ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates,
388 sepMemStates, memStates, sepProcStates, procStates, now);
389 return true;
390 }
391 return false;
392 }
393
394 static int[] parseStateList(String[] states, int mult, String arg, boolean[] outSep,
395 String[] outError) {
396 ArrayList<Integer> res = new ArrayList<Integer>();
397 int lastPos = 0;
398 for (int i=0; i<=arg.length(); i++) {
399 char c = i < arg.length() ? arg.charAt(i) : 0;
400 if (c != ',' && c != '+' && c != ' ' && c != 0) {
401 continue;
402 }
403 boolean isSep = c == ',';
404 if (lastPos == 0) {
405 // We now know the type of op.
406 outSep[0] = isSep;
407 } else if (c != 0 && outSep[0] != isSep) {
408 outError[0] = "inconsistent separators (can't mix ',' with '+')";
409 return null;
410 }
411 if (lastPos < (i-1)) {
412 String str = arg.substring(lastPos, i);
413 for (int j=0; j<states.length; j++) {
414 if (str.equals(states[j])) {
415 res.add(j);
416 str = null;
417 break;
418 }
419 }
420 if (str != null) {
421 outError[0] = "invalid word \"" + str + "\"";
422 return null;
423 }
424 }
425 lastPos = i + 1;
426 }
427
428 int[] finalRes = new int[res.size()];
429 for (int i=0; i<res.size(); i++) {
430 finalRes[i] = res.get(i) * mult;
431 }
432 return finalRes;
433 }
434
Dianne Hackborn23fb6e82013-08-07 10:08:22 -0700435 public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
Dianne Hackborn53459a72013-09-17 17:14:57 -0700436 mAm.mContext.enforceCallingOrSelfPermission(
437 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
Dianne Hackborn23fb6e82013-08-07 10:08:22 -0700438 Parcel current = Parcel.obtain();
439 mWriteLock.lock();
440 try {
Dianne Hackborn8a0de582013-08-07 15:22:07 -0700441 synchronized (mAm) {
Dianne Hackbornd6d54a42013-09-06 11:46:43 -0700442 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
Dianne Hackborn23fb6e82013-08-07 10:08:22 -0700443 mProcessStats.writeToParcel(current, 0);
444 }
445 if (historic != null) {
Dianne Hackborn53459a72013-09-17 17:14:57 -0700446 ArrayList<String> files = getCommittedFiles(0, false, true);
Dianne Hackborn23fb6e82013-08-07 10:08:22 -0700447 if (files != null) {
448 for (int i=files.size()-1; i>=0; i--) {
449 try {
450 ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
451 new File(files.get(i)), ParcelFileDescriptor.MODE_READ_ONLY);
452 historic.add(pfd);
453 } catch (IOException e) {
454 Slog.w(TAG, "Failure opening procstat file " + files.get(i), e);
455 }
456 }
457 }
458 }
459 } finally {
460 mWriteLock.unlock();
461 }
462 return current.marshall();
463 }
464
Dianne Hackborn53459a72013-09-17 17:14:57 -0700465 public ParcelFileDescriptor getStatsOverTime(long minTime) {
466 mAm.mContext.enforceCallingOrSelfPermission(
467 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
468 mWriteLock.lock();
469 try {
470 Parcel current = Parcel.obtain();
471 long curTime;
472 synchronized (mAm) {
473 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
474 mProcessStats.writeToParcel(current, 0);
475 curTime = mProcessStats.mTimePeriodEndRealtime
476 - mProcessStats.mTimePeriodStartRealtime;
477 }
478 if (curTime < minTime) {
479 // Need to add in older stats to reach desired time.
480 ArrayList<String> files = getCommittedFiles(0, false, true);
Dianne Hackborncb428552013-09-26 11:07:17 -0700481 if (files != null && files.size() > 0) {
Dianne Hackborn53459a72013-09-17 17:14:57 -0700482 current.setDataPosition(0);
483 ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
484 current.recycle();
Dianne Hackborn59da6792013-10-10 18:27:24 -0700485 int i = files.size()-1;
486 while (i >= 0 && (stats.mTimePeriodEndRealtime
Dianne Hackborn53459a72013-09-17 17:14:57 -0700487 - stats.mTimePeriodStartRealtime) < minTime) {
488 AtomicFile file = new AtomicFile(new File(files.get(i)));
Dianne Hackborn59da6792013-10-10 18:27:24 -0700489 i--;
Dianne Hackborn53459a72013-09-17 17:14:57 -0700490 ProcessStats moreStats = new ProcessStats(false);
491 readLocked(moreStats, file);
492 if (moreStats.mReadError == null) {
493 stats.add(moreStats);
494 StringBuilder sb = new StringBuilder();
495 sb.append("Added stats: ");
496 sb.append(moreStats.mTimePeriodStartClockStr);
497 sb.append(", over ");
498 TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime
499 - moreStats.mTimePeriodStartRealtime, sb);
500 Slog.i(TAG, sb.toString());
501 } else {
Dianne Hackborn59da6792013-10-10 18:27:24 -0700502 Slog.w(TAG, "Failure reading " + files.get(i+1) + "; "
Dianne Hackborn53459a72013-09-17 17:14:57 -0700503 + moreStats.mReadError);
504 continue;
505 }
506 }
507 current = Parcel.obtain();
508 stats.writeToParcel(current, 0);
509 }
510 }
511 final byte[] outData = current.marshall();
512 current.recycle();
513 final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
514 Thread thr = new Thread("ProcessStats pipe output") {
515 public void run() {
516 FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]);
517 try {
518 fout.write(outData);
519 fout.close();
520 } catch (IOException e) {
521 Slog.w(TAG, "Failure writing pipe", e);
522 }
523 }
524 };
525 thr.start();
526 return fds[0];
527 } catch (IOException e) {
528 Slog.w(TAG, "Failed building output pipe", e);
529 } finally {
530 mWriteLock.unlock();
531 }
532 return null;
533 }
534
Dianne Hackborn69cb00b2013-08-09 16:16:56 -0700535 public int getCurrentMemoryState() {
536 synchronized (mAm) {
537 return mLastMemOnlyState;
538 }
539 }
540
Dianne Hackborn237cefb2013-10-22 18:45:27 -0700541 private void dumpAggregatedStats(PrintWriter pw, long aggregateHours, long now,
542 String reqPackage, boolean isCompact, boolean dumpDetails, boolean dumpFullDetails,
543 boolean dumpAll, boolean activeOnly) {
544 ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000
545 - (ProcessStats.COMMIT_PERIOD/2));
546 if (pfd == null) {
547 pw.println("Unable to build stats!");
548 return;
549 }
550 ProcessStats stats = new ProcessStats(false);
551 InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
552 stats.read(stream);
553 if (stats.mReadError != null) {
554 pw.print("Failure reading: "); pw.println(stats.mReadError);
555 return;
556 }
557 if (isCompact) {
558 stats.dumpCheckinLocked(pw, reqPackage);
559 } else {
560 if (dumpDetails || dumpFullDetails) {
561 stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
562 } else {
563 stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
564 }
565 }
566 }
567
Dianne Hackbornd2932242013-08-05 18:18:42 -0700568 static private void dumpHelp(PrintWriter pw) {
569 pw.println("Process stats (procstats) dump options:");
570 pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
Dianne Hackborn164371f2013-10-01 19:10:13 -0700571 pw.println(" [--details] [--full-details] [--current] [--hours] [--active]");
Dianne Hackborn53459a72013-09-17 17:14:57 -0700572 pw.println(" [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
Dianne Hackbornd2932242013-08-05 18:18:42 -0700573 pw.println(" --checkin: perform a checkin: print and delete old committed states.");
574 pw.println(" --c: print only state in checkin format.");
575 pw.println(" --csv: output data suitable for putting in a spreadsheet.");
576 pw.println(" --csv-screen: on, off.");
577 pw.println(" --csv-mem: norm, mod, low, crit.");
578 pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,");
579 pw.println(" service, home, prev, cached");
Dianne Hackborn164371f2013-10-01 19:10:13 -0700580 pw.println(" --details: dump per-package details, not just summary.");
581 pw.println(" --full-details: dump all timing and active state details.");
Dianne Hackbornd2932242013-08-05 18:18:42 -0700582 pw.println(" --current: only dump current state.");
Dianne Hackborncb428552013-09-26 11:07:17 -0700583 pw.println(" --hours: aggregate over about N last hours.");
Dianne Hackborn164371f2013-10-01 19:10:13 -0700584 pw.println(" --active: only show currently active processes/services.");
Dianne Hackbornd2932242013-08-05 18:18:42 -0700585 pw.println(" --commit: commit current stats to disk and reset to start new stats.");
Dianne Hackborn0d97cd12013-09-16 19:02:52 -0700586 pw.println(" --reset: reset current stats, without committing.");
Dianne Hackborn53459a72013-09-17 17:14:57 -0700587 pw.println(" --clear: clear all stats; does both --reset and deletes old stats.");
Dianne Hackbornd2932242013-08-05 18:18:42 -0700588 pw.println(" --write: write current in-memory stats to disk.");
589 pw.println(" --read: replace current stats with last-written stats.");
590 pw.println(" -a: print everything.");
591 pw.println(" -h: print this help text.");
592 pw.println(" <package.name>: optional name of package to filter output by.");
593 }
594
Dianne Hackborn8a0de582013-08-07 15:22:07 -0700595 @Override
596 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
597 if (mAm.checkCallingPermission(android.Manifest.permission.DUMP)
598 != PackageManager.PERMISSION_GRANTED) {
599 pw.println("Permission Denial: can't dump procstats from from pid="
600 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
601 + " without permission " + android.Manifest.permission.DUMP);
602 return;
603 }
604
Dianne Hackborn08b36a42013-10-09 12:14:11 -0700605 long ident = Binder.clearCallingIdentity();
606 try {
607 dumpInner(fd, pw, args);
608 } finally {
609 Binder.restoreCallingIdentity(ident);
610 }
611 }
612
613 private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700614 final long now = SystemClock.uptimeMillis();
615
616 boolean isCheckin = false;
617 boolean isCompact = false;
618 boolean isCsv = false;
619 boolean currentOnly = false;
620 boolean dumpDetails = false;
Dianne Hackborn53459a72013-09-17 17:14:57 -0700621 boolean dumpFullDetails = false;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700622 boolean dumpAll = false;
Dianne Hackborncb428552013-09-26 11:07:17 -0700623 int aggregateHours = 0;
Dianne Hackborn164371f2013-10-01 19:10:13 -0700624 boolean activeOnly = false;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700625 String reqPackage = null;
626 boolean csvSepScreenStats = false;
627 int[] csvScreenStats = new int[] { ProcessStats.ADJ_SCREEN_OFF, ProcessStats.ADJ_SCREEN_ON};
628 boolean csvSepMemStats = false;
629 int[] csvMemStats = new int[] { ProcessStats.ADJ_MEM_FACTOR_CRITICAL};
630 boolean csvSepProcStats = true;
631 int[] csvProcStats = ProcessStats.ALL_PROC_STATES;
632 if (args != null) {
633 for (int i=0; i<args.length; i++) {
634 String arg = args[i];
635 if ("--checkin".equals(arg)) {
636 isCheckin = true;
637 } else if ("-c".equals(arg)) {
638 isCompact = true;
639 } else if ("--csv".equals(arg)) {
640 isCsv = true;
641 } else if ("--csv-screen".equals(arg)) {
642 i++;
643 if (i >= args.length) {
644 pw.println("Error: argument required for --csv-screen");
645 dumpHelp(pw);
646 return;
647 }
648 boolean[] sep = new boolean[1];
649 String[] error = new String[1];
650 csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD,
651 args[i], sep, error);
652 if (csvScreenStats == null) {
653 pw.println("Error in \"" + args[i] + "\": " + error[0]);
654 dumpHelp(pw);
655 return;
656 }
657 csvSepScreenStats = sep[0];
658 } else if ("--csv-mem".equals(arg)) {
659 i++;
660 if (i >= args.length) {
661 pw.println("Error: argument required for --csv-mem");
662 dumpHelp(pw);
663 return;
664 }
665 boolean[] sep = new boolean[1];
666 String[] error = new String[1];
667 csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error);
668 if (csvMemStats == null) {
669 pw.println("Error in \"" + args[i] + "\": " + error[0]);
670 dumpHelp(pw);
671 return;
672 }
673 csvSepMemStats = sep[0];
674 } else if ("--csv-proc".equals(arg)) {
675 i++;
676 if (i >= args.length) {
677 pw.println("Error: argument required for --csv-proc");
678 dumpHelp(pw);
679 return;
680 }
681 boolean[] sep = new boolean[1];
682 String[] error = new String[1];
683 csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error);
684 if (csvProcStats == null) {
685 pw.println("Error in \"" + args[i] + "\": " + error[0]);
686 dumpHelp(pw);
687 return;
688 }
689 csvSepProcStats = sep[0];
690 } else if ("--details".equals(arg)) {
691 dumpDetails = true;
Dianne Hackborn53459a72013-09-17 17:14:57 -0700692 } else if ("--full-details".equals(arg)) {
693 dumpFullDetails = true;
Dianne Hackborncb428552013-09-26 11:07:17 -0700694 } else if ("--hours".equals(arg)) {
695 i++;
696 if (i >= args.length) {
697 pw.println("Error: argument required for --hours");
698 dumpHelp(pw);
699 return;
700 }
701 try {
702 aggregateHours = Integer.parseInt(args[i]);
703 } catch (NumberFormatException e) {
704 pw.println("Error: --hours argument not an int -- " + args[i]);
705 dumpHelp(pw);
706 return;
707 }
Dianne Hackborn164371f2013-10-01 19:10:13 -0700708 } else if ("--active".equals(arg)) {
709 activeOnly = true;
710 currentOnly = true;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700711 } else if ("--current".equals(arg)) {
712 currentOnly = true;
713 } else if ("--commit".equals(arg)) {
Dianne Hackborn50ef0b62013-09-16 17:40:27 -0700714 synchronized (mAm) {
715 mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
716 writeStateLocked(true, true);
717 pw.println("Process stats committed.");
718 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700719 return;
Dianne Hackborn0d97cd12013-09-16 19:02:52 -0700720 } else if ("--reset".equals(arg)) {
721 synchronized (mAm) {
722 mProcessStats.resetSafely();
723 pw.println("Process stats reset.");
724 }
725 return;
Dianne Hackborn53459a72013-09-17 17:14:57 -0700726 } else if ("--clear".equals(arg)) {
727 synchronized (mAm) {
728 mProcessStats.resetSafely();
729 ArrayList<String> files = getCommittedFiles(0, true, true);
730 if (files != null) {
731 for (int fi=0; fi<files.size(); fi++) {
732 (new File(files.get(fi))).delete();
733 }
734 }
735 pw.println("All process stats cleared.");
736 }
737 return;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700738 } else if ("--write".equals(arg)) {
Dianne Hackborn50ef0b62013-09-16 17:40:27 -0700739 synchronized (mAm) {
740 writeStateSyncLocked();
741 pw.println("Process stats written.");
742 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700743 return;
744 } else if ("--read".equals(arg)) {
Dianne Hackborn50ef0b62013-09-16 17:40:27 -0700745 synchronized (mAm) {
746 readLocked(mProcessStats, mFile);
747 pw.println("Process stats read.");
748 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700749 return;
750 } else if ("-h".equals(arg)) {
751 dumpHelp(pw);
752 return;
753 } else if ("-a".equals(arg)) {
754 dumpDetails = true;
755 dumpAll = true;
756 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
757 pw.println("Unknown option: " + arg);
758 dumpHelp(pw);
759 return;
760 } else {
761 // Not an option, last argument must be a package name.
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800762 reqPackage = arg;
763 // Include all details, since we know we are only going to
764 // be dumping a smaller set of data. In fact only the details
765 // container per-package data, so that are needed to be able
766 // to dump anything at all when filtering by package.
767 dumpDetails = true;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700768 }
769 }
770 }
771
772 if (isCsv) {
773 pw.print("Processes running summed over");
774 if (!csvSepScreenStats) {
775 for (int i=0; i<csvScreenStats.length; i++) {
776 pw.print(" ");
777 ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]);
778 }
779 }
780 if (!csvSepMemStats) {
781 for (int i=0; i<csvMemStats.length; i++) {
782 pw.print(" ");
783 ProcessStats.printMemLabelCsv(pw, csvMemStats[i]);
784 }
785 }
786 if (!csvSepProcStats) {
787 for (int i=0; i<csvProcStats.length; i++) {
788 pw.print(" ");
789 pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]);
790 }
791 }
792 pw.println();
Dianne Hackborn8a0de582013-08-07 15:22:07 -0700793 synchronized (mAm) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700794 dumpFilteredProcessesCsvLocked(pw, null,
795 csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
796 csvSepProcStats, csvProcStats, now, reqPackage);
797 /*
798 dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:",
799 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
800 true, new int[] {ADJ_MEM_FACTOR_CRITICAL},
801 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
802 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
803 STATE_PREVIOUS, STATE_CACHED},
804 now, reqPackage);
805 dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:",
806 false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
807 false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW,
808 ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE},
809 true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
810 STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
811 STATE_PREVIOUS, STATE_CACHED},
812 now, reqPackage);
813 */
814 }
815 return;
Dianne Hackborncb428552013-09-26 11:07:17 -0700816 } else if (aggregateHours != 0) {
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800817 pw.print("AGGREGATED OVER LAST "); pw.print(aggregateHours); pw.println(" HOURS:");
Dianne Hackborn237cefb2013-10-22 18:45:27 -0700818 dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
819 dumpDetails, dumpFullDetails, dumpAll, activeOnly);
Dianne Hackborn53459a72013-09-17 17:14:57 -0700820 return;
Dianne Hackbornd2932242013-08-05 18:18:42 -0700821 }
822
823 boolean sepNeeded = false;
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800824 if (dumpAll || isCheckin) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700825 mWriteLock.lock();
826 try {
Dianne Hackborn53459a72013-09-17 17:14:57 -0700827 ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700828 if (files != null) {
829 for (int i=0; i<files.size(); i++) {
830 if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
831 try {
832 AtomicFile file = new AtomicFile(new File(files.get(i)));
833 ProcessStats processStats = new ProcessStats(false);
834 readLocked(processStats, file);
835 if (processStats.mReadError != null) {
836 if (isCheckin || isCompact) pw.print("err,");
837 pw.print("Failure reading "); pw.print(files.get(i));
838 pw.print("; "); pw.println(processStats.mReadError);
839 if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
840 (new File(files.get(i))).delete();
841 continue;
842 }
843 String fileStr = file.getBaseFile().getPath();
844 boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
845 if (isCheckin || isCompact) {
846 // Don't really need to lock because we uniquely own this object.
847 processStats.dumpCheckinLocked(pw, reqPackage);
848 } else {
849 if (sepNeeded) {
850 pw.println();
851 } else {
852 sepNeeded = true;
853 }
854 pw.print("COMMITTED STATS FROM ");
855 pw.print(processStats.mTimePeriodStartClockStr);
856 if (checkedIn) pw.print(" (checked in)");
857 pw.println(":");
858 // Don't really need to lock because we uniquely own this object.
Dianne Hackbornae36b232013-09-03 18:12:53 -0700859 // Always dump summary here, dumping all details is just too
860 // much crud.
Dianne Hackborn53459a72013-09-17 17:14:57 -0700861 if (dumpFullDetails) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700862 mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
863 activeOnly);
Dianne Hackborn53459a72013-09-17 17:14:57 -0700864 } else {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700865 processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
Dianne Hackborn53459a72013-09-17 17:14:57 -0700866 }
Dianne Hackbornd2932242013-08-05 18:18:42 -0700867 }
868 if (isCheckin) {
869 // Rename file suffix to mark that it has checked in.
870 file.getBaseFile().renameTo(new File(
871 fileStr + STATE_FILE_CHECKIN_SUFFIX));
872 }
873 } catch (Throwable e) {
874 pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
875 e.printStackTrace(pw);
876 }
877 }
878 }
879 } finally {
880 mWriteLock.unlock();
881 }
882 }
883 if (!isCheckin) {
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800884 if (!currentOnly) {
Dianne Hackborn237cefb2013-10-22 18:45:27 -0700885 if (sepNeeded) {
886 pw.println();
Dianne Hackborn237cefb2013-10-22 18:45:27 -0700887 }
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800888 pw.println("AGGREGATED OVER LAST 24 HOURS:");
Dianne Hackborn237cefb2013-10-22 18:45:27 -0700889 dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
890 dumpDetails, dumpFullDetails, dumpAll, activeOnly);
891 pw.println();
892 pw.println("AGGREGATED OVER LAST 3 HOURS:");
893 dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
894 dumpDetails, dumpFullDetails, dumpAll, activeOnly);
895 sepNeeded = true;
896 }
Dianne Hackborn8a0de582013-08-07 15:22:07 -0700897 synchronized (mAm) {
Dianne Hackbornd2932242013-08-05 18:18:42 -0700898 if (isCompact) {
899 mProcessStats.dumpCheckinLocked(pw, reqPackage);
900 } else {
901 if (sepNeeded) {
902 pw.println();
Dianne Hackbornd2932242013-08-05 18:18:42 -0700903 }
Dianne Hackborndaa0d5c2013-11-06 16:30:29 -0800904 pw.println("CURRENT STATS:");
Dianne Hackborn53459a72013-09-17 17:14:57 -0700905 if (dumpDetails || dumpFullDetails) {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700906 mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
907 activeOnly);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700908 if (dumpAll) {
909 pw.print(" mFile="); pw.println(mFile.getBaseFile());
910 }
911 } else {
Dianne Hackborn164371f2013-10-01 19:10:13 -0700912 mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
Dianne Hackbornd2932242013-08-05 18:18:42 -0700913 }
914 }
915 }
916 }
917 }
918}