Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for Issue #253 - Features: Disassembler, Cross-platform Disassembler, Cross-profiling settings dialog #14

Merged
merged 4 commits into from
Dec 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion app/perfaddresscache.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,19 @@ class PerfAddressCache

struct SymbolCacheEntry
{
SymbolCacheEntry(quint64 offset = 0, quint64 size = 0, const QByteArray &symname = {})
SymbolCacheEntry(quint64 offset = 0, quint64 value = 0, quint64 size = 0, const QByteArray &symname = {})
: offset(offset)
, value(value)
, size(size)
, symname(symname)
{}

bool isValid() const { return !symname.isEmpty(); }

// adjusted/absolute st_value, see documentation of the `addr` arg in `dwfl_module_getsym_info`
quint64 offset;
// unadjusted/relative st_value
quint64 value;
milianw marked this conversation as resolved.
Show resolved Hide resolved
milianw marked this conversation as resolved.
Show resolved Hide resolved
quint64 size;
QByteArray symname;
bool demangled = false;
Expand Down
63 changes: 43 additions & 20 deletions app/perfsymboltable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,8 @@ void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QByteArray &
}

int PerfSymbolTable::insertSubprogram(CuDieRangeMapping *cudie, Dwarf_Die *top, Dwarf_Addr entry,
qint32 binaryId, qint32 binaryPathId,
quint64 offset, quint64 size, quint64 relAddr,
qint32 binaryId, qint32 binaryPathId, qint32 actualPathId,
qint32 inlineCallLocationId, bool isKernel)
{
int line = 0;
Expand All @@ -327,15 +328,15 @@ int PerfSymbolTable::insertSubprogram(CuDieRangeMapping *cudie, Dwarf_Die *top,
const QByteArray file = dwarf_decl_file(top);

qint32 fileId = m_unwind->resolveString(file);
int locationId = m_unwind->resolveLocation(PerfUnwind::Location(entry, fileId, m_pid, line,
int locationId = m_unwind->resolveLocation(PerfUnwind::Location(entry, relAddr, fileId, m_pid, line,
column, inlineCallLocationId));
qint32 symId = m_unwind->resolveString(cudie->dieName(top));
milianw marked this conversation as resolved.
Show resolved Hide resolved
m_unwind->resolveSymbol(locationId, PerfUnwind::Symbol(symId, binaryId, binaryPathId, isKernel));
m_unwind->resolveSymbol(locationId, PerfUnwind::Symbol{symId, offset, size, binaryId, binaryPathId, actualPathId, isKernel});

return locationId;
}

int PerfSymbolTable::parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, qint32 binaryId, qint32 binaryPathId,
int PerfSymbolTable::parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId,
milianw marked this conversation as resolved.
Show resolved Hide resolved
milianw marked this conversation as resolved.
Show resolved Hide resolved
bool isKernel, Dwarf_Files *files, Dwarf_Addr entry, qint32 parentLocationId)
{
int tag = dwarf_tag(top);
Expand All @@ -359,17 +360,17 @@ int PerfSymbolTable::parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, qint32 b
location.parentLocationId = parentLocationId;

int callLocationId = m_unwind->resolveLocation(location);
return insertSubprogram(cudie, top, entry, binaryId, binaryPathId, callLocationId, isKernel);
return insertSubprogram(cudie, top, entry, offset, size, relAddr, binaryId, binaryPathId, actualPathId, callLocationId, isKernel);
}
case DW_TAG_subprogram:
return insertSubprogram(cudie, top, entry, binaryId, binaryPathId, -1, isKernel);
return insertSubprogram(cudie, top, entry, offset, size, relAddr, binaryId, binaryPathId, actualPathId, -1, isKernel);
default:
return -1;
}
}

