当前位置:网站首页>Capture and report the crash log of the flutter app

Capture and report the crash log of the flutter app

2021-01-16 09:34:24 osc_ p23q7y3z

flutter There are two main aspects of crash log collection :

  1. flutter dart Code exception ( contain app and framework There are two cases of code , Generally, it will not cause flashback , Guess why )
  2. flutter engine The crash log of ( It usually goes back )

Flutter App Code exception capture

The code written by people is the accidental product of innumerable anomalies , Code exception is normal .

In addition to adding... In key places  try-catch  Make them out of the known exception , Catching the unknown is the real skill .

For example, in the following code try-catch It's invalid :

1
2
3
4
5
try {
    Future.error("asynchronous surprise");
} catch (e){
    print(e)
}

Fortunately ,Dart There is one  Zone  The concept of , It's kind of similar sandbox It means . Different Zone The code context is different and does not affect each other ,Zone You can also create new subdomains Zone.Zone You can redefine your printtimersmicrotasks And the most important how uncaught errors are handled  Exception handling not caught

1
2
3
4
5
runZoned(() {
    Future.error("asynchronous error");
}, onError: (dynamic e, StackTrace stack) {
    reportError(e, stack);
});

stay  reportError  You can report to the higher authorities ( See the introduction later ).

Flutter framework Exception trapping

register  FlutterError.onError  Callback , Used to collect Flutter framework It's an anomaly .

1
2
3
FlutterError.onError = (FlutterErrorDetails details) {
    reportError(details.exception, details.stack);
};

The error It's usually by  Widget  stay  build  Throw out , as follows :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@override
void performRebuild() {
  Widget built;
  try {
    built = build();
  } catch (e, stack) {
    built = ErrorWidget.builder(_debugReportException(ErrorDescription("building $this"), e, stack));
  } finally {
    _dirty = false;
  }
  try {
    _child = updateChild(_child, built, slot);
  } catch (e, stack) {
    built = ErrorWidget.builder(_debugReportException(ErrorDescription("building $this"), e, stack));
    _child = updateChild(null, built, slot);
  }
}

The code has deletions , Please refer to framework.dart Source code . Among them _debugReportException  Namely FlutterError Call point of :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FlutterErrorDetails _debugReportException(
  DiagnosticsNode context,
  dynamic exception,
  StackTrace stack, {
  InformationCollector informationCollector,
}) {
  final FlutterErrorDetails details = FlutterErrorDetails(
    exception: exception,
    stack: stack,
    library: 'widgets library',
    context: context,
    informationCollector: informationCollector,
  );
  FlutterError.reportError(details);
  return details;
}

Flutter engine Exception trapping

flutter engine Some of the anomalies , With Android For example , Mainly for  libfutter.so What happened .

This part can be given directly to native Crash collection sdk To deal with it , such as  firebase crashlytics、 buglyxCrash  wait

reportError Stack escalation

on-line app There is an exception, although it captures , But just printing it out can't solve the problem , You also need to report it to where developers can see it .

There are a lot of crash collections mentioned above sdk, With bugly For example , It supports custom exception reporting , We just need to dart Exception and stack pass  MethodChannel Pass to bugly sdk that will do .

Dart:

1
2
3
4
5
6
7
8
9
10
11
12
var channel = new MethodChannel("crash_handler");

Future<void> reportError(dynamic exception, StackTrace stack) async {
  try {
    return await channel.invokeMethod("report_error", <String, dynamic>{
      "type": exception.runtimeType.toString(), // exception type
      "message": exception.toString(),          // message
      "stack": stack.toString(),                // stacktrace
    });
  } catch(ignored) {
  }
}

Android Java The code is as follows :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CrashHandler implements MethodCallHandler {
  @Override
  public void onMethodCall(MethodCall call, Result result) {
    switch (call.method) {
      case "report_error":
        postFlutterExcetion(call.argument("type"), call.argument("message"), call.argument("stack"))
        result.success(true);
        break;
      default:
        result.notImplemented();
    }
  }
  
  static void postFlutterExcetion(String excpetionType, String excpetionMessage, String stack) {
    if (!CrashModule.hasInitialized()) return;
    CrashReport.postException(4, excpetionType, excpetionMessage, stack, null);
  }
}

