summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dianne Hackborn <hackbod@google.com> 2013-06-29 02:14:10 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2013-06-29 02:14:10 +0000
commitf406e230125bc0328e80ffbc2ede92e61affe733 (patch)
tree600877a6e778d5ce77680ce080814234af810b2c
parent04971a736ae742e6593afa3537e2676b067ac46d (diff)
parent904a857d5a319e32d1df065b38e3191324b35b0f (diff)
Merge "More work on procstats: save/restore state, fixes."
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java1
-rw-r--r--services/java/com/android/server/am/ActiveServices.java20
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java49
-rw-r--r--services/java/com/android/server/am/ProcessTracker.java1125
-rw-r--r--services/java/com/android/server/am/UsageStatsService.java62
6 files changed, 903 insertions, 356 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 6f75f7f83a29..5fa874bedf72 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2205,6 +2205,8 @@ public class ActivityManager {
pw.println();
dumpService(pw, fd, "procstats", new String[] { packageName });
pw.println();
+ dumpService(pw, fd, "usagestats", new String[] { "--packages", packageName });
+ pw.println();
dumpService(pw, fd, "package", new String[] { packageName });
pw.println();
dumpService(pw, fd, BatteryStats.SERVICE_NAME, new String[] { packageName });
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 061bf17455a7..e0a154cea29a 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -613,7 +613,6 @@ public final class BatteryStatsImpl extends BatteryStats {
* Constructs from a parcel.
* @param type
* @param unpluggables
- * @param powerType
* @param in
*/
Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index cd718a271a40..3c10480c2c9b 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -244,7 +244,7 @@ public final class ActiveServices {
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
if (r.tracker != null) {
- r.tracker.setStarted(true, mAm.mProcessTracker.getMemFactor(), r.lastActivity);
+ r.tracker.setStarted(true, mAm.mProcessTracker.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
@@ -265,7 +265,7 @@ public final class ActiveServices {
}
service.startRequested = false;
if (service.tracker != null) {
- service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+ service.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
service.callStart = false;
@@ -365,7 +365,7 @@ public final class ActiveServices {
}
r.startRequested = false;
if (r.tracker != null) {
- r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+ r.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
r.callStart = false;
@@ -505,7 +505,7 @@ public final class ActiveServices {
if (!s.hasAutoCreateConnections()) {
// This is the first binding, let the tracker know.
if (s.tracker != null) {
- s.tracker.setBound(true, mAm.mProcessTracker.getMemFactor(),
+ s.tracker.setBound(true, mAm.mProcessTracker.getMemFactorLocked(),
s.lastActivity);
}
}
@@ -827,7 +827,7 @@ public final class ActiveServices {
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) {
if (r.tracker != null) {
- r.tracker.setExecuting(true, mAm.mProcessTracker.getMemFactor(), now);
+ r.tracker.setExecuting(true, mAm.mProcessTracker.getMemFactorLocked(), now);
}
if (r.app != null) {
if (r.app.executingServices.size() == 0) {
@@ -1327,7 +1327,7 @@ public final class ActiveServices {
((ServiceRestarter)r.restarter).setService(null);
}
- int memFactor = mAm.mProcessTracker.getMemFactor();
+ int memFactor = mAm.mProcessTracker.getMemFactorLocked();
long now = SystemClock.uptimeMillis();
if (r.tracker != null) {
r.tracker.setStarted(false, memFactor, now);
@@ -1394,7 +1394,7 @@ public final class ActiveServices {
boolean hasAutoCreate = s.hasAutoCreateConnections();
if (!hasAutoCreate) {
if (s.tracker != null) {
- s.tracker.setBound(false, mAm.mProcessTracker.getMemFactor(),
+ s.tracker.setBound(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
}
@@ -1490,7 +1490,7 @@ public final class ActiveServices {
mAm.updateOomAdjLocked(r.app);
}
if (r.tracker != null) {
- r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactor(),
+ r.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
}
@@ -1685,7 +1685,7 @@ public final class ActiveServices {
sr.isolatedProc = null;
sr.executeNesting = 0;
if (sr.tracker != null) {
- sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactor(),
+ sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
if (mStoppingServices.remove(sr)) {
@@ -1720,7 +1720,7 @@ public final class ActiveServices {
if (sr.pendingStarts.size() == 0) {
sr.startRequested = false;
if (sr.tracker != null) {
- sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactor(),
+ sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
if (!sr.hasAutoCreateConnections()) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ed9416eaffb0..9c98848b5038 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -272,7 +272,11 @@ public final class ActivityManagerService extends ActivityManagerNative
// The amount of time we will sample PSS of the current top process while the
// screen is on.
- static final int PSS_TOP_INTERVAL = 5*60*1000;
+ static final int PSS_TOP_INTERVAL = 2*60*1000;
+
+ // The amount of time we will sample PSS of any processes that more at least as
+ // important as perceptible while the screen is on.
+ static final int PSS_PERCEPTIBLE_INTERVAL = 10*60*1000;
// The maximum amount of time for a process to be around until we will take
// a PSS snapshot on its next oom change.
@@ -423,7 +427,7 @@ public final class ActivityManagerService extends ActivityManagerNative
* Tracking long-term execution of processes to look for abuse and other
* bad app behavior.
*/
- ProcessTracker mProcessTracker;
+ final ProcessTracker mProcessTracker;
/**
* The currently running isolated processes.
@@ -1528,12 +1532,12 @@ public final class ActivityManagerService extends ActivityManagerNative
if (proc.thread != null) {
oomAdj = proc.setAdj;
pid = proc.pid;
- i++;
} else {
proc = null;
oomAdj = 0;
pid = 0;
}
+ i++;
}
if (proc != null) {
long pss = Debug.getPss(pid);
@@ -1620,7 +1624,6 @@ public final class ActivityManagerService extends ActivityManagerNative
m.mContext = context;
m.mFactoryTest = factoryTest;
m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
- m.mProcessTracker = new ProcessTracker(context);
m.mStackSupervisor = new ActivityStackSupervisor(m, context, thr.mLooper);
@@ -1817,8 +1820,10 @@ public final class ActivityManagerService extends ActivityManagerNative
: mBatteryStatsService.getActiveStatistics().getIsOnBattery();
mBatteryStatsService.getActiveStatistics().setCallback(this);
- mUsageStatsService = new UsageStatsService(new File(
- systemDir, "usagestats").toString());
+ mProcessTracker = new ProcessTracker(new File(systemDir, "procstats"));
+ mProcessTracker.readLocked();
+
+ mUsageStatsService = new UsageStatsService(new File(systemDir, "usagestats").toString());
mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));
mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));
@@ -7636,6 +7641,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mAppOpsService.shutdown();
mUsageStatsService.shutdown();
mBatteryStatsService.shutdown();
+ synchronized (this) {
+ mProcessTracker.shutdownLocked();
+ }
return timedout;
}
@@ -14203,8 +14211,17 @@ public final class ActivityManagerService extends ActivityManagerNative
app.setRawAdj = app.curRawAdj;
}
- if (app == TOP_APP && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
- requestPssLocked(app, now, true);
+ if (!mSleeping) {
+ if (app == TOP_APP && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
+ // For the current top application we will very aggressively collect
+ // PSS data to have a good measure of memory use while in the foreground.
+ requestPssLocked(app, now, true);
+ } else if (app.curAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+ && now > (app.lastPssTime+PSS_TOP_INTERVAL)) {
+ // For any unkillable processes, we will more regularly collect their PSS
+ // since they have a significant impact on the memory state of the device.
+ requestPssLocked(app, now, true);
+ }
}
if (app.curAdj != app.setAdj) {
@@ -14223,7 +14240,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.setAdj = app.curAdj;
app.setAdjChanged = true;
if (!doingAll) {
- app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactor(),
+ app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactorLocked(),
now, mProcessList);
}
} else {
@@ -14599,9 +14616,9 @@ public final class ActivityManagerService extends ActivityManagerNative
mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
}
- boolean allChanged = mProcessTracker.setMemFactor(memFactor, !mSleeping, now);
+ boolean allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
if (changed || allChanged) {
- memFactor = mProcessTracker.getMemFactor();
+ memFactor = mProcessTracker.getMemFactorLocked();
for (i=mLruProcesses.size()-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (allChanged || app.setAdjChanged) {
@@ -14613,6 +14630,16 @@ public final class ActivityManagerService extends ActivityManagerNative
requestPssAllProcsLocked(now, false);
}
+ if (mProcessTracker.shouldWriteNowLocked(now)) {
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ synchronized (ActivityManagerService.this) {
+ mProcessTracker.writeStateAsyncLocked();
+ }
+ }
+ });
+ }
+
if (DEBUG_OOM_ADJ) {
Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms");
}
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index 82b2158d2e84..7985df48056e 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -16,25 +16,37 @@
package com.android.server.am;
-import android.content.Context;
-import android.content.pm.PackageManager;
+import android.app.AppGlobals;
+import android.content.pm.IPackageManager;
+import android.os.Parcel;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.server.ProcessMap;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.concurrent.locks.ReentrantLock;
public final class ProcessTracker {
+ static final String TAG = "ProcessTracker";
+
public static final int STATE_NOTHING = -1;
public static final int STATE_PERSISTENT = 0;
public static final int STATE_TOP = 1;
@@ -118,14 +130,23 @@ public final class ProcessTracker {
};
static final String[] STATE_TAGS = new String[] {
- "p", "t", "f", "v", "t",
- "b", "s", "h", "r", "c"
+ "y", "t", "f", "v", "r",
+ "b", "s", "h", "p", "c"
};
static final String CSV_SEP = "\t";
- final Context mContext;
+ static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so.
+
+ final File mBaseDir;
+ final AtomicFile mFile;
final State mState = new State();
+ long mLastWriteTime;
+ boolean mShuttingDown;
+
+ final Object mPendingWriteLock = new Object();
+ Parcel mPendingWrite;
+ final ReentrantLock mWriteLock = new ReentrantLock();
public static final class ProcessState {
final State mState;
@@ -184,33 +205,33 @@ public final class ProcessTracker {
ProcessState clone(String pkg, long now) {
ProcessState pnew = new ProcessState(this, pkg, mUid, mName, now);
if (mDurationsTable != null) {
- mState.mFindTable = new int[mDurationsTable.length];
- mState.mFindTableSize = 0;
+ mState.mAddLongTable = new int[mDurationsTable.length];
+ mState.mAddLongTableSize = 0;
for (int i=0; i<mDurationsTableSize; i++) {
int origEnt = mDurationsTable[i];
int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
int newOff = mState.addLongData(i, type, 1);
- mState.mFindTable[i] = newOff | type;
+ mState.mAddLongTable[i] = newOff | type;
mState.setLong(newOff, 0, mState.getLong(origEnt, 0));
}
- pnew.mDurationsTable = mState.mFindTable;
- pnew.mDurationsTableSize = mState.mFindTableSize;
+ pnew.mDurationsTable = mState.mAddLongTable;
+ pnew.mDurationsTableSize = mState.mAddLongTableSize;
}
/*
if (mPssTable != null) {
- mState.mFindTable = new int[mPssTable.length];
- mState.mFindTableSize = 0;
+ mState.mAddLongTable = new int[mPssTable.length];
+ mState.mAddLongTableSize = 0;
for (int i=0; i<mPssTableSize; i++) {
int origEnt = mPssTable[i];
int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
int newOff = mState.addLongData(i, type, PSS_COUNT);
- mState.mFindTable[i] = newOff | type;
+ mState.mAddLongTable[i] = newOff | type;
for (int j=0; j<PSS_COUNT; j++) {
mState.setLong(newOff, j, mState.getLong(origEnt, j));
}
}
- pnew.mPssTable = mState.mFindTable;
- pnew.mPssTableSize = mState.mFindTableSize;
+ pnew.mPssTable = mState.mAddLongTable;
+ pnew.mPssTableSize = mState.mAddLongTableSize;
}
*/
pnew.mNumExcessiveWake = mNumExcessiveWake;
@@ -218,6 +239,56 @@ public final class ProcessTracker {
return pnew;
}
+ void writeToParcel(Parcel out, long now) {
+ commitStateTime(now);
+ out.writeInt(mMultiPackage ? 1 : 0);
+ out.writeInt(mDurationsTableSize);
+ for (int i=0; i<mDurationsTableSize; i++) {
+ out.writeInt(mDurationsTable[i]);
+ }
+ out.writeInt(mPssTableSize);
+ for (int i=0; i<mPssTableSize; i++) {
+ out.writeInt(mPssTable[i]);
+ }
+ out.writeInt(mNumExcessiveWake);
+ out.writeInt(mNumExcessiveCpu);
+ }
+
+ private int[] readTable(Parcel in, String what) {
+ int size = in.readInt();
+ if (size < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
+ return null;
+ }
+ int[] table = new int[size];
+ for (int i=0; i<size; i++) {
+ table[i] = in.readInt();
+ if (!mState.validateLongOffset(table[i])) {
+ Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: 0x"
+ + Integer.toHexString(table[i]));
+ return null;
+ }
+ }
+ return table;
+ }
+
+ boolean readFromParcel(Parcel in) {
+ mMultiPackage = in.readInt() != 0;
+ mDurationsTable = readTable(in, "durations");
+ if (mDurationsTable == null) {
+ return false;
+ }
+ mDurationsTableSize = mDurationsTable.length;
+ mPssTable = readTable(in, "pss");
+ if (mPssTable == null) {
+ return false;
+ }
+ mPssTableSize = mPssTable.length;
+ mNumExcessiveWake = in.readInt();
+ mNumExcessiveCpu = in.readInt();
+ return true;
+ }
+
public void setState(int state, int memFactor, long now,
ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
if (state != STATE_NOTHING) {
@@ -239,27 +310,31 @@ public final class ProcessTracker {
void setState(int state, long now) {
if (mCurState != state) {
- if (mCurState != STATE_NOTHING) {
- long dur = now - mStartTime;
- int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
- int off;
- if (idx >= 0) {
- off = mDurationsTable[idx];
- } else {
- mState.mFindTable = mDurationsTable;
- mState.mFindTableSize = mDurationsTableSize;
- off = mState.addLongData(~idx, mCurState, 1);
- mDurationsTable = mState.mFindTable;
- mDurationsTableSize = mState.mFindTableSize;
- }
- long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
- }
+ commitStateTime(now);
mCurState = state;
- mStartTime = now;
}
}
+ void commitStateTime(long now) {
+ if (mCurState != STATE_NOTHING) {
+ long dur = now - mStartTime;
+ int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, mCurState);
+ int off;
+ if (idx >= 0) {
+ off = mDurationsTable[idx];
+ } else {
+ mState.mAddLongTable = mDurationsTable;
+ mState.mAddLongTableSize = mDurationsTableSize;
+ off = mState.addLongData(~idx, mCurState, 1);
+ mDurationsTable = mState.mAddLongTable;
+ mDurationsTableSize = mState.mAddLongTableSize;
+ }
+ long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
+ }
+ mStartTime = now;
+ }
+
public void addPss(long pss, boolean always) {
if (!always) {
if (mLastPssState == mCurState && SystemClock.uptimeMillis()
@@ -275,11 +350,11 @@ public final class ProcessTracker {
if (idx >= 0) {
off = mPssTable[idx];
} else {
- mState.mFindTable = mPssTable;
- mState.mFindTableSize = mPssTableSize;
+ mState.mAddLongTable = mPssTable;
+ mState.mAddLongTableSize = mPssTableSize;
off = mState.addLongData(~idx, mCurState, PSS_COUNT);
- mPssTable = mState.mFindTable;
- mPssTableSize = mState.mFindTableSize;
+ mPssTable = mState.mAddLongTable;
+ mPssTableSize = mState.mAddLongTableSize;
}
long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
@@ -388,6 +463,37 @@ public final class ProcessTracker {
int mExecState = STATE_NOTHING;
long mExecStartTime;
+ void writeToParcel(Parcel out, long now) {
+ if (mStartedState != STATE_NOTHING) {
+ mStartedDurations[mStartedState] += now - mStartedStartTime;
+ mStartedStartTime = now;
+ }
+ if (mBoundState != STATE_NOTHING) {
+ mBoundDurations[mBoundState] += now - mBoundStartTime;
+ mBoundStartTime = now;
+ }
+ if (mExecState != STATE_NOTHING) {
+ mExecDurations[mExecState] += now - mExecStartTime;
+ mExecStartTime = now;
+ }
+ out.writeLongArray(mStartedDurations);
+ out.writeInt(mStartedCount);
+ out.writeLongArray(mBoundDurations);
+ out.writeInt(mBoundCount);
+ out.writeLongArray(mExecDurations);
+ out.writeInt(mExecCount);
+ }
+
+ boolean readFromParcel(Parcel in) {
+ in.readLongArray(mStartedDurations);
+ mStartedCount = in.readInt();
+ in.readLongArray(mBoundDurations);
+ mBoundCount = in.readInt();
+ in.readLongArray(mExecDurations);
+ mExecCount = in.readInt();
+ return true;
+ }
+
public void setStarted(boolean started, int memFactor, long now) {
int state = started ? memFactor : STATE_NOTHING;
if (mStartedState != state) {
@@ -439,6 +545,14 @@ public final class ProcessTracker {
}
static final class State {
+ // Current version of the parcel format.
+ public static final int PARCEL_VERSION = 1;
+ // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
+ private static final int MAGIC = 0x50535453;
+
+ long mTimePeriodStart;
+ long mTimePeriodEnd;
+
final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
final long[] mMemFactorDurations = new long[ADJ_COUNT];
@@ -449,26 +563,286 @@ public final class ProcessTracker {
final ArrayList<long[]> mLongs = new ArrayList<long[]>();
int mNextLong;
- int[] mFindTable;
- int mFindTableSize;
+ int[] mAddLongTable;
+ int mAddLongTableSize;
+
+ State() {
+ reset();
+ }
+
+ void reset() {
+ mTimePeriodStart = mTimePeriodEnd = System.currentTimeMillis();
+ mPackages.getMap().clear();
+ mProcesses.getMap().clear();
+ mLongs.clear();
+ mLongs.add(new long[LONGS_SIZE]);
+ mNextLong = 0;
+ Arrays.fill(mMemFactorDurations, 0);
+ mMemFactor = STATE_NOTHING;
+ mStartTime = 0;
+ }
+
+ void writeToParcel(Parcel out) {
+ long now = SystemClock.uptimeMillis();
+ out.writeInt(MAGIC);
+ out.writeInt(PARCEL_VERSION);
+ out.writeInt(STATE_COUNT);
+ out.writeInt(ADJ_COUNT);
+ out.writeInt(PSS_COUNT);
+ out.writeInt(LONGS_SIZE);
+
+ out.writeLong(mTimePeriodStart);
+ out.writeLong(mTimePeriodEnd);
+
+ out.writeInt(mLongs.size());
+ out.writeInt(mNextLong);
+ for (int i=0; i<(mLongs.size()-1); i++) {
+ out.writeLongArray(mLongs.get(i));
+ }
+ long[] lastLongs = mLongs.get(mLongs.size()-1);
+ for (int i=0; i<mNextLong; i++) {
+ out.writeLong(lastLongs[i]);
+ }
+
+ if (mMemFactor != STATE_NOTHING) {
+ mMemFactorDurations[mMemFactor] += now - mStartTime;
+ mStartTime = now;
+ }
+ out.writeLongArray(mMemFactorDurations);
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ out.writeInt(NPROC);
+ for (int ip=0; ip<NPROC; ip++) {
+ out.writeString(procMap.keyAt(ip));
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ ProcessState proc = uids.valueAt(iu);
+ out.writeString(proc.mPackage);
+ proc.writeToParcel(out, now);
+ }
+ }
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ out.writeInt(NPKG);
+ for (int ip=0; ip<NPKG; ip++) {
+ out.writeString(pkgMap.keyAt(ip));
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ out.writeInt(NUID);
+ for (int iu=0; iu<NUID; iu++) {
+ out.writeInt(uids.keyAt(iu));
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ out.writeInt(NPROCS);
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ out.writeString(pkgState.mProcesses.keyAt(iproc));
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ if (proc.mCommonProcess == proc) {
+ // This is the same as the common process we wrote above.
+ out.writeInt(0);
+ } else {
+ // There is separate data for this package's process.
+ out.writeInt(1);
+ proc.writeToParcel(out, now);
+ }
+ }
+ final int NSRVS = pkgState.mServices.size();
+ out.writeInt(NSRVS);
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ out.writeString(pkgState.mServices.keyAt(isvc));
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ svc.writeToParcel(out, now);
+ }
+ }
+ }
+ }
+
+ private boolean readCheckedInt(Parcel in, int val, String what) {
+ int got;
+ if ((got=in.readInt()) != val) {
+ Slog.w(TAG, "Ignoring existing stats; bad " + ": " + got);
+ return false;
+ }
+ return true;
+ }
+
+ void readFromParcel(Parcel in) {
+ reset();
+ if (!readCheckedInt(in, MAGIC, "magic number")) {
+ return;
+ }
+ if (!readCheckedInt(in, PARCEL_VERSION, "version")) {
+ return;
+ }
+ if (!readCheckedInt(in, STATE_COUNT, "state count")) {
+ return;
+ }
+ if (!readCheckedInt(in, ADJ_COUNT, "adj count")) {
+ return;
+ }
+ if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
+ return;
+ }
+ if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
+ return;
+ }
+
+ mTimePeriodStart = in.readLong();
+ mTimePeriodEnd = in.readLong();
+
+ int NLONGS = in.readInt();
+ int NEXTLONG = in.readInt();
+ for (int i=0; i<(NLONGS-1); i++) {
+ while (i >= mLongs.size()) {
+ mLongs.add(new long[LONGS_SIZE]);
+ }
+ in.readLongArray(mLongs.get(i));
+ }
+ long[] longs = new long[LONGS_SIZE];
+ mNextLong = NEXTLONG;
+ for (int i=0; i<NEXTLONG; i++) {
+ longs[i] = in.readLong();
+ }
+ mLongs.add(longs);
+
+ in.readLongArray(mMemFactorDurations);
+
+ int NPROC = in.readInt();
+ if (NPROC < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad process count: " + NPROC);
+ return;
+ }
+ while (NPROC > 0) {
+ NPROC--;
+ String procName = in.readString();
+ if (procName == null) {
+ Slog.w(TAG, "Ignoring existing stats; bad process name");
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad uid count: " + NUID);
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ int uid = in.readInt();
+ if (uid < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad uid: " + uid);
+ return;
+ }
+ String pkgName = in.readString();
+ if (pkgName == null) {
+ Slog.w(TAG, "Ignoring existing stats; bad process package name");
+ return;
+ }
+ ProcessState proc = new ProcessState(this, pkgName, uid, procName);
+ if (!proc.readFromParcel(in)) {
+ return;
+ }
+ mProcesses.put(procName, uid, proc);
+ }
+ }
+
+ int NPKG = in.readInt();
+ if (NPKG < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad package count: " + NPKG);
+ return;
+ }
+ while (NPKG > 0) {
+ NPKG--;
+ String pkgName = in.readString();
+ if (pkgName == null) {
+ Slog.w(TAG, "Ignoring existing stats; bad package name");
+ return;
+ }
+ int NUID = in.readInt();
+ if (NUID < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad uid count: " + NUID);
+ return;
+ }
+ while (NUID > 0) {
+ NUID--;
+ int uid = in.readInt();
+ if (uid < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad uid: " + uid);
+ return;
+ }
+ PackageState pkgState = new PackageState(uid);
+ int NPROCS = in.readInt();
+ if (NPROCS < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad package process count: " + NPROCS);
+ return;
+ }
+ while (NPROCS > 0) {
+ NPROCS--;
+ String procName = in.readString();
+ if (procName == null) {
+ Slog.w(TAG, "Ignoring existing stats; bad package process name");
+ return;
+ }
+ int hasProc = in.readInt();
+ if (hasProc != 0) {
+ // The process for this package is unique to the package; we
+ // need to load it. We don't need to do anything about it if
+ // it is not unique because if someone later looks for it
+ // they will find and use it from the global procs.
+ ProcessState commonProc = mProcesses.get(procName, uid);
+ if (commonProc == null) {
+ Slog.w(TAG, "Ignoring existing stats; no common proc: " + procName);
+ return;
+ }
+ ProcessState proc = new ProcessState(commonProc, pkgName, uid,
+ procName, 0);
+ if (!proc.readFromParcel(in)) {
+ return;
+ }
+ pkgState.mProcesses.put(procName, proc);
+ }
+ }
+ int NSRVS = in.readInt();
+ if (NSRVS < 0) {
+ Slog.w(TAG, "Ignoring existing stats; bad package service count: " + NSRVS);
+ return;
+ }
+ while (NSRVS > 0) {
+ NSRVS--;
+ String serviceName = in.readString();
+ if (serviceName == null) {
+ Slog.w(TAG, "Ignoring existing stats; bad package service name");
+ return;
+ }
+ ServiceState serv = new ServiceState();
+ if (!serv.readFromParcel(in)) {
+ return;
+ }
+ pkgState.mServices.put(serviceName, serv);
+ }
+ }
+ }
+ }
int addLongData(int index, int type, int num) {
- int tableLen = mFindTable != null ? mFindTable.length : 0;
- if (mFindTableSize >= tableLen) {
+ int tableLen = mAddLongTable != null ? mAddLongTable.length : 0;
+ if (mAddLongTableSize >= tableLen) {
int newSize = ArrayUtils.idealIntArraySize(tableLen + 1);
int[] newTable = new int[newSize];
if (tableLen > 0) {
- System.arraycopy(mFindTable, 0, newTable, 0, tableLen);
+ System.arraycopy(mAddLongTable, 0, newTable, 0, tableLen);
}
- mFindTable = newTable;
+ mAddLongTable = newTable;
}
- if (mFindTableSize > 0 && mFindTableSize - index != 0) {
- System.arraycopy(mFindTable, index, mFindTable, index + 1,
- mFindTableSize - index);
+ if (mAddLongTableSize > 0 && mAddLongTableSize - index != 0) {
+ System.arraycopy(mAddLongTable, index, mAddLongTable, index + 1,
+ mAddLongTableSize - index);
}
int off = allocLongData(num);
- mFindTable[index] = type | off;
- mFindTableSize++;
+ mAddLongTable[index] = type | off;
+ mAddLongTableSize++;
return off;
}
@@ -486,6 +860,18 @@ public final class ProcessTracker {
return off;
}
+ boolean validateLongOffset(int off) {
+ int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK;
+ if (arr >= mLongs.size()) {
+ return false;
+ }
+ int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+ if (idx >= LONGS_SIZE) {
+ return false;
+ }
+ return true;
+ }
+
void setLong(int off, int index, long value) {
long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value;
@@ -563,24 +949,266 @@ public final class ProcessTracker {
return ps;
}
- State() {
- reset();
+ void dump(PrintWriter pw, String reqPackage, boolean dumpAll) {
+ final long now = SystemClock.uptimeMillis();
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ boolean printedHeader = false;
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ if (NPROCS > 0 || NSRVS > 0) {
+ if (!printedHeader) {
+ pw.println("Per-Package Process Stats:");
+ printedHeader = true;
+ }
+ pw.print(" * "); pw.print(pkgName); pw.print(" / ");
+ UserHandle.formatUid(pw, uid); pw.println(":");
+ }
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ pw.print(" Process ");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(" (");
+ pw.print(proc.mDurationsTableSize);
+ pw.print(" entries)");
+ pw.println(":");
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ pw.print(" Service ");
+ pw.print(pkgState.mServices.keyAt(isvc));
+ pw.println(":");
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ if (svc.mStartedCount != 0) {
+ pw.print(" Started op count "); pw.print(svc.mStartedCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mStartedDurations, svc.mStartedState,
+ svc.mStartedStartTime, now);
+ }
+ if (svc.mBoundCount != 0) {
+ pw.print(" Bound op count "); pw.print(svc.mBoundCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mBoundDurations, svc.mBoundState,
+ svc.mBoundStartTime, now);
+ }
+ if (svc.mExecCount != 0) {
+ pw.print(" Executing op count "); pw.print(svc.mExecCount);
+ pw.println(":");
+ dumpSingleTime(pw, " ", svc.mExecDurations, svc.mExecState,
+ svc.mExecStartTime, now);
+ }
+ }
+ }
+ }
+
+ dumpFilteredProcesses(pw, "Processes running while critical mem:", " ",
+ new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ new int[] {ADJ_MEM_FACTOR_CRITICAL},
+ new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+ now, reqPackage);
+ dumpFilteredProcesses(pw, "Processes running while low mem:", " ",
+ new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ new int[] {ADJ_MEM_FACTOR_LOW},
+ new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+ now, reqPackage);
+ dumpFilteredProcesses(pw, "Processes running while moderate mem:", " ",
+ new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ new int[] {ADJ_MEM_FACTOR_MODERATE},
+ new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+ now, reqPackage);
+ dumpFilteredProcesses(pw, "Processes running while normal mem:", " ",
+ new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
+ new int[] {ADJ_MEM_FACTOR_NORMAL},
+ new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
+ STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
+ now, reqPackage);
+ pw.println();
+ pw.println("Run time Stats:");
+ dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
+ if (dumpAll) {
+ pw.println();
+ pw.println("Internal state:");
+ pw.print(" Num long arrays: "); pw.println(mLongs.size());
+ pw.print(" Next long entry: "); pw.println(mNextLong);
+ }
}
- void reset() {
- mPackages.getMap().clear();
- mProcesses.getMap().clear();
- mLongs.clear();
- mLongs.add(new long[LONGS_SIZE]);
- mNextLong = 0;
- Arrays.fill(mMemFactorDurations, 0);
- mMemFactor = STATE_NOTHING;
- mStartTime = 0;
+ void dumpFilteredProcesses(PrintWriter pw, String header, String prefix,
+ int[] screenStates, int[] memStates, int[] procStates, long now, String reqPackage) {
+ ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+ procStates, now, reqPackage);
+ if (procs.size() > 0) {
+ pw.println();
+ pw.println(header);
+ dumpProcessList(pw, prefix, procs, screenStates, memStates, procStates, now);
+ }
+ }
+
+ ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
+ int[] procStates, long now, String reqPackage) {
+ ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
+ continue;
+ }
+ SparseArray<PackageState> procs = pkgMap.valueAt(ip);
+ for (int iu=0; iu<procs.size(); iu++) {
+ PackageState state = procs.valueAt(iu);
+ for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
+ ProcessState proc = state.mProcesses.valueAt(iproc);
+ foundProcs.add(proc.mCommonProcess);
+ }
+ }
+ }
+ ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
+ for (int i=0; i<foundProcs.size(); i++) {
+ ProcessState proc = foundProcs.valueAt(i);
+ if (computeProcessTimeLocked(proc, screenStates, memStates,
+ procStates, now) > 0) {
+ outProcs.add(proc);
+ }
+ }
+ Collections.sort(outProcs, new Comparator<ProcessState>() {
+ @Override
+ public int compare(ProcessState lhs, ProcessState rhs) {
+ if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
+ return -1;
+ } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ return outProcs;
+ }
+
+ void dumpCheckin(PrintWriter pw, String reqPackage) {
+ final long now = SystemClock.uptimeMillis();
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ pw.println("vers,1");
+ for (int ip=0; ip<pkgMap.size(); ip++) {
+ String pkgName = pkgMap.keyAt(ip);
+ if (reqPackage != null && !reqPackage.equals(pkgName)) {
+ continue;
+ }
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ final int NSRVS = pkgState.mServices.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ ProcessState proc = pkgState.mProcesses.valueAt(iproc);
+ pw.print("pkgproc,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ dumpAllProcessStateCheckin(pw, proc, now);
+ pw.println();
+ if (proc.mPssTableSize > 0) {
+ pw.print("pkgpss,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ dumpAllProcessPssCheckin(pw, proc);
+ pw.println();
+ }
+ if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
+ pw.print("pkgkills,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(",");
+ pw.print(proc.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(proc.mNumExcessiveCpu);
+ pw.println();
+ }
+ }
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ String serviceName = pkgState.mServices.keyAt(isvc);
+ ServiceState svc = pkgState.mServices.valueAt(isvc);
+ dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
+ svc, svc.mStartedCount, svc.mStartedDurations, svc.mStartedState,
+ svc.mStartedStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
+ svc, svc.mBoundCount, svc.mBoundDurations, svc.mBoundState,
+ svc.mBoundStartTime, now);
+ dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
+ svc, svc.mExecCount, svc.mExecDurations, svc.mExecState,
+ svc.mExecStartTime, now);
+ }
+ }
+ }
+
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ for (int ip=0; ip<procMap.size(); ip++) {
+ String procName = procMap.keyAt(ip);
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ for (int iu=0; iu<uids.size(); iu++) {
+ int uid = uids.keyAt(iu);
+ ProcessState procState = uids.valueAt(iu);
+ if (procState.mDurationsTableSize > 0) {
+ pw.print("proc,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllProcessStateCheckin(pw, procState, now);
+ pw.println();
+ }
+ if (procState.mPssTableSize > 0) {
+ pw.print("pss,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ dumpAllProcessPssCheckin(pw, procState);
+ pw.println();
+ }
+ if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0) {
+ pw.print("kills,");
+ pw.print(procName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(procState.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(procState.mNumExcessiveCpu);
+ pw.println();
+ }
+ }
+ }
+ pw.print("total");
+ dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
+ mStartTime, now);
+ pw.println();
}
}
- public ProcessTracker(Context context) {
- mContext = context;
+ public ProcessTracker(File file) {
+ mBaseDir = file;
+ mBaseDir.mkdirs();
+ mFile = new AtomicFile(new File(file, "current.bin"));
}
public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
@@ -598,7 +1226,7 @@ public final class ProcessTracker {
return ss;
}
- public boolean setMemFactor(int memFactor, boolean screenOn, long now) {
+ public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
if (screenOn) {
memFactor += ADJ_SCREEN_ON;
}
@@ -630,10 +1258,119 @@ public final class ProcessTracker {
return false;
}
- public int getMemFactor() {
+ public int getMemFactorLocked() {
return mState.mMemFactor != STATE_NOTHING ? mState.mMemFactor : 0;
}
+ static byte[] readFully(FileInputStream stream) throws java.io.IOException {
+ int pos = 0;
+ int avail = stream.available();
+ byte[] data = new byte[avail];
+ while (true) {
+ int amt = stream.read(data, pos, data.length-pos);
+ //Log.i("foo", "Read " + amt + " bytes at " + pos
+ // + " of avail " + data.length);
+ if (amt <= 0) {
+ //Log.i("foo", "**** FINISHED READING: pos=" + pos
+ // + " len=" + data.length);
+ return data;
+ }
+ pos += amt;
+ avail = stream.available();
+ if (avail > data.length-pos) {
+ byte[] newData = new byte[pos+avail];
+ System.arraycopy(data, 0, newData, 0, pos);
+ data = newData;
+ }
+ }
+ }
+
+ public void readLocked() {
+ try {
+ FileInputStream stream = mFile.openRead();
+
+ byte[] raw = readFully(stream);
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ stream.close();
+
+ mState.readFromParcel(in);
+ } catch(Throwable e) {
+ Slog.e(TAG, "Error reading process statistics", e);
+ }
+
+ }
+
+ public boolean shouldWriteNowLocked(long now) {
+ return now > (mLastWriteTime+WRITE_PERIOD);
+ }
+
+ public void shutdownLocked() {
+ Slog.w(TAG, "Writing process stats before shutdown...");
+ writeStateSyncLocked();
+ mShuttingDown = true;
+ }
+
+ public void writeStateAsyncLocked() {
+ writeStateLocked(false);
+ }
+
+ public void writeStateSyncLocked() {
+ writeStateLocked(true);
+ }
+
+ private void writeStateLocked(boolean sync) {
+ if (mShuttingDown) {
+ return;
+ }
+
+ synchronized (mPendingWriteLock) {
+ long now = SystemClock.uptimeMillis();
+ mPendingWrite = Parcel.obtain();
+ mState.mTimePeriodEnd = System.currentTimeMillis();
+ mState.writeToParcel(mPendingWrite);
+ mLastWriteTime = SystemClock.uptimeMillis();
+ Slog.i(TAG, "Prepared write state in " + (SystemClock.uptimeMillis()-now) + "ms");
+ if (!sync) {
+ BackgroundThread.getHandler().post(new Runnable() {
+ @Override public void run() {
+ commitWriteState();
+ }
+ });
+ return;
+ }
+ }
+
+ commitWriteState();
+ }
+
+ void commitWriteState() {
+ Parcel data;
+ synchronized (mPendingWriteLock) {
+ data = mPendingWrite;
+ if (data == null) {
+ return;
+ }
+ mPendingWrite = null;
+ mWriteLock.lock();
+ }
+
+ FileOutputStream stream = null;
+ try {
+ stream = mFile.startWrite();
+ stream.write(data.marshall());
+ stream.flush();
+ mFile.finishWrite(stream);
+ } catch (IOException e) {
+ Slog.w(TAG, "Error writing process statistics", e);
+ mFile.failWrite(stream);
+ } finally {
+ data.recycle();
+ mWriteLock.unlock();
+ }
+ }
+
static private void printScreenLabel(PrintWriter pw, int offset) {
switch (offset) {
case ADJ_NOTHING:
@@ -794,45 +1531,6 @@ public final class ProcessTracker {
return totalTime;
}
- ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates,
- int[] procStates, long now, String reqPackage) {
- ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>();
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mState.mPackages.getMap();
- for (int ip=0; ip<pkgMap.size(); ip++) {
- if (reqPackage != null && !reqPackage.equals(pkgMap.keyAt(ip))) {
- continue;
- }
- SparseArray<PackageState> procs = pkgMap.valueAt(ip);
- for (int iu=0; iu<procs.size(); iu++) {
- PackageState state = procs.valueAt(iu);
- for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
- ProcessState proc = state.mProcesses.valueAt(iproc);
- foundProcs.add(proc.mCommonProcess);
- }
- }
- }
- ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size());
- for (int i=0; i<foundProcs.size(); i++) {
- ProcessState proc = foundProcs.valueAt(i);
- if (computeProcessTimeLocked(proc, screenStates, memStates,
- procStates, now) > 0) {
- outProcs.add(proc);
- }
- }
- Collections.sort(outProcs, new Comparator<ProcessState>() {
- @Override
- public int compare(ProcessState lhs, ProcessState rhs) {
- if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) {
- return -1;
- } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) {
- return 1;
- }
- return 0;
- }
- });
- return outProcs;
- }
-
static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
int[] screenStates, int[] memStates, int[] procStates, long now) {
long totalTime = 0;
@@ -1043,21 +1741,10 @@ public final class ProcessTracker {
}
}
- void dumpFilteredProcesses(PrintWriter pw, String header, String prefix,
- int[] screenStates, int[] memStates, int[] procStates, long now, String reqPackage) {
- ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
- procStates, now, reqPackage);
- if (procs.size() > 0) {
- pw.println();
- pw.println(header);
- dumpProcessList(pw, prefix, procs, screenStates, memStates, procStates, now);
- }
- }
-
boolean dumpFilteredProcessesCsv(PrintWriter pw, String header,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now, String reqPackage) {
- ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates,
+ ArrayList<ProcessState> procs = mState.collectProcessesLocked(screenStates, memStates,
procStates, now, reqPackage);
if (procs.size() > 0) {
if (header != null) {
@@ -1088,7 +1775,7 @@ public final class ProcessTracker {
static void printAdjTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
- printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
+ printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
}
static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
@@ -1117,7 +1804,7 @@ public final class ProcessTracker {
}
printProcStateTagAndValue(pw, type, time);
}
- if (!didCurState) {
+ if (!didCurState && proc.mCurState != STATE_NOTHING) {
printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime);
}
}
@@ -1187,7 +1874,7 @@ public final class ProcessTracker {
static private void dumpHelp(PrintWriter pw) {
pw.println("Process stats (procstats) dump options:");
pw.println(" [--checkin|--csv] [csv-screen] [csv-proc] [csv-mem]");
- pw.println(" [--reset] [-h] [<package.name>]");
+ pw.println(" [--reset] [--write] [-h] [<package.name>]");
pw.println(" --checkin: format output for a checkin report.");
pw.println(" --csv: output data suitable for putting in a spreadsheet.");
pw.println(" --csv-screen: on, off.");
@@ -1195,6 +1882,7 @@ public final class ProcessTracker {
pw.println(" --csv-proc: pers, top, fore, vis, precept, backup,");
pw.println(" service, home, prev, cached");
pw.println(" --reset: reset the stats, clearing all current data.");
+ pw.println(" --write: write current in-memory stats to disk.");
pw.println(" -a: print everything.");
pw.println(" -h: print this help text.");
pw.println(" <package.name>: optional name of package to filter output by.");
@@ -1276,6 +1964,10 @@ public final class ProcessTracker {
mState.reset();
pw.println("Process stats reset.");
return;
+ } else if ("--write".equals(arg)) {
+ writeStateSyncLocked();
+ pw.println("Process stats written.");
+ return;
} else if ("-h".equals(arg)) {
dumpHelp(pw);
return;
@@ -1288,10 +1980,13 @@ public final class ProcessTracker {
} else {
// Not an option, last argument must be a package name.
try {
- mContext.getPackageManager().getPackageUid(arg,
- UserHandle.getCallingUserId());
- reqPackage = arg;
- } catch (PackageManager.NameNotFoundException e) {
+ IPackageManager pm = AppGlobals.getPackageManager();
+ if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
+ reqPackage = arg;
+ }
+ } catch (RemoteException e) {
+ }
+ if (reqPackage == null) {
pw.println("Unknown package: " + arg);
dumpHelp(pw);
return;
@@ -1344,196 +2039,10 @@ public final class ProcessTracker {
return;
}
- ArrayMap<String, SparseArray<PackageState>> pkgMap = mState.mPackages.getMap();
- boolean printedHeader = false;
if (isCheckin) {
- pw.println("vers,1");
- }
- for (int ip=0; ip<pkgMap.size(); ip++) {
- String pkgName = pkgMap.keyAt(ip);
- if (reqPackage != null && !reqPackage.equals(pkgName)) {
- continue;
- }
- SparseArray<PackageState> uids = pkgMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- PackageState state = uids.valueAt(iu);
- final int NPROCS = state.mProcesses.size();
- final int NSRVS = state.mServices.size();
- if (!isCheckin) {
- if (NPROCS > 0 || NSRVS > 0) {
- if (!printedHeader) {
- pw.println("Per-Package Process Stats:");
- printedHeader = true;
- }
- pw.print(" * "); pw.print(pkgName); pw.print(" / ");
- UserHandle.formatUid(pw, uid); pw.println(":");
- }
- }
- for (int iproc=0; iproc<NPROCS; iproc++) {
- ProcessState proc = state.mProcesses.valueAt(iproc);
- if (!isCheckin) {
- pw.print(" Process ");
- pw.print(state.mProcesses.keyAt(iproc));
- pw.print(" (");
- pw.print(proc.mDurationsTableSize);
- pw.print(" entries)");
- pw.println(":");
- dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES, now);
- dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
- ALL_PROC_STATES);
- } else {
- pw.print("pkgproc,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(state.mProcesses.keyAt(iproc));
- dumpAllProcessStateCheckin(pw, proc, now);
- pw.println();
- if (proc.mPssTableSize > 0) {
- pw.print("pkgpss,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(state.mProcesses.keyAt(iproc));
- dumpAllProcessPssCheckin(pw, proc);
- pw.println();
- }
- if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
- pw.print("pkgkills,");
- pw.print(pkgName);
- pw.print(",");
- pw.print(uid);
- pw.print(",");
- pw.print(state.mProcesses.keyAt(iproc));
- pw.print(",");
- pw.print(proc.mNumExcessiveWake);
- pw.print(",");
- pw.print(proc.mNumExcessiveCpu);
- pw.println();
- }
- }
- }
- for (int isvc=0; isvc<NSRVS; isvc++) {
- if (!isCheckin) {
- pw.print(" Service ");
- pw.print(state.mServices.keyAt(isvc));
- pw.println(":");
- ServiceState svc = state.mServices.valueAt(isvc);
- if (svc.mStartedCount != 0) {
- pw.print(" Started op count "); pw.print(svc.mStartedCount);
- pw.println(":");
- dumpSingleTime(pw, " ", svc.mStartedDurations, svc.mStartedState,
- svc.mStartedStartTime, now);
- }
- if (svc.mBoundCount != 0) {
- pw.print(" Bound op count "); pw.print(svc.mBoundCount);
- pw.println(":");
- dumpSingleTime(pw, " ", svc.mBoundDurations, svc.mBoundState,
- svc.mBoundStartTime, now);
- }
- if (svc.mExecCount != 0) {
- pw.print(" Executing op count "); pw.print(svc.mExecCount);
- pw.println(":");
- dumpSingleTime(pw, " ", svc.mExecDurations, svc.mExecState,
- svc.mExecStartTime, now);
- }
- } else {
- String serviceName = state.mServices.keyAt(isvc);
- ServiceState svc = state.mServices.valueAt(isvc);
- dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
- svc, svc.mStartedCount, svc.mStartedDurations, svc.mStartedState,
- svc.mStartedStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, serviceName,
- svc, svc.mBoundCount, svc.mBoundDurations, svc.mBoundState,
- svc.mBoundStartTime, now);
- dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, serviceName,
- svc, svc.mExecCount, svc.mExecDurations, svc.mExecState,
- svc.mExecStartTime, now);
- }
- }
- }
- }
-
- if (!isCheckin) {
- dumpFilteredProcesses(pw, "Processes running while critical mem:", " ",
- new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
- new int[] {ADJ_MEM_FACTOR_CRITICAL},
- new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
- STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
- now, reqPackage);
- dumpFilteredProcesses(pw, "Processes running while low mem:", " ",
- new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
- new int[] {ADJ_MEM_FACTOR_LOW},
- new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
- STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
- now, reqPackage);
- dumpFilteredProcesses(pw, "Processes running while moderate mem:", " ",
- new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
- new int[] {ADJ_MEM_FACTOR_MODERATE},
- new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
- STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
- now, reqPackage);
- dumpFilteredProcesses(pw, "Processes running while normal mem:", " ",
- new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON},
- new int[] {ADJ_MEM_FACTOR_NORMAL},
- new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
- STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS},
- now, reqPackage);
- pw.println();
- pw.println("Run time Stats:");
- dumpSingleTime(pw, " ", mState.mMemFactorDurations, mState.mMemFactor,
- mState.mStartTime, now);
- if (dumpAll) {
- pw.println();
- pw.println("Internal state:");
- pw.print(" Num long arrays: "); pw.println(mState.mLongs.size());
- pw.print(" Next long entry: "); pw.println(mState.mNextLong);
- }
+ mState.dumpCheckin(pw, reqPackage);
} else {
- ArrayMap<String, SparseArray<ProcessState>> procMap = mState.mProcesses.getMap();
- for (int ip=0; ip<procMap.size(); ip++) {
- String procName = procMap.keyAt(ip);
- SparseArray<ProcessState> uids = procMap.valueAt(ip);
- for (int iu=0; iu<uids.size(); iu++) {
- int uid = uids.keyAt(iu);
- ProcessState state = uids.valueAt(iu);
- if (state.mDurationsTableSize > 0) {
- pw.print("proc,");
- pw.print(procName);
- pw.print(",");
- pw.print(uid);
- dumpAllProcessStateCheckin(pw, state, now);
- pw.println();
- }
- if (state.mPssTableSize > 0) {
- pw.print("pss,");
- pw.print(procName);
- pw.print(",");
- pw.print(uid);
- dumpAllProcessPssCheckin(pw, state);
- pw.println();
- }
- if (state.mNumExcessiveWake > 0 || state.mNumExcessiveCpu > 0) {
- pw.print("kills,");
- pw.print(uid);
- pw.print(",");
- pw.print(procName);
- pw.print(",");
- pw.print(state.mNumExcessiveWake);
- pw.print(",");
- pw.print(state.mNumExcessiveCpu);
- pw.println();
- }
- }
- }
- pw.print("total");
- dumpAdjTimesCheckin(pw, ",", mState.mMemFactorDurations, mState.mMemFactor,
- mState.mStartTime, now);
- pw.println();
+ mState.dump(pw, reqPackage, dumpAll);
}
}
}
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 6dae4aa1754c..f799535e6cfd 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -16,8 +16,10 @@
package com.android.server.am;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -25,8 +27,10 @@ import android.os.IBinder;
import android.os.FileUtils;
import android.os.Parcel;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
@@ -93,10 +97,10 @@ public final class UsageStatsService extends IUsageStats.Stub {
static IUsageStats sService;
private Context mContext;
// structure used to maintain statistics since the last checkin.
- final private Map<String, PkgUsageStatsExtended> mStats;
+ final private ArrayMap<String, PkgUsageStatsExtended> mStats;
// Maintains the last time any component was resumed, for all time.
- final private Map<String, Map<String, Long>> mLastResumeTimes;
+ final private ArrayMap<String, ArrayMap<String, Long>> mLastResumeTimes;
// To remove last-resume time stats when a pacakge is removed.
private PackageMonitor mPackageMonitor;
@@ -242,8 +246,8 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
UsageStatsService(String dir) {
- mStats = new HashMap<String, PkgUsageStatsExtended>();
- mLastResumeTimes = new HashMap<String, Map<String, Long>>();
+ mStats = new ArrayMap<String, PkgUsageStatsExtended>();
+ mLastResumeTimes = new ArrayMap<String, ArrayMap<String, Long>>();
mStatsLock = new Object();
mFileLock = new Object();
mDir = new File(dir);
@@ -386,9 +390,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
try {
long lastResumeTime = Long.parseLong(lastResumeTimeStr);
synchronized (mStatsLock) {
- Map<String, Long> lrt = mLastResumeTimes.get(pkg);
+ ArrayMap<String, Long> lrt = mLastResumeTimes.get(pkg);
if (lrt == null) {
- lrt = new HashMap<String, Long>();
+ lrt = new ArrayMap<String, Long>();
mLastResumeTimes.put(pkg, lrt);
}
lrt.put(comp, lastResumeTime);
@@ -591,14 +595,15 @@ public final class UsageStatsService extends IUsageStats.Stub {
/** Filter out stats for any packages which aren't present anymore. */
private void filterHistoryStats() {
synchronized (mStatsLock) {
- // Copy and clear the last resume times map, then copy back stats
- // for all installed packages.
- Map<String, Map<String, Long>> tmpLastResumeTimes =
- new HashMap<String, Map<String, Long>>(mLastResumeTimes);
- mLastResumeTimes.clear();
- for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) {
- if (tmpLastResumeTimes.containsKey(info.packageName)) {
- mLastResumeTimes.put(info.packageName, tmpLastResumeTimes.get(info.packageName));
+ IPackageManager pm = AppGlobals.getPackageManager();
+ for (int i=0; i<mLastResumeTimes.size(); i++) {
+ String pkg = mLastResumeTimes.keyAt(i);
+ try {
+ if (pm.getPackageUid(pkg, 0) < 0) {
+ mLastResumeTimes.removeAt(i);
+ i--;
+ }
+ } catch (RemoteException e) {
}
}
}
@@ -614,13 +619,14 @@ public final class UsageStatsService extends IUsageStats.Stub {
out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
out.startTag(null, "usage-history");
synchronized (mStatsLock) {
- for (Map.Entry<String, Map<String, Long>> pkgEntry : mLastResumeTimes.entrySet()) {
+ for (int i=0; i<mLastResumeTimes.size(); i++) {
out.startTag(null, "pkg");
- out.attribute(null, "name", pkgEntry.getKey());
- for (Map.Entry<String, Long> compEntry : pkgEntry.getValue().entrySet()) {
+ out.attribute(null, "name", mLastResumeTimes.keyAt(i));
+ ArrayMap<String, Long> comp = mLastResumeTimes.valueAt(i);
+ for (int j=0; j<comp.size(); j++) {
out.startTag(null, "comp");
- out.attribute(null, "name", compEntry.getKey());
- out.attribute(null, "lrt", compEntry.getValue().toString());
+ out.attribute(null, "name", comp.keyAt(j));
+ out.attribute(null, "lrt", comp.valueAt(j).toString());
out.endTag(null, "comp");
}
out.endTag(null, "pkg");
@@ -718,9 +724,9 @@ public final class UsageStatsService extends IUsageStats.Stub {
pus.addLaunchCount(mLastResumedComp);
}
- Map<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
+ ArrayMap<String, Long> componentResumeTimes = mLastResumeTimes.get(pkgName);
if (componentResumeTimes == null) {
- componentResumeTimes = new HashMap<String, Long>();
+ componentResumeTimes = new ArrayMap<String, Long>();
mLastResumeTimes.put(pkgName, componentResumeTimes);
}
componentResumeTimes.put(mLastResumedComp, System.currentTimeMillis());
@@ -814,9 +820,8 @@ public final class UsageStatsService extends IUsageStats.Stub {
return null;
}
PkgUsageStats retArr[] = new PkgUsageStats[size];
- int i = 0;
- for (Map.Entry<String, Map<String, Long>> entry : mLastResumeTimes.entrySet()) {
- String pkg = entry.getKey();
+ for (int i=0; i<size; i++) {
+ String pkg = mLastResumeTimes.keyAt(i);
long usageTime = 0;
int launchCount = 0;
@@ -825,8 +830,8 @@ public final class UsageStatsService extends IUsageStats.Stub {
usageTime = pus.mUsageTime;
launchCount = pus.mLaunchCount;
}
- retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime, entry.getValue());
- i++;
+ retArr[i] = new PkgUsageStats(pkg, launchCount, usageTime,
+ mLastResumeTimes.valueAt(i));
}
return retArr;
}
@@ -866,6 +871,11 @@ public final class UsageStatsService extends IUsageStats.Stub {
}
File dFile = new File(mDir, file);
String dateStr = file.substring(FILE_PREFIX.length());
+ if (dateStr.length() > 0 && (dateStr.charAt(0) <= '0' || dateStr.charAt(0) >= '9')) {
+ // If the remainder does not start with a number, it is not a date,
+ // so we should ignore it for purposes here.
+ continue;
+ }
try {
Parcel in = getParcelForFile(dFile);
collectDumpInfoFromParcelFLOCK(in, pw, dateStr, isCompactOutput,