qint32 PerfSymbolTable::parseDwarf(CuDieRangeMapping *cudie, SubProgramDie *subprogram, const QVector<Dwarf_Die> &inlined,
Dwarf_Addr bias, qint32 binaryId, qint32 binaryPathId, bool isKernel)
Dwarf_Addr bias, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, bool isKernel)
{
Dwarf_Files *files = nullptr;
dwarf_getsrcfiles(cudie->cudie(), &files, nullptr);
Expand All @@ -381,7 +382,7 @@ qint32 PerfSymbolTable::parseDwarf(CuDieRangeMapping *cudie, SubProgramDie *subp
if (dwarf_entrypc(&scope, &entry) == 0 && entry != 0)
scopeAddr += entry;

auto locationId = parseDie(cudie, &scope, binaryId, binaryPathId, isKernel, files, scopeAddr, parentLocationId);
auto locationId = parseDie(cudie, &scope, offset, size, relAddr, binaryId, binaryPathId, actualPathId, isKernel, files, scopeAddr, parentLocationId);
if (locationId != -1)
parentLocationId = locationId;
};
Expand Down Expand Up @@ -722,7 +723,19 @@ static QByteArray fakeSymbolFromSection(Dwfl_Module *mod, Dwarf_Addr addr)
return sym;
}

static PerfAddressCache::SymbolCache cacheSymbols(Dwfl_Module *module, quint64 elfStart)
static quint64 symbolAddress(quint64 addr, bool isArmArch)
{
// For dwfl API call we need the raw pointer into symtab, so we need to adjust ip.
return (!isArmArch || (addr & 1)) ? addr : addr + 1;
}

static quint64 alignedAddress(quint64 addr, bool isArmArch)
{
// Adjust addr back. The symtab entries are 1 off for all practical purposes.
return (isArmArch && (addr & 1)) ? addr - 1 : addr;
}

static PerfAddressCache::SymbolCache cacheSymbols(Dwfl_Module *module, quint64 elfStart, bool isArmArch)
{
PerfAddressCache::SymbolCache cache;

Expand All @@ -731,8 +744,10 @@ static PerfAddressCache::SymbolCache cacheSymbols(Dwfl_Module *module, quint64 e
GElf_Sym sym;
GElf_Addr symAddr;
const auto symbol = dwfl_module_getsym_info(module, i, &sym, &symAddr, nullptr, nullptr, nullptr);
if (symbol)
cache.append({symAddr - elfStart, sym.st_size, symbol});
if (symbol) {
quint64 start = alignedAddress(sym.st_value, isArmArch);
cache.append({symAddr - elfStart, start, sym.st_size, symbol});
}
}
return cache;
}
Expand All @@ -751,35 +766,42 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,

qint32 binaryId = -1;
qint32 binaryPathId = -1;
qint32 actualPathId = -1;
quint64 elfStart = 0;
if (elf.isValid()) {
binaryId = m_unwind->resolveString(elf.originalFileName);
binaryPathId = m_unwind->resolveString(elf.originalPath);
actualPathId = m_unwind->resolveString(elf.localFile.absoluteFilePath().toUtf8());
elfStart = elf.hasBaseAddr() ? elf.baseAddr : elf.addr;
}

Dwfl_Module *mod = module(ip, elf);

PerfUnwind::Location addressLocation(
(m_unwind->architecture() != PerfRegisterInfo::ARCH_ARM || (ip & 1))
? ip : ip + 1, -1, m_pid);
bool isArmArch = (m_unwind->architecture() == PerfRegisterInfo::ARCH_ARM);
PerfUnwind::Location addressLocation(symbolAddress(ip, isArmArch), 0, -1, m_pid);
PerfUnwind::Location functionLocation(addressLocation);

QByteArray symname;
GElf_Off off = 0;

