summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Mohamad Mahmoud <mohamadmahmoud@google.com> 2024-05-10 09:15:55 +0000
committer Mohamad Mahmoud <mohamadmahmoud@google.com> 2024-05-15 11:43:25 +0000
commitef79ae59abe88efcc0d1e010d22a56de30168d78 (patch)
tree2c55175286f05a407e5e34ba9f99e643d39536d5
parentafa3ba5227bf7bd356a40770190e8d973ade514d (diff)
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
-rw-r--r--core/java/android/provider/Settings.java11
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java242
3 files changed, 137 insertions, 117 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 009713f0f942..90ec5aad235d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14909,6 +14909,17 @@ public final class Settings {
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,
* "logcat_for_system_server_anr" controls the lines of logcat captured
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 92167ee4d423..ab9a30b65e08 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -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,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 00c2df67c8f0..e2452cc17c87 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -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,126 +9942,70 @@ 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) {
- @Override
- public void run() {
- if (report != null) {
- sb.append(report);
- }
-
- String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
- String maxBytesSetting =
- Settings.Global.MAX_ERROR_BYTES_PREFIX + dropboxTag;
- int lines =
- Build.IS_USER
- ? 0
- : Settings.Global.getInt(
- mContext.getContentResolver(), logcatSetting, 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
- // by file if they have access.
- sb.append(DATA_FILE_PATH_HEADER)
- .append(dataFile.getAbsolutePath())
- .append('\n');
-
- int maxDataFileSize =
- dropboxMaxSize
- - sb.length()
- - lines * 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,
- "\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
- // dataFile contents.
- sb.append(DATA_FILE_PATH_FOOTER);
- }
-
- if (crashInfo != null && crashInfo.stackTrace != null) {
- sb.append(crashInfo.stackTrace);
- }
-
- if (lines > 0 && !runSynchronously) {
- 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) {
- }
- }
+ Thread worker = new Thread("Error dump: " + dropboxTag) {
+ @Override
+ public void run() {
+ if (report != null) {
+ sb.append(report);
+ }
+
+ String logcatSetting = Settings.Global.ERROR_LOGCAT_PREFIX + dropboxTag;
+ 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 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
+ // by file if they have access.
+ sb.append(DATA_FILE_PATH_HEADER)
+ .append(dataFile.getAbsolutePath()).append('\n');
+
+ int maxDataFileSize = dropboxMaxSize
+ - sb.length()
+ - 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,
+ "\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
+ // dataFile contents.
+ sb.append(DATA_FILE_PATH_FOOTER);
+ }
- dbox.addText(dropboxTag, sb.toString());
+ if (crashInfo != null && crashInfo.stackTrace != null) {
+ sb.append(crashInfo.stackTrace);
+ }
+ boolean shouldAddLogs = logcatLines > 0 || kernelLogLines > 0;
+ if (!runSynchronously && shouldAddLogs) {
+ sb.append("\n");
+ 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"));
+ }
+ }
+
+ dbox.addText(dropboxTag, sb.toString());
+ }
+ };
if (runSynchronously) {
final int oldMask = StrictMode.allowThreadDiskWritesMask();
@@ -10321,6 +10268,67 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * 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.
*