diff options
Diffstat (limited to 'libdw/dwarf_getfuncs.c')
-rw-r--r-- | libdw/dwarf_getfuncs.c | 96 |
1 files changed, 70 insertions, 26 deletions
diff --git a/libdw/dwarf_getfuncs.c b/libdw/dwarf_getfuncs.c index afc5b6eb..87e0341a 100644 --- a/libdw/dwarf_getfuncs.c +++ b/libdw/dwarf_getfuncs.c @@ -1,5 +1,5 @@ /* Get function information. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005, 2013 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2005. @@ -35,6 +35,63 @@ #include "libdwP.h" +struct visitor_info +{ + /* The user callback of dwarf_getfuncs. */ + int (*callback) (Dwarf_Die *, void *); + + /* The user arg value to dwarf_getfuncs. */ + void *arg; + + /* The DIE offset where to (re)start the search. Zero for all. */ + Dwarf_Off start_offset; + + /* Last subprogram DIE offset seen. */ + Dwarf_Off last_offset; + + /* The CU only contains C functions. Allows pruning of most subtrees. */ + bool c_cu; +}; + +static int +tree_visitor (unsigned int depth __attribute__ ((unused)), + struct Dwarf_Die_Chain *chain, void *arg) +{ + struct visitor_info *const v = arg; + Dwarf_Die *die = &chain->die; + Dwarf_Off start_offset = v->start_offset; + Dwarf_Off die_offset = INTUSE(dwarf_dieoffset) (die); + + /* Pure C CUs can only contain defining subprogram DIEs as direct + children of the CU DIE or as nested function inside a normal C + code constructs. */ + int tag = INTUSE(dwarf_tag) (die); + if (v->c_cu + && tag != DW_TAG_subprogram + && tag != DW_TAG_lexical_block + && tag != DW_TAG_inlined_subroutine) + { + chain->prune = true; + return DWARF_CB_OK; + } + + /* Skip all DIEs till we found the (re)start offset. */ + if (start_offset != 0) + { + if (die_offset == start_offset) + v->start_offset = 0; + return DWARF_CB_OK; + } + + /* If this isn't a (defining) subprogram entity, skip DIE. */ + if (tag != DW_TAG_subprogram + || INTUSE(dwarf_hasattr) (die, DW_AT_declaration)) + return DWARF_CB_OK; + + v->last_offset = die_offset; + return (*v->callback) (die, v->arg); +} + ptrdiff_t dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *), void *arg, ptrdiff_t offset) @@ -43,31 +100,18 @@ dwarf_getfuncs (Dwarf_Die *cudie, int (*callback) (Dwarf_Die *, void *), || INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit)) return -1; - Dwarf_Die die_mem; - Dwarf_Die *die; + int lang = INTUSE(dwarf_srclang) (cudie); + bool c_cu = (lang == DW_LANG_C89 + || lang == DW_LANG_C + || lang == DW_LANG_C99); - int res; - if (offset == 0) - res = INTUSE(dwarf_child) (cudie, &die_mem); - else - { - die = INTUSE(dwarf_offdie) (cudie->cu->dbg, offset, &die_mem); - res = INTUSE(dwarf_siblingof) (die, &die_mem); - } - die = res != 0 ? NULL : &die_mem; + struct visitor_info v = { callback, arg, offset, 0, c_cu }; + struct Dwarf_Die_Chain chain = { .die = CUDIE (cudie->cu), + .parent = NULL }; + int res = __libdw_visit_scopes (0, &chain, &tree_visitor, NULL, &v); - while (die != NULL) - { - if (INTUSE(dwarf_tag) (die) == DW_TAG_subprogram) - { - if (callback (die, arg) != DWARF_CB_OK) - return INTUSE(dwarf_dieoffset) (die); - } - - if (INTUSE(dwarf_siblingof) (die, &die_mem) != 0) - break; - } - - /* That's all. */ - return 0; + if (res == DWARF_CB_ABORT) + return v.last_offset; + else + return res; } |