This is mostly a learning process, looking at the unwind library and the ELF file structure, and hopefully pointing out any misdescriptions.

I. Content composition of crash logs

1.1 Composition Example

Details: source. Android. Google. Cn/devices/tec…

Here is an example of crash content:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** Build fingerprint: 'Android/aosp_flounder/flounder: 5.1.51 / AOSP/enh08201009: eng/test - keys' Revision:' 0 'ABI:' arm 'pid: 1656, dar: 1656, name: crasher >>> crasher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'some_file.c:123: some_function: assertion "false" failed' r0 00000000 r1 00000678 r2 00000006 r3 f70b6dc8 r4 f70b6dd0 r5 f70b6d80 r6 00000002 r7 0000010c r8 ffffffed r9 00000000 sl 00000000 fp ff96ae1c ip 00000006 sp ff96ad18 lr f700ced5 pc f700dc98 cpsr 400b0010 backtrace: #00 pc 00042c98 /system/lib/libc.so (tgkill+12) #01 pc 00041ed1 /system/lib/libc.so (pthread_kill+32) #02 pc 0001bb87 /system/lib/libc.so (raise+10) #03 pc 00018cad /system/lib/libc.so (__libc_android_abort+34) #04 pc 000168e8 /system/lib/libc.so (abort+4) #05 pc 0001a78f /system/lib/libc.so (__libc_fatal+16) #06 pc 00018d35 /system/lib/libc.so (__assert2+20) #07 pc 00000f21 /system/xbin/crasher #08 pc 00016795 /system/lib/libc.so (__libc_init+44) #09 pc 00000abc  /system/xbin/crasher Tombstone written to: /data/tombstones/tombstone_06 *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***Copy the code
1.2 Contents
  • Device fingerprinting

    Records device information

  • Revision

    Revision refers to hardware, not software. Revision is not usually used, but using revision helps you automatically ignore known errors caused by bad hardware

  • ABI

    ABI is one of ARM, ARM64, MIPS, MIPS64, x86, or x86-64. This value is mainly used to distinguish which tool chain to use in subsequent information analysis

  • Process Information

    The value includes the process id and process name

  • The error signal

    The received signal (SIGABRT in this case) and more information about how to receive that signal (SI_TKILL in this case)

  • Interrupt messages

    Not all crash problems have an abort message line, but it does appear when a abort occurs. This is automatically collected from the last line of critical logCAT output from this PID/TID, and in the case of intentional abort, this can explain why the program terminates on its own

  • Register information

    Shows the contents of all registers, the usefulness of which depends on the exact crash problem

  • The call stack

    Crash function call stack

2. Capture Crash

In Linux, when an exception occurs, the kernel uses a signal mechanism to notify the process. After receiving the signal, the process can handle it in the following three ways:

  • Ignore the signal
  • Use the default operations
  • Specifies that the handler handles

Obviously, we need to specify our own signal handlers to capture signals.

The following code shows how to set up custom signal handling:

  • crash_dumper.h
#define SIGNAL_COUNT 8
static int signals[SIGNAL_COUNT] = {
        SIGTRAP,
        SIGABRT,
        SIGILL,
        SIGSEGV,
        SIGFPE,
        SIGBUS,
        SIGPIPE,
        SIGSYS
};
Copy the code
  • crash_dumper.cpp
void registerSigHandler(const char *tombStonePath) {
    // set dir to save tombstone
    long strLen = strlen(tombStonePath);
    if(tombstone_file_dir ! =NULL) {
        free(tombstone_file_dir);
    }
    tombstone_file_dir = static_cast<char* > (malloc(strLen + 1));
    memset(tombstone_file_dir, 0, strLen);
    strcpy(tombstone_file_dir, tombStonePath);

    // create new sigaction
    struct sigaction newSigAction;
    sigemptyset(&newSigAction.sa_mask);
    newSigAction.sa_flags = SA_SIGINFO;
    // set sigaction handler
    newSigAction.sa_sigaction = sigHandler;

    // register new sigaction handler and save old actions
    for (int i = 0; i < SIGNAL_COUNT; ++i) { sigaction(signals[i], &newSigAction, &g_oldSigActions[i]); }}Copy the code

