diff --git a/source/ucli/Makefile b/source/ucli/Makefile new file mode 100644 index 0000000..fa87363 --- /dev/null +++ b/source/ucli/Makefile @@ -0,0 +1,16 @@ +TARGET_EXE=ucli +SOURCES=ucli.cc ucli-lib.cc unwind.cc symbol.cc accessors.cc elf.cc +OBJECTS=$(SOURCES:.cc=.o) + +CFLAGS=-g -O0 +INCLUDES=-I/usr/include/elf +LIBS=-lunwind-x86_64 -lunwind -lelf + +%.o: %.cc + $(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +$(TARGET_EXE): $(OBJECTS) + $(CXX) $^ $(LIBS) -o $@ + +clean: + $(RM) $(TARGET_EXE) $(OBJECTS) \ No newline at end of file diff --git a/source/ucli/accessors.cc b/source/ucli/accessors.cc new file mode 100644 index 0000000..621ad95 --- /dev/null +++ b/source/ucli/accessors.cc @@ -0,0 +1,485 @@ +#include +#include // for GElf_Ehdr | Elf +#include +#include +#include +#include + +#include "unwind.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 */ + +static vma *find_map(unw_word_t ip, struct unwind_info *ui) { + return ui->sp->find_vma(ui->pid, ip); +} + +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 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; +} + +#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; \ + }) + +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 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; +} + +struct table_entry { + u32 start_ip_offset; + u32 fde_offset; +}; + +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 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 get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dil_addr, + void *arg) { + return -UNW_ENOINFO; +} + +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); +} + +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; +} + +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; +} + +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 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 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; +} + +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, +}; \ No newline at end of file diff --git a/source/ucli/misc.cc b/source/ucli/misc.cc new file mode 100644 index 0000000..1c31b9f --- /dev/null +++ b/source/ucli/misc.cc @@ -0,0 +1,40 @@ +#include +#include +#include + +class pid_cmdline pid_cmdline; + +class pid_cmdline { + private: + std::map cmdlines; + + public: + void clear(void); + std::string& get_pid_cmdline(int pid); +}; + +static string unknow_symbol("UNKNOWN"); + +void pid_cmdline::clear(void) { cmdlines.clear(); } + +std::string& pid_cmdline::get_pid_cmdline(int pid) { + if (cmdlines.count(pid) == 0) { + int i; + char buf[255]; + char file[255]; + std::fstream ifs; + + snprintf(file, sizeof(file), "/proc/%d/cmdline", pid); + ifs.open(file, ios::binary | ios::in); + ifs.getline(buf, 255); + for (i = 0; i < ifs.gcount() && i < 255; i++) { + if (buf[i] < ' ') { + buf[i] = ' '; + } + } + + cmdlines[pid] = buf; + } + + return cmdlines[pid]; +} \ No newline at end of file diff --git a/source/ucli/symbol.cc b/source/ucli/symbol.cc index 30e2256..c07cfe2 100644 --- a/source/ucli/symbol.cc +++ b/source/ucli/symbol.cc @@ -1,6 +1,11 @@ +#include "symbol.h" + #include #include +#include +#include + #include "elf.h" symbol_parser g_symbol_parser; @@ -213,16 +218,95 @@ bool symbol_parser::find_elf_symbol(symbol &sym, const elf_file &file, int pid, 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; +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::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/ucli b/source/ucli/ucli new file mode 100755 index 0000000..03b867d Binary files /dev/null and b/source/ucli/ucli differ diff --git a/source/ucli/ucli-lib.cc b/source/ucli/ucli-lib.cc index a9954b9..b468135 100644 --- a/source/ucli/ucli-lib.cc +++ b/source/ucli/ucli-lib.cc @@ -4,8 +4,50 @@ #include #include -#include "ucli.h" +#include + #include "symbol.h" +#include "ucli.h" +#include "unwind.h" + +using namespace std; + +class pid_cmdline { + private: + std::map cmdlines; + + public: + void clear(void); + std::string &get_pid_cmdline(int pid); +}; + +class pid_cmdline pid_cmdline; + +static string unknow_symbol("UNKNOWN"); + +void pid_cmdline::clear(void) { cmdlines.clear(); } + +std::string &pid_cmdline::get_pid_cmdline(int pid) { + if (cmdlines.count(pid) == 0) { + int i; + char buf[255]; + char file[255]; + std::fstream ifs; + + snprintf(file, sizeof(file), "/proc/%d/cmdline", pid); + ifs.open(file, ios::binary | ios::in); + ifs.getline(buf, 255); + for (i = 0; i < ifs.gcount() && i < 255; i++) { + if (buf[i] < ' ') { + buf[i] = ' '; + } + } + + cmdlines[pid] = buf; + } + + return cmdlines[pid]; +} /** * @brief 调用ioctl @@ -31,6 +73,34 @@ err: return ret; } +static int unwind_frame_callback(struct unwind_entry *entry, void *arg) { + symbol sym; + std::string symbol; // Use std::string instead of string + elf_file file; + + sym.reset(entry->ip); + + if (g_symbol_parser.find_symbol_in_cache(entry->pid, entry->ip, symbol)) { + printf("#~ 0x%lx %s ([symbol])\n", entry->ip, symbol.c_str()); + return 0; + } + + if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) { + if (g_symbol_parser.find_elf_symbol(sym, file, entry->pid, entry->pid_ns)) { + printf("#~ 0x%lx %s ([symbol])\n", entry->ip, sym.name.c_str()); + g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, sym.name); + } else { + printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[symbol]"); + g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); + } + } else { + printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[vma,elf]"); + g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); + } + + return 0; +} + void extract_variant_buffer(char *buf, unsigned int len, int (*func)(void *, unsigned int, void *), void *arg) { @@ -61,41 +131,69 @@ void extract_variant_buffer(char *buf, unsigned int len, } } -void printk_task_brief(struct diag_task_detail *detail) { - printk(" 进程信息: [%s / %s], PID: %d / %d\n", detail->cgroup_buf, +void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, + raw_stack_detail *raw_stack) { + struct perf_sample stack_sample; + entry_cb_arg_t unwind_arg; + static u64 regs_buf[3]; + + printf("##C++ pid %d\n", pid); + + printf(" 用户态堆栈SP:%lx, BP:%lx, IP:%lx\n", raw_stack->sp, + raw_stack->bp, raw_stack->ip); + stack_sample.user_stack.offset = 0; + stack_sample.user_stack.size = raw_stack->stack_size; + stack_sample.user_stack.data = (char *)&raw_stack->stack[0]; + stack_sample.user_regs.regs = regs_buf; + stack_sample.user_regs.regs[PERF_REG_IP] = raw_stack->ip; + stack_sample.user_regs.regs[PERF_REG_SP] = raw_stack->sp; + stack_sample.user_regs.regs[PERF_REG_BP] = raw_stack->bp; + unwind__get_entries(unwind_frame_callback, &unwind_arg, &g_symbol_parser, pid, + ns_pid, &stack_sample); + fflush(stdout); +} + +void printk_task_brief(task_detail *detail) { + printf(" 进程信息: [%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) { +void diag_printf_kern_stack(kern_stack_detail *kern_stack) { 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"); - } + for (i = 0; i < BACKTRACE_DEPTH; i++) { + if (kern_stack->stack[i] == (size_t)-1 || kern_stack->stack[i] == 0) { + break; } - } 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"); - } + 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"); } } } + +void diag_printf_proc_chains(proc_chains_detail *proc_chains) { + int detail = 1; + int i; + + printf(" 进程链信息:\n"); + for (i = 0; i < PROCESS_CHAINS_COUNT; i++) { + if (proc_chains->chains[i][0] == 0) break; + if (proc_chains->full_argv[i] == 0 && detail) { + string cmdline = pid_cmdline.get_pid_cmdline(proc_chains->tgid[i]); + if (cmdline.length() > 0) + printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", cmdline.c_str()); + else + printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", + proc_chains->chains[i]); + } else { + printf("#^ 0xffffffffffffff %s (UNKNOWN)\n", + proc_chains->chains[i]); + } + } +} \ No newline at end of file diff --git a/source/ucli/ucli.h b/source/ucli/ucli.h index 96cba56..ab3f056 100644 --- a/source/ucli/ucli.h +++ b/source/ucli/ucli.h @@ -1,12 +1,108 @@ #ifndef UAPI_H #define UAPI_H -#include // size_t +#include // struct pt_regs +#include -#include "../module/monitor_trace.h" +#include // size_t #define DEVICE "/dev/variable_monitor" +#define CGROUP_NAME_LEN 32 // max length of cgroup name +#define TASK_COMM_LEN 16 // max length of task name + +#define BACKTRACE_DEPTH 30 // max depth of backtrace + +#define PROCESS_CHAINS_COUNT 10 // max count of process chains +#define PROCESS_ARGV_LEN 128 // max length of process argv + +#define MAX_NAME_LEN (15) // max name length +#define TIMER_MAX_WATCH_NUM (32) // A timer max watch number at once time + +#define DIAG_USER_STACK_SIZE (16 * 1024) + +typedef struct { + pid_t task_id; // current process id + char name[MAX_NAME_LEN + 1]; // name + void *ptr; // virtual address + long long threshold; // threshold value +} threshold; + +typedef struct { + int et_type; + unsigned long id; + unsigned long long tv; + int threshold_num; + threshold threshold_record[TIMER_MAX_WATCH_NUM]; +} variable_monitor_record; + +typedef struct { + char cgroup_buf[CGROUP_NAME_LEN]; + char cgroup_cpuset[CGROUP_NAME_LEN]; + int pid; + int tgid; + int container_pid; + int container_tgid; + long state; + int task_type; + unsigned long syscallno; + /** + * 0->user 1->sys 2->idle + */ + unsigned long sys_task; + /** + * 1->user mode 0->sys mode -1->unknown + */ + unsigned long user_mode; + char comm[TASK_COMM_LEN]; +} task_detail; + +typedef struct { + unsigned long stack[BACKTRACE_DEPTH]; +} kern_stack_detail; + +typedef struct { + struct pt_regs regs; + unsigned long ip; + unsigned long bp; + unsigned long sp; + unsigned long stack[BACKTRACE_DEPTH]; +} user_stack_detail; + +typedef struct { + struct pt_regs regs; + unsigned long ip; + unsigned long bp; + unsigned long sp; + unsigned long stack_size; + unsigned long stack[DIAG_USER_STACK_SIZE / sizeof(unsigned long)]; +} raw_stack_detail; + +typedef struct { + unsigned int full_argv[PROCESS_CHAINS_COUNT]; // + char chains[PROCESS_CHAINS_COUNT][PROCESS_ARGV_LEN]; // process chains argv + unsigned int tgid[PROCESS_CHAINS_COUNT]; // process chains tgid +} proc_chains_detail; + +// most important struct +typedef struct { + int et_type; + unsigned long id; + unsigned long long tv; + task_detail task; // brief + user_stack_detail user_stack; // user stack + kern_stack_detail kern_stack; // kernel stack + proc_chains_detail proc_chains; // process chains argv + raw_stack_detail raw_stack; +} variable_monitor_task; + +#define DIAG_VARIANT_BUFFER_HEAD_MAGIC_SEALED 197612031122 +#define DIAG_VARIANT_BUFFER_HEAD_MAGIC_UNSEALED 197612031234 + +struct diag_variant_buffer_head { + unsigned long magic; + unsigned long len; +}; struct diag_ioctl_dump_param { int *user_ptr_len; size_t user_buf_len; diff --git a/source/ucli/unwind.cc b/source/ucli/unwind.cc new file mode 100644 index 0000000..bc57c15 --- /dev/null +++ b/source/ucli/unwind.cc @@ -0,0 +1,133 @@ +#include "unwind.h" + +#include +#include + +#include + +static std::string unknow_symbol("UNKNOWN"); + +// static int unwind_frame_callback(struct unwind_entry *entry, void *arg) { +// symbol sym; +// std::string symbol; // Use std::string instead of string +// elf_file file; + +// sym.reset(entry->ip); + +// if (g_symbol_parser.find_symbol_in_cache(entry->pid, entry->ip, symbol)) { +// printf("#~ 0x%lx %s ([symbol])\n", entry->ip, symbol.c_str()); +// return 0; +// } + +// if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) { +// if (g_symbol_parser.find_elf_symbol(sym, file, entry->pid, entry->pid_ns)) { +// printf("#~ 0x%lx %s ([symbol])\n", entry->ip, sym.name.c_str()); +// g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, sym.name); +// } else { +// printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[symbol]"); +// g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); +// } +// } else { +// printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[vma,elf]"); +// g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol); +// } + +// return 0; +// } +/* + * 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; +} + +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 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); +} + +// void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, +// raw_stack_detail *raw_stack, int attach){ +// struct perf_sample stack_sample; +// entry_cb_arg_t unwind_arg; +// static u64 regs_buf[3]; + +// printf("##C++ pid %d\n", pid); + +// printf(" 用户态堆栈SP:%lx, BP:%lx, IP:%lx\n", raw_stack->sp, +// raw_stack->bp, raw_stack->ip); +// stack_sample.user_stack.offset = 0; +// stack_sample.user_stack.size = raw_stack->stack_size; +// stack_sample.user_stack.data = (char *)&raw_stack->stack[0]; +// stack_sample.user_regs.regs = regs_buf; +// stack_sample.user_regs.regs[PERF_REG_IP] = raw_stack->ip; +// stack_sample.user_regs.regs[PERF_REG_SP] = raw_stack->sp; +// stack_sample.user_regs.regs[PERF_REG_BP] = raw_stack->bp; +// unwind__get_entries(unwind_frame_callback, &unwind_arg, &g_symbol_parser, pid, +// ns_pid, &stack_sample); +// fflush(stdout); +// } \ No newline at end of file diff --git a/source/ucli/unwind.h b/source/ucli/unwind.h new file mode 100644 index 0000000..a0e74c4 --- /dev/null +++ b/source/ucli/unwind.h @@ -0,0 +1,109 @@ +#ifndef __UNWIND_H +#define __UNWIND_H + +#include +#include +#include + +#include "symbol.h" + +#define DIAG_USER_STACK_SIZE (16 * 1024) + +typedef unsigned long u64; +typedef unsigned char u8; +typedef unsigned int u32; +typedef long s64; +typedef char s8; +typedef int s32; + +// typedef struct { +// struct pt_regs regs; +// unsigned long ip; +// unsigned long bp; +// unsigned long sp; +// unsigned long stack_size; +// unsigned long stack[DIAG_USER_STACK_SIZE / sizeof(unsigned long)]; +// } raw_stack_detail; + +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); + +// unw_create_addr_space need +extern unw_accessors_t accessors; + +struct unwind_info { + struct perf_sample *sample; + int pid; + int pid_ns; + symbol_parser *sp; +}; + +// extern "C" void diag_printf_raw_stack(int pid, int ns_pid, const char *comm, +// raw_stack_detail *raw_stack, int attach); +int unwind__get_entries(unwind_entry_cb_t cb, void *arg, symbol_parser *sp, + int pid, int pid_ns, struct perf_sample *data); +#endif /* __UNWIND_H */