On Fri, Sep 20, 2013 at 06:49:49PM -0700, Josh Stone wrote: > But this also > shows that the new dwarf_getfuncs slowed down about .5s either way, with > no benefit.
On irc Tom Tromey suggested to trust the CU language to win back most speed for programs mainly written in C (like the kernel). The attached patch does that. With this variant of the patch you get all correctness benefits (imported/partial unit support and detection of nested functions in C code, plus detection of defining subprograms inside other constructs for other langauges), but with a slowdown of < 0.1s in this particular case. Cheers, Mark
>From 798732b08d07b4419e97a3e267165cb930cd005b Mon Sep 17 00:00:00 2001 From: Mark Wielaard <[email protected]> Date: Fri, 20 Sep 2013 09:50:42 -0400 Subject: [PATCH] libdw: Make dwarf_getfuncs find all (defining) DW_TAG_subprogram DIEs. dwarf_getfuncs used to return only the DW_TAG_subprogram DIEs that were direct children of the given CU. This is normally how GCC outputs the subprogram DIEs. But not always. For nested functions the subprogram DIE is placed under the code construct DIE where it is nested. Other compilers might output the defining subprogram DIE of a C++ class function under the DW_TAG_namespace DIE where it was defined. Both such constructs seem allowed by the DWARF specification. So just searching the CU DIE children was wrong. To find all (defining) subprogram DIEs in a CU dwarf_getfuncs should use __libdw_visit_scopes to walk the tree for all DIEs that can contain subprograms as children. The only tricky part is making sure the offset returned and used when the callback returns DWARF_CB_ABORT is correct and the search continues at the right spot in the CU DIE tree. This operation now needs to rewalk the whole tree. Two new testcases were added that fail without this patch. And the allfcts test was tweaked so that it always returns DWARF_CB_ABORT from its callback to make sure the offset handling is correct. Signed-off-by: Mark Wielaard <[email protected]> --- libdw/ChangeLog | 7 +++ libdw/dwarf_getfuncs.c | 96 +++++++++++++++++++++++++++++----------- libdw/libdw.h | 11 ++++- tests/ChangeLog | 10 +++++ tests/Makefile.am | 1 + tests/allfcts.c | 12 +++-- tests/run-allfcts.sh | 56 ++++++++++++++++++++++- tests/testfile_class_func.bz2 | Bin 0 -> 2962 bytes tests/testfile_nested_funcs.bz2 | Bin 0 -> 3045 bytes 9 files changed, 162 insertions(+), 31 deletions(-) create mode 100755 tests/testfile_class_func.bz2 create mode 100755 tests/testfile_nested_funcs.bz2 diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 1a85194..8e1dd92 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,10 @@ +2013-09-20 Mark Wielaard <[email protected]> + + * 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 <[email protected]> * 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 afc5b6e..87e0341 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 <[email protected]>, 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 d1cd177..0d94c52 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 9ea285f..34cffd4 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,13 @@ +2013-09-20 Mark Wielaard <[email protected]> + + * 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 <[email protected]> * Makefile.am (check_PROGRAMS): Add varlocs. diff --git a/tests/Makefile.am b/tests/Makefile.am index e06d914..58db6c3 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 f14b493..7803722 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 30f7dd4..6eaf13c 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 <[email protected]>, 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 0000000000000000000000000000000000000000..e40dcf2623637a709110115e013273e3e8277eaa GIT binary patch literal 2962 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 I0;uKJAjNljEdT%j literal 0 HcmV?d00001 diff --git a/tests/testfile_nested_funcs.bz2 b/tests/testfile_nested_funcs.bz2 new file mode 100755 index 0000000000000000000000000000000000000000..d36b603ebd25ddd53367909dc7512b05930fdd6b GIT binary patch literal 3045 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 literal 0 HcmV?d00001 -- 1.8.3.1
