Merge "Improve how procstats history/checkins are managed."
This commit is contained in:
committed by
Android (Google) Code Review
commit
db3c19fd40
@@ -1822,7 +1822,6 @@ public final class ActivityManagerService extends ActivityManagerNative
|
||||
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"));
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.os.Parcel;
|
||||
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 @@ public final class ProcessTracker {
|
||||
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
}
|
||||
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
}
|
||||
}
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
|
||||
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 @@ public final class ProcessTracker {
|
||||
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
// 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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
}
|
||||
}
|
||||
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 final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ 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] [--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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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 @@ public final class ProcessTracker {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user