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>
* 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. */
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. */
struct varinfo *variable_table;
@ -1214,9 +1220,9 @@ non_mangled (int lang)
struct line_info
{
struct line_info* prev_line;
struct line_info * prev_line;
bfd_vma address;
char *filename;
char * filename;
unsigned int line;
unsigned int column;
unsigned int discriminator;
@ -1226,7 +1232,7 @@ struct line_info
struct fileinfo
{
char *name;
char * name;
unsigned int dir;
unsigned int time;
unsigned int size;
@ -1237,11 +1243,13 @@ struct line_sequence
bfd_vma low_pc;
struct line_sequence* prev_sequence;
struct line_info* last_line; /* Largest VMA. */
struct line_info** line_info_lookup;
bfd_size_type num_lines;
};
struct line_info_table
{
bfd* abfd;
bfd * abfd;
unsigned int num_files;
unsigned int num_dirs;
unsigned int num_sequences;
@ -1260,23 +1268,37 @@ struct line_info_table
struct funcinfo
{
/* Pointer to previous function in list of all functions. */
struct funcinfo *prev_func;
struct funcinfo * prev_func;
/* Pointer to function one scope higher. */
struct funcinfo *caller_func;
struct funcinfo * caller_func;
/* Source location file name where caller_func inlines this func. */
char *caller_file;
char * caller_file;
/* Source location file name. */
char *file;
char * file;
/* Source location line number where caller_func inlines this func. */
int caller_line;
/* Source location line number. */
int line;
int tag;
bfd_boolean is_linkage;
const char *name;
bfd boolean is_linkage;
const char * name;
struct arange arange;
/* 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
@ -1579,6 +1601,51 @@ compare_sequences (const void* a, const void* b)
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. */
static bfd_boolean
@ -1610,6 +1677,8 @@ sort_line_sequences (struct line_info_table* table)
sequences[n].low_pc = seq->low_pc;
sequences[n].prev_sequence = NULL;
sequences[n].last_line = seq->last_line;
sequences[n].line_info_lookup = NULL;
sequences[n].num_lines = 0;
seq = seq->prev_sequence;
free (last_seq);
}
@ -2091,7 +2160,7 @@ lookup_address_in_line_info_table (struct line_info_table *table,
unsigned int *discriminator_ptr)
{
struct line_sequence *seq = NULL;
struct line_info *each_line;
struct line_info *info;
int low, high, mid;
/* Binary search the array of sequences. */
@ -2109,26 +2178,43 @@ lookup_address_in_line_info_table (struct line_info_table *table,
break;
}
if (seq && addr >= seq->low_pc && addr < seq->last_line->address)
{
/* Note: seq->last_line should be a descendingly sorted list. */
for (each_line = seq->last_line;
each_line;
each_line = each_line->prev_line)
if (addr >= each_line->address)
break;
/* Check for a valid sequence. */
if (!seq || addr < seq->low_pc || addr >= seq->last_line->address)
goto fail;
if (each_line
&& !(each_line->end_sequence || each_line == seq->last_line))
if (!build_line_info_table (table, seq))
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;
*linenumber_ptr = each_line->line;
mid = (low + high) / 2;
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)
*discriminator_ptr = each_line->discriminator;
*discriminator_ptr = info->discriminator;
return seq->last_line->address - seq->low_pc;
}
}
fail:
*filename_ptr = NULL;
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. */
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],
stash->syms, 0,
&stash->dwarf_ranges_buffer, &stash->dwarf_ranges_size);
&stash->dwarf_ranges_buffer,
&stash->dwarf_ranges_size);
}
/* 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
TRUE. Note that we need to find the function that has the smallest range
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,
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;
bfd_vma best_fit_len = 0;
bfd_size_type low, high, mid, first;
struct arange *arange;
for (each_func = unit->function_table;
each_func;
each_func = each_func->prev_func)
if (!build_lookup_funcinfo_table (unit))
return FALSE;
/* 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;
arange;
arange = arange->next)
mid = (low + high) / 2;
lookup_funcinfo = &unit->lookup_funcinfo_table[mid];
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
|| 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;
}
}
}
first++;
}
if (best_fit)
{
if (!best_fit)
return FALSE;
*function_ptr = best_fit;
return TRUE;
}
return FALSE;
}
/* 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;
return TRUE;
}
else
return FALSE;
}
@ -2515,6 +2721,7 @@ scan_unit_for_symbols (struct comp_unit *unit)
func->tag = abbrev->tag;
func->prev_func = unit->function_table;
unit->function_table = func;
unit->number_of_functions++;
BFD_ASSERT (!unit->cached);
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;
}
if (each->lookup_funcinfo_table)
{
free (each->lookup_funcinfo_table);
each->lookup_funcinfo_table = NULL;
}
while (variable_table)
{
if (variable_table->file)