Umeng+U-APM Flutter Dart exception monitoring practice

63200155eb20fdffa08e26cc8f828014.gifdd34600f34be7e5fc241efbe6191cc3e.gif

As the project scale expands and user needs continue to grow, especially in terms of application stability, we need more rigorous and detailed monitoring methods to ensure a good user experience. As the core of Flutter development, Dart language is responsible for the logic and business implementation of applications. However, due to the flexibility and dynamic nature of Dart, we can easily introduce various potential errors and exceptions into our code. These anomalies may cause the application to function abnormally, seriously affecting the user experience and satisfaction.

Therefore, in order to improve the stability of the application and improve user satisfaction, we specifically support the introduction of Umeng+ U-APMinto the Flutter project /strong>The ability of exception monitoring functions. Umeng + U-APM is a professional mobile application performance monitoring tool that can help us monitor abnormalities in applications in real time and take appropriate measures to deal with them in a timely manner.

Umeng+ U-APMIn terms of Flutter Dart exception monitoring, it automatically captures and records unhandled exception information, helping us quickly locate problems and fix them; it can also analyze the reasons for exceptions. Key information such as version, time, path, and equipment help us deeply understand the causes and patterns of exceptions. In additionUmeng + U-APM also provides an instant alarm function, which can promptly notify the development team when an exception occurs so thatthey can quickly respond and solve the problem.

In this article, we will delve into the application of Umeng + U-APM in practice and share some of our experiences. We hope that through our practice, we can help more developers successfully introduce exception monitoring functions into Flutter projects and build more stable and reliable mobile applications.

1 ? SDK simple architecture diagram

8498208c2243705247224c48231b46a3.jpeg

When Flutter exception monitoring SDK relies on Native APM SDK, you can use Native APM SDK to collect device information and cloud configuration information, and send a signal to Flutter exception monitoring SDK after initialization is completed.

1. Equipment information collection:

Native APM SDK can obtain basic information of the device, such as device model, operating system version, CPU architecture, etc. This information can help developers better understand the user’s device environment and provide reference for abnormal analysis and troubleshooting.

2. Cloud distribution information collection:

Cloud configuration information refers to dynamically updating application configuration parameters through remote configuration. Native APM SDK can obtain cloud configuration information from the remote server and provide this information to the Flutter exception monitoring SDK. For example, you can configure exception monitoring switches, collection strategies, etc. through cloud configuration information.

3. Initialization completion signal:

After the Native APM SDK completes initialization, it can send a signal to the Flutter exception monitoring SDK to notify it to start collection and reporting. This is used to ensure that the Flutter exception monitoring SDK starts and works at the appropriate time.

By relying on Native APM SDK, Flutter exception monitoring SDK can obtain more comprehensive device information and cloud configuration information, improving the accuracy and flexibility of exception monitoring. At the same time, completing signal interaction through initialization can ensure the smoothness and stability of the two SDKs when they work together.

The simple architecture diagram of Flutter exception monitoring SDK mainly includes three parts: initialization, collection and reporting.

1. Initialization:

Initialization of the SDK is completed when the application starts. During the initialization process, the SDK needs to complete the following steps:

1. Configuration parameters: Developers need to provide some necessary configuration parameters, such as Flutter SDK version number, application version number, etc.

2. Register listeners: The SDK needs to register some listeners in order to capture and handle abnormal events and monitor routing behaviors.

2. Collection:

The collection module of the SDK is responsible for collecting exception information that occurs in applications. It can capture dart exceptions and preprocess and standardize these exception information to facilitate subsequent reporting and analysis.

3. Report:

The reporting module of the SDK is responsible for sending the collected exception information to the server. Before reporting, the SDK may perform some processing on the exception information, such as compression, log number and size condition determination, etc.

2 ? Backend data support

3d15f54062de7d5e5daa3757f61eab31.jpeg

047e29bf4bd9db14123604e2c9d2ba04.png