// ...

MethodChannel(flutterView, "crash_handler").setMethodCallHandler(new CrashHandler())

iOS Similar to the registration of a MethodChannel And in handleMethodCall Call in Bugly Of reportExceptionWithCategory that will do .

The rest of the sdk Similar treatment , Don't skip here .

Stack restore

After collecting the exception , You need to look up the symbol table (symbols) Restore stack . Let's say Android End bugly Take the stack as an example :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1 #00 pc 00016998 /system/lib/libc.so (__memcpy_base+104) [armeabi-v7a::2b2dac1c583b68da2f7c58e7ed352851]
2 #01 pc 00158aed /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
3 #02 pc 00138041 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
4 #03 pc 00139461 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
5 #04 pc 00013057 /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi+530) [armeabi-v7a::ac423f49f579c99cfababb65014363e9]
6 #05 pc 00013127 /system/lib/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv+130) [armeabi-v7a::ac423f49f579c99cfababb65014363e9]
7 #06 pc 00007889 /system/lib/libandroid.so (ALooper_pollOnce+64) [armeabi-v7a::954c216fdf1faa9aa08f41bc27503a87]
8 #07 pc 001394fb /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
9 #08 pc 001371eb /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
10 #09 pc 00138441 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
11 #10 pc 0004185b /system/lib/libc.so (_ZL15__pthread_startPv+30) [armeabi-v7a::2b2dac1c583b68da2f7c58e7ed352851]
12 #11 pc 000192a5 /system/lib/libc.so (__start_thread+6) [armeabi-v7a::2b2dac1c583b68da2f7c58e7ed352851]
13 java:
14 [Failed to get Java stack]

First of all, we need to confirm that flutter engine Version number , Execute on the command line :

1
flutter --version

Output is as follows :

1
2
3
4
Flutter 1.5.4-hotfix.2  channel stable  https://github.com/flutter/flutter.git
Framework  revision 7a4c33425d (9 weeks ago)  2019-04-29 11:05:24 -0700
Engine  revision 52c7a1e849
Tools  Dart 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)

You can see Engine Of revision by  52c7a1e849.

secondly , stay  flutter infra  Find the corresponding on cpu abi Of  symbols.zip  And download :https://console.cloud.google.com/storage/browser/flutter_infra/flutter/52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f/android-arm-release

After decompressing , You can get... With symbolic information debug so file —— libflutter.so, And put it in a folder armeabi-v7a Next . If needed x86 And so on , Similar operation .

1
2
mkdir -p ~/Downloads/flutter-52c7a1e849/armeabi-v7a
unzip symbols.zip -d ~/Downloads/flutter-52c7a1e849/armeabi-v7a

Use ndk-stack

Android Manual restore on libflutter.so Stack , have access to NDK Tools provided  ndk-stack

  1. Save the original stack to stack.txt in , Note that the first line should be marked with  *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***  start

    1
    2
    3
    4
    5
    6
    7
    *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    1 #00 pc 00016998 /system/lib/libc.so (__memcpy_base+104) [armeabi-v7a::2b2dac1c583b68da2f7c58e7ed352851]
    2 #01 pc 00158aed /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
    3 #02 pc 00138041 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
    4 #03 pc 00139461 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
    5 #04 pc 00013057 /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi+530) [armeabi-v7a::ac423f49f579c99cfababb65014363e9]
    ...
  2. perform ndk-stack command

    1
    $ANDROID_NDK/ndk-stack -sym ~/Downloads/flutter-52c7a1e849/armeabi-v7a -dump stack.txt > re-stack.txt

