+/* 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);
+
+ /* Because of symbol_matches_domain we need VAR_DOMAIN and STRUCT_DOMAIN
+ to map to the same slot. */
+ if (domain == STRUCT_DOMAIN)
+ hash += VAR_DOMAIN * 7;
+ else
+ hash += domain * 7;
+
+ 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.symbol);
+ slot_domain = SYMBOL_DOMAIN (slot->value.found.symbol);
+ }
+
+ /* 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.symbol;
+
+ 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.
+ If the symbol is not present in the cache, then *BSC_PTR and *SLOT_PTR are
+ set to the cache and slot of the symbol to save the result of a full lookup
+ attempt. */
+
+static struct block_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 (struct block_symbol) {NULL, NULL};
+ }
+
+ hash = hash_symbol_entry (objfile_context, name, domain);
+ slot = bsc->symbols + hash % bsc->size;
+
+ 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;
+ }
+
+ /* Symbol is not present in the cache. */
+
+ *bsc_ptr = bsc;
+ *slot_ptr = slot;
+
+ 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 (struct block_symbol) {NULL, 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,
+ const struct block *block)
+{
+ 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 = symbol;
+ slot->value.found.block = block;
+}
+
+/* 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 %s (not found)\n", i,
+ host_address_to_string (slot->objfile_context),
+ slot->value.not_found.name,
+ domain_name (slot->value.not_found.domain));
+ break;
+ case SYMBOL_SLOT_FOUND:
+ {
+ struct symbol *found = slot->value.found.symbol;
+ const struct objfile *context = slot->objfile_context;
+
+ printf_filtered (" [%4u] = %s, %s %s\n", i,
+ host_address_to_string (context),
+ SYMBOL_PRINT_NAME (found),
+ domain_name (SYMBOL_DOMAIN (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);
+}
+\f