This commit is contained in:
zy
2023-11-23 22:09:58 -05:00
parent 1df11cd6e4
commit 85fd3e79ae
8 changed files with 2262 additions and 0 deletions

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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

View File

@@ -0,0 +1,189 @@
// /*
// * Linux内核诊断工具--杂项定义头文件
// *
// * Copyright (C) 2020 Alibaba Ltd.
// *
// * 作者: Baoyou Xie <baoyou.xie@linux.alibaba.com>
// *
// * 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);
// }

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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 <davidm@hpl.hp.com>
*
* And the bugs have been added by:
*
* Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
* Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
*
*/
#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);
}

View File

@@ -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 */