Android Crash處理流程分析

Android的crash主要有3種,java層的force close,native層的crash和ANR。檢查這三種crash的log方法也不相同:分別搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。這三種crash的處理流程有不同,也有很多共性,但不管哪種crash,都先要導致瞭解下App的啓動流程。

App的啓動流程

App啓動時,如果不和其他應用shareUserId,將會爲自己創建一個進程空間,是通過調用ActivityManagerService的startProcessLocked()完成的,而Android的crash主要有3種,java層的force close,native層的crash和ANR。檢查這三種crash的log方法也不相同:分別搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。這三種crash的處理流程有不同,也有很多共性,但不管哪種crash,都先要導致瞭解下App的啓動流程。

App的啓動流程

App啓動時,如果不和其他應用shareUserId,將會爲自己創建一個進程空間,是通過調用ActivityManagerService的startProcessLocked()完成的,用下面的流程圖表示:

這裏寫圖片描述

第4步是不準確的,Zygote起來後,ZygoteInit就把runSelectLoop起來了,一直在等着有人通過socket連接它。第7步中,已經fork了App進程,從這後面的代碼就是運行在App的進程中了,下面重點看下commonInit函數的處理流程。

Force close的處理流程

UncaughtHandler

App之所以出現了force close,就是有異常拋出,而App應用沒有catch到。上節說到commonInit,這兒看下commonInit的代碼:

    private static final void commonInit() {
        ......

        /* set default handler; this applies to all threads in the VM */
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());

        ......
    }

設置了一個默認的異常處理器。

    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);
                }

                ......

                // 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);
            }
        }
    }

在這裏面會打出異常棧中的信息,之所以force close搜索時使用“FATAL EXCEPTION”也是在這兒體現的。

ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e))從註釋上看是要調用crash的對話框,過會看下它,在finally的處理中,會將發生異常的進程幹掉。

handleApplicationCrash的處理流程

ActivityManagerNative.getDefault()返回的ActivityManagerProxy的實例,每一個應用進程中都有一個唯一的ActivityManagerProxy實例,它通過Binder發送命令“HANDLE_APPLICATION_CRASH_TRANSACTION”給ActivityManagerNative(實際上就是ActivityManagerService)的Server端,然後ActivityManagerService的handleApplicationCrash函數就會被調用。

    public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
        ProcessRecord r = findAppProcess(app, "Crash");
        final String processName = app == null ? "system_server"
                : (r == null ? "unknown" : r.processName);

        handleApplicationCrashInner("crash", r, processName, crashInfo);
    }

這裏面有兩個參數,第一個參數,就是UncaughtHandler實例傳進來的mApplicationObject,第二個參數是根據Throwable構建的一個ApplicationErrorReport.CrashInfo實例。第二個參數很容易懂,那麼第一個參數代表啥意思呢?

第一節中講應用啓動時只說了一個片段,剩下的還要好多步驟。應用的進程創建了,只是創建了一個運行環境而已。在AMS調用Process.start的第一個參數是“android.app.ActivityThread”,在第11步中applicationInit會調用其main方法,main中會實例化一個ActivityThread,所以很多人說ActivityThread代表着App的主線程。ActivityThread中的attach會被調用,在該方法中會調用RuntimeInit.setApplicationObject(mAppThread.asBinder()),此時mAppThread.asBinder())的值被賦給了mApplicationObject,實際上它就是ApplicationThread,而ApplicationThread是作爲App的Binder存在的,從某種意義上代表着ActivityThread。

    public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
        ProcessRecord r = findAppProcess(app, "Crash");
        final String processName = app == null ? "system_server"
                : (r == null ? "unknown" : r.processName);

        handleApplicationCrashInner("crash", r, processName, crashInfo);
    }

ProcessRecord 代表着該應用的進程信息,在想zygote要求fork進程錢會創建它。
findAppProcess會根據ApplicationThread查找到對應的應用進程的記錄。ApplicationThread的值是在完成應用進程的創建後,調用AMS的attachApplication是穿過來的,attachApplication會根據fork進程得到的應用的pid值,查找到ProcessRecord的實例,然後它的makeActive方法設置的。

    void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
            ApplicationErrorReport.CrashInfo crashInfo) {
        EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
                UserHandle.getUserId(Binder.getCallingUid()), processName,
                r == null ? -1 : r.info.flags,
                crashInfo.exceptionClassName,
                crashInfo.exceptionMessage,
                crashInfo.throwFileName,
                crashInfo.throwLineNumber);

        addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);

        mAppErrors.crashApplication(r, crashInfo);
    }
  1. 將crash信息寫入到EventLog中,eventlog保存在/system/etc/event-log-tag中。
  2. 將log記錄到DropBox中,dropbox的log保存在/data/system/dropbox目錄下,它的文件名以system_server或system_app或data_app打頭,然後以_crash開始。
  3. 調用crashApplication繼續後續的處理

