From 71e15a01742e5d5de9c6260d4526146be54e5a8a Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sat, 27 Aug 2005 10:33:26 +0000 Subject: [PATCH] libdw/ 2005-08-27 Roland McGrath * dwarf_getscopes.c (dwarf_getscopes): Rewritten using __libdw_visit_scopes. * dwarf_getscopes_die.c: New file. * Makefile.am (libdw_a_SOURCES): Add it. * libdw.h: Declare dwarf_getscopes_die. * libdw.map: Bump to 0.115 and add it. * libdw_visit_scopes.c (__libdw_visit_scopes): Pass a struct containing a DIE and its parent pointer, instead of just Dwarf_Die. Take two functions for both preorder and postorder visitors. * libdwP.h: Update decl. (struct Dwarf_Die_Chain): New type. * dwarf_func_inline.c: Update uses. * dwarf_diename.c (dwarf_diename): Use dwarf_attr_integrate. Add INTDEF. * libdwP.h: Add INTDECL. * dwarf_func_name.c (dwarf_func_name): Use dwarf_diename. src/ 2005-08-27 Roland McGrath * addr2line.c (dwarf_diename_integrate): Function removed. (print_dwarf_function): Use plain dwarf_diename. tests/ 2005-08-27 Roland McGrath * run-funcscopes.sh: New file. * testfile25.bz2: New data file. * Makefile.am (TESTS, EXTRA_DIST): Add them. 2005-08-26 Roland McGrath * addrscopes.c (dwarf_diename_integrate): Removed. (print_vars, handle_address): Use plain dwarf_diename. 2005-08-25 Roland McGrath * funcscopes.c: New file. * Makefile.am (noinst_PROGRAMS): Add it. (funcscopes_LDADD): New variable. * run-addrscopes.sh: Add another case. * testfile24.bz2: New data file. * Makefile.am (EXTRA_DIST): Add it. * addrscopes.c (handle_address): Take new argument IGNORE_INLINES, pass it to dwarf_getscopes. (main): Pass it, true when '=' follows an address. --- libdw/ChangeLog | 22 +++ libdw/Makefile.am | 2 +- libdw/dwarf_diename.c | 6 +- libdw/dwarf_func_inline.c | 11 +- libdw/dwarf_func_name.c | 5 +- libdw/dwarf_getscopes.c | 383 ++++++++++-------------------------- libdw/dwarf_getscopes_die.c | 69 +++++++ libdw/libdw.h | 9 + libdw/libdw.map | 3 +- libdw/libdwP.h | 18 +- libdw/libdw_visit_scopes.c | 93 +++++---- src/ChangeLog | 5 + src/addr2line.c | 11 +- tests/ChangeLog | 25 +++ tests/Makefile.am | 9 +- tests/addrscopes.c | 13 +- tests/funcscopes.c | 192 ++++++++++++++++++ tests/run-addrscopes.sh | 18 ++ tests/run-funcscopes.sh | 30 +++ tests/testfile24.bz2 | Bin 0 -> 2644 bytes tests/testfile25.bz2 | Bin 0 -> 2575 bytes 21 files changed, 569 insertions(+), 355 deletions(-) create mode 100644 libdw/dwarf_getscopes_die.c create mode 100644 tests/funcscopes.c create mode 100644 tests/run-funcscopes.sh create mode 100644 tests/testfile24.bz2 create mode 100644 tests/testfile25.bz2 diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 3b84ab9e6..fcae44223 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,25 @@ +2005-08-27 Roland McGrath + + * dwarf_getscopes.c (dwarf_getscopes): Rewritten using + __libdw_visit_scopes. + + * dwarf_getscopes_die.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * libdw.h: Declare dwarf_getscopes_die. + * libdw.map: Bump to 0.115 and add it. + + * libdw_visit_scopes.c (__libdw_visit_scopes): Pass a struct + containing a DIE and its parent pointer, instead of just Dwarf_Die. + Take two functions for both preorder and postorder visitors. + * libdwP.h: Update decl. + (struct Dwarf_Die_Chain): New type. + * dwarf_func_inline.c: Update uses. + + * dwarf_diename.c (dwarf_diename): Use dwarf_attr_integrate. + Add INTDEF. + * libdwP.h: Add INTDECL. + * dwarf_func_name.c (dwarf_func_name): Use dwarf_diename. + 2005-08-23 Roland McGrath * dwarf_attr_integrate.c (dwarf_attr_integrate): Treat diff --git a/libdw/Makefile.am b/libdw/Makefile.am index ad6b28a16..264e47842 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -48,7 +48,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_getabbrevcode.c dwarf_abbrevhaschildren.c \ dwarf_getattrcnt.c dwarf_getabbrevattr.c \ dwarf_getsrclines.c dwarf_getsrc_die.c \ - dwarf_getscopes.c dwarf_getscopevar.c \ + dwarf_getscopes.c dwarf_getscopes_die.c dwarf_getscopevar.c \ dwarf_linesrc.c dwarf_lineno.c dwarf_lineaddr.c \ dwarf_linecol.c dwarf_linebeginstatement.c \ dwarf_lineendsequence.c dwarf_lineblock.c \ diff --git a/libdw/dwarf_diename.c b/libdw/dwarf_diename.c index 41d763c54..daef5fbd1 100644 --- a/libdw/dwarf_diename.c +++ b/libdw/dwarf_diename.c @@ -26,6 +26,8 @@ dwarf_diename (die) { Dwarf_Attribute attr_mem; - return INTUSE(dwarf_formstring) (INTUSE(dwarf_attr) (die, DW_AT_name, - &attr_mem)); + return INTUSE(dwarf_formstring) (INTUSE(dwarf_attr_integrate) (die, + DW_AT_name, + &attr_mem)); } +INTDEF (dwarf_diename) diff --git a/libdw/dwarf_func_inline.c b/libdw/dwarf_func_inline.c index 7656286fc..4472515d3 100644 --- a/libdw/dwarf_func_inline.c +++ b/libdw/dwarf_func_inline.c @@ -14,15 +14,15 @@ struct visitor_info static int scope_visitor (unsigned int depth __attribute__ ((unused)), - Dwarf_Die *die, void *arg) + struct Dwarf_Die_Chain *die, void *arg) { struct visitor_info *const v = arg; - if (INTUSE(dwarf_tag) (die) != DW_TAG_inlined_subroutine) + if (INTUSE(dwarf_tag) (&die->die) != DW_TAG_inlined_subroutine) return DWARF_CB_OK; Dwarf_Attribute attr_mem; - Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_abstract_origin, + Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&die->die, DW_AT_abstract_origin, &attr_mem); if (attr == NULL) return DWARF_CB_OK; @@ -35,7 +35,7 @@ scope_visitor (unsigned int depth __attribute__ ((unused)), if (origin->addr != v->die_addr) return DWARF_CB_OK; - return (*v->callback) (die, v->arg); + return (*v->callback) (&die->die, v->arg); } int @@ -68,5 +68,6 @@ dwarf_func_inline_instances (Dwarf_Func *func, void *arg) { struct visitor_info v = { func->die->addr, callback, arg }; - return __libdw_visit_scopes (0, func->cudie, &scope_visitor, &v); + struct Dwarf_Die_Chain cu = { .die = *func->cudie, .parent = NULL }; + return __libdw_visit_scopes (0, &cu, &scope_visitor, NULL, &v); } diff --git a/libdw/dwarf_func_name.c b/libdw/dwarf_func_name.c index 4151c359a..c6912aa65 100644 --- a/libdw/dwarf_func_name.c +++ b/libdw/dwarf_func_name.c @@ -23,8 +23,5 @@ const char * dwarf_func_name (Dwarf_Func *func) { - Dwarf_Attribute attr_mem; - - return INTUSE(dwarf_formstring) (INTUSE(dwarf_attr) (func->die, DW_AT_name, - &attr_mem)); + return INTUSE(dwarf_diename) (func->die); } diff --git a/libdw/dwarf_getscopes.c b/libdw/dwarf_getscopes.c index 7863036b2..2d5345ed7 100644 --- a/libdw/dwarf_getscopes.c +++ b/libdw/dwarf_getscopes.c @@ -15,279 +15,131 @@ # include #endif +#include #include #include "libdwP.h" #include -enum die_class { ignore, match, match_inline, walk, imported }; +struct args +{ + Dwarf_Addr pc; + Dwarf_Die *scopes; + unsigned int inlined, nscopes; + Dwarf_Die inlined_origin; +}; -static enum die_class -classify_die (Dwarf_Die *die) +/* Preorder visitor: prune the traversal if this DIE does not contain PC. */ +static int +pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) { - switch (INTUSE(dwarf_tag) (die)) - { - /* DIEs with addresses we can try to match. */ - case DW_TAG_compile_unit: - case DW_TAG_module: - case DW_TAG_lexical_block: - case DW_TAG_with_stmt: - case DW_TAG_catch_block: - case DW_TAG_try_block: - case DW_TAG_entry_point: - return match; - case DW_TAG_inlined_subroutine: - return match_inline; - case DW_TAG_subprogram: - /* This might be a concrete out-of-line instance of an inline, in - which case it is not guaranteed to be owned by the right scope and - we will search for its origin as for DW_TAG_inlined_subroutine. */ - return (INTUSE(dwarf_hasattr) (die, DW_AT_abstract_origin) - ? match_inline : match); - - /* DIEs without addresses that can own DIEs with addresses. */ - case DW_TAG_namespace: - return walk; - - /* Special indirection required. */ - case DW_TAG_imported_unit: - return imported; - - /* Other DIEs we have no reason to descend. */ - default: - break; - } - return ignore; + struct args *a = arg; + + if (a->scopes != NULL || INTUSE(dwarf_haspc) (&die->die, a->pc) <= 0) + die->prune = true; + else if (INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine) + a->inlined = depth; + + return 0; } -/* DIE contains PC. Find its child that contains PC. Returns -1 for - errors, 0 for no matches. On success, *SCOPES gets the malloc'd array - of containing scopes. A positive return value is the number of those - scopes. A return value < -1 is -1 - number of those scopes, when the - outermost scope is a concrete instance of an inline subroutine. */ +/* Preorder visitor for second partial traversal after finding a + concrete inlined instance. */ static int -find_pc (unsigned int depth, Dwarf_Die *die, Dwarf_Addr pc, Dwarf_Die **scopes) +origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) { - Dwarf_Die child; - if (INTUSE(dwarf_child) (die, &child) != 0) - return -1; + struct args *a = arg; + + if (die->die.addr != a->inlined_origin.addr) + return 0; - /* Recurse on this DIE to search within its children. - Return nonzero if this gets an error or a final result. */ - inline int search_child (void) + /* We have a winner! This is the abstract definition of the inline + function of which A->scopes[A->nscopes - 1] is a concrete instance. + */ + + unsigned int nscopes = a->nscopes + depth; + Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]); + if (scopes == NULL) { - int n = find_pc (depth + 1, &child, pc, scopes); - if (n > 0) - /* That stored the N innermost scopes. Now store ours. */ - (*scopes)[n++] = child; - return n; + free (a->scopes); + __libdw_seterrno (DWARF_E_NOMEM); + return -1; } - /* Check each of our child DIEs. */ - enum die_class got = ignore; + a->scopes = scopes; do { - enum die_class child_class = classify_die (&child); - switch (child_class) - { - case match: - case match_inline: - if (INTUSE(dwarf_haspc) (&child, pc) > 0) - break; - continue; - - case walk: - if (INTUSE(dwarf_haschildren) (&child)) - got = walk; - continue; - - case imported: - got = walk; - continue; - - default: - case ignore: - continue; - } - - /* We get here only when the PC has matched. */ - got = child_class; - break; + die = die->parent; + scopes[a->nscopes++] = die->die; } - while (INTUSE(dwarf_siblingof) (&child, &child) == 0); + while (a->nscopes < nscopes); + assert (die->parent == NULL); + return a->nscopes; +} + +/* Postorder visitor: first (innermost) call wins. */ +static int +pc_record (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) +{ + struct args *a = arg; - switch (got) + if (a->scopes == NULL) { - case match: - case match_inline: - /* We have a DIE that matched the PC. */ - if (INTUSE(dwarf_haschildren) (&child)) - { - /* Recurse on this DIE to narrow within its children. - Return now if this gets an error or a final result. */ - int result = search_child (); - if (result < 0 || (got == match && result > 0)) - return result; - if (result > 0) /* got == match_inline */ - /* We have a winner, but CHILD is a concrete inline instance - so DIE and its containing scopes do not actually apply. - DIE is the scope that inlined the function. Our root - caller must find the abstract scope that defines us. */ - return -1 - result; - } + if (die->prune) + return 0; + + /* We have hit the innermost DIE that contains the target PC. */ - /* This DIE has no children containing the PC, so this is it. */ - *scopes = malloc (depth * sizeof (*scopes)[0]); - if (*scopes == NULL) + a->nscopes = depth + 1 - a->inlined; + a->scopes = malloc (a->nscopes * sizeof a->scopes[0]); + if (a->scopes == NULL) { __libdw_seterrno (DWARF_E_NOMEM); return -1; } - (*scopes)[0] = child; - return got == match ? 1 : -2; - case walk: - /* We don't have anything matching the PC, but we have some things - we might descend to find one. Recurse on each of those. */ - if (INTUSE(dwarf_child) (die, &child) != 0) - return -1; - do - switch (classify_die (&child)) - { - case walk: - if (INTUSE(dwarf_haschildren) (&child)) - { - /* Recurse on this DIE to look for the PC within its children. - Return now if this gets an error or a final result. */ - int result = search_child (); - if (result != 0) - return result; - } - break; - - case imported: - { - /* This imports another compilation unit to appear - as part of this one, inside the current scope. - Recurse to search the referenced unit, but without - recording it as an inner scoping level. */ - - Dwarf_Attribute attr_mem; - Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child, DW_AT_import, - &attr_mem); - if (INTUSE(dwarf_formref_die) (attr, &child) != NULL) - { - int result = find_pc (depth, &child, pc, scopes); - if (result != 0) - return result; - } - } - break; - - default: - break; - } - while (INTUSE(dwarf_siblingof) (&child, &child) == 0); - break; - - default: - case ignore: - /* Nothing to see here. */ - break; - } + for (unsigned int i = 0; i < a->nscopes; ++i) + { + a->scopes[i] = die->die; + die = die->parent; + } - /* No matches. */ - return 0; -} + if (a->inlined == 0) + { + assert (die == NULL); + return a->nscopes; + } + /* This is the concrete inlined instance itself. + Record its abstract_origin pointer. */ + Dwarf_Die *const inlinedie = &a->scopes[depth - a->inlined]; -/* OWNER owns OWNED. Find intermediate scopes. *SCOPES was allocated by - find_pc and has SKIP elements. We realloc it, append more containing - scopes, and return 1 + the number appended. Returns -1 on errors, - or 0 when OWNED was not found within OWNER. */ -static int -find_die (unsigned int depth, Dwarf_Die *owner, Dwarf_Die *owned, - Dwarf_Die **scopes, unsigned int skip) -{ - Dwarf_Die child; - if (INTUSE(dwarf_child) (owner, &child) != 0) - return -1; + assert (INTUSE (dwarf_tag) (inlinedie) == DW_TAG_inlined_subroutine); + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = INTUSE (dwarf_attr) (inlinedie, + DW_AT_abstract_origin, + &attr_mem); + if (INTUSE (dwarf_formref_die) (attr, &a->inlined_origin) == NULL) + return -1; + return 0; + } - do - { - if (child.addr == owned->addr) - /* This is the one. OWNER is the innermost owner. */ - return 1; - /* Unfortunately we cannot short-circuit the dead-end paths just by - checking the physical layout to see if OWNED falls within CHILD. - If it doesn't, there may still be a DW_TAG_imported_unit that - refers to its true owner indirectly. */ + /* We've recorded the scopes back to one that is a concrete inlined + instance. Now return out of the traversal back to the scope + containing that instance. */ - switch (classify_die (&child)) - { - case match: - case match_inline: - case walk: - if (INTUSE(dwarf_haschildren) (&child)) - { - /* Recurse on this DIE to look for OWNED within its children. - Return now if this gets an error or a final result. */ - int n = find_die (depth + 1, &child, owned, scopes, skip); - if (n < 0) - return n; - if (n > 1) - { - /* We have a winner. CHILD owns the owner of OWNED. */ - (*scopes)[skip + n - 1] = child; - return n + 1; - } - if (n > 0) /* n == 1 */ - { - /* CHILD is the direct owner of OWNED. */ - Dwarf_Die *nscopes = realloc (*scopes, - (skip + depth) - * sizeof nscopes[0]); - if (nscopes == NULL) - { - free (*scopes); - *scopes = NULL; - __libdw_seterrno (DWARF_E_NOMEM); - return -1; - } - nscopes[skip] = child; - *scopes = nscopes; - return 2; - } - } - break; - - case imported: - { - /* This is imports another compilation unit to appear - as part of this one, inside the current scope. - Recurse to search the referenced unit, but without - recording it as an inner scoping level. */ - - Dwarf_Attribute attr_mem; - Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child, DW_AT_import, - &attr_mem); - if (INTUSE(dwarf_formref_die) (attr, &child) != NULL) - { - int result = find_die (depth, &child, owner, scopes, skip); - if (result != 0) - return result; - } - } - break; - - default: - break; - } - } - while (INTUSE(dwarf_siblingof) (&child, &child) == 0); + assert (a->inlined); + if (depth >= a->inlined) + /* Not there yet. */ + return 0; - return 0; + /* Now we are in a scope that contains the concrete inlined instance. + Search it for the inline function's abstract definition. + If we don't find it, return to search the containing scope. + If we do find it, the nonzero return value will bail us out + of the postorder traversal. */ + return __libdw_visit_scopes (depth, die, &origin_match, NULL, &a); } @@ -297,47 +149,16 @@ dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes) if (cudie == NULL) return -1; - int n = find_pc (2, cudie, pc, scopes); - if (likely (n >= 0)) - { - /* We have a final result. Now store the outermost scope, the CU. */ - (*scopes)[n++] = *cudie; - return n; - } - if (n == -1) - return n; - - /* We have the scopes out to one that is a concrete instance of an - inlined subroutine (usually DW_TAG_inlined_subroutine, but can - be DW_TAG_subprogram for a concrete out-of-line instance). - Now we must find the lexical scopes that contain the - corresponding abstract inline subroutine definition. */ - - n = -n - 1; - - Dwarf_Attribute attr_mem; - Dwarf_Die die_mem; - Dwarf_Die *origin = INTUSE(dwarf_formref_die) - (INTUSE(dwarf_attr) (&(*scopes)[n - 1], DW_AT_abstract_origin, &attr_mem), - &die_mem); - if (unlikely (origin == NULL)) - goto invalid; - - int result = find_die (0, cudie, origin, scopes, n); - if (likely (result > 0)) - { - n = n + result - 1; - (*scopes)[n++] = *cudie; - return n; - } + struct Dwarf_Die_Chain cu = { .parent = NULL, .die = *cudie }; + struct args a = { .pc = pc }; - if (result == 0) /* No match, shouldn't happen. */ - { - invalid: - __libdw_seterrno (DWARF_E_INVALID_DWARF); - } + int result = __libdw_visit_scopes (0, &cu, &pc_match, &pc_record, &a); + + if (result == 0 && a.scopes != NULL) + result = __libdw_visit_scopes (0, &cu, &origin_match, NULL, &a); + + if (result > 0) + *scopes = a.scopes; - free (*scopes); - *scopes = NULL; - return -1; + return result; } diff --git a/libdw/dwarf_getscopes_die.c b/libdw/dwarf_getscopes_die.c new file mode 100644 index 000000000..bdcee3540 --- /dev/null +++ b/libdw/dwarf_getscopes_die.c @@ -0,0 +1,69 @@ +/* Return scope DIEs containing given DIE. + Copyright (C) 2005 Red Hat, Inc. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "libdwP.h" +#include +#include + +static int +scope_visitor (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg) +{ + if (die->die.addr != *(void **) arg) + return 0; + + Dwarf_Die *scopes = malloc (depth * sizeof scopes[0]); + if (scopes == NULL) + { + __libdw_seterrno (DWARF_E_NOMEM); + return -1; + } + + unsigned int i = 0; + do + { + scopes[i++] = die->die; + die = die->parent; + } + while (die != NULL); + assert (i == depth); + + *(void **) arg = scopes; + return depth; +} + +int +dwarf_getscopes_die (Dwarf_Die *die, Dwarf_Die **scopes) +{ + if (die == NULL) + return -1; + + struct Dwarf_Die_Chain cu = + { + .parent = NULL, + .die = + { + .cu = die->cu, + .addr = ((char *) die->cu->dbg->sectiondata[IDX_debug_info]->d_buf + + die->cu->start + 3 * die->cu->offset_size - 4 + 3), + } + }; + + void *info = die->addr; + int result = __libdw_visit_scopes (1, &cu, &scope_visitor, NULL, &info); + if (result > 0) + *scopes = info; + return result; +} diff --git a/libdw/libdw.h b/libdw/libdw.h index e6b305970..350aa440e 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -442,6 +442,15 @@ extern int dwarf_addrloclists (Dwarf_Attribute *attr, Dwarf_Addr address, extern int dwarf_getscopes (Dwarf_Die *cudie, Dwarf_Addr pc, Dwarf_Die **scopes); +/* Return scope DIEs containing the given DIE. + Sets *SCOPES to a malloc'd array of Dwarf_Die structures, + and returns the number of elements in the array. + (*SCOPES)[0] is a copy of DIE. + (*SCOPES)[1] is the DIE for the scope containing that scope, and so on. + Returns -1 for errors or 0 if DIE is not found in any scope entry. */ +extern int dwarf_getscopes_die (Dwarf_Die *die, Dwarf_Die **scopes); + + /* Search SCOPES[0..NSCOPES-1] for a variable called NAME. Ignore the first SKIP_SHADOWS scopes that match the name. If MATCH_FILE is not null, accept only declaration in that source file; diff --git a/libdw/libdw.map b/libdw/libdw.map index 01ee5f606..a5324bd18 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -1,5 +1,5 @@ ELFUTILS_0 { }; -ELFUTILS_0.114 { +ELFUTILS_0.115 { global: dwarf_abbrevhaschildren; dwarf_addrdie; @@ -53,6 +53,7 @@ ELFUTILS_0.114 { dwarf_getmacros; dwarf_getpubnames; dwarf_getscopes; + dwarf_getscopes_die; dwarf_getscopevar; dwarf_getscn_info; dwarf_getsrc_die; diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 5f15cf9d3..37f287212 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -343,9 +343,20 @@ extern int __libdw_func_intval (Dwarf_Func *func, int *linep, int attval) __nonnull_attribute__ (1, 2) internal_function; /* Helper function to walk scopes. */ -extern int __libdw_visit_scopes (unsigned int depth, Dwarf_Die *root, - int (*visit) (unsigned int depth, - Dwarf_Die *die, void *arg), +struct Dwarf_Die_Chain +{ + Dwarf_Die die; + struct Dwarf_Die_Chain *parent; + bool prune; /* The PREVISIT function can set this. */ +}; +extern int __libdw_visit_scopes (unsigned int depth, + struct Dwarf_Die_Chain *root, + int (*previsit) (unsigned int depth, + struct Dwarf_Die_Chain *, + void *arg), + int (*postvisit) (unsigned int depth, + struct Dwarf_Die_Chain *, + void *arg), void *arg) __nonnull_attribute__ (2, 3) internal_function; @@ -360,6 +371,7 @@ INTDECL (dwarf_attr_integrate) INTDECL (dwarf_begin_elf) INTDECL (dwarf_child) INTDECL (dwarf_dieoffset) +INTDECL (dwarf_diename) INTDECL (dwarf_end) INTDECL (dwarf_errmsg) INTDECL (dwarf_formaddr) diff --git a/libdw/libdw_visit_scopes.c b/libdw/libdw_visit_scopes.c index 06168fb1b..3b92ea098 100644 --- a/libdw/libdw_visit_scopes.c +++ b/libdw/libdw_visit_scopes.c @@ -46,59 +46,82 @@ classify_die (Dwarf_Die *die) } int -__libdw_visit_scopes (depth, root, visit, arg) +__libdw_visit_scopes (depth, root, previsit, postvisit, arg) unsigned int depth; - Dwarf_Die *root; - int (*visit) (unsigned int depth, Dwarf_Die *die, void *arg); + struct Dwarf_Die_Chain *root; + int (*previsit) (unsigned int depth, struct Dwarf_Die_Chain *, void *); + int (*postvisit) (unsigned int depth, struct Dwarf_Die_Chain *, void *); void *arg; { - Dwarf_Die child; - if (INTUSE(dwarf_child) (root, &child) != 0) + struct Dwarf_Die_Chain child; + + child.parent = root; + if (INTUSE(dwarf_child) (&root->die, &child.die) != 0) return -1; + inline int recurse (void) + { + return __libdw_visit_scopes (depth + 1, &child, + previsit, postvisit, arg); + } + do { - int result = (*visit) (depth, &child, arg); - if (result != DWARF_CB_OK) - return result; + child.prune = false; - switch (classify_die (&child)) + if (previsit != NULL) { - case match: - case match_inline: - case walk: - if (INTUSE(dwarf_haschildren) (&child)) - { - result = __libdw_visit_scopes (depth + 1, &child, visit, arg); - if (result != DWARF_CB_OK) - return result; - } - break; + int result = (*previsit) (depth + 1, &child, arg); + if (result != DWARF_CB_OK) + return result; + } - case imported: + if (!child.prune) + switch (classify_die (&child.die)) { - /* This is imports another compilation unit to appear - as part of this one, inside the current scope. - Recurse to searesulth the referenced unit, but without - recording it as an inner scoping level. */ - - Dwarf_Attribute attr_mem; - Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child, DW_AT_import, - &attr_mem); - if (INTUSE(dwarf_formref_die) (attr, &child) != NULL) + case match: + case match_inline: + case walk: + if (INTUSE(dwarf_haschildren) (&child.die)) { - result = __libdw_visit_scopes (depth + 1, &child, visit, arg); - if (result != 0) + int result = recurse (); + if (result != DWARF_CB_OK) return result; } + break; + + case imported: + { + /* This imports another compilation unit to appear + as part of this one, inside the current scope. + Recurse to search the referenced unit, but without + recording it as an inner scoping level. */ + + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = INTUSE(dwarf_attr) (&child.die, + DW_AT_import, + &attr_mem); + if (INTUSE(dwarf_formref_die) (attr, &child.die) != NULL) + { + int result = recurse (); + if (result != DWARF_CB_OK) + return result; + } + } + break; + + default: + break; } - break; - default: - break; + if (postvisit != NULL) + { + int result = (*postvisit) (depth + 1, &child, arg); + if (result != DWARF_CB_OK) + return result; } } - while (INTUSE(dwarf_siblingof) (&child, &child) == 0); + while (INTUSE(dwarf_siblingof) (&child.die, &child.die) == 0); return 0; } diff --git a/src/ChangeLog b/src/ChangeLog index 7707ac173..1d485fe6e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2005-08-27 Roland McGrath + + * addr2line.c (dwarf_diename_integrate): Function removed. + (print_dwarf_function): Use plain dwarf_diename. + 2005-08-24 Ulrich Drepper * elflint.c (check_versym): Versioned symbols should not have diff --git a/src/addr2line.c b/src/addr2line.c index 97eaed10c..f79dc5f39 100644 --- a/src/addr2line.c +++ b/src/addr2line.c @@ -206,13 +206,6 @@ parse_opt (int key, char *arg __attribute__ ((unused)), } -static const char * -dwarf_diename_integrate (Dwarf_Die *die) -{ - Dwarf_Attribute attr_mem; - return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem)); -} - static bool print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) { @@ -229,7 +222,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) { case DW_TAG_subprogram: { - const char *name = dwarf_diename_integrate (&scopes[i]); + const char *name = dwarf_diename (&scopes[i]); if (name == NULL) return false; puts (name); @@ -238,7 +231,7 @@ print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr) case DW_TAG_inlined_subroutine: { - const char *name = dwarf_diename_integrate (&scopes[i]); + const char *name = dwarf_diename (&scopes[i]); if (name == NULL) return false; printf ("%s inlined", name); diff --git a/tests/ChangeLog b/tests/ChangeLog index bf58cb5a7..821d7e548 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,28 @@ +2005-08-27 Roland McGrath + + * run-funcscopes.sh: New file. + * testfile25.bz2: New data file. + * Makefile.am (TESTS, EXTRA_DIST): Add them. + +2005-08-26 Roland McGrath + + * addrscopes.c (dwarf_diename_integrate): Removed. + (print_vars, handle_address): Use plain dwarf_diename. + +2005-08-25 Roland McGrath + + * funcscopes.c: New file. + * Makefile.am (noinst_PROGRAMS): Add it. + (funcscopes_LDADD): New variable. + + * run-addrscopes.sh: Add another case. + * testfile24.bz2: New data file. + * Makefile.am (EXTRA_DIST): Add it. + + * addrscopes.c (handle_address): Take new argument IGNORE_INLINES, + pass it to dwarf_getscopes. + (main): Pass it, true when '=' follows an address. + 2005-08-24 Roland McGrath * line2addr.c (print_address): Omit () for DSOs. diff --git a/tests/Makefile.am b/tests/Makefile.am index b4cc1b040..3b88ff092 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -30,7 +30,7 @@ INCLUDES = -I$(top_srcdir)/libasm -I$(top_srcdir)/libdw \ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ showptable update1 update2 update3 update4 test-nlist \ show-die-info get-files get-lines get-pubnames \ - get-aranges allfcts line2addr addrscopes \ + get-aranges allfcts line2addr addrscopes funcscopes \ show-abbrev hash asm-tst1 asm-tst2 asm-tst3 \ asm-tst4 asm-tst5 asm-tst6 asm-tst7 asm-tst8 asm-tst9 \ msg_tst newscn ecp dwflmodtest @@ -47,7 +47,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-strip-test6.sh run-ecp-test.sh run-ecp-test2.sh \ run-elflint-test.sh run-elflint-self.sh run-ranlib-test.sh \ run-ranlib-test2.sh run-ranlib-test3.sh run-ranlib-test4.sh \ - run-addrscopes.sh + run-addrscopes.sh run-funcscopes.sh # run-show-ciefde.sh EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ @@ -63,14 +63,14 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \ run-strip-test4.sh run-strip-test5.sh run-strip-test6.sh \ run-elflint-self.sh run-ranlib-test.sh run-ranlib-test2.sh \ run-ranlib-test3.sh run-ranlib-test4.sh \ - run-addrscopes.sh \ + run-addrscopes.sh run-funcscopes.sh \ testfile15.bz2 testfile15.debug.bz2 \ testfile16.bz2 testfile16.debug.bz2 \ testfile17.bz2 testfile17.debug.bz2 \ testfile18.bz2 testfile19.bz2 testfile19.index.bz2 \ testfile20.bz2 testfile20.index.bz2 \ testfile21.bz2 testfile21.index.bz2 \ - testfile22.bz2 testfile23.bz2 + testfile22.bz2 testfile23.bz2 testfile24.bz2 testfile25.bz2 if MUDFLAP static_build=yes @@ -114,6 +114,7 @@ allfcts_LDADD = $(libdw) $(libelf) $(libmudflap) line2addr_no_Wformat = yes line2addr_LDADD = $(libdw) $(libmudflap) addrscopes_LDADD = $(libdw) $(libmudflap) +funcscopes_LDADD = $(libdw) $(libmudflap) #show_ciefde_LDADD = ../libdwarf/libdwarf.so $(libelf) $(libmudflap) asm_tst1_LDADD = $(libasm) $(libebl) $(libelf) $(libmudflap) -ldl asm_tst2_LDADD = $(libasm) $(libebl) $(libelf) $(libmudflap) -ldl diff --git a/tests/addrscopes.c b/tests/addrscopes.c index 4ef006483..9870175cd 100644 --- a/tests/addrscopes.c +++ b/tests/addrscopes.c @@ -45,13 +45,6 @@ paddr (const char *prefix, Dwarf_Addr addr, Dwfl_Line *line) printf ("%s%#" PRIx64, prefix, addr); } -static const char * -dwarf_diename_integrate (Dwarf_Die *die) -{ - Dwarf_Attribute attr_mem; - return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem)); -} - static void print_vars (unsigned int indent, Dwarf_Die *die) { @@ -63,7 +56,7 @@ print_vars (unsigned int indent, Dwarf_Die *die) case DW_TAG_variable: case DW_TAG_formal_parameter: printf ("%*s%-30s[%6" PRIx64 "]\n", indent, "", - dwarf_diename_integrate (&child), + dwarf_diename (&child), (uint64_t) dwarf_dieoffset (&child)); break; default: @@ -83,7 +76,7 @@ print_vars (unsigned int indent, Dwarf_Die *die) case DW_TAG_variable: case DW_TAG_formal_parameter: printf ("%*s%s (abstract)\n", indent, "", - dwarf_diename_integrate (&child)); + dwarf_diename (&child)); break; default: break; @@ -118,7 +111,7 @@ handle_address (GElf_Addr pc, Dwfl *dwfl) indent += INDENT; printf ("%*s%s (%#x)", indent, "", - dwarf_diename_integrate (die) ?: "", + dwarf_diename (die) ?: "", dwarf_tag (die)); Dwarf_Addr lowpc, highpc; diff --git a/tests/funcscopes.c b/tests/funcscopes.c new file mode 100644 index 000000000..a74e8d87b --- /dev/null +++ b/tests/funcscopes.c @@ -0,0 +1,192 @@ +/* Test program for dwarf_getscopes. + Copyright (C) 2005 Red Hat, Inc. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static void +paddr (const char *prefix, Dwarf_Addr addr, Dwfl_Line *line) +{ + const char *src; + int lineno, linecol; + if (line != NULL + && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol, + NULL, NULL)) != NULL) + { + if (linecol != 0) + printf ("%s%#" PRIx64 " (%s:%d:%d)", + prefix, addr, src, lineno, linecol); + else + printf ("%s%#" PRIx64 " (%s:%d)", + prefix, addr, src, lineno); + } + else + printf ("%s%#" PRIx64, prefix, addr); +} + + +static void +print_vars (unsigned int indent, Dwarf_Die *die) +{ + Dwarf_Die child; + if (dwarf_child (die, &child) == 0) + do + switch (dwarf_tag (&child)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + printf ("%*s%-30s[%6" PRIx64 "]\n", indent, "", + dwarf_diename (&child), + (uint64_t) dwarf_dieoffset (&child)); + break; + default: + break; + } + while (dwarf_siblingof (&child, &child) == 0); + + Dwarf_Attribute attr_mem; + Dwarf_Die origin; + if (dwarf_hasattr (die, DW_AT_abstract_origin) + && dwarf_formref_die (dwarf_attr (die, DW_AT_abstract_origin, &attr_mem), + &origin) != NULL + && dwarf_child (&origin, &child) == 0) + do + switch (dwarf_tag (&child)) + { + case DW_TAG_variable: + case DW_TAG_formal_parameter: + printf ("%*s%s (abstract)\n", indent, "", + dwarf_diename (&child)); + break; + default: + break; + } + while (dwarf_siblingof (&child, &child) == 0); +} + + +#define INDENT 4 + +struct args +{ + Dwfl *dwfl; + Dwarf_Die *cu; + Dwarf_Addr dwbias; + char **argv; +}; + +static int +handle_function (Dwarf_Func *func, void *arg) +{ + struct args *a = arg; + + const char *name = dwarf_func_name (func); + char **argv = a->argv; + if (argv[0] != NULL) + { + bool match; + do + match = fnmatch (*argv, name, 0) == 0; + while (!match && *++argv); + if (!match) + return 0; + } + + Dwarf_Die funcdie_mem; + Dwarf_Die *funcdie = dwarf_func_die (func, &funcdie_mem); + assert (funcdie == &funcdie_mem); + + Dwarf_Die *scopes; + int n = dwarf_getscopes_die (funcdie, &scopes); + if (n <= 0) + error (EXIT_FAILURE, 0, "dwarf_getscopes_die: %s", dwarf_errmsg (-1)); + else + { + Dwarf_Addr start, end; + const char *fname; + const char *modname = dwfl_module_info (dwfl_cumodule (a->cu), NULL, + &start, &end, + NULL, NULL, + &fname, NULL); + if (modname == NULL) + error (EXIT_FAILURE, 0, "dwfl_module_info: %s", dwarf_errmsg (-1)); + if (modname[0] == '\0') + modname = fname; + printf ("%s: %#" PRIx64 " .. %#" PRIx64 "\n", modname, start, end); + + unsigned int indent = 0; + while (n-- > 0) + { + Dwarf_Die *const die = &scopes[n]; + + indent += INDENT; + printf ("%*s%s (%#x)", indent, "", + dwarf_diename (die) ?: "", + dwarf_tag (die)); + + Dwarf_Addr lowpc, highpc; + if (dwarf_lowpc (die, &lowpc) == 0 + && dwarf_highpc (die, &highpc) == 0) + { + lowpc += a->dwbias; + highpc += a->dwbias; + Dwfl_Line *loline = dwfl_getsrc (a->dwfl, lowpc); + Dwfl_Line *hiline = dwfl_getsrc (a->dwfl, highpc); + paddr (": ", lowpc, loline); + if (highpc != lowpc) + paddr (" .. ", lowpc, hiline == loline ? NULL : hiline); + } + puts (""); + + print_vars (indent + INDENT, die); + } + } + + return 0; +} + + +int +main (int argc, char *argv[]) +{ + int remaining; + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + struct args a = { .dwfl = NULL, .cu = NULL }; + + (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &remaining, + &a.dwfl); + assert (a.dwfl != NULL); + a.argv = &argv[remaining]; + + int result = 0; + + while ((a.cu = dwfl_nextcu (a.dwfl, a.cu, &a.dwbias)) != NULL) + dwarf_getfuncs (a.cu, &handle_function, &a, 0); + + return result; +} diff --git a/tests/run-addrscopes.sh b/tests/run-addrscopes.sh index 901d02e12..57a610b81 100755 --- a/tests/run-addrscopes.sh +++ b/tests/run-addrscopes.sh @@ -28,4 +28,22 @@ EOF rm -f testfile22 addrscopes-test.out +# Don't fail if we cannot decompress the file. +bunzip2 -c $srcdir/testfile24.bz2 > testfile24 2>/dev/null || exit 0 + +LD_LIBRARY_PATH=../libdw:../libebl:../libelf${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH \ + ./addrscopes -e testfile24 0x804834e >& addrscopes-test.out || : + +diff -Bbu addrscopes-test.out - <<\EOF +0x804834e: + inline-test.c (0x11): 0x8048348 (/home/roland/build/stock-elfutils/inline-test.c:7) .. 0x8048364 (/home/roland/build/stock-elfutils/inline-test.c:16) + add (0x1d): 0x804834e (/home/roland/build/stock-elfutils/inline-test.c:3) .. 0x8048350 (/home/roland/build/stock-elfutils/inline-test.c:9) + y [ 9d] + x [ a2] + x (abstract) + y (abstract) +EOF + +rm -f testfile24 addrscopes-test.out + exit 0 diff --git a/tests/run-funcscopes.sh b/tests/run-funcscopes.sh new file mode 100644 index 000000000..7236ef919 --- /dev/null +++ b/tests/run-funcscopes.sh @@ -0,0 +1,30 @@ +#! /bin/sh +# Copyright (C) 2005 Red Hat, Inc. +# +# This program is Open Source software; you can redistribute it and/or +# modify it under the terms of the Open Software License version 1.0 as +# published by the Open Source Initiative. +# +# You should have received a copy of the Open Software License along +# with this program; if not, you may obtain a copy of the Open Software +# License version 1.0 from http://www.opensource.org/licenses/osl.php or +# by writing the Open Source Initiative c/o Lawrence Rosen, Esq., +# 3001 King Ranch Road, Ukiah, CA 95482. +set -e + +# Don't fail if we cannot decompress the file. +bunzip2 -c $srcdir/testfile25.bz2 > testfile25 2>/dev/null || exit 0 + +LD_LIBRARY_PATH=../libdw:../libebl:../libelf${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH \ + ./funcscopes -e testfile25 incr >& funcscopes-test.out || : + +diff -Bbu funcscopes-test.out - <<\EOF +testfile25: 0x8048000 .. 0x8049528 + inline-test.c (0x11): 0x8048348 (/home/roland/build/stock-elfutils/inline-test.c:7) .. 0x804834f (/home/roland/build/stock-elfutils/inline-test.c:9) + incr (0x2e): 0x8048348 (/home/roland/build/stock-elfutils/inline-test.c:7) .. 0x804834f (/home/roland/build/stock-elfutils/inline-test.c:9) + x [ 66] +EOF + +rm -f testfile25 funcscopes-test.out + +exit 0 diff --git a/tests/testfile24.bz2 b/tests/testfile24.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..2320acb33077f17634caeeed7f072f4a3fb10508 GIT binary patch literal 2644 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 literal 0 Hc-jL100001 -- 2.47.2