Note that newsigaction. sa_flags must include the SA_SIGINFO MASK so that we can get the details in the second argument to the sigHandler function

void sigHandler(int sig, siginfo_t *info, void *context) {
	/ /...
}
Copy the code

The first parameter of this function is the signal, such as SIGABRT, the second parameter is the data carried by the signal, such as PID, UID, code, etc., and the third parameter is ucontext_t. The structure is as follows:

typedef struct ucontext {.mcontext_t uc_mcontext; // Save specific program execution context, such as PC value, stack pointer, register value and other information. }ucontext_t;
Copy the code

3. Obtain Header information

Get Build Fingerprint, Revision, and ABI:

  • Build fingerprint

    Fingerprint field of the system

    char finger[92] = {0};
    __system_property_get("ro.build.fingerprint", finger);
    Copy the code
  • revision

    Read the ro. Revision field information of the system

    char revision[92] = {0};
    __system_property_get("ro.revision", revision);
    Copy the code
  • abi

    We can read the content in so to judge, or we can judge at compile time. Here, we choose to judge at compile time

    const char *getABI(a) {
        //It's usually most convenient to determine the ABI at build time using #ifdef in conjunction with:
        //
        //__arm__ for 32-bit ARM
        //__aarch64__ for 64-bit ARM
        //__i386__ for 32-bit X86
        //__x86_64__ for 64-bit X86
        //Note that 32-bit X86 is called __i386__, not __x86__ as you might expect!
        //
        // https://developer.android.google.cn/ndk/guides/cpu-features?hl=en
    #if defined(__aarch64__)
        return "arm64";
    #elif defined(__arm__)
        return "arm";
    #else
        return "unknown";
    #endif
    }
    Copy the code

4. Obtain process information and signal information

Void sigHandler(int sig, siginfo_t *info, void *context) void sigHandler(int sig, siginfo_t *info, void *context)

4.1 Obtaining the Process ID
info->_sifields._kill._pid
Copy the code
4.2 Obtaining the Process Name
std: :string get_process_name_by_pid(const int pid) {
    char processName[256] = {0};
    char cmd[64] = {0};
    sprintf(cmd, "/proc/%d/cmdline", pid);
    FILE *f = fopen(cmd, "r");
    if (f) {
        size_t size;
        size = fread(processName, sizeof(char), 256, f);
        if (size > 0 && '\n' == processName[size - 1]) {
            processName[size - 1] = '\ 0';
        }
        fclose(f);
    }
    return processName;
}
Copy the code
4.3 Signal Information
  • signal

The function’s first argument: sig

  • code

It can be obtained from the second argument to the function: info->si_code

5. Register information

5.1 Data Structure Introduction

After capturing the signal and its accompanying information, we also get a context data, which is a structure of type UCONTEXt_t, where the UC_mcontext property records information about the current register.

The contents of this structure are different for AARCH64 and ARM:

  • aarch64

    struct sigcontext {
      __u64 fault_address;
      __u64 regs[31];
      __u64 sp;
      __u64 pc;
      __u64 pstate;
      __u8 __reserved[4096] __attribute__((__aligned__(16)));
    };
    Copy the code

    Register description:

    Developer.arm.com/documentati…

  • arm

    struct sigcontext {
      unsigned long trap_no;
      unsigned long error_code;
      unsigned long oldmask;
      unsigned long arm_r0;
      unsigned long arm_r1;
      unsigned long arm_r2;
      unsigned long arm_r3;
      unsigned long arm_r4;
      unsigned long arm_r5;
      unsigned long arm_r6;
      unsigned long arm_r7;
      unsigned long arm_r8;
      unsigned long arm_r9;
      unsigned long arm_r10;
      unsigned long arm_fp;
      unsigned long arm_ip;
      unsigned long arm_sp;
      unsigned long arm_lr;
      unsigned long arm_pc;
      unsigned long arm_cpsr;
      unsigned long fault_address;
    };
    Copy the code

    Register description:

    Developer.arm.com/documentati…

5.2 Code Implementation

Emulate the log format in android system logs

  std: :string getRegisterInfo(const ucontext_t *ucontext) {
      std: :string ctxTxt;
  #if defined(__aarch64__)
      for (int i = 0; i < 30; ++i) {
          if (i % 4= =0) {
              ctxTxt.append("");
          }
          char info[64] = {0};
          sprintf(info, "x%d %016lx ", i, ucontext->uc_mcontext.regs[i]);
          ctxTxt.append(info);
          if ((i + 1) % 4= =0) {
              ctxTxt.append("\r\n");
          }
      }
      ctxTxt.append("\r\n");

      char spInfo[64] = {0};
      sprintf(spInfo, "sp %016lx ", ucontext->uc_mcontext.sp);

      char lrInfo[64] = {0};
      sprintf(lrInfo, "lr %016lx ", ucontext->uc_mcontext.regs[30]);

      char pcInfo[64] = {0};
      sprintf(pcInfo, "pc %016lx ", ucontext->uc_mcontext.pc);

      ctxTxt.append("").append(spInfo).append(lrInfo).append(pcInfo);
  #elif defined(__arm__)
      char x0Info[64] = {0};
      sprintf(x0Info, "x0 %08lx ", ucontext->uc_mcontext.arm_r0);
      char x1Info[64] = {0};
      sprintf(x1Info, "x1 %08lx ", ucontext->uc_mcontext.arm_r1);
      char x2Info[64] = {0};
      sprintf(x2Info, "x2 %08lx ", ucontext->uc_mcontext.arm_r2);
      char x3Info[64] = {0};
      sprintf(x3Info, "x3 %08lx ", ucontext->uc_mcontext.arm_r3);
      char x4Info[64] = {0};
      sprintf(x4Info, "x4 %08lx ", ucontext->uc_mcontext.arm_r4);
      char x5Info[64] = {0};
      sprintf(x5Info, "x5 %08lx ", ucontext->uc_mcontext.arm_r5);
      char x6Info[64] = {0};
      sprintf(x6Info, "x6 %08lx ", ucontext->uc_mcontext.arm_r6);
      char x7Info[64] = {0};
      sprintf(x7Info, "x7 %08lx ", ucontext->uc_mcontext.arm_r7);
      char x8Info[64] = {0};
      sprintf(x8Info, "x8 %08lx ", ucontext->uc_mcontext.arm_r8);
      char x9Info[64] = {0};
      sprintf(x9Info, "x9 %08lx ", ucontext->uc_mcontext.arm_r9);
      char x10Info[64] = {0};
      sprintf(x10Info, "x10 %08lx ", ucontext->uc_mcontext.arm_r10);

      char ipInfo[64] = {0};
      sprintf(ipInfo, "ip %08lx ", ucontext->uc_mcontext.arm_ip);
      char spInfo[64] = {0};
      sprintf(spInfo, "sp %08lx ", ucontext->uc_mcontext.arm_sp);
      char lrInfo[64] = {0};
      sprintf(lrInfo, "lr %08lx ", ucontext->uc_mcontext.arm_lr);
      char pcInfo[64] = {0};
      sprintf(pcInfo, "pc %08lx ", ucontext->uc_mcontext.arm_pc);

      ctxTxt.append("").append(x0Info).append(x1Info).append(x2Info).append(x3Info).append(
                      "\r\n")
              .append("").append(x4Info).append(x5Info).append(x6Info).append(x7Info).append(
                      "\r\n")
              .append("").append(x8Info).append(x9Info).append(x10Info).append("\r\n")
              .append("").append(ipInfo).append(spInfo).append(lrInfo).append(pcInfo);
  #endif
      return ctxTxt;
  }
Copy the code

Get buildId

6.1 ELF File Format

So file belongs to the format of ELF file. We can obtain the buildId of SO by parsing the ELF file. The format of ELF file is introduced as follows:

En.wikipedia.org/wiki/Execut…

Here is the structure of the ELF Header and Section Header, and we will parse the so file based on this structure.

  • ELF header :(e_ident[EI_OSABI] and e_machine were deleted because the original table was too large)

  • A Section header:

6.2 get buildId
6.2.1 Process Description

We need to get the.shstrtab section contents, then iterate over each section and get the name of each section. If the name is.note.gnu. Build-id, get the contents:

  • Get. Shstrtab section

    The namesSectionHeader is the sectionHeader corresponding to.shstrtab

  • Iterate through each section to get.note.gnu. Build-id

6.2.2 Manual Analysis

Before writing the code to get buildId, manually parse the sectionHeader as described above to deepen your understanding of the ELF file format.

Take an so with an ABI of armeabi-V7A as an example to look at its hexadecimal contents:

  • Elf_Ehdr.e_ident[EI_DATA]

    Location: 0 x05

    Size: 1.

    Value: 01, indicating little endian

  • Elf_Ehdr.e_shoff

    Start position: 0x20

    Size: 4

    Values: 0 x00003218

  • Elf_Ehdr.e_shnum

    Start position: 0x30

    Size: 2

    Values: 0 x001b

  • Elf_Ehdr.e_shstrndx

    Start position: 0x32

    Size: 2

    Values: 0 x001a

  • Elf_Ehdr.e_shentsize

    Start position: 0x2E

    Size: 2

    Values: 0 x0028

  • Based on the preceding information, you can calculate the location of. Shstrtab

    • So let’s first compute the position of shstrTab sectionHeader

      Elf_Shdr *namesSectionHeader = (Elf_Shdr *) (elfData + elfHeader->e_shoff +
                                                     elfHeader->e_shentsize * elfHeader->e_shstrndx);
      Copy the code

      0x3218 + 0x28 * 0x1A = 0x3628

    • And get the location of the shstrtab section

      char *sectionNames = (char *) (elfData + namesSectionHeader->sh_offset);
      Copy the code

      0x3638 (0x3628 + 0x10, 0x10 represents the sh_offset of sectionHeader, as described in the section header table above) starts with 4 bytes of content, 0x3106

  • Iterate over all sectionHeaders to get the section name

    Shstrtab section’s header is 0x3628, so the sectionName’s nameOffset is 0x00F7

    The position of sectionName can be calculated as 0x3106 + 0x00F7 = 0x31FD

    The code iterates through all sections, printing the names:

        Elf_Ehdr *elfHeader = (Elf_Ehdr *) elfData;
        Elf_Shdr *namesSectionHeader = (Elf_Shdr *) (elfData + elfHeader->e_shoff +
                                                     elfHeader->e_shentsize * elfHeader->e_shstrndx);
        char *sectionNames = (char *) (elfData + namesSectionHeader->sh_offset);
        for (int i = 0; i < elfHeader->e_shnum; ++i) {
            Elf_Shdr *sectionHeader = (Elf_Shdr *) (elfData + i * elfHeader->e_shentsize +
                                                    elfHeader->e_shoff);
            LOGI("sectionName = %s",sectionNames + sectionHeader->sh_name);
        }
    Copy the code
  • The whole process

6.2.3 parsing. Note. The gnu. Build – id

If name is.note.gnu. Build-id, fetch the contents of the section:

The content structure of.note.gnu. Build-id is as follows:

+----------------+
|     namesz     |   32-bit, size of "name" field
+----------------+
|     descsz     |   32-bit, size of "desc" field
+----------------+
|      type      |   32-bit, vendor specific "type"
+----------------+
|      name      |   "namesz" bytes, null-terminated string
+----------------+
|      desc      |   "descsz" bytes, binary data
+----------------+
Copy the code

We need to get the content of desc, which has an offset of 4 + 4 + 4 + namesz relative to.note.gnu. Build-id and a data length of descsz.

Example code is as follows:

#define SEC_NAME_BUILD_ID ".note.gnu.build-id"

std: :string getBuildId(unsigned char *elfData) {
    Elf_Ehdr *elfHeader = (Elf_Ehdr *) elfData;
    Elf_Shdr *namesSectionHeader = (Elf_Shdr *) (elfData + elfHeader->e_shoff +
                                                 elfHeader->e_shstrndx * elfHeader->e_shentsize);

    char *sectionNames = (char *) (elfData + namesSectionHeader->sh_offset);
    for (int i = 0; i < elfHeader->e_shnum; ++i) {
        Elf_Shdr *sectionHeader = (Elf_Shdr *) (elfData + elfHeader->e_shoff +
                                                i * elfHeader->e_shentsize);
        if (strcmp((const char *) sectionNames + sectionHeader->sh_name, SEC_NAME_BUILD_ID) == 0) {
            char *buildId = (char *) malloc(sectionHeader->sh_size);
            memcpy(buildId, elfData + sectionHeader->sh_offset, sectionHeader->sh_size);
            std: :string buildIdStr;
            // offset 0x10 (16)
            // +----------------+
            // | namesz | 32-bit, size of "name" field
            // +----------------+
            // | descsz | 32-bit, size of "desc" field
            // +----------------+
            // | type | 32-bit, vendor specific "type"
            // +----------------+
            // | name | "namesz" bytes, null-terminated string
            // +----------------+
            // | desc | "descsz" bytes, binary data
            // +----------------+
            // https://interrupt.memfault.com/blog/gnu-build-id-for-firmware
            //
            // 04000000 14000000 03000000 47 4e 55 00
            // namesz descsz type G N U 0
            // 4 + 4 + 4 + 4 = 16
            int nameSize = *buildId;
            int descOffset = 4 + 4 + 4 + nameSize;
            for (int j = descOffset; j < sectionHeader->sh_size; ++j) {
                char ch[3] = {0};
                sprintf(ch, "%02x", buildId[j]);
                buildIdStr += ch;
            }
            free(buildId);
            returnbuildIdStr; }}return "";
}
Copy the code

Through the above way, we will complete the buildId in the dynamic library, the code is not long, mainly need to understand the ELF file structure.

Get the call stack

Android libcorkscrew. So can be used.

In 5.0 and up: Without libcorkscrew. So on Android, you can use the unwind library.

This is only compatible with Android 5.0 and later devices and uses the unwind library to collect the call stack.

7.1 This section describes the _Unwind_Backtrace function

We can get the call stack with _Unwind_Backtrace, which is declared as follows:

_Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn trace, void * trace_argument);
Copy the code
  • trace

    A custom trace function whose signature is:

    typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context * context, void * arg);
    Copy the code
    • Context:

      This structure type is not exposed, used to represent the context of the program runtime, mainly some register values and other information

    • arg

      Also known as trace_argument, it is used to transmit custom additional information

    • The return value

      See the _Unwind_Reason_Code enumeration definition

  • trace_argument

    Custom additional parameters

  • The return value

    See the _Unwind_Reason_Code enumeration definition