146988d9eeb639378a5e64c0538e7916.png

27d46220a9d3e4b2acd8b0174c62bb6b.png

f6bc27eb87aabebfa47b9983aeb3b19b.png

8b0f3414ebe3f46a40a515fb44c7658b.png

525946d458c21d1a8b58a895736eef6f.png

01

Dart exception

1.1 Abnormal collection

Native exceptions are usually accompanied by application crashes, while dart exceptions provide a flexible mechanism to capture and handle exceptions, which will not directly cause the application to crash. They often appear as white screens on the page, stuck execution operations, etc. They are stable The user experience is not completely equal.

52c4866144aa3de127428103d8edc0b9.jpeg

App exception:

  • It is the logic layer exception of the application code, usually caused by exceptions thrown by other modules in the application layer that are not handled.

  • According to the execution timing of the exception code, App exceptions can be divided into two categories, namely synchronous exceptions and asynchronous exceptions.

Framework exception:

  • Abnormalities of the Flutter framework itself, such as inconsistencies in the widget tree, errors in the code, such as null pointer exceptions, array out-of-bounds, etc., leading to rendering exceptions;

  • Flutter plug-in exceptions, such as exceptions that occur when interacting with native code

How to catch exceptions:

  • Some of the above methods of catching exceptions can be used to catch exceptions in Flutter applications, and different methods are suitable for different scenarios.

○ FlutterError.onError is suitable for global exception handling

○ runZonedGuarded is suitable for catching exceptions within a specific zone

○ PlatformDispatcher.instance.onError is suitable for handling underlying platform exceptions (supported by Flutter SDK v3.3 and above)

○ try catch is suitable for catching exceptions in synchronized code blocks

○ Future.catchError is suitable for catching exceptions in asynchronous operations

1.2 Exception Level

Next, by simulating an array out of bounds and actively triggering an execution exception, we can see that the same type of errors occur in different locations, resulting in inconsistent page performance.

List<int> numbers = [1, 2, 3];
// Simulate array out-of-bounds exception
int fourthNumber = numbers[3];
  • Synchronization initialization exception:

An exception is actively thrown at the execution entry main(). Because the synchronous initialization execution is blocked, subsequent pages cannot be successfully rendered, which appears as a white screen.

5b471bed9563878e4c1b7f3304254fdd.png

  • Uncaught asynchronous error:

Actively throw an exception in Future.delayed, because the asynchronous task does not block the main thread page rendering, so the page behaves normally.

e326f428898e2494869e3c4121662c7a.png

  • Component rendering error:

Drawing actively triggers an exception, causing a framework exception, which appears as a white screen (red screen under dev).

ea4a8e4428c31b4f50f37cc3255da5b8.png

  • Gesture event callback exception:

Flutter’s event processing is asynchronous and processed in a separate thread, which will not block page rendering, but the execution logic after the exception code will be blocked and unable to respond.

168db0c4ccc3c4dc410f326a5bfe99ad.png

1.2.1 Abnormal rating

  • We are divided into three levels: ErrorWidget, Error, and Exception:

    ○ ErrorWidget is a rendering exception of page components, which will affect the user experience. It usually appears as a white screen and requires business focus and resolution.

    ○ Exception means that some unexpected situations occurred during the running of the program, causing the program to fail to execute normally. These situations typically include user input errors, network connection interruptions, file read failures, etc. The Exception class and its subclasses can usually be caught and handled to avoid program crashes or other problems.

    ○ Error means that some serious problems have occurred during the running of the program, such as insufficient memory, stack overflow, etc. These problems are usually caused by errors in the program itself, rather than external factors. The Error class and its subclasses usually cannot be caught, and if these errors occur, the program will usually crash.

  • ErrorWidgetLevel

