Merge "New ActivityManager<->debuggerd protocol for recoverable crashes." am: f62a0533d7 am: 23afbe95eb

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2485756

Change-Id: Iefaf8a4603261a5c2a198e661fc2b1712c4a0823
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mitch Phillips
2023-03-30 17:14:03 +00:00
committed by Automerger Merge Worker
3 changed files with 92 additions and 66 deletions

View File

@@ -8443,13 +8443,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
boolean recoverable = eventType.equals("native_recoverable_crash");
EventLogTags.writeAmCrash(Binder.getCallingPid(),
UserHandle.getUserId(Binder.getCallingUid()), processName,
r == null ? -1 : r.info.flags,
crashInfo.exceptionClassName,
crashInfo.exceptionMessage,
crashInfo.throwFileName,
crashInfo.throwLineNumber);
crashInfo.throwLineNumber,
recoverable ? 1 : 0);
int processClassEnum = processName.equals("system_server") ? ServerProtoEnums.SYSTEM_SERVER
: (r != null) ? r.getProcessClassEnum()
@@ -8517,7 +8520,13 @@ public class ActivityManagerService extends IActivityManager.Stub
eventType, r, processName, null, null, null, null, null, null, crashInfo,
new Float(loadingProgress), incrementalMetrics, null);
mAppErrors.crashApplication(r, crashInfo);
// For GWP-ASan recoverable crashes, don't make the app crash (the whole point of
// 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
// debuggerd will terminate the process, but there's a backup where ActivityManager will
// also kill it. Avoid that.
if (!recoverable) {
mAppErrors.crashApplication(r, crashInfo);
}
}
public void handleApplicationStrictModeViolation(

View File

@@ -53,7 +53,7 @@ option java_package com.android.server.am
30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3)
# Unhandled exception
30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5),(Recoverable|1|5)
# Log.wtf() called
30040 am_wtf (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Tag|3),(Message|3)

View File

