diff options
| -rw-r--r-- | core/java/android/app/ApplicationErrorReport.java | 15 | ||||
| -rw-r--r-- | core/java/android/os/StrictMode.java | 1 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 39 |
3 files changed, 51 insertions, 4 deletions
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 57a23aeb8914..9834c4c0f542 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -254,6 +254,10 @@ public class ApplicationErrorReport implements Parcelable { /** * Describes an application crash. + * + * <p>This is also used to marshal around stack traces of ANRs and + * StrictMode violations which aren't necessarily crashes, but have + * a lot in common. */ public static class CrashInfo { /** @@ -292,6 +296,12 @@ public class ApplicationErrorReport implements Parcelable { public String stackTrace; /** + * For StrictMode violations, the wall time duration of the + * violation, when known. + */ + public long durationMillis = -1; + + /** * Create an uninitialized instance of CrashInfo. */ public CrashInfo() { @@ -334,6 +344,7 @@ public class ApplicationErrorReport implements Parcelable { throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); + durationMillis = in.readLong(); } /** @@ -347,6 +358,7 @@ public class ApplicationErrorReport implements Parcelable { dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); + dest.writeLong(durationMillis); } /** @@ -360,6 +372,9 @@ public class ApplicationErrorReport implements Parcelable { pw.println(prefix + "throwMethodName: " + throwMethodName); pw.println(prefix + "throwLineNumber: " + throwLineNumber); pw.println(prefix + "stackTrace: " + stackTrace); + if (durationMillis != -1) { + pw.println(prefix + "durationMillis: " + durationMillis); + } } } diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index c0ae263d7c2c..9b18719670fb 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -187,6 +187,7 @@ public final class StrictMode { // Not _really_ a Crash, but we use the same data structure... ApplicationErrorReport.CrashInfo crashInfo = new ApplicationErrorReport.CrashInfo(violation); + crashInfo.durationMillis = durationMillis; // Not perfect, but fast and good enough for dup suppression. Integer crashFingerprint = crashInfo.stackTrace.hashCode(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index e93f7ff2c669..ec209eda3178 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -648,6 +648,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen = new HashMap<PendingIntentRecord.Key, WeakReference<PendingIntentRecord>>(); /** + * Fingerprints (String.hashCode()) of stack traces that we've + * already logged DropBox entries for. Guarded by itself. If + * something (rogue user app) forces this over + * MAX_DUP_SUPPRESSED_STACKS entries, the contents are cleared. + */ + private final HashSet<Integer> mAlreadyLoggedViolatedStacks = new HashSet<Integer>(); + private static final int MAX_DUP_SUPPRESSED_STACKS = 5000; + + /** * Intent broadcast that we have tried to start, but are * waiting for its application's process to be created. We only * need one (instead of a list) because we always process broadcasts @@ -9357,12 +9366,29 @@ public final class ActivityManagerService extends ActivityManagerNative implemen public void handleApplicationStrictModeViolation( IBinder app, int violationMask, ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord r = findAppProcess(app); - // TODO: implement - Log.w(TAG, "handleApplicationStrictModeViolation."); if ((violationMask & StrictMode.PENALTY_DROPBOX) != 0) { - Integer crashFingerprint = crashInfo.stackTrace.hashCode(); - Log.d(TAG, "supposed to drop box for fingerprint " + crashFingerprint); + Integer stackFingerprint = crashInfo.stackTrace.hashCode(); + boolean logIt = true; + synchronized (mAlreadyLoggedViolatedStacks) { + if (mAlreadyLoggedViolatedStacks.contains(stackFingerprint)) { + logIt = false; + // TODO: sub-sample into EventLog for these, with + // the crashInfo.durationMillis? Then we'd get + // the relative pain numbers, without logging all + // the stack traces repeatedly. We'd want to do + // likewise in the client code, which also does + // dup suppression, before the Binder call. + } else { + if (mAlreadyLoggedViolatedStacks.size() >= MAX_DUP_SUPPRESSED_STACKS) { + mAlreadyLoggedViolatedStacks.clear(); + } + mAlreadyLoggedViolatedStacks.add(stackFingerprint); + } + } + if (logIt) { + addErrorToDropBox("strictmode", r, null, null, null, null, null, crashInfo); + } } if ((violationMask & StrictMode.PENALTY_DIALOG) != 0) { @@ -9375,6 +9401,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen HashMap<String, Object> data = new HashMap<String, Object>(); data.put("result", result); data.put("app", r); + data.put("violationMask", violationMask); + data.put("crashInfo", crashInfo); msg.obj = data; mHandler.sendMessage(msg); @@ -9510,6 +9538,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen sb.append("Subject: ").append(subject).append("\n"); } sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); + if (crashInfo.durationMillis != -1) { + sb.append("Duration-Millis: ").append(crashInfo.durationMillis).append("\n"); + } sb.append("\n"); // Do the rest in a worker thread to avoid blocking the caller on I/O |