Merge "Improve how procstats history/checkins are managed."
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6425693..0bd0d8e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1822,7 +1822,6 @@
mBatteryStatsService.getActiveStatistics().setCallback(this);
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"));
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index fbdbdd9..488582d 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -22,6 +22,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.format.DateFormat;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
@@ -137,19 +138,21 @@
static final String CSV_SEP = "\t";
+ static final int MAX_HISTORIC_STATES = 4; // Maximum number of historic states we will keep.
+ static final String STATE_FILE_PREFIX = "state-"; // Prefix to use for state filenames.
static long WRITE_PERIOD = 30*60*1000; // Write file every 30 minutes or so.
+ static long COMMIT_PERIOD = 24*60*60*1000; // Commit current stats every day.
final File mBaseDir;
- final AtomicFile mFile;
- final State mState = new State();
- long mLastWriteTime;
+ State mState;
+ boolean mCommitPending;
boolean mShuttingDown;
- final Object mPendingWriteLock = new Object();
- Parcel mPendingWrite;
final ReentrantLock mWriteLock = new ReentrantLock();
public static final class ProcessState {
+ static final int[] BAD_TABLE = new int[0];
+
final State mState;
final ProcessState mCommonProcess;
final String mPackage;
@@ -257,10 +260,14 @@
out.writeInt(mMultiPackage ? 1 : 0);
out.writeInt(mDurationsTableSize);
for (int i=0; i<mDurationsTableSize; i++) {
+ if (DEBUG) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": "
+ + State.printLongOffset(mDurationsTable[i]));
out.writeInt(mDurationsTable[i]);
}
out.writeInt(mPssTableSize);
for (int i=0; i<mPssTableSize; i++) {
+ if (DEBUG) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": "
+ + State.printLongOffset(mPssTable[i]));
out.writeInt(mPssTable[i]);
}
out.writeInt(mNumExcessiveWake);
@@ -268,17 +275,22 @@
}
private int[] readTable(Parcel in, String what) {
- int size = in.readInt();
+ final int size = in.readInt();
if (size < 0) {
Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size);
+ return BAD_TABLE;
+ }
+ if (size == 0) {
return null;
}
- int[] table = new int[size];
+ final int[] table = new int[size];
for (int i=0; i<size; i++) {
table[i] = in.readInt();
+ if (DEBUG) Slog.i(TAG, "Reading in " + mName + " table #" + i + ": "
+ + State.printLongOffset(table[i]));
if (!mState.validateLongOffset(table[i])) {
- Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: 0x"
- + Integer.toHexString(table[i]));
+ Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: "
+ + State.printLongOffset(table[i]));
return null;
}
}
@@ -290,16 +302,18 @@
if (fully) {
mMultiPackage = multiPackage;
}
+ if (DEBUG) Slog.d(TAG, "Reading durations table...");
mDurationsTable = readTable(in, "durations");
- if (mDurationsTable == null) {
+ if (mDurationsTable == BAD_TABLE) {
return false;
}
- mDurationsTableSize = mDurationsTable.length;
+ mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0;
+ if (DEBUG) Slog.d(TAG, "Reading pss table...");
mPssTable = readTable(in, "pss");
- if (mPssTable == null) {
+ if (mPssTable == BAD_TABLE) {
return false;
}
- mPssTableSize = mPssTable.length;
+ mPssTableSize = mPssTable != null ? mPssTable.length : 0;
mNumExcessiveWake = in.readInt();
mNumExcessiveCpu = in.readInt();
return true;
@@ -334,19 +348,21 @@
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;
+ if (dur > 0) {
+ 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;
}
- long[] longs = mState.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
- longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur;
}
mStartTime = now;
}
@@ -570,12 +586,20 @@
static final class State {
// Current version of the parcel format.
- public static final int PARCEL_VERSION = 1;
+ private static final int PARCEL_VERSION = 3;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535453;
- long mTimePeriodStart;
- long mTimePeriodEnd;
+ final File mBaseDir;
+ final ProcessTracker mProcessTracker;
+ AtomicFile mFile;
+ String mReadError;
+
+ long mTimePeriodStartClock;
+ String mTimePeriodStartClockStr;
+ long mTimePeriodStartRealtime;
+ long mTimePeriodEndRealtime;
+ boolean mRunning;
final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
@@ -590,19 +614,36 @@
int[] mAddLongTable;
int mAddLongTableSize;
- State() {
+ final Object mPendingWriteLock = new Object();
+ Parcel mPendingWrite;
+ long mLastWriteTime;
+
+ State(File baseDir, ProcessTracker tracker) {
+ mBaseDir = baseDir;
reset();
+ mProcessTracker = tracker;
+ }
+
+ State(String file) {
+ mBaseDir = null;
+ reset();
+ mFile = new AtomicFile(new File(file));
+ mProcessTracker = null;
+ readLocked();
}
void reset() {
+ if (DEBUG && mFile != null) Slog.d(TAG, "Resetting state of " + mFile.getBaseFile());
resetCommon();
mPackages.getMap().clear();
mProcesses.getMap().clear();
mMemFactor = STATE_NOTHING;
mStartTime = 0;
+ if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
}
void resetSafely() {
+ if (DEBUG && mFile != null) Slog.d(TAG, "Safely resetting state of " + mFile.getBaseFile());
resetCommon();
long now = SystemClock.uptimeMillis();
ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
@@ -632,10 +673,14 @@
}
}
mStartTime = SystemClock.uptimeMillis();
+ if (DEBUG && mFile != null) Slog.d(TAG, "State reset; now " + mFile.getBaseFile());
}
private void resetCommon() {
- mTimePeriodStart = mTimePeriodEnd = System.currentTimeMillis();
+ mLastWriteTime = SystemClock.uptimeMillis();
+ mTimePeriodStartClock = System.currentTimeMillis();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
mLongs.clear();
mLongs.add(new long[LONGS_SIZE]);
mNextLong = 0;
@@ -644,6 +689,150 @@
mStartTime = 0;
}
+ private void buildTimePeriodStartClockStr() {
+ mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+ mTimePeriodStartClock).toString();
+ if (mBaseDir != null) {
+ mFile = new AtomicFile(new File(mBaseDir,
+ STATE_FILE_PREFIX + mTimePeriodStartClockStr));
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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();
+
+ readFromParcel(in);
+ if (mReadError != null) {
+ Slog.w(TAG, "Ignoring existing stats; " + mReadError);
+ if (DEBUG) {
+ ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+ final int NPROC = procMap.size();
+ for (int ip=0; ip<NPROC; ip++) {
+ Slog.w(TAG, "Process: " + procMap.keyAt(ip));
+ SparseArray<ProcessState> uids = procMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu));
+ }
+ }
+ ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
+ final int NPKG = pkgMap.size();
+ for (int ip=0; ip<NPKG; ip++) {
+ Slog.w(TAG, "Package: " + pkgMap.keyAt(ip));
+ SparseArray<PackageState> uids = pkgMap.valueAt(ip);
+ final int NUID = uids.size();
+ for (int iu=0; iu<NUID; iu++) {
+ Slog.w(TAG, " Uid: " + uids.keyAt(iu));
+ PackageState pkgState = uids.valueAt(iu);
+ final int NPROCS = pkgState.mProcesses.size();
+ for (int iproc=0; iproc<NPROCS; iproc++) {
+ Slog.w(TAG, " Process " + pkgState.mProcesses.keyAt(iproc)
+ + ": " + pkgState.mProcesses.valueAt(iproc));
+ }
+ final int NSRVS = pkgState.mServices.size();
+ for (int isvc=0; isvc<NSRVS; isvc++) {
+ Slog.w(TAG, " Service " + pkgState.mServices.keyAt(isvc)
+ + ": " + pkgState.mServices.valueAt(isvc));
+ }
+ }
+ }
+ }
+ }
+ } catch (Throwable e) {
+ mReadError = "error reading: " + e;
+ Slog.e(TAG, "Error reading process statistics", e);
+ }
+ }
+
+ private void writeStateLocked(boolean sync, final boolean commit) {
+ synchronized (mPendingWriteLock) {
+ long now = SystemClock.uptimeMillis();
+ mPendingWrite = Parcel.obtain();
+ mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+ 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() {
+ performWriteState(commit);
+ }
+ });
+ return;
+ }
+ }
+
+ performWriteState(commit);
+ }
+
+ void performWriteState(boolean commit) {
+ if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile()
+ + " commit=" + commit);
+ Parcel data;
+ synchronized (mPendingWriteLock) {
+ data = mPendingWrite;
+ if (data == null) {
+ return;
+ }
+ mPendingWrite = null;
+ if (mProcessTracker != null) {
+ mProcessTracker.mWriteLock.lock();
+ }
+ }
+
+ FileOutputStream stream = null;
+ try {
+ stream = mFile.startWrite();
+ stream.write(data.marshall());
+ stream.flush();
+ mFile.finishWrite(stream);
+ if (DEBUG) Slog.d(TAG, "Write completed successfully!");
+ } catch (IOException e) {
+ Slog.w(TAG, "Error writing process statistics", e);
+ mFile.failWrite(stream);
+ } finally {
+ data.recycle();
+ if (mProcessTracker != null) {
+ mProcessTracker.trimHistoricStatesWriteLocked();
+ mProcessTracker.mWriteLock.unlock();
+ }
+ }
+
+ if (commit) {
+ resetSafely();
+ }
+ }
+
void writeToParcel(Parcel out) {
long now = SystemClock.uptimeMillis();
out.writeInt(MAGIC);
@@ -653,8 +842,9 @@
out.writeInt(PSS_COUNT);
out.writeInt(LONGS_SIZE);
- out.writeLong(mTimePeriodStart);
- out.writeLong(mTimePeriodEnd);
+ out.writeLong(mTimePeriodStartClock);
+ out.writeLong(mTimePeriodStartRealtime);
+ out.writeLong(mTimePeriodEndRealtime);
out.writeInt(mLongs.size());
out.writeInt(mNextLong);
@@ -664,6 +854,7 @@
long[] lastLongs = mLongs.get(mLongs.size()-1);
for (int i=0; i<mNextLong; i++) {
out.writeLong(lastLongs[i]);
+ if (DEBUG) Slog.d(TAG, "Writing last long #" + i + ": " + lastLongs[i]);
}
if (mMemFactor != STATE_NOTHING) {
@@ -726,13 +917,13 @@
private boolean readCheckedInt(Parcel in, int val, String what) {
int got;
if ((got=in.readInt()) != val) {
- Slog.w(TAG, "Ignoring existing stats; bad " + ": " + got);
+ mReadError = "bad " + ": " + got;
return false;
}
return true;
}
- void readFromParcel(Parcel in) {
+ private void readFromParcel(Parcel in) {
final boolean hadData = mPackages.getMap().size() > 0
|| mProcesses.getMap().size() > 0;
if (hadData) {
@@ -758,11 +949,14 @@
return;
}
- mTimePeriodStart = in.readLong();
- mTimePeriodEnd = in.readLong();
+ mTimePeriodStartClock = in.readLong();
+ buildTimePeriodStartClockStr();
+ mTimePeriodStartRealtime = in.readLong();
+ mTimePeriodEndRealtime = in.readLong();
- int NLONGS = in.readInt();
- int NEXTLONG = in.readInt();
+ final int NLONGS = in.readInt();
+ final int NEXTLONG = in.readInt();
+ mLongs.clear();
for (int i=0; i<(NLONGS-1); i++) {
while (i >= mLongs.size()) {
mLongs.add(new long[LONGS_SIZE]);
@@ -773,6 +967,7 @@
mNextLong = NEXTLONG;
for (int i=0; i<NEXTLONG; i++) {
longs[i] = in.readLong();
+ if (DEBUG) Slog.d(TAG, "Reading last long #" + i + ": " + longs[i]);
}
mLongs.add(longs);
@@ -780,31 +975,31 @@
int NPROC = in.readInt();
if (NPROC < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad process count: " + NPROC);
+ mReadError = "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");
+ mReadError = "bad process name";
return;
}
int NUID = in.readInt();
if (NUID < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad uid count: " + NUID);
+ mReadError = "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);
+ mReadError = "bad uid: " + uid;
return;
}
String pkgName = in.readString();
if (pkgName == null) {
- Slog.w(TAG, "Ignoring existing stats; bad process package name");
+ mReadError = "bad process package name";
return;
}
ProcessState proc = hadData ? mProcesses.get(procName, uid) : null;
@@ -827,57 +1022,57 @@
int NPKG = in.readInt();
if (NPKG < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad package count: " + NPKG);
+ mReadError = "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");
+ mReadError = "bad package name";
return;
}
int NUID = in.readInt();
if (NUID < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad uid count: " + NUID);
+ mReadError = "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);
+ mReadError = "bad uid: " + uid;
return;
}
PackageState pkgState = new PackageState(uid);
mPackages.put(pkgName, uid, pkgState);
int NPROCS = in.readInt();
if (NPROCS < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad package process count: " + NPROCS);
+ mReadError = "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");
+ mReadError = "bad package process name";
return;
}
int hasProc = in.readInt();
if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid
+ " process " + procName + " hasProc=" + hasProc);
+ ProcessState commonProc = mProcesses.get(procName, uid);
+ if (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid
+ + ": " + commonProc);
+ if (commonProc == null) {
+ mReadError = "no common proc: " + procName;
+ return;
+ }
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 (DEBUG) Slog.d(TAG, "Got common proc " + procName + " " + uid
- + ": " + commonProc);
- if (commonProc == null) {
- Slog.w(TAG, "Ignoring existing stats; no common proc: " + procName);
- return;
- }
ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null;
if (proc != null) {
if (!proc.readFromParcel(in, false)) {
@@ -892,18 +1087,22 @@
if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ procName + " " + uid + " " + proc);
pkgState.mProcesses.put(procName, proc);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: "
+ + procName + " " + uid + " " + commonProc);
+ pkgState.mProcesses.put(procName, commonProc);
}
}
int NSRVS = in.readInt();
if (NSRVS < 0) {
- Slog.w(TAG, "Ignoring existing stats; bad package service count: " + NSRVS);
+ mReadError = "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");
+ mReadError = "bad package service name";
return;
}
ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null;
@@ -966,9 +1165,19 @@
if (idx >= LONGS_SIZE) {
return false;
}
+ if (DEBUG) Slog.d(TAG, "Validated long " + printLongOffset(off)
+ + ": " + getLong(off, 0));
return true;
}
+ static String printLongOffset(int off) {
+ StringBuilder sb = new StringBuilder(16);
+ sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+ sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK);
+ sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK);
+ return sb.toString();
+ }
+
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;
@@ -1032,7 +1241,7 @@
// The original package it was created for now needs to point
// to its own copy.
long now = SystemClock.uptimeMillis();
- pkgState.mProcesses.put(commonProc.mPackage, commonProc.clone(
+ pkgState.mProcesses.put(commonProc.mName, commonProc.clone(
commonProc.mPackage, now));
ps = new ProcessState(commonProc, packageName, uid, processName, now);
}
@@ -1136,11 +1345,22 @@
pw.println();
pw.println("Run time Stats:");
dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now);
+ pw.println();
+ pw.print(" Start time: ");
+ pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
+ pw.println();
+ pw.print(" Total elapsed time: ");
+ TimeUtils.formatDuration(
+ (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
+ - mTimePeriodStartRealtime, pw);
+ pw.println();
if (dumpAll) {
pw.println();
pw.println("Internal state:");
+ pw.print(" mFile="); pw.println(mFile.getBaseFile());
pw.print(" Num long arrays: "); pw.println(mLongs.size());
pw.print(" Next long entry: "); pw.println(mNextLong);
+ pw.print(" mRunning="); pw.println(mRunning);
}
}
@@ -1194,10 +1414,29 @@
return outProcs;
}
+ String collapseString(String pkgName, String itemName) {
+ if (itemName.startsWith(pkgName)) {
+ final int ITEMLEN = itemName.length();
+ final int PKGLEN = pkgName.length();
+ if (ITEMLEN == PKGLEN) {
+ return "";
+ } else if (ITEMLEN >= PKGLEN) {
+ if (itemName.charAt(PKGLEN) == '.') {
+ return itemName.substring(PKGLEN);
+ }
+ }
+ }
+ return itemName;
+ }
+
void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
final long now = SystemClock.uptimeMillis();
ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
pw.println("vers,1");
+ pw.print("period,"); pw.print(mTimePeriodStartClockStr);
+ pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
+ pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+ pw.println();
for (int ip=0; ip<pkgMap.size(); ip++) {
String pkgName = pkgMap.keyAt(ip);
if (reqPackage != null && !reqPackage.equals(pkgName)) {
@@ -1216,7 +1455,7 @@
pw.print(",");
pw.print(uid);
pw.print(",");
- pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
dumpAllProcessStateCheckin(pw, proc, now);
pw.println();
if (proc.mPssTableSize > 0) {
@@ -1225,7 +1464,7 @@
pw.print(",");
pw.print(uid);
pw.print(",");
- pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
dumpAllProcessPssCheckin(pw, proc);
pw.println();
}
@@ -1235,7 +1474,7 @@
pw.print(",");
pw.print(uid);
pw.print(",");
- pw.print(pkgState.mProcesses.keyAt(iproc));
+ pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc)));
pw.print(",");
pw.print(proc.mNumExcessiveWake);
pw.print(",");
@@ -1244,7 +1483,8 @@
}
}
for (int isvc=0; isvc<NSRVS; isvc++) {
- String serviceName = pkgState.mServices.keyAt(isvc);
+ String serviceName = collapseString(pkgName,
+ pkgState.mServices.keyAt(isvc));
ServiceState svc = pkgState.mServices.valueAt(isvc);
dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, serviceName,
svc, svc.mStartedCount, svc.mStartedDurations, svc.mStartedState,
@@ -1305,7 +1545,8 @@
public ProcessTracker(File file) {
mBaseDir = file;
mBaseDir.mkdirs();
- mFile = new AtomicFile(new File(file, "current.bin"));
+ mState = new State(mBaseDir, this);
+ mState.mRunning = true;
}
public ProcessState getProcessStateLocked(String packageName, int uid, String processName) {
@@ -1359,48 +1600,18 @@
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);
- }
-
+ mState.readLocked();
}
public boolean shouldWriteNowLocked(long now) {
- return now > (mLastWriteTime+WRITE_PERIOD);
+ if (now > (mState.mLastWriteTime+WRITE_PERIOD)) {
+ if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
+ mCommitPending = true;
+ }
+ return true;
+ }
+ return false;
}
public void shutdownLocked() {
@@ -1421,50 +1632,39 @@
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();
+ boolean commitPending = mCommitPending;
+ mCommitPending = false;
+ mState.writeStateLocked(sync, commitPending);
}
- void commitWriteState() {
- Parcel data;
- synchronized (mPendingWriteLock) {
- data = mPendingWrite;
- if (data == null) {
- return;
- }
- mPendingWrite = null;
- mWriteLock.lock();
+ private ArrayList<String> getCommittedFiles(int minNum) {
+ File[] files = mBaseDir.listFiles();
+ if (files == null || files.length <= minNum) {
+ return null;
}
+ ArrayList<String> filesArray = new ArrayList<String>(files.length);
+ String currentFile = mState.mFile.getBaseFile().toString();
+ if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile);
+ for (int i=0; i<files.length; i++) {
+ File file = files[i];
+ if (DEBUG) Slog.d(TAG, "Collecting: " + file);
+ if (!file.toString().equals(currentFile)) {
+ filesArray.add(files[i].toString());
+ }
+ }
+ Collections.sort(filesArray);
+ return filesArray;
+ }
- 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();
+ public void trimHistoricStatesWriteLocked() {
+ ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES);
+ if (filesArray == null) {
+ return;
+ }
+ while (filesArray.size() > MAX_HISTORIC_STATES) {
+ String file = filesArray.remove(0);
+ Slog.i(TAG, "Pruning old procstats: " + file);
+ (new File(file)).delete();
}
}
@@ -1970,15 +2170,17 @@
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] [--write] [-h] [<package.name>]");
- pw.println(" --checkin: format output for a checkin report.");
+ pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
+ pw.println(" [--include-committed] [--commit] [--write] [-h] [<package.name>]");
+ pw.println(" --checkin: perform a checkin: print and delete old committed states.");
+ pw.println(" --c: print only state in checkin format.");
pw.println(" --csv: output data suitable for putting in a spreadsheet.");
pw.println(" --csv-screen: on, off.");
pw.println(" --csv-mem: norm, mod, low, crit.");
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(" --include-committed: also dump any old committed states.");
+ pw.println(" --commit: commit current stats to disk and reset to start new stats.");
pw.println(" --write: write current in-memory stats to disk.");
pw.println(" --read: replace current stats with last-written stats.");
pw.println(" -a: print everything.");
@@ -1990,7 +2192,9 @@
final long now = SystemClock.uptimeMillis();
boolean isCheckin = false;
+ boolean isCompact = false;
boolean isCsv = false;
+ boolean includeCommitted = false;
boolean dumpAll = false;
String reqPackage = null;
boolean csvSepScreenStats = false;
@@ -2007,6 +2211,8 @@
String arg = args[i];
if ("--checkin".equals(arg)) {
isCheckin = true;
+ } else if ("-c".equals(arg)) {
+ isCompact = true;
} else if ("--csv".equals(arg)) {
isCsv = true;
} else if ("--csv-screen".equals(arg)) {
@@ -2058,9 +2264,11 @@
return;
}
csvSepProcStats = sep[0];
- } else if ("--reset".equals(arg)) {
- mState.resetSafely();
- pw.println("Process stats reset.");
+ } else if ("--include-committed".equals(arg)) {
+ includeCommitted = true;
+ } else if ("--commit".equals(arg)) {
+ mState.writeStateLocked(true, true);
+ pw.println("Process stats committed.");
return;
} else if ("--write".equals(arg)) {
writeStateSyncLocked();
@@ -2075,6 +2283,7 @@
return;
} else if ("-a".equals(arg)) {
dumpAll = true;
+ includeCommitted = true;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
dumpHelp(pw);
@@ -2085,6 +2294,9 @@
IPackageManager pm = AppGlobals.getPackageManager();
if (pm.getPackageUid(arg, UserHandle.getCallingUserId()) >= 0) {
reqPackage = arg;
+ // We will automatically include all committed state,
+ // since we are going to end up with much less printed.
+ includeCommitted = true;
}
} catch (RemoteException e) {
}
@@ -2141,10 +2353,48 @@
return;
}
- if (isCheckin) {
- mState.dumpCheckinLocked(pw, reqPackage);
- } else {
- mState.dumpLocked(pw, reqPackage, dumpAll);
+ boolean sepNeeded = false;
+ if (includeCommitted || isCheckin) {
+ ArrayList<String> files = getCommittedFiles(0);
+ if (files != null) {
+ for (int i=0; i<files.size(); i++) {
+ if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
+ try {
+ State state = new State(files.get(i));
+ if (isCheckin || isCompact) {
+ state.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ if (sepNeeded) {
+ pw.println();
+ } else {
+ sepNeeded = true;
+ }
+ pw.print("COMMITTED STATS FROM ");
+ pw.print(state.mTimePeriodStartClockStr);
+ pw.println(":");
+ state.dumpLocked(pw, reqPackage, dumpAll);
+ }
+ if (isCheckin) {
+ state.mFile.delete();
+ }
+ } catch (Throwable e) {
+ pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
+ e.printStackTrace(pw);
+ }
+ if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
+ }
+ }
+ }
+ if (!isCheckin) {
+ if (isCompact) {
+ mState.dumpCheckinLocked(pw, reqPackage);
+ } else {
+ if (sepNeeded) {
+ pw.println();
+ pw.println("CURRENT STATS:");
+ }
+ mState.dumpLocked(pw, reqPackage, dumpAll);
+ }
}
}
}