diff --git a/coregrind/Makefile.am b/coregrind/Makefile.am index 700751dea..48ddd01e9 100644 --- a/coregrind/Makefile.am +++ b/coregrind/Makefile.am @@ -363,6 +363,7 @@ COREGRIND_SOURCES_COMMON = \ m_debuginfo/d3basics.c \ m_debuginfo/debuginfo.c \ m_debuginfo/image.c \ + m_debuginfo/inltab_lookup.c \ m_debuginfo/minilzo-inl.c \ m_debuginfo/readdwarf.c \ m_debuginfo/readdwarf3.c \ diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c index fb3674565..c801fc5c0 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -56,6 +56,7 @@ #include "priv_d3basics.h" /* ML_(pp_GX) */ #include "priv_tytypes.h" #include "priv_storage.h" +#include "priv_inltab_lookup.h" #include "priv_readdwarf.h" #if defined(VGO_linux) || defined(VGO_solaris) || defined(VGO_freebsd) # include "priv_readelf.h" @@ -345,6 +346,7 @@ static void free_DebugInfo ( DebugInfo* di ) if (di->loctab) ML_(dinfo_free)(di->loctab); if (di->loctab_fndn_ix) ML_(dinfo_free)(di->loctab_fndn_ix); if (di->inltab) ML_(dinfo_free)(di->inltab); + if (di->inltab_lookup) VG_(inltab_lookup_cleanup)(di->inltab_lookup); if (di->cfsi_base) ML_(dinfo_free)(di->cfsi_base); if (di->cfsi_m_ix) ML_(dinfo_free)(di->cfsi_m_ix); if (di->cfsi_rd) ML_(dinfo_free)(di->cfsi_rd); @@ -2388,13 +2390,12 @@ Bool VG_(get_inline_fnname) ( DiEpoch ep, Addr a, const HChar** inl_fnname ) return False; /* Search the inline table for an entry containing this address */ - /* We search from the end backwards to find the innermost inline function first */ - for (Word i = si->inltab_used - 1; i >= 0; i--) { - if (si->inltab[i].addr_lo <= a && a < si->inltab[i].addr_hi) { - /* Found it! Return the inline function name */ - *inl_fnname = si->inltab[i].inlinedfn; - return True; - } + /* Lookup with hash table cache and binary search fallback */ + Word inl_idx; + if (VG_(inltab_lookup_get)(si->inltab_lookup, a, &inl_idx, si)) { + /* Found it! Return the inline function name */ + *inl_fnname = si->inltab[inl_idx].inlinedfn; + return True; } return False; diff --git a/coregrind/m_debuginfo/inltab_lookup.c b/coregrind/m_debuginfo/inltab_lookup.c new file mode 100644 index 000000000..89c748415 --- /dev/null +++ b/coregrind/m_debuginfo/inltab_lookup.c @@ -0,0 +1,124 @@ +/* VgHashTable-based implementation for inline lookup table. + Maps addresses to inline function info indices. */ + +#include "pub_core_basics.h" +#include "pub_core_libcbase.h" +#include "pub_core_libcassert.h" +#include "pub_core_mallocfree.h" +#include "pub_core_hashtable.h" +#include "priv_storage.h" + +/* Node structure for VgHashTable + Extends VgHashNode with our key (Addr) and value (Word) */ +typedef struct { + VgHashNode node; /* Must be first for VgHashTable API */ + Word value; /* The inline table index */ +} InlLookupNode; + +/* Binary search for an inline function entry containing address 'a'. + Uses the sorted inltab array to efficiently find candidates. + Returns the index of the matching entry, or -1 if not found. + + The inltab is sorted by addr_lo, so we: + 1. Binary search to find entries where addr_lo <= a + 2. Scan backwards to find an entry that contains address a + (this finds the innermost inline function if multiple are nested) +*/ +static Word binary_search_inltab(const DebugInfo* di, Addr a) { + if (di == NULL || di->inltab == NULL || di->inltab_used == 0) + return -1; + + Word lo = 0, hi = di->inltab_used; + + /* Binary search: find the first entry where addr_lo > a */ + while (lo < hi) { + Word mid = lo + (hi - lo) / 2; + if (a < di->inltab[mid].addr_lo) { + hi = mid; + } else { + lo = mid + 1; + } + } + + /* lo now points to the first entry where addr_lo > a. + Search backwards from lo-1 to find an entry that contains address a */ + for (Word i = lo - 1; i >= 0; i--) { + if (di->inltab[i].addr_lo <= a && a < di->inltab[i].addr_hi) { + /* Found it! Return the index */ + return i; + } + /* Since we're scanning backwards through sorted addr_lo entries, + if we encounter an addr_hi <= a, we can stop as we've passed + the potential range */ + if (di->inltab[i].addr_hi <= a) { + break; + } + } + + return -1; +} + +/* Public API for inline lookup table */ + +VgHashTable *VG_(inltab_lookup_new)(void) { + return VG_(HT_construct)("inltab_lookup"); +} + +void VG_(inltab_lookup_insert)(VgHashTable *ht, Addr addr, Word inl_idx) { + if (ht == NULL) + ht = VG_(inltab_lookup_new)(); + + /* Check if entry already exists */ + InlLookupNode *existing = VG_(HT_lookup)(ht, (UWord)addr); + if (existing) { + /* Update existing value */ + existing->value = inl_idx; + return; + } + + /* Allocate new node */ + InlLookupNode *node = VG_(malloc)("di.inltab_vghash.node", sizeof(InlLookupNode)); + if (!node) + return; + node->node.key = (UWord)addr; + node->value = inl_idx; + + VG_(HT_add_node)(ht, node); +} + +/* Enhanced lookup that combines hash table lookup with binary search fallback. + First tries the hash table for exact addr_lo matches. + If not found, uses binary search on the sorted inltab array. + If found via binary search, caches the result in the hash table. +*/ +Bool VG_(inltab_lookup_get)(VgHashTable *ht, Addr addr, Word *inl_idx, + const DebugInfo* di) { + if (ht == NULL) + return False; + + /* Try hash table lookup first */ + InlLookupNode *node = VG_(HT_lookup)(ht, (UWord)addr); + if (node) { + *inl_idx = node->value; + return True; + } + + /* Hash table miss, try binary search on the inltab array */ + Word idx = binary_search_inltab(di, addr); + if (idx >= 0) { + /* Found via binary search, cache it for future lookups */ + *inl_idx = idx; + VG_(inltab_lookup_insert)(ht, addr, idx); + return True; + } + + return False; +} + +void VG_(inltab_lookup_cleanup)(VgHashTable *ht) { + if (ht == NULL) + return; + + /* Free all nodes */ + VG_(HT_destruct)(ht, VG_(free)); +} diff --git a/coregrind/m_debuginfo/priv_inltab_lookup.h b/coregrind/m_debuginfo/priv_inltab_lookup.h new file mode 100644 index 000000000..72785f5bc --- /dev/null +++ b/coregrind/m_debuginfo/priv_inltab_lookup.h @@ -0,0 +1,13 @@ +#ifndef __PRIV_INLTAB_LOOKUP_H +#define __PRIV_INLTAB_LOOKUP_H + +#include "pub_core_basics.h" +#include "pub_tool_hashtable.h" + +extern VgHashTable *VG_(inltab_lookup_new)(void); +extern void VG_(inltab_lookup_insert)(VgHashTable *ht, Addr addr, Word inl_idx); +extern Bool VG_(inltab_lookup_get)(VgHashTable *ht, Addr addr, Word *inl_idx, + const DebugInfo *di); +extern void VG_(inltab_lookup_cleanup)(VgHashTable *ht); + +#endif \ No newline at end of file diff --git a/coregrind/m_debuginfo/priv_storage.h b/coregrind/m_debuginfo/priv_storage.h index 055d75e6f..e7946e931 100644 --- a/coregrind/m_debuginfo/priv_storage.h +++ b/coregrind/m_debuginfo/priv_storage.h @@ -44,6 +44,7 @@ #include "pub_core_basics.h" // Addr #include "pub_core_xarray.h" // XArray #include "pub_core_deduppoolalloc.h" // DedupPoolAlloc +#include "pub_tool_hashtable.h" // VgHashTable #include "priv_d3basics.h" // GExpr et al. #include "priv_image.h" // DiCursor @@ -953,6 +954,10 @@ struct _DebugInfo { UWord inltab_size; SizeT maxinl_codesz; + /* Hash table for O(1) inline lookups by address. + Maps arbitrary addresses to indices in inltab; populated on-demand during lookups for cache efficiency. */ + VgHashTable* inltab_lookup; + /* A set of expandable arrays to store CFI summary info records. The machine specific information (i.e. the DiCfSI_m struct) are stored in cfsi_m_pool, as these are highly duplicated. diff --git a/coregrind/m_debuginfo/storage.c b/coregrind/m_debuginfo/storage.c index 12ad68130..477d5ef4e 100644 --- a/coregrind/m_debuginfo/storage.c +++ b/coregrind/m_debuginfo/storage.c @@ -51,6 +51,7 @@ #include "priv_d3basics.h" /* ML_(pp_GX) */ #include "priv_tytypes.h" #include "priv_storage.h" /* self */ +#include "priv_inltab_lookup.h" /*------------------------------------------------------------*/ @@ -2164,6 +2165,10 @@ static void canonicaliseInltab ( struct _DebugInfo* di ) /* Free up unused space at the end of the table. */ shrinkInlTab(di); + + if (di->inltab_lookup == NULL) { + di->inltab_lookup = VG_(inltab_lookup_new)(); + } }