perf: parse the .debug_frame section in case .eh_frame is not present
On ARM the debug info is not present in the .eh_frame sections but in .debug_frame instead, in dwarf format. Use libunwind to load and parse the debug info. Dependencies: . if present, libunwind >= 1.1 is needed to prevent a segfault when parsing the dwarf info, . libunwind needs to be configured with --enable-debug-frame. Note: --enable-debug-frame is automatically selected on ARM. Acked-by: Jiri Olsa <jolsa@redhat.com> Signed-off-by: Jean Pihet <jean.pihet@linaro.org> Signed-off-by: Will Deacon <will.deacon@arm.com>
This commit is contained in:
Родитель
405ffbd498
Коммит
ab255e7220
|
@ -39,6 +39,15 @@ UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
|
||||||
|
|
||||||
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
|
||||||
|
|
||||||
|
extern int
|
||||||
|
UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
|
||||||
|
unw_word_t ip,
|
||||||
|
unw_word_t segbase,
|
||||||
|
const char *obj_name, unw_word_t start,
|
||||||
|
unw_word_t end);
|
||||||
|
|
||||||
|
#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
|
||||||
|
|
||||||
#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
|
#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 */
|
#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
|
||||||
|
|
||||||
|
@ -245,8 +254,9 @@ static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
static int read_unwind_spec_eh_frame(struct dso *dso, struct machine *machine,
|
||||||
u64 *table_data, u64 *segbase, u64 *fde_count)
|
u64 *table_data, u64 *segbase,
|
||||||
|
u64 *fde_count)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL, fd;
|
int ret = -EINVAL, fd;
|
||||||
u64 offset;
|
u64 offset;
|
||||||
|
@ -255,6 +265,7 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check the .eh_frame section for unwinding info */
|
||||||
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
offset = elf_section_offset(fd, ".eh_frame_hdr");
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
|
@ -263,10 +274,29 @@ static int read_unwind_spec(struct dso *dso, struct machine *machine,
|
||||||
table_data, segbase,
|
table_data, segbase,
|
||||||
fde_count);
|
fde_count);
|
||||||
|
|
||||||
/* TODO .debug_frame check if eh_frame_hdr fails */
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
||||||
|
static int read_unwind_spec_debug_frame(struct dso *dso,
|
||||||
|
struct machine *machine, u64 *offset)
|
||||||
|
{
|
||||||
|
int fd = dso__data_fd(dso, machine);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check the .debug_frame section for unwinding info */
|
||||||
|
*offset = elf_section_offset(fd, ".debug_frame");
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
if (*offset)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
|
||||||
{
|
{
|
||||||
struct addr_location al;
|
struct addr_location al;
|
||||||
|
@ -291,20 +321,33 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
||||||
|
|
||||||
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
|
pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
|
||||||
|
|
||||||
if (read_unwind_spec(map->dso, ui->machine,
|
/* Check the .eh_frame section for unwinding info */
|
||||||
&table_data, &segbase, &fde_count))
|
if (!read_unwind_spec_eh_frame(map->dso, ui->machine,
|
||||||
return -EINVAL;
|
&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);
|
||||||
|
}
|
||||||
|
|
||||||
memset(&di, 0, sizeof(di));
|
#ifndef NO_LIBUNWIND_DEBUG_FRAME
|
||||||
di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
/* Check the .debug_frame section for unwinding info */
|
||||||
di.start_ip = map->start;
|
if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) {
|
||||||
di.end_ip = map->end;
|
memset(&di, 0, sizeof(di));
|
||||||
di.u.rti.segbase = map->start + segbase;
|
dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name,
|
||||||
di.u.rti.table_data = map->start + table_data;
|
map->start, map->end);
|
||||||
di.u.rti.table_len = fde_count * sizeof(struct table_entry)
|
return dwarf_search_unwind_table(as, ip, &di, pi,
|
||||||
/ sizeof(unw_word_t);
|
need_unwind_info, arg);
|
||||||
return dwarf_search_unwind_table(as, ip, &di, pi,
|
}
|
||||||
need_unwind_info, arg);
|
#endif
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int access_fpreg(unw_addr_space_t __maybe_unused as,
|
static int access_fpreg(unw_addr_space_t __maybe_unused as,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче