diff options
author | Dan Egnor <egnor@google.com> | 2009-12-16 16:32:58 -0800 |
---|---|---|
committer | Dan Egnor <egnor@google.com> | 2009-12-21 16:03:19 -0800 |
commit | 60d8762413e8daba5f73559786312a9ec5e3b827 (patch) | |
tree | 7f61aea1775472b8b45581b1417ef484c3ddc58c | |
parent | 223bd7af9832971075ba9fd9b0e41b7d693bd791 (diff) | |
download | frameworks_base-60d8762413e8daba5f73559786312a9ec5e3b827.zip frameworks_base-60d8762413e8daba5f73559786312a9ec5e3b827.tar.gz frameworks_base-60d8762413e8daba5f73559786312a9ec5e3b827.tar.bz2 |
DropBox logging of app & system server crashes.
The crashes are also reported to the event log (and of course the
main logcat, like they always have been). Ordinary Log.e(t,m,e) isn't dropboxed
but there's a new Log.wtf() which always is. (Still @pending in this change.)
Add a hook to IPowerManager to crash the system server on demand
(only for apps with REBOOT permission, since it's basically a restart).
This is not exposed in PowerManager, must be invoked directly -- mostly
this is there so "Bad Behavior" in dev tools can do it.
-rw-r--r-- | core/java/android/app/ActivityManager.java | 2 | ||||
-rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 33 | ||||
-rw-r--r-- | core/java/android/app/ApplicationErrorReport.java | 1 | ||||
-rw-r--r-- | core/java/android/app/IActivityController.aidl | 2 | ||||
-rw-r--r-- | core/java/android/app/IActivityManager.java | 8 | ||||
-rw-r--r-- | core/java/android/os/FileObserver.java | 4 | ||||
-rw-r--r-- | core/java/android/os/IPowerManager.aidl | 1 | ||||
-rw-r--r-- | core/java/android/os/MessageQueue.java | 4 | ||||
-rw-r--r-- | core/java/android/os/PowerManager.java | 3 | ||||
-rw-r--r-- | core/java/android/provider/Settings.java | 5 | ||||
-rw-r--r-- | core/java/android/util/Log.java | 55 | ||||
-rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 82 | ||||
-rw-r--r-- | services/java/com/android/server/PowerManagerService.java | 18 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 168 | ||||
-rw-r--r-- | services/java/com/android/server/am/EventLogTags.logtags | 5 |
15 files changed, 291 insertions, 100 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 676d6d5..7d07604 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -606,7 +606,7 @@ public class ActivityManager { public int uid; /** - * The tag that was provided when the process crashed. + * The activity name associated with the error, if known. May be null. */ public String tag; diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a0498aa..90f46dd 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -979,13 +979,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case HANDLE_APPLICATION_ERROR_TRANSACTION: { + case HANDLE_APPLICATION_CRASH_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder app = data.readStrongBinder(); + ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); + handleApplicationCrash(app, ci); + reply.writeNoException(); + return true; + } + + case HANDLE_APPLICATION_WTF_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder app = data.readStrongBinder(); String tag = data.readString(); ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); - handleApplicationError(app, tag, ci); + boolean res = handleApplicationWtf(app, tag, ci); reply.writeNoException(); + reply.writeInt(res ? 1 : 0); return true; } @@ -2337,7 +2347,20 @@ class ActivityManagerProxy implements IActivityManager /* this base class version is never called */ return true; } - public void handleApplicationError(IBinder app, String tag, + public void handleApplicationCrash(IBinder app, + ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(app); + crashInfo.writeToParcel(data, 0); + mRemote.transact(HANDLE_APPLICATION_CRASH_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException { Parcel data = Parcel.obtain(); @@ -2346,10 +2369,12 @@ class ActivityManagerProxy implements IActivityManager data.writeStrongBinder(app); data.writeString(tag); crashInfo.writeToParcel(data, 0); - mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0); + mRemote.transact(HANDLE_APPLICATION_WTF_TRANSACTION, data, reply, 0); reply.readException(); + boolean res = reply.readInt() != 0; reply.recycle(); data.recycle(); + return res; } public void signalPersistentProcesses(int sig) throws RemoteException { diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index e89b3ad0..a4b692f 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -195,6 +195,7 @@ public class ApplicationErrorReport implements Parcelable { StringWriter sw = new StringWriter(); tr.printStackTrace(new PrintWriter(sw)); stackTrace = sw.toString(); + exceptionMessage = tr.getMessage(); // Populate fields with the "root cause" exception while (tr.getCause() != null) { diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl index 804dd61..c76a517 100644 --- a/core/java/android/app/IActivityController.aidl +++ b/core/java/android/app/IActivityController.aidl @@ -44,7 +44,7 @@ interface IActivityController * it immediately. */ boolean appCrashed(String processName, int pid, - String tag, String shortMsg, String longMsg, + String shortMsg, String longMsg, long timeMillis, String stackTrace); /** diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index c890c4c..ca6bfa7 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -242,8 +242,9 @@ public interface IActivityManager extends IInterface { // Special low-level communication with activity manager. public void startRunning(String pkg, String cls, String action, String data) throws RemoteException; - - public void handleApplicationError(IBinder app, String tag, + public void handleApplicationCrash(IBinder app, + ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; /* @@ -349,7 +350,7 @@ public interface IActivityManager extends IInterface { // Please keep these transaction codes the same -- they are also // sent by C++ code. int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; - int HANDLE_APPLICATION_ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; + int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2; int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3; int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4; @@ -446,4 +447,5 @@ public interface IActivityManager extends IInterface { int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98; int START_ACTIVITY_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99; int OVERRIDE_PENDING_TRANSITION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+100; + int HANDLE_APPLICATION_WTF_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+101; } diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java index 38d252e..3457815 100644 --- a/core/java/android/os/FileObserver.java +++ b/core/java/android/os/FileObserver.java @@ -103,9 +103,7 @@ public abstract class FileObserver { try { observer.onEvent(mask, path); } catch (Throwable throwable) { - Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() + - " (returned by observer " + observer + ")", throwable); - RuntimeInit.crash("FileObserver", throwable); + Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); } } } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 0afc537..23762ca 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -31,6 +31,7 @@ interface IPowerManager void preventScreenOn(boolean prevent); boolean isScreenOn(); void reboot(String reason); + void crash(String message); // sets the brightness of the backlights (screen, keyboard, button) 0-255 void setBacklightBrightness(int brightness); diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index caf0923..bc653d6 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -115,9 +115,7 @@ public class MessageQueue { didIdle = true; keep = ((IdleHandler)idler).queueIdle(); } catch (Throwable t) { - Log.e("MessageQueue", - "IdleHandler threw exception", t); - RuntimeInit.crash("MessageQueue", t); + Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 293dabc..e4eaf45 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -326,12 +326,11 @@ public class PowerManager { synchronized (mToken) { if (mHeld) { + Log.wtf(TAG, "WakeLock finalized while still held: " + mTag); try { mService.releaseWakeLock(mToken, 0); } catch (RemoteException e) { } - RuntimeInit.crash(TAG, new Exception( - "WakeLock finalized while still held: "+mTag)); } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 947ab19..09651f1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3710,6 +3710,11 @@ public final class Settings { "dropbox:"; /** + * Nonzero causes Log.wtf() to crash. + */ + public static final String WTF_IS_FATAL = "wtf_is_fatal"; + + /** * The length of time in milli-seconds that automatic small adjustments to * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. */ diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index e95d0be..7a959a6 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -81,6 +81,13 @@ public final class Log { */ public static final int ASSERT = 7; + /** + * Exception class used to capture a stack trace in {@link #wtf()}. + */ + private static class TerribleFailure extends Exception { + TerribleFailure(String msg, Throwable cause) { super(msg, cause); } + } + private Log() { } @@ -170,24 +177,24 @@ public final class Log { /** * Checks to see whether or not a log for the specified tag is loggable at the specified level. - * + * * The default level of any tag is set to INFO. This means that any level above and including * INFO will be logged. Before you make any calls to a logging method you should check to see * if your tag should be logged. You can change the default level by setting a system property: * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' - * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will + * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will * turn off all logging for your tag. You can also create a local.prop file that with the * following in it: * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' * and place that in /data/local.prop. - * + * * @param tag The tag to check. * @param level The level to check. * @return Whether or not that this is allowed to be logged. * @throws IllegalArgumentException is thrown if the tag.length() > 23. */ public static native boolean isLoggable(String tag, int level); - + /* * Send a {@link #WARN} log message and log the exception. * @param tag Used to identify the source of a log message. It usually identifies @@ -220,6 +227,46 @@ public final class Log { } /** + * What a Terrible Failure: Report a condition that should never happen. + * The error will always be logged at level ASSERT with the call stack. + * Depending on system configuration, a report may be added to the + * {@link android.os.DropBoxManager} and/or the process may be terminated + * immediately with an error dialog. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @pending + */ + public static int wtf(String tag, String msg) { + return wtf(tag, msg, null); + } + + /** + * What a Terrible Failure: Report an exception that should never happen. + * Similar to {@link #wtf(String, String)}, with an exception to log. + * @param tag Used to identify the source of a log message. + * @param tr An exception to log. + * @pending + */ + public static int wtf(String tag, Throwable tr) { + return wtf(tag, tr.getMessage(), tr); + } + + /** + * What a Terrible Failure: Report an exception that should never happen. + * Similar to {@link #wtf(String, Throwable)}, with a message as well. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param tr An exception to log. May be null. + * @pending + */ + public static int wtf(String tag, String msg, Throwable tr) { + tr = new TerribleFailure(msg, tr); + int bytes = println(ASSERT, tag, getStackTraceString(tr)); + RuntimeInit.wtf(tag, tr); + return bytes; + } + + /** * Handy function to get a loggable stack trace from a Throwable * @param tr An exception to log */ diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index b7bb72d..57a28e6 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -58,6 +58,10 @@ public class RuntimeInit { /** true if commonInit() has been called */ private static boolean initialized; + private static IBinder mApplicationObject; + + private static volatile boolean mCrashing = false; + /** * Use this to log a message when a thread exits due to an uncaught * exception. The framework catches these for the main threads, so @@ -66,14 +70,30 @@ public class RuntimeInit { private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { try { - Log.e(TAG, "Uncaught handler: thread " + t.getName() - + " exiting due to uncaught exception"); - } catch (Throwable error) { - // Ignore the throwable, since we're in the process of crashing anyway. - // If we don't, the crash won't happen properly and the process will - // be left around in a bad state. + // Don't re-enter -- avoid infinite loops if crash-reporting crashes. + if (mCrashing) return; + mCrashing = true; + + if (mApplicationObject == null) { + Log.e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); + } else { + Log.e(TAG, "FATAL EXCEPTION: " + t.getName(), e); + } + + // Bring up crash dialog, wait for it to be dismissed + ActivityManagerNative.getDefault().handleApplicationCrash( + mApplicationObject, new ApplicationErrorReport.CrashInfo(e)); + } catch (Throwable t2) { + try { + Log.e(TAG, "Error reporting crash", t2); + } catch (Throwable t3) { + // Even Log.e() fails! Oh well. + } + } finally { + // Try everything to make sure this process goes away. + Process.killProcess(Process.myPid()); + System.exit(10); } - crash(TAG, e); } } @@ -300,46 +320,22 @@ public class RuntimeInit { public static native int getQwertyKeyboard(); /** - * Report a fatal error in the current process. If this is a user-process, - * a dialog may be displayed informing the user of the error. This - * function does not return; it forces the current process to exit. + * Report a serious error in the current process. May or may not cause + * the process to terminate (depends on system settings). * - * @param tag to use when logging the error - * @param t exception that was generated by the error + * @param tag to record with the error + * @param t exception describing the error site and conditions */ - public static void crash(String tag, Throwable t) { - if (mApplicationObject != null) { - try { - // Log exception. - Log.e(TAG, Log.getStackTraceString(t)); - - // Show a message to the user. - IActivityManager am = ActivityManagerNative.getDefault(); - am.handleApplicationError(mApplicationObject, tag, - new ApplicationErrorReport.CrashInfo(t)); - } catch (Throwable t2) { - try { - // Log exception as a string so we don't get in an infinite loop. - Log.e(TAG, "Error reporting crash: " + Log.getStackTraceString(t2)); - } catch (Throwable t3) { - // Do nothing, must be OOM so we can't format the message - } - } finally { - // Try everything to make sure this process goes away. - Process.killProcess(Process.myPid()); - System.exit(10); - } - } else { - try { - Log.e(TAG, "*** EXCEPTION IN SYSTEM PROCESS. System will crash."); - Log.e(tag, Log.getStackTraceString(t)); - } catch (Throwable t2) { - // Do nothing, must be OOM so we can't format the message - } finally { - // Try everything to make sure this process goes away. + public static void wtf(String tag, Throwable t) { + try { + if (ActivityManagerNative.getDefault().handleApplicationWtf( + mApplicationObject, tag, new ApplicationErrorReport.CrashInfo(t))) { + // The Activity Manager has already written us off -- now exit. Process.killProcess(Process.myPid()); System.exit(10); } + } catch (Throwable t2) { + Log.e(TAG, "Error reporting WTF", t2); } } @@ -361,6 +357,4 @@ public class RuntimeInit { // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers(); } - - private static IBinder mApplicationObject; } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index e11c312..d740ce1 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -2137,6 +2137,24 @@ class PowerManagerService extends IPowerManager.Stub } } + /** + * Crash the runtime (causing a complete restart of the Android framework). + * Requires REBOOT permission. Mostly for testing. Should not return. + */ + public void crash(final String message) + { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); + Thread t = new Thread("PowerManagerService.crash()") { + public void run() { throw new RuntimeException(message); } + }; + try { + t.start(); + t.join(); + } catch (InterruptedException e) { + Log.wtf(TAG, e); + } + } + private void goToSleepLocked(long time, int reason) { if (mLastEventTime <= time) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 266f213..31edaf2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -71,7 +71,9 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Binder; import android.os.Bundle; +import android.os.Build; import android.os.Debug; +import android.os.DropBoxManager; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -8580,11 +8582,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private boolean makeAppCrashingLocked(ProcessRecord app, - String tag, String shortMsg, String longMsg, String stackTrace) { + String shortMsg, String longMsg, String stackTrace) { app.crashing = true; app.crashingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.CRASHED, tag, shortMsg, longMsg, - stackTrace); + ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); startAppProblemLocked(app); app.stopFreezingAllLocked(); return handleAppCrashLocked(app); @@ -8658,10 +8659,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } private void makeAppNotRespondingLocked(ProcessRecord app, - String tag, String shortMsg, String longMsg) { + String activity, String shortMsg, String longMsg) { app.notResponding = true; app.notRespondingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, tag, shortMsg, longMsg, null); + ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, + activity, shortMsg, longMsg, null); startAppProblemLocked(app); app.stopFreezingAllLocked(); } @@ -8672,7 +8674,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @param app The ProcessRecord in which the error occurred. * @param condition Crashing, Application Not Responding, etc. Values are defined in * ActivityManager.AppErrorStateInfo - * @param tag The tag that was passed into handleApplicationError(). Typically the classname. + * @param activity The activity associated with the crash, if known. * @param shortMsg Short message describing the crash. * @param longMsg Long message describing the crash. * @param stackTrace Full crash stack trace, may be null. @@ -8680,14 +8682,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen * @return Returns a fully-formed AppErrorStateInfo record. */ private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, - int condition, String tag, String shortMsg, String longMsg, String stackTrace) { + int condition, String activity, String shortMsg, String longMsg, String stackTrace) { ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); report.condition = condition; report.processName = app.processName; report.pid = app.pid; report.uid = app.info.uid; - report.tag = tag; + report.tag = activity; report.shortMsg = shortMsg; report.longMsg = longMsg; report.stackTrace = stackTrace; @@ -8804,10 +8806,126 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } - public void handleApplicationError(IBinder app, String tag, + /** + * Used by {@link com.android.internal.os.RuntimeInit} to report when an application crashes. + * The application process will exit immediately after this call returns. + * @param app object of the crashing app, null for the system server + * @param crashInfo describing the exception + */ + public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) { + ProcessRecord r = findAppProcess(app); + + EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(), + app == null ? "system" : (r == null ? "unknown" : r.processName), + crashInfo.exceptionClassName, + crashInfo.exceptionMessage, + crashInfo.throwFileName, + crashInfo.throwLineNumber); + + addExceptionToDropBox("crash", r, null, crashInfo); + + crashApplication(r, crashInfo); + } + + /** + * Used by {@link Log} via {@link com.android.internal.os.RuntimeInit} to report serious errors. + * @param app object of the crashing app, null for the system server + * @param tag reported by the caller + * @param crashInfo describing the context of the error + * @return true if the process should exit immediately (WTF is fatal) + */ + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) { - AppErrorResult result = new AppErrorResult(); - ProcessRecord r = null; + ProcessRecord r = findAppProcess(app); + + EventLog.writeEvent(EventLogTags.AM_WTF, Binder.getCallingPid(), + app == null ? "system" : (r == null ? "unknown" : r.processName), + tag, crashInfo.exceptionMessage); + + addExceptionToDropBox("wtf", r, tag, crashInfo); + + if (Settings.Gservices.getInt(mContext.getContentResolver(), + Settings.Gservices.WTF_IS_FATAL, 0) != 0) { + crashApplication(r, crashInfo); + return true; + } else { + return false; + } + } + + /** + * @param app object of some object (as stored in {@link com.android.internal.os.RuntimeInit}) + * @return the corresponding {@link ProcessRecord} object, or null if none could be found + */ + private ProcessRecord findAppProcess(IBinder app) { + if (app == null) { + return null; + } + + synchronized (this) { + for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { + final int NA = apps.size(); + for (int ia=0; ia<NA; ia++) { + ProcessRecord p = apps.valueAt(ia); + if (p.thread != null && p.thread.asBinder() == app) { + return p; + } + } + } + + Log.w(TAG, "Can't find mystery application: " + app); + return null; + } + } + + /** + * Write a description of an exception (from a crash or WTF report) to the drop box. + * @param eventType to include in the drop box tag ("crash", "wtf", etc.) + * @param r the process which crashed, null for the system server + * @param tag supplied by the application (in the case of WTF), or null + * @param crashInfo describing the exception + */ + private void addExceptionToDropBox(String eventType, ProcessRecord r, String tag, + ApplicationErrorReport.CrashInfo crashInfo) { + String dropboxTag, processName; + if (r == null) { + dropboxTag = "system_server_" + eventType; + } else if ((r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + dropboxTag = "system_app_" + eventType; + } else { + dropboxTag = "data_app_" + eventType; + } + + DropBoxManager dbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE); + if (dbox != null && dbox.isTagEnabled(dropboxTag)) { + StringBuilder sb = new StringBuilder(1024); + sb.append("Build: ").append(Build.FINGERPRINT).append("\n"); + if (r == null) { + sb.append("Process: system_server\n"); + } else { + sb.append("Package: ").append(r.info.packageName).append("\n"); + if (!r.processName.equals(r.info.packageName)) { + sb.append("Process: ").append(r.processName).append("\n"); + } + } + if (tag != null) { + sb.append("Tag: ").append(tag).append("\n"); + } + if (crashInfo != null && crashInfo.stackTrace != null) { + sb.append("\n").append(crashInfo.stackTrace); + } + dbox.addText(dropboxTag, sb.toString()); + } + } + + /** + * Bring up the "unexpected error" dialog box for a crashing app. + * Deal with edge cases (intercepts from instrumented applications, + * ActivityController, error intent receivers, that sort of thing). + * @param r the application crashing + * @param crashInfo describing the failure + */ + private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { long timeMillis = System.currentTimeMillis(); String shortMsg = crashInfo.exceptionClassName; String longMsg = crashInfo.exceptionMessage; @@ -8818,20 +8936,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen longMsg = shortMsg; } + AppErrorResult result = new AppErrorResult(); synchronized (this) { - if (app != null) { - for (SparseArray<ProcessRecord> apps : mProcessNames.getMap().values()) { - final int NA = apps.size(); - for (int ia=0; ia<NA; ia++) { - ProcessRecord p = apps.valueAt(ia); - if (p.thread != null && p.thread.asBinder() == app) { - r = p; - break; - } - } - } - } - if (r != null) { // The application has crashed. Send the SIGQUIT to the process so // that it can dump its state. @@ -8844,7 +8950,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen try { String name = r != null ? r.processName : null; int pid = r != null ? r.pid : Binder.getCallingPid(); - if (!mController.appCrashed(name, pid, tag, + if (!mController.appCrashed(name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { Log.w(TAG, "Force-killing crashed app " + name + " at watcher's request"); @@ -8872,15 +8978,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } - if (r != null) { - if (!makeAppCrashingLocked(r, tag, shortMsg, longMsg, stackTrace)) { - return; - } - } else { - Log.w(TAG, "Some application object " + app + " tag " + tag - + " has crashed, but I don't know who it is."); - Log.w(TAG, "ShortMsg:" + shortMsg); - Log.w(TAG, "LongMsg:" + longMsg); + // If we can't identify the process or it's already exceeded its crash quota, + // quit right away without showing a crash dialog. + if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) { Binder.restoreCallingIdentity(origId); return; } diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags index 7e4ea35..952555b 100644 --- a/services/java/com/android/server/am/EventLogTags.logtags +++ b/services/java/com/android/server/am/EventLogTags.logtags @@ -79,4 +79,7 @@ option java_package com.android.server.am # The activity manager gave up on a new process taking too long to start 30037 am_process_start_timeout (PID|1|5),(UID|1|5),(Process Name|3) - +# Unhandled exception +30039 am_crash (PID|1|5),(Process Name|3),(Exception|3),(Message|3),(File|3),(Line|1|5) +# Log.wtf() called +30040 am_wtf (PID|1|5),(Process Name|3),(Tag|3),(Message|3) |