xCrashNativeCrash

原理时序图

sequenceDiagram

CrashProcess->>CrashProcess: xcc_signal_crash_register
Note right of CrashProcess: 迭代注册每种信号监听,接收到信号调用xc_crash_signal_handler


CrashProcess->>CrashProcess: xc_crash_signal_handler(int sig, siginfo_t *si, void *uc)

CrashProcess->>DumperProcess: pid_t dumper_pid = xc_crash_fork(xc_crash_exec_dumper);

CrashProcess->>CrashProcess: waitpid(dumper_pid,...)
Note right of CrashProcess: wait the crash dumper process terminated

DumperProcess->>DumperProcess: xc_crash_exec_dumper(void *arg)

DumperProcess->>DumperProcess: execl执行libxcrash_dumper.so.main

activate DumperProcess

DumperProcess->>DumperProcess: xcd_process_create
activate DumperProcess


Note right of DumperProcess: 统计崩溃进程所有线程信息到队列xcd_core_proc->thds,通过读取/proc/%d/task", self->pid实现
deactivate DumperProcess

DumperProcess->>CrashProcess: xcd_process_suspend_threads(xcd_core_proc)

activate DumperProcess

Note over DumperProcess,CrashProcess:  suspend all threads in Crash process,通过系统调用ptrace(PTRACE_ATTACH, self->tid,...))实现

deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_process_load_info
activate DumperProcess

DumperProcess->>DumperProcess: xcc_util_get_process_name
activate DumperProcess
Note right of DumperProcess: /proc/%d/cmdline,pid
deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_thread_load_info(&(thd->t));
activate DumperProcess
Note right of DumperProcess: 迭代每个线程,load thread info,"/proc/%d/comm", tid,设置tname
deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_thread_load_regs(&(thd->t));
activate DumperProcess
Note right of DumperProcess: 迭代每个非crash线程,load thread regs,ptrace(PTRACE_GETREGSET, self->tid...),设置regs
deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_thread_load_regs_from_ucontext(&(thd->t), self->uc);
activate DumperProcess
Note right of DumperProcess: crash线程,load thread regs,从uc中获取并设置regs
deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_maps_create(&(self->maps), self->pid)
activate DumperProcess
Note right of DumperProcess: load maps,"/proc/%d/maps", pid,设置maps
deactivate DumperProcess

deactivate DumperProcess

DumperProcess->>DumperProcess: xcd_process_record
activate DumperProcess

DumperProcess->>DumperProcess: 记录写入崩溃线程信息
activate DumperProcess

DumperProcess->>DumperProcess: xcd_thread_load_frames
activate DumperProcess
Note right of DumperProcess: 循环每次读取一个栈帧信息,根据rel_pc等信息在xcd_elf_step时更新regs_copy
deactivate DumperProcess

deactivate DumperProcess

deactivate DumperProcess

DumperProcess->>CrashProcess: xcd_process_resume_threads(xcd_core_proc)
activate DumperProcess
Note over DumperProcess,CrashProcess: resume all threads in the process,遍历线程ptrace(PTRACE_DETACH, self->tid,)
deactivate DumperProcess

deactivate DumperProcess

DumperProcess->>CrashProcess: _exit dumper process

XCrash.init

