new FileObserver("/data/anr/", CLOSE_WRITE)
SIGQUIT
sequenceDiagram
SystemServer->>xcc_signal_trace_register:SIGQUIT
xcc_signal_trace_register->>xcc_signal_trace_register:xc_trace_handler
xcc_signal_trace_register->>xc_trace_dumper:write(xc_trace_notifier, &data, sizeof(data)
Note over xcc_signal_trace_register,xc_trace_dumper:和xc_trace_dumper线程通过阻塞式io通信
xc_trace_dumper->>xc_trace_dumper: xc_trace_load_symbols
activate xc_trace_dumper
xc_trace_dumper->>xc_trace_dumper: xc_dl_create
Note right of xc_trace_dumper: xc_dl_find_map_start,/proc/self/maps中找到指定so,设置self->map_start
Note right of xc_trace_dumper: xc_dl_file_open(open and mmap so)
Note right of xc_trace_dumper: xc_dl_parse_elf
xc_trace_dumper->>xc_trace_dumper: xc_dl_sym
Note right of xc_trace_dumper: read each .symtab/.dynsym, 需要str_offset作为index指向的str等于当前目标str,则找到Sym
Note right of xc_trace_dumper: (void *)(self->map_start + sym->st_value - self->load_bias);
deactivate xc_trace_dumper
xc_trace_dumper->>xc_trace_dumper: xc_trace_libart_runtime_dump
activate xc_trace_dumper
deactivate xc_trace_dumper
Note right of xc_trace_dumper: 转调动态链接的系统so方法处理
Note right of xc_trace_dumper: Runtime::DumpForSigQuit(std::ostream& os),dump本进程anr信息
//xc_trace.c
int xc_trace_init(JNIEnv *env,
int rethrow,
unsigned int logcat_system_lines,
unsigned int logcat_events_lines,
unsigned int logcat_main_lines,
int dump_fds,
int dump_network_info)
{
//capture SIGQUIT only for ART
if(xc_common_api_level < 21) return 0;
//is Android Lollipop (5.x)?
xc_trace_is_lollipop = ((21 == xc_common_api_level || 22 == xc_common_api_level) ? 1 : 0);
//init for JNI callback
xc_trace_init_callback(env);
//create event FD
if(0 > (xc_trace_notifier = eventfd(0, EFD_CLOEXEC))) return XCC_ERRNO_SYS;
//register signal handler
if(0 != (r = xcc_signal_trace_register(xc_trace_handler))) goto err2;//main
//create thread for dump trace
if(0 != (r = pthread_create(&thd, NULL, xc_trace_dumper, NULL))) goto err1;//main
return 0;
//注册SIGQUIT信号处理函数
int xcc_signal_trace_register(void (*handler)(int, siginfo_t *, void *))
{
}
static void xc_trace_handler(int sig, siginfo_t *si, void *uc)
{
uint64_t data;
if(xc_trace_notifier >= 0)
{
data = 1;
XCC_UTIL_TEMP_FAILURE_RETRY(write(xc_trace_notifier, &data, sizeof(data)));//和xc_trace_dumper线程通过阻塞式io通信
}
}
static void *xc_trace_dumper(void *arg)
{
while(1)
{
//block here, waiting for sigquit
XCC_UTIL_TEMP_FAILURE_RETRY(read(xc_trace_notifier, &data, sizeof(data)));
......
//write header info
if(0 != xc_trace_write_header(fd, trace_time)) goto end;
xc_trace_load_symbols()//main
xc_trace_check_address_valid()
dup2(fd, STDERR_FILENO) < 0//接收下面的stderr输出
xc_trace_libart_runtime_dump(*xc_trace_libart_runtime_instance, xc_trace_libcpp_cerr);//转调动态链接的系统so方法处理,是系统在data/anr中trace.txt文件中当前pid的部分,main
skip:
if(0 != xcc_util_write_str(fd, "\n"XCC_UTIL_THREAD_END"\n")) goto end;
//write other info
if(0 != xcc_util_record_logcat(fd, xc_common_process_id, xc_common_api_level, xc_trace_logcat_system_lines, xc_trace_logcat_events_lines, xc_trace_logcat_main_lines)) goto end;
if(xc_trace_dump_fds)
if(0 != xcc_util_record_fds(fd, xc_common_process_id)) goto end;
if(xc_trace_dump_network_info)
if(0 != xcc_util_record_network_info(fd, xc_common_process_id, xc_common_api_level)) goto end;
if(0 != xcc_meminfo_record(fd, xc_common_process_id)) goto end;
end:
//close log file
xc_common_close_trace_log(fd);
//rethrow SIGQUIT to ART Signal Catcher
if(xc_trace_rethrow) xc_trace_send_sigquit();
//JNI callback
//Do we need to implement an emergency buffer for disk exhausted?
if(NULL == xc_trace_cb_method) continue;
if(NULL == (j_pathname = (*env)->NewStringUTF(env, pathname))) continue;
(*env)->CallStaticVoidMethod(env, xc_common_cb_class, xc_trace_cb_method, j_pathname, NULL);
XC_JNI_IGNORE_PENDING_EXCEPTION();
(*env)->DeleteLocalRef(env, j_pathname);
static int xc_trace_load_symbols()
{//动态链接系统so执行任务
if(xc_common_api_level >= 29) libcpp = xc_dl_create(XCC_UTIL_LIBCPP_APEX);
if(NULL == libcpp && NULL == (libcpp = xc_dl_create(XCC_UTIL_LIBCPP))) goto end;
if(NULL == (xc_trace_libcpp_cerr = xc_dl_sym(libcpp, XCC_UTIL_LIBCPP_CERR))) goto end;//main
if(xc_common_api_level >= 29) libart = xc_dl_create(XCC_UTIL_LIBART_APEX);
if(NULL == libart && NULL == (libart = xc_dl_create(XCC_UTIL_LIBART))) goto end;
if(NULL == (xc_trace_libart_runtime_instance = (void **)xc_dl_sym(libart, XCC_UTIL_LIBART_RUNTIME_INSTANCE))) goto end;
if(NULL == (xc_trace_libart_runtime_dump = (xcc_util_libart_runtime_dump_t)xc_dl_sym(libart, XCC_UTIL_LIBART_RUNTIME_DUMP))) goto end;//main
xc_dl_t *xc_dl_create(const char *pathname)
{
xc_dl_t *self;
if(NULL == (self = calloc(1, sizeof(xc_dl_t)))) return NULL;
self->fd = -1;
self->data = MAP_FAILED;
TAILQ_INIT(&(self->symbolsq));
//main
if(0 != xc_dl_find_map_start(self, pathname)) goto err;//确保/proc/self/maps中包含指定的so,设置self->map_start
if(0 != xc_dl_file_open(self, pathname)) goto err;//mmap so
if(0 != xc_dl_parse_elf(self)) goto err;
return self;
err:
xc_dl_destroy(&self);
return NULL;
}
static int xc_dl_find_map_start(xc_dl_t *self, const char *pathname)
{
FILE *f = NULL;
char line[512];
uintptr_t offset;
int pos;
char *p;
int r = XCC_ERRNO_NOTFND;
if(NULL == (f = fopen("/proc/self/maps", "r"))) return XCC_ERRNO_SYS;
while(fgets(line, sizeof(line), f))
{
//main
if(2 != sscanf(line, "%"SCNxPTR"-%*"SCNxPTR" %*4s %"SCNxPTR" %*x:%*x %*d%n", &(self->map_start), &offset, &pos)) continue;
if(0 != offset) continue;
p = xcc_util_trim(line + pos);
if(0 != strcmp(p, pathname)) continue;
r = 0; //found
break;
}
fclose(f);
return r;
}
static int xc_dl_file_open(xc_dl_t *self, const char *pathname)
{
//open file,main
if(0 > (self->fd = XCC_UTIL_TEMP_FAILURE_RETRY(open(pathname, O_RDONLY | O_CLOEXEC)))) return XCC_ERRNO_SYS;
//get file size
//mmap the file,main
if(MAP_FAILED == (self->data = (uint8_t *)mmap(NULL, self->size, PROT_READ, MAP_PRIVATE, self->fd, 0))) return XCC_ERRNO_SYS;
return 0;
}
static int xc_dl_parse_elf(xc_dl_t *self)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr;
ElfW(Shdr) *shdr, *str_shdr;
xc_dl_symbols_t *symbols;
size_t i, cnt = 0;
//get ELF header
if(NULL == (ehdr = xc_dl_file_get(self, 0, sizeof(ElfW(Ehdr))))) return XCC_ERRNO_FORMAT;
//find load_bias in program headers
for(i = 0; i < ehdr->e_phnum * ehdr->e_phentsize; i += ehdr->e_phentsize)
{
if(NULL == (phdr = xc_dl_file_get(self, ehdr->e_phoff + i, sizeof(ElfW(Phdr))))) return XCC_ERRNO_FORMAT;
//PT_LOAD The array element specifies a loadable segment
//p_offset: This member holds the offset from the beginning of the file at which the first byte of the segment resides
if((PT_LOAD == phdr->p_type) && (phdr->p_flags & PF_X) && (0 == phdr->p_offset))//main
{
//p_vaddr: This member holds the virtual address at which the first byte of the segment resides in memory.
self->load_bias = phdr->p_vaddr;
break;
}
}
//find symbol tables in section headers
for(i = ehdr->e_shentsize; i < ehdr->e_shnum * ehdr->e_shentsize; i += ehdr->e_shentsize)
{
if(NULL == (shdr = xc_dl_file_get(self, ehdr->e_shoff + i, sizeof(ElfW(Shdr))))) return XCC_ERRNO_FORMAT;
//SHT_SYMTAB This section holds a symbol table.
//SHT_DYNSYM This section holds a minimal set of dynamic linking symbols.
if(SHT_SYMTAB == shdr->sh_type || SHT_DYNSYM == shdr->sh_type)//main
{
//sh_link This member holds a section header table index link
//e_shnum This member holds the number of entries in the section header table.
if(shdr->sh_link >= ehdr->e_shnum) continue;
//e_shoff This member holds the section header table's file offset in bytes
//e_shentsize This member holds a sections header's size in bytes. A section header is one entry in the section header table; all entries are the same size.
// sh_type This member categorizes the section's contents and semantics.
// SHT_STRTAB This section holds a string table. An object file may have multiple string table sections.
if(NULL == (str_shdr = xc_dl_file_get(self, ehdr->e_shoff + shdr->sh_link * ehdr->e_shentsize, sizeof(ElfW(Shdr))))) return XCC_ERRNO_FORMAT;
if(SHT_STRTAB != str_shdr->sh_type) continue;//main
if(NULL == (symbols = malloc(sizeof(xc_dl_symbols_t)))) return XCC_ERRNO_NOMEM;
//sh_offset This member's value holds the byte offset from the beginning of the file to the first byte in the section
symbols->sym_offset = shdr->sh_offset;
//sh_size This member holds the section's size in bytes.
symbols->sym_end = shdr->sh_offset + shdr->sh_size;
//sh_entsize Some sections hold a table of fixed-sized entries, such as a symbol table.
//For such a section, this member gives the size in bytes for each entry.
symbols->sym_entry_size = shdr->sh_entsize;
symbols->str_offset = str_shdr->sh_offset;
symbols->str_end = str_shdr->sh_offset + str_shdr->sh_size;
TAILQ_INSERT_TAIL(&(self->symbolsq), symbols, link);//main
cnt++;
}
}
if(0 == cnt) return XCC_ERRNO_FORMAT;
return 0;
}
void *xc_dl_sym(xc_dl_t *self, const char *symbol)
{
xc_dl_symbols_t *symbols;
ElfW(Sym) *sym;//main
size_t offset, str_offset;
char *str;
TAILQ_FOREACH(symbols, &(self->symbolsq), link)//main
{
for(offset = symbols->sym_offset; offset < symbols->sym_end; offset += symbols->sym_entry_size)
{
//read .symtab / .dynsym
if(NULL == (sym = xc_dl_file_get(self, offset, sizeof(ElfW(Sym))))) break;//main
// st_shndx Every symbol table entry is "defined" in relation to somesection.
// This member holds the relevant section header table index.
if(SHN_UNDEF == sym->st_shndx) continue;
//read .strtab / .dynstr
//st_name This member holds an index into the object file's symbol string table,
//which holds character representations of the symbol names.
//If the value is nonzero, it represents a string table index that gives the symbol name.
//Otherwise, the symbol has no name.
str_offset = symbols->str_offset + sym->st_name;//main
if(str_offset >= symbols->str_end) continue;
if(NULL == (str = xc_dl_file_get_string(self, str_offset))) continue;
//compare symbol name
if(0 != strcmp(symbol, str)) continue;
//found
//st_value This member gives the value of the associated symbol.是地址
return (void *)(self->map_start + sym->st_value - self->load_bias);//main
}
}
return NULL;
}
//对应libart.so中art/runtime/runtime.cc的方法Runtime::DumpForSigQuit(std::ostream& os),dump本进程的anr信息
#define XCC_UTIL_LIBART_RUNTIME_DUMP "_ZN3art7Runtime14DumpForSigQuitERNSt3__113basic_ostreamIcNS1_11char_traitsIcEEEE"
//symbol address in libc++.so and libart.so
static void *xc_trace_libcpp_cerr = NULL;
typedef void (*xcc_util_libart_runtime_dump_t)(void *runtime, void *ostream);