diff options
-rw-r--r-- | libdw/ChangeLog | 7 | ||||
-rw-r--r-- | libdw/dwarf_getfuncs.c | 96 | ||||
-rw-r--r-- | libdw/libdw.h | 11 | ||||
-rw-r--r-- | tests/ChangeLog | 10 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rw-r--r-- | tests/allfcts.c | 12 | ||||
-rwxr-xr-x | tests/run-allfcts.sh | 56 | ||||
-rwxr-xr-x | tests/testfile_class_func.bz2 | bin | 0 -> 2962 bytes | |||
-rwxr-xr-x | tests/testfile_nested_funcs.bz2 | bin | 0 -> 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 Binary files differnew file mode 100755 index 00000000..e40dcf26 --- /dev/null +++ b/tests/testfile_class_func.bz2 diff --git a/tests/testfile_nested_funcs.bz2 b/tests/testfile_nested_funcs.bz2 Binary files differnew file mode 100755 index 00000000..d36b603e --- /dev/null +++ b/tests/testfile_nested_funcs.bz2 |