Merge "Start really collecting PSS data for process stats."
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bf89fa6..4568525 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -552,7 +552,7 @@
private static final String DB_INFO_FORMAT = " %8s %8s %14s %14s %s";
// Formatting for checkin service - update version if row format changes
- private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 2;
+ private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
private void updatePendingConfiguration(Configuration config) {
synchronized (mPackages) {
@@ -972,43 +972,48 @@
pw.print(memInfo.nativePss); pw.print(',');
pw.print(memInfo.dalvikPss); pw.print(',');
pw.print(memInfo.otherPss); pw.print(',');
- pw.print(memInfo.nativePss + memInfo.dalvikPss + memInfo.otherPss); pw.print(',');
+ pw.print(memInfo.getTotalPss()); pw.print(',');
- // Heap info - proportional set size
+ // Heap info - swappable set size
pw.print(memInfo.nativeSwappablePss); pw.print(',');
pw.print(memInfo.dalvikSwappablePss); pw.print(',');
pw.print(memInfo.otherSwappablePss); pw.print(',');
- pw.print(memInfo.nativeSwappablePss + memInfo.dalvikSwappablePss + memInfo.otherSwappablePss); pw.print(',');
+ pw.print(memInfo.getTotalSwappablePss()); pw.print(',');
// Heap info - shared dirty
pw.print(memInfo.nativeSharedDirty); pw.print(',');
pw.print(memInfo.dalvikSharedDirty); pw.print(',');
pw.print(memInfo.otherSharedDirty); pw.print(',');
- pw.print(memInfo.nativeSharedDirty + memInfo.dalvikSharedDirty
- + memInfo.otherSharedDirty); pw.print(',');
+ pw.print(memInfo.getTotalSharedDirty()); pw.print(',');
// Heap info - shared clean
pw.print(memInfo.nativeSharedClean); pw.print(',');
pw.print(memInfo.dalvikSharedClean); pw.print(',');
pw.print(memInfo.otherSharedClean); pw.print(',');
- pw.print(memInfo.nativeSharedClean + memInfo.dalvikSharedClean
- + memInfo.otherSharedClean); pw.print(',');
+ pw.print(memInfo.getTotalSharedClean()); pw.print(',');
// Heap info - private Dirty
pw.print(memInfo.nativePrivateDirty); pw.print(',');
pw.print(memInfo.dalvikPrivateDirty); pw.print(',');
pw.print(memInfo.otherPrivateDirty); pw.print(',');
- pw.print(memInfo.nativePrivateDirty + memInfo.dalvikPrivateDirty
- + memInfo.otherPrivateDirty); pw.print(',');
-
+ pw.print(memInfo.getTotalPrivateDirty()); pw.print(',');
// Heap info - private Clean
pw.print(memInfo.nativePrivateClean); pw.print(',');
pw.print(memInfo.dalvikPrivateClean); pw.print(',');
pw.print(memInfo.otherPrivateClean); pw.print(',');
- pw.print(memInfo.nativePrivateClean + memInfo.dalvikPrivateClean
- + memInfo.otherPrivateClean); pw.print(',');
+ pw.print(memInfo.getTotalPrivateClean()); pw.print(',');
+ // Heap info - other areas
+ for (int i=0; i<Debug.MemoryInfo.NUM_OTHER_STATS; i++) {
+ pw.print(Debug.MemoryInfo.getOtherLabel(i)); pw.print(',');
+ pw.print(memInfo.getOtherPss(i)); pw.print(',');
+ pw.print(memInfo.getOtherSwappablePss(i)); pw.print(',');
+ pw.print(memInfo.getOtherSharedDirty(i)); pw.print(',');
+ pw.print(memInfo.getOtherSharedClean(i)); pw.print(',');
+ pw.print(memInfo.getOtherPrivateDirty(i)); pw.print(',');
+ pw.print(memInfo.getOtherPrivateClean(i)); pw.print(',');
+ }
// Object counts
pw.print(viewInstanceCount); pw.print(',');
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index f4cf2ee..8b7f3dd 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -394,12 +394,16 @@
if (fp == 0) return 0;
while (true) {
- if (fgets(line, 1024, fp) == 0) {
+ if (fgets(line, 1024, fp) == NULL) {
break;
}
- if (sscanf(line, "Pss: %d kB", &temp) == 1) {
- pss += temp;
+ if (strncmp(line, "Pss: ", 5) == 0) {
+ char* c = line + 5;
+ while (*c != 0 && (*c < '0' || *c > '9')) {
+ c++;
+ }
+ pss += atoi(c);
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index ad6ae43..ed9416e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
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 @@
// 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 @@
/**
* 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 @@
}
};
+ 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 @@
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 @@
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 @@
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 @@
// 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 @@
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);
@@ -10956,7 +11068,7 @@
long uptime = SystemClock.uptimeMillis();
long realtime = SystemClock.elapsedRealtime();
- if (procs.size() == 1 || isCheckinRequest) {
+ if (!brief && !oomOnly && (procs.size() == 1 || isCheckinRequest)) {
dumpDetails = true;
}
@@ -10982,17 +11094,24 @@
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!");
@@ -11000,21 +11119,31 @@
}
}
} 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;
@@ -11092,7 +11221,7 @@
}
}
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(" ");
@@ -11102,10 +11231,10 @@
}
}
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) {
@@ -11131,7 +11260,7 @@
}
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:");
@@ -11139,29 +11268,31 @@
}
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");
+ }
}
}
@@ -11258,6 +11389,7 @@
}
mProcessesToGc.remove(app);
+ mPendingPssProcesses.remove(app);
// Dismiss any open dialogs.
if (app.crashDialog != null && !app.forceCrashReport) {
@@ -11276,7 +11408,7 @@
app.crashing = false;
app.notResponding = false;
- app.resetPackageList();
+ app.resetPackageList(mProcessTracker);
app.unlinkDeathRecipient();
app.thread = null;
app.forcingToForeground = null;
@@ -13771,6 +13903,41 @@
}
/**
+ * 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) {
@@ -13981,6 +14148,7 @@
+ " 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) {
@@ -13993,6 +14161,7 @@
+ " 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;
@@ -14034,11 +14203,23 @@
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) {
@@ -14428,6 +14609,9 @@
}
}
}
+ 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 f730e07..973b9aa 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -222,7 +222,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
curAdj = setAdj = -100;
persistent = false;
removed = false;
+ lastPssTime = SystemClock.uptimeMillis();
}
public void setPid(int _pid) {
@@ -451,21 +458,20 @@
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 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 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 @@
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 @@
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 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 @@
pnew.mPssTableSize = mState.mFindTableSize;
}
*/
+ pnew.mNumExcessiveWake = mNumExcessiveWake;
+ pnew.mNumExcessiveCpu = mNumExcessiveCpu;
return pnew;
}
@@ -208,30 +224,16 @@
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 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 @@
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 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 @@
}
}
- 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 @@
if (curState == state) {
time += now - curStartTime;
}
- pw.print(sep);
- pw.print(time);
+ if (time != 0) {
+ printAdjTagAndValue(pw, state, time);
+ }
}
}
}
@@ -714,11 +765,11 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
}
}
}
+ 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 @@
}
}
- 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 @@
}
}
- 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 @@
}
}
- 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 @@
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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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();
}