FlutterError.onError = (FlutterErrorDetails details) {
  final library = details.library;
  final bool isWidgetsLibrary =
      library is String & amp; & amp; 'widgets library'.compareTo(library) == 0;
  if (isWidgetsLibrary) {
    print('A rendering exception occurs, defined as ErrorWidget and usually displays a white screen');
  }
  
}
  • Error and Exception levels

if (e is Exception) {
  print('Caught an Exception: $e');
} else {
  print('Caught an error: $e');
}

We will automatically collect exceptions for judgment and classification, and use log marking to help developers quickly locate urgent and high-priority exceptions that need to be handled in the background.

1.3 Exception subcategories

Flutter exception types mainly include the following (not all inclusive):

1. FlutterError: This is the base class of Flutter exceptions, used to represent errors within the Flutter framework. It provides a message describing the error and optional error details.

try {
  // ...
} catch (e) {
  if (e is FlutterError) {
    print('Flutter Error: ${e.message}');
    print('Error Details: ${e.details}');
  }
}

2. AssertionError: This is an assertion exception used to detect errors or invalid operations during development. This exception is thrown when an assertion fails.

void divide(int a, int b) {
  assert(b != 0, 'Division by zero is not allowed');
  // ...
}

3. FormatException: Indicates an exception caused by a format error. For example, when converting a string to a number, this exception will be thrown if the string is not in the correct format.

try {
  int number = int.parse('abc');
} catch (e) {
  if (e is FormatException) {
    print('Invalid format: ${e.source}');
  }
}

4. RangeError: Indicates an exception caused by exceeding the range. For example, this exception is thrown when the index exceeds the range of the list.

try {
  List<int> numbers = [1, 2, 3];
  int number = numbers[4];
} catch (e) {
  if (e is RangeError) {
    print('Index out of range');
  }
}

5. NoSuchMethodError: Indicates an exception thrown when trying to call a non-existent method or access a non-existent property.

try {
  String name;
  name.toLowerCase();
} catch (e) {
  if (e is NoSuchMethodError) {
    print('Method not found');
  }
}

These are some common Flutter exception types and corresponding cases. Depending on the specific needs and implementation details of your project, other types of exceptions may be encountered.

The SDK collects the detailed exception types of exceptions through the runtimeType of the exception summary. For various error types such as CastError (type conversion error), RangeError, PlatformException, NoSuchMethodError, MissingPluginException, etc., we believe that they affect the business, so we classify Flutter exceptions. processing.

5d0988876d599f4aeb7ac50300585cae.png

1.4 Backstage display

Refer to the following output

2005f89114168ee745fc43e6e8c9384c.png

403ae4c8022add439b38b13e051189f2.png

· Error levels:ErrorWidget, Error, Exception

○ Guide users to handle priorities by defining error levels)

· Error types:CastError, RangeError, PlatformException, NoSuchMethodError, MissingPluginException, etc.

○ Further identify problem types and processing priorities by error type

· User perspective experience: Whether the exception causes a white screen

○ Determine high priority for user processing from an experience perspective

02

Exception summary aggregation

2.1 Aggregation goals

By extracting more accurate calculation factors for cluster identification calculations, ① the same abstract stack and different aggregation lead to different problems being masked and unable to be discovered and solved, ② the same abnormal problem causes problems to be scattered due to the presence of variables in the abstract (such as: including url, Discrete values such as id and md5) cause the number of problems to be scattered and problems cannot be discovered in time.

2.2 Abnormal clustering factor

