binutils/dwarf.c: abbrev caching

I'm inclined to think that abbrev caching is counter-productive.  The
time taken to search the list of abbrevs converted to internal form is
non-zero, and it's easy to decode the raw abbrevs.  It's especially
silly to cache empty lists of decoded abbrevs (happens with zero
padding in .debug_abbrev), or abbrevs as they are displayed when there
is no further use of those abbrevs.  This patch stops caching in those
cases.

	* dwarf.c (record_abbrev_list_for_cu): Add free_list param.
	Put abbrevs on abbrev_lists here.
	(new_abbrev_list): Delete function.
	(process_abbrev_set): Return newly allocated list.  Move
	abbrev base, offset and size checking to..
	(find_and_process_abbrev_set): ..here, new function.  Handle
	lookup of cached abbrevs here, and calculate start and end
	for process_abbrev_set.  Return free_list if newly alloc'd.
	(process_debug_info): Consolidate cached list lookup, new list
	alloc and processing into find_and_process_abbrev_set call.
	Free list when not cached.
	(display_debug_abbrev): Similarly.
This commit is contained in:
Alan Modra
2022-07-21 08:38:14 +09:30
parent 175b91507b
commit f07c08e115

View File

@ -882,8 +882,15 @@ static unsigned long next_free_abbrev_map_entry = 0;
#define ABBREV_MAP_ENTRIES_INCREMENT 8 #define ABBREV_MAP_ENTRIES_INCREMENT 8
static void static void
record_abbrev_list_for_cu (dwarf_vma start, dwarf_vma end, abbrev_list * list) record_abbrev_list_for_cu (dwarf_vma start, dwarf_vma end,
abbrev_list *list, abbrev_list *free_list)
{ {
if (free_list != NULL)
{
list->next = abbrev_lists;
abbrev_lists = list;
}
if (cu_abbrev_map == NULL) if (cu_abbrev_map == NULL)
{ {
num_abbrev_map_entries = INITIAL_NUM_ABBREV_MAP_ENTRIES; num_abbrev_map_entries = INITIAL_NUM_ABBREV_MAP_ENTRIES;
@ -938,20 +945,6 @@ free_all_abbrevs (void)
next_free_abbrev_map_entry = 0; next_free_abbrev_map_entry = 0;
} }
static abbrev_list *
new_abbrev_list (dwarf_vma abbrev_base, dwarf_vma abbrev_offset)
{
abbrev_list * list = (abbrev_list *) xcalloc (sizeof * list, 1);
list->abbrev_base = abbrev_base;
list->abbrev_offset = abbrev_offset;
list->next = abbrev_lists;
abbrev_lists = list;
return list;
}
static abbrev_list * static abbrev_list *
find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base, find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base,
dwarf_vma abbrev_offset) dwarf_vma abbrev_offset)
@ -969,7 +962,7 @@ find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base,
/* Find the abbreviation map for the CU that includes OFFSET. /* Find the abbreviation map for the CU that includes OFFSET.
OFFSET is an absolute offset from the start of the .debug_info section. */ OFFSET is an absolute offset from the start of the .debug_info section. */
/* FIXME: This function is going to slow down readelf & objdump. /* FIXME: This function is going to slow down readelf & objdump.
Consider using a better algorithm to mitigate this effect. */ Not caching abbrevs is likely the answer. */
static abbrev_map * static abbrev_map *
find_abbrev_map_by_offset (dwarf_vma offset) find_abbrev_map_by_offset (dwarf_vma offset)
@ -1036,40 +1029,18 @@ add_abbrev_attr (unsigned long attribute,
list->last_abbrev->last_attr = attr; list->last_abbrev->last_attr = attr;
} }
/* Processes the (partial) contents of a .debug_abbrev section. /* Return processed (partial) contents of a .debug_abbrev section.
Returns NULL if the end of the section was encountered. Returns NULL on errors. */
Returns the address after the last byte read if the end of
an abbreviation set was found. */
static unsigned char * static abbrev_list *
process_abbrev_set (struct dwarf_section *section, process_abbrev_set (struct dwarf_section *section,
dwarf_vma abbrev_base, unsigned char *start,
dwarf_vma abbrev_size, unsigned char *end)
dwarf_vma abbrev_offset,
abbrev_list *list)
{ {
if (abbrev_base >= section->size abbrev_list *list = xmalloc (sizeof (*list));
|| abbrev_size > section->size - abbrev_base) list->first_abbrev = NULL;
{ list->last_abbrev = NULL;
/* PR 17531: file:4bcd9ce9. */
warn (_("Debug info is corrupted, abbrev size (%lx) is larger than "
"abbrev section size (%lx)\n"),
(unsigned long) (abbrev_base + abbrev_size),
(unsigned long) section->size);
return NULL;
}
if (abbrev_offset >= abbrev_size)
{
warn (_("Debug info is corrupted, abbrev offset (%lx) is larger than "
"abbrev section size (%lx)\n"),
(unsigned long) abbrev_offset,
(unsigned long) abbrev_size);
return NULL;
}
unsigned char *start = section->start + abbrev_base;
unsigned char *end = start + abbrev_size;
start += abbrev_offset;
while (start < end) while (start < end)
{ {
unsigned long entry; unsigned long entry;
@ -1082,14 +1053,18 @@ process_abbrev_set (struct dwarf_section *section,
/* A single zero is supposed to end the set according /* A single zero is supposed to end the set according
to the standard. If there's more, then signal that to to the standard. If there's more, then signal that to
the caller. */ the caller. */
if (start == end) if (start == end || entry == 0)
return NULL; {
if (entry == 0) list->start_of_next_abbrevs = start != end ? start : NULL;
return start; return list;
}
READ_ULEB (tag, start, end); READ_ULEB (tag, start, end);
if (start == end) if (start == end)
{
free (list);
return NULL; return NULL;
}
children = *start++; children = *start++;
@ -1124,9 +1099,67 @@ process_abbrev_set (struct dwarf_section *section,
/* Report the missing single zero which ends the section. */ /* Report the missing single zero which ends the section. */
error (_("%s section not zero terminated\n"), section->name); error (_("%s section not zero terminated\n"), section->name);
free (list);
return NULL; return NULL;
} }
/* Return a sequence of abbrevs in SECTION starting at ABBREV_BASE
plus ABBREV_OFFSET and finishing at ABBREV_BASE + ABBREV_SIZE.
If FREE_LIST is non-NULL search the already decoded abbrevs on
abbrev_lists first and if found set *FREE_LIST to NULL. If
searching doesn't find a matching abbrev, set *FREE_LIST to the
newly allocated list. If FREE_LIST is NULL, no search is done and
the returned abbrev_list is always newly allocated. */
static abbrev_list *
find_and_process_abbrev_set (struct dwarf_section *section,
dwarf_vma abbrev_base,
dwarf_vma abbrev_size,
dwarf_vma abbrev_offset,
abbrev_list **free_list)
{
if (free_list)
*free_list = NULL;
if (abbrev_base >= section->size
|| abbrev_size > section->size - abbrev_base)
{
/* PR 17531: file:4bcd9ce9. */
warn (_("Debug info is corrupted, abbrev size (%lx) is larger than "
"abbrev section size (%lx)\n"),
(unsigned long) (abbrev_base + abbrev_size),
(unsigned long) section->size);
return NULL;
}
if (abbrev_offset >= abbrev_size)
{
warn (_("Debug info is corrupted, abbrev offset (%lx) is larger than "
"abbrev section size (%lx)\n"),
(unsigned long) abbrev_offset,
(unsigned long) abbrev_size);
return NULL;
}
unsigned char *start = section->start + abbrev_base + abbrev_offset;
unsigned char *end = section->start + abbrev_base + abbrev_size;
abbrev_list *list = NULL;
if (free_list)
list = find_abbrev_list_by_abbrev_offset (abbrev_base, abbrev_offset);
if (list == NULL)
{
list = process_abbrev_set (section, start, end);
if (list)
{
list->abbrev_base = abbrev_base;
list->abbrev_offset = abbrev_offset;
list->next = NULL;
}
if (free_list)
*free_list = list;
}
return list;
}
static const char * static const char *
get_TAG_name (unsigned long tag) get_TAG_name (unsigned long tag)
{ {
@ -3671,7 +3704,6 @@ process_debug_info (struct dwarf_section * section,
dwarf_vma cu_offset; dwarf_vma cu_offset;
unsigned int offset_size; unsigned int offset_size;
struct cu_tu_set * this_set; struct cu_tu_set * this_set;
abbrev_list * list;
unsigned char *end_cu; unsigned char *end_cu;
hdrptr = start; hdrptr = start;
@ -3727,22 +3759,18 @@ process_debug_info (struct dwarf_section * section,
abbrev_size = this_set->section_sizes [DW_SECT_ABBREV]; abbrev_size = this_set->section_sizes [DW_SECT_ABBREV];
} }
list = find_abbrev_list_by_abbrev_offset (abbrev_base, abbrev_list *list;
compunit.cu_abbrev_offset); abbrev_list *free_list;
if (list == NULL) list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section,
{
unsigned char * next;
list = new_abbrev_list (abbrev_base,
compunit.cu_abbrev_offset);
next = process_abbrev_set (&debug_displays[abbrev_sec].section,
abbrev_base, abbrev_size, abbrev_base, abbrev_size,
compunit.cu_abbrev_offset, list); compunit.cu_abbrev_offset,
list->start_of_next_abbrevs = next; &free_list);
}
start = end_cu; start = end_cu;
record_abbrev_list_for_cu (cu_offset, start - section_begin, list); if (list != NULL && list->first_abbrev != NULL)
record_abbrev_list_for_cu (cu_offset, start - section_begin,
list, free_list);
else if (free_list != NULL)
free_abbrev_list (free_list);
} }
for (start = section_begin, unit = 0; start < end; unit++) for (start = section_begin, unit = 0; start < end; unit++)
@ -3758,7 +3786,6 @@ process_debug_info (struct dwarf_section * section,
struct cu_tu_set *this_set; struct cu_tu_set *this_set;
dwarf_vma abbrev_base; dwarf_vma abbrev_base;
size_t abbrev_size; size_t abbrev_size;
abbrev_list * list = NULL;
unsigned char *end_cu; unsigned char *end_cu;
hdrptr = start; hdrptr = start;
@ -3937,20 +3964,10 @@ process_debug_info (struct dwarf_section * section,
} }
/* Process the abbrevs used by this compilation unit. */ /* Process the abbrevs used by this compilation unit. */
list = find_abbrev_list_by_abbrev_offset (abbrev_base, abbrev_list *list;
compunit.cu_abbrev_offset); list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section,
if (list == NULL)
{
unsigned char *next;
list = new_abbrev_list (abbrev_base,
compunit.cu_abbrev_offset);
next = process_abbrev_set (&debug_displays[abbrev_sec].section,
abbrev_base, abbrev_size, abbrev_base, abbrev_size,
compunit.cu_abbrev_offset, list); compunit.cu_abbrev_offset, NULL);
list->start_of_next_abbrevs = next;
}
level = 0; level = 0;
last_level = level; last_level = level;
saved_level = -1; saved_level = -1;
@ -4128,6 +4145,8 @@ process_debug_info (struct dwarf_section * section,
if (entry->children) if (entry->children)
++level; ++level;
} }
if (list != NULL)
free_abbrev_list (list);
} }
/* Set num_debug_info_entries here so that it can be used to check if /* Set num_debug_info_entries here so that it can be used to check if
@ -6353,23 +6372,14 @@ display_debug_abbrev (struct dwarf_section *section,
do do
{ {
abbrev_list * list; dwarf_vma offset = start - section->start;
dwarf_vma offset; abbrev_list *list = find_and_process_abbrev_set (section, 0,
section->size, offset,
offset = start - section->start; NULL);
list = find_abbrev_list_by_abbrev_offset (0, offset);
if (list == NULL) if (list == NULL)
{ break;
list = new_abbrev_list (0, offset);
start = process_abbrev_set (section, 0, section->size, offset, list);
list->start_of_next_abbrevs = start;
}
else
start = list->start_of_next_abbrevs;
if (list->first_abbrev == NULL)
continue;
if (list->first_abbrev)
printf (_(" Number TAG (0x%lx)\n"), (long) offset); printf (_(" Number TAG (0x%lx)\n"), (long) offset);
for (entry = list->first_abbrev; entry; entry = entry->next) for (entry = list->first_abbrev; entry; entry = entry->next)
@ -6391,6 +6401,8 @@ display_debug_abbrev (struct dwarf_section *section,
putchar ('\n'); putchar ('\n');
} }
} }
start = list->start_of_next_abbrevs;
free_abbrev_list (list);
} }
while (start); while (start);