Greatly improve the speed if looking up DWARF line number information.

* dwarf2.c (comp_unit): Add new fields 'lookup_funcinfo_table' and
	'number_of_functions' to keep lookup table and number of entries in
	the table.
	(line_sequence): Add new fields 'line_info_lookup' and 'num_lines'
	to keep lookup table and number of entries in the table.
	(lookup_funcinfo): New structure for lookup table for function
	references.
	(build_line_info_table): New function to create and build the lookup
	table for line information.
	(lookup_address_in_line_info_table): Use the lookup table instead of
	traverse a linked list.
	(compare_lookup_funcinfos): New compare fuction used in sorting of
	lookup table for function references.
	(build_lookup_funcinfo_table): New function to create, build and
	sort the lookup table for functions references.
	(lookup_address_in_function_table): Use the table instead of
	traverse a linked list.
	(_bfd_dwarf2_cleanup_debug_info): Free memory from function references
	lookup table.
This commit is contained in:
Igor Tsimbalist 2016-11-08 12:01:58 +00:00 committed by Nick Clifton
parent 20955dbf71
commit 089e3718bd
2 changed files with 320 additions and 85 deletions

View File

@ -1,3 +1,25 @@
2016-11-08 Igor Tsimbalist <tigor.tools@gmail.com>
* dwarf2.c (comp_unit): Add new fields 'lookup_funcinfo_table' and
'number_of_functions' to keep lookup table and number of entries in
the table.
(line_sequence): Add new fields 'line_info_lookup' and 'num_lines'
to keep lookup table and number of entries in the table.
(lookup_funcinfo): New structure for lookup table for function
references.
(build_line_info_table): New function to create and build the lookup
table for line information.
(lookup_address_in_line_info_table): Use the lookup table instead of
traverse a linked list.
(compare_lookup_funcinfos): New compare fuction used in sorting of
lookup table for function references.
(build_lookup_funcinfo_table): New function to create, build and
sort the lookup table for functions references.
(lookup_address_in_function_table): Use the table instead of
traverse a linked list.
(_bfd_dwarf2_cleanup_debug_info): Free memory from function references
lookup table.
2016-11-04 Nick Clifton <nickc@redhat.com> 2016-11-04 Nick Clifton <nickc@redhat.com>
* targets.c (bfd_target_vector): Only add riscv_elf32_vec target * targets.c (bfd_target_vector): Only add riscv_elf32_vec target

View File

