diff --git a/source/ucli/elf.cc b/source/ucli/elf.cc new file mode 100644 index 0000000..7be03f1 --- /dev/null +++ b/source/ucli/elf.cc @@ -0,0 +1,336 @@ +#include "elf.h" + +#include +#include // for open +#include // for GElf_Ehdr +#include +#include +#include +#include + +#define NS_NAME_LEN 128 + +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; +}; + +static int global_mnt_ns_fd = -1; +static char global_mnt_ns_name[NS_NAME_LEN]; +static char g_mnt_ns_name[128]; + +int init_global_env(void) { + struct utsname utsname; + int ret = uname(&utsname); + if (ret < 0) { + return -1; + } + + if (readlink("/proc/1/ns/mnt", g_mnt_ns_name, sizeof(g_mnt_ns_name)) < 0) { + return -1; + } + + ret = readlink("/proc/1/ns/mnt", global_mnt_ns_name, NS_NAME_LEN); + if (ret <= 0) { + return -1; + } else { + global_mnt_ns_fd = open("/proc/1/ns/mnt", 0); + if (global_mnt_ns_fd < 0) { + return -1; + } + } + return 0; +} + +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 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 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_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_all_symbols(std::set &ss, symbol_sections_ctx *si, + Elf *elf) { + __get_symbol(ss, si, elf); + __get_plt_symbol(ss, si, elf); +} + +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; +} \ No newline at end of file diff --git a/source/ucli/elf.h b/source/ucli/elf.h new file mode 100644 index 0000000..7b4c5e9 --- /dev/null +++ b/source/ucli/elf.h @@ -0,0 +1,11 @@ +#ifndef _PERF_ELF_H__ +#define _PERF_ELF_H__ + +#include +#include + +#include "symbol.h" + +bool get_symbol_from_elf(std::set &ss, const char *path); + +#endif diff --git a/source/ucli/miss.cc b/source/ucli/miss.cc new file mode 100644 index 0000000..e69de29 diff --git a/source/ucli/symbol.cc b/source/ucli/symbol.cc new file mode 100644 index 0000000..30e2256 --- /dev/null +++ b/source/ucli/symbol.cc @@ -0,0 +1,228 @@ +#include +#include + +#include "elf.h" + +symbol_parser g_symbol_parser; + +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; +} + +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::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::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::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 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 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_elf_symbol(symbol &sym, const elf_file &file, int pid, + int 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; +} + +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; +} \ No newline at end of file diff --git a/source/ucli/symbol.h b/source/ucli/symbol.h new file mode 100644 index 0000000..a2bd973 --- /dev/null +++ b/source/ucli/symbol.h @@ -0,0 +1,149 @@ +#ifndef __PERF_SYMBOL_H__ +#define __PERF_SYMBOL_H__ + +#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 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; + } +}; + +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; + } +}; + +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 \ No newline at end of file diff --git a/source/ucli/ucli-lib.cc b/source/ucli/ucli-lib.cc index 92a3c65..a9954b9 100644 --- a/source/ucli/ucli-lib.cc +++ b/source/ucli/ucli-lib.cc @@ -5,6 +5,7 @@ #include #include "ucli.h" +#include "symbol.h" /** * @brief 调用ioctl @@ -30,3 +31,71 @@ err: return ret; } +void extract_variant_buffer(char *buf, unsigned int len, + int (*func)(void *, unsigned int, void *), + void *arg) { + unsigned int pos = 0; + struct diag_variant_buffer_head *head; + void *rec; + int rec_len; + char *ret; + char dir[1024] = {0}; + + ret = getcwd(dir, sizeof(dir)); + + while (pos < len) { + head = (struct diag_variant_buffer_head *)(buf + pos); + if (pos + sizeof(struct diag_variant_buffer_head) >= len) break; + if (head->magic != DIAG_VARIANT_BUFFER_HEAD_MAGIC_SEALED) break; + if (head->len < sizeof(struct diag_variant_buffer_head)) break; + + rec = (void *)(buf + pos + sizeof(struct diag_variant_buffer_head)); + rec_len = head->len - sizeof(struct diag_variant_buffer_head); + func(rec, rec_len, arg); + + pos += head->len; + } + + if (ret) { + (void)chdir(dir); + } +} + +void printk_task_brief(struct diag_task_detail *detail) { + printk(" 进程信息: [%s / %s], PID: %d / %d\n", detail->cgroup_buf, + detail->comm, detail->tgid, detail->pid); +} + +void diag_printf_kern_stack(kern_stack_detail *kern_stack, int reverse) { + int i; + symbol sym; + + printf(" 内核态堆栈:\n"); + if (reverse) { + for (i = BACKTRACE_DEPTH - 1; i >= 0; i--) { + if (kern_stack->stack[i] == (size_t)-1 || kern_stack->stack[i] == 0) { + continue; + } + sym.reset(kern_stack->stack[i]); + if (g_symbol_parser.find_kernel_symbol(sym)) { + printf("#@ 0x%lx %s ([kernel.kallsyms])\n", kern_stack->stack[i], + sym.name.c_str()); + } else { + printf("#@ 0x%lx %s\n", kern_stack->stack[i], "UNKNOWN"); + } + } + } else { + for (i = 0; i < BACKTRACE_DEPTH; i++) { + if (kern_stack->stack[i] == (size_t)-1 || kern_stack->stack[i] == 0) { + break; + } + sym.reset(kern_stack->stack[i]); + if (g_symbol_parser.find_kernel_symbol(sym)) { + printf("#@ 0x%lx %s ([kernel.kallsyms])\n", kern_stack->stack[i], + sym.name.c_str()); + } else { + printf("#@ 0x%lx %s\n", kern_stack->stack[i], "UNKNOWN"); + } + } + } +} diff --git a/source/ucli/ucli.cc b/source/ucli/ucli.cc index b949aa0..6fd140e 100644 --- a/source/ucli/ucli.cc +++ b/source/ucli/ucli.cc @@ -8,7 +8,7 @@ #include -static int buffer_extract(void *buf, unsigned int len) { +static int task_info_extract(void *buf, unsigned int len, void *) { int *et_type; variable_monitor_record *vm_record; variable_monitor_task *tsk_info; @@ -60,6 +60,10 @@ static int buffer_extract(void *buf, unsigned int len) { return 0; } +static void do_extract(char *buf, int len) { + extract_variant_buffer(buf, len, task_info_extract, NULL); +} + static void do_dump(const char *arg) { static char variant_buf[50 * 1024 * 1024]; int len; @@ -77,7 +81,7 @@ static void do_dump(const char *arg) { ret = diag_call_ioctl(1, (long)&dump_param); // !todo arg -> #define if (ret == 0) { - buffer_extract(variant_buf, len); + do_extract(variant_buf, len); } } diff --git a/source/ucli/ucli.h b/source/ucli/ucli.h index f069537..96cba56 100644 --- a/source/ucli/ucli.h +++ b/source/ucli/ucli.h @@ -15,6 +15,10 @@ struct diag_ioctl_dump_param { long diag_call_ioctl(unsigned long request, unsigned long arg); +void extract_variant_buffer(char *buf, unsigned int len, + int (*func)(void *, unsigned int, void *), + void *arg); + // all print fun void printk_task_brief(task_detail *detail); void diag_printf_raw_stack(int pid, int ns_pid, const char *comm,