java层crash通知
当一个进程在通过zygote创建时就会调用 zygoteInit--> commonInit
private static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/* set default handler; this applies to all threads in the VM */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
/*
* Install a TimezoneGetter subclass for ZoneInfo.db
*/
TimezoneGetter.setInstance(new TimezoneGetter() {
@Override
public String getId() {
return SystemProperties.get("persist.sys.timezone");
}
});
TimeZone.setDefault(null);
...
}
然后设置UncaughtHandler监视进程不能捕获的Exception,然后先通知AMS,再调用killProcess干掉自己
/**
* Use this to log a message when a thread exits due to an uncaught
* exception. The framework catches these for the main threads, so
* this should only matter for threads created by applications.
*/
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
try {
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
StringBuilder message = new StringBuilder();
message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
final String processName = ActivityThread.currentProcessName();
if (processName != null) {
message.append("Process: ").append(processName).append(", ");
}
message.append("PID: ").append(Process.myPid());
Clog_e(TAG, message.toString(), e);
}
// Try to end profiling. If a profiler is running at this point, and we kill the
// process (below), the in-memory buffer will be lost. So try to stop, which will
// flush the buffer. (This makes method trace profiling useful to debug crashes.)
if (ActivityThread.currentActivityThread() != null) {
ActivityThread.currentActivityThread().stopProfiling();
}
// Bring up crash dialog, wait for it to be dismissed
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
// System process is dead; ignore
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
// Even Clog_e() fails! Oh well.
}
}
} finally {
// Try everything to make sure this process goes away.
Process.killProcess(Process.myPid());
System.exit(10);
}
}
}
ANR引起的原因有以下几种
BroadcastQueue
Service超时
Input 事件分发无响应
provider无响应
ActivityManagerService中处理error信息,并保存crash日志
java层的Crash,ANR最终都会到addErrorToDropBox处理
- anr/crash
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, ActivityRecord activity,
ActivityRecord parent, String subject,
final String report, final File logFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
// NOTE -- this must never acquire the ActivityManagerService lock,
// otherwise the watchdog may be prevented from resetting the system.
UploadErrorMessage uploadError = UploadErrorMessage.getInstance(mContext);
uploadError.noteProcessError(eventType, process, processName,
activity, parent, subject, report, logFile, crashInfo);
}
上传信息代码
package com.android.server.am;
import android.app.ApplicationErrorReport;
import android.content.Context;
import android.util.Slog;
import java.io.File;
import org.json.JSONStringer;
import org.json.JSONObject;
import org.json.JSONException;
import com.putao.minisdk.MiniCollectionSdk;
/**
* upload message log to server when app chash or anr
* Created by putao on 17-8-2.
*/
class UploadErrorMessage {
private static final String TAG = "UploadErrorMessage";
private static final boolean DEBUG = true;
private final Context mContext;
private static UploadErrorMessage mInstance = null;
// sdk for use upload data to server
private MiniCollectionSdk minisdk = null;
public static UploadErrorMessage getInstance(Context context) {
if (mInstance == null) {
synchronized (UploadErrorMessage.class) {
if (mInstance == null) {
mInstance = new UploadErrorMessage(context);
}
}
}
return mInstance;
}
private UploadErrorMessage(Context context) {
mContext = context;
// config minisdk appid logtype ...
if (minisdk == null) {
minisdk = MiniCollectionSdk.getInstance(context);
}
minisdk.setOnBindChangedListener(new MiniCollectionSdk.OnBindChangedListener() {
public void onBindSuccess() {
Slog.d(TAG, "onBindSuccess...");
minisdk.setDataInfo("Paios", "23", "custom");
minisdk.didFinishLaunched("85bfac7251c93e16b7b946ea5eded05d",
"164594b08b39e5f74a891699da8aff62");
}
});
minisdk.bindService(); // 绑定后台service.
}
/**
* Notice a description of an error (crash, WTF, ANR) to the server.
* @param eventType to include in the drop box tag ("crash", "wtf", etc.)
* @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
*/
public void noteProcessError(String eventType, final ProcessRecord process,
final String processName, final ActivityRecord activity,
final ActivityRecord parent, final String subject,
final String report, final File logFile,
final ApplicationErrorReport.CrashInfo crashInfo) {
if (eventType != null && eventType.equals("anr")) {
new Thread(new Runnable() {
@Override
public void run() {
noteProcessAnr(processName, subject, report, activity.shortComponentName);
}
}).start();
}
if (eventType != null && eventType.equals("crash")) {
new Thread(new Runnable() {
@Override
public void run() {
noteProcessCrash(processName, crashInfo);
}
}).start();
}
}
/**
* Notice that the server this app is crash
*
* @param processName process name of crash
* @param crashInfo Contains all the crash information
*
* eg.
* upload crash json format is as follows
* {
* "processName":"com.autonavi.minimap",
* "type":"crashlog",
* "appVersion":1.6.1,
* "timestamp":1500617925676,
* "errorType":"crash",
* "exceptionClassName":"java.lang.NullPointerException",
* "exceptionMessage":"Attempt to ... on a null object reference",
* "throwFileName":"MainActivity.java",
* "throwClassName":"com.example.putao.crashanrapp.MainActivity",
* "throwMethodName":"CreateCrash",
* "stackTrace":"...",
* "throwLineNumber":"56",
* }
*/
private void noteProcessCrash(String processName,
ApplicationErrorReport.CrashInfo crashInfo) {
JSONStringer json = new JSONStringer();
try {
json.object();
json.key("processName").value(processName);
json.key("type").value("crashlog");
json.key("appVersion").value("unknow");
json.key("timestamp").value(System.currentTimeMillis());
json.key("errorType").value("crash");
json.key("exceptionClassName").value(crashInfo.exceptionClassName);
// Can not contain single quotation marks
json.key("exceptionMessage").value(crashInfo.exceptionMessage.replace('\'', ' '));
json.key("throwFileName").value(crashInfo.throwFileName);
json.key("throwClassName").value(crashInfo.throwClassName);
json.key("throwMethodName").value(crashInfo.throwMethodName);
json.key("stackTrace").value(crashInfo.stackTrace.replace('\'', ' '));
json.key("throwLineNumber").value(crashInfo.throwLineNumber);
json.endObject();
} catch (JSONException e) {
e.printStackTrace();
}
// upload crash log to server
minisdk.addUnlimitedInfo(json.toString(), "custom");
}
/**
* Notice that the server this app is anr
*
* @param processName process name of crash
* @param cause the anr reason
* @param info anr information, contains cpu infomation
* @param activity anr of activity
*
* eg.
* upload anr json format is as follows
* {
* "processName":"com.autonavi.minimap",
* "type":"anrlog",
* "appVersion":1.6.1,
* "timestamp":1500617925676,
* "errorType":"anr",
* "activity":"com.example.putao.crashanrapp/.MainActivity",
* "cause":"Input dispatching timed out (Waiting to send non-key ... ",
* "info":"ANR in com.example.putao.crashanrapp ... ",
* }
*/
private void noteProcessAnr(String processName, String cause,
String info, String activity) {
JSONStringer json = new JSONStringer();
try {
json.object();
json.key("processName").value(processName);
json.key("type").value("anrlog");
json.key("appVersion").value("unknow");
json.key("timestamp").value(System.currentTimeMillis());
json.key("errorType").value("anr");
json.key("activity").value(activity);
json.key("cause").value(cause);
json.key("info").value(info);
json.endObject();
} catch (JSONException e) {
e.printStackTrace();
}
// upload anr log to server
minisdk.addUnlimitedInfo(json.toString(), "custom");
}
}