@ -256,6 +256,12 @@ struct comp_unit
/* A list of the functions found in this comp. unit. */ /* A list of the functions found in this comp. unit. */
struct funcinfo *function_table; struct funcinfo *function_table;
/* A table of function information references searchable by address. */
struct lookup_funcinfo *lookup_funcinfo_table;
/* Number of functions in the function_table and sorted_function_table. */
bfd_size_type number_of_functions;
/* A list of the variables found in this comp. unit. */ /* A list of the variables found in this comp. unit. */
struct varinfo *variable_table; struct varinfo *variable_table;
@ -1214,9 +1220,9 @@ non_mangled (int lang)
struct line_info struct line_info
{ {
struct line_info* prev_line; struct line_info * prev_line;
bfd_vma address; bfd_vma address;
char *filename; char * filename;
unsigned int line; unsigned int line;
unsigned int column; unsigned int column;
unsigned int discriminator; unsigned int discriminator;
@ -1226,7 +1232,7 @@ struct line_info
struct fileinfo struct fileinfo
{ {
char *name; char * name;
unsigned int dir; unsigned int dir;
unsigned int time; unsigned int time;
unsigned int size; unsigned int size;
@ -1237,11 +1243,13 @@ struct line_sequence
bfd_vma low_pc; bfd_vma low_pc;
struct line_sequence* prev_sequence; struct line_sequence* prev_sequence;
struct line_info* last_line; /* Largest VMA. */ struct line_info* last_line; /* Largest VMA. */
struct line_info** line_info_lookup;
bfd_size_type num_lines;
}; };
struct line_info_table struct line_info_table
{ {
bfd* abfd; bfd * abfd;
unsigned int num_files; unsigned int num_files;
unsigned int num_dirs; unsigned int num_dirs;
unsigned int num_sequences; unsigned int num_sequences;
@ -1260,23 +1268,37 @@ struct line_info_table
struct funcinfo struct funcinfo
{ {
/* Pointer to previous function in list of all functions. */ /* Pointer to previous function in list of all functions. */
struct funcinfo *prev_func; struct funcinfo * prev_func;
/* Pointer to function one scope higher. */ /* Pointer to function one scope higher. */
struct funcinfo *caller_func; struct funcinfo * caller_func;
/* Source location file name where caller_func inlines this func. */ /* Source location file name where caller_func inlines this func. */
char *caller_file; char * caller_file;
/* Source location file name. */ /* Source location file name. */
char *file; char * file;
/* Source location line number where caller_func inlines this func. */ /* Source location line number where caller_func inlines this func. */
int caller_line; int caller_line;
/* Source location line number. */ /* Source location line number. */
int line; int line;
int tag; int tag;
bfd_boolean is_linkage; bfd boolean is_linkage;
const char *name; const char * name;
struct arange arange; struct arange arange;
/* Where the symbol is defined. */ /* Where the symbol is defined. */
asection *sec; asection * sec;
};
struct lookup_funcinfo
{
/* Function information corresponding to this lookup table entry. */
struct funcinfo * funcinfo;
/* The lowest address for this specific function. */
bfd_vma low_addr;
/* The highest address of this function before the lookup table is sorted.
The highest address of all prior functions after the lookup table is
sorted, which is used for binary search. */
bfd_vma high_addr;
}; };
struct varinfo struct varinfo
@ -1579,6 +1601,51 @@ compare_sequences (const void* a, const void* b)
return 0; return 0;
} }
/* Construct the line information table for quick lookup. */
static bfd_boolean
build_line_info_table (struct line_info_table * table,
struct line_sequence * seq)
{
bfd_size_type amt;
struct line_info** line_info_lookup;
struct line_info* each_line;
unsigned int num_lines;
unsigned int index;
if (seq->line_info_lookup != NULL)
return TRUE;
/* Count the number of line information entries. We could do this while
scanning the debug information, but some entries may be added via
lcl_head without having a sequence handy to increment the number of
lines. */
num_lines = 0;
for (each_line = seq->last_line; each_line; each_line = each_line->prev_line)
num_lines++;
if (num_lines == 0)
return TRUE;
/* Allocate space for the line information lookup table. */
amt = sizeof (struct line_info*) * num_lines;
line_info_lookup = (struct line_info**) bfd_alloc (table->abfd, amt);
if (line_info_lookup == NULL)
return FALSE;
/* Create the line information lookup table. */
index = num_lines;
for (each_line = seq->last_line; each_line; each_line = each_line->prev_line)
line_info_lookup[--index] = each_line;
BFD_ASSERT (index == 0);
seq->num_lines = num_lines;
seq->line_info_lookup = line_info_lookup;
return TRUE;
}
/* Sort the line sequences for quick lookup. */ /* Sort the line sequences for quick lookup. */
static bfd_boolean static bfd_boolean
@ -1610,6 +1677,8 @@ sort_line_sequences (struct line_info_table* table)
sequences[n].low_pc = seq->low_pc; sequences[n].low_pc = seq->low_pc;
sequences[n].prev_sequence = NULL; sequences[n].prev_sequence = NULL;
sequences[n].last_line = seq->last_line; sequences[n].last_line = seq->last_line;
sequences[n].line_info_lookup = NULL;
sequences[n].num_lines = 0;
seq = seq->prev_sequence; seq = seq->prev_sequence;
free (last_seq); free (last_seq);
} }
@ -2091,7 +2160,7 @@ lookup_address_in_line_info_table (struct line_info_table *table,
unsigned int *discriminator_ptr) unsigned int *discriminator_ptr)
{ {
struct line_sequence *seq = NULL; struct line_sequence *seq = NULL;
struct line_info *each_line; struct line_info *info;
int low, high, mid; int low, high, mid;
/* Binary search the array of sequences. */ /* Binary search the array of sequences. */
@ -2109,26 +2178,43 @@ lookup_address_in_line_info_table (struct line_info_table *table,
break; break;
} }
if (seq && addr >= seq->low_pc && addr < seq->last_line->address) /* Check for a valid sequence. */
{ if (!seq || addr < seq->low_pc || addr >= seq->last_line->address)
/* Note: seq->last_line should be a descendingly sorted list. */ goto fail;
for (each_line = seq->last_line;
each_line;
each_line = each_line->prev_line)
if (addr >= each_line->address)
break;
if (each_line if (!build_line_info_table (table, seq))
&& !(each_line->end_sequence || each_line == seq->last_line)) goto fail;
/* Binary search the array of line information. */
low = 0;
high = seq->num_lines;
info = NULL;
while (low < high)
{ {
*filename_ptr = each_line->filename; mid = (low + high) / 2;
*linenumber_ptr = each_line->line; info = seq->line_info_lookup[mid];
if (addr < info->address)
high = mid;
else if (addr >= seq->line_info_lookup[mid + 1]->address)
low = mid + 1;
else
break;
}
/* Check for a valid line information entry. */
if (info
&& addr >= info->address
&& addr < seq->line_info_lookup[mid + 1]->address
&& !(info->end_sequence || info == seq->last_line))
{
*filename_ptr = info->filename;
*linenumber_ptr = info->line;
if (discriminator_ptr) if (discriminator_ptr)
*discriminator_ptr = each_line->discriminator; *discriminator_ptr = info->discriminator;
return seq->last_line->address - seq->low_pc; return seq->last_line->address - seq->low_pc;
} }
}
fail:
*filename_ptr = NULL; *filename_ptr = NULL;
return 0; return 0;
} }
@ -2136,16 +2222,102 @@ lookup_address_in_line_info_table (struct line_info_table *table,
/* Read in the .debug_ranges section for future reference. */ /* Read in the .debug_ranges section for future reference. */
static bfd_boolean static bfd_boolean
read_debug_ranges (struct comp_unit *unit) read_debug_ranges (struct comp_unit * unit)
{ {
struct dwarf2_debug *stash = unit->stash; struct dwarf2_debug * stash = unit->stash;
return read_section (unit->abfd, &stash->debug_sections[debug_ranges], return read_section (unit->abfd, &stash->debug_sections[debug_ranges],
stash->syms, 0, stash->syms, 0,
&stash->dwarf_ranges_buffer, &stash->dwarf_ranges_size); &stash->dwarf_ranges_buffer,
&stash->dwarf_ranges_size);
} }
/* Function table functions. */ /* Function table functions. */
static int
compare_lookup_funcinfos (const void * a, const void * b)
{
const struct lookup_funcinfo * lookup1 = a;
const struct lookup_funcinfo * lookup2 = b;
if (lookup1->low_addr < lookup2->low_addr)
return -1;
if (lookup1->low_addr > lookup2->low_addr)
return 1;
if (lookup1->high_addr < lookup2->high_addr)
return -1;
if (lookup1->high_addr > lookup2->high_addr)
return 1;
return 0;
}
static bfd_boolean
build_lookup_funcinfo_table (struct comp_unit * unit)
{
struct lookup_funcinfo *lookup_funcinfo_table = unit->lookup_funcinfo_table;
unsigned int number_of_functions = unit->number_of_functions;
struct funcinfo *each;
struct lookup_funcinfo *entry;
size_t index;
struct arange *range;
bfd_vma low_addr, high_addr;
if (lookup_funcinfo_table || number_of_functions == 0)
return TRUE;
/* Create the function info lookup table. */
lookup_funcinfo_table = (struct lookup_funcinfo *)
bfd_malloc (number_of_functions * sizeof (struct lookup_funcinfo));
if (lookup_funcinfo_table == NULL)
return FALSE;
/* Populate the function info lookup table. */
index = number_of_functions;
for (each = unit->function_table; each; each = each->prev_func)
{
entry = &lookup_funcinfo_table[--index];
entry->funcinfo = each;
/* Calculate the lowest and highest address for this function entry. */
low_addr = entry->funcinfo->arange.low;
high_addr = entry->funcinfo->arange.high;
for (range = entry->funcinfo->arange.next; range; range = range->next)
{
if (range->low < low_addr)
low_addr = range->low;
if (range->high > high_addr)
high_addr = range->high;
}
entry->low_addr = low_addr;
entry->high_addr = high_addr;
}
BFD_ASSERT (index == 0);
/* Sort the function by address. */
qsort (lookup_funcinfo_table,
number_of_functions,
sizeof (struct lookup_funcinfo),
compare_lookup_funcinfos);
/* Calculate the high watermark for each function in the lookup table. */
high_addr = lookup_funcinfo_table[0].high_addr;
for (index = 1; index < number_of_functions; index++)
{
entry = &lookup_funcinfo_table[index];
if (entry->high_addr > high_addr)
high_addr = entry->high_addr;
else
entry->high_addr = high_addr;
}
unit->lookup_funcinfo_table = lookup_funcinfo_table;
return TRUE;
}
/* If ADDR is within UNIT's function tables, set FUNCTION_PTR, and return /* If ADDR is within UNIT's function tables, set FUNCTION_PTR, and return
TRUE. Note that we need to find the function that has the smallest range TRUE. Note that we need to find the function that has the smallest range
that contains ADDR, to handle inlined functions without depending upon that contains ADDR, to handle inlined functions without depending upon
@ -2156,37 +2328,71 @@ lookup_address_in_function_table (struct comp_unit *unit,
bfd_vma addr, bfd_vma addr,
struct funcinfo **function_ptr) struct funcinfo **function_ptr)
{ {
struct funcinfo* each_func; unsigned int number_of_functions = unit->number_of_functions;
struct lookup_funcinfo* lookup_funcinfo = NULL;
struct funcinfo* funcinfo = NULL;
struct funcinfo* best_fit = NULL; struct funcinfo* best_fit = NULL;
bfd_vma best_fit_len = 0; bfd_vma best_fit_len = 0;
bfd_size_type low, high, mid, first;
struct arange *arange; struct arange *arange;
for (each_func = unit->function_table; if (!build_lookup_funcinfo_table (unit))
each_func; return FALSE;
each_func = each_func->prev_func)
/* Find the first function in the lookup table which may contain the
specified address. */
low = 0;
high = number_of_functions;
first = high;
while (low < high)
{ {
for (arange = &each_func->arange; mid = (low + high) / 2;
arange; lookup_funcinfo = &unit->lookup_funcinfo_table[mid];
arange = arange->next) if (addr < lookup_funcinfo->low_addr)
high = mid;
else if (addr >= lookup_funcinfo->high_addr)
low = mid + 1;
else
high = first = mid;
}
/* Find the 'best' match for the address. The prior algorithm defined the
best match as the function with the smallest address range containing
the specified address. This definition should probably be changed to the
innermost inline routine containing the address, but right now we want
to get the same results we did before. */
while (first < number_of_functions)
{ {
if (addr >= arange->low && addr < arange->high) if (addr < unit->lookup_funcinfo_table[first].low_addr)
break;
funcinfo = unit->lookup_funcinfo_table[first].funcinfo;
for (arange = &funcinfo->arange; arange; arange = arange->next)
{ {
if (addr < arange->low || addr >= arange->high)
continue;
if (!best_fit if (!best_fit
|| arange->high - arange->low < best_fit_len) || arange->high - arange->low < best_fit_len
/* The following comparison is designed to return the same
match as the previous algorithm for routines which have the
same best fit length. */
|| (arange->high - arange->low == best_fit_len
&& funcinfo > best_fit))
{ {
best_fit = each_func; best_fit = funcinfo;
best_fit_len = arange->high - arange->low; best_fit_len = arange->high - arange->low;
} }
} }
}
first++;
} }
if (best_fit) if (!best_fit)
{ return FALSE;
*function_ptr = best_fit; *function_ptr = best_fit;
return TRUE; return TRUE;
}
return FALSE;
} }
/* If SYM at ADDR is within function table of UNIT, set FILENAME_PTR /* If SYM at ADDR is within function table of UNIT, set FILENAME_PTR
@ -2271,7 +2477,7 @@ lookup_symbol_in_variable_table (struct comp_unit *unit,
*linenumber_ptr = each->line; *linenumber_ptr = each->line;
return TRUE; return TRUE;
} }
else
return FALSE; return FALSE;
} }
@ -2515,6 +2721,7 @@ scan_unit_for_symbols (struct comp_unit *unit)
func->tag = abbrev->tag; func->tag = abbrev->tag;
func->prev_func = unit->function_table; func->prev_func = unit->function_table;
unit->function_table = func; unit->function_table = func;
unit->number_of_functions++;
BFD_ASSERT (!unit->cached); BFD_ASSERT (!unit->cached);
if (func->tag == DW_TAG_inlined_subroutine) if (func->tag == DW_TAG_inlined_subroutine)
@ -4244,6 +4451,12 @@ _bfd_dwarf2_cleanup_debug_info (bfd *abfd, void **pinfo)
function_table = function_table->prev_func; function_table = function_table->prev_func;
} }
if (each->lookup_funcinfo_table)
{
free (each->lookup_funcinfo_table);
each->lookup_funcinfo_table = NULL;
}
while (variable_table) while (variable_table)
{ {
if (variable_table->file) if (variable_table->file)