当前位置:   article > 正文

Framework篇 - framework 层的 crash 处理流程_android 系统framework收集所有进程 crash

android 系统framework收集所有进程 crash

本文源代码基于 Android 7.0。

App crash (全称 Application crash),对于 crash 可分为 native crash 和 framework crash (包含 app crash 在内),对于 crash 相信很多 app 开发者都会遇到,那么上层什么时候会出现 crash 呢,系统又是如何处理 crash 的呢?

例如,在 app 中大家经常使用 try...catch 语句,那么如果没有有效 catch exception,就会导致应用 crash。发现没有 catch exception,系统便会来进行捕获,并进入 crash 流程。如果你是从事 Android 系统开发或者架构相关工作,或者遇到需要解系统性的疑难杂症,那么很有必要了解系统 crash 处理流程。

 

目录:

  1. 概述
  2. framework crash 处理流程
  3. 拓展

 

1. 概述

上层应用都是由 Zygote fork 孵化而来,分为 system_server 系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器。

对于 system_server 进程:前面的文章 SystemServer 进程详解 分析了 system_server 的启动过程。system_server 启动过程中由RuntimeInit.java 的 commonInit 方法设置 UncaughtHandler,用于处理未捕获异常。

对于普通应用进程:进程创建过程中,同样会调用 RuntimeInit.java 的 commonInit 方法设置 UncaughtHandler。

/base/core/java/com/android/internal/os/RuntimeInit.java

  1. private static final void commonInit() {
  2. if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
  3. /* set default handler; this applies to all threads in the VM */
  4. Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
  5. }

注意,Thread.setDefaultUncaughtExceptionHandler() 可以捕获所有线程的异常,而 Thread.currentThread().setDefaultUncaughtExceptionHandler() 是作用在当前线程上的。

 

 

2. framework crash 处理流程

 

  • 2.1 UncaughtHandler

setDefaultUncaughtExceptionHandler() 只是将异常处理器 handler 对象赋给 Thread 成员变量,即Thread.defaultUncaughtHandler = new UncaughtHandler(),接下来看看 UncaughtHandler 对象实例化过程。

/base/core/java/com/android/internal/os/RuntimeInit.java

  1. private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
  2. public void uncaughtException(Thread t, Throwable e) {
  3. try {
  4. // 保证crash处理过程不会重入
  5. if (mCrashing) return;
  6. mCrashing = true;
  7. if (mApplicationObject == null) {
  8. // system_server进程
  9. Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e)
  10. } else {
  11. // 普通应用进程
  12. StringBuilder message = new StringBuilder();
  13. message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");
  14. final String processName = ActivityThread.currentProcessName();
  15. if (processName != null) {
  16. message.append("Process: ").append(processName).append(", ");
  17. }
  18. message.append("PID: ").append(Process.myPid());
  19. Clog_e(TAG, message.toString(), e);
  20. }
  21. if (ActivityThread.currentActivityThread() != null) {
  22. ActivityThread.currentActivityThread().stopProfiling();
  23. }
  24. // 启动crash对话框,等待处理完成
  25. ActivityManagerNative.getDefault().handleApplicationCrash(
  26. mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
  27. } catch (Throwable t2) {
  28. // ...
  29. } finally {
  30. // 确保当前进程彻底杀掉
  31. Process.killProcess(Process.myPid());
  32. System.exit(10);
  33. }
  34. }
  35. }

system 进程 crash 时的 log 信息: 

开头*** FATAL EXCEPTION IN SYSTEM PROCESS [线程名];
接着输出发生crash时的调用栈信息。

app 进程 crash 时的 log 信息:

开头 FATAL EXCEPTION: [线程名];
紧接着 Process: [进程名],PID: [进程id];
最后输出发生 crash 时的调用栈信息。

看到这里,你就会发现要从 log 中搜索 crash 信息,只需要搜索关键词 FATAL EXCEPTION;如果需要进一步筛选只搜索系统crash 信息,则可以搜索的关键词可以有多样,比如 *** FATAL EXCEPTION。

当输出完 crash 信息到 logcat 里面,这只是 crash 流程的刚开始阶段,接下来弹出 crash 对话框,ActivityManagerNative.getDefault() 返回的是 ActivityManagerProxy,ActivityManagerProxy 经过 Binder 调用最终交给ActivityManagerService 中相应的方法去处理,故接下来调用的是 ActivityManagerService.handleApplicationCrash()。

注意: mApplicationObject 等于 null,一定不是普通的 app 进程。但是除了 system 进程,也有可能是 shell 进程,即通过app_process + 命令参数 的方式创建的进程。

 

  • 2.2 ActivityManagerService.handleApplicationCrash()

/base/services/core/java/com/android/server/am/ActivityManagerService.java

  1. public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {
  2. // 获取进程record对象
  3. ProcessRecord r = findAppProcess(app, "Crash");
  4. final String processName = app == null ? "system_server"
  5. : (r == null ? "unknown" : r.processName);
  6. handleApplicationCrashInner("crash", r, processName, crashInfo);
  7. }