quint64 start = 0;
quint64 size = 0;
quint64 relAddr = 0;
if (mod) {
if (!addressCache->hasSymbolCache(elf.originalPath)) {
// cache all symbols in a sorted lookup table and demangle them on-demand
// note that the symbols within the symtab aren't necessarily sorted,
// which makes searching repeatedly via dwfl_module_addrinfo potentially very slow
addressCache->setSymbolCache(elf.originalPath, cacheSymbols(mod, elfStart));
addressCache->setSymbolCache(elf.originalPath, cacheSymbols(mod, elfStart, isArmArch));
}

auto cachedAddrInfo = addressCache->findSymbol(elf.originalPath, addressLocation.address - elfStart);
if (cachedAddrInfo.isValid()) {
off = addressLocation.address - elfStart - cachedAddrInfo.offset;
symname = cachedAddrInfo.symname;
start = cachedAddrInfo.value;
size = cachedAddrInfo.size;
relAddr = alignedAddress(start + off, isArmArch);

Dwarf_Addr bias = 0;
functionLocation.address -= off; // in case we don't find anything better
Expand Down Expand Up @@ -821,8 +843,8 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
addressLocation.parentLocationId = m_unwind->lookupLocation(functionLocation);
// otherwise resolve the inline chain if possible
if (!scopes.isEmpty() && !m_unwind->hasSymbol(addressLocation.parentLocationId)) {
functionLocation.parentLocationId = parseDwarf(cudie, subprogram, scopes, bias,
binaryId, binaryPathId, isKernel);
functionLocation.parentLocationId = parseDwarf(cudie, subprogram, scopes, bias, start, size, relAddr,
binaryId, binaryPathId, actualPathId, isKernel);
}
}
}
Expand All @@ -840,7 +862,7 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
// no sufficient debug information. Use what we already know
qint32 symId = m_unwind->resolveString(symname);
m_unwind->resolveSymbol(addressLocation.parentLocationId,
PerfUnwind::Symbol(symId, binaryId, binaryPathId, isKernel));
PerfUnwind::Symbol(symId, start, size, binaryId, binaryPathId, actualPathId, isKernel));
}
} else {
if (isKernel) {
Expand All @@ -861,10 +883,11 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
if (!m_unwind->hasSymbol(addressLocation.parentLocationId)) {
qint32 symId = m_unwind->resolveString(symname);
m_unwind->resolveSymbol(addressLocation.parentLocationId,
PerfUnwind::Symbol(symId, binaryId, binaryPathId, isKernel));
PerfUnwind::Symbol(symId, start, size, binaryId, binaryPathId, actualPathId, isKernel));
}
}

// relAddr - relative address of the function start added with offset from the function start
addressLocation.relAddr = relAddr;
Q_ASSERT(addressLocation.parentLocationId != -1);
Q_ASSERT(m_unwind->hasSymbol(addressLocation.parentLocationId));

Expand Down
6 changes: 3 additions & 3 deletions app/perfsymboltable.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,12 @@ class PerfSymbolTable
qint32 m_pid;

QByteArray symbolFromPerfMap(quint64 ip, GElf_Off *offset) const;
int parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, qint32 binaryId, qint32 binaryPathId, bool isKernel,
int parseDie(CuDieRangeMapping *cudie, Dwarf_Die *top, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, bool isKernel,
Dwarf_Files *files, Dwarf_Addr entry, qint32 parentLocationId);
int insertSubprogram(CuDieRangeMapping *cudie, Dwarf_Die *top, Dwarf_Addr entry, qint32 binaryId, qint32 binaryPathId,
int insertSubprogram(CuDieRangeMapping *cudie, Dwarf_Die *top, Dwarf_Addr entry, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId,
qint32 inlineParent, bool isKernel);
qint32 parseDwarf(CuDieRangeMapping *cudie, SubProgramDie *subprogram, const QVector<Dwarf_Die> &inlined,
Dwarf_Addr bias, qint32 binaryId, qint32 binaryPathId, bool isKernel);
Dwarf_Addr bias, quint64 offset, quint64 size, quint64 relAddr, qint32 binaryId, qint32 binaryPathId, qint32 actualPathId, bool isKernel);
};

