mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
Add symbol lookup cache.
gdb/ChangeLog: Add symbol lookup cache. * NEWS: Document new options and commands. * symtab.c (symbol_cache_key): New static global. (DEFAULT_SYMBOL_CACHE_SIZE, MAX_SYMBOL_CACHE_SIZE): New macros. (SYMBOL_LOOKUP_FAILED): New macro. (symbol_cache_slot_state): New enum. (block_symbol_cache): New struct. (symbol_cache): New struct. (new_symbol_cache_size, symbol_cache_size): New static globals. (hash_symbol_entry, eq_symbol_entry): New functions. (symbol_cache_byte_size, resize_symbol_cache): New functions. (make_symbol_cache, free_symbol_cache): New functions. (get_symbol_cache, symbol_cache_cleanup): New function. (set_symbol_cache_size, set_symbol_cache_size_handler): New functions. (symbol_cache_lookup, symbol_cache_clear_slot): New function. (symbol_cache_mark_found, symbol_cache_mark_not_found): New functions. (symbol_cache_flush, symbol_cache_dump): New functions. (maintenance_print_symbol_cache): New function. (maintenance_flush_symbol_cache): New function. (symbol_cache_stats): New function. (maintenance_print_symbol_cache_statistics): New function. (symtab_new_objfile_observer): New function. (symtab_free_objfile_observer): New function. (lookup_static_symbol, lookup_global_symbol): Use symbol cache. (_initialize_symtab): Init symbol_cache_key. New parameter maint symbol-cache-size. New maint commands print symbol-cache, print symbol-cache-statistics, flush-symbol-cache. Install new_objfile, free_objfile observers. gdb/doc/ChangeLog: * gdb.texinfo (Symbols): Document new commands "maint print symbol-cache", "maint print symbol-cache-statistics", "maint flush-symbol-cache". Document new option "maint set symbol-cache-size".
This commit is contained in:
parent
e700d1b279
commit
f57d2163da
@ -1,3 +1,34 @@
|
|||||||
|
2015-01-31 Doug Evans <xdje42@gmail.com>
|
||||||
|
|
||||||
|
Add symbol lookup cache.
|
||||||
|
* NEWS: Document new options and commands.
|
||||||
|
* symtab.c (symbol_cache_key): New static global.
|
||||||
|
(DEFAULT_SYMBOL_CACHE_SIZE, MAX_SYMBOL_CACHE_SIZE): New macros.
|
||||||
|
(SYMBOL_LOOKUP_FAILED): New macro.
|
||||||
|
(symbol_cache_slot_state): New enum.
|
||||||
|
(block_symbol_cache): New struct.
|
||||||
|
(symbol_cache): New struct.
|
||||||
|
(new_symbol_cache_size, symbol_cache_size): New static globals.
|
||||||
|
(hash_symbol_entry, eq_symbol_entry): New functions.
|
||||||
|
(symbol_cache_byte_size, resize_symbol_cache): New functions.
|
||||||
|
(make_symbol_cache, free_symbol_cache): New functions.
|
||||||
|
(get_symbol_cache, symbol_cache_cleanup): New function.
|
||||||
|
(set_symbol_cache_size, set_symbol_cache_size_handler): New functions.
|
||||||
|
(symbol_cache_lookup, symbol_cache_clear_slot): New function.
|
||||||
|
(symbol_cache_mark_found, symbol_cache_mark_not_found): New functions.
|
||||||
|
(symbol_cache_flush, symbol_cache_dump): New functions.
|
||||||
|
(maintenance_print_symbol_cache): New function.
|
||||||
|
(maintenance_flush_symbol_cache): New function.
|
||||||
|
(symbol_cache_stats): New function.
|
||||||
|
(maintenance_print_symbol_cache_statistics): New function.
|
||||||
|
(symtab_new_objfile_observer): New function.
|
||||||
|
(symtab_free_objfile_observer): New function.
|
||||||
|
(lookup_static_symbol, lookup_global_symbol): Use symbol cache.
|
||||||
|
(_initialize_symtab): Init symbol_cache_key. New parameter
|
||||||
|
maint symbol-cache-size. New maint commands print symbol-cache,
|
||||||
|
print symbol-cache-statistics, flush-symbol-cache.
|
||||||
|
Install new_objfile, free_objfile observers.
|
||||||
|
|
||||||
2015-01-31 Joel Brobecker <brobecker@adacore.com>
|
2015-01-31 Joel Brobecker <brobecker@adacore.com>
|
||||||
|
|
||||||
PR symtab/17855
|
PR symtab/17855
|
||||||
|
15
gdb/NEWS
15
gdb/NEWS
@ -13,8 +13,23 @@
|
|||||||
which is the name of the objfile as specified by the user,
|
which is the name of the objfile as specified by the user,
|
||||||
without, for example, resolving symlinks.
|
without, for example, resolving symlinks.
|
||||||
|
|
||||||
|
* New commands
|
||||||
|
|
||||||
|
maint print symbol-cache
|
||||||
|
Print the contents of the symbol cache.
|
||||||
|
|
||||||
|
maint print symbol-cache-statistics
|
||||||
|
Print statistics of symbol cache usage.
|
||||||
|
|
||||||
|
maint flush-symbol-cache
|
||||||
|
Flush the contents of the symbol cache.
|
||||||
|
|
||||||
* New options
|
* New options
|
||||||
|
|
||||||
|
maint set symbol-cache-size
|
||||||
|
maint show symbol-cache-size
|
||||||
|
Control the size of the symbol cache.
|
||||||
|
|
||||||
* The command 'thread apply all' can now support new option '-ascending'
|
* The command 'thread apply all' can now support new option '-ascending'
|
||||||
to call its specified command for all threads in ascending order.
|
to call its specified command for all threads in ascending order.
|
||||||
|
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
2015-01-31 Doug Evans <xdje42@gmail.com>
|
||||||
|
|
||||||
|
* gdb.texinfo (Symbols): Document new commands
|
||||||
|
"maint print symbol-cache", "maint print symbol-cache-statistics",
|
||||||
|
"maint flush-symbol-cache". Document new option
|
||||||
|
"maint set symbol-cache-size".
|
||||||
|
|
||||||
2015-01-31 Doug Evans <xdje42@gmail.com>
|
2015-01-31 Doug Evans <xdje42@gmail.com>
|
||||||
|
|
||||||
* gdb.texinfo (dotdebug_gdb_scripts section): Update docs to
|
* gdb.texinfo (dotdebug_gdb_scripts section): Update docs to
|
||||||
|
@ -16495,8 +16495,39 @@ line 1574.
|
|||||||
@}
|
@}
|
||||||
(@value{GDBP})
|
(@value{GDBP})
|
||||||
@end smallexample
|
@end smallexample
|
||||||
@end table
|
|
||||||
|
|
||||||
|
@kindex maint set symbol-cache-size
|
||||||
|
@cindex symbol cache size
|
||||||
|
@item maint set symbol-cache-size @var{size}
|
||||||
|
Set the size of the symbol cache to @var{size}.
|
||||||
|
The default size is intended to be good enough for debugging
|
||||||
|
most applications. This option exists to allow for experimenting
|
||||||
|
with different sizes.
|
||||||
|
|
||||||
|
@kindex maint show symbol-cache-size
|
||||||
|
@item maint show symbol-cache-size
|
||||||
|
Show the size of the symbol cache.
|
||||||
|
|
||||||
|
@kindex maint print symbol-cache
|
||||||
|
@cindex symbol cache, printing its contents
|
||||||
|
@item maint print symbol-cache
|
||||||
|
Print the contents of the symbol cache.
|
||||||
|
This is useful when debugging symbol cache issues.
|
||||||
|
|
||||||
|
@kindex maint print symbol-cache-statistics
|
||||||
|
@cindex symbol cache, printing usage statistics
|
||||||
|
@item maint print symbol-cache-statistics
|
||||||
|
Print symbol cache usage statistics.
|
||||||
|
This helps determine how well the cache is being utilized.
|
||||||
|
|
||||||
|
@kindex maint flush-symbol-cache
|
||||||
|
@cindex symbol cache, flushing
|
||||||
|
@item maint flush-symbol-cache
|
||||||
|
Flush the contents of the symbol cache, all entries are removed.
|
||||||
|
This command is useful when debugging the symbol cache.
|
||||||
|
It is also useful when collecting performance data.
|
||||||
|
|
||||||
|
@end table
|
||||||
|
|
||||||
@node Altering
|
@node Altering
|
||||||
@chapter Altering Execution
|
@chapter Altering Execution
|
||||||
|
747
gdb/symtab.c
747
gdb/symtab.c
@ -102,12 +102,115 @@ struct main_info
|
|||||||
enum language language_of_main;
|
enum language language_of_main;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Program space key for finding its symbol cache. */
|
||||||
|
|
||||||
|
static const struct program_space_data *symbol_cache_key;
|
||||||
|
|
||||||
|
/* The default symbol cache size.
|
||||||
|
There is no extra cpu cost for large N (except when flushing the cache,
|
||||||
|
which is rare). The value here is just a first attempt. A better default
|
||||||
|
value may be higher or lower. A prime number can make up for a bad hash
|
||||||
|
computation, so that's why the number is what it is. */
|
||||||
|
#define DEFAULT_SYMBOL_CACHE_SIZE 1021
|
||||||
|
|
||||||
|
/* The maximum symbol cache size.
|
||||||
|
There's no method to the decision of what value to use here, other than
|
||||||
|
there's no point in allowing a user typo to make gdb consume all memory. */
|
||||||
|
#define MAX_SYMBOL_CACHE_SIZE (1024*1024)
|
||||||
|
|
||||||
|
/* symbol_cache_lookup returns this if a previous lookup failed to find the
|
||||||
|
symbol in any objfile. */
|
||||||
|
#define SYMBOL_LOOKUP_FAILED ((struct symbol *) 1)
|
||||||
|
|
||||||
|
/* Recording lookups that don't find the symbol is just as important, if not
|
||||||
|
more so, than recording found symbols. */
|
||||||
|
|
||||||
|
enum symbol_cache_slot_state
|
||||||
|
{
|
||||||
|
SYMBOL_SLOT_UNUSED,
|
||||||
|
SYMBOL_SLOT_NOT_FOUND,
|
||||||
|
SYMBOL_SLOT_FOUND
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Symbols don't specify global vs static block.
|
||||||
|
So keep them in separate caches. */
|
||||||
|
|
||||||
|
struct block_symbol_cache
|
||||||
|
{
|
||||||
|
unsigned int hits;
|
||||||
|
unsigned int misses;
|
||||||
|
unsigned int collisions;
|
||||||
|
|
||||||
|
/* SYMBOLS is a variable length array of this size.
|
||||||
|
One can imagine that in general one cache (global/static) should be a
|
||||||
|
fraction of the size of the other, but there's no data at the moment
|
||||||
|
on which to decide. */
|
||||||
|
unsigned int size;
|
||||||
|
|
||||||
|
struct symbol_cache_slot
|
||||||
|
{
|
||||||
|
enum symbol_cache_slot_state state;
|
||||||
|
|
||||||
|
/* The objfile that was current when the symbol was looked up.
|
||||||
|
This is only needed for global blocks, but for simplicity's sake
|
||||||
|
we allocate the space for both. If data shows the extra space used
|
||||||
|
for static blocks is a problem, we can split things up then.
|
||||||
|
|
||||||
|
Global blocks need cache lookup to include the objfile context because
|
||||||
|
we need to account for gdbarch_iterate_over_objfiles_in_search_order
|
||||||
|
which can traverse objfiles in, effectively, any order, depending on
|
||||||
|
the current objfile, thus affecting which symbol is found. Normally,
|
||||||
|
only the current objfile is searched first, and then the rest are
|
||||||
|
searched in recorded order; but putting cache lookup inside
|
||||||
|
gdbarch_iterate_over_objfiles_in_search_order would be awkward.
|
||||||
|
Instead we just make the current objfile part of the context of
|
||||||
|
cache lookup. This means we can record the same symbol multiple times,
|
||||||
|
each with a different "current objfile" that was in effect when the
|
||||||
|
lookup was saved in the cache, but cache space is pretty cheap. */
|
||||||
|
const struct objfile *objfile_context;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct symbol *found;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
domain_enum domain;
|
||||||
|
} not_found;
|
||||||
|
} value;
|
||||||
|
} symbols[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The symbol cache.
|
||||||
|
|
||||||
|
Searching for symbols in the static and global blocks over multiple objfiles
|
||||||
|
again and again can be slow, as can searching very big objfiles. This is a
|
||||||
|
simple cache to improve symbol lookup performance, which is critical to
|
||||||
|
overall gdb performance.
|
||||||
|
|
||||||
|
Symbols are hashed on the name, its domain, and block.
|
||||||
|
They are also hashed on their objfile for objfile-specific lookups. */
|
||||||
|
|
||||||
|
struct symbol_cache
|
||||||
|
{
|
||||||
|
struct block_symbol_cache *global_symbols;
|
||||||
|
struct block_symbol_cache *static_symbols;
|
||||||
|
};
|
||||||
|
|
||||||
/* When non-zero, print debugging messages related to symtab creation. */
|
/* When non-zero, print debugging messages related to symtab creation. */
|
||||||
unsigned int symtab_create_debug = 0;
|
unsigned int symtab_create_debug = 0;
|
||||||
|
|
||||||
/* When non-zero, print debugging messages related to symbol lookup. */
|
/* When non-zero, print debugging messages related to symbol lookup. */
|
||||||
unsigned int symbol_lookup_debug = 0;
|
unsigned int symbol_lookup_debug = 0;
|
||||||
|
|
||||||
|
/* The size of the cache is staged here. */
|
||||||
|
static unsigned int new_symbol_cache_size = DEFAULT_SYMBOL_CACHE_SIZE;
|
||||||
|
|
||||||
|
/* The current value of the symbol cache size.
|
||||||
|
This is saved so that if the user enters a value too big we can restore
|
||||||
|
the original value from here. */
|
||||||
|
static unsigned int symbol_cache_size = DEFAULT_SYMBOL_CACHE_SIZE;
|
||||||
|
|
||||||
/* Non-zero if a file may be known by two different basenames.
|
/* Non-zero if a file may be known by two different basenames.
|
||||||
This is the uncommon case, and significantly slows down gdb.
|
This is the uncommon case, and significantly slows down gdb.
|
||||||
Default set to "off" to not slow down the common case. */
|
Default set to "off" to not slow down the common case. */
|
||||||
@ -1058,6 +1161,552 @@ expand_symtab_containing_pc (CORE_ADDR pc, struct obj_section *section)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hash function for the symbol cache. */
|
||||||
|
|
||||||
|
static unsigned int
|
||||||
|
hash_symbol_entry (const struct objfile *objfile_context,
|
||||||
|
const char *name, domain_enum domain)
|
||||||
|
{
|
||||||
|
unsigned int hash = (uintptr_t) objfile_context;
|
||||||
|
|
||||||
|
if (name != NULL)
|
||||||
|
hash += htab_hash_string (name);
|
||||||
|
|
||||||
|
hash += domain;
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Equality function for the symbol cache. */
|
||||||
|
|
||||||
|
static int
|
||||||
|
eq_symbol_entry (const struct symbol_cache_slot *slot,
|
||||||
|
const struct objfile *objfile_context,
|
||||||
|
const char *name, domain_enum domain)
|
||||||
|
{
|
||||||
|
const char *slot_name;
|
||||||
|
domain_enum slot_domain;
|
||||||
|
|
||||||
|
if (slot->state == SYMBOL_SLOT_UNUSED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (slot->objfile_context != objfile_context)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (slot->state == SYMBOL_SLOT_NOT_FOUND)
|
||||||
|
{
|
||||||
|
slot_name = slot->value.not_found.name;
|
||||||
|
slot_domain = slot->value.not_found.domain;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
slot_name = SYMBOL_SEARCH_NAME (slot->value.found);
|
||||||
|
slot_domain = SYMBOL_DOMAIN (slot->value.found);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NULL names match. */
|
||||||
|
if (slot_name == NULL && name == NULL)
|
||||||
|
{
|
||||||
|
/* But there's no point in calling symbol_matches_domain in the
|
||||||
|
SYMBOL_SLOT_FOUND case. */
|
||||||
|
if (slot_domain != domain)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (slot_name != NULL && name != NULL)
|
||||||
|
{
|
||||||
|
/* It's important that we use the same comparison that was done the
|
||||||
|
first time through. If the slot records a found symbol, then this
|
||||||
|
means using strcmp_iw on SYMBOL_SEARCH_NAME. See dictionary.c.
|
||||||
|
It also means using symbol_matches_domain for found symbols.
|
||||||
|
See block.c.
|
||||||
|
|
||||||
|
If the slot records a not-found symbol, then require a precise match.
|
||||||
|
We could still be lax with whitespace like strcmp_iw though. */
|
||||||
|
|
||||||
|
if (slot->state == SYMBOL_SLOT_NOT_FOUND)
|
||||||
|
{
|
||||||
|
if (strcmp (slot_name, name) != 0)
|
||||||
|
return 0;
|
||||||
|
if (slot_domain != domain)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct symbol *sym = slot->value.found;
|
||||||
|
|
||||||
|
if (strcmp_iw (slot_name, name) != 0)
|
||||||
|
return 0;
|
||||||
|
if (!symbol_matches_domain (SYMBOL_LANGUAGE (sym),
|
||||||
|
slot_domain, domain))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Only one name is NULL. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a cache of size SIZE, return the size of the struct (with variable
|
||||||
|
length array) in bytes. */
|
||||||
|
|
||||||
|
static size_t
|
||||||
|
symbol_cache_byte_size (unsigned int size)
|
||||||
|
{
|
||||||
|
return (sizeof (struct block_symbol_cache)
|
||||||
|
+ ((size - 1) * sizeof (struct symbol_cache_slot)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resize CACHE. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
resize_symbol_cache (struct symbol_cache *cache, unsigned int new_size)
|
||||||
|
{
|
||||||
|
/* If there's no change in size, don't do anything.
|
||||||
|
All caches have the same size, so we can just compare with the size
|
||||||
|
of the global symbols cache. */
|
||||||
|
if ((cache->global_symbols != NULL
|
||||||
|
&& cache->global_symbols->size == new_size)
|
||||||
|
|| (cache->global_symbols == NULL
|
||||||
|
&& new_size == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
xfree (cache->global_symbols);
|
||||||
|
xfree (cache->static_symbols);
|
||||||
|
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
cache->global_symbols = NULL;
|
||||||
|
cache->static_symbols = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t total_size = symbol_cache_byte_size (new_size);
|
||||||
|
|
||||||
|
cache->global_symbols = xcalloc (1, total_size);
|
||||||
|
cache->static_symbols = xcalloc (1, total_size);
|
||||||
|
cache->global_symbols->size = new_size;
|
||||||
|
cache->static_symbols->size = new_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make a symbol cache of size SIZE. */
|
||||||
|
|
||||||
|
static struct symbol_cache *
|
||||||
|
make_symbol_cache (unsigned int size)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache;
|
||||||
|
|
||||||
|
cache = XCNEW (struct symbol_cache);
|
||||||
|
resize_symbol_cache (cache, symbol_cache_size);
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free the space used by CACHE. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_symbol_cache (struct symbol_cache *cache)
|
||||||
|
{
|
||||||
|
xfree (cache->global_symbols);
|
||||||
|
xfree (cache->static_symbols);
|
||||||
|
xfree (cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the symbol cache of PSPACE.
|
||||||
|
Create one if it doesn't exist yet. */
|
||||||
|
|
||||||
|
static struct symbol_cache *
|
||||||
|
get_symbol_cache (struct program_space *pspace)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache = program_space_data (pspace, symbol_cache_key);
|
||||||
|
|
||||||
|
if (cache == NULL)
|
||||||
|
{
|
||||||
|
cache = make_symbol_cache (symbol_cache_size);
|
||||||
|
set_program_space_data (pspace, symbol_cache_key, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Delete the symbol cache of PSPACE.
|
||||||
|
Called when PSPACE is destroyed. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_cleanup (struct program_space *pspace, void *data)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache = data;
|
||||||
|
|
||||||
|
free_symbol_cache (cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the size of the symbol cache in all program spaces. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_symbol_cache_size (unsigned int new_size)
|
||||||
|
{
|
||||||
|
struct program_space *pspace;
|
||||||
|
|
||||||
|
ALL_PSPACES (pspace)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache
|
||||||
|
= program_space_data (pspace, symbol_cache_key);
|
||||||
|
|
||||||
|
/* The pspace could have been created but not have a cache yet. */
|
||||||
|
if (cache != NULL)
|
||||||
|
resize_symbol_cache (cache, new_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called when symbol-cache-size is set. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_symbol_cache_size_handler (char *args, int from_tty,
|
||||||
|
struct cmd_list_element *c)
|
||||||
|
{
|
||||||
|
if (new_symbol_cache_size > MAX_SYMBOL_CACHE_SIZE)
|
||||||
|
{
|
||||||
|
/* Restore the previous value.
|
||||||
|
This is the value the "show" command prints. */
|
||||||
|
new_symbol_cache_size = symbol_cache_size;
|
||||||
|
|
||||||
|
error (_("Symbol cache size is too large, max is %u."),
|
||||||
|
MAX_SYMBOL_CACHE_SIZE);
|
||||||
|
}
|
||||||
|
symbol_cache_size = new_symbol_cache_size;
|
||||||
|
|
||||||
|
set_symbol_cache_size (symbol_cache_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lookup symbol NAME,DOMAIN in BLOCK in the symbol cache of PSPACE.
|
||||||
|
OBJFILE_CONTEXT is the current objfile, which may be NULL.
|
||||||
|
The result is the symbol if found, SYMBOL_LOOKUP_FAILED if a previous lookup
|
||||||
|
failed (and thus this one will too), or NULL if the symbol is not present
|
||||||
|
in the cache.
|
||||||
|
*BSC_PTR, *SLOT_PTR are set to the cache and slot of the symbol, whether
|
||||||
|
found or not found. */
|
||||||
|
|
||||||
|
static struct symbol *
|
||||||
|
symbol_cache_lookup (struct symbol_cache *cache,
|
||||||
|
struct objfile *objfile_context, int block,
|
||||||
|
const char *name, domain_enum domain,
|
||||||
|
struct block_symbol_cache **bsc_ptr,
|
||||||
|
struct symbol_cache_slot **slot_ptr)
|
||||||
|
{
|
||||||
|
struct block_symbol_cache *bsc;
|
||||||
|
unsigned int hash;
|
||||||
|
struct symbol_cache_slot *slot;
|
||||||
|
|
||||||
|
if (block == GLOBAL_BLOCK)
|
||||||
|
bsc = cache->global_symbols;
|
||||||
|
else
|
||||||
|
bsc = cache->static_symbols;
|
||||||
|
if (bsc == NULL)
|
||||||
|
{
|
||||||
|
*bsc_ptr = NULL;
|
||||||
|
*slot_ptr = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = hash_symbol_entry (objfile_context, name, domain);
|
||||||
|
slot = bsc->symbols + hash % bsc->size;
|
||||||
|
*bsc_ptr = bsc;
|
||||||
|
*slot_ptr = slot;
|
||||||
|
|
||||||
|
if (eq_symbol_entry (slot, objfile_context, name, domain))
|
||||||
|
{
|
||||||
|
if (symbol_lookup_debug)
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"%s block symbol cache hit%s for %s, %s\n",
|
||||||
|
block == GLOBAL_BLOCK ? "Global" : "Static",
|
||||||
|
slot->state == SYMBOL_SLOT_NOT_FOUND
|
||||||
|
? " (not found)" : "",
|
||||||
|
name, domain_name (domain));
|
||||||
|
++bsc->hits;
|
||||||
|
if (slot->state == SYMBOL_SLOT_NOT_FOUND)
|
||||||
|
return SYMBOL_LOOKUP_FAILED;
|
||||||
|
return slot->value.found;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (symbol_lookup_debug)
|
||||||
|
{
|
||||||
|
fprintf_unfiltered (gdb_stdlog,
|
||||||
|
"%s block symbol cache miss for %s, %s\n",
|
||||||
|
block == GLOBAL_BLOCK ? "Global" : "Static",
|
||||||
|
name, domain_name (domain));
|
||||||
|
}
|
||||||
|
++bsc->misses;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear out SLOT. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_clear_slot (struct symbol_cache_slot *slot)
|
||||||
|
{
|
||||||
|
if (slot->state == SYMBOL_SLOT_NOT_FOUND)
|
||||||
|
xfree (slot->value.not_found.name);
|
||||||
|
slot->state = SYMBOL_SLOT_UNUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark SYMBOL as found in SLOT.
|
||||||
|
OBJFILE_CONTEXT is the current objfile when the lookup was done, or NULL
|
||||||
|
if it's not needed to distinguish lookups (STATIC_BLOCK). It is *not*
|
||||||
|
necessarily the objfile the symbol was found in. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_mark_found (struct block_symbol_cache *bsc,
|
||||||
|
struct symbol_cache_slot *slot,
|
||||||
|
struct objfile *objfile_context,
|
||||||
|
struct symbol *symbol)
|
||||||
|
{
|
||||||
|
if (bsc == NULL)
|
||||||
|
return;
|
||||||
|
if (slot->state != SYMBOL_SLOT_UNUSED)
|
||||||
|
{
|
||||||
|
++bsc->collisions;
|
||||||
|
symbol_cache_clear_slot (slot);
|
||||||
|
}
|
||||||
|
slot->state = SYMBOL_SLOT_FOUND;
|
||||||
|
slot->objfile_context = objfile_context;
|
||||||
|
slot->value.found = symbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mark symbol NAME, DOMAIN as not found in SLOT.
|
||||||
|
OBJFILE_CONTEXT is the current objfile when the lookup was done, or NULL
|
||||||
|
if it's not needed to distinguish lookups (STATIC_BLOCK). */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_mark_not_found (struct block_symbol_cache *bsc,
|
||||||
|
struct symbol_cache_slot *slot,
|
||||||
|
struct objfile *objfile_context,
|
||||||
|
const char *name, domain_enum domain)
|
||||||
|
{
|
||||||
|
if (bsc == NULL)
|
||||||
|
return;
|
||||||
|
if (slot->state != SYMBOL_SLOT_UNUSED)
|
||||||
|
{
|
||||||
|
++bsc->collisions;
|
||||||
|
symbol_cache_clear_slot (slot);
|
||||||
|
}
|
||||||
|
slot->state = SYMBOL_SLOT_NOT_FOUND;
|
||||||
|
slot->objfile_context = objfile_context;
|
||||||
|
slot->value.not_found.name = xstrdup (name);
|
||||||
|
slot->value.not_found.domain = domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Flush the symbol cache of PSPACE. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_flush (struct program_space *pspace)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache = program_space_data (pspace, symbol_cache_key);
|
||||||
|
int pass;
|
||||||
|
size_t total_size;
|
||||||
|
|
||||||
|
if (cache == NULL)
|
||||||
|
return;
|
||||||
|
if (cache->global_symbols == NULL)
|
||||||
|
{
|
||||||
|
gdb_assert (symbol_cache_size == 0);
|
||||||
|
gdb_assert (cache->static_symbols == NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the cache is untouched since the last flush, early exit.
|
||||||
|
This is important for performance during the startup of a program linked
|
||||||
|
with 100s (or 1000s) of shared libraries. */
|
||||||
|
if (cache->global_symbols->misses == 0
|
||||||
|
&& cache->static_symbols->misses == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gdb_assert (cache->global_symbols->size == symbol_cache_size);
|
||||||
|
gdb_assert (cache->static_symbols->size == symbol_cache_size);
|
||||||
|
|
||||||
|
for (pass = 0; pass < 2; ++pass)
|
||||||
|
{
|
||||||
|
struct block_symbol_cache *bsc
|
||||||
|
= pass == 0 ? cache->global_symbols : cache->static_symbols;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bsc->size; ++i)
|
||||||
|
symbol_cache_clear_slot (&bsc->symbols[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cache->global_symbols->hits = 0;
|
||||||
|
cache->global_symbols->misses = 0;
|
||||||
|
cache->global_symbols->collisions = 0;
|
||||||
|
cache->static_symbols->hits = 0;
|
||||||
|
cache->static_symbols->misses = 0;
|
||||||
|
cache->static_symbols->collisions = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dump CACHE. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_dump (const struct symbol_cache *cache)
|
||||||
|
{
|
||||||
|
int pass;
|
||||||
|
|
||||||
|
if (cache->global_symbols == NULL)
|
||||||
|
{
|
||||||
|
printf_filtered (" <disabled>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pass = 0; pass < 2; ++pass)
|
||||||
|
{
|
||||||
|
const struct block_symbol_cache *bsc
|
||||||
|
= pass == 0 ? cache->global_symbols : cache->static_symbols;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
if (pass == 0)
|
||||||
|
printf_filtered ("Global symbols:\n");
|
||||||
|
else
|
||||||
|
printf_filtered ("Static symbols:\n");
|
||||||
|
|
||||||
|
for (i = 0; i < bsc->size; ++i)
|
||||||
|
{
|
||||||
|
const struct symbol_cache_slot *slot = &bsc->symbols[i];
|
||||||
|
|
||||||
|
QUIT;
|
||||||
|
|
||||||
|
switch (slot->state)
|
||||||
|
{
|
||||||
|
case SYMBOL_SLOT_UNUSED:
|
||||||
|
break;
|
||||||
|
case SYMBOL_SLOT_NOT_FOUND:
|
||||||
|
printf_filtered (" [%-4u] = %s, %s (not found)\n", i,
|
||||||
|
host_address_to_string (slot->objfile_context),
|
||||||
|
slot->value.not_found.name);
|
||||||
|
break;
|
||||||
|
case SYMBOL_SLOT_FOUND:
|
||||||
|
printf_filtered (" [%-4u] = %s, %s\n", i,
|
||||||
|
host_address_to_string (slot->objfile_context),
|
||||||
|
SYMBOL_PRINT_NAME (slot->value.found));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "mt print symbol-cache" command. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
maintenance_print_symbol_cache (char *args, int from_tty)
|
||||||
|
{
|
||||||
|
struct program_space *pspace;
|
||||||
|
|
||||||
|
ALL_PSPACES (pspace)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache;
|
||||||
|
|
||||||
|
printf_filtered (_("Symbol cache for pspace %d\n%s:\n"),
|
||||||
|
pspace->num,
|
||||||
|
pspace->symfile_object_file != NULL
|
||||||
|
? objfile_name (pspace->symfile_object_file)
|
||||||
|
: "(no object file)");
|
||||||
|
|
||||||
|
/* If the cache hasn't been created yet, avoid creating one. */
|
||||||
|
cache = program_space_data (pspace, symbol_cache_key);
|
||||||
|
if (cache == NULL)
|
||||||
|
printf_filtered (" <empty>\n");
|
||||||
|
else
|
||||||
|
symbol_cache_dump (cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "mt flush-symbol-cache" command. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
maintenance_flush_symbol_cache (char *args, int from_tty)
|
||||||
|
{
|
||||||
|
struct program_space *pspace;
|
||||||
|
|
||||||
|
ALL_PSPACES (pspace)
|
||||||
|
{
|
||||||
|
symbol_cache_flush (pspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print usage statistics of CACHE. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symbol_cache_stats (struct symbol_cache *cache)
|
||||||
|
{
|
||||||
|
int pass;
|
||||||
|
|
||||||
|
if (cache->global_symbols == NULL)
|
||||||
|
{
|
||||||
|
printf_filtered (" <disabled>\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pass = 0; pass < 2; ++pass)
|
||||||
|
{
|
||||||
|
const struct block_symbol_cache *bsc
|
||||||
|
= pass == 0 ? cache->global_symbols : cache->static_symbols;
|
||||||
|
|
||||||
|
QUIT;
|
||||||
|
|
||||||
|
if (pass == 0)
|
||||||
|
printf_filtered ("Global block cache stats:\n");
|
||||||
|
else
|
||||||
|
printf_filtered ("Static block cache stats:\n");
|
||||||
|
|
||||||
|
printf_filtered (" size: %u\n", bsc->size);
|
||||||
|
printf_filtered (" hits: %u\n", bsc->hits);
|
||||||
|
printf_filtered (" misses: %u\n", bsc->misses);
|
||||||
|
printf_filtered (" collisions: %u\n", bsc->collisions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The "mt print symbol-cache-statistics" command. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
maintenance_print_symbol_cache_statistics (char *args, int from_tty)
|
||||||
|
{
|
||||||
|
struct program_space *pspace;
|
||||||
|
|
||||||
|
ALL_PSPACES (pspace)
|
||||||
|
{
|
||||||
|
struct symbol_cache *cache;
|
||||||
|
|
||||||
|
printf_filtered (_("Symbol cache statistics for pspace %d\n%s:\n"),
|
||||||
|
pspace->num,
|
||||||
|
pspace->symfile_object_file != NULL
|
||||||
|
? objfile_name (pspace->symfile_object_file)
|
||||||
|
: "(no object file)");
|
||||||
|
|
||||||
|
/* If the cache hasn't been created yet, avoid creating one. */
|
||||||
|
cache = program_space_data (pspace, symbol_cache_key);
|
||||||
|
if (cache == NULL)
|
||||||
|
printf_filtered (" empty, no stats available\n");
|
||||||
|
else
|
||||||
|
symbol_cache_stats (cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This module's 'new_objfile' observer. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symtab_new_objfile_observer (struct objfile *objfile)
|
||||||
|
{
|
||||||
|
/* Ideally we'd use OBJFILE->pspace, but OBJFILE may be NULL. */
|
||||||
|
symbol_cache_flush (current_program_space);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This module's 'free_objfile' observer. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
symtab_free_objfile_observer (struct objfile *objfile)
|
||||||
|
{
|
||||||
|
symbol_cache_flush (objfile->pspace);
|
||||||
|
}
|
||||||
|
|
||||||
/* Debug symbols usually don't have section information. We need to dig that
|
/* Debug symbols usually don't have section information. We need to dig that
|
||||||
out of the minimal symbols and stash that in the debug symbol. */
|
out of the minimal symbols and stash that in the debug symbol. */
|
||||||
|
|
||||||
@ -1970,16 +2619,36 @@ lookup_symbol_in_objfile (struct objfile *objfile, int block_index,
|
|||||||
struct symbol *
|
struct symbol *
|
||||||
lookup_static_symbol (const char *name, const domain_enum domain)
|
lookup_static_symbol (const char *name, const domain_enum domain)
|
||||||
{
|
{
|
||||||
|
struct symbol_cache *cache = get_symbol_cache (current_program_space);
|
||||||
struct objfile *objfile;
|
struct objfile *objfile;
|
||||||
struct symbol *result;
|
struct symbol *result;
|
||||||
|
struct block_symbol_cache *bsc;
|
||||||
|
struct symbol_cache_slot *slot;
|
||||||
|
|
||||||
|
/* Lookup in STATIC_BLOCK is not current-objfile-dependent, so just pass
|
||||||
|
NULL for OBJFILE_CONTEXT. */
|
||||||
|
result = symbol_cache_lookup (cache, NULL, STATIC_BLOCK, name, domain,
|
||||||
|
&bsc, &slot);
|
||||||
|
if (result != NULL)
|
||||||
|
{
|
||||||
|
if (result == SYMBOL_LOOKUP_FAILED)
|
||||||
|
return NULL;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
ALL_OBJFILES (objfile)
|
ALL_OBJFILES (objfile)
|
||||||
{
|
{
|
||||||
result = lookup_symbol_in_objfile (objfile, STATIC_BLOCK, name, domain);
|
result = lookup_symbol_in_objfile (objfile, STATIC_BLOCK, name, domain);
|
||||||
if (result != NULL)
|
if (result != NULL)
|
||||||
return result;
|
{
|
||||||
|
/* Still pass NULL for OBJFILE_CONTEXT here. */
|
||||||
|
symbol_cache_mark_found (bsc, slot, NULL, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Still pass NULL for OBJFILE_CONTEXT here. */
|
||||||
|
symbol_cache_mark_not_found (bsc, slot, NULL, name, domain);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2027,25 +2696,48 @@ lookup_global_symbol (const char *name,
|
|||||||
const struct block *block,
|
const struct block *block,
|
||||||
const domain_enum domain)
|
const domain_enum domain)
|
||||||
{
|
{
|
||||||
struct symbol *sym = NULL;
|
struct symbol_cache *cache = get_symbol_cache (current_program_space);
|
||||||
struct objfile *objfile = NULL;
|
struct symbol *sym;
|
||||||
|
struct objfile *objfile;
|
||||||
struct global_sym_lookup_data lookup_data;
|
struct global_sym_lookup_data lookup_data;
|
||||||
|
struct block_symbol_cache *bsc;
|
||||||
|
struct symbol_cache_slot *slot;
|
||||||
|
|
||||||
|
objfile = lookup_objfile_from_block (block);
|
||||||
|
|
||||||
|
/* First see if we can find the symbol in the cache.
|
||||||
|
This works because we use the current objfile to qualify the lookup. */
|
||||||
|
sym = symbol_cache_lookup (cache, objfile, GLOBAL_BLOCK, name, domain,
|
||||||
|
&bsc, &slot);
|
||||||
|
if (sym != NULL)
|
||||||
|
{
|
||||||
|
if (sym == SYMBOL_LOOKUP_FAILED)
|
||||||
|
return NULL;
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
/* Call library-specific lookup procedure. */
|
/* Call library-specific lookup procedure. */
|
||||||
objfile = lookup_objfile_from_block (block);
|
|
||||||
if (objfile != NULL)
|
if (objfile != NULL)
|
||||||
sym = solib_global_lookup (objfile, name, domain);
|
sym = solib_global_lookup (objfile, name, domain);
|
||||||
|
|
||||||
|
/* If that didn't work go a global search (of global blocks, heh). */
|
||||||
|
if (sym == NULL)
|
||||||
|
{
|
||||||
|
memset (&lookup_data, 0, sizeof (lookup_data));
|
||||||
|
lookup_data.name = name;
|
||||||
|
lookup_data.domain = domain;
|
||||||
|
gdbarch_iterate_over_objfiles_in_search_order
|
||||||
|
(objfile != NULL ? get_objfile_arch (objfile) : target_gdbarch (),
|
||||||
|
lookup_symbol_global_iterator_cb, &lookup_data, objfile);
|
||||||
|
sym = lookup_data.result;
|
||||||
|
}
|
||||||
|
|
||||||
if (sym != NULL)
|
if (sym != NULL)
|
||||||
return sym;
|
symbol_cache_mark_found (bsc, slot, objfile, sym);
|
||||||
|
else
|
||||||
|
symbol_cache_mark_not_found (bsc, slot, objfile, name, domain);
|
||||||
|
|
||||||
memset (&lookup_data, 0, sizeof (lookup_data));
|
return sym;
|
||||||
lookup_data.name = name;
|
|
||||||
lookup_data.domain = domain;
|
|
||||||
gdbarch_iterate_over_objfiles_in_search_order
|
|
||||||
(objfile != NULL ? get_objfile_arch (objfile) : target_gdbarch (),
|
|
||||||
lookup_symbol_global_iterator_cb, &lookup_data, objfile);
|
|
||||||
|
|
||||||
return lookup_data.result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
@ -5436,6 +6128,9 @@ _initialize_symtab (void)
|
|||||||
main_progspace_key
|
main_progspace_key
|
||||||
= register_program_space_data_with_cleanup (NULL, main_info_cleanup);
|
= register_program_space_data_with_cleanup (NULL, main_info_cleanup);
|
||||||
|
|
||||||
|
symbol_cache_key
|
||||||
|
= register_program_space_data_with_cleanup (NULL, symbol_cache_cleanup);
|
||||||
|
|
||||||
add_info ("variables", variables_info, _("\
|
add_info ("variables", variables_info, _("\
|
||||||
All global and static variable names, or those matching REGEXP."));
|
All global and static variable names, or those matching REGEXP."));
|
||||||
if (dbx_commands)
|
if (dbx_commands)
|
||||||
@ -5511,5 +6206,31 @@ When enabled (non-zero), symbol lookups are logged."),
|
|||||||
NULL, NULL,
|
NULL, NULL,
|
||||||
&setdebuglist, &showdebuglist);
|
&setdebuglist, &showdebuglist);
|
||||||
|
|
||||||
|
add_setshow_zuinteger_cmd ("symbol-cache-size", no_class,
|
||||||
|
&new_symbol_cache_size,
|
||||||
|
_("Set the size of the symbol cache."),
|
||||||
|
_("Show the size of the symbol cache."), _("\
|
||||||
|
The size of the symbol cache.\n\
|
||||||
|
If zero then the symbol cache is disabled."),
|
||||||
|
set_symbol_cache_size_handler, NULL,
|
||||||
|
&maintenance_set_cmdlist,
|
||||||
|
&maintenance_show_cmdlist);
|
||||||
|
|
||||||
|
add_cmd ("symbol-cache", class_maintenance, maintenance_print_symbol_cache,
|
||||||
|
_("Dump the symbol cache for each program space."),
|
||||||
|
&maintenanceprintlist);
|
||||||
|
|
||||||
|
add_cmd ("symbol-cache-statistics", class_maintenance,
|
||||||
|
maintenance_print_symbol_cache_statistics,
|
||||||
|
_("Print symbol cache statistics for each program space."),
|
||||||
|
&maintenanceprintlist);
|
||||||
|
|
||||||
|
add_cmd ("flush-symbol-cache", class_maintenance,
|
||||||
|
maintenance_flush_symbol_cache,
|
||||||
|
_("Flush the symbol cache for each program space."),
|
||||||
|
&maintenancelist);
|
||||||
|
|
||||||
observer_attach_executable_changed (symtab_observer_executable_changed);
|
observer_attach_executable_changed (symtab_observer_executable_changed);
|
||||||
|
observer_attach_new_objfile (symtab_new_objfile_observer);
|
||||||
|
observer_attach_free_objfile (symtab_free_objfile_observer);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user