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

Commit f5cce5c1 authored by Ben Miles's avatar Ben Miles Committed by Android (Google) Code Review
Browse files

Merge "Log ANRs in critical event log"

parents 987141d3 85a40089
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