Open file re-stack.txt You can see the restored stack :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
********** Crash dump: **********
#00 0x00016998 /system/lib/libc.so (__memcpy_base+104) [armeabi-v7a::2b2dac1c583b68da2f7c58e7ed352851]
#01 0x00158aed /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
fml::WriteAtomically(fml::UniqueObject<int, fml::internal::os_unix::UniqueFDTraits> const&, char const*, fml::Mapping const&)
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/platform/posix/file_posix.cc:203:3
flutter::PersistentCacheStore(fml::RefPtr<fml::TaskRunner>, std::__1::shared_ptr<fml::UniqueObject<int, fml::internal::os_unix::UniqueFDTraits> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::unique_ptr<fml::Mapping, std::__1::default_delete<fml::Mapping> >)::$_0::operator()()
/b/s/w/ir/k/src/out/android_release/../../flutter/shell/common/persistent_cache.cc:114:0
auto fml::internal::CopyableLambda<flutter::PersistentCacheStore(fml::RefPtr<fml::TaskRunner>, std::__1::shared_ptr<fml::UniqueObject<int, fml::internal::os_unix::UniqueFDTraits> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::unique_ptr<fml::Mapping, std::__1::default_delete<fml::Mapping> >)::$_0>::operator()<>() const
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/make_copyable.h:24:0
#02 0x00138041 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
fml::MessageLoopImpl::FlushTasks(fml::MessageLoopImpl::FlushType)
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/message_loop_impl.cc:140:5
fml::MessageLoopImpl::RunExpiredTasksNow()
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/message_loop_impl.cc:148:0
#03 0x00139461 /data/app/com.netease.cartoonreader-1/lib/arm/libflutter.so [armeabi-v7a::2c9d8634bdb07ea641970181b0b00b84]
fml::MessageLoopAndroid::OnEventFired()
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/platform/android/message_loop_android.cc:92:5
fml::MessageLoopAndroid::MessageLoopAndroid()::$_0::operator()(int, int, void*) const
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/platform/android/message_loop_android.cc:42:0
fml::MessageLoopAndroid::MessageLoopAndroid()::$_0::__invoke(int, int, void*)
/b/s/w/ir/k/src/out/android_release/../../flutter/fml/platform/android/message_loop_android.cc:40:0
#04 0x00013057 /system/lib/libutils.so (_ZN7android6Looper9pollInnerEi+530) [armeabi-v7a::ac423f49f579c99cfababb65014363e9]

...

bugly Automatically restore the stack

It's troublesome to manually restore one by one crash stack ( alas How can you collapse so much ), Download it debug so You can also use bugly Upload the symbol table to bugly On , See :https://bugly.qq.com/docs/user-guide/symbol-configuration-android/?v=20181014122344#_4

Output symbol table :

1
java -jar buglySymbolAndroid.jar -i ~/Downloads/flutter-52c7a1e849/armeabi-v7a

After uploading the symbol table ,bugly Will automatically restore the stack , As shown in the figure below

Use atos

iOS Upper Flutter Engine crash stack restore steps and Android similar , Download the corresponding symbol table first (Flutter.dSYM.zip), And then through  atos  Restore , Such as :

1
atos -arch arm64 -o ~/Downloads/flutter_e1e6ced81d029258d449bdec2ba3cddca9c2ca0c_ios-release_Flutter.dSYM/Flutter.dSYM/Contents/Resources/DWARF/Flutter -l 0x000000010277c000 0x00000001027b91dc

Output :

1
fml::MessageLoopDarwin::OnTimerFire(__CFRunLoopTimer*, fml::MessageLoopDarwin*) (in Flutter) (message_loop_darwin.mm:76)

The end of this paper , If you have a better plan, please don't hesitate to leave me a message .

*

*

This part is a welfare for the readers who see it later <(▰˘◡˘▰)>

If you don't want to use a third-party error collection platform , It can be used  https://github.com/flutter/sentry  and  https://sentry.io Of Open source Edition   With the use of their own background . Besides, I fork Version of  https://github.com/yrom/sentry/tree/2.2.0-patch  Using happiness is higher ~.

版权声明
本文为[osc_ p23q7y3z]所创,转载请带上原文链接,感谢
https://chowdera.com/2021/01/20210116093319836H.html

随机推荐