Flutter 引擎崩溃分析方法简介
Android 项目信息 Flutter 后上线,偶尔会遇到一些 Flutter 引擎的崩溃。简单记录下如何还原引擎崩溃日志,方便在源码中定位问题。
Flutter Engine Crash 堆栈还原
首先,在 bin/internal/engine.version
找到 flutter engine revision
1 | b851c718295a896918dc93cb1ff14f2f895a1b90 |
第二步,下载带符号表的 libflutter.so
文件
1 | https://console.cloud.google.com/storage/browser/flutter_infra/flutter/b851c718295a896918dc93cb1ff14f2f895a1b90 |
第三步,利用 ndk-stack
还原堆栈信息
1 | /path/to/android-ndk-r16b/ndk-stack -sym /path/to/symbols/armeabi-v7a -dump /path/to/stacktrace.txt |
如果是将 crash 上报到 bugly 平台,则不必使用 ndk-stack
,直接在 bugly 上配置符号表即可。
配置成功后就能在 bugly 平台上看到 native crash 堆栈信息。
原始堆栈信息
还原后的堆栈信息
拿到还原后的堆栈信息,就可以对着源码分析问题了。
一个疑问
官方提到每个 libflutter.so
文件中有一个 build id,native crash 堆栈中见到的 BuildId
要跟 libflutter.so
的匹配,否则没法还原 native crash 堆栈信息。原文如下:
The build system sets a build id for each libflutter.so file. In the tombstones, you would see the ID like so:
1 #00 pc 000000000062d6e0 /data/app/com.app-tARy3eLH2Y-QN8J0d0WFog==/lib/arm64/libflutter.so!libflutter.so (offset 0x270000) (BuildId: 34ad5bdf0830d77a)This equals to a build id of 34ad5bdf0830d77a. The libflutter.so debug files downloaded as shown above could be verified using the file command:
1
2 % file ~/Downloads/libflutter.so
/Users/user/Downloads/libflutter.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[xxHash]=34ad5bdf0830d77a, with debug_info, not stripped
Ensure the build IDs match, else you will not be able to symbolicate.
不过我往 bugly 上传了一个非严格匹配的符号表(上报 crash 的 app 使用的是自己编译的 libflutter.so
,符号表是官方提供的 libflutter.so
),也能还原出来。让人很是疑惑。
观察了下,bugly 上看到的 native crash 日志中并没有找到类似 (BuildId: 34ad5bdf0830d77a)
这样的信息。推测由此导致匹配变得不严格,所以也能还原出来。
实例
某个 native crash 还原后得到如下堆栈信息。
首先看到的是 message_loop_impl.cc
中的 MessageLoopImpl::RunExpiredTasksNow
。这个方法会立即执行任务队列中的已过期任务。
1 | void MessageLoopImpl::FlushTasks(FlushType type) { |
接下来看到的是 platform_view_android_jni.cc
中的 FlutterViewUpdateSemantics
1 |
|
最后看到的是 logging.cc
第92行。
1 | LogMessage::~LogMessage() { |
初看起来可能会有点不明白 crash 如何产生,梳理一下就渐渐明了:
- 调用
RunExpiredTasksNow
立即执行队伍中的过期任务 - 过期任务中的某一个任务调用了
FlutterViewUpdateSemantics()
(C++层) FlutterViewUpdateSemantics()
调用FlutterJNI.updateSemantics()
(Java层)FlutterViewUpdateSemantics()
调用CheckException()
检查 Java 代码执行后是否产生异常FlutterViewUpdateSemantics()
调用FML_CHECK()
记录错误日志 (如有 Java 异常)
所以我们可以把怀疑目标锁定到 FlutterJNI.updateSemantics()
方法。剩下的工作就是进入 Java 层分析该方法的出错原因了。
1 |
|