libunwind make success
This commit is contained in:
18
source/ucli_py/libunwind/Makefile
Normal file
18
source/ucli_py/libunwind/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
TARGET_SO=libunwind.so
|
||||
SOURCES=unwind.cc symbol.cc
|
||||
OBJECTS=$(SOURCES:.cc=.o)
|
||||
|
||||
INCLUDES=-I.
|
||||
|
||||
CFLAGS=-g -O0 -fPIC
|
||||
|
||||
LFLAGS=-shared
|
||||
|
||||
%.o: %.cc
|
||||
$(CXX) $(CFLAGS) $(INCLUDES) -c $< -o $@
|
||||
|
||||
$(TARGET_SO): $(OBJECTS)
|
||||
$(CXX) $(LFLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET_SO) $(OBJECTS)
|
||||
483
source/ucli_py/libunwind/accessors.cc
Normal file
483
source/ucli_py/libunwind/accessors.cc
Normal file
@@ -0,0 +1,483 @@
|
||||
#include <fcntl.h>
|
||||
#include <gelf.h> // for GElf_Ehdr | Elf
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "unwind.h"
|
||||
|
||||
extern "C" {
|
||||
int UNW_OBJ(dwarf_search_unwind_table)(unw_addr_space_t as, unw_word_t ip,
|
||||
unw_dyn_info_t *di, unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg);
|
||||
}
|
||||
|
||||
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
||||
|
||||
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
|
||||
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
|
||||
|
||||
/* Pointer-encoding formats: */
|
||||
#define DW_EH_PE_omit 0xff
|
||||
#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
|
||||
#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
|
||||
#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
|
||||
#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
|
||||
#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
|
||||
|
||||
/* Pointer-encoding application: */
|
||||
#define DW_EH_PE_absptr 0x00 /* absolute value */
|
||||
#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
|
||||
|
||||
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 vma *find_map(unw_word_t ip, struct unwind_info *ui) {
|
||||
return ui->sp->find_vma(ui->pid, ip);
|
||||
}
|
||||
|
||||
static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, GElf_Shdr *shp,
|
||||
const char *name) {
|
||||
Elf_Scn *sec = NULL;
|
||||
|
||||
while ((sec = elf_nextscn(elf, sec)) != NULL) {
|
||||
char *str;
|
||||
|
||||
gelf_getshdr(sec, shp);
|
||||
str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
|
||||
if (!strcmp(name, str)) break;
|
||||
}
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
static u64 elf_section_offset(int fd, const char *name) {
|
||||
Elf *elf;
|
||||
GElf_Ehdr ehdr;
|
||||
GElf_Shdr shdr;
|
||||
u64 offset = 0;
|
||||
|
||||
elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
|
||||
if (elf == NULL) return 0;
|
||||
|
||||
do {
|
||||
if (gelf_getehdr(elf, &ehdr) == NULL) break;
|
||||
|
||||
if (!elf_section_by_name(elf, &ehdr, &shdr, name)) break;
|
||||
|
||||
offset = shdr.sh_offset;
|
||||
} while (0);
|
||||
|
||||
elf_end(elf);
|
||||
return offset;
|
||||
}
|
||||
|
||||
struct eh_frame_hdr {
|
||||
unsigned char version;
|
||||
unsigned char eh_frame_ptr_enc;
|
||||
unsigned char fde_count_enc;
|
||||
unsigned char table_enc;
|
||||
|
||||
/*
|
||||
* The rest of the header is variable-length and consists of the
|
||||
* following members:
|
||||
*
|
||||
* encoded_t eh_frame_ptr;
|
||||
* encoded_t fde_count;
|
||||
*/
|
||||
|
||||
/* A single encoded pointer should not be more than 8 bytes. */
|
||||
u64 enc[2];
|
||||
|
||||
/*
|
||||
* struct {
|
||||
* encoded_t start_ip;
|
||||
* encoded_t fde_addr;
|
||||
* } binary_search_table[fde_count];
|
||||
*/
|
||||
char data[0];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
ssize_t dso_read(vma *dso, u64 offset, u8 *data, ssize_t size) {
|
||||
ssize_t ret = -1;
|
||||
int fd;
|
||||
|
||||
fd = dso_data_fd(dso);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
do {
|
||||
if (-1 == lseek(fd, offset, SEEK_SET)) break;
|
||||
|
||||
ret = read(fd, data, size);
|
||||
if (ret <= 0) break;
|
||||
} while (0);
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_offset(vma *dso, u64 offset, u8 *data, ssize_t size) {
|
||||
ssize_t r = 0;
|
||||
u8 *p = data;
|
||||
|
||||
do {
|
||||
ssize_t ret;
|
||||
ret = dso_read(dso, offset, p, size);
|
||||
if (ret <= 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret > size) {
|
||||
return -1;
|
||||
}
|
||||
r += ret;
|
||||
p += ret;
|
||||
offset += ret;
|
||||
size -= ret;
|
||||
} while (size);
|
||||
return r;
|
||||
}
|
||||
|
||||
#define dw_read(ptr, type, end) \
|
||||
({ \
|
||||
type *__p = (type *)ptr; \
|
||||
type __v; \
|
||||
if ((__p + 1) > (type *)end) return -EINVAL; \
|
||||
__v = *__p++; \
|
||||
ptr = (typeof(ptr))__p; \
|
||||
__v; \
|
||||
})
|
||||
|
||||
static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, u8 encoding) {
|
||||
u8 *cur = *p;
|
||||
*val = 0;
|
||||
|
||||
switch (encoding) {
|
||||
case DW_EH_PE_omit:
|
||||
*val = 0;
|
||||
goto out;
|
||||
case DW_EH_PE_ptr:
|
||||
*val = dw_read(cur, unsigned long, end);
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (encoding & DW_EH_PE_APPL_MASK) {
|
||||
case DW_EH_PE_absptr:
|
||||
break;
|
||||
case DW_EH_PE_pcrel:
|
||||
*val = (unsigned long)cur;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((encoding & 0x07) == 0x00) encoding |= DW_EH_PE_udata4;
|
||||
|
||||
switch (encoding & DW_EH_PE_FORMAT_MASK) {
|
||||
case DW_EH_PE_sdata4:
|
||||
*val += dw_read(cur, s32, end);
|
||||
break;
|
||||
case DW_EH_PE_udata4:
|
||||
*val += dw_read(cur, u32, end);
|
||||
break;
|
||||
case DW_EH_PE_sdata8:
|
||||
*val += dw_read(cur, s64, end);
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
*val += dw_read(cur, u64, end);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
*p = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define dw_read_encoded_value(ptr, end, enc) \
|
||||
({ \
|
||||
u64 __v; \
|
||||
if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
|
||||
return -EINVAL; \
|
||||
} \
|
||||
__v; \
|
||||
})
|
||||
|
||||
static int unwind_spec_ehframe(vma *dso, u64 offset, u64 *table_data,
|
||||
u64 *segbase, u64 *fde_count) {
|
||||
struct eh_frame_hdr hdr;
|
||||
u8 *enc = (u8 *)&hdr.enc;
|
||||
u8 *end = (u8 *)&hdr.data;
|
||||
ssize_t r;
|
||||
|
||||
r = dso__data_read_offset(dso, offset, (u8 *)&hdr, sizeof(hdr));
|
||||
if (r != sizeof(hdr)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* We dont need eh_frame_ptr, just skip it. */
|
||||
dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
|
||||
|
||||
*fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
|
||||
*segbase = offset;
|
||||
*table_data = (enc - (u8 *)&hdr) + offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dso_data_fd(vma *dso) { return open(dso->name.c_str(), O_RDONLY); }
|
||||
|
||||
static int read_unwind_spec(vma *dso, u64 *table_data, u64 *segbase,
|
||||
u64 *fde_count) {
|
||||
int ret = -EINVAL, fd;
|
||||
|
||||
if (dso->eh_frame_hdr_offset == 0 && dso->elf_read_error == 0) {
|
||||
fd = dso_data_fd(dso);
|
||||
if (fd < 0) return -EINVAL;
|
||||
|
||||
dso->eh_frame_hdr_offset = elf_section_offset(fd, ".eh_frame_hdr");
|
||||
close(fd);
|
||||
ret = unwind_spec_ehframe(dso, dso->eh_frame_hdr_offset, &dso->table_data,
|
||||
&dso->eh_frame_hdr_offset, &dso->fde_count);
|
||||
if (ret != 0) {
|
||||
dso->eh_frame_hdr_offset = 0;
|
||||
dso->elf_read_error = 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
*table_data = dso->table_data;
|
||||
*segbase = dso->eh_frame_hdr_offset;
|
||||
*fde_count = dso->fde_count;
|
||||
|
||||
/* TODO .debug_frame check if eh_frame_hdr fails */
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct table_entry {
|
||||
u32 start_ip_offset;
|
||||
u32 fde_offset;
|
||||
};
|
||||
|
||||
static int find_proc_info(unw_addr_space_t as, unw_word_t ip,
|
||||
unw_proc_info_t *pi, int need_unwind_info,
|
||||
void *arg) {
|
||||
struct unwind_info *ui = (struct unwind_info *)arg;
|
||||
unw_dyn_info_t di;
|
||||
u64 table_data, segbase, fde_count;
|
||||
|
||||
vma *map;
|
||||
map = find_map(ip, ui);
|
||||
if (!map) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!read_unwind_spec(map, &table_data, &segbase, &fde_count)) {
|
||||
memset(&di, 0, sizeof(di));
|
||||
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
||||
di.start_ip = map->start;
|
||||
di.end_ip = map->end;
|
||||
di.u.rti.segbase = map->start + segbase;
|
||||
di.u.rti.table_data = map->start + table_data;
|
||||
di.u.rti.table_len =
|
||||
fde_count * sizeof(struct table_entry) / sizeof(unw_word_t);
|
||||
return dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
|
||||
}
|
||||
// return -EINVAL;
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
static void put_unwind_info(unw_addr_space_t as, unw_proc_info_t *pi,
|
||||
void *arg) {
|
||||
// pr_debug("unwind: put_unwind_info called\n");
|
||||
}
|
||||
|
||||
static int get_dyn_info_list_addr(unw_addr_space_t as, unw_word_t *dil_addr,
|
||||
void *arg) {
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
ssize_t dso__data_read_addr(vma *map, u64 addr, u8 *data, ssize_t size) {
|
||||
u64 offset;
|
||||
|
||||
if (map->name.size() > 0 && map->name[0] != '/') return 0;
|
||||
|
||||
offset = addr - map->start + map->offset;
|
||||
return dso__data_read_offset(map, offset, data, size);
|
||||
}
|
||||
|
||||
struct map *last_map = NULL;
|
||||
static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
|
||||
unw_word_t *data) {
|
||||
ssize_t size;
|
||||
|
||||
// ip in the first page is invalid
|
||||
if (addr == 0 || addr == (unsigned long)(-1) || (long)addr < 4096) {
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
vma *map;
|
||||
map = find_map(addr, ui);
|
||||
if (!map) {
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
if (map->type != NATIVE_TYPE) {
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
size = dso__data_read_addr(map, addr, (u8 *)data, sizeof(*data));
|
||||
|
||||
return !(size == sizeof(*data));
|
||||
}
|
||||
|
||||
/*
|
||||
* Optimization point.
|
||||
*/
|
||||
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id) {
|
||||
/* we only support 3 registers. RIP, RSP and RBP */
|
||||
if (id < 0 || id > 2) return -EINVAL;
|
||||
|
||||
*valp = regs->regs[id];
|
||||
return 0;
|
||||
}
|
||||
|
||||
unw_word_t last_addr = 0;
|
||||
unw_word_t last_val = 0;
|
||||
int stack_offset = 0;
|
||||
|
||||
static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
|
||||
int __write, void *arg) {
|
||||
struct unwind_info *ui = (struct unwind_info *)arg;
|
||||
struct stack_dump *stack = &ui->sample->user_stack;
|
||||
unw_word_t start, end;
|
||||
int offset;
|
||||
int ret;
|
||||
|
||||
if (addr == last_addr) {
|
||||
(*valp) = last_val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
last_addr = addr;
|
||||
|
||||
/* Don't support write, probably not needed. */
|
||||
if (__write || !stack || !ui->sample->user_regs.regs) {
|
||||
*valp = 0;
|
||||
// fprintf(stderr, "access_mem: __write memory\n");
|
||||
last_val = *valp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* start is the SP */
|
||||
ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP);
|
||||
if (ret) {
|
||||
// fprintf(stderr, "access_mem: reg_value error (ret: %d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
end = start + stack->size;
|
||||
|
||||
/* Check overflow. */
|
||||
if (addr + sizeof(unw_word_t) < addr) {
|
||||
// fprintf(stderr, "access_mem: Check overflow.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (addr < start || addr + sizeof(unw_word_t) >= end) {
|
||||
ret = access_dso_mem(ui, addr, valp);
|
||||
if (ret) {
|
||||
// pr_debug("unwind: access_mem %p not inside range %p-%p\n",
|
||||
// (void *)addr, (void *)start, (void *)end);
|
||||
*valp = 0;
|
||||
last_val = 0;
|
||||
return ret;
|
||||
}
|
||||
last_val = *valp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
offset = addr - start;
|
||||
*valp = *(unw_word_t *)&stack->data[offset];
|
||||
last_val = *valp;
|
||||
stack_offset = offset;
|
||||
|
||||
// pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n",
|
||||
// (void *)addr, (unsigned long)*valp, offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int unwind__arch_reg_id(int regnum) {
|
||||
int id;
|
||||
|
||||
switch (regnum) {
|
||||
case UNW_X86_64_RBP:
|
||||
id = PERF_REG_BP;
|
||||
break;
|
||||
case UNW_X86_64_RSP:
|
||||
id = PERF_REG_SP;
|
||||
break;
|
||||
case UNW_X86_64_RIP:
|
||||
id = PERF_REG_IP;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static int access_reg(unw_addr_space_t as, unw_regnum_t regnum,
|
||||
unw_word_t *valp, int __write, void *arg) {
|
||||
struct unwind_info *ui = (struct unwind_info *)arg;
|
||||
int id, ret;
|
||||
|
||||
/* Don't support write, I suspect we don't need it. */
|
||||
if (__write) {
|
||||
// pr_err("unwind: access_reg w %d\n", regnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ui->sample->user_regs.regs) {
|
||||
*valp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
id = unwind__arch_reg_id(regnum);
|
||||
if (id < 0) {
|
||||
// fprintf(stderr, "Cannot get reg: %d\n", regnum);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = reg_value(valp, &ui->sample->user_regs, id);
|
||||
if (ret) {
|
||||
// pr_err("unwind: can't read reg %d\n", regnum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int access_fpreg(unw_addr_space_t as, unw_regnum_t num, unw_fpreg_t *val,
|
||||
int __write, void *arg) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static int resume(unw_addr_space_t as, unw_cursor_t *cu, void *arg) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
static int get_proc_name(unw_addr_space_t as, unw_word_t addr, char *bufp,
|
||||
size_t buf_len, unw_word_t *offp, void *arg) {
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
32
source/ucli_py/libunwind/symbol.cc
Normal file
32
source/ucli_py/libunwind/symbol.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
#include "symbol.h"
|
||||
|
||||
symbol_parser g_symbol_parser;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,21 +1,7 @@
|
||||
/*
|
||||
* 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,
|
||||
@@ -55,25 +41,6 @@ struct elf_file {
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -117,9 +84,24 @@ struct vma {
|
||||
}
|
||||
};
|
||||
|
||||
static inline bool operator==(const vma &lhs, const vma &rhs) {
|
||||
return lhs.start == rhs.start && lhs.end == rhs.end && lhs.name == rhs.name;
|
||||
}
|
||||
struct symbol {
|
||||
size_t start;
|
||||
size_t end;
|
||||
size_t ip;
|
||||
std::string name;
|
||||
|
||||
symbol() :start(0), end(0), ip(0) {}
|
||||
symbol(size_t pc) :start(0), end(0), ip(pc) {}
|
||||
|
||||
void reset(size_t va) { start = end = 0; ip = va; }
|
||||
bool operator< (const symbol &sym) const {
|
||||
return sym.ip < start;
|
||||
}
|
||||
|
||||
bool operator> (const symbol &sym) const {
|
||||
return sym.ip > end;
|
||||
}
|
||||
};
|
||||
|
||||
class symbol_parser {
|
||||
private:
|
||||
@@ -159,6 +141,4 @@ public:
|
||||
int user_symbol;
|
||||
};
|
||||
|
||||
extern symbol_parser g_symbol_parser;
|
||||
|
||||
#endif
|
||||
extern symbol_parser g_symbol_parser;
|
||||
131
source/ucli_py/libunwind/unwind.cc
Normal file
131
source/ucli_py/libunwind/unwind.cc
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "unwind.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
static std::string unknow_symbol("UNKNOWN");
|
||||
|
||||
static int unwind_frame_callback(struct unwind_entry *entry, void *arg) {
|
||||
//! todo: 未实现
|
||||
symbol sym;
|
||||
std::string symbol; // Use std::string instead of string
|
||||
elf_file file;
|
||||
|
||||
sym.reset(entry->ip);
|
||||
|
||||
if (g_symbol_parser.find_symbol_in_cache(entry->pid, entry->ip, symbol)) {
|
||||
printf("#~ 0x%lx %s ([symbol])\n", entry->ip, symbol.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (g_symbol_parser.get_symbol_info(entry->pid, sym, file)) {
|
||||
if (g_symbol_parser.find_elf_symbol(sym, file, entry->pid, entry->pid_ns)) {
|
||||
printf("#~ 0x%lx %s ([symbol])\n", entry->ip, sym.name.c_str());
|
||||
g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, sym.name);
|
||||
} else {
|
||||
printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[symbol]");
|
||||
g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol);
|
||||
}
|
||||
} else {
|
||||
printf("#~ 0x%lx %s ([symbol])\n", entry->ip, "(unknown)[vma,elf]");
|
||||
g_symbol_parser.putin_symbol_cache(entry->pid, entry->ip, unknow_symbol);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Optimization point.
|
||||
*/
|
||||
static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id) {
|
||||
/* we only support 3 registers. RIP, RSP and RBP */
|
||||
if (id < 0 || id > 2) return -EINVAL;
|
||||
|
||||
*valp = regs->regs[id];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int entry(u64 ip, int pid, int pid_ns, unwind_entry_cb_t cb, void *arg) {
|
||||
struct unwind_entry e;
|
||||
|
||||
e.ip = ip;
|
||||
e.pid = pid;
|
||||
e.pid_ns = pid_ns;
|
||||
|
||||
return cb(&e, arg);
|
||||
}
|
||||
|
||||
static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
|
||||
void *arg) {
|
||||
unw_addr_space_t addr_space;
|
||||
unw_cursor_t c;
|
||||
entry_cb_arg_t *cb_arg = (entry_cb_arg_t *)arg;
|
||||
int ret;
|
||||
int loops = 0;
|
||||
|
||||
addr_space = unw_create_addr_space(&accessors, 0);
|
||||
if (!addr_space) {
|
||||
// pr_err("unwind: Can't create unwind address space.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
|
||||
|
||||
ret = unw_init_remote(&c, addr_space, ui); /* @ui is args */
|
||||
|
||||
while (!ret && (unw_step(&c) > 0)) {
|
||||
unw_word_t ip;
|
||||
|
||||
unw_get_reg(&c, UNW_REG_IP, &ip); // get IP from current step;
|
||||
cb_arg->arg = &c;
|
||||
ret = entry(ip, ui->pid, ui->pid_ns, cb, cb_arg);
|
||||
|
||||
loops++;
|
||||
if (loops >= 50) break;
|
||||
}
|
||||
|
||||
unw_destroy_addr_space(addr_space);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int unwind__get_entries(unwind_entry_cb_t cb, void *arg, symbol_parser *sp,
|
||||
int pid, int pid_ns, struct perf_sample *data) {
|
||||
unw_word_t ip;
|
||||
struct unwind_info ui = {
|
||||
.sample = data,
|
||||
.pid = pid,
|
||||
.pid_ns = pid_ns,
|
||||
.sp = sp,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!data->user_regs.regs) return -EINVAL;
|
||||
|
||||
ret = reg_value(&ip, &data->user_regs, PERF_REG_IP);
|
||||
if (ret) return ret;
|
||||
|
||||
ret = entry(ip, pid, pid_ns, cb, arg);
|
||||
if (ret) return -ENOMEM;
|
||||
|
||||
return get_entries(&ui, cb, arg);
|
||||
}
|
||||
|
||||
void diag_printf_raw_stack(int pid, int ns_pid, const char *comm,
|
||||
raw_stack_detail *raw_stack, int attach) {
|
||||
struct perf_sample stack_sample;
|
||||
entry_cb_arg_t unwind_arg;
|
||||
static u64 regs_buf[3];
|
||||
|
||||
printf(" 用户态堆栈SP:%lx, BP:%lx, IP:%lx\n", raw_stack->sp,
|
||||
raw_stack->bp, raw_stack->ip);
|
||||
stack_sample.user_stack.offset = 0;
|
||||
stack_sample.user_stack.size = raw_stack->stack_size;
|
||||
stack_sample.user_stack.data = (char *)&raw_stack->stack[0];
|
||||
stack_sample.user_regs.regs = regs_buf;
|
||||
stack_sample.user_regs.regs[PERF_REG_IP] = raw_stack->ip;
|
||||
stack_sample.user_regs.regs[PERF_REG_SP] = raw_stack->sp;
|
||||
stack_sample.user_regs.regs[PERF_REG_BP] = raw_stack->bp;
|
||||
unwind__get_entries(unwind_frame_callback, &unwind_arg, &g_symbol_parser, pid,
|
||||
ns_pid, &stack_sample);
|
||||
}
|
||||
103
source/ucli_py/libunwind/unwind.h
Normal file
103
source/ucli_py/libunwind/unwind.h
Normal file
@@ -0,0 +1,103 @@
|
||||
#include <libunwind-ptrace.h>
|
||||
#include <libunwind.h>
|
||||
#include <linux/ptrace.h>
|
||||
|
||||
#include "symbol.h"
|
||||
|
||||
#define DIAG_USER_STACK_SIZE (16 * 1024)
|
||||
|
||||
typedef unsigned long u64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned int u32;
|
||||
typedef long s64;
|
||||
typedef char s8;
|
||||
typedef int s32;
|
||||
|
||||
typedef struct {
|
||||
struct pt_regs regs;
|
||||
unsigned long ip;
|
||||
unsigned long bp;
|
||||
unsigned long sp;
|
||||
unsigned long stack_size;
|
||||
unsigned long stack[DIAG_USER_STACK_SIZE / sizeof(unsigned long)];
|
||||
} raw_stack_detail;
|
||||
|
||||
struct regs_dump {
|
||||
u64 *regs;
|
||||
};
|
||||
|
||||
struct ip_callchain {
|
||||
u64 nr;
|
||||
u64 ips[0];
|
||||
};
|
||||
|
||||
struct branch_flags {
|
||||
u64 mispred : 1;
|
||||
u64 predicted : 1;
|
||||
u64 reserved : 62;
|
||||
};
|
||||
|
||||
struct branch_entry {
|
||||
u64 from;
|
||||
u64 to;
|
||||
struct branch_flags flags;
|
||||
};
|
||||
|
||||
struct branch_stack {
|
||||
u64 nr;
|
||||
struct branch_entry entries[0];
|
||||
};
|
||||
|
||||
struct stack_dump {
|
||||
unsigned short offset;
|
||||
u64 size;
|
||||
char *data;
|
||||
};
|
||||
|
||||
struct perf_sample {
|
||||
u64 ip;
|
||||
u32 pid, tid;
|
||||
u64 time;
|
||||
u64 addr;
|
||||
u64 id;
|
||||
u64 stream_id;
|
||||
u64 period;
|
||||
u32 cpu;
|
||||
u32 raw_size;
|
||||
void *raw_data;
|
||||
struct ip_callchain *callchain;
|
||||
struct branch_stack *branch_stack;
|
||||
struct regs_dump user_regs;
|
||||
struct stack_dump user_stack;
|
||||
};
|
||||
|
||||
#define PERF_REG_IP 0
|
||||
#define PERF_REG_SP 1
|
||||
#define PERF_REG_BP 2
|
||||
|
||||
struct unwind_entry {
|
||||
int pid;
|
||||
int pid_ns;
|
||||
u64 ip;
|
||||
struct vma *map;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
struct perf_sample *stack_sample;
|
||||
void *arg;
|
||||
} entry_cb_arg_t;
|
||||
|
||||
typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
|
||||
|
||||
// unw_create_addr_space need
|
||||
extern unw_accessors_t accessors;
|
||||
|
||||
struct unwind_info {
|
||||
struct perf_sample *sample;
|
||||
int pid;
|
||||
int pid_ns;
|
||||
symbol_parser *sp;
|
||||
};
|
||||
|
||||
void diag_printf_raw_stack(int pid, int ns_pid, const char *comm,
|
||||
raw_stack_detail *raw_stack, int attach);
|
||||
@@ -1,29 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,566 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,189 +0,0 @@
|
||||
// /*
|
||||
// * 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);
|
||||
// }
|
||||
@@ -1,504 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
@@ -1,684 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
#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 */
|
||||
Reference in New Issue
Block a user