summaryrefslogtreecommitdiffstats
path: root/libdw/dwarf_getfuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/dwarf_getfuncs.c')
-rw-r--r--libdw/dwarf_getfuncs.c96
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;
}