7.2 Dladdr Function Description

We can obtain some information from dladdr, which is declared as follows:

int dladdr(const void* __addr, Dl_info* __info);
Copy the code
  • __addr

    Instruction address

  • __info

    The data structure is as follows, including so path, so loaded address in memory, symbol, symbol address, see the comment for details

    typedef struct {
      /* Pathname of shared object that contains address. */
      const char* dli_fname;
      /* Address at which shared object is loaded. */
      void* dli_fbase;
      /* Name of nearest symbol with address lower than addr. */
      const char* dli_sname;
      /* Exact address of symbol named in dli_sname. */
      void* dli_saddr;
    } Dl_info;
    Copy the code
  • The return value

    Non-0 indicates success, and 0 is returned if the value does not match any libraries

7.3 Obtaining the instruction offset address, library path, symbol, and symbol offset information

The general process is shown in the figure below:

  • Get instruction address

    _Unwind_Word ip = _Unwind_GetIP(context);
    Copy the code
  • Get information about libraries, symbols, etc

    • Get Dl_info first

      Dl_info info;
      int res = dladdr((void *) ip, &info);
      Copy the code
    • Instruction offset address

      Subtract the instruction address from the base address of the library to get the address of the instruction relative to the library:

      offset = ip - (_Unwind_Word) info.dli_saddr
      Copy the code
    • The library path

      info.dli_sname
      Copy the code
    • Symbol offset address

      info.dli_saddr
      Copy the code
    • Symbolic names

      info.dli_sname
      Copy the code
  • Sample calling code

    • Trace_argument definition, including call stack depth and stack description information

      typedef struct TraceInfo {
          int depth;
          std: :string result;
      } BackTraceInfo;
      Copy the code
    • Save call stack

      void storeCallStack(std: :string *result) {
          BackTraceInfo traceInfo;
          traceInfo.depth = 0;
          _Unwind_Backtrace(traceCallBack, &traceInfo);
          *result += traceInfo.result;
      }
      Copy the code
    • Gets the details of each call

      _Unwind_Reason_Code traceCallBack(_Unwind_Context *context, void *hnd) {
          auto *traceHandle = (BackTraceInfo *) hnd;
      
          _Unwind_Word ip = _Unwind_GetIP(context);
      
          Dl_info info;
          int res = dladdr((void *) ip, &info);
      
          if (res == 0) {
              return _URC_END_OF_STACK;
          }
      
          char *desc = (char *) malloc(1024);
          memset(desc, 0.1024);
          std: :string buildId;
          if(info.dli_fname ! =NULL) {
      
              char *symbol = (char *) malloc(256);
      
              if (info.dli_sname == NULL) {
                  strcpy(symbol, "unknown");
              } else {
                  sprintf(symbol, "%s+%ld", info.dli_sname,
                          ip - (_Unwind_Word) info.dli_saddr);
              }
      
              buildId = getSharedObjectBuildId(info.dli_fname);
              if (buildId.length() > 0) {
                  buildId = "(BuildId: " + buildId + ")";
              }
      #if defined(__arm__)
              sprintf(desc, " #%02d pc %08lx %s (%s) ", traceHandle->depth,
                      ip - (_Unwind_Word) info.dli_fbase,
                      info.dli_fname, symbol);
      #elif defined(__aarch64__)
              sprintf(desc, " #%02d pc %016lx %s (%s) ", traceHandle->depth,
                      ip - (_Unwind_Word) info.dli_fbase,
                      info.dli_fname, symbol);
      #endif
              free(symbol);
          }
      
      
          if(traceHandle->result.length() ! =0) {
              traceHandle->result.append("\r\n");
          }
          traceHandle->result.append(desc).append(buildId);
          free(desc);
          ++traceHandle->depth;
          // FIXME: crash if call stack is over 5 on ARM32, unknown reason
      #if! defined(__aarch64__) && defined(__arm__)
          if (traceHandle->depth == 5) {
              return _URC_END_OF_STACK;
          }
      #endif
          return _URC_NO_REASON;
      }
      Copy the code

