summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2013-06-27 18:32:07 -0700
committerDianne Hackborn <hackbod@google.com>2013-06-27 18:56:00 -0700
commitcfc837f7fa43362b5049f648fe1bfdf5a010cc1c (patch)
treea633f0dff8d27965896ccda7e600fdaf5c1bd582 /services
parent97e1d53d36d3ce11fc1b8322c11320cbd614f960 (diff)
downloadframeworks_base-cfc837f7fa43362b5049f648fe1bfdf5a010cc1c.zip
frameworks_base-cfc837f7fa43362b5049f648fe1bfdf5a010cc1c.tar.gz
frameworks_base-cfc837f7fa43362b5049f648fe1bfdf5a010cc1c.tar.bz2
Start really collecting PSS data for process stats.
The activity manager now uses some heuristics to try to sample PSS data from processes so that it can get enough data to over reasonable time have something useful, without doing it too aggressively. The current policy is: 1. Whenever a significant global change happens (memory state, sceen on or off), we collect PSS from all processes; this will not happen more than every 10 minutes. 2. When all activities become idle, we will collect PSS from the current top process; this will not happen more than every 2 minutes per process. 3. We will sample the top-most process's PSS every 5 minutes. 4. When an process's oom adj changes and it has been more than 30 minutes since PSS has been collected from it, we will collect a new PSS sample. 5. If a process changes from service A to service B (meaning it has been running a service for a long time), we will collect a PSS sample from it. 6. If someone explicitly requests PSS data (for running services UI or dumpsys), record that. Also: - Finish moving the procstats output all to the new format. - Record information about processes being killed due to excessive wake locks or CPU use in procstats. - Rework how we structure common vs. per-package process stats to make it simpler to deal with. - Optimize the Debug.getPss() implementation (we use it a lot now). Should probably optimize it further at some point. Change-Id: I179f1f7ae5852c7e567de4127d8457b50d27e0f0
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java274
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java2
-rw-r--r--services/java/com/android/server/am/ActivityStackSupervisor.java1
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java30
-rw-r--r--services/java/com/android/server/am/ProcessTracker.java253
5 files changed, 412 insertions, 148 deletions
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e37eec6..f201563 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@ import android.util.ArrayMap;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessStats;
import com.android.internal.os.TransferPipe;
@@ -266,6 +267,20 @@ public final class ActivityManagerService extends ActivityManagerNative
// The minimum amount of time between successive GC requests for a process.
static final int GC_MIN_INTERVAL = 60*1000;
+ // The minimum amount of time between successive PSS requests for a process.
+ static final int PSS_MIN_INTERVAL = 2*60*1000;
+
+ // 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;
+
+ // The maximum amount of time for a process to be around until we will take
+ // a PSS snapshot on its next oom change.
+ static final int PSS_MAX_INTERVAL = 30*60*1000;
+
+ // The minimum amount of time between successive PSS requests for a process.
+ static final int FULL_PSS_MIN_INTERVAL = 10*60*1000;
+
// The rate at which we check for apps using excessive power -- 15 mins.
static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000;
@@ -495,8 +510,17 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* List of processes that should gc as soon as things are idle.
*/
- final ArrayList<ProcessRecord> mProcessesToGc
- = new ArrayList<ProcessRecord>();
+ final ArrayList<ProcessRecord> mProcessesToGc = new ArrayList<ProcessRecord>();
+
+ /**
+ * Processes we want to collect PSS data from.
+ */
+ final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
+
+ /**
+ * Last time we requested PSS data of all processes.
+ */
+ long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* This is the process holding what we currently consider to be
@@ -1480,6 +1504,51 @@ public final class ActivityManagerService extends ActivityManagerNative
}
};
+ static final int COLLECT_PSS_BG_MSG = 1;
+
+ final Handler mBgHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case COLLECT_PSS_BG_MSG: {
+ int i=0;
+ long start = SystemClock.uptimeMillis();
+ do {
+ ProcessRecord proc;
+ int oomAdj;
+ int pid;
+ synchronized (ActivityManagerService.this) {
+ if (i >= mPendingPssProcesses.size()) {
+ Slog.i(TAG, "Collected PSS of " + i + " processes in "
+ + (SystemClock.uptimeMillis()-start) + "ms");
+ mPendingPssProcesses.clear();
+ return;
+ }
+ proc = mPendingPssProcesses.get(i);
+ if (proc.thread != null) {
+ oomAdj = proc.setAdj;
+ pid = proc.pid;
+ i++;
+ } else {
+ proc = null;
+ oomAdj = 0;
+ pid = 0;
+ }
+ }
+ if (proc != null) {
+ long pss = Debug.getPss(pid);
+ synchronized (ActivityManagerService.this) {
+ if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) {
+ proc.baseProcessTracker.addPss(pss, true);
+ }
+ }
+ }
+ } while (true);
+ }
+ }
+ }
+ };
+
public static void setSystemProcess() {
try {
ActivityManagerService m = mSelf;
@@ -3906,8 +3975,24 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceNotIsolatedCaller("getProcessMemoryInfo");
Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
for (int i=pids.length-1; i>=0; i--) {
+ ProcessRecord proc;
+ int oomAdj;
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pids[i]);
+ oomAdj = proc != null ? proc.setAdj : 0;
+ }
+ }
infos[i] = new Debug.MemoryInfo();
Debug.getMemoryInfo(pids[i], infos[i]);
+ if (proc != null) {
+ synchronized (this) {
+ if (proc.thread != null && proc.setAdj == oomAdj) {
+ // Record this for posterity if the process has been stable.
+ proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false);
+ }
+ }
+ }
}
return infos;
}
@@ -3917,7 +4002,23 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceNotIsolatedCaller("getProcessPss");
long[] pss = new long[pids.length];
for (int i=pids.length-1; i>=0; i--) {
+ ProcessRecord proc;
+ int oomAdj;
+ synchronized (this) {
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pids[i]);
+ oomAdj = proc != null ? proc.setAdj : 0;
+ }
+ }
pss[i] = Debug.getPss(pids[i]);
+ if (proc != null) {
+ synchronized (this) {
+ if (proc.thread != null && proc.setAdj == oomAdj) {
+ // Record this for posterity if the process has been stable.
+ proc.baseProcessTracker.addPss(pss[i], false);
+ }
+ }
+ }
}
return pss;
}
@@ -4350,7 +4451,7 @@ public final class ActivityManagerService extends ActivityManagerNative
thread.asBinder().linkToDeath(adr, 0);
app.deathRecipient = adr;
} catch (RemoteException e) {
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
startProcessLocked(app, "link fail", processName);
return false;
}
@@ -4442,7 +4543,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// an infinite loop of restarting processes...
Slog.w(TAG, "Exception thrown during bind!", e);
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
app.unlinkDeathRecipient();
startProcessLocked(app, "bind fail", processName);
return false;
@@ -4630,8 +4731,19 @@ public final class ActivityManagerService extends ActivityManagerNative
final int userId = mStartedUsers.keyAt(i);
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- broadcastIntentLocked(null, null, intent,
- null, null, 0, null, null,
+ broadcastIntentLocked(null, null, intent, null,
+ new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode,
+ String data, Bundle extras, boolean ordered,
+ boolean sticky, int sendingUser) {
+ synchronized (ActivityManagerService.this) {
+ requestPssAllProcsLocked(SystemClock.uptimeMillis(),
+ true);
+ }
+ }
+ },
+ 0, null, null,
android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
AppOpsManager.OP_NONE, false, false, MY_PID, Process.SYSTEM_UID,
userId);
@@ -10934,7 +11046,7 @@ public final class ActivityManagerService extends ActivityManagerNative
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
- if (procs.size() == 1 || isCheckinRequest) {
+ if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest)) {
dumpDetails = true;
}
@@ -10960,17 +11072,24 @@ public final class ActivityManagerService extends ActivityManagerNative
long totalPss = 0;
+ Debug.MemoryInfo mi = null;
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
- if (r.thread != null) {
+ IApplicationThread thread;
+ int oomAdj;
+ synchronized (this) {
+ thread = r.thread;
+ oomAdj = r.setAdj;
+ }
+ if (thread != null) {
if (!isCheckinRequest && dumpDetails) {
pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
pw.flush();
}
- Debug.MemoryInfo mi = null;
if (dumpDetails) {
try {
- mi = r.thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
+ mi = null;
+ mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
} catch (RemoteException e) {
if (!isCheckinRequest) {
pw.println("Got RemoteException!");
@@ -10978,21 +11097,31 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
} else {
- mi = new Debug.MemoryInfo();
- Debug.getMemoryInfo(r.pid, mi);
+ if (mi == null) {
+ mi = new Debug.MemoryInfo();
+ }
+ if (!brief && !oomOnly) {
+ Debug.getMemoryInfo(r.pid, mi);
+ } else {
+ mi.dalvikPss = (int)Debug.getPss(r.pid);
+ }
+ }
+
+ final long myTotalPss = mi.getTotalPss();
+
+ synchronized (this) {
+ if (r.thread != null && oomAdj == r.setAdj) {
+ // Record this for posterity if the process has been stable.
+ r.baseProcessTracker.addPss(myTotalPss, true);
+ }
}
if (!isCheckinRequest && mi != null) {
- long myTotalPss = mi.getTotalPss();
totalPss += myTotalPss;
MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
r.processName, myTotalPss, 0);
procMems.add(pssItem);
- synchronized (this) {
- r.baseProcessTracker.addPss(myTotalPss);
- }
-
nativePss += mi.nativePss;
dalvikPss += mi.dalvikPss;
otherPss += mi.otherPss;
@@ -11070,7 +11199,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
for (int j=0; j<miCat.subitems.size(); j++) {
- MemItem mi = miCat.subitems.get(j);
+ MemItem memi = miCat.subitems.get(j);
if (j > 0) {
if (outTag != null) {
outTag.append(" ");
@@ -11080,10 +11209,10 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
if (outTag != null && miCat.id <= ProcessList.FOREGROUND_APP_ADJ) {
- appendMemBucket(outTag, mi.pss, mi.shortLabel, false);
+ appendMemBucket(outTag, memi.pss, memi.shortLabel, false);
}
if (outStack != null) {
- appendMemBucket(outStack, mi.pss, mi.shortLabel, true);
+ appendMemBucket(outStack, memi.pss, memi.shortLabel, true);
}
}
if (outStack != null && miCat.id >= ProcessList.FOREGROUND_APP_ADJ) {
@@ -11109,7 +11238,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
pw.println("Total PSS by OOM adjustment:");
dumpMemItems(pw, " ", oomMems, false);
- if (!oomOnly) {
+ if (!brief && !oomOnly) {
PrintWriter out = categoryPw != null ? categoryPw : pw;
out.println();
out.println("Total PSS by category:");
@@ -11117,29 +11246,31 @@ public final class ActivityManagerService extends ActivityManagerNative
}
pw.println();
pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB");
- final int[] SINGLE_LONG_FORMAT = new int[] {
- Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
- };
- long[] longOut = new long[1];
- Process.readProcFile("/sys/kernel/mm/ksm/pages_shared",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared",
- SINGLE_LONG_FORMAT, null, longOut, null);
- long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
- longOut[0] = 0;
- 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 (!brief) {
+ final int[] SINGLE_LONG_FORMAT = new int[] {
+ Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
+ };
+ long[] longOut = new long[1];
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_shared",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long shared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_sharing",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long sharing = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ Process.readProcFile("/sys/kernel/mm/ksm/pages_unshared",
+ SINGLE_LONG_FORMAT, null, longOut, null);
+ long unshared = longOut[0] * ProcessList.PAGE_SIZE / 1024;
+ longOut[0] = 0;
+ 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");
+ }
}
}
@@ -11236,6 +11367,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
mProcessesToGc.remove(app);
+ mPendingPssProcesses.remove(app);
// Dismiss any open dialogs.
if (app.crashDialog != null && !app.forceCrashReport) {
@@ -11254,7 +11386,7 @@ public final class ActivityManagerService extends ActivityManagerNative
app.crashing = false;
app.notResponding = false;
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
app.unlinkDeathRecipient();
app.thread = null;
app.forcingToForeground = null;
@@ -13749,6 +13881,41 @@ public final class ActivityManagerService extends ActivityManagerNative
}
/**
+ * Schedule PSS collection of a process.
+ */
+ void requestPssLocked(ProcessRecord proc, long now, boolean always) {
+ if (!always && now < (proc.lastPssTime+PSS_MIN_INTERVAL)) {
+ return;
+ }
+ if (mPendingPssProcesses.contains(proc)) {
+ return;
+ }
+ proc.lastPssTime = now;
+ if (mPendingPssProcesses.size() == 0) {
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
+ mPendingPssProcesses.add(proc);
+ }
+
+ /**
+ * Schedule PSS collection of all processes.
+ */
+ void requestPssAllProcsLocked(long now, boolean always) {
+ if (!always && now < (mLastFullPssTime+FULL_PSS_MIN_INTERVAL)) {
+ return;
+ }
+ mLastFullPssTime = now;
+ mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
+ mPendingPssProcesses.clear();
+ for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ app.lastPssTime = now;
+ mPendingPssProcesses.add(app);
+ }
+ mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
+ }
+
+ /**
* Ask a given process to GC right now.
*/
final void performAppGcLocked(ProcessRecord app) {
@@ -13959,6 +14126,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " during " + realtimeSince);
EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
app.processName, app.setAdj, "excessive wake lock");
+ app.baseProcessTracker.reportExcessiveWake(app.pkgList);
Process.killProcessQuiet(app.pid);
} else if (doCpuKills && uptimeSince > 0
&& ((cputimeUsed*100)/uptimeSince) >= 50) {
@@ -13971,6 +14139,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+ " during " + uptimeSince);
EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
app.processName, app.setAdj, "excessive cpu");
+ app.baseProcessTracker.reportExcessiveCpu(app.pkgList);
Process.killProcessQuiet(app.pid);
} else {
app.lastWakeTime = wtime;
@@ -14012,11 +14181,23 @@ 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 (app.curAdj != app.setAdj) {
if (Process.setOomAdj(app.pid, app.curAdj)) {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(
TAG, "Set " + app.pid + " " + app.processName +
" adj " + app.curAdj + ": " + app.adjType);
+ if (app.setAdj == ProcessList.SERVICE_ADJ
+ && app.curAdj == ProcessList.SERVICE_B_ADJ) {
+ // If a service is dropping to the B list, it has been running for
+ // a while, take a PSS snapshot.
+ requestPssLocked(app, now, false);
+ } else if (now > (app.lastPssTime+PSS_MAX_INTERVAL)) {
+ requestPssLocked(app, now, true);
+ }
app.setAdj = app.curAdj;
app.setAdjChanged = true;
if (!doingAll) {
@@ -14406,6 +14587,9 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+ if (allChanged) {
+ requestPssAllProcsLocked(now, false);
+ }
if (DEBUG_OOM_ADJ) {
Slog.d(TAG, "Did OOM ADJ in " + (SystemClock.uptimeMillis()-now) + "ms");
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 561dd0f..44b61d0 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -222,7 +222,7 @@ final class ActivityRecord {
if (lastLaunchTime == 0) pw.print("0");
else TimeUtils.formatDuration(lastLaunchTime, now, pw);
pw.println();
- pw.print(prefix); pw.print(" haveState="); pw.print(haveState);
+ pw.print(prefix); pw.print("haveState="); pw.print(haveState);
pw.print(" icicle="); pw.println(icicle);
pw.print(prefix); pw.print("state="); pw.print(state);
pw.print(" stopped="); pw.print(stopped);
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 8d98109..92a1523 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1707,6 +1707,7 @@ public final class ActivityStackSupervisor {
r.idle = true;
if (allResumedActivitiesIdle()) {
mService.scheduleAppGcsLocked();
+ mService.requestPssLocked(r.app, SystemClock.uptimeMillis(), false);
}
if (r.thumbnailNeeded && r.app != null && r.app.thread != null) {
sendThumbnail = r.app.thread;
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index e72656f..365009d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -62,6 +62,7 @@ final class ProcessRecord {
boolean starting; // True if the process is being started
long lastActivityTime; // For managing the LRU list
long lruWeight; // Weight for ordering in LRU list
+ long lastPssTime; // Last time we requested PSS data
int maxAdj; // Maximum OOM adjustment for this process
int cachedAdj; // If cached, this is the adjustment to use
int clientCachedAdj; // If empty but cached client, this is the adjustment to use
@@ -112,7 +113,6 @@ final class ProcessRecord {
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
boolean cached; // Is this a cached process?
- int lastPss; // Last pss size reported by app.
String adjType; // Debugging: primary thing impacting oom_adj.
int adjTypeCode; // Debugging: adj code to report to app.
Object adjSource; // Debugging: option dependent object.
@@ -180,7 +180,12 @@ final class ProcessRecord {
pw.print(prefix); pw.print("dir="); pw.print(info.sourceDir);
pw.print(" publicDir="); pw.print(info.publicSourceDir);
pw.print(" data="); pw.println(info.dataDir);
- pw.print(prefix); pw.print("packageList="); pw.println(pkgList);
+ pw.print(prefix); pw.print("packageList={");
+ for (int i=0; i<pkgList.size(); i++) {
+ if (i > 0) pw.print(", ");
+ pw.print(pkgList.keyAt(i));
+ }
+ pw.println("}");
pw.print(prefix); pw.print("compat="); pw.println(compat);
if (instrumentationClass != null || instrumentationProfileFile != null
|| instrumentationArguments != null) {
@@ -198,7 +203,7 @@ final class ProcessRecord {
}
pw.print(prefix); pw.print("thread="); pw.println(thread);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
- pw.print(starting); pw.print(" lastPss="); pw.println(lastPss);
+ pw.println(starting);
pw.print(prefix); pw.print("lastActivityTime=");
TimeUtils.formatDuration(lastActivityTime, now, pw);
pw.print(" lruWeight="); pw.print(lruWeight);
@@ -220,7 +225,8 @@ final class ProcessRecord {
pw.print(" systemNoUi="); pw.print(systemNoUi);
pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
- pw.print(" lruSeq="); pw.println(lruSeq);
+ pw.print(" lruSeq="); pw.print(lruSeq);
+ pw.print(" lastPssTime="); pw.println(lastPssTime);
if (hasShownUi || pendingUiClean || hasAboveClient) {
pw.print(prefix); pw.print("hasShownUi="); pw.print(hasShownUi);
pw.print(" pendingUiClean="); pw.print(pendingUiClean);
@@ -345,6 +351,7 @@ final class ProcessRecord {
curAdj = setAdj = -100;
persistent = false;
removed = false;
+ lastPssTime = SystemClock.uptimeMillis();
}
public void setPid(int _pid) {
@@ -451,21 +458,20 @@ final class ProcessRecord {
ProcessList plist) {
int state = this == TOP_APP ? ProcessTracker.STATE_TOP
: plist.adjToTrackedState(setAdj);
- if (pkgList.size() > 0) {
- pkgList.valueAt(0).setState(state, memFactor, now, pkgList);
- }
+ baseProcessTracker.setState(state, memFactor, now, pkgList);
}
/*
* Delete all packages from list except the package indicated in info
*/
- public void resetPackageList() {
+ public void resetPackageList(ProcessTracker tracker) {
long now = SystemClock.uptimeMillis();
- if (pkgList.size() > 0) {
- pkgList.valueAt(0).setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList);
+ baseProcessTracker.setState(ProcessTracker.STATE_NOTHING, 0, now, pkgList);
+ if (pkgList.size() != 1) {
+ pkgList.clear();
+ pkgList.put(info.packageName, tracker.getProcessStateLocked(
+ info.packageName, info.uid, processName));
}
- pkgList.clear();
- pkgList.put(info.packageName, baseProcessTracker);
}
public String[] getPackageList() {
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index 30470f1..82b2158 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -48,6 +48,11 @@ public final class ProcessTracker {
public static final int STATE_CACHED = 9;
public static final int STATE_COUNT = STATE_CACHED+1;
+ static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP,
+ STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP,
+ STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED
+ };
+
public static final int PSS_SAMPLE_COUNT = 0;
public static final int PSS_MINIMUM = 1;
public static final int PSS_AVERAGE = 2;
@@ -65,6 +70,10 @@ public final class ProcessTracker {
public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD;
public static final int ADJ_COUNT = ADJ_SCREEN_ON*2;
+ static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON };
+ static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, ADJ_MEM_FACTOR_MODERATE,
+ ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL };
+
// Most data is kept in a sparse data structure: an integer array which integer
// holds the type of the entry, and the identifier for a long array that data
// exists in and the offset into the array to find it. The constants below
@@ -110,7 +119,7 @@ public final class ProcessTracker {
static final String[] STATE_TAGS = new String[] {
"p", "t", "f", "v", "t",
- "b", "s", "h", "v", "c"
+ "b", "s", "h", "r", "c"
};
static final String CSV_SEP = "\t";
@@ -132,9 +141,14 @@ public final class ProcessTracker {
int mCurState = STATE_NOTHING;
long mStartTime;
+ int mLastPssState = STATE_NOTHING;
+ long mLastPssTime;
int[] mPssTable;
int mPssTableSize;
+ int mNumExcessiveWake;
+ int mNumExcessiveCpu;
+
boolean mMultiPackage;
long mTmpTotalTime;
@@ -145,7 +159,7 @@ public final class ProcessTracker {
*/
public ProcessState(State state, String pkg, int uid, String name) {
mState = state;
- mCommonProcess = null;
+ mCommonProcess = this;
mPackage = pkg;
mUid = uid;
mName = name;
@@ -199,6 +213,8 @@ public final class ProcessTracker {
pnew.mPssTableSize = mState.mFindTableSize;
}
*/
+ pnew.mNumExcessiveWake = mNumExcessiveWake;
+ pnew.mNumExcessiveCpu = mNumExcessiveCpu;
return pnew;
}
@@ -208,30 +224,16 @@ public final class ProcessTracker {
state += memFactor*STATE_COUNT;
}
- if (mCommonProcess != null) {
- // First update the common process.
- mCommonProcess.setState(state, now);
- if (!mCommonProcess.mMultiPackage) {
- // This common process is for a single package, so it is shared
- // with the per-package state. Nothing more to do.
- return;
- }
+ // First update the common process.
+ mCommonProcess.setState(state, now);
+
+ // If the common process is not multi-package, there is nothing else to do.
+ if (!mCommonProcess.mMultiPackage) {
+ return;
}
for (int ip=pkgList.size()-1; ip>=0; ip--) {
- ProcessState proc = pkgList.valueAt(ip);
- if (proc.mMultiPackage) {
- // The array map is still pointing to a common process state
- // that is now shared across packages. Update it to point to
- // the new per-package state.
- proc = mState.mPackages.get(pkgList.keyAt(ip),
- proc.mUid).mProcesses.get(proc.mName);
- if (proc == null) {
- throw new IllegalStateException("Didn't create per-package process");
- }
- pkgList.setValueAt(ip, proc);
- }
- proc.setState(state, now);
+ pullFixedProc(pkgList, ip).setState(state, now);
}
}
@@ -258,7 +260,15 @@ public final class ProcessTracker {
}
}
- public void addPss(long pss) {
+ public void addPss(long pss, boolean always) {
+ if (!always) {
+ if (mLastPssState == mCurState && SystemClock.uptimeMillis()
+ < (mLastPssTime+(30*1000))) {
+ return;
+ }
+ }
+ mLastPssState = mCurState;
+ mLastPssTime = SystemClock.uptimeMillis();
if (mCurState != STATE_NOTHING) {
int idx = State.binarySearch(mPssTable, mPssTableSize, mCurState);
int off;
@@ -284,7 +294,8 @@ public final class ProcessTracker {
if (longs[idx+PSS_MINIMUM] > pss) {
longs[idx+PSS_MINIMUM] = pss;
}
- longs[idx+PSS_AVERAGE] = ((longs[idx+PSS_AVERAGE]*count)+pss)/(count+1);
+ longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss)
+ / (count+1) );
if (longs[idx+PSS_MAXIMUM] < pss) {
longs[idx+PSS_MAXIMUM] = pss;
}
@@ -292,6 +303,45 @@ public final class ProcessTracker {
}
}
+ public void reportExcessiveWake(ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
+ mCommonProcess.mNumExcessiveWake++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveWake++;
+ }
+ }
+
+ public void reportExcessiveCpu(ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
+ mCommonProcess.mNumExcessiveCpu++;
+ if (!mCommonProcess.mMultiPackage) {
+ return;
+ }
+
+ for (int ip=pkgList.size()-1; ip>=0; ip--) {
+ pullFixedProc(pkgList, ip).mNumExcessiveCpu++;
+ }
+ }
+
+ private ProcessState pullFixedProc(ArrayMap<String, ProcessTracker.ProcessState> pkgList,
+ int index) {
+ ProcessState proc = pkgList.valueAt(index);
+ if (proc.mMultiPackage) {
+ // The array map is still pointing to a common process state
+ // that is now shared across packages. Update it to point to
+ // the new per-package state.
+ proc = mState.mPackages.get(pkgList.keyAt(index),
+ proc.mUid).mProcesses.get(proc.mName);
+ if (proc == null) {
+ throw new IllegalStateException("Didn't create per-package process");
+ }
+ pkgList.setValueAt(index, proc);
+ }
+ return proc;
+ }
+
long getDuration(int state, long now) {
int idx = State.binarySearch(mDurationsTable, mDurationsTableSize, state);
long time = idx >= 0 ? mState.getLong(mDurationsTable[idx], 0) : 0;
@@ -685,7 +735,7 @@ public final class ProcessTracker {
}
}
- static void dumpSingleTimeCsv(PrintWriter pw, String sep, long[] durations,
+ static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
int curState, long curStartTime, long now) {
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
@@ -694,8 +744,9 @@ public final class ProcessTracker {
if (curState == state) {
time += now - curStartTime;
}
- pw.print(sep);
- pw.print(time);
+ if (time != 0) {
+ printAdjTagAndValue(pw, state, time);
+ }
}
}
}
@@ -714,11 +765,11 @@ public final class ProcessTracker {
pw.print(",");
pw.print(serviceName);
pw.print(opCount);
- dumpSingleTimeCsv(pw, ",", durations, curState, curStartTime, now);
+ dumpAdjTimesCheckin(pw, ",", durations, curState, curStartTime, now);
pw.println();
}
- long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
+ static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates,
int[] procStates, long now) {
long totalTime = 0;
/*
@@ -756,10 +807,7 @@ public final class ProcessTracker {
PackageState state = procs.valueAt(iu);
for (int iproc=0; iproc<state.mProcesses.size(); iproc++) {
ProcessState proc = state.mProcesses.valueAt(iproc);
- if (proc.mCommonProcess != null) {
- proc = proc.mCommonProcess;
- }
- foundProcs.add(proc);
+ foundProcs.add(proc.mCommonProcess);
}
}
}
@@ -785,8 +833,8 @@ public final class ProcessTracker {
return outProcs;
}
- void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
- int[] memStates, int[] procStates, long now) {
+ static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
+ int[] screenStates, int[] memStates, int[] procStates, long now) {
long totalTime = 0;
int printedScreen = -1;
for (int is=0; is<screenStates.length; is++) {
@@ -833,7 +881,7 @@ public final class ProcessTracker {
}
}
- void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
+ static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates,
int[] memStates, int[] procStates) {
boolean printedHeader = false;
int printedScreen = -1;
@@ -877,9 +925,17 @@ public final class ProcessTracker {
}
}
}
+ if (proc.mNumExcessiveWake != 0) {
+ pw.print(prefix); pw.print("Killed for excessive wake locks: ");
+ pw.print(proc.mNumExcessiveWake); pw.println(" times");
+ }
+ if (proc.mNumExcessiveCpu != 0) {
+ pw.print(prefix); pw.print("Killed for excessive CPU use: ");
+ pw.print(proc.mNumExcessiveCpu); pw.println(" times");
+ }
}
- void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
+ static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
int[] memStates, int[] procStates) {
final int NS = screenStates != null ? screenStates.length : 1;
final int NM = memStates != null ? memStates.length : 1;
@@ -911,7 +967,7 @@ public final class ProcessTracker {
}
}
- void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
+ static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now) {
final int NSS = sepScreenStates ? screenStates.length : 1;
@@ -946,7 +1002,7 @@ public final class ProcessTracker {
}
}
- void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
+ static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs,
int[] screenStates, int[] memStates, int[] procStates, long now) {
String innerPrefix = prefix + " ";
for (int i=procs.size()-1; i>=0; i--) {
@@ -966,7 +1022,7 @@ public final class ProcessTracker {
}
}
- void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
+ static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now) {
pw.print("process");
@@ -1014,44 +1070,6 @@ public final class ProcessTracker {
return false;
}
- void dumpAllProcessState(PrintWriter pw, String prefix, ProcessState proc, long now) {
- long totalTime = 0;
- int printedScreen = -1;
- for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
- int printedMem = -1;
- for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
- for (int is=0; is<STATE_NAMES.length; is++) {
- int bucket = is+(STATE_COUNT*(imem+iscreen));
- long time = proc.getDuration(bucket, now);
- String running = "";
- if (proc.mCurState == bucket) {
- running = " (running)";
- }
- if (time != 0) {
- pw.print(prefix);
- printScreenLabel(pw, printedScreen != iscreen
- ? iscreen : STATE_NOTHING);
- printedScreen = iscreen;
- printMemLabel(pw, printedMem != imem
- ? imem : STATE_NOTHING);
- printedMem = imem;
- pw.print(STATE_NAMES[is]); pw.print(": ");
- TimeUtils.formatDuration(time, pw); pw.println(running);
- totalTime += time;
- }
- }
- }
- }
- if (totalTime != 0) {
- pw.print(prefix);
- printScreenLabel(pw, STATE_NOTHING);
- printMemLabel(pw, STATE_NOTHING);
- pw.print("TOTAL : ");
- TimeUtils.formatDuration(totalTime, pw);
- pw.println();
- }
- }
-
static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
int index = value/mod;
if (index >= 0 && index < array.length) {
@@ -1062,20 +1080,32 @@ public final class ProcessTracker {
return value - index*mod;
}
- void printProcStateTag(PrintWriter pw, int state) {
+ static void printProcStateTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
printArrayEntry(pw, STATE_TAGS, state, 1);
}
- void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
+ static void printAdjTag(PrintWriter pw, int state) {
+ state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD);
+ printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
+ }
+
+ static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
pw.print(',');
printProcStateTag(pw, state);
pw.print(':');
pw.print(value);
}
- void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
+ static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
+ pw.print(',');
+ printAdjTag(pw, state);
+ pw.print(':');
+ pw.print(value);
+ }
+
+ static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) {
boolean didCurState = false;
for (int i=0; i<proc.mDurationsTableSize; i++) {
int off = proc.mDurationsTable[i];
@@ -1092,7 +1122,7 @@ public final class ProcessTracker {
}
}
- void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc, long now) {
+ static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) {
for (int i=0; i<proc.mPssTableSize; i++) {
int off = proc.mPssTable[i];
int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
@@ -1154,7 +1184,7 @@ public final class ProcessTracker {
return finalRes;
}
- private void dumpHelp(PrintWriter pw) {
+ static private void dumpHelp(PrintWriter pw) {
pw.println("Process stats (procstats) dump options:");
pw.println(" [--checkin|--csv] [csv-screen] [csv-proc] [csv-mem]");
pw.println(" [--reset] [-h] [<package.name>]");
@@ -1165,6 +1195,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(" -a: print everything.");
pw.println(" -h: print this help text.");
pw.println(" <package.name>: optional name of package to filter output by.");
}
@@ -1174,6 +1205,7 @@ public final class ProcessTracker {
boolean isCheckin = false;
boolean isCsv = false;
+ boolean dumpAll = false;
String reqPackage = null;
boolean csvSepScreenStats = false;
int[] csvScreenStats = new int[] {ADJ_SCREEN_OFF, ADJ_SCREEN_ON};
@@ -1248,7 +1280,7 @@ public final class ProcessTracker {
dumpHelp(pw);
return;
} else if ("-a".equals(arg)) {
- // ignore
+ dumpAll = true;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
pw.println("Unknown option: " + arg);
dumpHelp(pw);
@@ -1332,8 +1364,6 @@ public final class ProcessTracker {
if (NPROCS > 0 || NSRVS > 0) {
if (!printedHeader) {
pw.println("Per-Package Process Stats:");
- pw.print(" Num long arrays: "); pw.println(mState.mLongs.size());
- pw.print(" Next long entry: "); pw.println(mState.mNextLong);
printedHeader = true;
}
pw.print(" * "); pw.print(pkgName); pw.print(" / ");
@@ -1349,7 +1379,10 @@ public final class ProcessTracker {
pw.print(proc.mDurationsTableSize);
pw.print(" entries)");
pw.println(":");
- dumpAllProcessState(pw, " ", proc, now);
+ dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES, now);
+ dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ,
+ ALL_PROC_STATES);
} else {
pw.print("pkgproc,");
pw.print(pkgName);
@@ -1359,6 +1392,29 @@ public final class ProcessTracker {
pw.print(state.mProcesses.keyAt(iproc));
dumpAllProcessStateCheckin(pw, proc, now);
pw.println();
+ if (proc.mPssTableSize > 0) {
+ pw.print("pkgpss,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(state.mProcesses.keyAt(iproc));
+ dumpAllProcessPssCheckin(pw, proc);
+ pw.println();
+ }
+ if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0) {
+ pw.print("pkgkills,");
+ pw.print(pkgName);
+ pw.print(",");
+ pw.print(uid);
+ pw.print(",");
+ pw.print(state.mProcesses.keyAt(iproc));
+ pw.print(",");
+ pw.print(proc.mNumExcessiveWake);
+ pw.print(",");
+ pw.print(proc.mNumExcessiveCpu);
+ pw.println();
+ }
}
}
for (int isvc=0; isvc<NSRVS; isvc++) {
@@ -1431,6 +1487,12 @@ public final class ProcessTracker {
pw.println("Run time Stats:");
dumpSingleTime(pw, " ", mState.mMemFactorDurations, mState.mMemFactor,
mState.mStartTime, now);
+ if (dumpAll) {
+ pw.println();
+ pw.println("Internal state:");
+ pw.print(" Num long arrays: "); pw.println(mState.mLongs.size());
+ pw.print(" Next long entry: "); pw.println(mState.mNextLong);
+ }
} else {
ArrayMap<String, SparseArray<ProcessState>> procMap = mState.mProcesses.getMap();
for (int ip=0; ip<procMap.size(); ip++) {
@@ -1452,13 +1514,24 @@ public final class ProcessTracker {
pw.print(procName);
pw.print(",");
pw.print(uid);
- dumpAllProcessPssCheckin(pw, state, now);
+ 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");
- dumpSingleTimeCsv(pw, ",", mState.mMemFactorDurations, mState.mMemFactor,
+ dumpAdjTimesCheckin(pw, ",", mState.mMemFactorDurations, mState.mMemFactor,
mState.mStartTime, now);
pw.println();
}