QT_BEGIN_NAMESPACE
Expand Down
7 changes: 4 additions & 3 deletions app/perfunwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ uint qHash(const PerfUnwind::Location &location, uint seed)
{
QtPrivate::QHashCombine hash;
seed = hash(seed, location.address);
seed = hash(seed, location.relAddr);
seed = hash(seed, location.file);
seed = hash(seed, location.pid);
seed = hash(seed, location.line);
Expand All @@ -44,7 +45,7 @@ uint qHash(const PerfUnwind::Location &location, uint seed)

bool operator==(const PerfUnwind::Location &a, const PerfUnwind::Location &b)
{
return a.address == b.address && a.file == b.file && a.pid == b.pid && a.line == b.line
return a.address == b.address && a.relAddr == b.relAddr && a.file == b.file && a.pid == b.pid && a.line == b.line
&& a.column == b.column;
}

Expand Down Expand Up @@ -359,13 +360,13 @@ bool PerfUnwind::ipIsInKernelSpace(quint64 ip) const

QDataStream &operator<<(QDataStream &stream, const PerfUnwind::Location &location)
{
return stream << location.address << location.file << location.pid << location.line
return stream << location.address << location.relAddr << location.file << location.pid << location.line
<< location.column << location.parentLocationId;
}

QDataStream &operator<<(QDataStream &stream, const PerfUnwind::Symbol &symbol)
{
return stream << symbol.name << symbol.binary << symbol.path << symbol.isKernel;
return stream << symbol.name << symbol.relAddr << symbol.size << symbol.binary << symbol.path << symbol.actualPath << symbol.isKernel;
}

static int frameCallback(Dwfl_Frame *state, void *arg)
Expand Down
12 changes: 8 additions & 4 deletions app/perfunwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ class PerfUnwind : public QObject
};

struct Location {
explicit Location(quint64 address = 0, qint32 file = -1,
explicit Location(quint64 address = 0, quint64 relAddr = 0, qint32 file = -1,
quint32 pid = 0, qint32 line = 0, qint32 column = 0,
qint32 parentLocationId = -1) :
address(address), file(file), pid(pid), line(line), column(column),
address(address), relAddr(relAddr), file(file), pid(pid), line(line), column(column),
parentLocationId(parentLocationId) {}

quint64 address;
quint64 relAddr;
qint32 file;
quint32 pid;
qint32 line;
Expand All @@ -80,14 +81,17 @@ class PerfUnwind : public QObject
};

struct Symbol {
explicit Symbol(qint32 name = -1, qint32 binary = -1, qint32 path = -1,
explicit Symbol(qint32 name = -1, quint64 relAddr = 0, quint64 size = 0, qint32 binary = -1, qint32 path = -1, qint32 actualPath = -1,
bool isKernel = false) :
name(name), binary(binary), path(path), isKernel(isKernel)
name(name), relAddr(relAddr), size(size), binary(binary), path(path), actualPath(actualPath), isKernel(isKernel)
{}

qint32 name;
quint64 relAddr;
quint64 size;
qint32 binary;
qint32 path;
qint32 actualPath;
bool isKernel;
};

Expand Down
2 changes: 1 addition & 1 deletion tests/auto/addresscache/tst_addresscache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private slots:
QVERIFY(!cache.findSymbol(libfoo_a, 0).isValid());
QVERIFY(!cache.findSymbol(libfoo_b, 0).isValid());

cache.setSymbolCache(libfoo_a, {{0x100, 10, "Foo"}, {0x11a, 0, "FooZ"}, {0x12a, 10, "FooN"}});
cache.setSymbolCache(libfoo_a, {{0x100, 0x100, 10, "Foo"}, {0x11a, 0x11a, 0, "FooZ"}, {0x12a, 0x12a, 10, "FooN"}});
for (auto addr : {0x100, 0x100 + 9}) {
const auto cached = cache.findSymbol(libfoo_a, addr);
QVERIFY(cached.isValid());
Expand Down
Loading