Changeset View
Standalone View
contrib/elftoolchain/addr2line/addr2line.c
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | struct Func { | ||||
Dwarf_Unsigned call_file; | Dwarf_Unsigned call_file; | ||||
Dwarf_Unsigned call_line; | Dwarf_Unsigned call_line; | ||||
Dwarf_Ranges *ranges; | Dwarf_Ranges *ranges; | ||||
Dwarf_Signed ranges_cnt; | Dwarf_Signed ranges_cnt; | ||||
struct Func *inlined_caller; | struct Func *inlined_caller; | ||||
STAILQ_ENTRY(Func) next; | STAILQ_ENTRY(Func) next; | ||||
}; | }; | ||||
struct CU { | struct range { | ||||
emaste: This s/CU/range/ is just for clarity? I'm curious about reordering so die and dbg are at the… | |||||
Done Inline ActionsYes, I think I asked for the rename since we no longer really assume that a CU can be represented by a single address range. I'm not sure about the purpose of reordering the fields. markj: Yes, I think I asked for the rename since we no longer really assume that a CU can be… | |||||
Done Inline ActionsI don't remember why I reordered the fields. I will revert it. tig_freebsdfoundation.org: I don't remember why I reordered the fields. I will revert it. | |||||
RB_ENTRY(CU) entry; | RB_ENTRY(range) entry; | ||||
Dwarf_Debug dbg; | |||||
Dwarf_Die die; | |||||
Dwarf_Off off; | Dwarf_Off off; | ||||
Dwarf_Unsigned lopc; | Dwarf_Unsigned lopc; | ||||
Dwarf_Unsigned hipc; | Dwarf_Unsigned hipc; | ||||
char **srcfiles; | char **srcfiles; | ||||
Dwarf_Signed nsrcfiles; | Dwarf_Signed nsrcfiles; | ||||
STAILQ_HEAD(, Func) funclist; | STAILQ_HEAD(, Func) funclist; | ||||
Dwarf_Die die; | |||||
Dwarf_Debug dbg; | |||||
}; | }; | ||||
static struct option longopts[] = { | static struct option longopts[] = { | ||||
{"addresses", no_argument, NULL, 'a'}, | {"addresses", no_argument, NULL, 'a'}, | ||||
{"target" , required_argument, NULL, 'b'}, | {"target" , required_argument, NULL, 'b'}, | ||||
{"demangle", no_argument, NULL, 'C'}, | {"demangle", no_argument, NULL, 'C'}, | ||||
{"exe", required_argument, NULL, 'e'}, | {"exe", required_argument, NULL, 'e'}, | ||||
{"functions", no_argument, NULL, 'f'}, | {"functions", no_argument, NULL, 'f'}, | ||||
{"inlines", no_argument, NULL, 'i'}, | {"inlines", no_argument, NULL, 'i'}, | ||||
{"section", required_argument, NULL, 'j'}, | {"section", required_argument, NULL, 'j'}, | ||||
{"pretty-print", no_argument, NULL, 'p'}, | {"pretty-print", no_argument, NULL, 'p'}, | ||||
{"basename", no_argument, NULL, 's'}, | {"basename", no_argument, NULL, 's'}, | ||||
{"help", no_argument, NULL, 'H'}, | {"help", no_argument, NULL, 'H'}, | ||||
{"version", no_argument, NULL, 'V'}, | {"version", no_argument, NULL, 'V'}, | ||||
{NULL, 0, NULL, 0} | {NULL, 0, NULL, 0} | ||||
}; | }; | ||||
static int demangle, func, base, inlines, print_addr, pretty_print; | static int demangle, func, base, inlines, print_addr, pretty_print; | ||||
static char unknown[] = { '?', '?', '\0' }; | static char unknown[] = { '?', '?', '\0' }; | ||||
static Dwarf_Addr section_base; | static Dwarf_Addr section_base; | ||||
/* Need a new curlopc that stores last lopc value. */ | /* Need a new curlopc that stores last lopc value. */ | ||||
static Dwarf_Unsigned curlopc = ~0ULL; | static Dwarf_Unsigned curlopc = ~0ULL; | ||||
static RB_HEAD(cutree, CU) cuhead = RB_INITIALIZER(&cuhead); | static RB_HEAD(cutree, range) cuhead = RB_INITIALIZER(&cuhead); | ||||
static int | static int | ||||
lopccmp(struct CU *e1, struct CU *e2) | lopccmp(struct range *e1, struct range *e2) | ||||
{ | { | ||||
return (e1->lopc < e2->lopc ? -1 : e1->lopc > e2->lopc); | return (e1->lopc < e2->lopc ? -1 : e1->lopc > e2->lopc); | ||||
} | } | ||||
RB_PROTOTYPE(cutree, CU, entry, lopccmp); | RB_PROTOTYPE(cutree, range, entry, lopccmp); | ||||
RB_GENERATE(cutree, CU, entry, lopccmp) | RB_GENERATE(cutree, range, entry, lopccmp) | ||||
#define USAGE_MESSAGE "\ | #define USAGE_MESSAGE "\ | ||||
Usage: %s [options] hexaddress...\n\ | Usage: %s [options] hexaddress...\n\ | ||||
Map program addresses to source file names and line numbers.\n\n\ | Map program addresses to source file names and line numbers.\n\n\ | ||||
Options:\n\ | Options:\n\ | ||||
-a | --addresses Display address prior to line number info.\n\ | -a | --addresses Display address prior to line number info.\n\ | ||||
-b TGT | --target=TGT (Accepted but ignored).\n\ | -b TGT | --target=TGT (Accepted but ignored).\n\ | ||||
-e EXE | --exe=EXE Use program \"EXE\" to translate addresses.\n\ | -e EXE | --exe=EXE Use program \"EXE\" to translate addresses.\n\ | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | handle_high_pc(Dwarf_Die die, Dwarf_Unsigned lopc, Dwarf_Unsigned *hipc) | ||||
} | } | ||||
if (dwarf_get_form_class(2, 0, 0, form) == DW_FORM_CLASS_CONSTANT) | if (dwarf_get_form_class(2, 0, 0, form) == DW_FORM_CLASS_CONSTANT) | ||||
*hipc += lopc; | *hipc += lopc; | ||||
return (DW_DLV_OK); | return (DW_DLV_OK); | ||||
} | } | ||||
static struct Func * | static struct Func * | ||||
search_func(struct CU *cu, Dwarf_Unsigned addr) | search_func(struct range *range, Dwarf_Unsigned addr) | ||||
{ | { | ||||
struct Func *f, *f0; | struct Func *f, *f0; | ||||
Dwarf_Unsigned lopc, hipc, addr_base; | Dwarf_Unsigned lopc, hipc, addr_base; | ||||
int i; | int i; | ||||
f0 = NULL; | f0 = NULL; | ||||
STAILQ_FOREACH(f, &cu->funclist, next) { | STAILQ_FOREACH(f, &range->funclist, next) { | ||||
if (f->ranges != NULL) { | if (f->ranges != NULL) { | ||||
addr_base = 0; | addr_base = 0; | ||||
for (i = 0; i < f->ranges_cnt; i++) { | for (i = 0; i < f->ranges_cnt; i++) { | ||||
if (f->ranges[i].dwr_type == DW_RANGES_END) | if (f->ranges[i].dwr_type == DW_RANGES_END) | ||||
break; | break; | ||||
if (f->ranges[i].dwr_type == | if (f->ranges[i].dwr_type == | ||||
DW_RANGES_ADDRESS_SELECTION) { | DW_RANGES_ADDRESS_SELECTION) { | ||||
addr_base = f->ranges[i].dwr_addr2; | addr_base = f->ranges[i].dwr_addr2; | ||||
Show All 20 Lines | if (f->ranges != NULL) { | ||||
f0 = f; | f0 = f; | ||||
} | } | ||||
} | } | ||||
return (f0); | return (f0); | ||||
} | } | ||||
static void | static void | ||||
collect_func(Dwarf_Debug dbg, Dwarf_Die die, struct Func *parent, struct CU *cu) | collect_func(Dwarf_Debug dbg, Dwarf_Die die, struct Func *parent, struct range *range) | ||||
{ | { | ||||
Dwarf_Die ret_die, abst_die, spec_die; | Dwarf_Die ret_die, abst_die, spec_die; | ||||
Dwarf_Error de; | Dwarf_Error de; | ||||
Dwarf_Half tag; | Dwarf_Half tag; | ||||
Dwarf_Unsigned lopc, hipc, ranges_off; | Dwarf_Unsigned lopc, hipc, ranges_off; | ||||
Dwarf_Signed ranges_cnt; | Dwarf_Signed ranges_cnt; | ||||
Dwarf_Off ref; | Dwarf_Off ref; | ||||
Dwarf_Attribute abst_at, spec_at; | Dwarf_Attribute abst_at, spec_at; | ||||
Dwarf_Ranges *ranges; | Dwarf_Ranges *ranges; | ||||
const char *funcname; | const char *funcname; | ||||
struct Func *f; | struct Func *f; | ||||
int found_ranges, ret; | int found_ranges, ret; | ||||
f = NULL; | f = NULL; | ||||
abst_die = spec_die = NULL; | abst_die = spec_die = NULL; | ||||
if (dwarf_tag(die, &tag, &de)) { | if (dwarf_tag(die, &tag, &de)) { | ||||
warnx("dwarf_tag: %s", dwarf_errmsg(de)); | warnx("dwarf_tag: %s", dwarf_errmsg(de)); | ||||
goto cont_search; | goto cont_search; | ||||
} | } | ||||
if (tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || | if (tag == DW_TAG_subprogram || tag == DW_TAG_entry_point || | ||||
tag == DW_TAG_inlined_subroutine) { | tag == DW_TAG_inlined_subroutine || tag == DW_TAG_label) { | ||||
/* | /* | ||||
* Function address range can be specified by either | * Function address range can be specified by either | ||||
* a DW_AT_ranges attribute which points to a range list or | * a DW_AT_ranges attribute which points to a range list or | ||||
* by a pair of DW_AT_low_pc and DW_AT_high_pc attributes. | * by a pair of DW_AT_low_pc and DW_AT_high_pc attributes. | ||||
*/ | */ | ||||
ranges = NULL; | ranges = NULL; | ||||
ranges_cnt = 0; | ranges_cnt = 0; | ||||
found_ranges = 0; | found_ranges = 0; | ||||
if (dwarf_attrval_unsigned(die, DW_AT_ranges, &ranges_off, | if (dwarf_attrval_unsigned(die, DW_AT_ranges, &ranges_off, | ||||
&de) == DW_DLV_OK && | &de) == DW_DLV_OK && | ||||
dwarf_get_ranges(dbg, (Dwarf_Off) ranges_off, &ranges, | dwarf_get_ranges(dbg, (Dwarf_Off) ranges_off, &ranges, | ||||
&ranges_cnt, NULL, &de) == DW_DLV_OK) { | &ranges_cnt, NULL, &de) == DW_DLV_OK) { | ||||
if (ranges != NULL && ranges_cnt > 0) { | if (ranges != NULL && ranges_cnt > 0) { | ||||
found_ranges = 1; | found_ranges = 1; | ||||
goto get_func_name; | goto get_func_name; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Search for DW_AT_low_pc/DW_AT_high_pc if ranges pointer | * Search for DW_AT_low_pc/DW_AT_high_pc if ranges pointer | ||||
* not found. | * not found. Treat labels differently. | ||||
markjUnsubmitted Done Inline ActionsI think this comment could be a bit more descriptive. :) markj: I think this comment could be a bit more descriptive. :) | |||||
*/ | */ | ||||
if (tag == DW_TAG_label && | |||||
dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == DW_DLV_OK) | |||||
goto get_func_name; | |||||
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) || | if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) || | ||||
dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) | dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) | ||||
goto cont_search; | goto cont_search; | ||||
if (handle_high_pc(die, lopc, &hipc) != DW_DLV_OK) | if (handle_high_pc(die, lopc, &hipc) != DW_DLV_OK) | ||||
goto cont_search; | goto cont_search; | ||||
markjUnsubmitted Done Inline ActionsI think this block can be written without a goto: if (tag != DW_TAG_label) { <something> goto cont_search; } Also, what happens when we have a label without a low_pc attribute? markj: I think this block can be written without a goto:
```
if (tag != DW_TAG_label) {… | |||||
tig_freebsdfoundation.orgAuthorUnsubmitted Done Inline ActionsIf we have a label without a low_pc attribute, we should goto cont_search since we can't use the current label. I made it more explicit in this revision. tig_freebsdfoundation.org: If we have a label without a low_pc attribute, we should goto cont_search since we can't use… | |||||
get_func_name: | get_func_name: | ||||
Done Inline ActionsStyle: indentation should be by four spaces here. markj: Style: indentation should be by four spaces here. | |||||
/* | /* | ||||
* Most common case the function name is stored in DW_AT_name | * Most common case the function name is stored in DW_AT_name | ||||
* attribute. | * attribute. | ||||
*/ | */ | ||||
if (dwarf_attrval_string(die, DW_AT_name, &funcname, &de) == | if (dwarf_attrval_string(die, DW_AT_name, &funcname, &de) == | ||||
DW_DLV_OK) | DW_DLV_OK) | ||||
goto add_func; | goto add_func; | ||||
Show All 39 Lines | add_func: | ||||
} | } | ||||
if (tag == DW_TAG_inlined_subroutine) { | if (tag == DW_TAG_inlined_subroutine) { | ||||
f->inlined_caller = parent; | f->inlined_caller = parent; | ||||
dwarf_attrval_unsigned(die, DW_AT_call_file, | dwarf_attrval_unsigned(die, DW_AT_call_file, | ||||
&f->call_file, &de); | &f->call_file, &de); | ||||
dwarf_attrval_unsigned(die, DW_AT_call_line, | dwarf_attrval_unsigned(die, DW_AT_call_line, | ||||
&f->call_line, &de); | &f->call_line, &de); | ||||
} | } | ||||
STAILQ_INSERT_TAIL(&cu->funclist, f, next); | STAILQ_INSERT_TAIL(&range->funclist, f, next); | ||||
} | } | ||||
cont_search: | cont_search: | ||||
/* Search children. */ | /* Search children. */ | ||||
ret = dwarf_child(die, &ret_die, &de); | ret = dwarf_child(die, &ret_die, &de); | ||||
if (ret == DW_DLV_ERROR) | if (ret == DW_DLV_ERROR) | ||||
warnx("dwarf_child: %s", dwarf_errmsg(de)); | warnx("dwarf_child: %s", dwarf_errmsg(de)); | ||||
else if (ret == DW_DLV_OK) { | else if (ret == DW_DLV_OK) { | ||||
if (f != NULL) | if (f != NULL) | ||||
collect_func(dbg, ret_die, f, cu); | collect_func(dbg, ret_die, f, range); | ||||
else | else | ||||
collect_func(dbg, ret_die, parent, cu); | collect_func(dbg, ret_die, parent, range); | ||||
} | } | ||||
/* Search sibling. */ | /* Search sibling. */ | ||||
ret = dwarf_siblingof(dbg, die, &ret_die, &de); | ret = dwarf_siblingof(dbg, die, &ret_die, &de); | ||||
if (ret == DW_DLV_ERROR) | if (ret == DW_DLV_ERROR) | ||||
warnx("dwarf_siblingof: %s", dwarf_errmsg(de)); | warnx("dwarf_siblingof: %s", dwarf_errmsg(de)); | ||||
else if (ret == DW_DLV_OK) | else if (ret == DW_DLV_OK) | ||||
collect_func(dbg, ret_die, parent, cu); | collect_func(dbg, ret_die, parent, range); | ||||
/* Cleanup */ | /* Cleanup */ | ||||
if (die != cu->die) | if (die != range->die) | ||||
dwarf_dealloc(dbg, die, DW_DLA_DIE); | dwarf_dealloc(dbg, die, DW_DLA_DIE); | ||||
if (abst_die != NULL) | if (abst_die != NULL) | ||||
dwarf_dealloc(dbg, abst_die, DW_DLA_DIE); | dwarf_dealloc(dbg, abst_die, DW_DLA_DIE); | ||||
if (spec_die != NULL) | if (spec_die != NULL) | ||||
dwarf_dealloc(dbg, spec_die, DW_DLA_DIE); | dwarf_dealloc(dbg, spec_die, DW_DLA_DIE); | ||||
} | } | ||||
static void | static void | ||||
print_inlines(struct CU *cu, struct Func *f, Dwarf_Unsigned call_file, | print_inlines(struct range *range, struct Func *f, Dwarf_Unsigned call_file, | ||||
Dwarf_Unsigned call_line) | Dwarf_Unsigned call_line) | ||||
{ | { | ||||
char demangled[1024]; | char demangled[1024]; | ||||
char *file; | char *file; | ||||
if (call_file > 0 && (Dwarf_Signed) call_file <= cu->nsrcfiles) | if (call_file > 0 && (Dwarf_Signed) call_file <= range->nsrcfiles) | ||||
file = cu->srcfiles[call_file - 1]; | file = range->srcfiles[call_file - 1]; | ||||
else | else | ||||
file = unknown; | file = unknown; | ||||
if (pretty_print) | if (pretty_print) | ||||
printf(" (inlined by) "); | printf(" (inlined by) "); | ||||
if (func) { | if (func) { | ||||
if (demangle && !elftc_demangle(f->name, demangled, | if (demangle && !elftc_demangle(f->name, demangled, | ||||
sizeof(demangled), 0)) { | sizeof(demangled), 0)) { | ||||
if (pretty_print) | if (pretty_print) | ||||
printf("%s at ", demangled); | printf("%s at ", demangled); | ||||
else | else | ||||
printf("%s\n", demangled); | printf("%s\n", demangled); | ||||
} else { | } else { | ||||
if (pretty_print) | if (pretty_print) | ||||
printf("%s at ", f->name); | printf("%s at ", f->name); | ||||
else | else | ||||
printf("%s\n", f->name); | printf("%s\n", f->name); | ||||
} | } | ||||
} | } | ||||
(void) printf("%s:%ju\n", base ? basename(file) : file, | (void) printf("%s:%ju\n", base ? basename(file) : file, | ||||
(uintmax_t) call_line); | (uintmax_t) call_line); | ||||
if (f->inlined_caller != NULL) | if (f->inlined_caller != NULL) | ||||
print_inlines(cu, f->inlined_caller, f->call_file, | print_inlines(range, f->inlined_caller, f->call_file, | ||||
f->call_line); | f->call_line); | ||||
} | } | ||||
static struct CU * | static struct range * | ||||
culookup(Dwarf_Unsigned addr) | culookup(Dwarf_Unsigned addr) | ||||
{ | { | ||||
struct CU find, *res; | struct range find, *res; | ||||
find.lopc = addr; | find.lopc = addr; | ||||
res = RB_NFIND(cutree, &cuhead, &find); | res = RB_NFIND(cutree, &cuhead, &find); | ||||
if (res != NULL) { | if (res != NULL) { | ||||
if (res->lopc != addr) | if (res->lopc != addr) | ||||
res = RB_PREV(cutree, &cuhead, res); | res = RB_PREV(cutree, &cuhead, res); | ||||
if (res != NULL && addr >= res->lopc && addr < res->hipc) | if (res != NULL && addr >= res->lopc && addr < res->hipc) | ||||
return (res); | return (res); | ||||
} else { | } else { | ||||
res = RB_MAX(cutree, &cuhead); | res = RB_MAX(cutree, &cuhead); | ||||
if (res != NULL && addr >= res->lopc && addr < res->hipc) | if (res != NULL && addr >= res->lopc && addr < res->hipc) | ||||
return (res); | return (res); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static int | |||||
check_labels(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Unsigned addr, | |||||
markjUnsubmitted Done Inline ActionsI think a high-level description of what this function is doing would be useful. markj: I think a high-level description of what this function is doing would be useful. | |||||
struct range **range) { | |||||
Done Inline Actions"die" should be written "DIE". markj: "die" should be written "DIE". | |||||
Dwarf_Addr start; | |||||
Dwarf_Arange *aranges; | |||||
Dwarf_Die prev_die, ret_die; | |||||
Dwarf_Error de; | |||||
Dwarf_Half tag; | |||||
Dwarf_Off die_off; | |||||
Dwarf_Unsigned lopc, length; | |||||
Dwarf_Signed arcnt; | |||||
struct range *labelp, **labels; | |||||
int i, j, label_cnt, ret; | |||||
prev_die = ret_die = NULL; | |||||
labels = NULL; | |||||
i = label_cnt = 0; | |||||
/* find aranges */ | |||||
ret = dwarf_get_aranges(dbg, &aranges, &arcnt, &de); | |||||
if (ret != DW_DLV_OK && ret != DW_DLV_NO_ENTRY) { | |||||
warnx("dwarf_get_aranges failed: %s", dwarf_errmsg(de)); | |||||
} | |||||
/* Child of current CU */ | |||||
ret = dwarf_child(die, &prev_die, &de); | |||||
if (ret == DW_DLV_ERROR) | |||||
warnx("dwarf_child: %s", dwarf_errmsg(de)); | |||||
/* count labels */ | |||||
while (1) { | |||||
if (dwarf_tag(prev_die, &tag, &de) != DW_DLV_OK) { | |||||
warnx("dwarf_tag failed: %s", | |||||
dwarf_errmsg(de)); | |||||
return DW_DLV_ERROR; | |||||
} | |||||
if (tag == DW_TAG_label) { | |||||
if (dwarf_attrval_unsigned(prev_die, DW_AT_low_pc, &lopc, &de) == | |||||
DW_DLV_OK) { | |||||
label_cnt++; | |||||
} | |||||
} | |||||
if (dwarf_siblingof(dbg, prev_die, &ret_die, &de) != DW_DLV_OK) { | |||||
break; | |||||
} | |||||
if (prev_die != NULL) | |||||
dwarf_dealloc(dbg, prev_die, DW_DLA_DIE); | |||||
prev_die = ret_die; | |||||
} | |||||
if (label_cnt == 0) | |||||
return (DW_DLV_NO_ENTRY); | |||||
/* Allocate space for labels */ | |||||
if ((labels = (struct range **)calloc(label_cnt, sizeof(struct range *))) == NULL) | |||||
err(EXIT_FAILURE, "calloc"); | |||||
/* Add labels to list */ | |||||
ret = dwarf_child(die, &prev_die, &de); | |||||
Done Inline ActionsThere's no need to cast the return value of calloc() in C. markj: There's no need to cast the return value of calloc() in C. | |||||
if (ret == DW_DLV_ERROR) | |||||
warnx("dwarf_child: %s", dwarf_errmsg(de)); | |||||
while (1) { | |||||
if (dwarf_tag(prev_die, &tag, &de) != DW_DLV_OK) { | |||||
warnx("dwarf_tag failed: %s", | |||||
dwarf_errmsg(de)); | |||||
return DW_DLV_ERROR; | |||||
} | |||||
if (tag == DW_TAG_label) { | |||||
if (dwarf_attrval_unsigned(prev_die, DW_AT_low_pc, &lopc, &de) == | |||||
DW_DLV_OK) { | |||||
if (curlopc == lopc) { | |||||
/* free memory */ | |||||
for (i = 0; i < label_cnt - 1; i++) { | |||||
if (labels[i] != *range) | |||||
Done Inline ActionsIndentation by four spaces. markj: Indentation by four spaces. | |||||
free(labels[i]); | |||||
} | |||||
free(labels); | |||||
return DW_DLV_ERROR; | |||||
} | |||||
if ((labelp = calloc(1, sizeof(struct range))) == NULL) | |||||
err(EXIT_FAILURE, "calloc"); | |||||
labelp->lopc = lopc; | |||||
labelp->die = prev_die; | |||||
labelp->dbg = dbg; | |||||
STAILQ_INIT(&labelp->funclist); | |||||
labels[i++] = labelp; | |||||
} | |||||
} | |||||
if (dwarf_siblingof(dbg, prev_die, &ret_die, &de) != DW_DLV_OK) { | |||||
break; | |||||
} | |||||
if (prev_die != NULL && tag != DW_TAG_label) | |||||
dwarf_dealloc(dbg, prev_die, DW_DLA_DIE); | |||||
prev_die = ret_die; | |||||
} | |||||
/* set hipc using aranges */ | |||||
for (i = 0; i < label_cnt; i++) { | |||||
for (j = 0; j < arcnt; j++) { | |||||
if (dwarf_get_arange_info(aranges[j], &start, &length, | |||||
&die_off, &de) != DW_DLV_OK) { | |||||
warnx("dwarf_get_arange_info failed: %s", | |||||
dwarf_errmsg(de)); | |||||
continue; | |||||
} | |||||
if (labels[i]->lopc == (Dwarf_Unsigned)start) { | |||||
labels[i]->hipc = start + length; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* check in range */ | |||||
for (i = 0; i < label_cnt; i++) { | |||||
if (addr >= labels[i]->lopc && addr < labels[i]->hipc) { | |||||
*range = labels[i]; | |||||
/* Add to cache */ | |||||
RB_INSERT(cutree, &cuhead, (*range)); | |||||
curlopc = (*range)->lopc; | |||||
break; | |||||
} | |||||
} | |||||
/* free memory */ | |||||
for (i = 0; i < label_cnt - 1; i++) { | |||||
if (labels[i] != *range) | |||||
free(labels[i]); | |||||
} | |||||
Done Inline ActionsI think these comments ("free memory", "return status", "add to cache", etc.) which describe *what* the code is doing are not useful. It's better to ensure that the high-level algorithm is documented, and to write comments that explain *why* the code is doing something when it's not clear. markj: I think these comments ("free memory", "return status", "add to cache", etc.) which describe… | |||||
free(labels); | |||||
/* return status */ | |||||
if (*range != NULL) { | |||||
return (DW_DLV_OK); | |||||
} else { | |||||
return (DW_DLV_NO_ENTRY); | |||||
} | |||||
} | |||||
/* Checks whether addr falls into range(s) of current CU. | /* Checks whether addr falls into range(s) of current CU. | ||||
* If so, saves current CU to lookup tree */ | * If so, saves current CU to lookup tree */ | ||||
static int | static int | ||||
check_range(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Unsigned addr, | check_range(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Unsigned addr, | ||||
struct CU **cu) | struct range **range) | ||||
{ | { | ||||
Dwarf_Error de; | Dwarf_Error de; | ||||
Dwarf_Unsigned addr_base, lopc, hipc; | Dwarf_Unsigned addr_base, lopc, hipc; | ||||
Dwarf_Off ranges_off; | Dwarf_Off ranges_off; | ||||
Dwarf_Signed ranges_cnt; | Dwarf_Signed ranges_cnt; | ||||
Dwarf_Ranges *ranges; | Dwarf_Ranges *ranges; | ||||
int i, ret; | int i, ret; | ||||
bool in_range; | bool in_cu; | ||||
addr_base = 0; | addr_base = 0; | ||||
ranges = NULL; | ranges = NULL; | ||||
ranges_cnt = 0; | ranges_cnt = 0; | ||||
in_range = false; | in_cu = false; | ||||
ret = dwarf_attrval_unsigned(die, DW_AT_ranges, &ranges_off, &de); | ret = dwarf_attrval_unsigned(die, DW_AT_ranges, &ranges_off, &de); | ||||
if (ret == DW_DLV_NO_ENTRY) { | if (ret == DW_DLV_OK) { | ||||
Not Done Inline ActionsSwapped these around in FreeBSD in rS367853 emaste: Swapped these around in FreeBSD in rS367853 | |||||
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == | |||||
DW_DLV_OK) { | |||||
if (lopc == curlopc) | |||||
return (DW_DLV_ERROR); | |||||
if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, | |||||
&de) == DW_DLV_OK) { | |||||
/* | |||||
* Check if the address falls into the PC | |||||
* range of this CU. | |||||
*/ | |||||
if (handle_high_pc(die, lopc, &hipc) != | |||||
DW_DLV_OK) | |||||
return (DW_DLV_ERROR); | |||||
} else { | |||||
/* Assume ~0ULL if DW_AT_high_pc not present */ | |||||
hipc = ~0ULL; | |||||
} | |||||
if (addr >= lopc && addr < hipc) { | |||||
in_range = true; | |||||
} | |||||
} | |||||
} else if (ret == DW_DLV_OK) { | |||||
ret = dwarf_get_ranges(dbg, ranges_off, &ranges, | ret = dwarf_get_ranges(dbg, ranges_off, &ranges, | ||||
&ranges_cnt, NULL, &de); | &ranges_cnt, NULL, &de); | ||||
if (ret != DW_DLV_OK) | if (ret != DW_DLV_OK) | ||||
return (ret); | return (ret); | ||||
if (!ranges || ranges_cnt <= 0) | if (!ranges || ranges_cnt <= 0) | ||||
return (DW_DLV_ERROR); | return (DW_DLV_ERROR); | ||||
Show All 10 Lines | for (i = 0; i < ranges_cnt; i++) { | ||||
/* DW_RANGES_ENTRY */ | /* DW_RANGES_ENTRY */ | ||||
lopc = ranges[i].dwr_addr1 + addr_base; | lopc = ranges[i].dwr_addr1 + addr_base; | ||||
hipc = ranges[i].dwr_addr2 + addr_base; | hipc = ranges[i].dwr_addr2 + addr_base; | ||||
if (lopc == curlopc) | if (lopc == curlopc) | ||||
return (DW_DLV_ERROR); | return (DW_DLV_ERROR); | ||||
if (addr >= lopc && addr < hipc){ | if (addr >= lopc && addr < hipc){ | ||||
in_range = true; | in_cu = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
Not Done Inline Actionscould we actualy return anything other than DW_DLV_OK or DW_DLV_NO_ENTERY before? emaste: could we actualy return anything other than `DW_DLV_OK` or `DW_DLV_NO_ENTERY` before? | |||||
Not Done Inline ActionsI believe this function has always been returning either DW_DLV_OK or DW_DLV_NO_ENTRY tig_freebsdfoundation.org: I believe this function has always been returning either `DW_DLV_OK` or `DW_DLV_NO_ENTRY` | |||||
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == | |||||
DW_DLV_OK) { | |||||
if (lopc == curlopc) | |||||
return (DW_DLV_ERROR); | return (DW_DLV_ERROR); | ||||
if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, | |||||
&de) == DW_DLV_OK) { | |||||
/* | |||||
* Check if the address falls into the PC | |||||
* range of this CU. | |||||
*/ | |||||
if (handle_high_pc(die, lopc, &hipc) != | |||||
DW_DLV_OK) | |||||
return (DW_DLV_ERROR); | |||||
} else { | |||||
/* Assume ~0ULL if DW_AT_high_pc not present */ | |||||
hipc = ~0ULL; | |||||
} | } | ||||
if (in_range) { | if (addr >= lopc && addr < hipc) { | ||||
if ((*cu = calloc(1, sizeof(struct CU))) == NULL) | in_cu = true; | ||||
} | |||||
} else { | |||||
/* can't find addr in range die, try labels */ | |||||
ret = check_labels(dbg, die, addr, range); | |||||
return ret; | |||||
} | |||||
} | |||||
if (in_cu) { | |||||
if ((*range = calloc(1, sizeof(struct range))) == NULL) | |||||
err(EXIT_FAILURE, "calloc"); | err(EXIT_FAILURE, "calloc"); | ||||
(*cu)->lopc = lopc; | (*range)->lopc = lopc; | ||||
(*cu)->hipc = hipc; | (*range)->hipc = hipc; | ||||
(*cu)->die = die; | (*range)->die = die; | ||||
(*cu)->dbg = dbg; | (*range)->dbg = dbg; | ||||
STAILQ_INIT(&(*cu)->funclist); | STAILQ_INIT(&(*range)->funclist); | ||||
RB_INSERT(cutree, &cuhead, *cu); | RB_INSERT(cutree, &cuhead, *range); | ||||
curlopc = lopc; | curlopc = lopc; | ||||
return (DW_DLV_OK); | return (DW_DLV_OK); | ||||
} else { | } else { | ||||
return (DW_DLV_NO_ENTRY); | return (DW_DLV_NO_ENTRY); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
translate(Dwarf_Debug dbg, Elf *e, const char* addrstr) | translate(Dwarf_Debug dbg, Elf *e, const char* addrstr) | ||||
{ | { | ||||
Dwarf_Die die, ret_die; | Dwarf_Die die, ret_die; | ||||
Dwarf_Line *lbuf; | Dwarf_Line *lbuf; | ||||
Dwarf_Error de; | Dwarf_Error de; | ||||
Dwarf_Half tag; | Dwarf_Half tag; | ||||
Dwarf_Unsigned addr, lineno, plineno; | Dwarf_Unsigned addr, lineno, plineno; | ||||
Dwarf_Signed lcount; | Dwarf_Signed lcount; | ||||
Dwarf_Addr lineaddr, plineaddr; | Dwarf_Addr lineaddr, plineaddr; | ||||
struct CU *cu; | struct range *range; | ||||
struct Func *f; | struct Func *f; | ||||
const char *funcname; | const char *funcname; | ||||
char *file, *file0, *pfile; | char *file, *file0, *pfile; | ||||
char demangled[1024]; | char demangled[1024]; | ||||
int ec, i, ret; | int ec, i, ret; | ||||
addr = strtoull(addrstr, NULL, 16); | addr = strtoull(addrstr, NULL, 16); | ||||
addr += section_base; | addr += section_base; | ||||
lineno = 0; | lineno = 0; | ||||
file = unknown; | file = unknown; | ||||
die = NULL; | die = NULL; | ||||
ret = DW_DLV_OK; | ret = DW_DLV_OK; | ||||
cu = culookup(addr); | range = culookup(addr); | ||||
if (cu != NULL) { | if (range != NULL) { | ||||
die = cu->die; | die = range->die; | ||||
dbg = cu->dbg; | dbg = range->dbg; | ||||
goto status_ok; | goto status_ok; | ||||
} | } | ||||
while (true) { | while (true) { | ||||
/* | /* | ||||
* We resume the CU scan from the last place we found a match. | * We resume the CU scan from the last place we found a match. | ||||
* Because when we have 2 sequential addresses, and the second | * Because when we have 2 sequential addresses, and the second | ||||
* one is of the next CU, it is faster to just go to the next CU | * one is of the next CU, it is faster to just go to the next CU | ||||
Show All 22 Lines | while (dwarf_siblingof(dbg, die, &ret_die, &de) == DW_DLV_OK) { | ||||
if (tag == DW_TAG_compile_unit) | if (tag == DW_TAG_compile_unit) | ||||
break; | break; | ||||
} | } | ||||
if (ret_die == NULL) { | if (ret_die == NULL) { | ||||
warnx("could not find DW_TAG_compile_unit die"); | warnx("could not find DW_TAG_compile_unit die"); | ||||
goto next_cu; | goto next_cu; | ||||
} | } | ||||
ret = check_range(dbg, die, addr, &cu); | ret = check_range(dbg, die, addr, &range); | ||||
if (ret == DW_DLV_OK) | if (ret == DW_DLV_OK) | ||||
break; | break; | ||||
if (ret == DW_DLV_ERROR) | if (ret == DW_DLV_ERROR) | ||||
goto out; | goto out; | ||||
next_cu: | next_cu: | ||||
if (die != NULL) { | if (die != NULL) { | ||||
dwarf_dealloc(dbg, die, DW_DLA_DIE); | dwarf_dealloc(dbg, die, DW_DLA_DIE); | ||||
die = NULL; | die = NULL; | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | for (i = 0; i < lcount; i++) { | ||||
plineaddr = lineaddr; | plineaddr = lineaddr; | ||||
plineno = lineno; | plineno = lineno; | ||||
pfile = file; | pfile = file; | ||||
} | } | ||||
out: | out: | ||||
f = NULL; | f = NULL; | ||||
funcname = NULL; | funcname = NULL; | ||||
if (ret == DW_DLV_OK && (func || inlines) && cu != NULL) { | if (ret == DW_DLV_OK && (func || inlines) && range != NULL) { | ||||
if (cu->srcfiles == NULL) | if (range->srcfiles == NULL) | ||||
if (dwarf_srcfiles(die, &cu->srcfiles, &cu->nsrcfiles, | if (dwarf_srcfiles(die, &range->srcfiles, &range->nsrcfiles, | ||||
&de)) | &de)) | ||||
warnx("dwarf_srcfiles: %s", dwarf_errmsg(de)); | warnx("dwarf_srcfiles: %s", dwarf_errmsg(de)); | ||||
if (STAILQ_EMPTY(&cu->funclist)) { | if (STAILQ_EMPTY(&range->funclist)) { | ||||
collect_func(dbg, die, NULL, cu); | collect_func(dbg, range->die, NULL, range); | ||||
Not Done Inline ActionsIs this die -> range->die an incidental fix? emaste: Is this `die` -> `range->die` an incidental fix? | |||||
Not Done Inline ActionsIt would seem so. I don't remember exactly the reason for this change but when translating an address and at label out, range->die will be set to the correct die to use either from a cache lookup or a linear scan. I believe either die and range->die are both ok. tig_freebsdfoundation.org: It would seem so. I don't remember exactly the reason for this change but when translating an… | |||||
die = NULL; | die = NULL; | ||||
} | } | ||||
f = search_func(cu, addr); | f = search_func(range, addr); | ||||
if (f != NULL) | if (f != NULL) | ||||
funcname = f->name; | funcname = f->name; | ||||
} | } | ||||
if (print_addr) { | if (print_addr) { | ||||
if ((ec = gelf_getclass(e)) == ELFCLASSNONE) { | if ((ec = gelf_getclass(e)) == ELFCLASSNONE) { | ||||
warnx("gelf_getclass failed: %s", elf_errmsg(-1)); | warnx("gelf_getclass failed: %s", elf_errmsg(-1)); | ||||
ec = ELFCLASS64; | ec = ELFCLASS64; | ||||
Show All 26 Lines | if (demangle && !elftc_demangle(funcname, demangled, | ||||
else | else | ||||
printf("%s\n", funcname); | printf("%s\n", funcname); | ||||
} | } | ||||
} | } | ||||
(void) printf("%s:%ju\n", base ? basename(file) : file, | (void) printf("%s:%ju\n", base ? basename(file) : file, | ||||
(uintmax_t) lineno); | (uintmax_t) lineno); | ||||
if (ret == DW_DLV_OK && inlines && cu != NULL && | if (ret == DW_DLV_OK && inlines && range != NULL && | ||||
cu->srcfiles != NULL && f != NULL && f->inlined_caller != NULL) | range->srcfiles != NULL && f != NULL && f->inlined_caller != NULL) | ||||
print_inlines(cu, f->inlined_caller, f->call_file, | print_inlines(range, f->inlined_caller, f->call_file, | ||||
f->call_line); | f->call_line); | ||||
} | } | ||||
static void | static void | ||||
find_section_base(const char *exe, Elf *e, const char *section) | find_section_base(const char *exe, Elf *e, const char *section) | ||||
{ | { | ||||
Dwarf_Addr off; | Dwarf_Addr off; | ||||
Elf_Scn *scn; | Elf_Scn *scn; | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | main(int argc, char **argv) | ||||
if (section) | if (section) | ||||
find_section_base(exe, e, section); | find_section_base(exe, e, section); | ||||
else | else | ||||
section_base = 0; | section_base = 0; | ||||
if (argc > 0) | if (argc > 0) | ||||
for (i = 0; i < argc; i++) | for (i = 0; i < argc; i++) | ||||
translate(dbg, e, argv[i]); | translate(dbg, e, argv[i]); | ||||
Done Inline ActionsThere is no harm in calling dwarf_get_aranges() each time you need to use it. libdwarf's implementation first checks to see if it has already initialized the arange array for this particular dbg handle, and simply returns it if so. The first call does the initialization. So this caching that you're doing doesn't really help with anything, I believe. I also don't think it makes sense to print a warning if dwarf_get_aranges() returns DW_DLV_NO_ENTRY. If you get anything other than DW_DLV_OK or DW_DLV_NO_ENTRY it should probably be a fatal error. markj: There is no harm in calling dwarf_get_aranges() each time you need to use it. libdwarf's… | |||||
Done Inline ActionsI see. Moved dwarf_get_aranges to check_labels and when to print warning. tig_freebsdfoundation.org: I see. Moved dwarf_get_aranges to check_labels and when to print warning. | |||||
else { | else { | ||||
setvbuf(stdout, NULL, _IOLBF, 0); | setvbuf(stdout, NULL, _IOLBF, 0); | ||||
while (fgets(line, sizeof(line), stdin) != NULL) | while (fgets(line, sizeof(line), stdin) != NULL) | ||||
translate(dbg, e, line); | translate(dbg, e, line); | ||||
} | } | ||||
dwarf_finish(dbg, &de); | dwarf_finish(dbg, &de); | ||||
(void) elf_end(e); | (void) elf_end(e); | ||||
exit(0); | exit(0); | ||||
} | } |
This s/CU/range/ is just for clarity? I'm curious about reordering so die and dbg are at the top now. (Sorry if I missed something in the earlier discussion.)