Log java crashes & native crashes in critical event log
Change-Id: I9efe1ff6c98dec47edbc95974d0959c343c7474f
Test: atest CriticalEventLogTest
Bug: 200263868
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 0e03434..25814ec 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -57,6 +57,8 @@
Watchdog watchdog = 2;
HalfWatchdog half_watchdog = 3;
AppNotResponding anr = 4;
+ JavaCrash java_crash = 5;
+ NativeCrash native_crash = 6;
}
message Watchdog {
@@ -99,6 +101,47 @@
optional ProcessClass process_class = 5;
}
+ message JavaCrash {
+ // The crash exception class.
+ // Optional, may be redacted for privacy.
+ optional string exception_class = 1;
+
+ // Name of the crashed process.
+ // Optional, may be redacted for privacy.
+ optional string process = 2;
+
+ // PID of the crashed process.
+ // Required.
+ optional int32 pid = 3;
+
+ // UID of the crashed process.
+ // Required.
+ optional int32 uid = 4;
+
+ // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 5;
+ }
+
+ message NativeCrash {
+ // Name of the crashed process.
+ // Optional, may be redacted for privacy.
+ optional string process = 1;
+
+ // PID of the crashed process.
+ // Required.
+ optional int32 pid = 2;
+
+ // UID of the crashed process.
+ // Required.
+ optional int32 uid = 3;
+
+ // Category of the crashed process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 4;
+ }
+
+
// Mirrors definition & values in {@link android.server.ServerProtoEnums}.
enum ProcessClass {
PROCESS_CLASS_UNKNOWN = 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b1a1e0d..191feec 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8018,11 +8018,16 @@
crashInfo.throwFileName,
crashInfo.throwLineNumber);
+ int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
+ : (r != null) ? r.getProcessClassEnum()
+ : ServerProtoEnums.ERROR_SOURCE_UNKNOWN;
+ int uid = (r != null) ? r.uid : -1;
+ int pid = (r != null) ? r.getPid() : -1;
FrameworkStatsLog.write(FrameworkStatsLog.APP_CRASH_OCCURRED,
- (r != null) ? r.uid : -1,
+ uid,
eventType,
processName,
- (r != null) ? r.getPid() : -1,
+ pid,
(r != null && r.info != null) ? r.info.packageName : "",
(r != null && r.info != null) ? (r.info.isInstantApp()
? FrameworkStatsLog.APP_CRASH_OCCURRED__IS_INSTANT_APP__TRUE
@@ -8032,9 +8037,7 @@
? FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__FOREGROUND
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__BACKGROUND)
: FrameworkStatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN,
- processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
- : (r != null) ? r.getProcessClassEnum()
- : ServerProtoEnums.ERROR_SOURCE_UNKNOWN,
+ processClassEnum,
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
@@ -8061,6 +8064,13 @@
: -1
);
+ if (eventType.equals("native_crash")) {
+ CriticalEventLog.getInstance().logNativeCrash(processClassEnum, processName, uid, pid);
+ } else if (eventType.equals("crash")) {
+ CriticalEventLog.getInstance().logJavaCrash(crashInfo.exceptionClassName,
+ processClassEnum, processName, uid, pid);
+ }
+
final int relaunchReason = r == null ? RELAUNCH_REASON_NONE
: r.getWindowProcessController().computeRelaunchReason();
final String relaunchReasonString = relaunchReasonToString(relaunchReason);
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 30b3524..ab480e8 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -29,6 +29,8 @@
import com.android.server.criticalevents.nano.CriticalEventProto;
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
import java.io.File;
@@ -179,8 +181,56 @@
log(event);
}
+ /**
+ * Logs a java crash.
+ *
+ * @param exceptionClass the crash exception class.
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+ * process.
+ * @param processName name of the crashed process.
+ * @param uid uid of the crashed process.
+ * @param pid pid of the crashed process.
+ */
+ public void logJavaCrash(String exceptionClass, int processClassEnum, String processName,
+ int uid, int pid) {
+ JavaCrash crash = new JavaCrash();
+ crash.exceptionClass = exceptionClass;
+ crash.processClass = processClassEnum;
+ crash.process = processName;
+ crash.uid = uid;
+ crash.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setJavaCrash(crash);
+ log(event);
+ }
+
+ /**
+ * Logs a native crash.
+ *
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the crashed
+ * process.
+ * @param processName name of the crashed process.
+ * @param uid uid of the crashed process.
+ * @param pid pid of the crashed process.
+ */
+ public void logNativeCrash(int processClassEnum, String processName, int uid, int pid) {
+ NativeCrash crash = new NativeCrash();
+ crash.processClass = processClassEnum;
+ crash.process = processName;
+ crash.uid = uid;
+ crash.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setNativeCrash(crash);
+ log(event);
+ }
+
private void log(CriticalEventProto event) {
event.timestampMs = getWallTimeMillis();
+ appendAndSave(event);
+ }
+
+ @VisibleForTesting
+ void appendAndSave(CriticalEventProto event) {
mEvents.append(event);
saveLogToFile();
}
@@ -420,7 +470,18 @@
if (shouldSanitize(anr.processClass, anr.process, anr.uid)) {
return sanitizeAnr(event);
}
+ } else if (event.hasJavaCrash()) {
+ JavaCrash crash = event.getJavaCrash();
+ if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+ return sanitizeJavaCrash(event);
+ }
+ } else if (event.hasNativeCrash()) {
+ NativeCrash crash = event.getNativeCrash();
+ if (shouldSanitize(crash.processClass, crash.process, crash.uid)) {
+ return sanitizeNativeCrash(event);
+ }
}
+ // No redaction needed.
return event;
}
@@ -428,21 +489,52 @@
boolean sameApp = processName != null && processName.equals(mTraceProcessName)
&& mTraceUid == uid;
- // Only sanitize when both the ANR event and trace file are for different data apps.
+ // Only sanitize when both the critical event and trace file are for different data
+ // apps.
return processClassEnum == CriticalEventProto.DATA_APP
&& mTraceProcessClassEnum == CriticalEventProto.DATA_APP
&& !sameApp;
}
private static CriticalEventProto sanitizeAnr(CriticalEventProto base) {
- CriticalEventProto sanitized = new CriticalEventProto();
- sanitized.timestampMs = base.timestampMs;
AppNotResponding anr = new AppNotResponding();
- sanitized.setAnr(anr);
// Do not set subject and process.
anr.processClass = base.getAnr().processClass;
anr.uid = base.getAnr().uid;
anr.pid = base.getAnr().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setAnr(anr);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeJavaCrash(CriticalEventProto base) {
+ JavaCrash crash = new JavaCrash();
+ // Do not set exceptionClass and process.
+ crash.processClass = base.getJavaCrash().processClass;
+ crash.uid = base.getJavaCrash().uid;
+ crash.pid = base.getJavaCrash().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setJavaCrash(crash);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeNativeCrash(CriticalEventProto base) {
+ NativeCrash crash = new NativeCrash();
+ // Do not set process.
+ crash.processClass = base.getNativeCrash().processClass;
+ crash.uid = base.getNativeCrash().uid;
+ crash.pid = base.getNativeCrash().pid;
+
+ CriticalEventProto sanitized = sanitizeCriticalEventProto(base);
+ sanitized.setNativeCrash(crash);
+ return sanitized;
+ }
+
+ private static CriticalEventProto sanitizeCriticalEventProto(CriticalEventProto base) {
+ CriticalEventProto sanitized = new CriticalEventProto();
+ sanitized.timestampMs = base.timestampMs;
return sanitized;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
index 54e75aa..bf7c587 100644
--- a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -29,6 +29,8 @@
import com.android.server.criticalevents.nano.CriticalEventProto;
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
+import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.Watchdog;
import org.junit.Before;
@@ -255,24 +257,39 @@
}
@Test
- public void privacyRedaction_anr() {
+ public void logJavaCrash() {
mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
- SYSTEM_SERVER_UID, 0);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 2", ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
- SYSTEM_APP_UID, 1);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 3", ServerProtoEnums.DATA_APP, "com.foo",
- DATA_APP_UID, 2);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 4", ServerProtoEnums.DATA_APP, "com.foo",
- DATA_APP_UID_2, 3);
- mCriticalEventLog.incTimeSeconds(1);
- mCriticalEventLog.logAnr("Subject 5", ServerProtoEnums.DATA_APP, "com.bar",
- DATA_APP_UID_3, 4);
+ mCriticalEventLog.logJavaCrash("com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1);
mCriticalEventLog.incTimeSeconds(1);
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ javaCrash(START_TIME_MS + 1000, "com.android.MyClass", ServerProtoEnums.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1)
+ });
+ }
+
+ @Test
+ public void logNativeCrash() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logNativeCrash(ServerProtoEnums.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID,
+ 1);
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ nativeCrash(START_TIME_MS + 1000, ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID, 1)
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr() {
CriticalEventProto systemServerAnr = anr(START_TIME_MS + 1000, "Subject 1",
CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID, 0);
CriticalEventProto systemAppAnr = anr(START_TIME_MS + 2000, "Subject 2",
@@ -289,6 +306,8 @@
CriticalEventProto barAppAnrRedacted = anr(START_TIME_MS + 5000, "",
CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+ addToLog(systemServerAnr, systemAppAnr, fooAppAnr, fooAppAnrUid2, barAppAnr);
+
assertProtoArrayEquals(
getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
new CriticalEventProto[]{
@@ -325,9 +344,123 @@
}
@Test
- public void privacyRedaction_anr_doesNotMutateLogState() {
- mCriticalEventLog.logAnr("Subject", ServerProtoEnums.DATA_APP, "com.foo",
+ public void privacyRedaction_javaCrash() {
+ CriticalEventProto systemServerCrash = javaCrash(START_TIME_MS + 1000, "Exception class 1",
+ CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ CriticalEventProto systemAppCrash = javaCrash(START_TIME_MS + 2000, "Exception class 2",
+ CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppCrash = javaCrash(START_TIME_MS + 3000, "Exception class 3",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppCrashUid2 = javaCrash(START_TIME_MS + 4000, "Exception class 4",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppCrashUid2Redacted = javaCrash(START_TIME_MS + 4000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppCrash = javaCrash(START_TIME_MS + 5000, "Exception class 5",
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppCrashRedacted = javaCrash(START_TIME_MS + 5000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ // Redacted since the trace file and crash are for different uids.
+ fooAppCrashUid2Redacted,
+ // Redacted since the trace file and crash are for different data apps.
+ barAppCrashRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+ }
+
+ @Test
+ public void privacyRedaction_nativeCrash() {
+ CriticalEventProto systemServerCrash = nativeCrash(START_TIME_MS + 1000,
+ CriticalEventProto.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ CriticalEventProto systemAppCrash = nativeCrash(START_TIME_MS + 2000,
+ CriticalEventProto.SYSTEM_APP, "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppCrash = nativeCrash(START_TIME_MS + 3000,
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppCrashUid2 = nativeCrash(START_TIME_MS + 4000,
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppCrashUid2Redacted = nativeCrash(START_TIME_MS + 4000,
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppCrash = nativeCrash(START_TIME_MS + 5000,
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppCrashRedacted = nativeCrash(START_TIME_MS + 5000,
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ addToLog(systemServerCrash, systemAppCrash, fooAppCrash, fooAppCrashUid2, barAppCrash);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ // Redacted since the trace file and crash are for different uids.
+ fooAppCrashUid2Redacted,
+ // Redacted since the trace file and crash are for different data apps.
+ barAppCrashRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerCrash,
+ systemAppCrash,
+ fooAppCrash,
+ fooAppCrashUid2,
+ barAppCrash
+ });
+ }
+
+ @Test
+ public void privacyRedaction_doesNotMutateLogState() {
+ mCriticalEventLog.logAnr("ANR Subject", ServerProtoEnums.DATA_APP, "com.foo",
10_001, DATA_APP_UID);
+ mCriticalEventLog.logJavaCrash("com.foo.MyClass", ServerProtoEnums.DATA_APP, "com.foo",
+ 10_001, DATA_APP_UID);
+ mCriticalEventLog.logNativeCrash(ServerProtoEnums.DATA_APP, "com.foo", 10_001,
+ DATA_APP_UID);
CriticalEventLogProto unredactedLogBefore = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
"AID_SYSTEM", SYSTEM_SERVER_UID);
@@ -488,6 +621,12 @@
ServerProtoEnums.SYSTEM_SERVER);
}
+ private void addToLog(CriticalEventProto... events) {
+ for (CriticalEventProto event : events) {
+ mCriticalEventLog.appendAndSave(event);
+ }
+ }
+
private CriticalEventLogProto getLogOutput() {
return getLogOutput(mCriticalEventLog);
}
@@ -533,9 +672,29 @@
}
}
+ private static CriticalEventProto watchdog(long timestampMs, String subject) {
+ return watchdog(timestampMs, subject, "A UUID");
+ }
+
+ private static CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setWatchdog(new Watchdog());
+ event.getWatchdog().subject = subject;
+ event.getWatchdog().uuid = uuid;
+ return event;
+ }
+
+ private static CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setHalfWatchdog(new HalfWatchdog());
+ event.getHalfWatchdog().subject = subject;
+ return event;
+ }
+
private static CriticalEventProto anr(long timestampMs, String subject, int processClass,
- String processName,
- int uid, int pid) {
+ String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
event.setAnr(new AppNotResponding());
@@ -547,24 +706,28 @@
return event;
}
- private CriticalEventProto watchdog(long timestampMs, String subject) {
- return watchdog(timestampMs, subject, "A UUID");
- }
-
- private CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+ private static CriticalEventProto javaCrash(long timestampMs, String exceptionClass,
+ int processClass, String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
- event.setWatchdog(new Watchdog());
- event.getWatchdog().subject = subject;
- event.getWatchdog().uuid = uuid;
+ event.setJavaCrash(new JavaCrash());
+ event.getJavaCrash().exceptionClass = exceptionClass;
+ event.getJavaCrash().processClass = processClass;
+ event.getJavaCrash().process = processName;
+ event.getJavaCrash().uid = uid;
+ event.getJavaCrash().pid = pid;
return event;
}
- private CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+ private static CriticalEventProto nativeCrash(long timestampMs, int processClass,
+ String processName, int uid, int pid) {
CriticalEventProto event = new CriticalEventProto();
event.timestampMs = timestampMs;
- event.setHalfWatchdog(new HalfWatchdog());
- event.getHalfWatchdog().subject = subject;
+ event.setNativeCrash(new NativeCrash());
+ event.getNativeCrash().processClass = processClass;
+ event.getNativeCrash().process = processName;
+ event.getNativeCrash().uid = uid;
+ event.getNativeCrash().pid = pid;
return event;
}