Write information to log file

8.1 Integrate the preceding functions to output information to files

Here is the sigHandler implementation in detail, where all the relevant information is recorded:

void sigHandler(int sig, siginfo_t *info, void *context) { std::string desc; desc.append("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\r\n") .append(getHeaderInfo()).append("\r\n") .append("Timestamp: ").append(getCrashTimeStamp()).append("\r\n") .append("pid: ").append(std::to_string(info->_sifields._kill._pid)) .append(", uid: ").append(std::to_string(info->_sifields._kill._uid)) .append(", process: >>> ").append(get_process_name_by_pid(info->_sifields._kill._pid)).append(" <<<\r\n") .append("signal ").append(std::to_string(sig)).append(" (").append(signal_names[sig]) .append("), code ").append(std::to_string(info->si_code)) .append(" (").append(si_names[info->si_code]) .append("), fault addr --------\r\n") .append(getRegisterInfo((const ucontext_t *) context)) .append("\r\nbacktrace:"); std::string result; storeCallStack(&result); std::string path = tombstone_file_path; path += getDumpFileName(); std::ofstream outStream(path.c_str(), std::ios::out); if (outStream) { const char *logContent = result.c_str(); outStream.write(desc.c_str(), desc.length()); outStream.write("\r\n", strlen("\r\n")); outStream.write(logContent, result.length()); outStream.close(); } switch (handleMode) { case HANDLE_MODE_RAISE_ERROR: unregisterSigHandler(); raise(sig); break; case HANDLE_MODE_NOTICE_CALLBACK: noticeCallback(sig, path.c_str()); break; case HANDLE_MODE_DO_NOTHING: default: break; }}Copy the code
8.2 Example Output