有了进程记录对象 ProcessRecord 和进程名 processName,则进入执行 Crash 处理方法。

 

  • 2.3 ActivityManagerService.handleApplicationCrashInner()

/base/services/core/java/com/android/server/am/ActivityManagerService.java

  1. void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,
  2. ApplicationErrorReport.CrashInfo crashInfo) {
  3. // 将Crash信息写入到Event log
  4. EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
  5. UserHandle.getUserId(Binder.getCallingUid()), processName,
  6. r == null ? -1 : r.info.flags,
  7. crashInfo.exceptionClassName,
  8. crashInfo.exceptionMessage,
  9. crashInfo.throwFileName,
  10. crashInfo.throwLineNumber);
  11. // 将错误信息添加到DropBox
  12. addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
  13. mAppErrors.crashApplication(r, crashInfo);
  14. }

addErrorToDropBox 是将 crash 信息输出到目录 /data/system/dropbox,例如 system_server 的 dropbox 文件名为system_server_crash@xxx.txt (xxx代表的是时间戳)。

 

  • 2.4 AppErrors.crashApplication()

/base/services/core/java/com/android/server/am/AppErrors.java

  1. void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
  2. final long origId = Binder.clearCallingIdentity();
  3. try {
  4. crashApplicationInner(r, crashInfo);
  5. } finally {
  6. Binder.restoreCallingIdentity(origId);
  7. }
  8. }
  9. void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
  10. // 时间戳
  11. long timeMillis = System.currentTimeMillis();
  12. // 短消息
  13. String shortMsg = crashInfo.exceptionClassName;
  14. // 长消息
  15. String longMsg = crashInfo.exceptionMessage;
  16. // 堆栈信息
  17. String stackTrace = crashInfo.stackTrace;
  18. if (shortMsg != null && longMsg != null) {
  19. longMsg = shortMsg + ": " + longMsg;
  20. } else if (shortMsg != null) {
  21. longMsg = shortMsg;
  22. }
  23. AppErrorResult result = new AppErrorResult();
  24. TaskRecord task;
  25. synchronized (mService) {
  26. // 当存在 ActivityController 的情况,比如 monkey,则调用 monkey 的 appCrashed处理逻辑并返回
  27. if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
  28. timeMillis)) {
  29. return;
  30. }
  31. if (r != null && r.instrumentationClass != null) {
  32. return;
  33. }
  34. if (r != null) {
  35. mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
  36. }
  37. // 错误弹窗
  38. AppErrorDialog.Data data = new AppErrorDialog.Data();
  39. data.result = result;
  40. data.proc = r;
  41. if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
  42. return;
  43. }
  44. Message msg = Message.obtain();
  45. msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
  46. task = data.task;
  47. msg.obj = data;
  48. mService.mUiHandler.sendMessage(msg);
  49. }
  50. int res = result.get();
  51. Intent appErrorIntent = null;
  52. MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
  53. if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
  54. res = AppErrorDialog.FORCE_QUIT;
  55. }
  56. synchronized (mService) {
  57. if (res == AppErrorDialog.MUTE) {
  58. stopReportingCrashesLocked(r);
  59. }
  60. if (res == AppErrorDialog.RESTART) {
  61. // 重启
  62. }
  63. if (res == AppErrorDialog.FORCE_QUIT) {
  64. long orig = Binder.clearCallingIdentity();
  65. try {
  66. // 杀死进程
  67. } finally {
  68. Binder.restoreCallingIdentity(orig);
  69. }
  70. }
  71. if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
  72. appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo);
  73. }
  74. if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
  75. // XXX Can't keep track of crash time for isolated processes,
  76. // since they don't have a persistent identity.
  77. mProcessCrashTimes.put(r.info.processName, r.uid,
  78. SystemClock.uptimeMillis());
  79. }
  80. }
  81. if (appErrorIntent != null) {
  82. try {
  83. mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
  84. } catch (ActivityNotFoundException e) {
  85. Slog.w(TAG, "bug report receiver dissappeared", e);
  86. }
  87. }
  88. }

当存在 ActivityController 的情况,比如 monkey,则调用 monkey 的 appCrashed处理逻辑并返回。否则弹出 crash 框让用户选择是重启还是退出。

 

 

3. 拓展

        Thread.setDefaultUncaughtExceptionHandler(...);

调用这句代码可以捕获所有线程的异常,看看 Thread 类中的实现:

  1. public class Thread implements Runnable {
  2. private static UncaughtExceptionHandler defaultUncaughtHandler;
  3. public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
  4. return defaultUncaughtHandler;
  5. }
  6. public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
  7. Thread.defaultUncaughtHandler = handler;
  8. }
  9. }

可以看到,就是一个全局变量。所以如果我们 app 如果自己设置了 UncaughtExceptionHandler,是会覆盖掉系统的默认设置的。也就不会执行 framework crash 处理流程,而是执行你自定义的逻辑。如果自己设置 UncaughtExceptionHandler,那么可以用来实现错误日志上报,同时也能不让应用崩溃,不弹出 crash 窗。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/250851
推荐阅读
相关标签
  

闽ICP备14008679号