Changeset View
Standalone View
contrib/elftoolchain/addr2line/addr2line.c
Show First 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | static struct option longopts[] = { | ||||
{"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; | ||||
static struct CU *culist; | static struct CU *culist; | ||||
static Dwarf_Unsigned locache = ~0ULL, hicache; | |||||
static Dwarf_Die last_die = NULL; | |||||
#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 303 Lines • ▼ Show 20 Lines | translate(Dwarf_Debug dbg, Elf *e, const char* addrstr) | ||||
addr = strtoull(addrstr, NULL, 16); | addr = strtoull(addrstr, NULL, 16); | ||||
addr += section_base; | addr += section_base; | ||||
lineno = 0; | lineno = 0; | ||||
file = unknown; | file = unknown; | ||||
cu = NULL; | cu = NULL; | ||||
die = NULL; | die = NULL; | ||||
while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, | |||||
&de)) == DW_DLV_OK) { | if (addr >= locache && addr < hicache && last_die != NULL) { | ||||
markj: Shouldn't it be last_die != NULL? | |||||
die = last_die; | |||||
goto status_ok; | |||||
} | |||||
Not Done Inline ActionsHow do we know that last_die is valid here? Suppose addr2line is given two addresses as input. The first address is valid, and results in last_die being set. The second address is invalid (i.e., doesn't match any CU). When we process the first address, we will cache the DIE for its CU. When processing the second address, we will free last_die and set it to NULL, but locache and hicache are still left with their old values. So, when processing a third address, we may goto status_ok even though last_die is NULL. markj: How do we know that last_die is valid here? Suppose addr2line is given two addresses as input. | |||||
Not Done Inline ActionsI think we can remove the "else if" case since we changed next_cu to not invalidate cache until cache insertion. Next_cu now frees die if die is not NULL but die is always NULL in the "else if" case. tig_freebsdfoundation.org: I think we can remove the "else if" case since we changed next_cu to not invalidate cache until… | |||||
while (true) { | |||||
/* | |||||
* We resume the CU scan from the last place we found a match. 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 instead of starting from the beginning. | |||||
*/ | |||||
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, | |||||
&de); | |||||
Done Inline ActionsThe indentation here is still wrong. markj: The indentation here is still wrong. | |||||
if (ret == DW_DLV_NO_ENTRY) { | |||||
if (locache == ~0ULL) { | |||||
goto out; | |||||
Not Done Inline ActionsAs I understand it, this means that on a cache miss, we will resume the CU scan from the last place where we found a match. Is there any specific reason to do it that way? Why not always restart the search from the beginning on a cache miss. markj: As I understand it, this means that on a cache miss, we will resume the CU scan from the last… | |||||
Not Done Inline ActionsBecause 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 instead of starting from the beginning. tig_freebsdfoundation.org: Because when we have 2 sequential addresses, and the second one is of the next CU, it is faster… | |||||
Not Done Inline ActionsI still can't see the reason for this. markj: I still can't see the reason for this. | |||||
Done Inline ActionsFrom My understanding: tig_freebsdfoundation.org: From My understanding:
CU1: ffff0000-ffff0100
...
CU10: ffff1000 - ffff1100
CU11: ffff1100… | |||||
Not Done Inline ActionsGot it. You might consider adding a short comment above the goto next_cu to explain this. markj: Got it. You might consider adding a short comment above the goto next_cu to explain this. | |||||
} | |||||
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, | |||||
&de); | |||||
} | |||||
die = NULL; | die = NULL; | ||||
while (dwarf_siblingof(dbg, die, &ret_die, &de) == DW_DLV_OK) { | while (dwarf_siblingof(dbg, die, &ret_die, &de) == DW_DLV_OK) { | ||||
if (die != NULL) | if (die != NULL) | ||||
dwarf_dealloc(dbg, die, DW_DLA_DIE); | dwarf_dealloc(dbg, die, DW_DLA_DIE); | ||||
die = ret_die; | die = ret_die; | ||||
if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { | if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) { | ||||
warnx("dwarf_tag failed: %s", | warnx("dwarf_tag failed: %s", | ||||
dwarf_errmsg(de)); | dwarf_errmsg(de)); | ||||
goto next_cu; | goto next_cu; | ||||
} | } | ||||
/* XXX: What about DW_TAG_partial_unit? */ | /* XXX: What about DW_TAG_partial_unit? */ | ||||
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; | ||||
} | } | ||||
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == | if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == | ||||
DW_DLV_OK) { | DW_DLV_OK) { | ||||
if (lopc == locache) { | |||||
goto out; | |||||
} | |||||
Not Done Inline ActionsCan you explain this check? I don't quite see why it's there. markj: Can you explain this check? I don't quite see why it's there. | |||||
Not Done Inline ActionsThis is if we scanned across all the CU's and get back to where we started, we want to go to out because we cannot translate this address. tig_freebsdfoundation.org: This is if we scanned across all the CU's and get back to where we started, we want to go to… | |||||
Not Done Inline ActionsI see. But if the cache is invalid, this check will fail. markj: I see. But if the cache is invalid, this check will fail. | |||||
Not Done Inline ActionsBut I think we will only get here when cache is valid. If the cache is invalid it will keep looping until it finds the die addr lies in. tig_freebsdfoundation.org: But I think we will only get here when cache is valid. If the cache is invalid it will keep… | |||||
Not Done Inline ActionsOnly need this check** tig_freebsdfoundation.org: Only need this check**
| |||||
Not Done Inline ActionsOnly need this check on a cache miss. tig_freebsdfoundation.org: Only need this check on a cache miss. | |||||
Not Done Inline ActionsSorry, I was not specific enough. If the cache is invalid *and* the address is invalid, we will never exit the loop. markj: Sorry, I was not specific enough. If the cache is invalid *and* the address is invalid, we will… | |||||
Done Inline ActionsI think initializing locache and checking locache is not initial value after we scan all CU's should fix it. tig_freebsdfoundation.org: I think initializing locache and checking locache is not initial value after we scan all CU's… | |||||
if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, | if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, | ||||
&de) == DW_DLV_OK) { | &de) == DW_DLV_OK) { | ||||
/* | /* | ||||
* Check if the address falls into the PC | * Check if the address falls into the PC | ||||
* range of this CU. | * range of this CU. | ||||
*/ | */ | ||||
if (handle_high_pc(die, lopc, &hipc) != | if (handle_high_pc(die, lopc, &hipc) != | ||||
DW_DLV_OK) | DW_DLV_OK) | ||||
Show All 23 Lines | if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) == | ||||
HASH_ADD(hh, culist, off, sizeof(off), cu); | HASH_ADD(hh, culist, off, sizeof(off), cu); | ||||
} | } | ||||
if (addr >= lopc && addr < hipc) | if (addr >= lopc && addr < hipc) | ||||
break; | break; | ||||
} | } | ||||
next_cu: | next_cu: | ||||
if (die != NULL) { | if (die != NULL && die != last_die) { | ||||
dwarf_dealloc(dbg, die, DW_DLA_DIE); | dwarf_dealloc(dbg, die, DW_DLA_DIE); | ||||
die = NULL; | die = NULL; | ||||
} | } | ||||
} | } | ||||
if (ret != DW_DLV_OK || die == NULL) | if (ret != DW_DLV_OK || die == NULL) | ||||
goto out; | goto out; | ||||
Done Inline ActionsWhy not replace all instances of "goto not_found" with "goto out"? markj: Why not replace all instances of "goto not_found" with "goto out"? | |||||
locache = lopc; | |||||
hicache = hipc; | |||||
if (last_die != NULL) { | |||||
Done Inline ActionsI think it would be clearer if cache invalidation and cache insertion were done in the same place. That is, it is kind of confusing that last_die is freed above. Instead, you could keep last_die valid in the cache until a new match is found, instead of invalidating as soon as there is a cache miss. I think this should result in fewer gotos and make the control flow easier to follow. markj: I think it would be clearer if cache invalidation and cache insertion were done in the same… | |||||
dwarf_dealloc(dbg, last_die, DW_DLA_DIE); | |||||
last_die = NULL; | |||||
} | |||||
last_die = die; | |||||
status_ok: | |||||
switch (dwarf_srclines(die, &lbuf, &lcount, &de)) { | switch (dwarf_srclines(die, &lbuf, &lcount, &de)) { | ||||
case DW_DLV_OK: | case DW_DLV_OK: | ||||
break; | break; | ||||
case DW_DLV_NO_ENTRY: | case DW_DLV_NO_ENTRY: | ||||
/* If a CU lacks debug info, just skip it. */ | /* If a CU lacks debug info, just skip it. */ | ||||
goto out; | goto out; | ||||
default: | default: | ||||
warnx("dwarf_srclines: %s", dwarf_errmsg(de)); | warnx("dwarf_srclines: %s", dwarf_errmsg(de)); | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | out: | ||||
} | } | ||||
(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 && cu != NULL && | ||||
cu->srcfiles != NULL && f != NULL && f->inlined_caller != NULL) | cu->srcfiles != NULL && f != NULL && f->inlined_caller != NULL) | ||||
print_inlines(cu, f->inlined_caller, f->call_file, | print_inlines(cu, f->inlined_caller, f->call_file, | ||||
f->call_line); | f->call_line); | ||||
if (die != NULL) | |||||
dwarf_dealloc(dbg, die, DW_DLA_DIE); | |||||
/* | |||||
* Reset internal CU pointer, so we will start from the first CU | |||||
* next round. | |||||
*/ | |||||
while (ret != DW_DLV_NO_ENTRY) { | |||||
if (ret == DW_DLV_ERROR) | |||||
errx(EXIT_FAILURE, "dwarf_next_cu_header: %s", | |||||
dwarf_errmsg(de)); | |||||
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL, | |||||
&de); | |||||
} | } | ||||
Not Done Inline ActionsIs it possible to have die != last_die here? markj: Is it possible to have die != last_die here? | |||||
Not Done Inline ActionsI guess DIE always equals last_die tig_freebsdfoundation.org: I guess DIE always equals last_die | |||||
} | |||||
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; | ||||
GElf_Ehdr eh; | GElf_Ehdr eh; | ||||
GElf_Shdr sh; | GElf_Shdr sh; | ||||
▲ Show 20 Lines • Show All 133 Lines • ▼ Show 20 Lines | main(int argc, char **argv) | ||||
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]); | ||||
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); | ||||
} | |||||
if (last_die != NULL) { | |||||
dwarf_dealloc(dbg, last_die, DW_DLA_DIE); | |||||
} | } | ||||
dwarf_finish(dbg, &de); | dwarf_finish(dbg, &de); | ||||
(void) elf_end(e); | (void) elf_end(e); | ||||
exit(0); | exit(0); | ||||
} | } |
Shouldn't it be last_die != NULL?