Merge "Simplify & update ANR logging; report ANR data into the dropbox. Eliminate the per-process 200ms timeout during ANR thread-dumping. Dump all the threads at once, then wait for the file to stabilize. Seems to work great and is much, much, much faster."
This commit is contained in:
@@ -53,6 +53,15 @@ public class Build {
|
||||
/** The end-user-visible name for the end product. */
|
||||
public static final String MODEL = getString("ro.product.model");
|
||||
|
||||
/** @pending The system bootloader version number. */
|
||||
public static final String BOOTLOADER = getString("ro.bootloader");
|
||||
|
||||
/** @pending The radio firmware version number. */
|
||||
public static final String RADIO = getString("gsm.version.baseband");
|
||||
|
||||
/** @pending The device serial number. */
|
||||
public static final String SERIAL = getString("ro.serialno");
|
||||
|
||||
/** Various version strings. */
|
||||
public static class VERSION {
|
||||
/**
|
||||
|
||||
@@ -153,14 +153,16 @@ public class FileUtils
|
||||
public static String readTextFile(File file, int max, String ellipsis) throws IOException {
|
||||
InputStream input = new FileInputStream(file);
|
||||
try {
|
||||
if (max > 0) { // "head" mode: read the first N bytes
|
||||
long size = file.length();
|
||||
if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes
|
||||
if (size > 0 && (max == 0 || size < max)) max = (int) size;
|
||||
byte[] data = new byte[max + 1];
|
||||
int length = input.read(data);
|
||||
if (length <= 0) return "";
|
||||
if (length <= max) return new String(data, 0, length);
|
||||
if (ellipsis == null) return new String(data, 0, max);
|
||||
return new String(data, 0, max) + ellipsis;
|
||||
} else if (max < 0) { // "tail" mode: read it all, keep the last N
|
||||
} else if (max < 0) { // "tail" mode: keep the last N
|
||||
int len;
|
||||
boolean rolled = false;
|
||||
byte[] last = null, data = null;
|
||||
@@ -180,7 +182,7 @@ public class FileUtils
|
||||
}
|
||||
if (ellipsis == null || !rolled) return new String(last);
|
||||
return ellipsis + new String(last);
|
||||
} else { // "cat" mode: read it all
|
||||
} else { // "cat" mode: size unknown, read it all in streaming fashion
|
||||
ByteArrayOutputStream contents = new ByteArrayOutputStream();
|
||||
int len;
|
||||
byte[] data = new byte[1024];
|
||||
|
||||
@@ -2942,7 +2942,13 @@ public final class Settings {
|
||||
*/
|
||||
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
|
||||
|
||||
|
||||
/**
|
||||
* If nonzero, ANRs in invisible background processes bring up a dialog.
|
||||
* Otherwise, the process will be silently killed.
|
||||
* @hide
|
||||
*/
|
||||
public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -38,6 +38,10 @@ import java.io.IOException;
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
private static final String TAG = "BootReceiver";
|
||||
|
||||
// Negative meaning capture the *last* 64K of the file
|
||||
// (passed to FileUtils.readTextFile)
|
||||
private static final int LOG_SIZE = -65536;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
@@ -67,16 +71,20 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
private void logBootEvents(Context context) throws IOException {
|
||||
DropBoxManager db = (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE);
|
||||
|
||||
String build =
|
||||
"Build: " + Build.FINGERPRINT + "\nKernel: " +
|
||||
FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n");
|
||||
StringBuilder props = new StringBuilder();
|
||||
props.append("Build: ").append(Build.FINGERPRINT).append("\n");
|
||||
props.append("Hardware: ").append(Build.BOARD).append("\n");
|
||||
props.append("Bootloader: ").append(Build.BOOTLOADER).append("\n");
|
||||
props.append("Radio: ").append(Build.RADIO).append("\n");
|
||||
props.append("Kernel: ");
|
||||
props.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"));
|
||||
|
||||
if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
|
||||
String now = Long.toString(System.currentTimeMillis());
|
||||
SystemProperties.set("ro.runtime.firstboot", now);
|
||||
if (db != null) db.addText("SYSTEM_BOOT", build);
|
||||
if (db != null) db.addText("SYSTEM_BOOT", props.toString());
|
||||
} else {
|
||||
if (db != null) db.addText("SYSTEM_RESTART", build);
|
||||
if (db != null) db.addText("SYSTEM_RESTART", props.toString());
|
||||
return; // Subsequent boot, don't log kernel boot log
|
||||
}
|
||||
|
||||
@@ -98,6 +106,13 @@ public class BootReceiver extends BroadcastReceiver {
|
||||
String setting = "logfile:" + filename;
|
||||
long lastTime = Settings.Secure.getLong(cr, setting, 0);
|
||||
if (lastTime == fileTime) return; // Already logged this particular file
|
||||
db.addFile(tag, file, DropBoxManager.IS_TEXT);
|
||||
Settings.Secure.putLong(cr, setting, fileTime);
|
||||
|
||||
StringBuilder report = new StringBuilder();
|
||||
report.append("Build: ").append(Build.FINGERPRINT).append("\n");
|
||||
report.append("Kernel: ");
|
||||
report.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"));
|
||||
report.append(FileUtils.readTextFile(new File(filename), LOG_SIZE, "[[TRUNCATED]]\n"));
|
||||
db.addText(tag, report.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,7 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
|
||||
if (!match) continue;
|
||||
|
||||
numFound++;
|
||||
if (doPrint) out.append("========================================\n");
|
||||
out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag);
|
||||
if (entry.file == null) {
|
||||
out.append(" (no file)\n");
|
||||
@@ -339,6 +340,12 @@ public final class DropBoxManagerService extends IDropBoxManagerService.Stub {
|
||||
if (n <= 0) break;
|
||||
out.append(buf, 0, n);
|
||||
newline = (buf[n - 1] == '\n');
|
||||
|
||||
// Flush periodically when printing to avoid out-of-memory.
|
||||
if (out.length() > 65536) {
|
||||
pw.write(out.toString());
|
||||
out.setLength(0);
|
||||
}
|
||||
}
|
||||
if (!newline) out.append("\n");
|
||||
} else {
|
||||
|
||||
@@ -74,8 +74,6 @@ class ServerThread extends Thread {
|
||||
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
|
||||
SystemClock.uptimeMillis());
|
||||
|
||||
ActivityManagerService.prepareTraceFile(false); // create dir
|
||||
|
||||
Looper.prepare();
|
||||
|
||||
android.os.Process.setThreadPriority(
|
||||
|
||||
@@ -70,11 +70,12 @@ import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.DropBoxManager;
|
||||
import android.os.Environment;
|
||||
import android.os.FileObserver;
|
||||
import android.os.FileUtils;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
@@ -355,7 +356,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
Integer.valueOf(SystemProperties.get("ro.EMPTY_APP_MEM"))*PAGE_SIZE;
|
||||
}
|
||||
|
||||
final int MY_PID;
|
||||
static final int MY_PID = Process.myPid();
|
||||
|
||||
static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
@@ -1222,7 +1223,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
mSystemThread.getApplicationThread(), info,
|
||||
info.processName);
|
||||
app.persistent = true;
|
||||
app.pid = Process.myPid();
|
||||
app.pid = MY_PID;
|
||||
app.maxAdj = SYSTEM_ADJ;
|
||||
mSelf.mProcessNames.put(app.processName, app.info.uid, app);
|
||||
synchronized (mSelf.mPidsSelfLocked) {
|
||||
@@ -1383,8 +1384,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
|
||||
Log.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
|
||||
|
||||
MY_PID = Process.myPid();
|
||||
|
||||
File dataDir = Environment.getDataDirectory();
|
||||
File systemDir = new File(dataDir, "system");
|
||||
systemDir.mkdirs();
|
||||
@@ -4555,142 +4554,141 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
}
|
||||
|
||||
final String readFile(String filename) {
|
||||
try {
|
||||
FileInputStream fs = new FileInputStream(filename);
|
||||
byte[] inp = new byte[8192];
|
||||
int size = fs.read(inp);
|
||||
fs.close();
|
||||
return new String(inp, 0, 0, size);
|
||||
} catch (java.io.IOException e) {
|
||||
/**
|
||||
* If a stack trace dump file is configured, dump process stack traces.
|
||||
* @param pids of dalvik VM processes to dump stack traces for
|
||||
* @return file containing stack traces, or null if no dump file is configured
|
||||
*/
|
||||
private static File dumpStackTraces(ArrayList<Integer> pids) {
|
||||
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
|
||||
if (tracesPath == null || tracesPath.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return "";
|
||||
|
||||
File tracesFile = new File(tracesPath);
|
||||
try {
|
||||
File tracesDir = tracesFile.getParentFile();
|
||||
if (!tracesDir.exists()) tracesFile.mkdirs();
|
||||
FileUtils.setPermissions(tracesDir.getPath(), 0775, -1, -1); // drwxrwxr-x
|
||||
|
||||
if (tracesFile.exists()) tracesFile.delete();
|
||||
tracesFile.createNewFile();
|
||||
FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1); // -rw-rw-rw-
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Unable to prepare ANR traces file: " + tracesPath, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use a FileObserver to detect when traces finish writing.
|
||||
// The order of traces is considered important to maintain for legibility.
|
||||
FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
|
||||
public synchronized void onEvent(int event, String path) { notify(); }
|
||||
};
|
||||
|
||||
try {
|
||||
observer.startWatching();
|
||||
int num = pids.size();
|
||||
for (int i = 0; i < num; i++) {
|
||||
synchronized (observer) {
|
||||
Process.sendSignal(pids.get(i), Process.SIGNAL_QUIT);
|
||||
observer.wait(200); // Wait for write-close, give up after 200msec
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Log.wtf(TAG, e);
|
||||
} finally {
|
||||
observer.stopWatching();
|
||||
}
|
||||
|
||||
return tracesFile;
|
||||
}
|
||||
|
||||
final void appNotRespondingLocked(ProcessRecord app, HistoryRecord activity,
|
||||
HistoryRecord reportedActivity, final String annotation) {
|
||||
final void appNotRespondingLocked(ProcessRecord app, HistoryRecord activity,
|
||||
HistoryRecord parent, final String annotation) {
|
||||
if (app.notResponding || app.crashing) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Log the ANR to the event log.
|
||||
EventLog.writeEvent(EventLogTags.ANR, app.pid, app.processName, annotation);
|
||||
|
||||
// If we are on a secure build and the application is not interesting to the user (it is
|
||||
// not visible or in the background), just kill it instead of displaying a dialog.
|
||||
boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
|
||||
if (isSecure && !app.isInterestingToUserLocked() && Process.myPid() != app.pid) {
|
||||
Process.killProcess(app.pid);
|
||||
return;
|
||||
}
|
||||
|
||||
// DeviceMonitor.start();
|
||||
|
||||
String processInfo = null;
|
||||
if (MONITOR_CPU_USAGE) {
|
||||
updateCpuStatsNow();
|
||||
synchronized (mProcessStatsThread) {
|
||||
processInfo = mProcessStats.printCurrentState();
|
||||
// Dump thread traces as quickly as we can, starting with "interesting" processes.
|
||||
ArrayList<Integer> pids = new ArrayList<Integer>(20);
|
||||
pids.add(app.pid);
|
||||
|
||||
int parentPid = app.pid;
|
||||
if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
|
||||
if (parentPid != app.pid) pids.add(parentPid);
|
||||
|
||||
if (MY_PID != app.pid && MY_PID != parentPid) pids.add(MY_PID);
|
||||
|
||||
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
|
||||
ProcessRecord r = mLruProcesses.get(i);
|
||||
if (r != null && r.thread != null) {
|
||||
int pid = r.pid;
|
||||
if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) pids.add(pid);
|
||||
}
|
||||
}
|
||||
|
||||
File tracesFile = dumpStackTraces(pids);
|
||||
|
||||
// Log the ANR to the main log.
|
||||
StringBuilder info = mStringBuilder;
|
||||
info.setLength(0);
|
||||
info.append("ANR in process: ");
|
||||
info.append(app.processName);
|
||||
if (reportedActivity != null && reportedActivity.app != null) {
|
||||
info.append(" (last in ");
|
||||
info.append(reportedActivity.app.processName);
|
||||
info.append(")");
|
||||
info.append("ANR in ").append(app.processName);
|
||||
if (activity != null && activity.shortComponentName != null) {
|
||||
info.append(" (").append(activity.shortComponentName).append(")");
|
||||
}
|
||||
if (annotation != null) {
|
||||
info.append("\nAnnotation: ");
|
||||
info.append(annotation);
|
||||
info.append("\nReason: ").append(annotation).append("\n");
|
||||
}
|
||||
if (MONITOR_CPU_USAGE) {
|
||||
info.append("\nCPU usage:\n");
|
||||
info.append(processInfo);
|
||||
if (parent != null && parent != activity) {
|
||||
info.append("\nParent: ").append(parent.shortComponentName);
|
||||
}
|
||||
Log.i(TAG, info.toString());
|
||||
|
||||
// The application is not responding. Dump as many thread traces as we can.
|
||||
boolean fileDump = prepareTraceFile(true);
|
||||
if (!fileDump) {
|
||||
// Dumping traces to the log, just dump the process that isn't responding so
|
||||
// we don't overflow the log
|
||||
Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
|
||||
} else {
|
||||
// Dumping traces to a file so dump all active processes we know about
|
||||
synchronized (this) {
|
||||
// First, these are the most important processes.
|
||||
final int[] imppids = new int[3];
|
||||
int i=0;
|
||||
imppids[0] = app.pid;
|
||||
i++;
|
||||
if (reportedActivity != null && reportedActivity.app != null
|
||||
&& reportedActivity.app.thread != null
|
||||
&& reportedActivity.app.pid != app.pid) {
|
||||
imppids[i] = reportedActivity.app.pid;
|
||||
i++;
|
||||
}
|
||||
imppids[i] = Process.myPid();
|
||||
for (i=0; i<imppids.length && imppids[i] != 0; i++) {
|
||||
Process.sendSignal(imppids[i], Process.SIGNAL_QUIT);
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait(200);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
|
||||
ProcessRecord r = mLruProcesses.get(i);
|
||||
boolean done = false;
|
||||
for (int j=0; j<imppids.length && imppids[j] != 0; j++) {
|
||||
if (imppids[j] == r.pid) {
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!done && r.thread != null) {
|
||||
Process.sendSignal(r.pid, Process.SIGNAL_QUIT);
|
||||
synchronized (this) {
|
||||
try {
|
||||
wait(200);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String cpuInfo = null;
|
||||
if (MONITOR_CPU_USAGE) {
|
||||
updateCpuStatsNow();
|
||||
synchronized (mProcessStatsThread) { cpuInfo = mProcessStats.printCurrentState(); }
|
||||
info.append(cpuInfo);
|
||||
}
|
||||
|
||||
Log.e(TAG, info.toString());
|
||||
if (tracesFile == null) {
|
||||
// There is no trace file, so dump (only) the alleged culprit's threads to the log
|
||||
Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
|
||||
}
|
||||
|
||||
addErrorToDropBox("anr", app, activity, parent, annotation, cpuInfo, tracesFile, null);
|
||||
|
||||
if (mController != null) {
|
||||
try {
|
||||
int res = mController.appNotResponding(app.processName,
|
||||
app.pid, info.toString());
|
||||
// 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
|
||||
int res = mController.appNotResponding(app.processName, app.pid, info.toString());
|
||||
if (res != 0) {
|
||||
if (res < 0) {
|
||||
// wait until the SIGQUIT has had a chance to process before killing the
|
||||
// process.
|
||||
try {
|
||||
wait(2000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
|
||||
Process.killProcess(app.pid);
|
||||
return;
|
||||
}
|
||||
if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid);
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
mController = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Unless configured otherwise, swallow ANRs in background processes & kill the process.
|
||||
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
|
||||
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
|
||||
Process.killProcess(app.pid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the app's notResponding state, and look up the errorReportReceiver
|
||||
makeAppNotRespondingLocked(app,
|
||||
activity != null ? activity.shortComponentName : null,
|
||||
annotation != null ? "ANR " + annotation : "ANR",
|
||||
info.toString());
|
||||
|
||||
// Bring up the infamous App Not Responding dialog
|
||||
Message msg = Message.obtain();
|
||||
HashMap map = new HashMap();
|
||||
msg.what = SHOW_NOT_RESPONDING_MSG;
|
||||
@@ -4704,53 +4702,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a stack trace file has been configured, prepare the filesystem
|
||||
* by creating the directory if it doesn't exist and optionally
|
||||
* removing the old trace file.
|
||||
*
|
||||
* @param removeExisting If set, the existing trace file will be removed.
|
||||
* @return Returns true if the trace file preparations succeeded
|
||||
*/
|
||||
public static boolean prepareTraceFile(boolean removeExisting) {
|
||||
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
|
||||
boolean fileReady = false;
|
||||
if (!TextUtils.isEmpty(tracesPath)) {
|
||||
File f = new File(tracesPath);
|
||||
if (!f.exists()) {
|
||||
// Ensure the enclosing directory exists
|
||||
File dir = f.getParentFile();
|
||||
if (!dir.exists()) {
|
||||
fileReady = dir.mkdirs();
|
||||
FileUtils.setPermissions(dir.getAbsolutePath(),
|
||||
FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
|
||||
} else if (dir.isDirectory()) {
|
||||
fileReady = true;
|
||||
}
|
||||
} else if (removeExisting) {
|
||||
// Remove the previous traces file, so we don't fill the disk.
|
||||
// The VM will recreate it
|
||||
Log.i(TAG, "Removing old ANR trace file from " + tracesPath);
|
||||
fileReady = f.delete();
|
||||
}
|
||||
|
||||
if (removeExisting) {
|
||||
try {
|
||||
f.createNewFile();
|
||||
FileUtils.setPermissions(f.getAbsolutePath(),
|
||||
FileUtils.S_IRWXU | FileUtils.S_IRWXG
|
||||
| FileUtils.S_IWOTH | FileUtils.S_IROTH, -1, -1);
|
||||
fileReady = true;
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Unable to make ANR traces file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileReady;
|
||||
}
|
||||
|
||||
|
||||
private final void decPersistentCountLocked(ProcessRecord app)
|
||||
{
|
||||
app.persistentActivities--;
|
||||
@@ -8684,8 +8635,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
return report;
|
||||
}
|
||||
|
||||
void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog,
|
||||
boolean crashed) {
|
||||
void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) {
|
||||
synchronized (this) {
|
||||
app.crashing = false;
|
||||
app.crashingReport = null;
|
||||
@@ -8698,18 +8648,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
app.waitDialog = null;
|
||||
}
|
||||
if (app.pid > 0 && app.pid != MY_PID) {
|
||||
if (crashed) {
|
||||
handleAppCrashLocked(app);
|
||||
}
|
||||
handleAppCrashLocked(app);
|
||||
Log.i(ActivityManagerService.TAG, "Killing process "
|
||||
+ app.processName
|
||||
+ " (pid=" + app.pid + ") at user's request");
|
||||
Process.killProcess(app.pid);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean handleAppCrashLocked(ProcessRecord app) {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
|
||||
@@ -8808,7 +8755,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
crashInfo.throwFileName,
|
||||
crashInfo.throwLineNumber);
|
||||
|
||||
addExceptionToDropBox("crash", r, null, crashInfo);
|
||||
addErrorToDropBox("crash", r, null, null, null, null, null, crashInfo);
|
||||
|
||||
crashApplication(r, crashInfo);
|
||||
}
|
||||
@@ -8828,7 +8775,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
app == null ? "system" : (r == null ? "unknown" : r.processName),
|
||||
tag, crashInfo.exceptionMessage);
|
||||
|
||||
addExceptionToDropBox("wtf", r, tag, crashInfo);
|
||||
addErrorToDropBox("wtf", r, null, null, tag, null, null, crashInfo);
|
||||
|
||||
if (Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Secure.WTF_IS_FATAL, 0) != 0) {
|
||||
@@ -8865,18 +8812,24 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a description of an exception (from a crash or WTF report) to the drop box.
|
||||
* Write a description of an error (crash, WTF, ANR) 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
|
||||
* @param process which caused the error, null means the system server
|
||||
* @param activity which triggered the error, null if unknown
|
||||
* @param parent activity related to the error, null if unknown
|
||||
* @param subject line related to the error, null if absent
|
||||
* @param report in long form describing the error, null if absent
|
||||
* @param logFile to include in the report, null if none
|
||||
* @param crashInfo giving an application stack trace, null if absent
|
||||
*/
|
||||
private void addExceptionToDropBox(String eventType, ProcessRecord r, String tag,
|
||||
private void addErrorToDropBox(String eventType,
|
||||
ProcessRecord process, HistoryRecord activity, HistoryRecord parent,
|
||||
String subject, String report, File logFile,
|
||||
ApplicationErrorReport.CrashInfo crashInfo) {
|
||||
String dropboxTag, processName;
|
||||
if (r == null) {
|
||||
String dropboxTag;
|
||||
if (process == null || process.pid == MY_PID) {
|
||||
dropboxTag = "system_server_" + eventType;
|
||||
} else if ((r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
} else if ((process.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
|
||||
dropboxTag = "system_app_" + eventType;
|
||||
} else {
|
||||
dropboxTag = "data_app_" + eventType;
|
||||
@@ -8885,20 +8838,37 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
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) {
|
||||
if (process == null || process.pid == MY_PID) {
|
||||
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");
|
||||
sb.append("Process: ").append(process.processName).append("\n");
|
||||
}
|
||||
if (activity != null) {
|
||||
sb.append("Activity: ").append(activity.shortComponentName).append("\n");
|
||||
}
|
||||
if (parent != null && parent.app != null && parent.app.pid != process.pid) {
|
||||
sb.append("Parent-Process: ").append(parent.app.processName).append("\n");
|
||||
}
|
||||
if (parent != null && parent != activity) {
|
||||
sb.append("Parent-Activity: ").append(parent.shortComponentName).append("\n");
|
||||
}
|
||||
if (subject != null) {
|
||||
sb.append("Subject: ").append(subject).append("\n");
|
||||
}
|
||||
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
|
||||
sb.append("\n");
|
||||
if (report != null) {
|
||||
sb.append(report);
|
||||
}
|
||||
if (logFile != null) {
|
||||
try {
|
||||
sb.append(FileUtils.readTextFile(logFile, 128 * 1024, "\n\n[[TRUNCATED]]"));
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Error reading " + logFile, e);
|
||||
}
|
||||
}
|
||||
if (tag != null) {
|
||||
sb.append("Tag: ").append(tag).append("\n");
|
||||
}
|
||||
if (crashInfo != null && crashInfo.stackTrace != null) {
|
||||
sb.append("\n").append(crashInfo.stackTrace);
|
||||
sb.append(crashInfo.stackTrace);
|
||||
}
|
||||
dbox.addText(dropboxTag, sb.toString());
|
||||
}
|
||||
@@ -8924,14 +8894,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
|
||||
AppErrorResult result = new AppErrorResult();
|
||||
synchronized (this) {
|
||||
if (r != null) {
|
||||
// The application has crashed. Send the SIGQUIT to the process so
|
||||
// that it can dump its state.
|
||||
Process.sendSignal(r.pid, Process.SIGNAL_QUIT);
|
||||
//Log.i(TAG, "Current system threads:");
|
||||
//Process.sendSignal(MY_PID, Process.SIGNAL_QUIT);
|
||||
}
|
||||
|
||||
if (mController != null) {
|
||||
try {
|
||||
String name = r != null ? r.processName : null;
|
||||
@@ -10320,7 +10282,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
if (r.startRequested) {
|
||||
info.flags |= ActivityManager.RunningServiceInfo.FLAG_STARTED;
|
||||
}
|
||||
if (r.app != null && r.app.pid == Process.myPid()) {
|
||||
if (r.app != null && r.app.pid == MY_PID) {
|
||||
info.flags |= ActivityManager.RunningServiceInfo.FLAG_SYSTEM_PROCESS;
|
||||
}
|
||||
if (r.app != null && r.app.persistent) {
|
||||
@@ -11623,7 +11585,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
||||
}
|
||||
if (timeout != null && mLruProcesses.contains(proc)) {
|
||||
Log.w(TAG, "Timeout executing service: " + timeout);
|
||||
appNotRespondingLocked(proc, null, null, "Executing service " + timeout.name);
|
||||
appNotRespondingLocked(proc, null, null, "Executing service " + timeout.shortName);
|
||||
} else {
|
||||
Message msg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG);
|
||||
msg.obj = proc;
|
||||
|
||||
@@ -104,8 +104,7 @@ class AppNotRespondingDialog extends BaseErrorDialog {
|
||||
switch (msg.what) {
|
||||
case FORCE_CLOSE:
|
||||
// Kill the application.
|
||||
mService.killAppAtUsersRequest(mProc,
|
||||
AppNotRespondingDialog.this, true);
|
||||
mService.killAppAtUsersRequest(mProc, AppNotRespondingDialog.this);
|
||||
break;
|
||||
case WAIT_AND_REPORT:
|
||||
case WAIT:
|
||||
|
||||
@@ -62,8 +62,7 @@ class AppWaitingForDebuggerDialog extends BaseErrorDialog {
|
||||
switch (msg.what) {
|
||||
case 1:
|
||||
// Kill the application.
|
||||
mService.killAppAtUsersRequest(mProc,
|
||||
AppWaitingForDebuggerDialog.this, true);
|
||||
mService.killAppAtUsersRequest(mProc, AppWaitingForDebuggerDialog.this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user