Complete log:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'OnePlus/OnePlus7T_CH/OnePlus7T: 10 / QKQ1.190716.003/2001030004: user/release - keys'
Revision: '0'
ABI: 'arm64'Timestamp: 2020-07-24 19:49:58+0800 pid: 11626, uid: 10675, process: >>> com.wsy.crashcatcher <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- x0 0000000000000000 x1 0000000000002d6a x2 0000000000000006 x3 0000007ffa592720 x4 0000007ffa590a00 x5 0000007ffa590a00 x6 0000007ffa590a00 x7 aa5402445aac8798 x8 0000000000000083 x9 aa5402445aac8798 x10 0000000000430000  x11 0000007d48f19000 x12 000000000000018c x13 0000000000489900 x14 0000000000000006 x15 ffffffffffffffff x16 0000007dcc1b1cd0 x17 0000007dcc1907a0 x18 0000007dcf59a000 x19 0000007d49a10800 
     x20 0000000000000000 x21 0000007d49a10800 x22 0000007ffa5909a0 x23 0000007d3d7dc63b 
     x24 0000000000000004 x25 0000007dceff6020 x26 0000007d49a108b0 x27 0000000000000001 
     x28 0000007ffa590730 x29 0000007ffa5906e0 
     sp 0000007ffa5906d0 lr 0000007d2cd726a4 pc 0000007dcc1907a8 
