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
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.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.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;
}
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.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.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.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;
}
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;
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.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.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();
......
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.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;
//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;
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;
}
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.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.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(®s_copy);//循环的每次通过更新regs_copy进行,每次读取一个栈帧信息,main
cur_sp = xcd_regs_get_sp(®s_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, ®s_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(®s_copy) && cur_sp == xcd_regs_get_sp(®s_copy))
break;
}
//xcd_regs_arm64.c
uintptr_t xcd_regs_get_pc(xcd_regs_t *self)
{
return self->r[XCD_REGS_PC];
}
//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.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.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;
};
typedef struct xcd_process xcd_process_t;
typedef struct xcd_thread_info
{
xcd_thread_t t;
TAILQ_ENTRY(xcd_thread_info,) link;
} xcd_thread_info_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.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.c
struct xcd_frames
{
pid_t pid;
xcd_regs_t *regs;
xcd_maps_t *maps;
xcd_frame_queue_t frames;
size_t frames_num;
};
typedef struct xcd_frames xcd_frames_t;
struct xcd_maps
{
xcd_maps_item_queue_t maps;
pid_t pid;
};
typedef struct xcd_maps xcd_maps_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;
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;
«Advanced Design and Implementation of Virtual Machines»
第八章 stack unwinding