diff --git a/app/perfaddresscache.cpp b/app/perfaddresscache.cpp index 2bf05c0..27b646c 100644 --- a/app/perfaddresscache.cpp +++ b/app/perfaddresscache.cpp @@ -47,3 +47,38 @@ void PerfAddressCache::cache(const PerfElfMap::ElfInfo& elf, quint64 addr, else (*invalidAddressCache)[addr] = entry; } + +static bool operator<(const PerfAddressCache::SymbolCacheEntry &lhs, quint64 addr) +{ + return lhs.offset < addr; +} + +PerfAddressCache::SymbolCacheEntry PerfAddressCache::findSymbol(const PerfElfMap::ElfInfo& elf, + quint64 addr) const +{ + Q_ASSERT(elf.isValid()); + const auto &symbols = m_symbolCache.value(elf.originalPath); + const auto relAddr = relativeAddress(elf, addr); + auto it = std::lower_bound(symbols.begin(), symbols.end(), relAddr); + + if (it != symbols.end() && it->offset == relAddr) + return *it; + if (it == symbols.begin()) + return {}; + + --it; + + if (it->offset <= relAddr && it->offset + it->size > relAddr) + return *it; + return {}; +} + +void PerfAddressCache::cacheSymbol(const PerfElfMap::ElfInfo& elf, quint64 startAddr, quint64 size, + const QByteArray& symname) +{ + Q_ASSERT(elf.isValid()); + auto &symbols = m_symbolCache[elf.originalPath]; + const auto offset = relativeAddress(elf, startAddr); + auto it = std::lower_bound(symbols.begin(), symbols.end(), offset); + symbols.insert(it, {offset, size, symname}); +} diff --git a/app/perfaddresscache.h b/app/perfaddresscache.h index bfbb06e..e372e77 100644 --- a/app/perfaddresscache.h +++ b/app/perfaddresscache.h @@ -21,6 +21,8 @@ #define PERFADDRESSCACHE_H #include +#include + #include "perfelfmap.h" class PerfAddressCache @@ -38,12 +40,34 @@ class PerfAddressCache }; using OffsetAddressCache = QHash; + struct SymbolCacheEntry + { + SymbolCacheEntry(quint64 offset = 0, quint64 size = 0, const QByteArray &symname = {}) + : offset(offset) + , size(size) + , symname(symname) + {} + + bool isValid() const { return size != 0; } + + quint64 offset; + quint64 size; + QByteArray symname; + }; + using SymbolCache = QVector; + AddressCacheEntry find(const PerfElfMap::ElfInfo& elf, quint64 addr, OffsetAddressCache *invalidAddressCache) const; void cache(const PerfElfMap::ElfInfo& elf, quint64 addr, const AddressCacheEntry& entry, OffsetAddressCache *invalidAddressCache); + + SymbolCacheEntry findSymbol(const PerfElfMap::ElfInfo &elf, quint64 addr) const; + void cacheSymbol(const PerfElfMap::ElfInfo &elf, quint64 startAddr, quint64 size, + const QByteArray &symname); private: QHash m_cache; + QHash m_symbolCache; }; +Q_DECLARE_TYPEINFO(PerfAddressCache::SymbolCacheEntry, Q_MOVABLE_TYPE); #endif diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp index f260fd3..8383b47 100644 --- a/app/perfsymboltable.cpp +++ b/app/perfsymboltable.cpp @@ -894,8 +894,10 @@ static QByteArray fakeSymbolFromSection(Dwfl_Module *mod, Dwarf_Addr addr) int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel, bool *isInterworking) { + auto addressCache = m_unwind->addressCache(); + const auto& elf = findElf(ip); - auto cached = m_unwind->addressCache()->find(elf, ip, &m_invalidAddressCache); + auto cached = addressCache->find(elf, ip, &m_invalidAddressCache); if (cached.isValid()) { *isInterworking = cached.isInterworking; return cached.locationId; @@ -918,13 +920,21 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel, PerfUnwind::Location functionLocation(addressLocation); QByteArray symname; - GElf_Sym sym; GElf_Off off = 0; if (mod) { - // For addrinfo we need the raw pointer into symtab, so we need to adjust ourselves. - symname = dwfl_module_addrinfo(mod, addressLocation.address, &off, &sym, nullptr, nullptr, - nullptr); + auto cachedAddrInfo = addressCache->findSymbol(elf, addressLocation.address); + if (cachedAddrInfo.isValid()) { + off = addressLocation.address - elf.addr - cachedAddrInfo.offset; + symname = cachedAddrInfo.symname; + } else { + GElf_Sym sym; + // For addrinfo we need the raw pointer into symtab, so we need to adjust ourselves. + symname = dwfl_module_addrinfo(mod, addressLocation.address, &off, &sym, nullptr, nullptr, + nullptr); + if (off != addressLocation.address) + addressCache->cacheSymbol(elf, addressLocation.address - off, sym.st_size, symname); + } if (off == addressLocation.address) {// no symbol found symname = fakeSymbolFromSection(mod, addressLocation.address); @@ -1023,7 +1033,7 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel, int locationId = m_unwind->resolveLocation(addressLocation); *isInterworking = (symname == "$a" || symname == "$t"); - m_unwind->addressCache()->cache(elf, ip, {locationId, *isInterworking}, &m_invalidAddressCache); + addressCache->cache(elf, ip, {locationId, *isInterworking}, &m_invalidAddressCache); return locationId; } diff --git a/tests/auto/addresscache/tst_addresscache.cpp b/tests/auto/addresscache/tst_addresscache.cpp index 604274e..c17df81 100644 --- a/tests/auto/addresscache/tst_addresscache.cpp +++ b/tests/auto/addresscache/tst_addresscache.cpp @@ -61,6 +61,33 @@ private slots: PerfAddressCache::OffsetAddressCache invalidAddressCache; QCOMPARE(cache.find(PerfElfMap::ElfInfo{}, 0x123, &invalidAddressCache).locationId, -1); } + + void testSymbolCache() + { + PerfElfMap::ElfInfo info_a{{}, 0x100, 100, 0, + QByteArrayLiteral("libfoo_a.so"), + QByteArrayLiteral("/usr/lib/libfoo_a.so")}; + PerfElfMap::ElfInfo info_b{{}, 0x200, 100, 0, + QByteArrayLiteral("libfoo_b.so"), + QByteArrayLiteral("/usr/lib/libfoo_b.so")}; + + PerfAddressCache cache; + + QVERIFY(!cache.findSymbol(info_a, 0x100).isValid()); + QVERIFY(!cache.findSymbol(info_b, 0x100).isValid()); + + cache.cacheSymbol(info_a, 0x100, 10, "Foo"); + for (auto addr : {0x100, 0x100 + 9}) { + const auto cached = cache.findSymbol(info_a, addr); + QVERIFY(cached.isValid()); + QCOMPARE(cached.offset, 0); + QCOMPARE(cached.size, 10); + QCOMPARE(cached.symname, "Foo"); + } + QVERIFY(!cache.findSymbol(info_a, 0x100 + 10).isValid()); + QVERIFY(!cache.findSymbol(info_b, 0x100).isValid()); + QVERIFY(!cache.findSymbol(info_b, 0x100 + 9).isValid()); + } }; QTEST_GUILESS_MAIN(TestAddressCache)