| From f07c08e115e27cddf5a0030dc6332bbee1bd9c6a Mon Sep 17 00:00:00 2001 |
| From: Alan Modra <amodra@gmail.com> |
| Date: Thu, 21 Jul 2022 08:38:14 +0930 |
| Subject: [PATCH] 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. |
| |
| Upstream-Status: Backport [https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=f07c08e115e27cddf5a0030dc6332bbee1bd9c6a] |
| |
| Signed-off-by: Pgowda <pgowda.cve@gmail.com> |
| --- |
| binutils/dwarf.c | 208 +++++++++++++++++++++++++---------------------- |
| 1 file changed, 110 insertions(+), 98 deletions(-) |
| |
| diff --git a/binutils/dwarf.c b/binutils/dwarf.c |
| index 267ed3bb382..2fc352f74c5 100644 |
| --- a/binutils/dwarf.c |
| +++ b/binutils/dwarf.c |
| @@ -882,8 +882,15 @@ static unsigned long next_free_abbrev_m |
| #define ABBREV_MAP_ENTRIES_INCREMENT 8 |
| |
| 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) |
| { |
| num_abbrev_map_entries = INITIAL_NUM_ABBREV_MAP_ENTRIES; |
| @@ -936,20 +943,6 @@ free_all_abbrevs (void) |
| } |
| |
| 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 * |
| find_abbrev_list_by_abbrev_offset (dwarf_vma abbrev_base, |
| dwarf_vma abbrev_offset) |
| { |
| @@ -966,7 +959,7 @@ find_abbrev_list_by_abbrev_offset (dwarf |
| /* Find the abbreviation map for the CU that includes OFFSET. |
| OFFSET is an absolute offset from the start of the .debug_info section. */ |
| /* 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 * |
| find_abbrev_map_by_offset (dwarf_vma offset) |
| @@ -1033,40 +1026,18 @@ add_abbrev_attr (unsigned long attrib |
| list->last_abbrev->last_attr = attr; |
| } |
| |
| -/* Processes the (partial) contents of a .debug_abbrev section. |
| - Returns NULL if the end of the section was encountered. |
| - Returns the address after the last byte read if the end of |
| - an abbreviation set was found. */ |
| +/* Return processed (partial) contents of a .debug_abbrev section. |
| + Returns NULL on errors. */ |
| |
| -static unsigned char * |
| +static abbrev_list * |
| process_abbrev_set (struct dwarf_section *section, |
| - dwarf_vma abbrev_base, |
| - dwarf_vma abbrev_size, |
| - dwarf_vma abbrev_offset, |
| - abbrev_list *list) |
| + unsigned char *start, |
| + unsigned char *end) |
| { |
| - 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; |
| - } |
| + abbrev_list *list = xmalloc (sizeof (*list)); |
| + list->first_abbrev = NULL; |
| + list->last_abbrev = NULL; |
| |
| - unsigned char *start = section->start + abbrev_base; |
| - unsigned char *end = start + abbrev_size; |
| - start += abbrev_offset; |
| while (start < end) |
| { |
| unsigned long entry; |
| @@ -1079,14 +1050,18 @@ process_abbrev_set (struct dwarf_section |
| /* A single zero is supposed to end the set according |
| to the standard. If there's more, then signal that to |
| the caller. */ |
| - if (start == end) |
| - return NULL; |
| - if (entry == 0) |
| - return start; |
| + if (start == end || entry == 0) |
| + { |
| + list->start_of_next_abbrevs = start != end ? start : NULL; |
| + return list; |
| + } |
| |
| READ_ULEB (tag, start, end); |
| if (start == end) |
| - return NULL; |
| + { |
| + free (list); |
| + return NULL; |
| + } |
| |
| children = *start++; |
| |
| @@ -1121,9 +1096,67 @@ process_abbrev_set (struct dwarf_section |
| /* Report the missing single zero which ends the section. */ |
| error (_(".debug_abbrev section not zero terminated\n")); |
| |
| + free (list); |
| 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 * |
| get_TAG_name (unsigned long tag) |
| { |
| @@ -3670,7 +3703,6 @@ process_debug_info (struct dwarf_section |
| dwarf_vma cu_offset; |
| unsigned int offset_size; |
| struct cu_tu_set * this_set; |
| - abbrev_list * list; |
| unsigned char *end_cu; |
| |
| hdrptr = start; |
| @@ -3726,22 +3758,18 @@ process_debug_info (struct dwarf_section |
| abbrev_size = this_set->section_sizes [DW_SECT_ABBREV]; |
| } |
| |
| - list = find_abbrev_list_by_abbrev_offset (abbrev_base, |
| - compunit.cu_abbrev_offset); |
| - 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, |
| - compunit.cu_abbrev_offset, list); |
| - list->start_of_next_abbrevs = next; |
| - } |
| - |
| + abbrev_list *list; |
| + abbrev_list *free_list; |
| + list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section, |
| + abbrev_base, abbrev_size, |
| + compunit.cu_abbrev_offset, |
| + &free_list); |
| 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++) |
| @@ -3757,7 +3785,6 @@ process_debug_info (struct dwarf_section |
| struct cu_tu_set *this_set; |
| dwarf_vma abbrev_base; |
| size_t abbrev_size; |
| - abbrev_list * list = NULL; |
| unsigned char *end_cu; |
| |
| hdrptr = start; |
| @@ -3936,20 +3963,10 @@ process_debug_info (struct dwarf_section |
| } |
| |
| /* Process the abbrevs used by this compilation unit. */ |
| - list = find_abbrev_list_by_abbrev_offset (abbrev_base, |
| - compunit.cu_abbrev_offset); |
| - 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, |
| - compunit.cu_abbrev_offset, list); |
| - list->start_of_next_abbrevs = next; |
| - } |
| - |
| + abbrev_list *list; |
| + list = find_and_process_abbrev_set (&debug_displays[abbrev_sec].section, |
| + abbrev_base, abbrev_size, |
| + compunit.cu_abbrev_offset, NULL); |
| level = 0; |
| last_level = level; |
| saved_level = -1; |
| @@ -4128,6 +4145,8 @@ process_debug_info (struct dwarf_section |
| if (entry->children) |
| ++level; |
| } |
| + if (list != NULL) |
| + free_abbrev_list (list); |
| } |
| |
| /* Set num_debug_info_entries here so that it can be used to check if |
| @@ -6353,24 +6372,15 @@ display_debug_abbrev (struct dwarf_secti |
| |
| do |
| { |
| - abbrev_list * list; |
| - dwarf_vma offset; |
| - |
| - offset = start - section->start; |
| - list = find_abbrev_list_by_abbrev_offset (0, offset); |
| + dwarf_vma offset = start - section->start; |
| + abbrev_list *list = find_and_process_abbrev_set (section, 0, |
| + section->size, offset, |
| + NULL); |
| if (list == NULL) |
| - { |
| - 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; |
| + break; |
| |
| - printf (_(" Number TAG (0x%lx)\n"), (long) offset); |
| + if (list->first_abbrev) |
| + printf (_(" Number TAG (0x%lx)\n"), (long) offset); |
| |
| for (entry = list->first_abbrev; entry; entry = entry->next) |
| { |
| @@ -6391,6 +6401,8 @@ display_debug_abbrev (struct dwarf_secti |
| putchar ('\n'); |
| } |
| } |
| + start = list->start_of_next_abbrevs; |
| + free_abbrev_list (list); |
| } |
| while (start); |
| |