ActivityManager: Add dumpsys command to report number of lmkd kills
Add support for "dumpsys activity lmk" command to report the number
of lmk kills. This change also enables two-way communicatoin between
ActivityManager and lmkd, introduces new LMK_GETKILLCNT command in lmkd
communication protocol and makes writeLmkd function synchronized to
prevent races.
Usage example:
#adb shell dumpsys activity lmk
ACTIVITY MANAGER LMK KILLS (dumpsys activity lmk)
Total number of kills: 55
kills at or below oom_adj 906: 55
kills at or below oom_adj 900: 40
kills at or below oom_adj 800: 38
kills at or below oom_adj 700: 22
kills at or below oom_adj 600: 19
kills at or below oom_adj 500: 18
kills at or below oom_adj 400: 13
kills at or below oom_adj 300: 12
kills at or below oom_adj 200: 10
kills at or below oom_adj 100: 8
kills at or below oom_adj 0: 1
Bug: 117126077
Test: used lmkd_unit_test to verify correct reporting
Change-Id: I71671b59040d670655171dacf4e7615b76398aab
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 88558b4..6aa049d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9147,6 +9147,11 @@
pw.println("-------------------------------------------------------------------------------");
}
dumpBinderProxies(pw);
+ pw.println();
+ if (dumpAll) {
+ pw.println("-------------------------------------------------------------------------------");
+ }
+ dumpLmkLocked(pw);
}
}
@@ -9337,6 +9342,10 @@
synchronized (this) {
dumpOomLocked(fd, pw, args, opti, true);
}
+ } else if ("lmk".equals(cmd)) {
+ synchronized (this) {
+ dumpLmkLocked(pw);
+ }
} else if ("permissions".equals(cmd) || "perm".equals(cmd)) {
synchronized (this) {
dumpPermissionsLocked(fd, pw, args, opti, true, null);
@@ -10371,6 +10380,37 @@
return true;
}
+ private boolean reportLmkKillAtOrBelow(PrintWriter pw, int oom_adj) {
+ Integer cnt = ProcessList.getLmkdKillCount(0, oom_adj);
+ if (cnt != null) {
+ pw.println(" kills at or below oom_adj " + oom_adj + ": " + cnt);
+ return true;
+ }
+ return false;
+ }
+
+ boolean dumpLmkLocked(PrintWriter pw) {
+ pw.println("ACTIVITY MANAGER LMK KILLS (dumpsys activity lmk)");
+ Integer cnt = ProcessList.getLmkdKillCount(ProcessList.UNKNOWN_ADJ,
+ ProcessList.UNKNOWN_ADJ);
+ if (cnt == null) {
+ return false;
+ }
+ pw.println(" Total number of kills: " + cnt);
+
+ return reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MAX_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.CACHED_APP_MIN_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_B_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.PREVIOUS_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.HOME_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.SERVICE_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.HEAVY_WEIGHT_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.BACKUP_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.PERCEPTIBLE_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.VISIBLE_APP_ADJ) &&
+ reportLmkKillAtOrBelow(pw, ProcessList.FOREGROUND_APP_ADJ);
+ }
+
/**
* There are three ways to call this:
* - no provider specified: dump all the providers
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 652d39b..6741dc0 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -99,14 +99,22 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import libcore.io.IoUtils;
+
/**
* Activity manager code dealing with processes.
+ *
+ * Method naming convention:
+ * <ul>
+ * <li> Methods suffixed with "LS" should be called within the {@link #sLmkdSocketLock} lock.
+ * </ul>
*/
public final class ProcessList {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
@@ -229,10 +237,12 @@
// LMK_PROCPRIO <pid> <uid> <prio>
// LMK_PROCREMOVE <pid>
// LMK_PROCPURGE
+ // LMK_GETKILLCNT
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
static final byte LMK_PROCPURGE = 3;
+ static final byte LMK_GETKILLCNT = 4;
ActivityManagerService mService = null;
@@ -268,9 +278,17 @@
private boolean mHaveDisplaySize;
+ private static Object sLmkdSocketLock = new Object();
+
+ @GuardedBy("sLmkdSocketLock")
private static LocalSocket sLmkdSocket;
+
+ @GuardedBy("sLmkdSocketLock")
private static OutputStream sLmkdOutputStream;
+ @GuardedBy("sLmkdSocketLock")
+ private static InputStream sLmkdInputStream;
+
/**
* Temporary to avoid allocations. Protected by main lock.
*/
@@ -505,7 +523,7 @@
buf.putInt(mOomAdj[i]);
}
- writeLmkd(buf);
+ writeLmkd(buf, null);
SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
}
// GB: 2048,3072,4096,6144,7168,8192
@@ -978,7 +996,7 @@
buf.putInt(pid);
buf.putInt(uid);
buf.putInt(amt);
- writeLmkd(buf);
+ writeLmkd(buf, null);
long now = SystemClock.elapsedRealtime();
if ((now-start) > 250) {
Slog.w("ActivityManager", "SLOW OOM ADJ: " + (now-start) + "ms for pid " + pid
@@ -997,16 +1015,38 @@
ByteBuffer buf = ByteBuffer.allocate(4 * 2);
buf.putInt(LMK_PROCREMOVE);
buf.putInt(pid);
- writeLmkd(buf);
+ writeLmkd(buf, null);
}
- private static boolean openLmkdSocket() {
+ /*
+ * {@hide}
+ */
+ public static final Integer getLmkdKillCount(int min_oom_adj, int max_oom_adj) {
+ ByteBuffer buf = ByteBuffer.allocate(4 * 3);
+ ByteBuffer repl = ByteBuffer.allocate(4 * 2);
+ buf.putInt(LMK_GETKILLCNT);
+ buf.putInt(min_oom_adj);
+ buf.putInt(max_oom_adj);
+ if (writeLmkd(buf, repl)) {
+ int i = repl.getInt();
+ if (i != LMK_GETKILLCNT) {
+ Slog.e("ActivityManager", "Failed to get kill count, code mismatch");
+ return null;
+ }
+ return new Integer(repl.getInt());
+ }
+ return null;
+ }
+
+ @GuardedBy("sLmkdSocketLock")
+ private static boolean openLmkdSocketLS() {
try {
sLmkdSocket = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
sLmkdSocket.connect(
new LocalSocketAddress("lmkd",
LocalSocketAddress.Namespace.RESERVED));
sLmkdOutputStream = sLmkdSocket.getOutputStream();
+ sLmkdInputStream = sLmkdSocket.getInputStream();
} catch (IOException ex) {
Slog.w(TAG, "lowmemorykiller daemon socket open failed");
sLmkdSocket = null;
@@ -1017,47 +1057,63 @@
}
// Never call directly, use writeLmkd() instead
- private static boolean writeLmkdCommand(ByteBuffer buf) {
+ @GuardedBy("sLmkdSocketLock")
+ private static boolean writeLmkdCommandLS(ByteBuffer buf) {
try {
sLmkdOutputStream.write(buf.array(), 0, buf.position());
} catch (IOException ex) {
Slog.w(TAG, "Error writing to lowmemorykiller socket");
-
- try {
- sLmkdSocket.close();
- } catch (IOException ex2) {
- }
-
+ IoUtils.closeQuietly(sLmkdSocket);
sLmkdSocket = null;
return false;
}
return true;
}
- private static void writeLmkd(ByteBuffer buf) {
-
- for (int i = 0; i < 3; i++) {
- if (sLmkdSocket == null) {
- if (openLmkdSocket() == false) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- }
- continue;
- }
-
- // Purge any previously registered pids
- ByteBuffer purge_buf = ByteBuffer.allocate(4);
- purge_buf.putInt(LMK_PROCPURGE);
- if (writeLmkdCommand(purge_buf) == false) {
- // Write failed, skip the rest and retry
- continue;
- }
+ // Never call directly, use writeLmkd() instead
+ @GuardedBy("sLmkdSocketLock")
+ private static boolean readLmkdReplyLS(ByteBuffer buf) {
+ int len;
+ try {
+ len = sLmkdInputStream.read(buf.array(), 0, buf.array().length);
+ if (len == buf.array().length) {
+ return true;
}
- if (writeLmkdCommand(buf)) {
- return;
+ } catch (IOException ex) {
+ Slog.w(TAG, "Error reading from lowmemorykiller socket");
+ }
+
+ IoUtils.closeQuietly(sLmkdSocket);
+ sLmkdSocket = null;
+ return false;
+ }
+
+ private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
+ synchronized (sLmkdSocketLock) {
+ for (int i = 0; i < 3; i++) {
+ if (sLmkdSocket == null) {
+ if (openLmkdSocketLS() == false) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ }
+ continue;
+ }
+
+ // Purge any previously registered pids
+ ByteBuffer purge_buf = ByteBuffer.allocate(4);
+ purge_buf.putInt(LMK_PROCPURGE);
+ if (writeLmkdCommandLS(purge_buf) == false) {
+ // Write failed, skip the rest and retry
+ continue;
+ }
+ }
+ if (writeLmkdCommandLS(buf) && (repl == null || readLmkdReplyLS(repl))) {
+ return true;
+ }
}
}
+ return false;
}
static void killProcessGroup(int uid, int pid) {