Log ANRs in critical event log
Change-Id: I4a851cb57f8624c44b06b8c243bf1671aedbfaea
Test: atest CriticalEventLogTest
Bug: b/200263868
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index 27787c2..0e03434 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -56,6 +56,7 @@
oneof event {
Watchdog watchdog = 2;
HalfWatchdog half_watchdog = 3;
+ AppNotResponding anr = 4;
}
message Watchdog {
@@ -75,4 +76,34 @@
// Required.
optional string subject = 1;
}
+
+ message AppNotResponding {
+ // The ANR subject.
+ // Optional, may be redacted for privacy.
+ optional string subject = 1;
+
+ // Name of the ANRing process.
+ // Optional, may be redacted for privacy.
+ optional string process = 2;
+
+ // PID of the ANRing process.
+ // Required.
+ optional int32 pid = 3;
+
+ // UID of the ANRing process.
+ // Required.
+ optional int32 uid = 4;
+
+ // Category of the ANRing process (DATA_APP, SYSTEM_APP, etc).
+ // Required.
+ optional ProcessClass process_class = 5;
+ }
+
+ // Mirrors definition & values in {@link android.server.ServerProtoEnums}.
+ enum ProcessClass {
+ PROCESS_CLASS_UNKNOWN = 0;
+ DATA_APP = 1;
+ SYSTEM_APP = 2;
+ SYSTEM_SERVER = 3;
+ }
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 0fde6fa..b019789 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -666,7 +666,8 @@
if (doWaitedHalfDump) {
// Get critical event log before logging the half watchdog so that it doesn't
// occur in the log.
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
CriticalEventLog.getInstance().logHalfWatchdog(subject);
// We've waited half the deadlock-detection interval. Pull a stack
@@ -693,7 +694,8 @@
// Get critical event log before logging the watchdog so that it doesn't occur in the
// log.
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
CriticalEventLog.getInstance().logWatchdog(subject, errorId);
long anrTime = SystemClock.uptimeMillis();
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index b7ea3dc..4220506 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -332,6 +332,13 @@
}
}
+ // Get critical event log before logging the ANR so that it doesn't occur in the log.
+ final String criticalEventLog =
+ CriticalEventLog.getInstance().logLinesForTraceFile(
+ mApp.getProcessClassEnum(), mApp.processName, mApp.uid);
+ CriticalEventLog.getInstance().logAnr(annotation, mApp.getProcessClassEnum(),
+ mApp.processName, mApp.uid, mApp.mPid);
+
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
@@ -401,7 +408,6 @@
StringWriter tracesFileException = new StringWriter();
// To hold the start and end offset to the ANR trace file respectively.
final long[] offsets = new long[2];
- final String criticalEventLog = CriticalEventLog.getInstance().logLinesForAnrFile();
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePids, tracesFileException, offsets, annotation, criticalEventLog);
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index d5fe9c9..30b3524 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -18,6 +18,7 @@
import android.os.Handler;
import android.os.HandlerThread;
+import android.server.ServerProtoEnums;
import android.util.Slog;
import com.android.framework.protobuf.nano.MessageNanoPrinter;
@@ -26,6 +27,7 @@
import com.android.server.criticalevents.nano.CriticalEventLogProto;
import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
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.Watchdog;
@@ -49,6 +51,9 @@
public class CriticalEventLog {
private static final String TAG = CriticalEventLog.class.getSimpleName();
+ /** UID for system_server. */
+ private static final int AID_SYSTEM = 1000;
+
private static CriticalEventLog sInstance;
/** Name of the file the log is saved to. */
@@ -153,6 +158,27 @@
log(event);
}
+ /**
+ * Logs an ANR.
+ *
+ * @param subject the ANR subject line.
+ * @param processClassEnum {@link android.server.ServerProtoEnums} value for the ANRing process.
+ * @param processName name of the ANRing process.
+ * @param uid uid of the ANRing process.
+ * @param pid pid of the ANRing process.
+ */
+ public void logAnr(String subject, int processClassEnum, String processName, int uid, int pid) {
+ AppNotResponding anr = new AppNotResponding();
+ anr.subject = subject;
+ anr.processClass = processClassEnum;
+ anr.process = processName;
+ anr.uid = uid;
+ anr.pid = pid;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setAnr(anr);
+ log(event);
+ }
+
private void log(CriticalEventProto event) {
event.timestampMs = getWallTimeMillis();
mEvents.append(event);
@@ -160,14 +186,37 @@
}
/**
- * Returns recent critical events in text format to include in logs such as ANR files.
+ * Returns recent critical events in text format to include in system server ANR stack trace
+ * file.
*
* Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
*/
- public String logLinesForAnrFile() {
+ public String logLinesForSystemServerTraceFile() {
+ return logLinesForTraceFile(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM", AID_SYSTEM);
+ }
+
+ /**
+ * Returns recent critical events in text format to include in logs such as ANR stack trace
+ * files.
+ *
+ * Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
+ *
+ * Some data in the returned log may be redacted for privacy. For example, a log for a data
+ * app will not include specific crash information for a different data app. See
+ * {@link LogSanitizer} for more information.
+ *
+ * @param traceProcessClassEnum {@link android.server.ServerProtoEnums} value for the process
+ * the ANR trace file is for.
+ * @param traceProcessName name of the process the ANR trace file is for.
+ * @param traceUid uid of the process the ANR trace file is for.
+ */
+ public String logLinesForTraceFile(int traceProcessClassEnum, String traceProcessName,
+ int traceUid) {
+ CriticalEventLogProto outputLogProto = getOutputLogProto(traceProcessClassEnum,
+ traceProcessName, traceUid);
return new StringBuilder()
.append("--- CriticalEventLog ---\n")
- .append(MessageNanoPrinter.print(getRecentEvents()))
+ .append(MessageNanoPrinter.print(outputLogProto))
.append('\n').toString();
}
@@ -177,12 +226,20 @@
* Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
*/
@VisibleForTesting
- protected CriticalEventLogProto getRecentEvents() {
+ protected CriticalEventLogProto getOutputLogProto(int traceProcessClassEnum,
+ String traceProcessName, int traceUid) {
CriticalEventLogProto log = new CriticalEventLogProto();
log.timestampMs = getWallTimeMillis();
log.windowMs = mWindowMs;
log.capacity = mEvents.capacity();
- log.events = recentEventsWithMinTimestamp(log.timestampMs - mWindowMs);
+
+ CriticalEventProto[] events = recentEventsWithMinTimestamp(log.timestampMs - mWindowMs);
+ LogSanitizer sanitizer = new LogSanitizer(traceProcessClassEnum, traceProcessName,
+ traceUid);
+ for (int i = 0; i < events.length; i++) {
+ events[i] = sanitizer.process(events[i]);
+ }
+ log.events = events;
return log;
}
@@ -325,4 +382,68 @@
}
}
}
+
+ /**
+ * Redacts private data app fields from the critical event protos.
+ *
+ * When a critical event log is requested, this class is used to redact specific information
+ * so that the trace file for a data app does not leak private information about other data
+ * apps.
+ */
+ private static class LogSanitizer {
+ /**
+ * The {@link CriticalEventProto.ProcessClass} of the process the output trace file is for.
+ */
+ int mTraceProcessClassEnum;
+
+ /** The name of the process that the output trace file is for. */
+ String mTraceProcessName;
+
+ /** The uid of the process that the output trace file is for. */
+ int mTraceUid;
+
+ LogSanitizer(int traceProcessClassEnum, String traceProcessName, int traceUid) {
+ mTraceProcessClassEnum = traceProcessClassEnum;
+ mTraceProcessName = traceProcessName;
+ mTraceUid = traceUid;
+ }
+
+ /**
+ * Redacts information from a critical event proto where necessary.
+ *
+ * This function does not mutate its input. If redaction happens, it returns a new proto.
+ * Otherwise, it returns the original proto.
+ */
+ CriticalEventProto process(CriticalEventProto event) {
+ if (event.hasAnr()) {
+ AppNotResponding anr = event.getAnr();
+ if (shouldSanitize(anr.processClass, anr.process, anr.uid)) {
+ return sanitizeAnr(event);
+ }
+ }
+ return event;
+ }
+
+ private boolean shouldSanitize(int processClassEnum, String processName, int uid) {
+ boolean sameApp = processName != null && processName.equals(mTraceProcessName)
+ && mTraceUid == uid;
+
+ // Only sanitize when both the ANR 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;
+ return sanitized;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index fa43b39..98cd40a 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -213,7 +213,7 @@
}
}
- String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ String criticalEvents = CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
null /* processCpuTracker */, null /* lastPids */, nativePids,
null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents);
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 dca666c..54e75aa 100644
--- a/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
+++ b/services/tests/servicestests/src/com/android/server/criticalevents/CriticalEventLogTest.java
@@ -19,12 +19,15 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.server.ServerProtoEnums;
+
import com.android.framework.protobuf.nano.MessageNano;
import com.android.server.criticalevents.CriticalEventLog.ILogLoader;
import com.android.server.criticalevents.CriticalEventLog.LogLoader;
import com.android.server.criticalevents.nano.CriticalEventLogProto;
import com.android.server.criticalevents.nano.CriticalEventLogStorageProto;
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.Watchdog;
@@ -63,6 +66,13 @@
private static final String UUID_STRING = "123e4567-e89b-12d3-a456-556642440000";
+ private static final int SYSTEM_SERVER_UID = 1000;
+ private static final int SYSTEM_APP_UID = 1001;
+
+ private static final int DATA_APP_UID = 10_001;
+ private static final int DATA_APP_UID_2 = 10_002;
+ private static final int DATA_APP_UID_3 = 10_003;
+
@Rule
public TemporaryFolder mFolder = new TemporaryFolder();
@@ -80,7 +90,7 @@
createTestFileWithEvents(2);
setLogInstance(); // Log instance reads the proto file at initialization.
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertProtoArrayEquals(
@@ -96,7 +106,7 @@
mTestFile.delete();
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -107,7 +117,7 @@
mFolder.delete();
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -119,7 +129,7 @@
mTestFile.setReadable(false);
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -132,7 +142,7 @@
}
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -143,7 +153,7 @@
createTestFileWithEvents(0);
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
assertThat(logProto.events).isEmpty();
@@ -154,7 +164,7 @@
createTestFileWithEvents(10); // Ring buffer capacity is 5
setLogInstance();
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
// Log contains the last 5 events only.
@@ -170,7 +180,7 @@
}
@Test
- public void logLinesForAnrFile() {
+ public void logLinesForTraceFile() {
mCriticalEventLog.incTimeSeconds(1);
mCriticalEventLog.logWatchdog("Watchdog subject",
UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
@@ -178,7 +188,7 @@
mCriticalEventLog.logHalfWatchdog("Half watchdog subject");
mCriticalEventLog.incTimeSeconds(1);
- assertThat(mCriticalEventLog.logLinesForAnrFile()).isEqualTo(
+ assertThat(mCriticalEventLog.logLinesForSystemServerTraceFile()).isEqualTo(
"--- CriticalEventLog ---\n"
+ "capacity: 5\n"
+ "events <\n"
@@ -205,7 +215,7 @@
UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
mCriticalEventLog.incTimeSeconds(1);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
@@ -220,7 +230,7 @@
mCriticalEventLog.logHalfWatchdog("Subject 1");
mCriticalEventLog.incTimeSeconds(1);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
@@ -229,6 +239,108 @@
}
@Test
+ public void logAnr() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logAnr("Subject 1", ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID, 0);
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = getLogOutput();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ anr(START_TIME_MS + 1000, "Subject 1", ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID, 0)
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr() {
+ 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.incTimeSeconds(1);
+
+ 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",
+ CriticalEventProto.SYSTEM_APP,
+ "AID_RADIO", SYSTEM_APP_UID, 1);
+ CriticalEventProto fooAppAnr = anr(START_TIME_MS + 3000, "Subject 3",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID, 2);
+ CriticalEventProto fooAppAnrUid2 = anr(START_TIME_MS + 4000, "Subject 4",
+ CriticalEventProto.DATA_APP, "com.foo", DATA_APP_UID_2, 3);
+ CriticalEventProto fooAppAnrUid2Redacted = anr(START_TIME_MS + 4000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_2, 3);
+ CriticalEventProto barAppAnr = anr(START_TIME_MS + 5000, "Subject 5",
+ CriticalEventProto.DATA_APP, "com.bar", DATA_APP_UID_3, 4);
+ CriticalEventProto barAppAnrRedacted = anr(START_TIME_MS + 5000, "",
+ CriticalEventProto.DATA_APP, "", DATA_APP_UID_3, 4);
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.DATA_APP, "com.foo", DATA_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ // Redacted since the trace file and ANR are for different uids.
+ fooAppAnrUid2Redacted,
+ // Redacted since the trace file and ANR are for different data apps.
+ barAppAnrRedacted
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM",
+ SYSTEM_SERVER_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ fooAppAnrUid2,
+ barAppAnr
+ });
+
+ assertProtoArrayEquals(
+ getLogOutput(ServerProtoEnums.SYSTEM_APP, "AID_RADIO",
+ SYSTEM_APP_UID).events,
+ new CriticalEventProto[]{
+ systemServerAnr,
+ systemAppAnr,
+ fooAppAnr,
+ fooAppAnrUid2,
+ barAppAnr
+ });
+ }
+
+ @Test
+ public void privacyRedaction_anr_doesNotMutateLogState() {
+ mCriticalEventLog.logAnr("Subject", ServerProtoEnums.DATA_APP, "com.foo",
+ 10_001, DATA_APP_UID);
+
+ CriticalEventLogProto unredactedLogBefore = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID);
+ CriticalEventLogProto redactedLog = getLogOutput(ServerProtoEnums.DATA_APP, "com.bar",
+ DATA_APP_UID);
+ CriticalEventLogProto unredactedLogAfter = getLogOutput(ServerProtoEnums.SYSTEM_SERVER,
+ "AID_SYSTEM", SYSTEM_SERVER_UID);
+
+ assertProtoNotEqual(unredactedLogBefore, redactedLog); // verify some redaction took place.
+ assertProtoEquals(unredactedLogBefore, unredactedLogAfter);
+ }
+
+ @Test
public void getOutputLogProto_numberOfEventsExceedsCapacity() {
// Log 10 events in 10 sec (capacity = 5)
for (int i = 0; i < 10; i++) {
@@ -237,7 +349,7 @@
mCriticalEventLog.incTimeSeconds(1);
}
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 10000);
assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
@@ -270,7 +382,7 @@
mCriticalEventLog.logHalfWatchdog("New event 2"); // 5m59s old
mCriticalEventLog.setCurrentTimeMillis(logTimestamp);
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
assertThat(logProto.timestampMs).isEqualTo(logTimestamp);
assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
@@ -288,7 +400,7 @@
createTestFileWithEvents(5);
setLogInstance(new NoOpLogLoader());
- CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput();
// Output log is empty.
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
@@ -350,7 +462,7 @@
log2.logHalfWatchdog("New subject");
log2.incTimeSeconds(1);
- CriticalEventLogProto logProto = log2.getRecentEvents();
+ CriticalEventLogProto logProto = getLogOutput(log2);
// Log contains 4 + 1 events.
assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 21_000);
@@ -363,6 +475,38 @@
});
}
+ @Test
+ public void processClassEnumParity() {
+ String message = "CriticalEventProto.ProcessClass and ServerProtoEnum are out of sync.";
+ assertWithMessage(message).that(CriticalEventProto.PROCESS_CLASS_UNKNOWN).isEqualTo(
+ ServerProtoEnums.ERROR_SOURCE_UNKNOWN);
+ assertWithMessage(message).that(CriticalEventProto.DATA_APP).isEqualTo(
+ ServerProtoEnums.DATA_APP);
+ assertWithMessage(message).that(CriticalEventProto.SYSTEM_APP).isEqualTo(
+ ServerProtoEnums.SYSTEM_APP);
+ assertWithMessage(message).that(CriticalEventProto.SYSTEM_SERVER).isEqualTo(
+ ServerProtoEnums.SYSTEM_SERVER);
+ }
+
+ private CriticalEventLogProto getLogOutput() {
+ return getLogOutput(mCriticalEventLog);
+ }
+
+ private CriticalEventLogProto getLogOutput(CriticalEventLog log) {
+ return getLogOutput(log, ServerProtoEnums.SYSTEM_SERVER, "AID_SYSTEM", SYSTEM_SERVER_UID);
+ }
+
+ private CriticalEventLogProto getLogOutput(int traceProcessClassEnum,
+ String traceProcessName, int traceProcessUid) {
+ return getLogOutput(mCriticalEventLog, traceProcessClassEnum, traceProcessName,
+ traceProcessUid);
+ }
+
+ private CriticalEventLogProto getLogOutput(CriticalEventLog log, int traceProcessClassEnum,
+ String traceProcessName, int traceProcessUid) {
+ return log.getOutputLogProto(traceProcessClassEnum, traceProcessName, traceProcessUid);
+ }
+
private CriticalEventLogStorageProto getEventsWritten() throws IOException {
return CriticalEventLogStorageProto.parseFrom(
Files.readAllBytes(mTestFile.toPath()));
@@ -389,6 +533,20 @@
}
}
+ private static CriticalEventProto anr(long timestampMs, String subject, int processClass,
+ String processName,
+ int uid, int pid) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setAnr(new AppNotResponding());
+ event.getAnr().subject = subject;
+ event.getAnr().processClass = processClass;
+ event.getAnr().process = processName;
+ event.getAnr().uid = uid;
+ event.getAnr().pid = pid;
+ return event;
+ }
+
private CriticalEventProto watchdog(long timestampMs, String subject) {
return watchdog(timestampMs, subject, "A UUID");
}
@@ -414,15 +572,32 @@
assertThat(expected).isNotNull();
assertThat(actual).isNotNull();
- String message =
- "Expected:\n" + Arrays.toString(expected) + "\nGot:\n" + Arrays.toString(actual);
- assertWithMessage(message).that(expected.length).isEqualTo(actual.length);
+ String baseMsg = String.format("Expected:\n%s\nGot:\n%s", Arrays.toString(expected),
+ Arrays.toString(actual));
+ String lengthMsg = String.format("%s\nGot different length arrays.\bExpected %d, got %d",
+ baseMsg, expected.length, actual.length);
+ assertWithMessage(lengthMsg).that(expected.length).isEqualTo(actual.length);
for (int i = 0; i < expected.length; i++) {
- assertWithMessage(message).that(
+ String pairMsg = String.format("%s\nMismatched pair.\nExpected:\n%s\nGot:\n%s",
+ baseMsg, expected[i], actual[i]);
+ assertWithMessage(pairMsg).that(
MessageNano.messageNanoEquals(expected[i], actual[i])).isTrue();
}
}
+ private static void assertProtoEquals(MessageNano actual, MessageNano expected) {
+ String message = String.format("Expected:\n%s\nGot:\n%s", expected, actual);
+ assertWithMessage(message).that(
+ MessageNano.messageNanoEquals(expected, actual)).isTrue();
+ }
+
+ private static void assertProtoNotEqual(MessageNano first, MessageNano second) {
+ String message = String.format("Expected protos to be different, but were equal:\n%s",
+ first);
+ assertWithMessage(message).that(
+ MessageNano.messageNanoEquals(first, second)).isFalse();
+ }
+
private TestableCriticalEventLog setLogInstance() {
return setLogInstance(new LogLoader());
}