// Summary
NoSuchMethodError: Class 'int' has no instance method 'add'.
// stack
pid: 10349, tid: 10382, name 1.ui
os: android arch: arm64 comp: yes sim: no
build_id: '349b9003b2c2867ab94ca373c1247263'
isolate_dso_base: 79d362b000, vm_dso_base: 79d362b000
isolate_instructions: 79d3700d00, vm_instructions: 79d36fb000
#0 Object.noSuchMethod (third_party/dart/sdk/lib/_internal/vm/lib/object_patch.dart:38:5)
#1 _objectNoSuchMethod (third_party/dart/sdk/lib/_internal/vm/lib/object_patch.dart:85:9)
#2 main.<anonymous closure> (/Users/tony/Desktop/project/apm-log-visual/lib/main.dart:17:12)
#3 _rootRun (third_party/dart/sdk/lib/async/zone.dart:1399:13)
#4 _rootRun (third_party/dart/sdk/lib/async/zone.dart:1390:1)
#5 _CustomZone.run (third_party/dart/sdk/lib/async/zone.dart:1301:19)
#6 _runZoned (third_party/dart/sdk/lib/async/zone.dart:1804:10)
#7 runZonedGuarded (third_party/dart/sdk/lib/async/zone.dart:1792:12)

Factor name

Factor Classification & Processing Rules

Factor Source

Abnormal level

ErrorWidget: Page rendering failed

Flutter APM SDK runs abnormal scene marking and collection

Error: Problems such as insufficient memory and stack overflow occur during operation.

Exception: An unexpected occurrence during runtime (such as a reference error, etc.)

Exception type

RangeError

Obtained through the runtime exception callback (Exception).runtimeType method

PlatformException

NoSuchMethodError

error stack

The first 6 lines of the stack

  • Remove line numbers (server preprocessing)

  • Eliminate the packaging machine path (server preprocessing)

Collection through runtime exception callback value Stack

2.3 Abnormal clustering feature point extraction

2.3.1 Feature points

Business Characteristics

Three-party library features

Flutter engine features

Non-third-party library non-engine + lib/ third_party/ flutter/packages
flutter/shell
flutter/runtime

…..

Non-third-party library, non-engine + package: variable/
package:flutter_module
dart:variable/ package: flutter
flutter:shell
flutter:runtime

…..


2.3.2 Stack type one

Tip: Eliminate the error stack output after parsing under the symbol file package ea38e04b9242d3ba2b6747411d42e730.p ng


2.3.3 Stack type two

Tip: The error stack output after parsing the symbol file is not removed 57f1fdca56ef5b557a767cc687aff7fe.png


2.4 Stack key exception point identification rules

The extraction logic of key abnormal points is as follows. The stack is traversed according to the following rules, and the abnormal points are located according to the following priorities during the traversal process:

Business stack (including business package name) > Third-party library stack > Engine stack

The detailed logic is as follows:

  • Refer to 2.3.1 System Library Feature Points. By default, if all stacks are system libraries, the first line of the stack library will be taken by default.

  • Refer to 2.3.1 Third-party library feature points. If you encounter a third-party library, change it to the first line of the third-party library, that is, the first business stack that appears.

  • If a business stack is encountered, modify it to the first line in the business stack, that is, the first occurrence of the business stack02

    ?

    03

    Symbol table parsing

In versions above Flutter 1.17, the function of removing symbol tables from Flutter products is officially supported. Considering the security and package size issues of apps that integrate Flutter products, this function is integrated into the packaging system. Through the officially supported packaging command to separate symbol table files during packaging.

To obfuscate your application, use the flutter build command in release mode with the –obfuscate and –split-debug-info options. The –split-debug-info option specifies the directory where Flutter outputs debug files. In the obfuscated case, it outputs a symbol map. For example:

// Android
flutter build apk --obfuscate --split-debug-info=/<project-name>/<directory>

Once you have obfuscated your binary, Save the symbol file. You’ll need this if you later want to deobfuscate the stack trace.

Therefore, the stack reported by Flutter exceptions is de-symbolized and difficult to read and understand, as shown below:

"Warning: This VM has been configured to produce stack traces that violate the Dart standard.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 16540, tid: 6125465600, name beikeshareengine_0.1.ui
isolate_dso_base: 10b860000, vm_dso_base: 10b860000
isolate_instructions: 10b86a000, vm_instructions: 10b866000
#00 abs 000000010bc7d08b _kDartIsolateSnapshotInstructions + 0x41308b
#01 abs 000000010bb0f037 _kDartIsolateSnapshotInstructions + 0x2a5037
#02 abs 000000010bcc72e7 _kDartIsolateSnapshotInstructions + 0x45d2e7

