diff --git a/source/ucli_py/unwind/Makefile b/source/ucli_py/unwind/Makefile new file mode 100644 index 0000000..56df97f --- /dev/null +++ b/source/ucli_py/unwind/Makefile @@ -0,0 +1,29 @@ +# Specify the compiler +CXX=g++ + +# Specify the target +TARGET=libunwind.so + +# List of source files +SRCS=symbol.cc unwind.cc + +# List of object files +OBJS=$(SRCS:.cc=.o) + +# Compilation flags +CXXFLAGS=-fPIC -c -Wall + +# Default target +all: $(TARGET) + +# Rule to link the target +$(TARGET): $(OBJS) + $(CXX) -shared -o $@ $^ + +# Rule to compile source files +%.o: %.cc + $(CXX) $(CXXFLAGS) $< -o $@ + +# Rule to clean intermediate files +clean: + rm -f $(OBJS) $(TARGET) \ No newline at end of file diff --git a/source/ucli_py/unwind/elf.cc b/source/ucli_py/unwind/elf.cc new file mode 100644 index 0000000..b4901e2 --- /dev/null +++ b/source/ucli_py/unwind/elf.cc @@ -0,0 +1,566 @@ +/* + * Linux内核诊断工具--elf相关公共函数 + * + * Copyright (C) 2020 Alibaba Ltd. + * + * License terms: GNU General Public License (GPL) version 3 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "elf.h" +#include "attach.h" +#include "internal.h" + +#define NOTE_ALIGN(n) (((n) + 3) & -4U) + +struct sym_section_ctx { + Elf_Data *syms; + Elf_Data *symstrs; + Elf_Data *rel_data; + int is_reloc; + int is_plt; + int sym_count; + int plt_rel_type; + unsigned long plt_offset; + unsigned long plt_entsize; +}; + +struct symbol_sections_ctx { + sym_section_ctx symtab; + sym_section_ctx symtab_in_dynsym; + sym_section_ctx dynsymtab; +}; + +struct section_info { + Elf_Scn *sec; + GElf_Shdr *hdr; +}; + +struct plt_ctx { + section_info dynsym; + section_info plt_rel; + section_info plt; +}; + +__attribute__((unused)) static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name, + size_t *idx) { + Elf_Scn *sec = NULL; + size_t cnt = 1; + + /* Elf is corrupted/truncated, avoid calling elf_strptr. */ + if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) + return NULL; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + + gelf_getshdr(sec, shp); + str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + + if (!strcmp(name, str)) { + if (idx) + *idx = cnt; + + break; + } + + ++cnt; + } + + return sec; +} + +__attribute__((unused)) static int elf_read_build_id(Elf *elf, char *bf, size_t size) { + int err = -1; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + Elf_Data *data; + Elf_Scn *sec; + Elf_Kind ek; + char *ptr; + + if (size < BUILD_ID_SIZE) + goto out; + + ek = elf_kind(elf); + + if (ek != ELF_K_ELF) + goto out; + + if (gelf_getehdr(elf, &ehdr) == NULL) { + fprintf(stderr, "%s: cannot get elf header.\n", __func__); + goto out; + } + + /* + * Check following sections for notes: + * '.note.gnu.build-id' + * '.notes' + * '.note' (VDSO specific) + */ + do { + sec = elf_section_by_name(elf, &ehdr, &shdr, + ".note.gnu.build-id", NULL); + + if (sec) + break; + + sec = elf_section_by_name(elf, &ehdr, &shdr, + ".notes", NULL); + + if (sec) + break; + + sec = elf_section_by_name(elf, &ehdr, &shdr, + ".note", NULL); + + if (sec) + break; + + return err; + + } while (0); + + data = elf_getdata(sec, NULL); + + if (data == NULL) + goto out; + + ptr = (char *)data->d_buf; + + while ((intptr_t)ptr < (intptr_t)((char *)data->d_buf + data->d_size)) { + GElf_Nhdr *nhdr = (GElf_Nhdr *)ptr; + size_t namesz = NOTE_ALIGN(nhdr->n_namesz), + descsz = NOTE_ALIGN(nhdr->n_descsz); + const char *name; + + ptr += sizeof(*nhdr); + name = (const char *)ptr; + ptr += namesz; + + if (nhdr->n_type == NT_GNU_BUILD_ID && + nhdr->n_namesz == sizeof("GNU")) { + if (memcmp(name, "GNU", sizeof("GNU")) == 0) { + size_t sz = size < descsz ? size : descsz; + memcpy(bf, ptr, sz); + memset(bf + sz, 0, size - sz); + err = descsz; + break; + } + } + + ptr += descsz; + } + +out: + return err; +} + +extern int calc_sha1_1M(const char *filename, unsigned char *buf); + +int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size) { + int fd, err = -1; + struct stat sb; + + if (size < BUILD_ID_SIZE) + goto out; + + fd = open(filename, O_RDONLY); + + if (fd < 0) + goto out; + + if (fstat(fd, &sb) == 0) { + snprintf(bf, size, "%s[%lu]", filename, sb.st_size); + err = 0; + } + + close(fd); +out: + return err; +} + +static int is_function(const GElf_Sym *sym) +{ + return GELF_ST_TYPE(sym->st_info) == STT_FUNC && + sym->st_name != 0 && + sym->st_shndx != SHN_UNDEF; +} + +static int get_symbols_in_section(sym_section_ctx *sym, Elf *elf, Elf_Scn *sec, GElf_Shdr *shdr, int is_reloc) +{ + sym->syms = elf_getdata(sec, NULL); + if (!sym->syms) { + return -1; + } + + Elf_Scn *symstrs_sec = elf_getscn(elf, shdr->sh_link); + if (!sec) { + return -1; + } + + sym->symstrs = elf_getdata(symstrs_sec, NULL); + if (!sym->symstrs) { + return -1; + } + + sym->sym_count = shdr->sh_size / shdr->sh_entsize; + sym->is_plt = 0; + sym->is_reloc = is_reloc; + + return 0; +} + +static int get_plt_symbols_in_section(sym_section_ctx *sym, Elf *elf, plt_ctx *plt) +{ + sym->syms = elf_getdata(plt->dynsym.sec, NULL); + if (!sym->syms) { + return -1; + } + + sym->rel_data = elf_getdata(plt->plt_rel.sec, NULL); + if (!sym->rel_data) { + return -1; + } + + Elf_Scn *symstrs_sec = elf_getscn(elf, plt->dynsym.hdr->sh_link); + if (!symstrs_sec) { + return -1; + } + + sym->symstrs = elf_getdata(symstrs_sec, NULL); + if (!sym->symstrs) { + return -1; + } + + sym->is_plt = 1; + sym->plt_entsize = plt->plt.hdr->sh_type; + sym->plt_offset = plt->plt.hdr->sh_offset; + sym->sym_count = plt->plt_rel.hdr->sh_size / plt->plt_rel.hdr->sh_entsize; + sym->plt_rel_type = plt->plt_rel.hdr->sh_type; + + return 0; +} + +static void __get_plt_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) +{ + symbol s; + GElf_Sym sym; + int symidx; + int index = 0; + const char *sym_name = NULL; + + s.end = 0; + s.start = 0; + + if (!si->dynsymtab.syms) { + return; + } + + while (index < si->dynsymtab.sym_count) { + if (si->dynsymtab.plt_rel_type == SHT_RELA) { + GElf_Rela pos_mem, *pos; + pos = gelf_getrela(si->dynsymtab.rel_data, index, &pos_mem); + symidx = GELF_R_SYM(pos->r_info); + } + else if (si->dynsymtab.plt_rel_type == SHT_REL) { + GElf_Rel pos_mem, *pos; + pos = gelf_getrel(si->dynsymtab.rel_data, index, &pos_mem); + symidx = GELF_R_SYM(pos->r_info); + } + else { + return; + } + index++; + si->dynsymtab.plt_offset += si->dynsymtab.plt_entsize; + gelf_getsym(si->dynsymtab.syms, symidx, &sym); + + sym_name = (const char *)si->dynsymtab.symstrs->d_buf + sym.st_name; + s.start = si->dynsymtab.plt_offset; + s.end = s.start + si->dynsymtab.plt_entsize; + s.ip = s.start; + s.name = sym_name; + ss.insert(s); + } +} + +static void __get_symbol_without_plt(std::set &ss, sym_section_ctx *tab, Elf *elf) +{ + GElf_Sym sym; + int index = 0; + const char *sym_name; + symbol s; + s.end = 0; + s.start = 0; + + while (index < tab->sym_count) { + gelf_getsym(tab->syms, index, &sym); + index++; + if (sym.st_shndx == SHN_ABS) { + continue; + } + if (!is_function(&sym)) { + continue; + } + sym_name = (const char *)tab->symstrs->d_buf + sym.st_name; + if (tab->is_reloc) { + Elf_Scn *sec = elf_getscn(elf, sym.st_shndx); + if (!sec) { + continue; + } + GElf_Shdr shdr; + gelf_getshdr(sec, &shdr); + sym.st_value -= shdr.sh_addr - shdr.sh_offset; + } + s.start = sym.st_value & 0xffffffff; + s.end = s.start + sym.st_size; + s.ip = s.start; + s.name = sym_name; + ss.insert(s); + } +} + +static void __get_symbol(std::set &ss, symbol_sections_ctx *si, Elf *elf) +{ + symbol s; + s.end = 0; + s.start = 0; + + if (!si->symtab.syms && !si->dynsymtab.syms) { + return; + } + + sym_section_ctx *tab = &si->symtab; + __get_symbol_without_plt(ss, tab, elf); + tab = &si->symtab_in_dynsym; + __get_symbol_without_plt(ss, tab, elf); +} + +static void get_all_symbols(std::set &ss, symbol_sections_ctx *si, Elf *elf) +{ + __get_symbol(ss, si, elf); + __get_plt_symbol(ss, si, elf); +} + +bool search_symbol(const std::set &ss, symbol &sym) +{ + std::set::const_iterator it = ss.find(sym); + + if (it != ss.end()) { + sym.end = it->end; + sym.start = it->start; + sym.name = it->name; + + return true; + } + + return false; +} + +bool get_symbol_from_elf(std::set &ss, const char *path) +{ + static int first_init = 0; + + if (!first_init) { + first_init = true; + init_global_env(); + } + + int is_reloc = 0; + elf_version(EV_CURRENT); + int fd = open(path, O_RDONLY); + + Elf *elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) { + close(fd); + return false; + } + + Elf_Kind ek = elf_kind(elf); + if (ek != ELF_K_ELF) { + elf_end(elf); + close(fd); + return false; + } + GElf_Ehdr hdr; + if (gelf_getehdr(elf, &hdr) == NULL) { + elf_end(elf); + close(fd); + return false; + } + + if (hdr.e_type == ET_EXEC) { + is_reloc = 1; + } + + if (!elf_rawdata(elf_getscn(elf, hdr.e_shstrndx), NULL)) { + elf_end(elf); + close(fd); + return false; + } + + GElf_Shdr shdr; + GElf_Shdr symtab_shdr; + GElf_Shdr dynsym_shdr; + GElf_Shdr plt_shdr; + GElf_Shdr plt_rel_shdr; + memset(&shdr, 0, sizeof(shdr)); + memset(&symtab_shdr, 0, sizeof(symtab_shdr)); + memset(&dynsym_shdr, 0, sizeof(dynsym_shdr)); + memset(&plt_shdr, 0, sizeof(plt_shdr)); + memset(&plt_rel_shdr, 0, sizeof(plt_rel_shdr)); + + Elf_Scn *sec = NULL; + Elf_Scn *dynsym_sec = NULL; + Elf_Scn *symtab_sec = NULL; + Elf_Scn *plt_sec = NULL; + Elf_Scn *plt_rel_sec = NULL; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + gelf_getshdr(sec, &shdr); + str = elf_strptr(elf, hdr.e_shstrndx, shdr.sh_name); + + if (str && strcmp(".symtab", str) == 0) { + symtab_sec = sec; + memcpy(&symtab_shdr, &shdr, sizeof(dynsym_shdr)); + } + if (str && strcmp(".dynsym", str) == 0) { + dynsym_sec = sec; + memcpy(&dynsym_shdr, &shdr, sizeof(dynsym_shdr)); + } + if (str && strcmp(".rela.plt", str) == 0) { + plt_rel_sec = sec; + memcpy(&plt_rel_shdr, &shdr, sizeof(plt_rel_shdr)); + } + if (str && strcmp(".plt", str) == 0) { + plt_sec = sec; + memcpy(&plt_shdr, &shdr, sizeof(plt_shdr)); + } + if (str && strcmp(".gnu.prelink_undo", str) == 0) { + is_reloc = 1; + } + } + + plt_ctx plt; + plt.dynsym.hdr = &dynsym_shdr; + plt.dynsym.sec = dynsym_sec; + plt.plt.hdr = &plt_shdr; + plt.plt.sec = plt_sec; + plt.plt_rel.hdr = &plt_rel_shdr; + plt.plt_rel.sec = plt_rel_sec; + + symbol_sections_ctx si; + memset(&si, 0, sizeof(si)); + if (symtab_sec) { + get_symbols_in_section(&si.symtab, elf, symtab_sec, &symtab_shdr, is_reloc); + } + if (dynsym_sec) { + get_symbols_in_section(&si.symtab_in_dynsym, elf, dynsym_sec, &dynsym_shdr, is_reloc); + } + if (dynsym_sec && plt_sec) { + get_plt_symbols_in_section(&si.dynsymtab, elf, &plt); + } + + get_all_symbols(ss, &si, elf); + elf_end(elf); + close(fd); + return true; +} + +struct symbol_cache_item { + int start; + int size; + char name[0]; +}; + +bool save_symbol_cache(std::set &ss, const char *path) +{ + char buf[2048]; + int len = 0; + bool status = true; + + int fd = open(path, O_RDONLY); + if (fd < 0) { + status = false; + return status; + } + int ret; + ret = read(fd, &len, 4); + if (ret <= 0) { + close(fd); + status = false; + return status; + } + ret = read(fd, buf, len); + if (ret <= 0) { + close(fd); + status = false; + return status; + } + + while (1) { + struct symbol_cache_item *sym; + symbol s; + ret = read(fd, &len, 4); + if (ret <= 0) { + status = false; + break; + } + ret = read(fd, buf, len); + if (ret < len) { + status = false; + break; + } + sym = (struct symbol_cache_item *)buf; + s.start = sym->start; + s.end = sym->start + sym->size; + s.ip = sym->start; + s.name = sym->name; + ss.insert(s); + } + close(fd); + return status; +} + +bool load_symbol_cache(std::set &ss, const char *path, const char *filename) +{ + int fd = open(path, O_RDWR | O_EXCL); + if (fd < 0) { + return false; + } + int len = strlen(filename); + int ret = write(fd, &len, 4); + if (ret < 0) { + close(fd); + return false; + } + ret = write(fd, filename, len); + if (ret < 0) { + close(fd); + return false; + } + + std::set::iterator it; + int v; + for (it = ss.begin(); it != ss.end(); ++it) { + v = it->start; + ret = write(fd, &v, 4); + v = it->end - it->start; + ret = write(fd, &v, 4); + ret = write(fd, it->name.c_str(), it->name.length()); + } + return true; +} diff --git a/source/ucli_py/unwind/elf.h b/source/ucli_py/unwind/elf.h new file mode 100644 index 0000000..7e02c0e --- /dev/null +++ b/source/ucli_py/unwind/elf.h @@ -0,0 +1,25 @@ +/* + * Linux内核诊断工具--elf相关函数头文件 + * + * Copyright (C) 2020 Alibaba Ltd. + * + * License terms: GNU General Public License (GPL) version 3 + * + */ + +#ifndef _PERF_ELF_H__ +#define _PERF_ELF_H__ + +#include +#include + +#include "symbol.h" + +#define BUILD_ID_SIZE 40 +bool save_symbol_cache(std::set &ss, const char *path); +bool load_symbol_cache(std::set &ss, const char *path, const char *filename); + +bool get_symbol_from_elf(std::set &ss, const char *path); +bool search_symbol(const std::set &ss, symbol &sym); +int filename__read_build_id(int pid, const char *mnt_ns_name, const char *filename, char *bf, size_t size); +#endif diff --git a/source/ucli_py/unwind/internal.h b/source/ucli_py/unwind/internal.h new file mode 100644 index 0000000..9139e1e --- /dev/null +++ b/source/ucli_py/unwind/internal.h @@ -0,0 +1,189 @@ +// /* +// * Linux内核诊断工具--杂项定义头文件 +// * +// * Copyright (C) 2020 Alibaba Ltd. +// * +// * 作者: Baoyou Xie +// * +// * License terms: GNU General Public License (GPL) version 3 +// * +// */ + +// #include +// #include +// #include "uapi/ali_diagnose.h" +// #include "json/json.h" + +// #include +// #include +// #include + +// #include "debug.h" + +// extern std::set g_proc_map; + +// int run_trace_main(int argc, char **argv); +// int sys_delay_main(int argc, char **argv); +// int sched_delay_main(int argc, char **argv); +// int throttle_delay_main(int argc, char **argv); +// int load_monitor_main(int argc, char **argv); +// int exit_monitor_main(int argc, char **argv); +// int utilization_main(int argc, char **argv); +// int perf_main(int argc, char **argv); +// int tcp_retrans_main(int argc, char **argv); +// int tcp_connect_main(int argc, char **argv); +// int rw_top_main(int argc, char **argv); +// int irq_delay_main(int argc, char **argv); +// int mutex_monitor_main(int argc, char **argv); +// int alloc_top_main(int argc, char **argv); +// int alloc_load_main(int argc, char **argv); +// int drop_packet_main(int argc, char **argv); +// int fs_orphan_main(int argc, char **argv); +// int df_du_main(int argc, char **argv); +// int exec_monitor_main(int argc, char **argv); +// int fs_shm_main(int argc, char **argv); +// int irq_stats_main(int argc, char **argv); +// int irq_trace_main(int argc, char **argv); +// int kprobe_main(int argc, char **argv); +// int mm_leak_main(int argc, char **argv); +// int proc_monitor_main(int argc, char **argv); +// int runq_info_main(int argc, char **argv); +// int reboot_main(int argc, char **argv); +// int pi_main(int argc, char *argv[]); +// int memcpy_main(int argc, char* argv[]); +// int md5_main(int argc, char *argv[]); +// int net_bandwidth_main(int argc, char *argv[]); +// int sig_info_main(int argc, char *argv[]); +// int task_monitor_main(int argc, char **argv); +// int rw_sem_main(int argc, char **argv); +// int rss_monitor_main(int argc, char **argv); + +// void usage_run_trace(void); +// void usage_sys_delay(void); +// void usage_load_monitor(void); +// void usage_exit_monitor(void); +// void usage_utilization(void); +// void usage_perf(); +// void usage_tcp_retrans(); +// void usage_rw_top(); +// void usage_irq_delay(); +// void usage_mutex_monitor(); +// void usage_alloc_top(); +// void usage_drop_packet(); +// void usage_fs_orphan(); +// void usage_exec_monitor(); +// void usage_fs_shm(); +// void usage_irq_stats(); +// void usage_irq_trace(); +// void usage_kprobe(); +// void usage_mm_leak(); +// void usage_testcase(void); +// void usage_pupil(void); +// void usage_sched_delay(void); +// void usage_reboot(void); +// void usage_test_memcpy(void); +// void usage_test_pi(void); +// void usage_test_md5(void); +// void usage_net_bandwidth(void); +// void usage_sig_info(void); +// void usage_task_monitor(void); +// void usage_rw_sem(void); +// void usage_rss_monitor(void); +// void usage_throttle_delay(void); + +// int uprobe_main(int argc, char **argv); +// void usage_uprobe(); + +// int ping_delay_main(int argc, char *argv[]); +// void usage_ping_delay(void); +// int ping_delay6_main(int argc, char *argv[]); +// void usage_ping_delay6(void); + +// int test_run_trace_main(int argc, char *argv[]); +// void usage_test_run_trace(void); + +// int memcg_stats_main(int argc, char *argv[]); +// void usage_memcg_stats(void); + +// int diag_activate(const char func[]); +// int diag_deactivate(const char func[]); + +// void diag_printf_inode(struct diag_inode_detail *inode); +// void diag_printf_time(struct diag_timespec *tv); +// void diag_printf_task(struct diag_task_detail *task); +// void diag_printf_proc_chains(struct diag_proc_chains_detail *proc_chains); +// void diag_printf_proc_chains(struct diag_proc_chains_detail *proc_chains, int reverse); +// void diag_printf_proc_chains(struct diag_proc_chains_detail *proc_chains, int reverse, int detail); +// void diag_printf_kern_stack(struct diag_kern_stack_detail *kern_stack); +// void diag_printf_kern_stack(struct diag_kern_stack_detail *kern_stack, int reverse); +// void diag_printf_user_stack(int pid, int ns_pid, const char *comm, +// struct diag_user_stack_detail *user_stack); +// void diag_printf_user_stack(int pid, int ns_pid, const char *comm, +// struct diag_user_stack_detail *user_stack, int attach); +// void diag_printf_user_stack(int pid, int ns_pid, const char *comm, +// struct diag_user_stack_detail *user_stack, int attach, int reverse); +// void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, +// struct diag_raw_stack_detail *raw_stack); +// void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, +// struct diag_raw_stack_detail *raw_stack, int attach); +// void init_java_env(const char *agent, int pid, int ns_pid, const char *comm, std::set &); +// void diag_unwind_raw_stack(int pid, int ns_pid, +// struct diag_raw_stack_detail *raw_stack, unsigned long stack[BACKTRACE_DEPTH]); + +// void diag_sls_time(struct diag_timespec *tv, Json::Value &owner); +// void diag_sls_task(struct diag_task_detail *tsk_info, Json::Value &task); +// void diag_sls_proc_chains(struct diag_proc_chains_detail *proc_chains, Json::Value &task); +// void diag_sls_kern_stack(struct diag_kern_stack_detail *kern_stack, Json::Value &task); +// void diag_sls_user_stack(pid_t pid, pid_t ns_pid, const char *comm, +// struct diag_user_stack_detail *user_stack, Json::Value &task); +// void diag_sls_user_stack(pid_t pid, pid_t ns_pid, const char *comm, +// struct diag_user_stack_detail *user_stack, Json::Value &task, int attach); +// void diag_sls_inode(struct diag_inode_detail *inode, Json::Value &root); +// int log_config(char *arg, char *sls_file, int *p_syslog_enabled); +// void write_syslog(int enabled, const char mod[], struct diag_timespec *tv, unsigned long id, int seq, Json::Value &root); +// void write_file(char *sls_file, const char mod[], struct diag_timespec *tv, unsigned long id, int seq, Json::Value &root); +// void diag_ip_addr_to_str(unsigned char *ip_addr,const char type[], Json::Value &root); +// #define ULONG_MAX (~0UL) +// #define STACK_IS_END(v) ((v) == 0 || (v) == ULONG_MAX) + +// class pid_cmdline { +// private: +// std::map cmdlines; +// public: +// void clear(void); +// std::string & get_pid_cmdline(int pid); +// }; + +// int jmaps_main(int argc, char **argv); +// void restore_global_env(); +// int attach_ns_env(int pid); +// int java_attach_once(int flag_no_attach = 0); + +// extern class pid_cmdline pid_cmdline; + +// extern void clear_symbol_info(class pid_cmdline &pid_cmdline, std::set &procs, int dist); +// extern unsigned int ipstr2int(const char *ipstr); +// extern char *int2ipstr(const unsigned int ip, char *ipstr, const unsigned int ip_str_len); + +// extern int is_linux_2_6_x(void); +// extern int linux_2_6_x; + +// int sys_cost_main(int argc, char **argv); +// void usage_sys_cost(); + +// int fs_cache_main(int argc, char *argv[]); +// void usage_fs_cache(void); + +// int high_order_main(int argc, char *argv[]); +// void usage_high_order(void); + +// int pmu_main(int argc, char **argv); +// void usage_pmu(void); + +// int testcase_main(int argc, char *argv[]); + +// struct timeval; +// struct timezone; +// extern "C" { +// void diag_gettimeofday(struct diag_timespec *tv, struct timezone *tz); +// } diff --git a/source/ucli_py/unwind/symbol.cc b/source/ucli_py/unwind/symbol.cc new file mode 100644 index 0000000..dac1680 --- /dev/null +++ b/source/ucli_py/unwind/symbol.cc @@ -0,0 +1,504 @@ +/* + * Linux内核诊断工具--用户态符号表解析 + * + * Copyright (C) 2020 Alibaba Ltd. + * + * License terms: GNU General Public License (GPL) version 3 + * + */ + +#include +#include +#include + +#include "symbol.h" +#include "elf.h" +#include "internal.h" + +void restore_global_env(); +int attach_ns_env(int pid); + +symbol_parser g_symbol_parser; + +bool symbol_parser::add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name) +{ + std::map::iterator it; + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + proc_vma proc; + machine_vma.insert(make_pair(pid, proc)); + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + return false; + } + } + + vma vm(start, end, offset, name); + it->second.insert(std::make_pair(vm.start, std::move(vm))); + + return true; +} + +bool symbol_parser::load_pid_maps(int pid) +{ + std::map::iterator it; + it = machine_vma.find(pid); + if (it != machine_vma.end()) { + return true; + } + + proc_vma proc; + char fn[256]; + sprintf(fn, "/proc/%d/maps", pid); + FILE *fp = fopen(fn, "r"); + if (!fp) { + return false; + } + + char buf[4096]; + char exename[4096]; + size_t start, end, offset; + while (fgets(buf, sizeof(buf), fp) != NULL) { + start = end = offset = 0; + exename[0] = '\0'; + sscanf(buf, "%lx-%lx %*s %lx %*x:%*x %*u %s %*s\n", &start, &end, &offset, exename); + if (exename[0] == '\0') { + strcpy(exename, "[anon]"); + } + vma vm(start, end, offset, exename); + proc.insert(std::make_pair(vm.start, std::move(vm))); + } + + fclose(fp); + + machine_vma.insert(std::make_pair(pid, std::move(proc))); + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + return false; + } + + return true; +} + +bool symbol_parser::load_perf_map(int pid, int pid_ns) +{ +#if 0 + if (pid != pid_ns) { + if (attach_ns_env(pid) < 0) { + return false; + } + } +#endif + char perfmapfile[64]; + snprintf(perfmapfile, sizeof(perfmapfile), "/tmp/perf-%d.map", pid); + FILE *fp = fopen(perfmapfile, "r"); + if (fp == NULL) { + return false; + } + char line[256]; + char *buf; + long start; + int size; + char name[256]; + std::set syms; + symbol sym; + while ((buf = fgets(line, sizeof(line), fp)) != NULL) { + sscanf(buf, "%lx %x %s\n", &start, &size, name); + sym.start = start; + sym.end = sym.start + size; + sym.ip = sym.start; + sym.name = name; + syms.insert(sym); + } + java_symbols.insert(make_pair(pid, std::move(syms))); +#if 0 + if (pid != pid_ns) { + restore_global_env(); + } +#endif + return true; +} + +bool symbol_parser::find_java_symbol(symbol &sym, int pid, int pid_ns) +{ + std::set ss; + std::map >::iterator it; + //bool load_now = false; + it = java_symbols.find(pid); + if (it == java_symbols.end()) { + if (!load_perf_map(pid, pid_ns)) { + return false; + } + //load_now = true; + it = java_symbols.find(pid); + return search_symbol(it->second, sym); + } else { + return search_symbol(it->second, sym); + } + return true; + + //bool ret = search_symbol(syms, sym); +#if 0 + if (!ret && !load_now) { + java_symbols.erase(pid); + if (!load_perf_map(pid)) { + return false; + } + syms = java_symbols.find(pid)->second; + return search_symbol(syms, sym); + } +#endif + //return ret; +} + +static bool load_kernel_symbol_list(std::vector &sym_list) +{ + FILE *fp = fopen("/proc/kallsyms", "r"); + if (!fp) { + return -1; + } + + char buf[256]; + char type; + int len; + while (fgets(buf, sizeof(buf), fp) != NULL) { + sscanf(buf, "%*p %c %*s\n", &type); + if ((type | 0x20) != 't') { + continue; + } + len = strlen(buf); + if (buf[len-1] == '\n') { + buf[len-1] = ' '; + } + sym_list.push_back(buf); + } + fclose(fp); + + std::sort(sym_list.begin(), sym_list.end()); + return true; +} + +bool is_space(int ch) { + return std::isspace(ch); +} + +static inline void rtrim(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), is_space).base(), s.end()); +} + +static bool get_next_kernel_symbol( + std::set &syms, + std::vector &sym_list, + std::vector::iterator cursor) +{ + if (cursor == sym_list.end()) { + return false; + } + symbol sym; + size_t start, end; + sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&start); + sym.name = cursor->c_str() + 19; + rtrim(sym.name); +#if 0 + if (sym.name[sym.name.size()-1] == '\n') { + sym.name[sym.name.size()-1] = '\0'; + } +#endif + cursor++; + if (cursor != sym_list.end()) { + sscanf(cursor->c_str(), "%p %*c %*s\n", (void **)&end); + } + else { + end = INVALID_ADDR; + } + sym.start = start; + sym.end = end; + sym.ip = start; + + syms.insert(sym); + return true; +} + +bool symbol_parser::load_kernel() +{ + if (kernel_symbols.size() != 0) { + return true; + } + + std::vector sym_list; + if (!load_kernel_symbol_list(sym_list)) { + exit(0); + return false; + } + + std::vector::iterator cursor = sym_list.begin(); + while (get_next_kernel_symbol(kernel_symbols, sym_list, cursor)) { + cursor++; + } + return true; +} + +bool symbol_parser::load_elf(pid_t pid, const elf_file &file) +{ + std::map >::iterator it; + it = file_symbols.find(file); + std::set tmp; + std::set &syms = tmp; + if (it != file_symbols.end()) { + return true; + } + if (get_symbol_from_elf(syms, file.filename.c_str())) { + file_symbols.insert(make_pair(file, std::move(syms))); + return true; + } + return false; +} + +bool symbol_parser::find_kernel_symbol(symbol &sym) +{ + load_kernel(); + sym.end = sym.start = 0; + std::set::iterator it = kernel_symbols.find(sym); + if (it != kernel_symbols.end()) { + sym.end = it->end; + sym.start = it->start; + sym.name = it->name; + return true; + } + return false; +} + +bool symbol_parser::find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol) +{ + std::map >::const_iterator it_pid = + symbols_cache.find(tgid); + + if (it_pid != symbols_cache.end()) { + std::map map = symbols_cache[tgid]; + std::map::const_iterator it_symbol = + map.find(addr); + + if (it_symbol != map.end()) { + symbol = map[addr]; + + return true; + } + } + + return false; +} + +bool symbol_parser::putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol) +{ + std::map >::const_iterator it_pid = + symbols_cache.find(tgid); + + if (it_pid == symbols_cache.end()) { + std::map map; + symbols_cache.insert(std::make_pair(tgid, map)); + } + + std::map &map = symbols_cache[tgid]; + std::map::const_iterator it_symbol = + map.find(addr); + + if (it_symbol == map.end()) { + map[addr] = symbol; + return true; + } + + return false; +} + +bool symbol_parser::get_symbol_info(int pid, symbol &sym, elf_file &file) +{ + std::map::iterator proc_vma_info; + + if (java_only) { + file.type = UNKNOWN; + return true; + } + + proc_vma_info = machine_vma.find(pid); + if (proc_vma_info == machine_vma.end()) { + if (!load_pid_maps(pid)) { + return false; + } + } + + vma area(sym.ip); + if (!find_vma(pid, area)) { + return false; + } + if (area.name == "[anon]") { + file.type = JIT_TYPE; + } + + file.reset(area.name); + if (file.type != JIT_TYPE) { + sym.reset(area.map(sym.ip)); + } + + return true; +} + +bool symbol_parser::find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns) +{ + if (java_only) { + return find_java_symbol(sym, pid, pid_ns); + } + + if (file.type == JIT_TYPE) { + return find_java_symbol(sym, pid, pid_ns); + } + + std::map >::iterator it; + it = file_symbols.find(file); + std::set ss; + if (it == file_symbols.end()) { + if (!load_elf(pid, file)) { + return false; + } + it = file_symbols.find(file); + return search_symbol(it->second, sym); + } else { + return search_symbol(it->second, sym); + } + return true; +} + +vma* symbol_parser::find_vma(pid_t pid, size_t pc) +{ + std::map::iterator it; + + it = machine_vma.find(pid); + if (it == machine_vma.end()) { + return NULL; + } + + proc_vma::iterator vma_iter = it->second.upper_bound(pc); + if (vma_iter == it->second.end() || vma_iter->second.end < pc) { + return NULL; + } + + if (vma_iter != it->second.begin()) { + --vma_iter; + } + + return &vma_iter->second; +} + +bool symbol_parser::find_vma(pid_t pid, vma &vm) +{ + std::map::iterator proc_vma_map; + + proc_vma_map = machine_vma.find(pid); + if (proc_vma_map == machine_vma.end()) { + return false; + } + + proc_vma::const_iterator vma_iter = proc_vma_map->second.upper_bound(vm.pc); + if (vma_iter == proc_vma_map->second.end()) { + return false; + } + if (vma_iter->second.end < vm.pc) { + return false; + } + + if (vma_iter != proc_vma_map->second.begin()) { + --vma_iter; + } + + vm.start = vma_iter->second.start; + vm.end = vma_iter->second.end; + vm.name = vma_iter->second.name; + vm.offset = vma_iter->second.offset; + + return true; +} + +void clear_symbol_info(class pid_cmdline &pid_cmdline, std::set &procs, int dist) +{ + pid_cmdline.clear(); + procs.clear(); + g_symbol_parser.clear_symbol_info(dist); +} + +void symbol_parser::clear_symbol_info(int dist) +{ + machine_vma.clear(); + java_symbols.clear(); + if (dist) { + kernel_symbols.clear(); + file_symbols.clear(); + } +} + +void symbol_parser::dump(void) +{ + int count1, count2, count3; + { + count1 = 0; + count2 = 0; + count3 = 0; + std::map >::iterator iter = file_symbols.begin(); + for(; iter != file_symbols.end(); ++iter) { + std::set& map = iter->second; + const elf_file& file = iter->first; + + count1++; + printf("xby-debug, file_symbols: %s, %lu\n", + file.filename.c_str(), + map.size()); + + count2 += map.size(); + std::set::iterator it = map.begin(); + for(; it != map.end(); ++it) { + count3 += it->name.length(); + } + } + printf("xby-debug, file_symbols: %d, %d, %d\n", count1, count2, count3); + printf("xby-debug, sizeof(symbol): %ld\n", sizeof(symbol)); + } + + { + count1 = 0; + count2 = 0; + std::map >::iterator iter = java_symbols.begin(); + for(; iter != java_symbols.end(); ++iter) { + count1++; + std::set& map = iter->second; + count2 += map.size(); + } + printf("xby-debug, java_symbols: %d, %d\n", count1, count2); + } + + { + printf("xby-debug, kernel_symbols: %lu\n", kernel_symbols.size()); + } + + { + count1 = 0; + count2 = 0; + std::map::iterator iter = machine_vma.begin(); + for(; iter != machine_vma.end(); ++iter) { + count1++; + proc_vma map = iter->second; + count2 += map.size(); + } + printf("xby-debug, machine_vma: %d, %d\n", count1, count2); + } + + { + count1 = 0; + count2 = 0; + std::map >::iterator iter = symbols_cache.begin(); + for(; iter != symbols_cache.end(); ++iter) { + count1++; + std::map& map = iter->second; + count2 += map.size(); + } + printf("xby-debug, symbols_cache: %d, %d\n", count1, count2); + } +} diff --git a/source/ucli_py/unwind/symbol.h b/source/ucli_py/unwind/symbol.h new file mode 100644 index 0000000..52a3855 --- /dev/null +++ b/source/ucli_py/unwind/symbol.h @@ -0,0 +1,164 @@ +/* + * Linux内核诊断工具--用户态符号表解析 + * + * Copyright (C) 2020 Alibaba Ltd. + * + * License terms: GNU General Public License (GPL) version 3 + * + */ + +#ifndef __PERF_SYMBOL_H__ +#define __PERF_SYMBOL_H__ + +#include +#include +#include + +//#include + +#define INVALID_ADDR ((size_t)(-1)) +enum { + NATIVE_TYPE = 0, + JIT_TYPE = 1, + UNKNOWN = 2, +}; + +struct elf_file { + unsigned char elf_read_error; + size_t eh_frame_hdr_offset; + size_t fde_count; + size_t table_data; + std::string filename; + int type; + + // TODO get builid from elf header or build hash for elf + elf_file(const std::string &name) : filename(name), type(NATIVE_TYPE) { + elf_read_error = 0; + eh_frame_hdr_offset = 0; + fde_count = 0; + table_data = 0; + } + + elf_file() :type(NATIVE_TYPE) {} + + // TODO get builid from elf header or build hash for elf + void reset(const std::string &name) { + filename = name; + elf_read_error = 0; + eh_frame_hdr_offset = 0; + fde_count = 0; + table_data = 0; + } + + bool operator< (const elf_file &rhs) const { + return filename < rhs.filename; + } +}; + +struct symbol { + size_t start; + size_t end; + size_t ip; + std::string name; + + symbol() :start(0), end(0), ip(0) {} + symbol(size_t pc) :start(0), end(0), ip(pc) {} + + void reset(size_t va) { start = end = 0; ip = va; } + bool operator< (const symbol &sym) const { + return sym.ip < start; + } + + bool operator> (const symbol &sym) const { + return sym.ip > end; + } +}; + +struct vma { + size_t start; + size_t end; + size_t offset; + size_t pc; + int type; + std::string name; + struct { + unsigned char elf_read_error; + size_t eh_frame_hdr_offset; + size_t fde_count; + size_t table_data; + }; + + size_t map(size_t pc) { + return pc - start + offset; + } + + void set_type(int t) { type = t; } + + vma(size_t s, size_t e, size_t o, const std::string &n) + :start(s), end(e), offset(o), pc(0), type(NATIVE_TYPE), name(n) {} + + vma() : start(0), end(0), offset(0), pc(0), type(NATIVE_TYPE) {} + + vma(size_t addr) : start(0), end(0), offset(0), pc(addr), type(NATIVE_TYPE) {} + + bool operator<(const vma &vm) { + return vm.start < vm.pc; + } + + vma &operator=(const vma &vm) { + if (this == &vm) { + return *this; + } + start = vm.start; + end = vm.end; + offset = vm.offset; + name = vm.name; + return *this; + } +}; + +static inline bool operator==(const vma &lhs, const vma &rhs) { + return lhs.start == rhs.start && lhs.end == rhs.end && lhs.name == rhs.name; +} + +class symbol_parser { +private: + typedef std::map proc_vma; + + std::map > file_symbols; + std::map > java_symbols; + std::set kernel_symbols; + std::map machine_vma; + std::set java_procs; + std::map > symbols_cache; +public: + bool load_kernel(); + std::set& get_java_procs() { return java_procs; } + + bool find_kernel_symbol(symbol &sym); + bool find_elf_symbol(symbol &sym, const elf_file &file, int pid, int pid_ns); + bool find_java_symbol(symbol &sym, int pid, int pid_ns); + + bool get_symbol_info(int pid, symbol &sym, elf_file &file); + + bool find_vma(pid_t pid, vma &vm); + vma* find_vma(pid_t pid, size_t pc); + void clear_symbol_info(int); + bool add_pid_maps(int pid, size_t start, size_t end, size_t offset, const char *name); + + bool find_symbol_in_cache(int tgid, unsigned long addr, std::string &symbol); + bool putin_symbol_cache(int tgid, unsigned long addr, std::string &symbol); + + void dump(void); +private: + bool load_pid_maps(int pid); + bool load_elf(pid_t pid, const elf_file& file); + bool load_perf_map(int pid, int pid_ns); +public: + int java_only; + int user_symbol; +}; + +extern symbol_parser g_symbol_parser; + +#endif diff --git a/source/ucli_py/unwind/unwind.cc b/source/ucli_py/unwind/unwind.cc new file mode 100644 index 0000000..85aa4b5 --- /dev/null +++ b/source/ucli_py/unwind/unwind.cc @@ -0,0 +1,684 @@ +/* + * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps. + * + * Lots of this code have been borrowed or heavily inspired from parts of + * the libunwind 0.99 code which are (amongst other contributors I may have + * forgotten): + * + * Copyright (C) 2002-2007 Hewlett-Packard Co + * Contributed by David Mosberger-Tang + * + * And the bugs have been added by: + * + * Copyright (C) 2010, Frederic Weisbecker + * Copyright (C) 2012, Jiri Olsa + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +//#include +#include +#include +#include +#include + + +#include "unwind.h" +#include "symbol.h" + + +extern "C" { +int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, + unw_word_t ip, + unw_dyn_info_t *di, + unw_proc_info_t *pi, + int need_unwind_info, void *arg); +} + +#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table) + +#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */ +#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */ + +/* Pointer-encoding formats: */ +#define DW_EH_PE_omit 0xff +#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */ +#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */ +#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */ +#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */ +#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */ + +/* Pointer-encoding application: */ +#define DW_EH_PE_absptr 0x00 /* absolute value */ +#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */ + +/* + * The following are not documented by LSB v1.3, yet they are used by + * GCC, presumably they aren't documented by LSB since they aren't + * used on Linux: + */ +#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */ +#define DW_EH_PE_aligned 0x50 /* aligned pointer */ + +/* Flags intentionaly not handled, since they're not needed: + * #define DW_EH_PE_indirect 0x80 + * #define DW_EH_PE_uleb128 0x01 + * #define DW_EH_PE_udata2 0x02 + * #define DW_EH_PE_sleb128 0x09 + * #define DW_EH_PE_sdata2 0x0a + * #define DW_EH_PE_textrel 0x20 + * #define DW_EH_PE_datarel 0x30 + */ + + +struct unwind_info { + struct perf_sample *sample; + int pid; + int pid_ns; + symbol_parser *sp; +}; + +#define dw_read(ptr, type, end) ({ \ + type *__p = (type *) ptr; \ + type __v; \ + if ((__p + 1) > (type *) end) \ + return -EINVAL; \ + __v = *__p++; \ + ptr = (typeof(ptr)) __p; \ + __v; \ + }) + +#ifdef __x86_64__ +int unwind__arch_reg_id(int regnum) +{ + int id; + + switch (regnum) { + case UNW_X86_64_RBP: + id = PERF_REG_BP; + break; + case UNW_X86_64_RSP: + id = PERF_REG_SP; + break; + case UNW_X86_64_RIP: + id = PERF_REG_IP; + break; + default: + return -EINVAL; + } + + return id; +} +#else +int unwind__arch_reg_id(int regnum) +{ + int id; + switch (regnum) { + case UNW_AARCH64_SP: + id = PERF_REG_SP; + break; + case UNW_AARCH64_PC: + id = PERF_REG_IP; + break; + default: + return -EINVAL; + } + + return id; +} +#endif + +static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, + u8 encoding) +{ + u8 *cur = *p; + *val = 0; + + switch (encoding) { + case DW_EH_PE_omit: + *val = 0; + goto out; + case DW_EH_PE_ptr: + *val = dw_read(cur, unsigned long, end); + goto out; + default: + break; + } + + switch (encoding & DW_EH_PE_APPL_MASK) { + case DW_EH_PE_absptr: + break; + case DW_EH_PE_pcrel: + *val = (unsigned long) cur; + break; + default: + return -EINVAL; + } + + if ((encoding & 0x07) == 0x00) + encoding |= DW_EH_PE_udata4; + + switch (encoding & DW_EH_PE_FORMAT_MASK) { + case DW_EH_PE_sdata4: + *val += dw_read(cur, s32, end); + break; + case DW_EH_PE_udata4: + *val += dw_read(cur, u32, end); + break; + case DW_EH_PE_sdata8: + *val += dw_read(cur, s64, end); + break; + case DW_EH_PE_udata8: + *val += dw_read(cur, u64, end); + break; + default: + return -EINVAL; + } + + out: + *p = cur; + return 0; +} + +#define dw_read_encoded_value(ptr, end, enc) ({ \ + u64 __v; \ + if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \ + return -EINVAL; \ + } \ + __v; \ + }) + +static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name) +{ + Elf_Scn *sec = NULL; + + while ((sec = elf_nextscn(elf, sec)) != NULL) { + char *str; + + gelf_getshdr(sec, shp); + str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + if (!strcmp(name, str)) + break; + } + + return sec; +} + +static u64 elf_section_offset(int fd, const char *name) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + u64 offset = 0; + + elf = elf_begin(fd, ELF_C_READ_MMAP, NULL); + if (elf == NULL) + return 0; + + do { + if (gelf_getehdr(elf, &ehdr) == NULL) + break; + + if (!elf_section_by_name(elf, &ehdr, &shdr, name)) + break; + + offset = shdr.sh_offset; + } while (0); + + elf_end(elf); + return offset; +} + +struct table_entry { + u32 start_ip_offset; + u32 fde_offset; +}; + +struct eh_frame_hdr { + unsigned char version; + unsigned char eh_frame_ptr_enc; + unsigned char fde_count_enc; + unsigned char table_enc; + + /* + * The rest of the header is variable-length and consists of the + * following members: + * + * encoded_t eh_frame_ptr; + * encoded_t fde_count; + */ + + /* A single encoded pointer should not be more than 8 bytes. */ + u64 enc[2]; + + /* + * struct { + * encoded_t start_ip; + * encoded_t fde_addr; + * } binary_search_table[fde_count]; + */ + char data[0]; +} __attribute__((__packed__)); + +int dso_data_fd(vma* dso) +{ + return open(dso->name.c_str(), O_RDONLY); +} + +ssize_t dso_read(vma *dso, u64 offset, u8 *data, ssize_t size) +{ + ssize_t ret = -1; + int fd; + + fd = dso_data_fd(dso); + if (fd < 0) + return -1; + + do { + if (-1 == lseek(fd, offset, SEEK_SET)) + break; + + ret = read(fd, data, size); + if (ret <= 0) + break; + } while (0); + + close(fd); + return ret; +} + +ssize_t dso__data_read_offset(vma *dso, u64 offset, u8 *data, ssize_t size) +{ + ssize_t r = 0; + u8 *p = data; + + do { + ssize_t ret; + ret = dso_read(dso, offset, p, size); + if (ret <= 0) { + return -1; + } + if (ret > size) { + return -1; + } + r += ret; + p += ret; + offset += ret; + size -= ret; + } while (size); + return r; +} + +ssize_t dso__data_read_addr(vma *map, + u64 addr, u8 *data, ssize_t size) +{ + u64 offset; + + if (map->name.size() > 0 && map->name[0] != '/') + return 0; + + offset = addr - map->start + map->offset; + return dso__data_read_offset(map, offset, data, size); +} + + +static int unwind_spec_ehframe(vma *dso, + u64 offset, u64 *table_data, u64 *segbase, + u64 *fde_count) +{ + struct eh_frame_hdr hdr; + u8 *enc = (u8 *) &hdr.enc; + u8 *end = (u8 *) &hdr.data; + ssize_t r; + + r = dso__data_read_offset(dso, offset, (u8 *) &hdr, sizeof(hdr)); + if (r != sizeof(hdr)) { + return -EINVAL; + } + + /* We dont need eh_frame_ptr, just skip it. */ + dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc); + + *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc); + *segbase = offset; + *table_data = (enc - (u8 *) &hdr) + offset; + + return 0; +} + +static int read_unwind_spec(vma* dso, u64 *table_data, u64 *segbase, u64 *fde_count) +{ + int ret = -EINVAL, fd; + + if (dso->eh_frame_hdr_offset == 0 && dso->elf_read_error == 0) { + fd = dso_data_fd(dso); + if (fd < 0) + return -EINVAL; + + dso->eh_frame_hdr_offset = elf_section_offset(fd, ".eh_frame_hdr"); + close(fd); + ret = unwind_spec_ehframe(dso, dso->eh_frame_hdr_offset, + &dso->table_data, &dso->eh_frame_hdr_offset, + &dso->fde_count); + if (ret != 0) { + dso->eh_frame_hdr_offset = 0; + dso->elf_read_error = 1; + return -EINVAL; + } + } + + *table_data = dso->table_data; + *segbase = dso->eh_frame_hdr_offset; + *fde_count = dso->fde_count; + + /* TODO .debug_frame check if eh_frame_hdr fails */ + return 0; +} + +static vma* find_map(unw_word_t ip, struct unwind_info *ui) +{ + return ui->sp->find_vma(ui->pid, ip); +} + +static int +find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, + int need_unwind_info, void *arg) +{ + struct unwind_info *ui = (struct unwind_info *)arg; + unw_dyn_info_t di; + u64 table_data, segbase, fde_count; + + vma* map; + map = find_map(ip, ui); + if (!map) { + return -EINVAL; + } + + if (!read_unwind_spec(map, &table_data, &segbase, &fde_count)) { + memset(&di, 0, sizeof(di)); + di.format = UNW_INFO_FORMAT_REMOTE_TABLE; + di.start_ip = map->start; + di.end_ip = map->end; + di.u.rti.segbase = map->start + segbase; + di.u.rti.table_data = map->start + table_data; + di.u.rti.table_len = fde_count * sizeof(struct table_entry) + / sizeof(unw_word_t); + return dwarf_search_unwind_table(as, ip, &di, pi, + need_unwind_info, arg); + } + //return -EINVAL; + return -UNW_ENOINFO; +} + +static int access_fpreg(unw_addr_space_t as, + unw_regnum_t num, + unw_fpreg_t *val, + int __write, + void *arg) +{ + return -UNW_EINVAL; +} + +static int get_dyn_info_list_addr(unw_addr_space_t as, + unw_word_t *dil_addr, + void *arg) +{ + return -UNW_ENOINFO; +} + +static int resume(unw_addr_space_t as, + unw_cursor_t *cu, + void *arg) +{ + return -UNW_EINVAL; +} + +static int +get_proc_name(unw_addr_space_t as, + unw_word_t addr, + char *bufp, size_t buf_len, + unw_word_t *offp, void *arg) +{ + return -UNW_EINVAL; +} + +struct map *last_map = NULL; +static int access_dso_mem(struct unwind_info *ui, unw_word_t addr, + unw_word_t *data) +{ + ssize_t size; + + // ip in the first page is invalid + if ( addr == 0 || addr == (unsigned long)(-1) || (long)addr < 4096 ) { + return -UNW_ENOINFO; + } + + vma *map; + map = find_map(addr, ui); + if (!map) { + return -UNW_ENOINFO; + } + + if (map->type != NATIVE_TYPE) { + return -UNW_ENOINFO; + } + + size = dso__data_read_addr(map, + addr, (u8 *) data, sizeof(*data)); + + return !(size == sizeof(*data)); +} + +/* + * Optimization point. + */ +static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id) +{ + /* we only support 3 registers. RIP, RSP and RBP */ + if (id < 0 || id > 2) + return -EINVAL; + + *valp = regs->regs[id]; + return 0; +} + +unw_word_t last_addr = 0; +unw_word_t last_val = 0; +int stack_offset = 0; + +static int access_mem(unw_addr_space_t as, + unw_word_t addr, unw_word_t *valp, + int __write, void *arg) +{ + struct unwind_info *ui = (struct unwind_info *)arg; + struct stack_dump *stack = &ui->sample->user_stack; + unw_word_t start, end; + int offset; + int ret; + + if (addr == last_addr) { + (*valp) = last_val; + return 0; + } + + last_addr = addr; + + /* Don't support write, probably not needed. */ + if (__write || !stack || !ui->sample->user_regs.regs) { + *valp = 0; + // fprintf(stderr, "access_mem: __write memory\n"); + last_val = *valp; + return 0; + } + + /* start is the SP */ + ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP); + if (ret) { + // fprintf(stderr, "access_mem: reg_value error (ret: %d)\n", ret); + return ret; + } + + end = start + stack->size; + + /* Check overflow. */ + if (addr + sizeof(unw_word_t) < addr) { + // fprintf(stderr, "access_mem: Check overflow.\n"); + return -EINVAL; + } + + if (addr < start || addr + sizeof(unw_word_t) >= end) { + ret = access_dso_mem(ui, addr, valp); + if (ret) { + // pr_debug("unwind: access_mem %p not inside range %p-%p\n", + // (void *)addr, (void *)start, (void *)end); + *valp = 0; + last_val = 0; + return ret; + } + last_val = *valp; + return 0; + } + + offset = addr - start; + *valp = *(unw_word_t *)&stack->data[offset]; + last_val = *valp; + stack_offset = offset; + + //pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n", + // (void *)addr, (unsigned long)*valp, offset); + return 0; +} + +static int access_reg(unw_addr_space_t as, + unw_regnum_t regnum, unw_word_t *valp, + int __write, void *arg) +{ + struct unwind_info *ui = (struct unwind_info *)arg; + int id, ret; + + /* Don't support write, I suspect we don't need it. */ + if (__write) { + //pr_err("unwind: access_reg w %d\n", regnum); + return 0; + } + + if (!ui->sample->user_regs.regs) { + *valp = 0; + return 0; + } + + id = unwind__arch_reg_id(regnum); + if (id < 0) { + //fprintf(stderr, "Cannot get reg: %d\n", regnum); + return -EINVAL; + } + + ret = reg_value(valp, &ui->sample->user_regs, id); + if (ret) { + //pr_err("unwind: can't read reg %d\n", regnum); + return ret; + } + + //pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp); + return 0; +} + +static void put_unwind_info(unw_addr_space_t as, + unw_proc_info_t *pi, + void *arg) +{ + //pr_debug("unwind: put_unwind_info called\n"); +} + +static int entry(u64 ip, int pid, int pid_ns, unwind_entry_cb_t cb, void *arg) +{ + struct unwind_entry e; + + e.ip = ip; + e.pid = pid; + e.pid_ns = pid_ns; + + return cb(&e, arg); +} + +static unw_accessors_t accessors = { + .find_proc_info = find_proc_info, + .put_unwind_info = put_unwind_info, + .get_dyn_info_list_addr = get_dyn_info_list_addr, + .access_mem = access_mem, + .access_reg = access_reg, + .access_fpreg = access_fpreg, + .resume = resume, + .get_proc_name = get_proc_name, +}; + +static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb, + void *arg) +{ + unw_addr_space_t addr_space; + unw_cursor_t c; + entry_cb_arg_t *cb_arg = (entry_cb_arg_t *)arg; + int ret; + int loops = 0; + + addr_space = unw_create_addr_space(&accessors, 0); + if (!addr_space) { + //pr_err("unwind: Can't create unwind address space.\n"); + return -ENOMEM; + } + + unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL); + + ret = unw_init_remote(&c, addr_space, ui); /* @ui is args */ + + while (!ret && (unw_step(&c) > 0)) { + unw_word_t ip; + + unw_get_reg(&c, UNW_REG_IP, &ip); //get IP from current step; + cb_arg->arg = &c; + ret = entry(ip, ui->pid, ui->pid_ns, cb, cb_arg); + + loops++; + if (loops >= 50) + break; + } + + unw_destroy_addr_space(addr_space); + return ret; +} + +int unwind__get_entries(unwind_entry_cb_t cb, void *arg, + symbol_parser *sp, int pid, int pid_ns, + struct perf_sample *data) +{ + unw_word_t ip; + struct unwind_info ui = { + .sample = data, + .pid = pid, + .pid_ns = pid_ns, + .sp = sp, + }; + int ret; + + if (!data->user_regs.regs) + return -EINVAL; + + ret = reg_value(&ip, &data->user_regs, PERF_REG_IP); + if (ret) + return ret; + + ret = entry(ip, pid, pid_ns, cb, arg); + if (ret) + return -ENOMEM; + + return get_entries(&ui, cb, arg); +} diff --git a/source/ucli_py/unwind/unwind.h b/source/ucli_py/unwind/unwind.h new file mode 100644 index 0000000..a18cea2 --- /dev/null +++ b/source/ucli_py/unwind/unwind.h @@ -0,0 +1,101 @@ +#ifndef __UNWIND_H +#define __UNWIND_H + +#include +#include "symbol.h" + +typedef unsigned long u64; +typedef unsigned char u8; +typedef unsigned int u32; +typedef long s64; +typedef char s8; +typedef int s32; + + +struct regs_dump { + u64 *regs; +}; + +struct ip_callchain { + u64 nr; + u64 ips[0]; +}; + +struct branch_flags { + u64 mispred:1; + u64 predicted:1; + u64 reserved:62; +}; + +struct branch_entry { + u64 from; + u64 to; + struct branch_flags flags; +}; + +struct branch_stack { + u64 nr; + struct branch_entry entries[0]; +}; + +struct stack_dump { + unsigned short offset; + u64 size; + char *data; +}; + +struct perf_sample { + u64 ip; + u32 pid, tid; + u64 time; + u64 addr; + u64 id; + u64 stream_id; + u64 period; + u32 cpu; + u32 raw_size; + void *raw_data; + struct ip_callchain *callchain; + struct branch_stack *branch_stack; + struct regs_dump user_regs; + struct stack_dump user_stack; +}; + +#define PERF_REG_IP 0 +#define PERF_REG_SP 1 +#define PERF_REG_BP 2 + +struct unwind_entry { + int pid; + int pid_ns; + u64 ip; + struct vma *map; +}; + +typedef struct { + struct perf_sample *stack_sample; + void *arg; +} entry_cb_arg_t; + +typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg); + +int unwind__get_entries(unwind_entry_cb_t cb, void *arg, + symbol_parser *sp, + int pid, int pid_ns, + struct perf_sample *data); +int unwind__arch_reg_id(int regnum); + +extern int stack_offset; +static inline void clear_stack_offset(void) +{ + stack_offset = 0; +} + +static inline int get_stack_offset(void) +{ + return stack_offset; +} + +extern void unwind__get_rbp(void *arg); + +#endif /* __UNWIND_H */