Inspect React Native App Crash with Source Map
App in Release build is minified and compiled. Crash reports won’t include the original file info as Debug build. However, most programming languages have some mechanisms to solve the problem. For example, dSYM on iOS and React Native uses Javascript Source Map which is a JSON file that contains information between the compiled file and the original files. Therefore, we can use it to find where cause crashes easier.
The above image is a crash report of a react native app on New Relic which is not really readable. However, it contains some useful information such as
- call .map() on an undefined object
- the last stack is in line 1856 and column 3113
Nevertheless, the two pieces of information are still too vague to find where is the crash since .map() can be used more than a hundred times in a project and line 1856 and column 3113 is a kind of magic number.
A Primitive Approach
Actually, line 1856 and column 3113 is the crash that happened in the minified and compiled file. We can unzip the apk or ipa file, you can find the minified and compiled file in these places.
- for iOS, it is on
Payload/[App Name].app/main.jsbundle
of ipa - for Android, it is on
assets/index.android.bundle
of apk
Open the file and go to the line and copy the line. In the file, each line is a minified and compiled code of a javascript file. Create a new js file, paste the line, and format it, we can at least get more information about the crash.
Source Map Approach
By default, React Native has different behaviors of generating source map for iOS and Android.
- for iOS, React Native won’t generate the source map(at least I cannot find it).
- for Android, it is on
./android/app/build/generated/sourcemaps/react/[variant]/index.android.bundle.map
You can pass --sourcemap-output
to change the output path. If your project uses CI/CD to build and distribute the app, it is recommended to include the source map files to artifacts because the source map of each build will be different, we cannot use a source map of A build for the app of B build.
- for iOS, go to build phases in Xcode on “Bundle React Native code and images” and add the following line shown as the below image. The source map will be output on
./main.jsbundle.map
export EXTRA_PACKAGER_ARGS="--sourcemap-output main.jsbundle.map"
- for Andorid, add the following line inside project.ext.react in
./android/app/build.gradle
shown as the below image. The source map will be output on./android/app/build/index.android.bundle.map
extraPackagerArgs: ["--sourcemap-output", file("$buildDir/index.android.bundle.map")]
However, the output path is for the demo purpose, you should change to the right place for your needs, for example the folder for CI/CD upload.
Source Map Lookup
I cannot find a GUI tool that can load the source map of react native successfully. Eventually, I created a very simple source-map-lookup library to wrap mozilla/source-map. Use this command to install source-map-lookup.
npm install -g https://github.com/weironghuang31/source-map-lookup
Then you can run this command to find the crash place of the original file.
source-map-lookup $filepath:$line:$column
Demo
In order to make a better interpretation, I created a demo app to show you the process.
- Create a demo app and add CrashButton.js which will cause a crash when you click the button.
2. Run .\gradlew installRelease
to build an Release app and install it on an android device.
3. Click the button to cause crash and find the crash on logcat. And know the crash is taking place at line 415 and column 189.
4. Try the primivite approach, open the index.android.bundle
, go to line 415, copy the line, paste it on a new file and format it. Although, it is still not easy to figure out which file, but we have a clue that a file includes “Touch me”.
5. Try source-map-lookup with the source map and the line and the column of the crash in a terminal. Then source-map-lookup prints the original file path, line and column.
This my approach of inspecting react native crash. There might be a better method, however, the approach really helps me a lot. I hope it can help you as well.
References
Path Problem
You might need to use — sourcemap-use-absolute-path
or — sourcemap-sources-root
parameters if the build machine is not your local machine.