backtrace:
     #00 pc 00000000000533d0 /data/app/com.wsy.crashcatcher-Sv1ILhrXAIcPR4jA9OCt3Q==/lib/arm64/libcrash_dumper.so (_Z14storeCallStackPNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE+68) (BuildId: 74b7663be098740b4561517c9cf17c2f057c089c)
     #01 pc 0000000000049934 /data/app/com.wsy.crashcatcher-Sv1ILhrXAIcPR4jA9OCt3Q==/lib/arm64/libcrash_dumper.so (_Z10sigHandleriP7siginfoPv+872) (BuildId: 74b7663be098740b4561517c9cf17c2f057c089c)
     #02 pc 000000000000063c [vdso] (__kernel_rt_sigreturn+0)
     #03 pc 00000000000c27a8 /apex/com.android.runtime/lib64/bionic/libc.so (tgkill+8) (BuildId: a2584ee8458a61d422edf24b4cd23b78)
     #04 pc 00000000000006a4 /data/app/com.wsy.crashcatcher-Sv1ILhrXAIcPR4jA9OCt3Q==/lib/arm64/libtest_crash.so (_Z10raiseErrori+24) (BuildId: b2cd4b1a020680de4f99e469e5544e21c4e49b5d)
     #05 pc 00000000000006d0 /data/app/com.wsy.crashcatcher-Sv1ILhrXAIcPR4jA9OCt3Q==/lib/arm64/libtest_crash.so (Java_com_wsy_crashcatcher_MainActivity_nativeCrash+32) (BuildId: b2cd4b1a020680de4f99e469e5544e21c4e49b5d)
     #06 pc 0000000000140354 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #07 pc 0000000000137338 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #08 pc 0000000000145ff0 /apex/com.android.runtime/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+248) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #09 pc 00000000002e394c  /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_11ShadowFrameEtPNS_6JValueE+388) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #10 pc 00000000002debac  /apex/com.android.runtime/lib64/libart.so (_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtPNS_6JValueE+896) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #11 pc 00000000005a1138 /apex/com.android.runtime/lib64/libart.so (MterpInvokeVirtual+652) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #12 pc 0000000000131818 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #13 pc 00000000002b4c60 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #14 pc 00000000005926a0 /apex/com.android.runtime/lib64/libart.so (artQuickToInterpreterBridge+1036) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #15 pc 000000000014046c /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #16 pc 0000000000137338 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #17 pc 0000000000145ff0 /apex/com.android.runtime/lib64/libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+248) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #18 pc 00000000004b1040 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #19 pc 00000000004b2be4 /apex/com.android.runtime/lib64/libart.so (_ZN3art12InvokeMethodERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectS4_S4_m+1484) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #20 pc 000000000043df18 /apex/com.android.runtime/lib64/libart.so (unknown) (BuildId: ced0e918261ca872f5cff4cdba80b1a9)
     #21 pc 00000000000c2c38 /system/framework/arm64/boot.oat (unknown) (BuildId: e30afaca6cda76e4ae36a16405b32e73a1434e56)
Copy the code

Nine, making

The complete code is as follows:

Github.com/wangshengya…