@@ -64,12 +64,15 @@ final class NativeCrashListener extends Thread {
class NativeCrashReporter extends Thread {
ProcessRecord mApp;
int mSignal;
boolean mGwpAsanRecoverableCrash;
String mCrashReport;
NativeCrashReporter(ProcessRecord app, int signal, String report) {
NativeCrashReporter(ProcessRecord app, int signal, boolean gwpAsanRecoverableCrash,
String report) {
super("NativeCrashReport");
mApp = app;
mSignal = signal;
mGwpAsanRecoverableCrash = gwpAsanRecoverableCrash;
mCrashReport = report;
}
@@ -85,7 +88,9 @@ final class NativeCrashListener extends Thread {
ci.stackTrace = mCrashReport;
if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
mAm.handleApplicationCrashInner("native_crash", mApp, mApp.processName, ci);
mAm.handleApplicationCrashInner(
mGwpAsanRecoverableCrash ? "native_recoverable_crash" : "native_crash",
mApp, mApp.processName, ci);
if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
} catch (Exception e) {
Slog.e(TAG, "Unable to report native crash", e);
@@ -207,9 +212,14 @@ final class NativeCrashListener extends Thread {
// permits crash_dump to connect to it. This allows us to trust the
// received values.
// first, the pid and signal number
int headerBytes = readExactly(fd, buf, 0, 8);
if (headerBytes != 8) {
// Activity Manager protocol:
// - 32-bit network-byte-order: pid
// - 32-bit network-byte-order: signal number
// - byte: gwpAsanRecoverableCrash
// - bytes: raw text of the dump
// - null terminator
int headerBytes = readExactly(fd, buf, 0, 9);
if (headerBytes != 9) {
// protocol failure; give up
Slog.e(TAG, "Unable to read from debuggerd");
return;
@@ -217,69 +227,76 @@ final class NativeCrashListener extends Thread {
int pid = unpackInt(buf, 0);
int signal = unpackInt(buf, 4);
boolean gwpAsanRecoverableCrash = buf[8] != 0;
if (DEBUG) {
Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
Slog.v(TAG, "Read pid=" + pid + " signal=" + signal
+ " recoverable=" + gwpAsanRecoverableCrash);
}
if (pid < 0) {
Slog.e(TAG, "Bogus pid!");
return;
}
// now the text of the dump
if (pid > 0) {
final ProcessRecord pr;
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(pid);
}
if (pr != null) {
// Don't attempt crash reporting for persistent apps
if (pr.isPersistent()) {
if (DEBUG) {
Slog.v(TAG, "Skipping report for persistent app " + pr);
}
return;
}
int bytes;
do {
// get some data
bytes = Os.read(fd, buf, 0, buf.length);
if (bytes > 0) {
if (MORE_DEBUG) {
String s = new String(buf, 0, bytes, "UTF-8");
Slog.v(TAG, "READ=" + bytes + "> " + s);
}
// did we just get the EOD null byte?
if (buf[bytes-1] == 0) {
os.write(buf, 0, bytes-1); // exclude the EOD token
break;
}
// no EOD, so collect it and read more
os.write(buf, 0, bytes);
}
} while (bytes > 0);
// Okay, we've got the report.
if (DEBUG) Slog.v(TAG, "processing");
// Mark the process record as being a native crash so that the
// cleanup mechanism knows we're still submitting the report
// even though the process will vanish as soon as we let
// debuggerd proceed.
synchronized (mAm) {
synchronized (mAm.mProcLock) {
pr.mErrorState.setCrashing(true);
pr.mErrorState.setForceCrashReport(true);
}
}
// Crash reporting is synchronous but we want to let debuggerd
// go about it business right away, so we spin off the actual
// reporting logic on a thread and let it take it's time.
final String reportString = new String(os.toByteArray(), "UTF-8");
(new NativeCrashReporter(pr, signal, reportString)).start();
} else {
Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
}
} else {
Slog.e(TAG, "Bogus pid!");
final ProcessRecord pr;
synchronized (mAm.mPidsSelfLocked) {
pr = mAm.mPidsSelfLocked.get(pid);
}
if (pr == null) {
Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
return;
}
// Don't attempt crash reporting for persistent apps
if (pr.isPersistent()) {
if (DEBUG) {
Slog.v(TAG, "Skipping report for persistent app " + pr);
}
return;
}
int bytes;
do {
// get some data
bytes = Os.read(fd, buf, 0, buf.length);
if (bytes > 0) {
if (MORE_DEBUG) {
String s = new String(buf, 0, bytes, "UTF-8");
Slog.v(TAG, "READ=" + bytes + "> " + s);
}
// did we just get the EOD null byte?
if (buf[bytes - 1] == 0) {
os.write(buf, 0, bytes - 1); // exclude the EOD token
break;
}
// no EOD, so collect it and read more
os.write(buf, 0, bytes);
}
} while (bytes > 0);
// Okay, we've got the report.
if (DEBUG) Slog.v(TAG, "processing");
// Mark the process record as being a native crash so that the
// cleanup mechanism knows we're still submitting the report even
// though the process will vanish as soon as we let debuggerd
// proceed. This isn't relevant for recoverable crashes, as we don't
// show the user an "app crashed" dialogue because the app (by
// design) didn't crash.
if (!gwpAsanRecoverableCrash) {
synchronized (mAm) {
synchronized (mAm.mProcLock) {
pr.mErrorState.setCrashing(true);
pr.mErrorState.setForceCrashReport(true);
}
}
}
// Crash reporting is synchronous but we want to let debuggerd
// go about it business right away, so we spin off the actual
// reporting logic on a thread and let it take it's time.
final String reportString = new String(os.toByteArray(), "UTF-8");
(new NativeCrashReporter(pr, signal, gwpAsanRecoverableCrash, reportString)).start();
} catch (Exception e) {
Slog.e(TAG, "Exception dealing with report", e);
// ugh, fail.