summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdw/ChangeLog7
-rw-r--r--libdw/dwarf_getfuncs.c96
-rw-r--r--libdw/libdw.h11
-rw-r--r--tests/ChangeLog10
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/allfcts.c12
-rwxr-xr-xtests/run-allfcts.sh56
-rwxr-xr-xtests/testfile_class_func.bz2bin0 -> 2962 bytes
-rwxr-xr-xtests/testfile_nested_funcs.bz2bin0 -> 3045 bytes
9 files changed, 162 insertions, 31 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index 1a851948..8e1dd92c 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -1,3 +1,10 @@
+2013-09-20 Mark Wielaard <mjw@redhat.com>
+
+ * dwarf_getfuncs.c (visitor_info): New struct.
+ (tree_visitor): New function.
+ (dwarf_getfuncs): Use __libdw_visit_scopes with tree_visitor.
+ * libdw.h (dwarf_getfuncs): Expand function documentation.
+
2013-09-12 Mark Wielaard <mjw@redhat.com>
* fde.c (intern_fde): Free fde and set libdw errno when start
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;
}
diff --git a/libdw/libdw.h b/libdw/libdw.h
index d1cd1775..0d94c526 100644
--- a/libdw/libdw.h
+++ b/libdw/libdw.h
@@ -747,7 +747,16 @@ extern Dwarf_Arange *dwarf_getarange_addr (Dwarf_Aranges *aranges,
-/* Get functions in CUDIE. */
+/* Get functions in CUDIE. The given callback will be called for all
+ defining DW_TAG_subprograms in the CU DIE tree. If the callback
+ returns DWARF_CB_ABORT the return value can be used as offset argument
+ to resume the function to find all remaining functions (this is not
+ really recommended, since it needs to rewalk the CU DIE tree first till
+ that offset is found again). If the callback returns DWARF_CB_OK
+ dwarf_getfuncs will not return but keep calling the callback for each
+ function DIE it finds. Pass zero for offset on the first call to walk
+ the full CU DIE tree. If no more functions can be found and the callback
+ returned DWARF_CB_OK then the function returns zero. */
extern ptrdiff_t dwarf_getfuncs (Dwarf_Die *cudie,
int (*callback) (Dwarf_Die *, void *),
void *arg, ptrdiff_t offset);
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 9ea285f7..34cffd42 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,13 @@
+2013-09-20 Mark Wielaard <mjw@redhat.com>
+
+ * allfcts.c (cb): Return DWARF_CB_ABORT.
+ (main): Iterate over all offsets returned by dwarf_getfuncs.
+ * run-allfcts.sh: Add nested_funcs and class_func testcases.
+ * testfile_nested_funcs.bz2: New test file.
+ * testfile_class_func.bz2: Likewise.
+ * Makefile.am (EXTRA_DIST): Add testfile_class_func.bz2 and
+ testfile_nested_funcs.bz2.
+
2013-08-30 Mark Wielaard <mjw@redhat.com>
* Makefile.am (check_PROGRAMS): Add varlocs.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e06d914d..58db6c36 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -119,6 +119,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
testfile5.bz2 testfile6.bz2 testfile7.bz2 testfile8.bz2 \
testfile9.bz2 testfile10.bz2 testfile11.bz2 testfile12.bz2 \
testfile13.bz2 run-strip-test3.sh run-allfcts.sh \
+ testfile_class_func.bz2 testfile_nested_funcs.bz2 \
run-line2addr.sh run-elflint-test.sh testfile14.bz2 \
run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \
run-strip-test7.sh run-strip-test8.sh run-strip-groups.sh \
diff --git a/tests/allfcts.c b/tests/allfcts.c
index f14b4935..7803722f 100644
--- a/tests/allfcts.c
+++ b/tests/allfcts.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005 Red Hat, Inc.
+/* Copyright (C) 2005, 2013 Red Hat, Inc.
This file is part of elfutils.
This file is free software; you can redistribute it and/or modify
@@ -34,7 +34,7 @@ cb (Dwarf_Die *func, void *arg __attribute__ ((unused)))
printf ("%s:%d:%s\n", file, line, fct);
- return DWARF_CB_OK;
+ return DWARF_CB_ABORT;
}
@@ -57,7 +57,13 @@ main (int argc, char *argv[])
Dwarf_Die die_mem;
Dwarf_Die *die = dwarf_offdie (dbg, off + cuhl, &die_mem);
- (void) dwarf_getfuncs (die, cb, NULL, 0);
+ /* Explicitly stop in the callback and then resume each time. */
+ ptrdiff_t doff = 0;
+ do
+ {
+ doff = dwarf_getfuncs (die, cb, NULL, doff);
+ }
+ while (doff > 0);
off = noff;
}
diff --git a/tests/run-allfcts.sh b/tests/run-allfcts.sh
index 30f7dd4d..6eaf13c8 100755
--- a/tests/run-allfcts.sh
+++ b/tests/run-allfcts.sh
@@ -1,5 +1,5 @@
#! /bin/sh
-# 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.
#
@@ -37,4 +37,58 @@ testrun_compare ${abs_builddir}/allfcts testfile testfile2 testfile8 <<\EOF
/home/drepper/gnu/elfutils/build/src/../../src/strip.c:313:handle_elf
EOF
+# = nested_funcs.c =
+#
+# static int
+# foo (int x)
+# {
+# int bar (int y)
+# {
+# return x - y;
+# }
+#
+# return bar (x * 2);
+# }
+#
+# int
+# main (int argc, char ** argv)
+# {
+# return foo (argc);
+# }
+#
+# gcc -g -o nested_funcs nested_funcs.c
+
+# = class_func.cxx =
+#
+# namespace foobar
+# {
+# class Foo
+# {
+# public:
+# int bar(int x);
+# };
+#
+# int Foo::bar(int x) { return x - 42; }
+# };
+#
+# int
+# main (int argc, char **argv)
+# {
+# foobar::Foo foo;
+#
+# return foo.bar (42);
+# }
+#
+# clang++ -g -o class_func class_func.cxx
+
+testfiles testfile_nested_funcs testfile_class_func
+
+testrun_compare ${abs_builddir}/allfcts testfile_nested_funcs testfile_class_func <<\EOF
+/home/mark/src/tests/nested/nested_funcs.c:2:foo
+/home/mark/src/tests/nested/nested_funcs.c:4:bar
+/home/mark/src/tests/nested/nested_funcs.c:13:main
+/home/mark/src/tests/nested/class_func.cxx:6:bar
+/home/mark/src/tests/nested/class_func.cxx:13:main
+EOF
+
exit 0
diff --git a/tests/testfile_class_func.bz2 b/tests/testfile_class_func.bz2
new file mode 100755
index 00000000..e40dcf26
--- /dev/null
+++ b/tests/testfile_class_func.bz2
Binary files differ
diff --git a/tests/testfile_nested_funcs.bz2 b/tests/testfile_nested_funcs.bz2
new file mode 100755
index 00000000..d36b603e
--- /dev/null
+++ b/tests/testfile_nested_funcs.bz2
Binary files differ