[toc]
ANR分析
ANR觸發(fā)場景
在android系統(tǒng)中经柴,特定的操作需要在一定時間內(nèi)完成,超過限定的時間就會觸發(fā)ANR。
組件 | 時長 |
---|---|
Service | 前臺服務在20s內(nèi),后臺服務200s內(nèi) 未執(zhí)行完成 |
Content Provider | 內(nèi)容提供者,在publish過超時10s; |
Broadcast | 前臺廣播在10s,后臺廣播60s內(nèi)未執(zhí)行完成 |
Input Dispatching | 輸入事件分發(fā)超時5s,包括按鍵和觸摸事件。 |
Service
service timeout 是位于 ActivityManager 線程中的 AMS.MainHandler收到SERVICE_TIMEOUT_MSG觸發(fā)的剑逃,觸發(fā)時長上表中可以查看,區(qū)分前后臺是通過變量ProcessRecord.
execServicesFg
注冊ANR
在Service啟動流程中官辽,當service attach到system_server進程的過程中會調(diào)用realStartServiceLock(),方法進行ANR的注冊
- ActivityService.java realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException {
...
//發(fā)送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
try {
...
//最終執(zhí)行服務的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
mAm.appDiedLocked(app);
throw e;
} finally {
...
}
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
...
scheduleServiceTimeoutLocked(r.app);
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
//當超時后仍沒有remove該SERVICE_TIMEOUT_MSG消息蛹磺,則執(zhí)行service Timeout流程
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
由此可見,是通過發(fā)送delay的消息進行ANR的注冊同仆,當超過時長(前臺20s,后臺200s就會觸發(fā)ANR)
解除注冊
在目標進程的線程中會進行撤銷發(fā)送上文中提到的SERVICE_TIMEOUT_MSG
- ActivityThread.java
private void handleCreateService(CreateServiceData data) {
...
java.lang.ClassLoader cl = packageInfo.getClassLoader();
Service service = (Service) cl.loadClass(data.info.name).newInstance();
...
try {
//創(chuàng)建ContextImpl對象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
//創(chuàng)建Application對象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
//調(diào)用服務onCreate()方法
service.onCreate();
//解除SERVICE_TIMEOUT_MSG
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
...
}
}
該過程會常見服務對象萤捆,并調(diào)用服務的onCreate方法,然后會通過多次調(diào)用回到system_server來執(zhí)行serviceDoneExecuting
- AS.serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying, boolean finishing) {
...
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
//當前服務所在進程中沒有正在執(zhí)行的service
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
...
}
...
}
當service啟動完成,則移除延時消息SERVICE_TIMEOUT_MSG
觸發(fā)ANR
如果沒有在規(guī)定的時間內(nèi)解除延時消息俗或,那么則就會觸發(fā)ANR市怎。
在system_server進程中有一個Handler線程,叫做“ActivityManager”.當?shù)褂嫊r結(jié)束便會向該Handler線程發(fā)送一條SERVICE_TIMEOUT_MSG.
- ActivityManagerService.java ::MainHandler
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG: {
...
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
...
}
...
}
}
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
Slog.w(TAG, "Timeout executing service: " + timeout);
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
}
}
if (anrMessage != null) {
//當存在timeout的service辛慰,則執(zhí)行appNotResponding
mAm.appNotResponding(proc, null, null, false, anrMessage);
}
}
BroadcastReceiver
當ActivityManager線程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息時就會觸發(fā)ANR
處理廣播中的anr消息
在廣播的啟動流程中区匠,通過調(diào)用processNextBroadcast來處理廣播,其流程為
- 并行廣播
- 當前有序廣播
- 有序廣播
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
...
//part 2: 處理當前有序廣播
do {
r = mOrderedBroadcasts.get(0);
//獲取所有該廣播所有的接收者
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
//當廣播處理時間超時帅腌,則強制結(jié)束這條廣播
broadcastTimeoutLocked(false);
...
}
}
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
//處理廣播消息消息
performReceiveLocked(r.callerApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.userId);
r.resultTo = null;
}
//取消廣播超時ANR
cancelBroadcastTimeoutLocked();
}
} while (r == null);
...
//part 3: 獲取下條有序廣播
r.receiverTime = SystemClock.uptimeMillis();
if (!mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mTimeoutPeriod;
//設置廣播超時anr
setBroadcastTimeoutLocked(timeoutTime);
}
...
}
}
對于廣播超時處理時機
首先在part3過程中setBroadcastTimeoutLocked(timeoutTime) 設置超時廣播消息驰弄;
-
然后在part2根據(jù)廣播處理情況來處理:
當廣播接收者等待時間過長,則調(diào)用 broadcastTimeoutLocked(false);
當執(zhí)行完廣播,則調(diào)用cancelBroadcastTimeoutLocked;
setBroadcastTimeoutLocked
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
取消設置廣播的超時anr和service類似速客,但是通過靜態(tài)注冊的廣播超時受SharePreference(SP)影響
相關(guān)代碼如下,只有xml靜態(tài)注冊的光爆超時檢測過程會考慮是否有SP尚未完成戚篙,動態(tài)廣播并不受其影響
public final void finish() {
if (mType == TYPE_COMPONENT) {
final IActivityManager mgr = ActivityManager.getService();
if (QueuedWork.hasPendingWork()) {
//當SP有未同步到磁盤的工作,則需等待其完成溺职,才告知系統(tǒng)已完成該廣播
QueuedWork.queue(new Runnable() {
public void run() {
sendFinished(mgr);
}
}, false);
} else {
sendFinished(mgr);
}
} else if (mOrderedHint && mType != TYPE_UNREGISTERED) {
final IActivityManager mgr = ActivityManager.getService();
sendFinished(mgr);
}
}
final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
}
觸發(fā)anr
- BroadcastQueue.java ::BroadcastHandler
private final class BroadcastHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
...
}
...
}
}
//fromMsg = true
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
if (mOrderedBroadcasts.size() == 0) {
return;
}
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mOrderedBroadcasts.get(0);
if (fromMsg) {
if (mService.mDidDexOpt) {
mService.mDidDexOpt = false;
long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
setBroadcastTimeoutLocked(timeoutTime);
return;
}
if (!mService.mProcessesReady) {
return; //當系統(tǒng)還沒有準備就緒時岔擂,廣播處理流程中不存在廣播超時
}
long timeoutTime = r.receiverTime + mTimeoutPeriod;
if (timeoutTime > now) {
//如果當前正在執(zhí)行的receiver沒有超時,則重新設置廣播超時
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
BroadcastRecord br = mOrderedBroadcasts.get(0);
if (br.state == BroadcastRecord.WAITING_SERVICES) {
//廣播已經(jīng)處理完成浪耘,但需要等待已啟動service執(zhí)行完成乱灵。當?shù)却銐驎r間,則處理下一條廣播点待。
br.curComponent = null;
br.state = BroadcastRecord.IDLE;
processNextBroadcast(false);
return;
}
r.receiverTime = now;
//當前BroadcastRecord的anr次數(shù)執(zhí)行加1操作
r.anrCount++;
if (r.nextReceiver <= 0) {
return;
}
...
Object curReceiver = r.receivers.get(r.nextReceiver-1);
//查詢App進程
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter)curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
synchronized (mService.mPidsSelfLocked) {
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}
if (app != null) {
anrMessage = "Broadcast of " + r.intent.toString();
}
if (mPendingBroadcast == r) {
mPendingBroadcast = null;
}
//繼續(xù)移動到下一個廣播接收者
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
if (anrMessage != null) {
// BroadcastQueue.java AppNotResponding
mHandler.post(new AppNotResponding(app, anrMessage));
}
}
private final class AppNotResponding implements Runnable {
...
public void run() {
// 進入ANR處理流程
mService.appNotResponding(mApp, null, null, false, mAnnotation);
}
}
- mOrderedBroadcasts已處理完成阔蛉,則不會anr;
- 在執(zhí)行dexopt弃舒,則不會anr;
- 系統(tǒng)還沒有進入ready狀態(tài)(mProcessesReady=false)癞埠,則不會anr;
- 如果當前正在執(zhí)行的receiver沒有超時,則重新設置廣播超時聋呢,不會anr;
ContnentProvider
ContentProvider Timeout是位于”ActivityManager”線程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息時觸發(fā)苗踪。
設置ANR超時
ContentProvider 超時為CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s. 這個跟前面的Service和BroadcastQueue完全不同, 由Provider進程啟動過程相關(guān).當進程創(chuàng)建后悔調(diào)用attachApplicationLocked()進入system_server進程.
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid); // 根據(jù)pid獲取ProcessRecord
}
}
...
//系統(tǒng)處于ready狀態(tài)或者該app為FLAG_PERSISTENT進程則為true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ? generateApplicationProvidersLocked(app) : null;
//app進程存在正在啟動中的provider,則超時10s后發(fā)送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
thread.bindApplication(...);
...
}
移除超時消息
當provider成功publish之后,移除延時的timeout消息
- AMS.publishContentProviders
public final void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) {
...
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
final int N = providers.size();
for (int i = 0; i < N; i++) {
ContentProviderHolder src = providers.get(i);
...
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst); //將該provider添加到mProviderMap
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int launchingCount = mLaunchingProviders.size();
int j;
boolean wasInLaunchingProviders = false;
for (j = 0; j < launchingCount; j++) {
if (mLaunchingProviders.get(j) == dst) {
//將該provider移除mLaunchingProviders隊列
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
launchingCount--;
}
}
//成功pubish則移除該消息
if (wasInLaunchingProviders) {
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
//喚醒客戶端的wait等待方法
dst.notifyAll();
}
...
}
}
}
}
觸發(fā)超時
在system_server中有一個handler線程叫做“ActivityManager”。當?shù)褂嫊r結(jié)束便會向該handler線程發(fā)送一條信息 CONTENT_PROVIDER_PUBLISH_TIMNEOUT_MSG
- ActivityManagerService.java -MainHandler.handleMessage
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
...
ProcessRecord app = (ProcessRecord)msg.obj;
synchronized (ActivityManagerService.this) {
processContentProviderPublishTimedOutLocked(app);
}
} break;
...
}
...
}
}
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
removeProcessLocked(app, false, true, "timeout publishing content providers");
}
boolean cleanupAppInLaunchingProvidersLocked(ProcessRecord app, boolean alwaysBad) {
boolean restart = false;
for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = mLaunchingProviders.get(i);
if (cpr.launchingApp == app) {
if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) {
restart = true;
} else {
//移除死亡的provider
removeDyingProviderLocked(app, cpr, true);
}
}
}
return restart;
}
- 對于stable類型的provider(即conn.stableCount > 0),則會殺掉所有跟該provider建立stable連接的非persistent進程.
- 對于unstable類的provider(即conn.unstableCount > 0),并不會導致client進程被級聯(lián)所殺.
總結(jié)
超時檢測
Service超時檢測機制:
超過一定時間沒有執(zhí)行完相應操作來觸發(fā)移除延時消息削锰,則會觸發(fā)anr;
BroadcastReceiver超時檢測機制:有序廣播的總執(zhí)行時間超過 2* receiver個數(shù) * timeout時長通铲,則會觸發(fā)anr;
有序廣播的某一個receiver執(zhí)行過程超過 timeout時長,則會觸發(fā)anr;
另外:對于Service, Broadcast, Input發(fā)生ANR之后,最終都會調(diào)用AMS.appNotResponding;
對于provider,在其進程啟動時publish過程可能會出現(xiàn)ANR, 則會直接殺進程以及清理相應信息,而不會彈出ANR的對話框
對于input anr 可通過adb shell dumpsys input來查看手機當前的input狀態(tài), 輸出內(nèi)容分別為EventHub.dump(), InputReader.dump(),InputDispatcher.dump()這3類,另外如果發(fā)生過input ANR,那么也會輸出上一個ANR的狀態(tài).
當ANR出現(xiàn)時器贩,無論是四大組件還是進程等颅夺,都是調(diào)用到AMS.appNotResponding()方法,provider除外
- AMS.appNotResponding
final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, boolean aboveSystem, final String annotation) {
...
updateCpuStatsNow(); //第一次 更新cpu統(tǒng)計信息
synchronized (this) {
//PowerManager.reboot() 會阻塞很長時間蛹稍,因此忽略關(guān)機時的ANR
if (mShuttingDown) {
return;
} else if (app.notResponding) {
return;
} else if (app.crashing) {
return;
}
//記錄ANR到EventLog
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
app.processName, app.info.flags, annotation);
// 將當前進程添加到firstPids
firstPids.add(app.pid);
int parentPid = app.pid;
//將system_server進程添加到firstPids
if (MY_PID != app.pid && MY_PID != parentPid) firstPids.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) {
if (r.persistent) {
firstPids.add(pid); //將persistent進程添加到firstPids
} else {
lastPids.put(pid, Boolean.TRUE); //其他進程添加到lastPids
}
}
}
}
}
// 記錄ANR輸出到main log
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
//創(chuàng)建CPU tracker對象
final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
//輸出traces信息
File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker,
lastPids, NATIVE_STACKS_OF_INTEREST);
updateCpuStatsNow(); //第二次更新cpu統(tǒng)計信息
//記錄當前各個進程的CPU使用情況
synchronized (mProcessCpuTracker) {
cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
}
//記錄當前CPU負載情況
info.append(processCpuTracker.printCurrentLoad());
info.append(cpuInfo);
//記錄從anr時間開始的Cpu使用情況
info.append(processCpuTracker.printCurrentState(anrTime));
//輸出當前ANR的reason吧黄,以及CPU使用率、負載信息
Slog.e(TAG, info.toString());
//將traces文件 和 CPU使用率信息保存到dropbox唆姐,即data/system/dropbox目錄
addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
cpuInfo, tracesFile, null);
synchronized (this) {
...
//后臺ANR的情況, 則直接殺掉
if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
app.kill("bg anr", true);
return;
}
//設置app的ANR狀態(tài)拗慨,病查詢錯誤報告receiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
//重命名trace文件
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath != null && tracesPath.length() != 0) {
//traceRenameFile = "/data/anr/traces.txt"
File traceRenameFile = new File(tracesPath);
String newTracesPath;
int lpos = tracesPath.lastIndexOf (".");
if (-1 != lpos)
// 新的traces文件= /data/anr/traces_進程名_當前日期.txt
newTracesPath = tracesPath.substring (0, lpos) + "_" + app.processName + "_" + mTraceDateFormat.format(new Date()) + tracesPath.substring (lpos);
else
newTracesPath = tracesPath + "_" + app.processName;
traceRenameFile.renameTo(new File(newTracesPath));
}
//彈出ANR對話框
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = SHOW_NOT_RESPONDING_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
//向ui線程發(fā)送,內(nèi)容為SHOW_NOT_RESPONDING_MSG的消息
mUiHandler.sendMessage(msg);
}
}
ANR發(fā)生后日志和log信息
當ANR時,會按順序依次執(zhí)行:
- 輸出ANR Reason信息到EventLog. 也就是說ANR觸發(fā)的時間點最接近的就是EventLog中輸出的am_anr信息;
- 收集并輸出重要進程列表中的各個線程的traces信息赵抢,該方法較耗時;
- 輸出當前各個進程的CPU使用情況以及CPU負載情況;
- 將traces文件和 CPU使用情況信息保存到dropbox剧蹂,即data/system/dropbox目錄
- 根據(jù)進程類型,來決定直接后臺殺掉,還是彈框告知用戶.
ANR輸出重要進程的traces信息,這些重要進程包括:
firstPids隊列:第一個是ANR進程烦却,第二個是system_server宠叼,剩余是所有persistent進程;
Native隊列:是指/system/bin/目錄的mediaserver,sdcard 以及surfaceflinger進程其爵;
lastPids隊列: 是指mLruProcesses中的不屬于firstPids的所有進程车吹。
AMS.dumpStackTraces
public static File dumpStackTraces(boolean clearTraces, ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
//默認為 data/anr/traces.txt
String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
if (tracesPath == null || tracesPath.length() == 0) {
return null;
}
File tracesFile = new File(tracesPath);
try {
//當clearTraces,則刪除已存在的traces文件
if (clearTraces && tracesFile.exists()) tracesFile.delete();
//創(chuàng)建traces文件
tracesFile.createNewFile();
FileUtils.setPermissions(tracesFile.getPath(), 0666, -1, -1);
} catch (IOException e) {
return null;
}
//輸出trace內(nèi)容
dumpStackTraces(tracesPath, firstPids, processCpuTracker, lastPids, nativeProcs);
return tracesFile;
}
//這里會保證data/anr/traces.txt文件內(nèi)容是全新的方式醋闭,而非追加窄驹。
private static void dumpStackTraces(String tracesPath, ArrayList<Integer> firstPids, ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
@Override
public synchronized void onEvent(int event, String path) { notify(); }
};
try {
observer.startWatching();
//首先,獲取最重要進程的stacks
if (firstPids != null) {
try {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
synchronized (observer) {
//向目標進程發(fā)送signal來輸出traces
Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);
observer.wait(200); //等待直到寫關(guān)閉证逻,或者200ms超時
}
}
} catch (InterruptedException e) {
Slog.wtf(TAG, e);
}
}
//下一步乐埠,獲取native進程的stacks
if (nativeProcs != null) {
int[] pids = Process.getPidsForCommands(nativeProcs);
if (pids != null) {
for (int pid : pids) {
//輸出native進程的trace【見小節(jié)4】
Debug.dumpNativeBacktraceToFile(pid, tracesPath);
}
}
}
if (processCpuTracker != null) {
processCpuTracker.init();
System.gc();
processCpuTracker.update();
synchronized (processCpuTracker) {
processCpuTracker.wait(500); //等待500ms
}
//測量CPU使用情況
processCpuTracker.update();
//從lastPids中選取CPU使用率 top 5的進程,輸出這些進程的stacks
final int N = processCpuTracker.countWorkingStats();
int numProcs = 0;
for (int i=0; i<N && numProcs<5; i++) {
ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
if (lastPids.indexOfKey(stats.pid) >= 0) {
numProcs++;
synchronized (observer) {
Process.sendSignal(stats.pid, Process.SIGNAL_QUIT);
observer.wait(200);
}
}
}
}
} finally {
observer.stopWatching();
}
}
dumpStackTraces方法主要輸出
- 收集firstPids進程的stacks囚企;
第一個是發(fā)生ANR進程丈咐;
第二個是system_server;
mLruProcesses中所有的persistent進程龙宏; - 收集Native進程的stacks棵逊;(dumpNativeBacktraceToFile)
依次是mediaserver,sdcard,surfaceflinger進程; - 收集lastPids進程的stacks;银酗;
依次輸出CPU使用率top 5的進程辆影;
觸發(fā)ANR時系統(tǒng)會輸出關(guān)鍵信息,依次打印各個進程信息和cpu的使用情況黍特,將會比較耗時
- 最接近ANR發(fā)生時間的是am_anr信息蛙讥,輸出到EventLog,所以查看ANR的起點應該看EventLog信息
- 獲取重要進程trace信息,保存到/data/anr/traces.txt灭衷;(會先刪除老的文件次慢、有的會重新命名trave文件)
- Java進程的traces;
- Native進程的traces;
- ANR reason以及cpu使用情況信息輸出到main log
- 再講cpu使用情況和進程trace文件信息保存到/data/system/dropbox;路徑下
ANR簡單案例分析
首先在頁面中制造一個簡單的ANR
當我們點擊這個按鈕的時候翔曲,就會觸發(fā)ANR迫像,然后在logcat中我們可以看到如下日志
在traces文件中(位于data/anr)可以看到主線程的線程狀態(tài)為SLEEPING
這只是一個非常簡單的案例,實際問題的分析會相對復雜的多瞳遍,但是通過分析log日志和trace日志闻妓,搜索一些關(guān)鍵字,來定位日志位置比如要排查的app進程包名傅蹂,主線程名字“main”纷闺,和cpu狀態(tài)等等可以獲取一些有效的信息