Should some students request, the previous articles to synthesize this one
Before starting this topic, let's start with a brief introduction of what is NDK and JNI, some of which are from the Web
What is the Android ndk and why do we use the NDK?
The Android NDK was added in front of the SDK with the word "native", the native development Kit, which Google called the "NDK". As is known to all, Android programs run in Dalvik virtual machines, and the NDK allows users to execute part of the program using native code languages like C + +. The NDK includes:
- The tools and build files required to generate the native code base from C + +.
- Embed a consistent native library into the application package files that you can deploy on your Android device (application packages files, the. apk file).
- Support for all future Android platforms with some column native system header files and libraries
Why use the NDK? Broadly speaking, it is mainly divided into the following situations:
- Code protection, because the Java layer Code of the APK is easy to decompile, and the C + + library counter-sink is more difficult.
- Call third-party C/S libraries in the NDK, because most of the open source libraries are written in C/s + + code.
- Easy to transplant, the library written in C + + can be reused on other embedded platforms.
What is Android jni? What is the relationship with the NDK?
The Java Native Interface (JNI) standard is part of the Java platform that allows Java code to interact with code written in other languages. JNI is a local programming interface that enables Java code running inside a Java Virtual machine (VM) to interoperate with applications and libraries written in other programming languages such as C, C + +, and assembly language.
In short, the NDK is a tool that can easily and quickly develop. so files. The process of JNI is more complex, generating. So requires a lot of action, and the NDK simplifies the process.
Does the NDK exception cause the common types of exceptions that the program CRASH,NDK?
The NDK compiles the generated. So file as part of the program, which also causes the program to crash when an exception is run. Unlike the Java code exception caused by the program crashes, when the NDK exception occurs, the program on the Android device will immediately exit, that is, usually said the flashback, and will not pop up "program XXX no response, whether immediately close" such as the Prompt box.
The NDK is developed using C + +, and programmers familiar with C + + know that pointers and memory management are the most important and most problematic places, with a slight loss of common problems such as invalid memory access, invalid objects, memory leaks, stack overflows, and finally the same result: program crashes. For example, we often say null pointer error, that is, when a memory pointer is set to NULL (NULL) and then access it again, another common mistake is to release a memory space somewhere in the program, and then try to access the memory address in other places of the program, which will result in an invalid address error. Common types of errors are as follows:
- Initialization error
- Access error
- Array index access out of bounds
- Pointer object access out of bounds
- Accessing a null pointer object
- Access Invalid pointer object
- Iterator access out of bounds
- Memory leaks
- Parameter error
- Stack Overflow
- Type conversion Error
- Number except 0 error
What information can we get when the NDK error occurs?
When developing native applications with the Android NDK, almost all programmers have experienced program crashes, but its crash will print a stack of seemingly heavenly book-like stacks in logcat. It is a matter of course to locate the number of lines that the error code is doing by adding a row of printed information alone. Searching for "Android Ndk crashes" on the Web, you can search for many articles about how to find and locate NDK errors using the tools provided by Android, but are mostly obscure. Here is a practical example of how to first generate an error and then demonstrate how to locate the wrong function name and line of code in two different ways.
First, let's look at what we did in the code of the Hello-jni program (about how to create or import a project, here), see: In Jni_onload () function, so load, call the Willcrash () function, and in the Willcrash () function, std:: This assignment method of string produces a null pointer error. This will cause the HELLO-JNI program to flash back when it loads. Let's remember these two rows: the Willcrash () function was called in line 61, and a crash occurred on line 69.
Here's a look at the Logcat log that the system prints when a crash (flashback) occurs:
[Plain]View Plaincopy
- *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
- Build fingerprint: ' Vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/jop40d/1372668680:user/test-keys '
- pid:32607, tid:32607, Name:xample.hellojni >>> com.example.hellojni <<<
- Signal (SIGSEGV), Code 1 (segv_maperr), fault Addr 00000000
- R0 00000000 R1 beb123a8 R2 80808080 R3 00000000
- R4 5d635f68 R5 5cdc3198 R6 41efcb18 R7 5d62df44
- R8 4121b0c0 R9 00000001 SL 00000000 FP beb1238c
- IP 5d635f7c SP beb12380 lr 5d62ddec pc 400e7438 CPSR 60000010
- BackTrace:
- #00 PC 00023438/system/lib/libc.so
- #01 PC 00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so
- #02 PC 000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so
- #03 PC 00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so
- #04 PC 00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so
- #05 PC 000505b9/system/lib/libdvm.so
- #06 PC 00068005/system/lib/libdvm.so
- #07 PC 000278a0/system/lib/libdvm.so
- #08 PC 0002b7fc/system/lib/libdvm.so
- #09 PC 00060fe1/system/lib/libdvm.so
- #10 PC 0006100b/system/lib/libdvm.so
- #11 PC 0006c6eb/system/lib/libdvm.so
- #12 PC 00067a1f/system/lib/libdvm.so
- #13 PC 000278a0/system/lib/libdvm.so
- #14 PC 0002b7fc/system/lib/libdvm.so
- #15 PC 00061307/system/lib/libdvm.so
- #16 PC 0006912d/system/lib/libdvm.so
- #17 PC 000278a0/system/lib/libdvm.so
- #18 PC 0002b7fc/system/lib/libdvm.so
- #19 PC 00060fe1/system/lib/libdvm.so
- #20 PC 00049ff9/system/lib/libdvm.so
- #21 PC 0004d419/system/lib/libandroid_runtime.so
- #22 PC 0004e1bd/system/lib/libandroid_runtime.so
- #23 PC 00001d37/system/bin/app_process
- #24 PC 0001bd98/system/lib/libc.so
- #25 PC 00001904/system/bin/app_process
- Stack
- beb12340 012153f8
- beb12344 00054290
- beb12348 00000035
- beb1234c beb123c0 [Stack]
- ......
If you have seen the Logcat printed NDK errors when the log will know that I omitted a lot of content, many people see so many dense log has been dizzy brain swelling, even many senior Android developers, in the face of the NDK log is also mostly silently chose to ignore.
How to "symbolize" NDK error messages
In fact, as long as you carefully review, and then with the tools provided by Google, we can quickly and accurately locate the wrong code location, this work we call "symbolic." It is important to note that if you want to symbolize the NDK errors, you need to keep the so files that are generated during compilation that contain the symbol tables, which are typically kept in the $project_path/obj/local/directory.
The first method: Ndk-stack
This command-line tool is included in the installation directory of the NDK tool, and is put together with ndk-build and some other commonly used NDK commands, such as on my Computer, where the location is/android-ndk-r9d/ndk-stack. According to Google's official documentation, the NDK provides the Ndk-stack command from the R6 version, and if you use the previous version, it is recommended that you upgrade to the latest version as soon as possible. There are two ways of using the Ndk–stack command
Using Ndk-stack to analyze logs in real time
While running the program, use ADB to get the Logcat log, and through the pipe character output to ndk-stack, but also need to specify the location of the so file containing the symbol table, if your program contains a variety of CPU architectures, where the requirements based on the error occurs when the phone CPU type, Select a different CPU architecture directory, such as:
[Plain]View Plaincopy
- ADB Shell Logcat | Ndk-stack-sym $PROJECT _path/obj/local/armeabi
When a crash occurs, you get the following information:
[Plain]View Plaincopy
- Crash Dump: **********
- Build fingerprint: ' Vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/jop40d/1372668680:user/test-keys '
- pid:32607, tid:32607, Name:xample.hellojni >>> com.example.hellojni <<<
- Signal (SIGSEGV), Code 1 (segv_maperr), fault Addr 00000000
- Stack frame #00 pc 00023438/system/lib/libc.so (strlen+72)
- Stack frame #01 pc 00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so (std::char_traits::length (char const *) +20): Routine std::char_traits::length (char const*) at/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_ traits.h:229
- Stack frame #02 pc 000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so (std::basic_string, Std::allocator & Gt;::basic_string (char const*, std::allocator const&) +44): Routine basic_string at/android-ndk-r9d/sources/ cxx-stl/stlport/stlport/stl/_string.c:639
- Stack frame #03 pc 00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so (Willcrash () +68): Routine Willcrash () at/home/testin/hello-jni/jni/hello-jni.cpp:69
- Stack frame #04 pc 00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so (jni_onload+20): Routine jni_onload at /home/testin/hello-jni/jni/hello-jni.cpp:61
- Stack frame #05 pc 000505b9/system/lib/libdvm.so (Dvmloadnativecode (char const*, object*, char**) +516)
- Stack Frame #06 pc 00068005/system/lib/libdvm.so
- Stack Frame #07 pc 000278a0/system/lib/libdvm.so
- Stack frame #08 pc 0002b7fc/system/lib/libdvm.so (Dvminterpret (thread*, Method const*, jvalue*) +180)
- Stack frame #09 pc 00060fe1/system/lib/libdvm.so (DVMCALLMETHODV (thread*, Method const*, object*, bool, jvalue*, Std::_ _va_list) +272)
- ...... (slightly later)
We focus on #03 and #04, these two lines are in our own generated libhello-jni.so in the error message, then we will find the following key information:
[Plain]View Plaincopy
- #03 (Willcrash () +68): Routine willcrash () at/home/testin/hello-jni/jni/hello-jni.cpp:69
- #04 (jni_onload+20): Routine jni_onload at/home/testin/hello-jni/jni/hello-jni.cpp:61
Recall our code, in the Jni_onload () function (line 61st), we called the Willcrash () function, and in the Willcrash () function (line 69th), we made an error. All of this information is accurately extracted! Isn't it very simple?
Get logs first, then use Ndk-stack analysis
There is no big difference between this method and the above method, just the way the Logcat log gets is different. You can save the Logcat log to a file while the program is running, and even when the crash occurs, quickly save the Logcat log, then analyze it, a little more flexible than the method above, and the log can be left to analyze later.
[Plain]View Plaincopy
- adb shell Logcat > 1.log
- Ndk-stack-sym $PROJECT _path/obj/local/armeabi–dump 1.log
Second method: Use the Addr2line and Objdump commands
This method is suitable for those who are not satisfied with the simple usage of the above ndk-stack, but like the inquisitive programmer, these two methods can reveal what the Ndk-stack command works, although it is a little cumbersome to use, but can satisfy the curiosity of programmers.
Let's just say these two commands, you can find them in most Linux distributions, if your operating system is Linux and you're testing your phone using the Intel x86 series, you can use the commands that come with your system. However, if that's the case, most people are desperate because most developers are using Windows and the phone is probably the Armeabi series.
Don't worry, the NDK has its own toolchain for each operating system and CPU architecture, including these two commands, but with a slight change in name, you can find them in the Toolchains directory of the NDK directory. Take my Mac computer as an example, if I'm looking for a tool for the Armeabi architecture, they're arm-linux-androideabi-addr2line and arm-linux-androideabi-objdump, respectively. Location in the following directory, this location will be omitted from subsequent introductions:
[Plain]View Plaincopy
- /developer/android_sdk/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/
Assuming your computer is Windows and the CPU architecture is MIPS, then the tool you want may be included in this directory:
[Plain]View Plaincopy
- D:\ android-ndk-r9d\toolchains\mipsel-linux-android-4.8\prebuilt\windows-x86_64\bin\
Okay, so, how to use these two tools, here are the details:
1. Find the key function pointers in the log
In fact, it is very simple, is to find backtrace information, belong to our own so file error lines.
First of all to find backtrace information, some of the opportunity to explicitly print a line of backtrace (such as the mobile phone we use this time), then this line of the following a series of "#两位数字 PC" line is backtrace information. Sometimes the phone does not print a line of backtrace, so just find a line that starts with "#两位数字 PC".
Next to find their own so file error lines, this is relatively simple. After you find these rows, note the address of the functions in these lines
2. Find the code location using Addr2line
Execute the following command, multiple pointer addresses can be entered in a command, separated by a space
[Plain]View Plaincopy
- Arm-linux-androideabi-addr2line–e obj/local/armeabi/libhello-jni.so 00004de8 000056c8 00004fb4 00004f58
The results are as follows
[Plain]View Plaincopy
- /android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_traits.h:229
- /android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639
- /wordspaces/hello-jni/jni/hello-jni.cpp:69
- /wordspaces Hello-jni/jni/hello-jni.cpp:6
As we can see from the results of the addr2line, we get the call relationship and the number of rows for our own error code, In the hello-jni.cpp of 69 rows and 61 lines (the other two lines because the standard function can be ignored), the results and ndk-stack are consistent, indicating that Ndk-stack is also addr2line to get the location of the code.
3. Using Objdump to get function information
With the Addr2line command, we've actually found the wrong place in our code, and we've been able to help the programmer locate the problem. However, this method can only get the number of lines of code, and does not display function information, it is not so "perfect", for the pursuit of the ultimate programmer, this is certainly not enough. We'll show you how to locate the function information below.
Use the following command to export the function table:
[Plain]View Plaincopy
- Arm-linux-androideabi-objdump–s obj/local/armeabi/libhello-jni.so > Hello.asm
Find the two key pointers 00004fb4 and 00004f58 that we have just positioned in the generated ASM file
From these two graphs can be clearly seen (note that in different NDK versions and different operating systems, the format of ASM files is not exactly the same, but all the same, please carefully compare), these two pointers belong to the Willcrash () and the Jni_onload () function, respectively, Combined with the results of the addr2line just now, the corresponding information for these two addresses is:
[Plain]View Plaincopy
- 00004fb4:willcrash ()/wordspaces/hello-jni/jni/hello-jni.cpp:69
- 00004f58:jni_onload ()/wordspaces/hello-jni/jni/hello-jni.cpp:61
Quite perfect, and ndk-stack get the information exactly the same!
Locating NDK errors using the Testin Crash Analysis service
The above mentioned method, only suitable during the development test, if your app or game has been released online, and users often feedback said crash, flash back, and expect users to help you collect information location problem, is almost impossible. At this point, we need to use other means to capture the crash information.
At present, some companies in the industry have launched crash information collection services, by embedding the SDK, in the event of a crash, collect stack information, sent to the cloud service platform, to help developers locate the error message. In this regard, the leading position is the domestic testin and foreign crittercism, of which crittercism need to pay, and no special Chinese developer support, we recommend Testin, its crash Analysis service is completely free.
Testin supports the NDK crash Analysis starting with version 1.4, and its latest version has been upgraded to 1.7. When a NDK error occurs in the program, the embedded SDK collects the stack information of the program when it crashes on the user's phone (mainly the function pointers we get through the Logcat log above), device information, thread information, and so on, and the SDK reports the information to the Testin cloud service platform. As long as the login to the Testin platform, you can see all the user reported crash information, including the NDK, and these crashes have been normalized processing, the different system and ROM version of the information will be slightly different, but on the Testin website these have done very good processing, Avoided some of our repetitive work.
The red box part, is reported from the user's mobile phone, our own so in the error function pointer address stack information, and we developed from Logcat read the log, is some obscure pointer address, testin for the NDK crash provides symbolic function, As long as we upload the so file containing the symbol table generated during our compilation (so in the obj/local/directory we mentioned above for each CPU architecture), you can automatically position the function pointer address to the function name and the number of lines of code. Once symbolized, it looks the same as the results we had in the local test before, at a glance.
And the advantage of using this feature is that the so file containing the symbol table will change every time we compile ourselves, and it is likely that we have just released a new version, and so has changed because the developer will modify the program, in which case, Even if we get the stack information at the time of the crash, it's no longer symbolic. So we remember to back up our so files after compiling the package. At this point we can upload these files to Testin for symbolic work, Testin will save and manage different versions of so files for us to ensure that the information is not lost. Take a look at the symbolic display:
How to locate errors encountered in Android NDK development