diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 6f75f7f83a29a..5fa874bedf728 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 061bf17455a7e..e0a154cea29ac 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 unpluggables, Parcel in) { diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java index cd718a271a405..3c10480c2c9b9 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 ed9416eaffb06..9c98848b5038d 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 82b2158d2e848..7985df48056e3 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>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>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 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 mPackages = new ProcessMap(); final ProcessMap mProcesses = new ProcessMap(); final long[] mMemFactorDurations = new long[ADJ_COUNT]; @@ -449,26 +563,286 @@ public final class ProcessTracker { final ArrayList mLongs = new ArrayList(); 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> procMap = mProcesses.getMap(); + final int NPROC = procMap.size(); + out.writeInt(NPROC); + for (int ip=0; ip uids = procMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu> pkgMap = mPackages.getMap(); + final int NPKG = pkgMap.size(); + out.writeInt(NPKG); + for (int ip=0; ip uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu= 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 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> pkgMap = mPackages.getMap(); + boolean printedHeader = false; + for (int ip=0; ip uids = pkgMap.valueAt(ip); + for (int iu=0; iu 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 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 collectProcessesLocked(int[] screenStates, int[] memStates, + int[] procStates, long now, String reqPackage) { + ArraySet foundProcs = new ArraySet(); + ArrayMap> pkgMap = mPackages.getMap(); + for (int ip=0; ip procs = pkgMap.valueAt(ip); + for (int iu=0; iu outProcs = new ArrayList(foundProcs.size()); + for (int i=0; i 0) { + outProcs.add(proc); + } + } + Collections.sort(outProcs, new Comparator() { + @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> pkgMap = mPackages.getMap(); + pw.println("vers,1"); + for (int ip=0; ip uids = pkgMap.valueAt(ip); + for (int iu=0; iu 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> procMap = mProcesses.getMap(); + for (int ip=0; ip uids = procMap.valueAt(ip); + for (int iu=0; iu 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 collectProcessesLocked(int[] screenStates, int[] memStates, - int[] procStates, long now, String reqPackage) { - ArraySet foundProcs = new ArraySet(); - ArrayMap> pkgMap = mState.mPackages.getMap(); - for (int ip=0; ip procs = pkgMap.valueAt(ip); - for (int iu=0; iu outProcs = new ArrayList(foundProcs.size()); - for (int i=0; i 0) { - outProcs.add(proc); - } - } - Collections.sort(outProcs, new Comparator() { - @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 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 procs = collectProcessesLocked(screenStates, memStates, + ArrayList 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] []"); + pw.println(" [--reset] [--write] [-h] []"); 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(" : 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> pkgMap = mState.mPackages.getMap(); - boolean printedHeader = false; if (isCheckin) { - pw.println("vers,1"); - } - for (int ip=0; ip uids = pkgMap.valueAt(ip); - for (int iu=0; iu 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 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> procMap = mState.mProcesses.getMap(); - for (int ip=0; ip uids = procMap.valueAt(ip); - for (int iu=0; iu 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 6dae4aa1754cf..f799535e6cfd4 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 mStats; + final private ArrayMap mStats; // Maintains the last time any component was resumed, for all time. - final private Map> mLastResumeTimes; + final private ArrayMap> 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(); - mLastResumeTimes = new HashMap>(); + mStats = new ArrayMap(); + mLastResumeTimes = new ArrayMap>(); 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 lrt = mLastResumeTimes.get(pkg); + ArrayMap lrt = mLastResumeTimes.get(pkg); if (lrt == null) { - lrt = new HashMap(); + lrt = new ArrayMap(); 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> tmpLastResumeTimes = - new HashMap>(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> pkgEntry : mLastResumeTimes.entrySet()) { + for (int i=0; i compEntry : pkgEntry.getValue().entrySet()) { + out.attribute(null, "name", mLastResumeTimes.keyAt(i)); + ArrayMap comp = mLastResumeTimes.valueAt(i); + for (int j=0; j componentResumeTimes = mLastResumeTimes.get(pkgName); + ArrayMap componentResumeTimes = mLastResumeTimes.get(pkgName); if (componentResumeTimes == null) { - componentResumeTimes = new HashMap(); + componentResumeTimes = new ArrayMap(); 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> entry : mLastResumeTimes.entrySet()) { - String pkg = entry.getKey(); + for (int i=0; i 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,