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

Commit ef79ae59 authored by Mohamad Mahmoud's avatar Mohamad Mahmoud
Browse files

Allow kernel logs to be appended to dropbox

Append logs from the kernel log buffer to dropbox through logcat, and
add the necessary settings for kernel log line count.

Bug: 324222683
Test: adb shell settings put global kernel_logs_for_data_app_anr 100
Change-Id: I760f1be849031e67bfed9e573cb76b9ad4727624
parent afa3ba52
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -14908,6 +14908,17 @@ public final class Settings {
        @Readable
        public static final String DROPBOX_TAG_PREFIX = "dropbox:";
        /**
         * Lines of kernel logs to include with system crash/ANR/etc. reports, as a
         * prefix of the dropbox tag of the report type. For example,
         * "kernel_logs_for_system_server_anr" controls the lines of kernel logs
         * captured with system server ANR reports. 0 to disable.
         *
         * @hide
         */
        @Readable
        public static final String ERROR_KERNEL_LOG_PREFIX = "kernel_logs_for_";
        /**
         * Lines of logcat to include with system crash/ANR/etc. reports, as a
         * prefix of the dropbox tag of the report type. For example,
+1 −0
Original line number Diff line number Diff line
@@ -233,6 +233,7 @@ public class SettingsBackupTest {
                    Settings.Global.ENHANCED_4G_MODE_ENABLED,
                    Settings.Global.ENABLE_16K_PAGES, // Added for 16K developer option
                    Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
                    Settings.Global.ERROR_KERNEL_LOG_PREFIX,
                    Settings.Global.ERROR_LOGCAT_PREFIX,
                    Settings.Global.EUICC_PROVISIONED,
                    Settings.Global.EUICC_SUPPORTED_COUNTRIES,
+125 −117
Original line number Diff line number Diff line
@@ -598,6 +598,9 @@ public class ActivityManagerService extends IActivityManager.Stub
    // as one line, but close enough for now.
    static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
    // How many seconds should the system wait before terminating the spawned logcat process.
    static final int LOGCAT_TIMEOUT_SEC = 10;
    // Necessary ApplicationInfo flags to mark an app as persistent
    static final int PERSISTENT_MASK =
            ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
@@ -9939,8 +9942,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        // If process is null, we are being called from some internal code
        // and may be about to die -- run this synchronously.
        final boolean runSynchronously = process == null;
        Thread worker =
                new Thread("Error dump: " + dropboxTag) {
        Thread worker = new Thread("Error dump: " + dropboxTag) {
            @Override
            public void run() {
                if (report != null) {
@@ -9948,48 +9950,39 @@ public class ActivityManagerService extends IActivityManager.Stub
                }
                String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
                        String maxBytesSetting =
                                Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
                        int lines =
                                Build.IS_USER
                String kerLogSetting = Settings.Global.ERROR_KERNEL_LOG_PREFIX + dropboxTag;
                String maxBytesSetting = Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
                int logcatLines = Build.IS_USER
                        ? 0
                                        : Settings.Global.getInt(
                                                mContext.getContentResolver(), logcatSetting, 0);
                        int dropboxMaxSize =
                                Settings.Global.getInt(
                                        mContext.getContentResolver(),
                                        maxBytesSetting,
                                        DROPBOX_DEFAULT_MAX_SIZE);
                        : Settings.Global.getInt(mContext.getContentResolver(), logcatSetting, 0);
                int kernelLogLines = Build.IS_USER
                        ? 0
                        : Settings.Global.getInt(mContext.getContentResolver(), kerLogSetting, 0);
                int dropboxMaxSize = Settings.Global.getInt(
                        mContext.getContentResolver(), maxBytesSetting, DROPBOX_DEFAULT_MAX_SIZE);
                if (dataFile != null) {
                            // Attach the stack traces file to the report so collectors can load
                            // them
                    // Attach the stack traces file to the report so collectors can load them
                    // by file if they have access.
                    sb.append(DATA_FILE_PATH_HEADER)
                                    .append(dataFile.getAbsolutePath())
                                    .append('\n');
                            .append(dataFile.getAbsolutePath()).append('\n');
                            int maxDataFileSize =
                                    dropboxMaxSize
                    int maxDataFileSize = dropboxMaxSize
                            - sb.length()
                                            - lines * RESERVED_BYTES_PER_LOGCAT_LINE
                            - logcatLines * RESERVED_BYTES_PER_LOGCAT_LINE
                            - kernelLogLines * RESERVED_BYTES_PER_LOGCAT_LINE
                            - DATA_FILE_PATH_FOOTER.length();
                    if (maxDataFileSize > 0) {
                        // Inline dataFile contents if there is room.
                        try {
                                    sb.append(
                                            FileUtils.readTextFile(
                                                    dataFile,
                                                    maxDataFileSize,
                            sb.append(FileUtils.readTextFile(dataFile, maxDataFileSize,
                                    "\n\n[[TRUNCATED]]\n"));
                        } catch (IOException e) {
                            Slog.e(TAG, "Error reading " + dataFile, e);
                        }
                    }
                            // Always append the footer, even there wasn't enough space to inline
                            // the
                    // Always append the footer, even there wasn't enough space to inline the
                    // dataFile contents.
                    sb.append(DATA_FILE_PATH_FOOTER);
                }
@@ -9997,62 +9990,16 @@ public class ActivityManagerService extends IActivityManager.Stub
                if (crashInfo != null && crashInfo.stackTrace != null) {
                    sb.append(crashInfo.stackTrace);
                }
                        if (lines > 0 && !runSynchronously) {
                boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
                if (!runSynchronously && shouldAddLogs) {
                    sb.append("\n");
                            InputStreamReader input = null;
                            try {
                                java.lang.Process logcat =
                                        new ProcessBuilder(
                                                        // Time out after 10s of inactivity, but
                                                        // kill logcat with SEGV
                                                        // so we can investigate why it didn't
                                                        // finish.
                                                        "/system/bin/timeout",
                                                        "-i",
                                                        "-s",
                                                        "SEGV",
                                                        "10s",
                                                        // Merge several logcat streams, and take
                                                        // the last N lines.
                                                        "/system/bin/logcat",
                                                        "-v",
                                                        "threadtime",
                                                        "-b",
                                                        "events",
                                                        "-b",
                                                        "system",
                                                        "-b",
                                                        "main",
                                                        "-b",
                                                        "crash",
                                                        "-t",
                                                        String.valueOf(lines))
                                                .redirectErrorStream(true)
                                                .start();
                                try {
                                    logcat.getOutputStream().close();
                                } catch (IOException e) {
                                }
                                try {
                                    logcat.getErrorStream().close();
                                } catch (IOException e) {
                                }
                                input = new InputStreamReader(logcat.getInputStream());
                                int num;
                                char[] buf = new char[8192];
                                while ((num = input.read(buf)) > 0) sb.append(buf, 0, num);
                            } catch (IOException e) {
                                Slog.e(TAG, "Error running logcat", e);
                            } finally {
                                if (input != null)
                                    try {
                                        input.close();
                                    } catch (IOException e) {
                    if (logcatLines > 0) {
                        fetchLogcatBuffers(sb, logcatLines, LOGCAT_TIMEOUT_SEC,
                                List.of("events", "system", "main", "crash"));
                    }
                    if (kernelLogLines > 0) {
                        fetchLogcatBuffers(sb, kernelLogLines, LOGCAT_TIMEOUT_SEC / 2,
                                List.of("kernel"));
                    }
                }
@@ -10320,6 +10267,67 @@ public class ActivityManagerService extends IActivityManager.Stub
                Binder.getCallingPid(), state);
    }
    /**
     * Retrieves logs from specified logcat buffers and appends them to a StringBuilder
     * in the supplied order. The method executes a logcat command to fetch specific
     * log entries from the supplied buffers.
     *
     * @param sb the StringBuilder to append the logcat output to.
     * @param lines the number of lines to retrieve.
     * @param timeout the maximum allowed time in seconds for logcat to run before being terminated.
     * @param buffers the list of log buffers from which to retrieve logs.
     */
    private static void fetchLogcatBuffers(StringBuilder sb, int lines,
            int timeout, List<String> buffers) {
        if (buffers.size() == 0 || lines <= 0 || timeout <= 0) {
            return;
        }
        List<String> command = new ArrayList<>(10 + (2 * buffers.size()));
        // Time out after 10s of inactivity, but kill logcat with SEGV
        // so we can investigate why it didn't finish.
        command.add("/system/bin/timeout");
        command.add("-i");
        command.add("-s");
        command.add("SEGV");
        command.add(timeout + "s");
        // Merge several logcat streams, and take the last N lines.
        command.add("/system/bin/logcat");
        command.add("-v");
        // This adds a timestamp and thread info to each log line.
        command.add("threadtime");
        for (String buffer : buffers) {
            command.add("-b");
            command.add(buffer);
        }
        // Limit the output to the last N lines.
        command.add("-t");
        command.add(String.valueOf(lines));
        try {
            java.lang.Process proc =
                    new ProcessBuilder(command).redirectErrorStream(true).start();
            // Close the output stream immediately as we do not send input to the process.
            try {
                proc.getOutputStream().close();
            } catch (IOException e) {
            }
            try (InputStreamReader reader = new InputStreamReader(proc.getInputStream())) {
                char[] buffer = new char[8192];
                int numRead;
                while ((numRead = reader.read(buffer, 0, buffer.length)) > 0) {
                    sb.append(buffer, 0, numRead);
                }
            }
        } catch (IOException e) {
            Slog.e(TAG, "Error running logcat", e);
        }
    }
    /**
     * Check if the calling process has the permission to dump given package,
     * throw SecurityException if it doesn't have the permission.