U-APM provides the function of uploading Dart symbol table. Users can restore the obfuscated Flutter stack in real time as long as they upload the corresponding version of the symbol table file. So, how is the technology behind it implemented?

3.1 Symbol table file analysis

The Flutter Dart exception symbol table file is a DWARF format file. The DWARF (“Debugging With Attributed RecordFormats”) file contains the debugging information of the application. We can use the dwarfdump tool on the Linux platform to parse out the debugging information.

For example, the following command can dump the debugging information in the symbol table file my-flutter-app.symbols to the debug.txt file in the form of text:

dwarfdump -a my-flutter-app.symbols > debug.txt

This debug.txt file contains the address mapping information we need to restore the stack. Below we give a brief introduction to the structure of this file.

3.1.1 Compilation unit

At the beginning of the .debug_info section, there is a compilation unit structure (COMPILE_UNIT). In this structure, we focus on obtaining the value of DW_AT_low_pc, which is the starting address offset of the entire compilation unit. Later, we calculate the relative offsets of functions and rows and columns. All are based on this parameter. b65e136eb51d6a28fe5f2d3361b30eae.png

3.1.2 Function information

Then the DW_TAG_subprogram part is the definition of the function. You can find information such as the function id, function name, and the file number where the function is located. d17e753da0fc69a2565cd3faf8ecceda.png

Notice that there is no function start and end address information in the function definition above. This is in another structure. As shown in the figure below, DW_AT_abstract_origin points to the function id and is associated with the function 0x0000002d 32a9824eada3fbcff686b06f7dce3752.png

c8741b5e0da223f35020b5afff0cbd90.png

Among them, [DW_AT_low_pc, DW_AT_high_pc) is the start and end address range of the function. By associating these two structures, you can find information such as the name of the function and the start and end address ranges at the same time. Note that the starting and ending addresses here need to be converted into relative addresses by subtracting the starting address offset of the compilation unit mentioned above.

3.1.3 Row and file information

Line and file information can be found in the .debug_line section of the file. As shown below: 9c1c1dce6aa667ac77fe51e8d9b12202.png

Where pc is the starting address of the row, arranged in positive order. Note that the starting address of each line is also the ending address of the previous line. [lno,col] is the decimal column number, and uri is the file path where the row is located. If the uri column of a row is empty, then the file path of this row is equal to the most recent uri that appeared previously. In this way, we can parse out the row and column numbers, file paths, and start and end addresses of all lines. Like the function part, the line address must also be converted into a relative address.

3.2 Stack Restore

How is the obfuscated stack restored based on the parsed debugging information?

As shown in the figure below, from the end of each line of the obfuscation stack, we can find the hexadecimal address starting with + 0x, which represents the relative offset of this line of stack. 2ac3be3da87ec1237ac8ea4208747453.png

Then use the relative offset of the stack to search and match the line address range and function address range in the debugging information, and you can find the corresponding file name, function name, row and column number and other information, thus completing the stack restoration.

The restoration result is as shown below: 023a8f9df5ba409c35fe689cc5621f04.png

The above is the general method of Umeng + U-APM to parse and restore Dart symbol table files. In the actual system processing process, a series of structuring, compression, deduplication and caching operations will also be performed to ensure that it can be used in a production environment. high performance and high availability.

In the first issue, we provide Flutter Dart abnormal monitoring functions and try to help developers solve application stability problems. However, we are not satisfied with this. In our futureplans, we will further add Flutter page performance and frame rate modules to improve the user experience and performance of developer applications.

For more specific product information and event content,

Click below[Read original text] to learn more! 80df58285a508f92bd3db9af370f453c.gif

syntaxbug.com © 2021 All Rights Reserved.