Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85a40089 authored by Ben Miles's avatar Ben Miles
Browse files

Log ANRs in critical event log

Change-Id: I4a851cb57f8624c44b06b8c243bf1671aedbfaea
Test: atest CriticalEventLogTest
Bug: b/200263868
parent 0f216d6f
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ message CriticalEventProto {
  oneof event {
    Watchdog watchdog = 2;
    HalfWatchdog half_watchdog = 3;
    AppNotResponding anr = 4;
  }

  message Watchdog {
@@ -75,4 +76,34 @@ message CriticalEventProto {
    // 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
+4 −2
Original line number Diff line number Diff line
@@ -666,7 +666,8 @@ public class Watchdog {
            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 @@ public class Watchdog {

            // 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();
+7 −1
Original line number Diff line number Diff line
@@ -332,6 +332,13 @@ class ProcessErrorStateRecord {
            }
        }

        // 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 @@ class ProcessErrorStateRecord {
        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);
+126 −5
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.criticalevents;

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.internal.util.RingBuffer;
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 @@ import java.util.UUID;
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 @@ public class CriticalEventLog {
        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 @@ public class CriticalEventLog {
    }

    /**
     * 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 @@ public class CriticalEventLog {
     * 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 @@ public class CriticalEventLog {
            }
        }
    }

    /**
     * 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;
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ class AnrController {
            }
        }

        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);
Loading