public static synchronized int init(Context ctx, InitParameters params) {
//save log dir
 if (TextUtils.isEmpty(params.logDir)) {
   params.logDir = ctx.getFilesDir() + "/tombstones";
 }
 XCrash.logDir = params.logDir;

//init file manager
 FileManager.getInstance().initialize(
   params.logDir,
   params.javaLogCountMax,
   params.nativeLogCountMax,
   params.anrLogCountMax,
   params.placeholderCountMax,
   params.placeholderSizeKb,
   params.logFileMaintainDelayMs);

//init java crash handler
 if (params.enableJavaCrashHandler) {
   JavaCrashHandler.getInstance().initialize(
     pid,
     processName,
     appId,
     params.appVersion,
     params.logDir,
     params.javaRethrow,
     params.javaLogcatSystemLines,
     params.javaLogcatEventsLines,
     params.javaLogcatMainLines,
     params.javaDumpFds,
     params.javaDumpNetworkInfo,
     params.javaDumpAllThreads,
     params.javaDumpAllThreadsCountMax,
     params.javaDumpAllThreadsWhiteList,
     params.javaCallback);
 }

//init ANR handler (API level < 21)
 if (params.enableAnrHandler && Build.VERSION.SDK_INT < 21) {
   AnrHandler.getInstance().initialize(
     ctx,
     pid,
     processName,
     appId,
     params.appVersion,
     params.logDir,
     params.anrCheckProcessState,
     params.anrLogcatSystemLines,
     params.anrLogcatEventsLines,
     params.anrLogcatMainLines,
     params.anrDumpFds,
     params.anrDumpNetworkInfo,
     params.anrCallback);
 }

//init native crash handler / ANR handler (API level >= 21)
 int r = Errno.OK;
 if (params.enableNativeCrashHandler || (params.enableAnrHandler && Build.VERSION.SDK_INT >= 21)) {
   r = NativeHandler.getInstance().initialize(//main
     ctx,
     params.libLoader,
     appId,
     params.appVersion,
     params.logDir,
     params.enableNativeCrashHandler,
     params.nativeRethrow,
     params.nativeLogcatSystemLines,
     params.nativeLogcatEventsLines,
     params.nativeLogcatMainLines,
     params.nativeDumpElfHash,
     params.nativeDumpMap,
     params.nativeDumpFds,
     params.nativeDumpNetworkInfo,
     params.nativeDumpAllThreads,
     params.nativeDumpAllThreadsCountMax,
     params.nativeDumpAllThreadsWhiteList,
     params.nativeCallback,
     params.enableAnrHandler && Build.VERSION.SDK_INT >= 21,
     params.anrRethrow,
     params.anrCheckProcessState,
     params.anrLogcatSystemLines,
     params.anrLogcatEventsLines,
     params.anrLogcatMainLines,
     params.anrDumpFds,
     params.anrDumpNetworkInfo,
     params.anrCallback);
 }
 
 //maintain tombstone and placeholder files in a background thread with some delay
 FileManager.getInstance().maintain();
 return r;

xc_jni_init

//xc_jni.c
static jint xc_jni_init(JNIEnv       *env,
                        jobject       thiz,
                        jint          api_level,
                        jstring       os_version,
                        jstring       abi_list,
                        jstring       manufacturer,
                        jstring       brand,
                        jstring       model,
                        jstring       build_fingerprint,
                        jstring       app_id,
                        jstring       app_version,
                        jstring       app_lib_dir,
                        jstring       log_dir,
                        jboolean      crash_enable,
                        jboolean      crash_rethrow,
                        jint          crash_logcat_system_lines,
                        jint          crash_logcat_events_lines,
                        jint          crash_logcat_main_lines,
                        jboolean      crash_dump_elf_hash,
                        jboolean      crash_dump_map,
                        jboolean      crash_dump_fds,
                        jboolean      crash_dump_network_info,
                        jboolean      crash_dump_all_threads,
                        jint          crash_dump_all_threads_count_max,
                        jobjectArray  crash_dump_all_threads_whitelist,
                        jboolean      trace_enable,
                        jboolean      trace_rethrow,
                        jint          trace_logcat_system_lines,
                        jint          trace_logcat_events_lines,
                        jint          trace_logcat_main_lines,
                        jboolean      trace_dump_fds,
                        jboolean      trace_dump_network_info)
{

    //common init
    if(0 != xc_common_init((int)api_level, //main
                           c_os_version,
                           c_abi_list,
                           c_manufacturer,
                           c_brand,
                           c_model,
                           c_build_fingerprint,
                           c_app_id,
                           c_app_version,
                           c_app_lib_dir,
                           c_log_dir)) goto clean;

    if(crash_enable)//对应native crash
    {
        //crash init
        r_crash = xc_crash_init(env,//main
                                crash_rethrow ? 1 : 0,
                                (unsigned int)crash_logcat_system_lines,
                                (unsigned int)crash_logcat_events_lines,
                                (unsigned int)crash_logcat_main_lines,
                                crash_dump_elf_hash ? 1 : 0,
                                crash_dump_map ? 1 : 0,
                                crash_dump_fds ? 1 : 0,
                                crash_dump_network_info ? 1 : 0,
                                crash_dump_all_threads ? 1 : 0,
                                (unsigned int)crash_dump_all_threads_count_max,
                                c_crash_dump_all_threads_whitelist,
                                c_crash_dump_all_threads_whitelist_len);
}

    if(trace_enable)//对应anr
    {
        //trace init
        r_trace = xc_trace_init(env,
                            trace_rethrow ? 1 : 0,
                            (unsigned int)trace_logcat_system_lines,
                            (unsigned int)trace_logcat_events_lines,
                            (unsigned int)trace_logcat_main_lines,
                            trace_dump_fds ? 1 : 0,
                            trace_dump_network_info ? 1 : 0);
    }

xc_common_init

//xc_common.c
int xc_common_init(int         api_level,
                   const char *os_version,
                   const char *abi_list,
                   const char *manufacturer,
                   const char *brand,
                   const char *model,
                   const char *build_fingerprint,
                   const char *app_id,
                   const char *app_version,
                   const char *app_lib_dir,
                   const char *log_dir)
{
    //check or create log directory
    if(0 != (r = xc_util_mkdirs(log_dir))) goto err;

    //create prepared FD for FD exhausted case
    xc_common_open_prepared_fd(1);
    xc_common_open_prepared_fd(0);

    return 0;
}

xc_crash_init

int xc_crash_init(JNIEnv *env,
                  int rethrow,
                  unsigned int logcat_system_lines,
                  unsigned int logcat_events_lines,
                  unsigned int logcat_main_lines,
                  int dump_elf_hash,
                  int dump_map,
                  int dump_fds,
                  int dump_network_info,
                  int dump_all_threads,
                  unsigned int dump_all_threads_count_max,
                  const char **dump_all_threads_whitelist,
                  size_t dump_all_threads_whitelist_len)
{

    //init the local unwinder for fallback mode
    xcc_unwind_init(xc_common_api_level);//main

    //init for JNI callback
    xc_crash_init_callback(env);//main

    //struct info passed to the dumper process
    memset(&xc_crash_spot, 0, sizeof(xcc_spot_t));
    xc_crash_spot.api_level = xc_common_api_level;
    xc_crash_spot.crash_pid = xc_common_process_id;
    xc_crash_spot.start_time = xc_common_start_time;
    xc_crash_spot.time_zone = xc_common_time_zone;
    xc_crash_spot.logcat_system_lines = logcat_system_lines;
    xc_crash_spot.logcat_events_lines = logcat_events_lines;
    xc_crash_spot.logcat_main_lines = logcat_main_lines;
    xc_crash_spot.dump_elf_hash = dump_elf_hash;
    xc_crash_spot.dump_map = dump_map;
    xc_crash_spot.dump_fds = dump_fds;
    xc_crash_spot.dump_network_info = dump_network_info;
    xc_crash_spot.dump_all_threads = dump_all_threads;
    xc_crash_spot.dump_all_threads_count_max = dump_all_threads_count_max;
    xc_crash_spot.os_version_len = strlen(xc_common_os_version);
    xc_crash_spot.kernel_version_len = strlen(xc_common_kernel_version);
    xc_crash_spot.abi_list_len = strlen(xc_common_abi_list);
    xc_crash_spot.manufacturer_len = strlen(xc_common_manufacturer);
    xc_crash_spot.brand_len = strlen(xc_common_brand);
    xc_crash_spot.model_len = strlen(xc_common_model);
    xc_crash_spot.build_fingerprint_len = strlen(xc_common_build_fingerprint);
    xc_crash_spot.app_id_len = strlen(xc_common_app_id);
    xc_crash_spot.app_version_len = strlen(xc_common_app_version);
    xc_crash_init_dump_all_threads_whitelist(dump_all_threads_whitelist, dump_all_threads_whitelist_len);

    //register signal handler
    return xcc_signal_crash_register(xc_crash_signal_handler);//main

xcc_unwind_init

//xcc_unwind.c
void xcc_unwind_init(int api_level)
{
#if defined(__arm__) || defined(__i386__)
    if(api_level >= 16 && api_level <= 20)
    {
        xcc_unwind_libcorkscrew_init();//main
    }
#endif
    
    if(api_level >= 21 && api_level <= 23)
    {
        xcc_unwind_libunwind_init();//main
    }
}
xcc_unwind_libcorkscrew_init
//xcc_unwind_libcorkscrew.c
void xcc_unwind_libcorkscrew_init(void)
{
    if(NULL == (libcorkscrew = dlopen("libcorkscrew.so", RTLD_NOW))) return;
    //获取libcorkscrew.so中的以下方法加载到内存中的地址,main
    if(NULL == (unwind_backtrace_signal_arch = (t_unwind_backtrace_signal_arch)dlsym(libcorkscrew, "unwind_backtrace_signal_arch"))) goto err;
    if(NULL == (acquire_my_map_info_list = (t_acquire_my_map_info_list)dlsym(libcorkscrew, "acquire_my_map_info_list"))) goto err;
    release_my_map_info_list = (t_release_my_map_info_list)dlsym(libcorkscrew, "release_my_map_info_list");
    if(NULL == (get_backtrace_symbols = (t_get_backtrace_symbols)dlsym(libcorkscrew, "get_backtrace_symbols"))) goto err;
    free_backtrace_symbols = (t_free_backtrace_symbols)dlsym(libcorkscrew, "free_backtrace_symbols");
    return;

 err:
    dlclose(libcorkscrew);
    libcorkscrew = NULL;
}
xcc_unwind_libunwind_init
//xcc_unwind_libunwind.c
void xcc_unwind_libunwind_init(void)
{
    if(NULL == (libunwind = dlopen("libunwind.so", RTLD_NOW))) return;
    //main
    if(NULL == (unw_init_local = (t_unw_init_local)dlsym(libunwind, "_U"UNW_TARGET"_init_local"))) goto err;
    if(NULL == (unw_get_reg = (t_unw_get_reg)dlsym(libunwind, "_U"UNW_TARGET"_get_reg"))) goto err;
    if(NULL == (unw_step = (t_unw_step)dlsym(libunwind, "_U"UNW_TARGET"_step"))) goto err;
    return;

 err:
    dlclose(libunwind);
    libunwind = NULL;
}

xc_crash_init_callback

static void xc_crash_init_callback(JNIEnv *env)
{
    if(NULL == xc_common_cb_class) return;
    
    xc_crash_cb_method = (*env)->GetStaticMethodID(env, xc_common_cb_class, XC_CRASH_CALLBACK_METHOD_NAME, XC_CRASH_CALLBACK_METHOD_SIGNATURE);
    XC_JNI_CHECK_NULL_AND_PENDING_EXCEPTION(xc_crash_cb_method, err);
    
    //eventfd and a new thread for callback
    if(0 > (xc_crash_cb_notifier = eventfd(0, EFD_CLOEXEC))) goto err;
    if(0 != pthread_create(&xc_crash_cb_thd, NULL, xc_crash_callback_thread, NULL)) goto err;//main
    return;
xc_crash_callback_thread
static void *xc_crash_callback_thread(void *arg)
{
    //do callback: xcrash/NativeHandler. crashCallback(),main
    (*env)->CallStaticVoidMethod(env, xc_common_cb_class, xc_crash_cb_method, j_pathname, j_emergency,
                                 j_dump_java_stacktrace, j_is_main_thread, j_thread_name);

xcc_signal_crash_register

//xcc_signal.c
int xcc_signal_crash_register(void (*handler)(int, siginfo_t *, void *))
{
    stack_t ss;
    if(NULL == (ss.ss_sp = calloc(1, XCC_SIGNAL_CRASH_STACK_SIZE))) return XCC_ERRNO_NOMEM;
    ss.ss_size  = XCC_SIGNAL_CRASH_STACK_SIZE;
    ss.ss_flags = 0;
    if(0 != sigaltstack(&ss, NULL)) return XCC_ERRNO_SYS;//main

    struct sigaction act;//main
    memset(&act, 0, sizeof(act));
    sigfillset(&act.sa_mask);
    act.sa_sigaction = handler;//main
    act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;//main
    
    size_t i;
    for(i = 0; i < sizeof(xcc_signal_crash_info) / sizeof(xcc_signal_crash_info[0]); i++)
      //每次迭代注册一种信号监听,main
        if(0 != sigaction(xcc_signal_crash_info[i].signum, &act, &(xcc_signal_crash_info[i].oldact)))
            return XCC_ERRNO_SYS;

    return 0;
}

xc_crash_signal_handler

//xc_crash.c
static void xc_crash_signal_handler(int sig, siginfo_t *si, void *uc)
{

    //restore the original/default signal handler
    if(xc_crash_rethrow)
    {
        if(0 != xcc_signal_crash_unregister()) goto exit;
    }
    else
    {
        if(0 != xcc_signal_crash_ignore()) goto exit;
    }

 //create and open log file
    if((xc_crash_log_fd = xc_common_open_crash_log(xc_crash_log_pathname, sizeof(xc_crash_log_pathname), &xc_crash_log_from_placeholder)) < 0) goto end;

    memcpy(&(xc_crash_spot.siginfo), si, sizeof(siginfo_t));//main
    memcpy(&(xc_crash_spot.ucontext), uc, sizeof(ucontext_t));//main

    //spawn crash dumper process
    errno = 0;
    pid_t dumper_pid = xc_crash_fork(xc_crash_exec_dumper);//main

    //parent process ...
    //wait the crash dumper process terminated
    errno = 0;
    int status = 0;
    int wait_r = XCC_UTIL_TEMP_FAILURE_RETRY(waitpid(dumper_pid, &status, __WALL));//main

......

    if(xc_crash_log_fd >= 0)
    {
        //record java stacktrace
        xc_xcrash_record_java_stacktrace();//main
        
        //we have written all the required information in the native layer, close the FD
        close(xc_crash_log_fd);
        xc_crash_log_fd = -1;
    }

    //JNI callback
    xc_crash_callback();

......

xc_crash_fork

static int xc_crash_fork(int (*fn)(void *))
{
#ifndef __i386__
    return clone(fn, xc_crash_child_stack, CLONE_VFORK | CLONE_FS | CLONE_UNTRACED, NULL);
#else
    pid_t dumper_pid = fork();//main
    if(-1 == dumper_pid)
    {
        return -1;
    }
    else if(0 == dumper_pid)
    {
        //child process ...
        char msg = 'a';
        XCC_UTIL_TEMP_FAILURE_RETRY(write(xc_crash_child_notifier[1], &msg, sizeof(char)));
        syscall(SYS_close, xc_crash_child_notifier[0]);
        syscall(SYS_close, xc_crash_child_notifier[1]);

        _exit(fn(NULL));//main
    }
    else
    {
        //parent process ...
        char msg;
        XCC_UTIL_TEMP_FAILURE_RETRY(read(xc_crash_child_notifier[0], &msg, sizeof(char)));
        syscall(SYS_close, xc_crash_child_notifier[0]);
        syscall(SYS_close, xc_crash_child_notifier[1]);

        return dumper_pid;
    }
#endif
}

xc_crash_exec_dumper

//xc_crash.c
static int xc_crash_exec_dumper(void *arg)
{    
    //escape to the dumper process
    errno = 0;
    execl(xc_crash_dumper_pathname, XCC_UTIL_XCRASH_DUMPER_FILENAME, NULL);//执行libxcrash_dumper.so,main
    return 100 + errno;

libxcrash_dumper.so.main

//xcd_core.c
int main(int argc, char** argv)
{
    //don't leave a zombie process
    alarm(30);

    //read args from stdin
    if(0 != xcd_core_read_args()) exit(1);//main

    //open log file
    if(0 > (xcd_core_log_fd = XCC_UTIL_TEMP_FAILURE_RETRY(open(xcd_core_log_pathname, O_WRONLY | O_CLOEXEC)))) exit(2);

    //register signal handler for catching self-crashing
    xcc_unwind_init(xcd_core_spot.api_level);
    xcc_signal_crash_register(xcd_core_signal_handler);

    //create process object,统计崩溃进程所有线程信息到队列xcd_core_proc->thds,通过读取/proc/%d/task", self->pid实现
    if(0 != xcd_process_create(&xcd_core_proc,
                               xcd_core_spot.crash_pid,//main
                               xcd_core_spot.crash_tid,
                               &(xcd_core_spot.siginfo),
                               &(xcd_core_spot.ucontext))) exit(3);

    //suspend all threads in the process
    xcd_process_suspend_threads(xcd_core_proc);//main

    //load process info
    if(0 != xcd_process_load_info(xcd_core_proc)) exit(4);//main

    //record system info,记录并写入tombstone文件开头的系统信息
    if(0 != xcd_sys_record(xcd_core_log_fd,
                           xcd_core_spot.time_zone,
                           xcd_core_spot.start_time,
                           xcd_core_spot.crash_time,
                           xcd_core_app_id,
                           xcd_core_app_version,
                           xcd_core_spot.api_level,
                           xcd_core_os_version,
                           xcd_core_kernel_version,
                           xcd_core_abi_list,
                           xcd_core_manufacturer,
                           xcd_core_brand,
                           xcd_core_model,
                           xcd_core_build_fingerprint)) exit(5);

    //record process info,main
    if(0 != xcd_process_record(xcd_core_proc,
                               xcd_core_log_fd,
                               xcd_core_spot.logcat_system_lines,
                               xcd_core_spot.logcat_events_lines,
                               xcd_core_spot.logcat_main_lines,
                               xcd_core_spot.dump_elf_hash,
                               xcd_core_spot.dump_map,
                               xcd_core_spot.dump_fds,
                               xcd_core_spot.dump_network_info,
                               xcd_core_spot.dump_all_threads,
                               xcd_core_spot.dump_all_threads_count_max,
                               xcd_core_dump_all_threads_whitelist,
                               xcd_core_spot.api_level)) exit(6);

    //resume all threads in the process
    xcd_process_resume_threads(xcd_core_proc);//遍历线程PTRACE_DETACH,main

return 0;

xcd_process_suspend_threads

void xcd_process_suspend_threads(xcd_process_t *self)
{
    xcd_thread_info_t *thd;
    TAILQ_FOREACH(thd, &(self->thds), link)//每个线程都暂停
        xcd_thread_suspend(&(thd->t));//main
}
void xcd_thread_suspend(xcd_thread_t *self)
{
    if(0 != ptrace(PTRACE_ATTACH, self->tid, NULL, NULL))
    {
        self->status = XCD_THREAD_STATUS_ATTACH;
        return;
    }

xcd_process_load_info

int xcd_process_load_info(xcd_process_t *self)
{
    int                r;
    xcd_thread_info_t *thd;
    char               buf[256];
    
    // /proc/%d/cmdline,pid
    xcc_util_get_process_name(self->pid, buf, sizeof(buf));
    if(NULL == (self->pname = strdup(buf))) self->pname = "unknown";

    TAILQ_FOREACH(thd, &(self->thds), link)
    {
        //load thread info,main
        xcd_thread_load_info(&(thd->t));//"/proc/%d/comm", tid,设置tname
        
        //load thread regs,main
        if(thd->t.tid != self->crash_tid)
            xcd_thread_load_regs(&(thd->t));//ptrace(PTRACE_GETREGSET, self->tid...),设置regs
        else
            xcd_thread_load_regs_from_ucontext(&(thd->t), self->uc);//从uc中获取并设置regs
    }

    //load maps,main
    if(0 != (r = xcd_maps_create(&(self->maps), self->pid)))//"/proc/%d/maps", pid,设置maps
        XCD_LOG_ERROR("PROCESS: create maps failed, errno=%d", r);

    return 0;
}

xcd_process_record

//xcd_process.c/h
int xcd_process_record(xcd_process_t *self,
                       int log_fd,
                       unsigned int logcat_system_lines,
                       unsigned int logcat_events_lines,
                       unsigned int logcat_main_lines,
                       int dump_elf_hash,
                       int dump_map,
                       int dump_fds,
                       int dump_network_info,
                       int dump_all_threads,
                       unsigned int dump_all_threads_count_max,
                       char *dump_all_threads_whitelist,
                       int api_level)
{
    //记录写入崩溃线程信息
    TAILQ_FOREACH(thd, &(self->thds), link)
    {
        if(thd->t.tid == self->crash_tid)
        {
            if(0 != (r = xcd_thread_record_info(&(thd->t), log_fd, self->pname))) return r;
            if(0 != (r = xcd_process_record_signal_info(self, log_fd))) return r;
            if(0 != (r = xcd_process_record_abort_message(self, log_fd, api_level))) return r;
            if(0 != (r = xcd_thread_record_regs(&(thd->t), log_fd))) return r;
            if(0 == xcd_thread_load_frames(&(thd->t), self->maps))//main
            {
                if(0 != (r = xcd_thread_record_backtrace(&(thd->t), log_fd))) return r;//main
                if(0 != (r = xcd_thread_record_buildid(&(thd->t), log_fd, dump_elf_hash, xcc_util_signal_has_si_addr(self->si) ? (uintptr_t)self->si->si_addr : 0))) return r;
                if(0 != (r = xcd_thread_record_stack(&(thd->t), log_fd))) return r;
                if(0 != (r = xcd_thread_record_memory(&(thd->t), log_fd))) return r;
            }
            if(dump_map) if(0 != (r = xcd_maps_record(self->maps, log_fd))) return r;
            if(0 != (r = xcc_util_record_logcat(log_fd, self->pid, api_level, logcat_system_lines, logcat_events_lines, logcat_main_lines))) return r;
            if(dump_fds) if(0 != (r = xcc_util_record_fds(log_fd, self->pid))) return r;
            if(dump_network_info) if(0 != (r = xcc_util_record_network_info(log_fd, self->pid, api_level))) return r;
            if(0 != (r = xcc_meminfo_record(log_fd, self->pid))) return r;

            break;
        }
    }

if(!dump_all_threads) return 0;
    //统计白名单中每个线程信息
    //parse thread name whitelist regex
    re = xcd_process_build_whitelist_regex(dump_all_threads_whitelist, &re_cnt);

    TAILQ_FOREACH(thd, &(self->thds), link)
    {
        if(thd->t.tid != self->crash_tid)
        {
            //check regex for thread name
            if(NULL != re && re_cnt > 0 && !xcd_process_if_need_dump(thd->t.tname, re, re_cnt))
            {
                continue;
            }
            thd_matched_regex++;

            //check dump count limit
            if(dump_all_threads_count_max > 0 && thd_dumped >= dump_all_threads_count_max)
            {
                thd_ignored_by_limit++;
                continue;
            }

            if(0 != (r = xcc_util_write_str(log_fd, XCC_UTIL_THREAD_SEP))) goto end;
            if(0 != (r = xcd_thread_record_info(&(thd->t), log_fd, self->pname))) goto end;
            if(0 != (r = xcd_thread_record_regs(&(thd->t), log_fd))) goto end;
            if(0 == xcd_thread_load_frames(&(thd->t), self->maps))
            {
                if(0 != (r = xcd_thread_record_backtrace(&(thd->t), log_fd))) goto end;
                if(0 != (r = xcd_thread_record_stack(&(thd->t), log_fd))) goto end;
            }
            thd_dumped++;
        }
    }

end:
    if(self->nthds > 1)
    {
        if(0 == thd_dumped)
            if(0 != (r = xcc_util_write_str(log_fd, XCC_UTIL_THREAD_SEP))) goto ret;

        if(0 != (r = xcc_util_write_format(log_fd, "total threads (exclude the crashed thread): %zu\n", self->nthds - 1))) goto ret;
        if(NULL != re && re_cnt > 0)
            if(0 != (r = xcc_util_write_format(log_fd, "threads matched whitelist: %d\n", thd_matched_regex))) goto ret;
        if(dump_all_threads_count_max > 0)
            if(0 != (r = xcc_util_write_format(log_fd, "threads ignored by max count limit: %d\n", thd_ignored_by_limit))) goto ret;
        if(0 != (r = xcc_util_write_format(log_fd, "dumped threads: %u\n", thd_dumped))) goto ret;
        
        if(0 != (r = xcc_util_write_str(log_fd, XCC_UTIL_THREAD_END))) goto ret;
    }
    
 ret:
    return r;

xcd_thread_load_frames

//xcd_thread.h/c
int xcd_thread_load_frames(xcd_thread_t *self, xcd_maps_t *maps)
{
    if(XCD_THREAD_STATUS_OK != self->status) return XCC_ERRNO_STATE; //do NOT ignore
    return xcd_frames_create(&(self->frames), &(self->regs), maps, self->pid);//main
}
int xcd_frames_create(xcd_frames_t **self, xcd_regs_t *regs, xcd_maps_t *maps, pid_t pid)
{
    if(NULL == (*self = malloc(sizeof(xcd_frames_t)))) return XCC_ERRNO_NOMEM;
    (*self)->pid = pid;
    (*self)->regs = regs;
    (*self)->maps = maps;
    TAILQ_INIT(&((*self)->frames));
    (*self)->frames_num = 0;
    
    xcd_frames_load(*self);//main
    return 0;
}
//xcd_frames.c
static void xcd_frames_load(xcd_frames_t *self)
{
    xcd_frame_t  *frame;
    xcd_map_t    *map;
    xcd_map_t    *map_sp;
    xcd_elf_t    *elf;
    xcd_regs_t    regs_copy = *(self->regs);//main
    while(self->frames_num < XCD_FRAMES_MAX)
    {
        cur_pc = xcd_regs_get_pc(&regs_copy);//循环的每次通过更新regs_copy进行,每次读取一个栈帧信息,main
        cur_sp = xcd_regs_get_sp(&regs_copy);

        
        if(NULL != (map = xcd_maps_find_map(self->maps, cur_pc)))//main
        {
            //get relative pc,main
            rel_pc = xcd_map_get_rel_pc(map, step_pc, self->pid, (void *)self->maps);
            step_pc = rel_pc;

            elf = xcd_map_get_elf(map, self->pid, (void *)self->maps);
            if(NULL != elf)
            {
                load_bias = xcd_elf_get_load_bias(elf);
                memory = xcd_elf_get_memory(elf);
            }

            if(adjust_pc)
                pc_adjustment = xcd_regs_get_adjust_pc(rel_pc, load_bias, memory);
            
            step_pc -= pc_adjustment;
        }
        adjust_pc = 1;

            if(0 == xcd_elf_step(elf, rel_pc, step_pc, &regs_copy, &finished, &sigreturn))//main
                stepped = 1;
            else
                stepped = 0;
......
        //If the pc and sp didn't change, then consider everything stopped.
        if(cur_pc == xcd_regs_get_pc(&regs_copy) && cur_sp == xcd_regs_get_sp(&regs_copy))
            break;
}
xcd_regs_get_pc
//xcd_regs_arm64.c
uintptr_t xcd_regs_get_pc(xcd_regs_t *self)
{
    return self->r[XCD_REGS_PC];
}
xcd_maps_find_map
//xcd_maps.h.c
xcd_map_t *xcd_maps_find_map(xcd_maps_t *self, uintptr_t pc)
{
    xcd_maps_item_t *mi;

    TAILQ_FOREACH(mi, &(self->maps), link)
        if(pc >= mi->map.start && pc < mi->map.end)
            return &(mi->map);

    return NULL;
}
xcd_map_get_rel_pc
//xcd.map.c
uintptr_t xcd_map_get_rel_pc(xcd_map_t *self, uintptr_t pc, pid_t pid, void *maps_obj)
{
    xcd_elf_t *elf = xcd_map_get_elf(self, pid, maps_obj);
    uintptr_t load_bias = (NULL == elf ? 0 : xcd_elf_get_load_bias(elf));
    
    return pc - self->start + load_bias + self->elf_offset;
}

其他

xcd_process

//xcd_process.c/h
struct xcd_process
{
    pid_t                    pid;
    char                    *pname;
    pid_t                    crash_tid;
    ucontext_t              *uc;
    siginfo_t               *si;
    xcd_thread_info_queue_t  thds;
    size_t                   nthds;
    xcd_maps_t              *maps;
};

xcd_process_t

typedef struct xcd_process xcd_process_t;

xcd_thread_info_t

typedef struct xcd_thread_info
{
    xcd_thread_t t;
    TAILQ_ENTRY(xcd_thread_info,) link;
} xcd_thread_info_t;

xcd_thread_status_t

//xcd_thread.h/c
typedef enum
{
    XCD_THREAD_STATUS_OK = 0,
    XCD_THREAD_STATUS_UNKNOWN,
    XCD_THREAD_STATUS_REGS,
    XCD_THREAD_STATUS_ATTACH,
    XCD_THREAD_STATUS_ATTACH_WAIT
} xcd_thread_status_t;

xcd_thread_t

//xcd_thread.h/c
typedef struct xcd_thread
{
    xcd_thread_status_t  status;
    pid_t                pid;
    pid_t                tid;
    char                *tname;
    xcd_regs_t           regs;
    xcd_frames_t        *frames;
} xcd_thread_t;

xcd_frames

//xcd_frames.c
struct xcd_frames
{
    pid_t              pid;
    xcd_regs_t        *regs;
    xcd_maps_t        *maps;
    xcd_frame_queue_t  frames;
    size_t             frames_num;
};

xcd_frames_t

typedef struct xcd_frames xcd_frames_t;

xcd_maps

struct xcd_maps
{
    xcd_maps_item_queue_t maps;
    pid_t                 pid;
};

xcd_maps_t

typedef struct xcd_maps xcd_maps_t;

xcd_maps_item,xcd_maps_item_t

xcd_maps_item_queue_t

typedef struct xcd_maps_item
{
    xcd_map_t map;
    TAILQ_ENTRY(xcd_maps_item,) link;
} xcd_maps_item_t;
typedef TAILQ_HEAD(xcd_maps_item_queue, xcd_maps_item,) xcd_maps_item_queue_t;

xcd_map,xcd_map_t

typedef struct xcd_map
{
    //base info from /proc/<PID>/maps
    uintptr_t  start;
    uintptr_t  end;
    size_t     offset;
    uint16_t   flags;
    char      *name;

    //ELF
    xcd_elf_t *elf;
    int        elf_loaded;
    size_t     elf_offset;
    size_t     elf_start_offset;
} xcd_map_t;

参考

stack unwinding

«Advanced Design and Implementation of Virtual Machines»

第八章 stack unwinding