diff options
| author | 2013-07-01 19:10:06 -0700 | |
|---|---|---|
| committer | 2013-07-02 11:21:04 -0700 | |
| commit | 2286cdc0cab77e61b75e6fe9a45b91f6e57cd46d (patch) | |
| tree | dd2f8bcc5adf79881e9ce27010b94b711eb53077 | |
| parent | eaccd577b99b5c3e58906dd1d12c4044eb7e6d3f (diff) | |
Misc memory stuff.
- New Activity.reportFullyDrawn() method that applicatins can call
when they know they are fully drawn, allowing us to have better
app launch time info. This data is also included in usage stats.
- Added total and free memory data "dumpsys meminfo".
- Tuned the moderate memory levels to be more aggressive about
considering the device getting low on RAM, and thus starting
to prune RAM from processes.
- Fixed issues in processstats when reading old data as well as
resetting and other various fixes.
Change-Id: I20efe7451afb4edfa1aeec448328ba601c24d869
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 24 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 19 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 3 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 44 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityRecord.java | 110 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityStack.java | 20 | ||||
| -rw-r--r-- | services/java/com/android/server/am/EventLogTags.logtags | 3 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessList.java | 27 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 13 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ProcessTracker.java | 144 | ||||
| -rw-r--r-- | services/java/com/android/server/am/UsageStatsService.java | 201 |
12 files changed, 471 insertions, 138 deletions
diff --git a/api/current.txt b/api/current.txt index d40832c877c7..a47b8c378341 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2821,6 +2821,7 @@ package android.app { method public void recreate(); method public void registerForContextMenu(android.view.View); method public final deprecated void removeDialog(int); + method public void reportFullyDrawn(); method public final boolean requestWindowFeature(int); method public final void runOnUiThread(java.lang.Runnable); method public void setContentView(int); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index e87d8050f286..d1efd0d18b26 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -686,6 +686,7 @@ public class Activity extends ContextThemeWrapper boolean mFinished; boolean mStartedActivity; private boolean mDestroyed; + private boolean mDoReportFullyDrawn = true; /** true if the activity is going through a transient pause */ /*package*/ boolean mTemporaryPause = false; /** true if the activity is being destroyed in order to recreate it with a new configuration */ @@ -1449,6 +1450,27 @@ public class Activity extends ContextThemeWrapper } /** + * Report to the system that your app is now fully drawn. This is only used + * to help instrument app launch times, so that the app can report when it is + * fully in a usable state; without this, all the system can determine is when + * its window is first drawn and displayed. To participate in app launch time + * measurement, you should always call this method after first launch (when + * {@link #onCreate(android.os.Bundle)} is called) at the point where you have + * entirely drawn your UI and populated with all of the significant data. You + * can safely call this method any time after first launch as well, in which case + * it will simply be ignored. + */ + public void reportFullyDrawn() { + if (mDoReportFullyDrawn) { + mDoReportFullyDrawn = false; + try { + ActivityManagerNative.getDefault().reportActivityFullyDrawn(mToken); + } catch (RemoteException e) { + } + } + } + + /** * Called by the system when the device configuration changes while your * activity is running. Note that this will <em>only</em> be called if * you have selected configurations you would like to handle with the @@ -5252,6 +5274,7 @@ public class Activity extends ContextThemeWrapper } final void performPause() { + mDoReportFullyDrawn = false; mFragments.dispatchPause(); mCalled = false; onPause(); @@ -5271,6 +5294,7 @@ public class Activity extends ContextThemeWrapper } final void performStop() { + mDoReportFullyDrawn = false; if (mLoadersStarted) { mLoadersStarted = false; if (mLoaderManager != null) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b966d6d3a3be..a23611ec6e62 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1949,6 +1949,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + reportActivityFullyDrawn(token); + reply.writeNoException(); + return true; + } + } return super.onTransact(code, data, reply, flags); @@ -4463,5 +4471,16 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } + public void reportActivityFullyDrawn(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 39f4cfa75746..3793c7338838 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -392,6 +392,8 @@ public interface IActivityManager extends IInterface { public void hang(IBinder who, boolean allowRestart) throws RemoteException; + public void reportActivityFullyDrawn(IBinder token) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -669,4 +671,5 @@ public interface IActivityManager extends IInterface { int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171; int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172; int CONVERT_TO_OPAQUE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173; + int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 72be39ba7c51..64256938be15 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -36,6 +36,7 @@ import com.android.internal.os.ProcessStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.MemInfoReader; import com.android.server.AppOpsService; import com.android.server.AttributeCache; import com.android.server.IntentResolver; @@ -2989,6 +2990,17 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public void reportActivityFullyDrawn(IBinder token) { + synchronized (this) { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + r.reportFullyDrawnLocked(); + } + } + + @Override public void setRequestedOrientation(IBinder token, int requestedOrientation) { synchronized (this) { ActivityRecord r = ActivityRecord.isInStackLocked(token); @@ -11100,6 +11112,7 @@ public final class ActivityManagerService extends ActivityManagerNative new ArrayList[DUMP_MEM_OOM_LABEL.length]; long totalPss = 0; + long cachedPss = 0; Debug.MemoryInfo mi = null; for (int i = procs.size() - 1 ; i >= 0 ; i--) { @@ -11108,7 +11121,7 @@ public final class ActivityManagerService extends ActivityManagerNative int oomAdj; synchronized (this) { thread = r.thread; - oomAdj = r.setAdj; + oomAdj = r.getSetAdjWithServices(); } if (thread != null) { if (!isCheckinRequest && dumpDetails) { @@ -11139,7 +11152,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long myTotalPss = mi.getTotalPss(); synchronized (this) { - if (r.thread != null && oomAdj == r.setAdj) { + if (r.thread != null && oomAdj == r.getSetAdjWithServices()) { // Record this for posterity if the process has been stable. r.baseProcessTracker.addPss(myTotalPss, true); } @@ -11160,6 +11173,10 @@ public final class ActivityManagerService extends ActivityManagerNative otherPss -= mem; } + if (oomAdj >= ProcessList.CACHED_APP_MIN_ADJ) { + cachedPss += myTotalPss; + } + for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex] || oomIndex == (oomPss.length-1)) { @@ -11274,7 +11291,14 @@ public final class ActivityManagerService extends ActivityManagerNative dumpMemItems(out, " ", catMems, true); } pw.println(); - pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB"); + if (!brief) { + MemInfoReader memInfo = new MemInfoReader(); + memInfo.readMemInfo(); + pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024); pw.println(" kB"); + pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024) + + (memInfo.getFreeSize()/1024)); pw.println(" kB"); + } + pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB"); if (!brief) { final int[] SINGLE_LONG_FORMAT = new int[] { Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG @@ -11295,10 +11319,12 @@ public final class ActivityManagerService extends ActivityManagerNative Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile", SINGLE_LONG_FORMAT, null, longOut, null); long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024; - pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared "); - pw.print(shared); pw.println(" kB"); - pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); - pw.print(voltile); pw.println(" kB volatile"); + if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) { + pw.print(" KSM: "); pw.print(sharing); pw.print(" kB saved from shared "); + pw.print(shared); pw.println(" kB"); + pw.print(" "); pw.print(unshared); pw.print(" kB unshared; "); + pw.print(voltile); pw.println(" kB volatile"); + } } } } @@ -13469,6 +13495,7 @@ public final class ActivityManagerService extends ActivityManagerNative // infinite recursion. app.adjSeq = mAdjSeq; app.curRawAdj = app.nonStoppingAdj = adj; + app.hasStartedServices = false; if (mBackupTarget != null && app == mBackupTarget.app) { // If possible we want to avoid killing apps while they're being backed up @@ -13489,6 +13516,7 @@ public final class ActivityManagerService extends ActivityManagerNative while (jt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) { ServiceRecord s = jt.next(); if (s.startRequested) { + app.hasStartedServices = true; if (app.hasShownUi && app != mHomeProcess) { // If this process has shown some UI, let it immediately // go to the LRU list because it may be pretty heavy with @@ -14333,7 +14361,7 @@ public final class ActivityManagerService extends ActivityManagerNative emptyProcessLimit = 1; cachedProcessLimit = 0; } else { - emptyProcessLimit = (mProcessLimit*2)/3; + emptyProcessLimit = ProcessList.computeEmptyProcessLimit(mProcessLimit); cachedProcessLimit = mProcessLimit - emptyProcessLimit; } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 973b9aad975a..83016df34b73 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -16,6 +16,7 @@ package com.android.server.am; +import android.os.Trace; import com.android.internal.app.ResolverActivity; import com.android.server.AttributeCache; import com.android.server.am.ActivityStack.ActivityState; @@ -92,7 +93,8 @@ final class ActivityRecord { int windowFlags; // custom window flags for preview window. TaskRecord task; // the task this is in. ThumbnailHolder thumbHolder; // where our thumbnails should go. - long launchTime; // when we starting launching this activity + long displayStartTime; // when we started launching this activity + long fullyDrawnStartTime; // when we started launching this activity long startTime; // last time this activity was started long lastVisibleTime; // last time this activity became visible long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity @@ -247,10 +249,10 @@ final class ActivityRecord { pw.print(" desc="); pw.print(thumbHolder.lastDescription); } pw.println(); - if (launchTime != 0 || startTime != 0) { - pw.print(prefix); pw.print("launchTime="); - if (launchTime == 0) pw.print("0"); - else TimeUtils.formatDuration(launchTime, now, pw); + if (displayStartTime != 0 || startTime != 0) { + pw.print(prefix); pw.print("displayStartTime="); + if (displayStartTime == 0) pw.print("0"); + else TimeUtils.formatDuration(displayStartTime, now, pw); pw.print(" startTime="); if (startTime == 0) pw.print("0"); else TimeUtils.formatDuration(startTime, now, pw); @@ -807,37 +809,77 @@ final class ActivityRecord { } } + public void reportFullyDrawnLocked() { + final long curTime = SystemClock.uptimeMillis(); + if (displayStartTime != 0) { + reportLaunchTimeLocked(curTime); + } + if (fullyDrawnStartTime != 0) { + final ActivityStack stack = task.stack; + final long thisTime = curTime - fullyDrawnStartTime; + final long totalTime = stack.mFullyDrawnStartTime != 0 + ? (curTime - stack.mFullyDrawnStartTime) : thisTime; + if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { + Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, "fully-drawn", (int)totalTime); + EventLog.writeEvent(EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME, + userId, System.identityHashCode(this), shortComponentName, + thisTime, totalTime); + StringBuilder sb = service.mStringBuilder; + sb.setLength(0); + sb.append("Fully drawn "); + sb.append(shortComponentName); + sb.append(": "); + TimeUtils.formatDuration(thisTime, sb); + if (thisTime != totalTime) { + sb.append(" (total "); + TimeUtils.formatDuration(totalTime, sb); + sb.append(")"); + } + Log.i(ActivityManagerService.TAG, sb.toString()); + } + if (totalTime > 0) { + service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime); + } + fullyDrawnStartTime = 0; + stack.mFullyDrawnStartTime = 0; + } + } + + private void reportLaunchTimeLocked(final long curTime) { + final ActivityStack stack = task.stack; + final long thisTime = curTime - displayStartTime; + final long totalTime = stack.mLaunchStartTime != 0 + ? (curTime - stack.mLaunchStartTime) : thisTime; + if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { + Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launch", (int)totalTime); + EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME, + userId, System.identityHashCode(this), shortComponentName, + thisTime, totalTime); + StringBuilder sb = service.mStringBuilder; + sb.setLength(0); + sb.append("Displayed "); + sb.append(shortComponentName); + sb.append(": "); + TimeUtils.formatDuration(thisTime, sb); + if (thisTime != totalTime) { + sb.append(" (total "); + TimeUtils.formatDuration(totalTime, sb); + sb.append(")"); + } + Log.i(ActivityManagerService.TAG, sb.toString()); + } + mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime); + if (totalTime > 0) { + service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); + } + displayStartTime = 0; + stack.mLaunchStartTime = 0; + } + public void windowsDrawn() { synchronized(service) { - if (launchTime != 0) { - final ActivityStack stack = task.stack; - final long curTime = SystemClock.uptimeMillis(); - final long thisTime = curTime - launchTime; - final long totalTime = stack.mInitialStartTime != 0 - ? (curTime - stack.mInitialStartTime) : thisTime; - if (ActivityManagerService.SHOW_ACTIVITY_START_TIME) { - EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME, - userId, System.identityHashCode(this), shortComponentName, - thisTime, totalTime); - StringBuilder sb = service.mStringBuilder; - sb.setLength(0); - sb.append("Displayed "); - sb.append(shortComponentName); - sb.append(": "); - TimeUtils.formatDuration(thisTime, sb); - if (thisTime != totalTime) { - sb.append(" (total "); - TimeUtils.formatDuration(totalTime, sb); - sb.append(")"); - } - Log.i(ActivityManagerService.TAG, sb.toString()); - } - mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime); - if (totalTime > 0) { - service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); - } - launchTime = 0; - stack.mInitialStartTime = 0; + if (displayStartTime != 0) { + reportLaunchTimeLocked(SystemClock.uptimeMillis()); } startTime = 0; finishLaunchTickingLocked(); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index 8e8bb557fd85..9013b4a48c19 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -205,7 +205,8 @@ final class ActivityStack { */ boolean mConfigWillChange; - long mInitialStartTime = 0; + long mLaunchStartTime = 0; + long mFullyDrawnStartTime = 0; /** * Save the most recent screenshot for reuse. This keeps Recents from taking two identical @@ -595,16 +596,20 @@ final class ActivityStack { } void setLaunchTime(ActivityRecord r) { - if (r.launchTime == 0) { - r.launchTime = SystemClock.uptimeMillis(); - if (mInitialStartTime == 0) { - mInitialStartTime = r.launchTime; + if (r.displayStartTime == 0) { + r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis(); + if (mLaunchStartTime == 0) { + mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime; } - } else if (mInitialStartTime == 0) { - mInitialStartTime = SystemClock.uptimeMillis(); + } else if (mLaunchStartTime == 0) { + mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis(); } } + void clearLaunchTime(ActivityRecord r) { + r.displayStartTime = r.fullyDrawnStartTime = 0; + } + void stopIfSleepingLocked() { if (mLaunchingActivity.isHeld()) { if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { @@ -710,6 +715,7 @@ final class ActivityStack { mLastPausedActivity = prev; prev.state = ActivityState.PAUSING; prev.task.touchActiveTime(); + clearLaunchTime(prev); prev.updateThumbnail(screenshotActivities(prev), null); mService.updateCpuStats(); diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index f784861a2990..f4bf9f388012 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -86,3 +86,6 @@ option java_package com.android.server.am # User switched 30041 am_switch_user (id|1|5) + +# Activity fully drawn time +30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3) diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java index 7ea4a32e02ad..5585ac8c1226 100644 --- a/services/java/com/android/server/am/ProcessList.java +++ b/services/java/com/android/server/am/ProcessList.java @@ -98,21 +98,30 @@ final class ProcessList { // without empty apps being able to push them out of memory. static final int MIN_CACHED_APPS = 2; - // The maximum number of cached processes we will keep around before - // killing them; this is just a control to not let us go too crazy with - // keeping around processes on devices with large amounts of RAM. + // The maximum number of cached processes we will keep around before killing them. + // NOTE: this constant is *only* a control to not let us go too crazy with + // keeping around processes on devices with large amounts of RAM. For devices that + // are tighter on RAM, the out of memory killer is responsible for killing background + // processes as RAM is needed, and we should *never* be relying on this limit to + // kill them. Also note that this limit only applies to cached background processes; + // we have no limit on the number of service, visible, foreground, or other such + // processes and the number of those processes does not count against the cached + // process limit. static final int MAX_CACHED_APPS = 24; // We allow empty processes to stick around for at most 30 minutes. static final long MAX_EMPTY_TIME = 30*60*1000; - // The number of cached at which we don't consider it necessary to do - // memory trimming. - static final int TRIM_CACHED_APPS = 3; + // The maximum number of empty app processes we will let sit around. + private static final int MAX_EMPTY_APPS = computeEmptyProcessLimit(MAX_CACHED_APPS); // The number of empty apps at which we don't consider it necessary to do // memory trimming. - static final int TRIM_EMPTY_APPS = 3; + static final int TRIM_EMPTY_APPS = MAX_EMPTY_APPS/2; + + // The number of cached at which we don't consider it necessary to do + // memory trimming. + static final int TRIM_CACHED_APPS = (MAX_CACHED_APPS-MAX_EMPTY_APPS)/2; // Threshold of number of cached+empty where we consider memory critical. static final int TRIM_CRITICAL_THRESHOLD = 3; @@ -234,6 +243,10 @@ final class ProcessList { // HC: 8192,10240,12288,14336,16384,20480 } + public static int computeEmptyProcessLimit(int totalProcessLimit) { + return (totalProcessLimit*2)/3; + } + long getMemLevel(int adjustment) { for (int i=0; i<mOomAdj.length; i++) { if (adjustment <= mOomAdj[i]) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 365009ddf842..1671d24b0d8e 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -81,6 +81,7 @@ final class ProcessRecord { boolean setIsForeground; // Running foreground UI when last set? boolean hasActivities; // Are there any activities running in this process? boolean hasClientActivities; // Are there any client services with activities? + boolean hasStartedServices; // Are there any started services running in this process? boolean foregroundServices; // Running any services that are foreground? boolean foregroundActivities; // Running any activities that are foreground? boolean systemNoUi; // This is a system process, but not currently showing UI. @@ -246,6 +247,9 @@ final class ProcessRecord { pw.print(" hasClientActivities="); pw.print(hasClientActivities); pw.print(" foregroundActivities="); pw.println(foregroundActivities); } + if (hasStartedServices) { + pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices); + } if (!keeping) { long wtime; synchronized (batteryStats.getBatteryStats()) { @@ -454,10 +458,17 @@ final class ProcessRecord { return false; } + public int getSetAdjWithServices() { + if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ && hasStartedServices) { + return ProcessList.SERVICE_B_ADJ; + } + return setAdj; + } + public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now, ProcessList plist) { int state = this == TOP_APP ? ProcessTracker.STATE_TOP - : plist.adjToTrackedState(setAdj); + : plist.adjToTrackedState(getSetAdjWithServices()); baseProcessTracker.setState(state, memFactor, now, pkgList); } diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java index 7985df48056e..fbdbdd9db193 100644 --- a/services/java/com/android/server/am/ProcessTracker.java +++ b/services/java/com/android/server/am/ProcessTracker.java @@ -46,6 +46,7 @@ import java.util.concurrent.locks.ReentrantLock; public final class ProcessTracker { static final String TAG = "ProcessTracker"; + static final boolean DEBUG = false; public static final int STATE_NOTHING = -1; public static final int STATE_PERSISTENT = 0; @@ -239,6 +240,18 @@ public final class ProcessTracker { return pnew; } + void resetSafely(long now) { + mDurationsTable = null; + mDurationsTableSize = 0; + mStartTime = now; + mLastPssState = STATE_NOTHING; + mLastPssTime = 0; + mPssTable = null; + mPssTableSize = 0; + mNumExcessiveWake = 0; + mNumExcessiveCpu = 0; + } + void writeToParcel(Parcel out, long now) { commitStateTime(now); out.writeInt(mMultiPackage ? 1 : 0); @@ -272,8 +285,11 @@ public final class ProcessTracker { return table; } - boolean readFromParcel(Parcel in) { - mMultiPackage = in.readInt() != 0; + boolean readFromParcel(Parcel in, boolean fully) { + boolean multiPackage = in.readInt() != 0; + if (fully) { + mMultiPackage = multiPackage; + } mDurationsTable = readTable(in, "durations"); if (mDurationsTable == null) { return false; @@ -463,6 +479,14 @@ public final class ProcessTracker { int mExecState = STATE_NOTHING; long mExecStartTime; + void resetSafely(long now) { + for (int i=0; i<ADJ_COUNT; i++) { + mStartedDurations[i] = mBoundDurations[i] = mExecDurations[i] = 0; + } + mStartedCount = mBoundCount = mExecCount = 0; + mStartedStartTime = mBoundStartTime = mExecStartTime = now; + } + void writeToParcel(Parcel out, long now) { if (mStartedState != STATE_NOTHING) { mStartedDurations[mStartedState] += now - mStartedStartTime; @@ -571,9 +595,47 @@ public final class ProcessTracker { } void reset() { - mTimePeriodStart = mTimePeriodEnd = System.currentTimeMillis(); + resetCommon(); mPackages.getMap().clear(); mProcesses.getMap().clear(); + mMemFactor = STATE_NOTHING; + mStartTime = 0; + } + + void resetSafely() { + resetCommon(); + long now = SystemClock.uptimeMillis(); + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + final int NPROC = procMap.size(); + for (int ip=0; ip<NPROC; ip++) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + uids.valueAt(iu).resetSafely(now); + } + } + ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); + final int NPKG = pkgMap.size(); + for (int ip=0; ip<NPKG; ip++) { + SparseArray<PackageState> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + PackageState pkgState = uids.valueAt(iu); + final int NPROCS = pkgState.mProcesses.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + pkgState.mProcesses.valueAt(iproc).resetSafely(now); + } + final int NSRVS = pkgState.mServices.size(); + for (int isvc=0; isvc<NSRVS; isvc++) { + pkgState.mServices.valueAt(isvc).resetSafely(now); + } + } + } + mStartTime = SystemClock.uptimeMillis(); + } + + private void resetCommon() { + mTimePeriodStart = mTimePeriodEnd = System.currentTimeMillis(); mLongs.clear(); mLongs.add(new long[LONGS_SIZE]); mNextLong = 0; @@ -671,7 +733,12 @@ public final class ProcessTracker { } void readFromParcel(Parcel in) { - reset(); + final boolean hadData = mPackages.getMap().size() > 0 + || mProcesses.getMap().size() > 0; + if (hadData) { + resetSafely(); + } + if (!readCheckedInt(in, MAGIC, "magic number")) { return; } @@ -740,14 +807,24 @@ public final class ProcessTracker { Slog.w(TAG, "Ignoring existing stats; bad process package name"); return; } - ProcessState proc = new ProcessState(this, pkgName, uid, procName); - if (!proc.readFromParcel(in)) { - return; + ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(this, pkgName, uid, procName); + if (!proc.readFromParcel(in, true)) { + return; + } } + if (DEBUG) Slog.d(TAG, "Adding process: " + procName + " " + uid + " " + proc); mProcesses.put(procName, uid, proc); } } + if (DEBUG) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); + int NPKG = in.readInt(); if (NPKG < 0) { Slog.w(TAG, "Ignoring existing stats; bad package count: " + NPKG); @@ -773,6 +850,7 @@ public final class ProcessTracker { 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); @@ -786,21 +864,33 @@ public final class ProcessTracker { return; } int hasProc = in.readInt(); + if (DEBUG) Slog.d(TAG, "Reading package " + pkgName + " " + uid + + " process " + procName + " hasProc=" + hasProc); 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 = new ProcessState(commonProc, pkgName, uid, - procName, 0); - if (!proc.readFromParcel(in)) { - return; + ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(commonProc, pkgName, uid, procName, 0); + if (!proc.readFromParcel(in, true)) { + return; + } } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + proc); pkgState.mProcesses.put(procName, proc); } } @@ -816,14 +906,21 @@ public final class ProcessTracker { Slog.w(TAG, "Ignoring existing stats; bad package service name"); return; } - ServiceState serv = new ServiceState(); + ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; + if (serv == null) { + serv = new ServiceState(); + } if (!serv.readFromParcel(in)) { return; } + if (DEBUG) Slog.d(TAG, "Adding package " + pkgName + " service: " + + serviceName + " " + uid + " " + serv); pkgState.mServices.put(serviceName, serv); } } } + + if (DEBUG) Slog.d(TAG, "Successfully read procstats!"); } int addLongData(int index, int type, int num) { @@ -949,7 +1046,7 @@ public final class ProcessTracker { return ps; } - void dump(PrintWriter pw, String reqPackage, boolean dumpAll) { + void dumpLocked(PrintWriter pw, String reqPackage, boolean dumpAll) { final long now = SystemClock.uptimeMillis(); ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); boolean printedHeader = false; @@ -1097,7 +1194,7 @@ public final class ProcessTracker { return outProcs; } - void dumpCheckin(PrintWriter pw, String reqPackage) { + void dumpCheckinLocked(PrintWriter pw, String reqPackage) { final long now = SystemClock.uptimeMillis(); ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap(); pw.println("vers,1"); @@ -1741,7 +1838,7 @@ public final class ProcessTracker { } } - boolean dumpFilteredProcessesCsv(PrintWriter pw, String header, + boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage) { ArrayList<ProcessState> procs = mState.collectProcessesLocked(screenStates, memStates, @@ -1883,6 +1980,7 @@ public final class ProcessTracker { 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(" --read: replace current stats with last-written stats."); pw.println(" -a: print everything."); pw.println(" -h: print this help text."); pw.println(" <package.name>: optional name of package to filter output by."); @@ -1961,13 +2059,17 @@ public final class ProcessTracker { } csvSepProcStats = sep[0]; } else if ("--reset".equals(arg)) { - mState.reset(); + mState.resetSafely(); pw.println("Process stats reset."); return; } else if ("--write".equals(arg)) { writeStateSyncLocked(); pw.println("Process stats written."); return; + } else if ("--read".equals(arg)) { + readLocked(); + pw.println("Process stats read."); + return; } else if ("-h".equals(arg)) { dumpHelp(pw); return; @@ -2016,18 +2118,18 @@ public final class ProcessTracker { } } pw.println(); - dumpFilteredProcessesCsv(pw, null, + dumpFilteredProcessesCsvLocked(pw, null, csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats, csvSepProcStats, csvProcStats, now, reqPackage); /* - dumpFilteredProcessesCsv(pw, "Processes running while critical mem:", + dumpFilteredProcessesCsvLocked(pw, "Processes running while critical mem:", false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, true, new int[] {ADJ_MEM_FACTOR_CRITICAL}, true, new int[] {STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED}, now, reqPackage); - dumpFilteredProcessesCsv(pw, "Processes running over all mem:", + dumpFilteredProcessesCsvLocked(pw, "Processes running over all mem:", false, new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON}, false, new int[] {ADJ_MEM_FACTOR_CRITICAL, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_MODERATE}, @@ -2040,9 +2142,9 @@ public final class ProcessTracker { } if (isCheckin) { - mState.dumpCheckin(pw, reqPackage); + mState.dumpCheckinLocked(pw, reqPackage); } else { - mState.dump(pw, reqPackage, dumpAll); + mState.dumpLocked(pw, reqPackage, dumpAll); } } } diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index f799535e6cfd..e96d8b1ce91c 100644 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -77,7 +77,7 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1007; + private static final int VERSION = 1008; private static final int CHECKIN_VERSION = 4; @@ -166,8 +166,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } private class PkgUsageStatsExtended { - final HashMap<String, TimeStats> mLaunchTimes - = new HashMap<String, TimeStats>(); + final ArrayMap<String, TimeStats> mLaunchTimes + = new ArrayMap<String, TimeStats>(); + final ArrayMap<String, TimeStats> mFullyDrawnTimes + = new ArrayMap<String, TimeStats>(); int mLaunchCount; long mUsageTime; long mPausedTime; @@ -184,14 +186,25 @@ public final class UsageStatsService extends IUsageStats.Stub { if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount + ", Usage time:" + mUsageTime); - final int numTimeStats = in.readInt(); - if (localLOGV) Slog.v(TAG, "Reading comps: " + numTimeStats); - for (int i=0; i<numTimeStats; i++) { + final int numLaunchTimeStats = in.readInt(); + if (localLOGV) Slog.v(TAG, "Reading launch times: " + numLaunchTimeStats); + mLaunchTimes.ensureCapacity(numLaunchTimeStats); + for (int i=0; i<numLaunchTimeStats; i++) { String comp = in.readString(); if (localLOGV) Slog.v(TAG, "Component: " + comp); TimeStats times = new TimeStats(in); mLaunchTimes.put(comp, times); } + + final int numFullyDrawnTimeStats = in.readInt(); + if (localLOGV) Slog.v(TAG, "Reading fully drawn times: " + numFullyDrawnTimeStats); + mFullyDrawnTimes.ensureCapacity(numFullyDrawnTimeStats); + for (int i=0; i<numFullyDrawnTimeStats; i++) { + String comp = in.readString(); + if (localLOGV) Slog.v(TAG, "Component: " + comp); + TimeStats times = new TimeStats(in); + mFullyDrawnTimes.put(comp, times); + } } void updateResume(String comp, boolean launched) { @@ -223,23 +236,36 @@ public final class UsageStatsService extends IUsageStats.Stub { } times.add(millis); } - + + void addFullyDrawnTime(String comp, int millis) { + TimeStats times = mFullyDrawnTimes.get(comp); + if (times == null) { + times = new TimeStats(); + mFullyDrawnTimes.put(comp, times); + } + times.add(millis); + } + void writeToParcel(Parcel out) { out.writeInt(mLaunchCount); out.writeLong(mUsageTime); - final int numTimeStats = mLaunchTimes.size(); - out.writeInt(numTimeStats); - if (numTimeStats > 0) { - for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) { - out.writeString(ent.getKey()); - TimeStats times = ent.getValue(); - times.writeToParcel(out); - } + final int numLaunchTimeStats = mLaunchTimes.size(); + out.writeInt(numLaunchTimeStats); + for (int i=0; i<numLaunchTimeStats; i++) { + out.writeString(mLaunchTimes.keyAt(i)); + mLaunchTimes.valueAt(i).writeToParcel(out); + } + final int numFullyDrawnTimeStats = mFullyDrawnTimes.size(); + out.writeInt(numFullyDrawnTimeStats); + for (int i=0; i<numFullyDrawnTimeStats; i++) { + out.writeString(mFullyDrawnTimes.keyAt(i)); + mFullyDrawnTimes.valueAt(i).writeToParcel(out); } } void clear() { mLaunchTimes.clear(); + mFullyDrawnTimes.clear(); mLaunchCount = 0; mUsageTime = 0; } @@ -783,6 +809,25 @@ public final class UsageStatsService extends IUsageStats.Stub { } } + public void noteFullyDrawnTime(ComponentName componentName, int millis) { + enforceCallingPermission(); + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + + // Persist current data to file if needed. + writeStatsToFile(false, false); + + synchronized (mStatsLock) { + PkgUsageStatsExtended pus = mStats.get(pkgName); + if (pus != null) { + pus.addFullyDrawnTime(componentName.getClassName(), millis); + } + } + } + public void enforceCallingPermission() { if (Binder.getCallingPid() == Process.myPid()) { return; @@ -935,29 +980,33 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(','); sb.append(pus.mUsageTime); sb.append('\n'); - final int NC = pus.mLaunchTimes.size(); - if (NC > 0) { - for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) { - sb.append("A:"); - String activity = ent.getKey(); - if (activity.startsWith(pkgName)) { - sb.append('*'); - sb.append(activity.substring( - pkgName.length(), activity.length())); - } else { - sb.append(activity); - } - TimeStats times = ent.getValue(); - sb.append(','); - sb.append(times.count); - for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { - sb.append(","); - sb.append(times.times[i]); - } - sb.append('\n'); + final int NLT = pus.mLaunchTimes.size(); + for (int i=0; i<NLT; i++) { + sb.append("A:"); + String activity = pus.mLaunchTimes.keyAt(i); + sb.append(activity); + TimeStats times = pus.mLaunchTimes.valueAt(i); + sb.append(','); + sb.append(times.count); + for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { + sb.append(","); + sb.append(times.times[j]); } + sb.append('\n'); } - + final int NFDT = pus.mFullyDrawnTimes.size(); + for (int i=0; i<NFDT; i++) { + sb.append("A:"); + String activity = pus.mFullyDrawnTimes.keyAt(i); + sb.append(activity); + TimeStats times = pus.mFullyDrawnTimes.valueAt(i); + for (int j=0; j<NUM_LAUNCH_TIME_BINS; j++) { + sb.append(","); + sb.append(times.times[j]); + } + sb.append('\n'); + } + } else { sb.append(" "); sb.append(pkgName); @@ -967,36 +1016,68 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(pus.mUsageTime); sb.append(" ms"); sb.append('\n'); - final int NC = pus.mLaunchTimes.size(); - if (NC > 0) { - for (Map.Entry<String, TimeStats> ent : pus.mLaunchTimes.entrySet()) { - sb.append(" "); - sb.append(ent.getKey()); - TimeStats times = ent.getValue(); - sb.append(": "); - sb.append(times.count); - sb.append(" starts"); - int lastBin = 0; - for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { - if (times.times[i] != 0) { + final int NLT = pus.mLaunchTimes.size(); + for (int i=0; i<NLT; i++) { + sb.append(" "); + sb.append(pus.mLaunchTimes.keyAt(i)); + TimeStats times = pus.mLaunchTimes.valueAt(i); + sb.append(": "); + sb.append(times.count); + sb.append(" starts"); + int lastBin = 0; + for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { + if (times.times[j] != 0) { + sb.append(", "); + sb.append(lastBin); + sb.append('-'); + sb.append(LAUNCH_TIME_BINS[j]); + sb.append("ms="); + sb.append(times.times[j]); + } + lastBin = LAUNCH_TIME_BINS[j]; + } + if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + sb.append(", "); + sb.append(">="); + sb.append(lastBin); + sb.append("ms="); + sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + } + sb.append('\n'); + } + final int NFDT = pus.mFullyDrawnTimes.size(); + for (int i=0; i<NFDT; i++) { + sb.append(" "); + sb.append(pus.mFullyDrawnTimes.keyAt(i)); + TimeStats times = pus.mFullyDrawnTimes.valueAt(i); + sb.append(": fully drawn "); + boolean needComma = false; + int lastBin = 0; + for (int j=0; j<NUM_LAUNCH_TIME_BINS-1; j++) { + if (times.times[j] != 0) { + if (needComma) { sb.append(", "); - sb.append(lastBin); - sb.append('-'); - sb.append(LAUNCH_TIME_BINS[i]); - sb.append("ms="); - sb.append(times.times[i]); + } else { + needComma = true; } - lastBin = LAUNCH_TIME_BINS[i]; - } - if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { - sb.append(", "); - sb.append(">="); sb.append(lastBin); + sb.append('-'); + sb.append(LAUNCH_TIME_BINS[j]); sb.append("ms="); - sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); + sb.append(times.times[j]); + } + lastBin = LAUNCH_TIME_BINS[j]; + } + if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { + if (needComma) { + sb.append(", "); } - sb.append('\n'); + sb.append(">="); + sb.append(lastBin); + sb.append("ms="); + sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); } + sb.append('\n'); } } |