crashApplication

    void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
        final long origId = Binder.clearCallingIdentity();
        try {
            crashApplicationInner(r, crashInfo);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
    void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
        ......

        synchronized (mService) {
            /**
             * If crash is handled by instance of {@link android.app.IActivityController},
             * finish now and don't show the app error dialog.
             */
            // 處理有Controller的情況,Controller是一個測試的接口
            if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
                    timeMillis)) {
                return;
            }

            /**
             * If this process was running instrumentation, finish now - it will be handled in
             * {@link ActivityManagerService#handleAppDiedLocked}.
             */
            if (r != null && r.instrumentationClass != null) {
                return;
            }

            ......

            // 彈出crash的對話框
            AppErrorDialog.Data data = new AppErrorDialog.Data();
            ...
            mService.mUiHandler.sendMessage(msg);
        }

        // 等待用戶選擇對話框的結果
        int res = result.get();

        // 根據用戶的選擇處理不同的case
        Intent appErrorIntent = null;
        MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
        if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
            res = AppErrorDialog.FORCE_QUIT;
        }
        synchronized (mService) {
            if (res == AppErrorDialog.MUTE) {
                stopReportingCrashesLocked(r);
            }
            if (res == AppErrorDialog.RESTART) {
                mService.removeProcessLocked(r, false, true, "crash");
                if (task != null) {
                    try {
                        mService.startActivityFromRecents(task.taskId,
                                ActivityOptions.makeBasic().toBundle());
                    } catch (IllegalArgumentException e) {
                        // Hmm, that didn't work, app might have crashed before creating a
                        // recents entry. Let's see if we have a safe-to-restart intent.
                        final Set<String> cats = task.intent.getCategories();
                        if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
                            mService.startActivityInPackage(task.mCallingUid,
                                    task.mCallingPackage, task.intent,
                                    null, null, null, 0, 0,
                                    ActivityOptions.makeBasic().toBundle(),
                                    task.userId, null, null);
                        }
                    }
                }
            }
            if (res == AppErrorDialog.FORCE_QUIT) {
                long orig = Binder.clearCallingIdentity();
                try {
                    // Kill it with fire!
                    mService.mStackSupervisor.handleAppCrashLocked(r);
                    if (!r.persistent) {
                        mService.removeProcessLocked(r, false, false, "crash");
                        mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
                    }
                } finally {
                    Binder.restoreCallingIdentity(orig);
                }
            }
            if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
                appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
            }
            if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
                // XXX Can't keep track of crash time for isolated processes,
                // since they don't have a persistent identity.
                mProcessCrashTimes.put(r.info.processName, r.uid,
                        SystemClock.uptimeMillis());
            }
        }

        if (appErrorIntent != null) {
            try {
                mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
            } catch (ActivityNotFoundException e) {
                Slog.w(TAG, "bug report receiver dissappeared", e);
            }
        }
    }

這些主要是一些清理工作,另外如果在彈出的對話框中,選擇上報,會發送Intent.ACTION_APP_ERROR啓動一個上報的Activity。

這一大坨代碼主要的作用就是做一些善後工作,並將應用進程殺死。

進程死後的善後

在應用進程fork後的attach步驟中,有類似如下的代碼

    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

private final boolean attachApplicationLocked(IApplicationThread thread,
    ......
            try {
            AppDeathRecipient adr = new AppDeathRecipient(
                    app, pid, thread);
            thread.asBinder().linkToDeath(adr, 0);
            app.deathRecipient = adr;
        } catch (RemoteException e) {
            app.resetPackageList(mProcessStats);
            startProcessLocked(app, "link fail", processName);
            return false;
        }
        ......
}

thread就是ApplicationThread類型的實例,這段代碼向ApplicationThread註冊了一個死亡通知,當app的進程被殺死後,AppDeathRecipient的binderDied函數將會被調用。

        public void binderDied() {
            if (DEBUG_ALL) Slog.v(
                TAG, "Death received in " + this
                + " for thread " + mAppThread.asBinder());
            synchronized(ActivityManagerService.this) {
                appDiedLocked(mApp, mPid, mAppThread, true);
            }
        }

appDiedLocked做的工作主要是清理。在cleanUpApplicationRecordLocked函數中還會檢查App的android:persistent是否設置了,如果設置了會重啓該應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章