summaryrefslogtreecommitdiff
path: root/source/ucli_py
diff options
context:
space:
mode:
authorzy <[email protected]>2023-11-23 22:09:58 -0500
committerzy <[email protected]>2023-11-23 22:09:58 -0500
commit85fd3e79aec31356876d7a2b25654c5a681fdc1c (patch)
tree508eb90e2b8b741d67704bc68f273a91eb61268c /source/ucli_py
parent1df11cd6e4534ac2c2759d29718743ef5ceb6f2b (diff)
unwind
Diffstat (limited to 'source/ucli_py')
-rw-r--r--source/ucli_py/unwind/Makefile29
-rw-r--r--source/ucli_py/unwind/elf.cc566
-rw-r--r--source/ucli_py/unwind/elf.h25
-rw-r--r--source/ucli_py/unwind/internal.h189
-rw-r--r--source/ucli_py/unwind/symbol.cc504
-rw-r--r--source/ucli_py/unwind/symbol.h164
-rw-r--r--source/ucli_py/unwind/unwind.cc684
-rw-r--r--source/ucli_py/unwind/unwind.h101
8 files changed, 2262 insertions, 0 deletions
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 <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdio.h>
+
+#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<symbol> &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<symbol> &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<symbol> &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<symbol> &ss, symbol_sections_ctx *si, Elf *elf)
+{
+ __get_symbol(ss, si, elf);
+ __get_plt_symbol(ss, si, elf);
+}
+
+bool search_symbol(const std::set<symbol> &ss, symbol &sym)
+{
+ std::set<symbol>::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<symbol> &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<symbol> &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<symbol> &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<symbol>::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 <set>
+#include <string>
+
+#include "symbol.h"
+
+#define BUILD_ID_SIZE 40
+bool save_symbol_cache(std::set<symbol> &ss, const char *path);
+bool load_symbol_cache(std::set<symbol> &ss, const char *path, const char *filename);
+
+bool get_symbol_from_elf(std::set<symbol> &ss, const char *path);
+bool search_symbol(const std::set<symbol> &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 <[email protected]>
+// *
+// * License terms: GNU General Public License (GPL) version 3
+// *
+// */
+
+// #include <string>
+// #include <set>
+// #include "uapi/ali_diagnose.h"
+// #include "json/json.h"
+
+// #include <fcntl.h>
+// #include <sys/ioctl.h>
+// #include <unistd.h>
+
+// #include "debug.h"
+
+// extern std::set<int> 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<int> &);
+// 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<int, std::string> 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<int> &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 <vector>
+#include <string.h>
+#include <algorithm>
+
+#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<int, proc_vma>::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<int, proc_vma>::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<symbol> 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<symbol> ss;
+ std::map<int, std::set<symbol> >::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<std::string> &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<symbol> &syms,
+ std::vector<std::string> &sym_list,
+ std::vector<std::string>::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<std::string> sym_list;
+ if (!load_kernel_symbol_list(sym_list)) {
+ exit(0);
+ return false;
+ }
+
+ std::vector<std::string>::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<elf_file, std::set<symbol> >::iterator it;
+ it = file_symbols.find(file);
+ std::set<symbol> tmp;
+ std::set<symbol> &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<symbol>::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<int, std::map<unsigned long, std::string> >::const_iterator it_pid =
+ symbols_cache.find(tgid);
+
+ if (it_pid != symbols_cache.end()) {
+ std::map<unsigned long, std::string> map = symbols_cache[tgid];
+ std::map<unsigned long, std::string>::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<int, std::map<unsigned long, std::string> >::const_iterator it_pid =
+ symbols_cache.find(tgid);
+
+ if (it_pid == symbols_cache.end()) {
+ std::map<unsigned long, std::string> map;
+ symbols_cache.insert(std::make_pair(tgid, map));
+ }
+
+ std::map<unsigned long, std::string> &map = symbols_cache[tgid];
+ std::map<unsigned long, std::string>::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<int, proc_vma>::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<elf_file, std::set<symbol> >::iterator it;
+ it = file_symbols.find(file);
+ std::set<symbol> 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<int, proc_vma>::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<int, proc_vma>::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<int> &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<elf_file, std::set<symbol> >::iterator iter = file_symbols.begin();
+ for(; iter != file_symbols.end(); ++iter) {
+ std::set<symbol>& 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<symbol>::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<int, std::set<symbol> >::iterator iter = java_symbols.begin();
+ for(; iter != java_symbols.end(); ++iter) {
+ count1++;
+ std::set<symbol>& 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<int, proc_vma>::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<int, std::map<unsigned long, std::string> >::iterator iter = symbols_cache.begin();
+ for(; iter != symbols_cache.end(); ++iter) {
+ count1++;
+ std::map<unsigned long, std::string>& 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 <map>
+#include <set>
+#include <string>
+
+//#include <boost/icl/interval_map.hpp>
+
+#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<size_t, vma> proc_vma;
+
+ std::map<elf_file, std::set<symbol> > file_symbols;
+ std::map<int, std::set<symbol> > java_symbols;
+ std::set<symbol> kernel_symbols;
+ std::map<int, proc_vma> machine_vma;
+ std::set<int> java_procs;
+ std::map<int, std::map<unsigned long, std::string> > symbols_cache;
+public:
+ bool load_kernel();
+ std::set<int>& 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 <[email protected]>
+ *
+ * And the bugs have been added by:
+ *
+ * Copyright (C) 2010, Frederic Weisbecker <[email protected]>
+ * Copyright (C) 2012, Jiri Olsa <[email protected]>
+ *
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+//#include <perf_regs.h>
+#include <elf.h>
+#include <gelf.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+
+
+#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 <libunwind.h>
+#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 */