diff options
-rw-r--r-- | app/perfsymboltable.cpp | 158 | ||||
-rw-r--r-- | app/perfsymboltable.h | 4 | ||||
-rw-r--r-- | tests/auto/shared/perfparsertestclient.cpp | 43 | ||||
-rw-r--r-- | tests/auto/shared/perfparsertestclient.h | 6 | ||||
-rw-r--r-- | tests/manual/perf2text/perf2text.cpp | 45 |
5 files changed, 85 insertions, 171 deletions
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp index 07d6f3a..28289fa 100644 --- a/app/perfsymboltable.cpp +++ b/app/perfsymboltable.cpp @@ -599,116 +599,23 @@ PerfElfMap::ElfInfo PerfSymbolTable::findElf(quint64 ip) const return m_elfs.findElf(ip); } -struct AddrRange +class CuDieRanges { - Dwarf_Addr low = 0; - Dwarf_Addr high = 0; - - void setMinMax(const AddrRange range) - { - if (range.low && (low == 0 || low > range.low)) - low = range.low; - if (range.high && (high == 0 || high < range.high)) - high = range.high; - } - - bool contains(Dwarf_Addr addr) const - { - return low <= addr && addr < high; - } - - bool operator<(const AddrRange &rhs) const - { - return std::tie(low, high) < std::tie(rhs.low, rhs.high); - } -}; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(AddrRange, Q_MOVABLE_TYPE); -QT_END_NAMESPACE - -struct DieRangeMap -{ - DieRangeMap(Dwarf_Die *die = nullptr, Dwarf_Addr bias = 0) - : die(die) - , bias(bias) - { - if (die) - gatherRanges(die, bias); - } - - bool contains(Dwarf_Addr addr) const - { - if (!range.contains(addr)) - return false; - return std::any_of(ranges.begin(), ranges.end(), - [addr](AddrRange range) { - return range.contains(addr); - }); - } - - bool operator<(const DieRangeMap &rhs) const - { - return range < rhs.range; - } - - Dwarf_Die *die = nullptr; - AddrRange range; // may be non-continuous, but allows quick checks and sorting - QVector<AddrRange> ranges; - Dwarf_Addr bias; - -private: - void gatherRanges(Dwarf_Die *parent_die, Dwarf_Addr bias) - { - Dwarf_Die die; - if (dwarf_child(parent_die, &die) != 0) - return; - - do { - switch (dwarf_tag(&die)) { - case DW_TAG_subprogram: - case DW_TAG_inlined_subroutine: - addRanges(&die, bias); - break; - }; - bool declaration = false; - Dwarf_Attribute attr_mem; - dwarf_formflag(dwarf_attr(&die, DW_AT_declaration, &attr_mem), &declaration); - if (!declaration) { - // let's be curious and look deeper in the tree, - // function are not necessarily at the first level, but - // might be nested inside a namespace, structure etc. - gatherRanges(&die, bias); - } - } while (dwarf_siblingof(&die, &die) == 0); - } - - void addRanges(Dwarf_Die *die, Dwarf_Addr bias) +public: + struct CuDieRange { - Dwarf_Addr low = 0, high = 0; - Dwarf_Addr base = 0; - ptrdiff_t offset = 0; - while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { - addRange(low, high, bias); + Dwarf_Die *cuDie; + Dwarf_Addr bias; + Dwarf_Addr low; + Dwarf_Addr high; + + bool contains(Dwarf_Addr addr) const + { + return low <= addr && addr < high; } - } + }; - void addRange(Dwarf_Addr low, Dwarf_Addr high, Dwarf_Addr bias) - { - AddrRange ret; - ret.low = low + bias; - ret.high = high + bias; - range.setMinMax(ret); - ranges.push_back(ret); - } -}; -QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(DieRangeMap, Q_MOVABLE_TYPE); -QT_END_NAMESPACE - -class DieRangeMaps -{ -public: - DieRangeMaps(Dwfl_Module *mod = nullptr) + CuDieRanges(Dwfl_Module *mod = nullptr) { if (!mod) return; @@ -716,37 +623,34 @@ public: Dwarf_Die *die = nullptr; Dwarf_Addr bias = 0; while ((die = dwfl_module_nextcu(mod, die, &bias))) { - DieRangeMap map(die, bias); - if (map.range.low == 0 && map.range.high == 0) { - // no range entries, skip - continue; + Dwarf_Addr low = 0; + Dwarf_Addr high = 0; + Dwarf_Addr base = 0; + ptrdiff_t offset = 0; + while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) { + ranges.push_back(CuDieRange{die, bias, low + bias, high + bias}); } - range.setMinMax(map.range); - maps.push_back(std::move(map)); } } Dwarf_Die *findDie(Dwarf_Addr addr, Dwarf_Addr *bias) const { - if (!range.contains(addr)) - return nullptr; - - auto it = std::find_if(maps.begin(), maps.end(), - [addr](const DieRangeMap &map) { - return map.contains(addr); + auto it = std::find_if(ranges.begin(), ranges.end(), + [addr](const CuDieRange &range) { + return range.contains(addr); }); - if (it == maps.end()) + if (it == ranges.end()) return nullptr; *bias = it->bias; - return it->die; + return it->cuDie; } public: - AddrRange range; // may be non-continuous, but allows quick checks - QVector<DieRangeMap> maps; + QVector<CuDieRange> ranges; }; QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(DieRangeMaps, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(CuDieRanges, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(CuDieRanges::CuDieRange, Q_MOVABLE_TYPE); QT_END_NAMESPACE int symbolIndex(const Elf64_Rel &rel) @@ -969,10 +873,10 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel, // broken DWARF emitter by clang, e.g. no aranges // cf.: https://sourceware.org/ml/elfutils-devel/2017-q2/msg00180.html // build a custom lookup table and query that one - if (!m_dieRangeMaps.contains(mod)) { - m_dieRangeMaps[mod] = DieRangeMaps(mod); + if (!m_cuDieRanges.contains(mod)) { + m_cuDieRanges[mod] = CuDieRanges(mod); } - const auto& maps = m_dieRangeMaps[mod]; + const auto& maps = m_cuDieRanges[mod]; die = maps.findDie(addressLocation.address, &bias); } @@ -1127,7 +1031,7 @@ Dwfl *PerfSymbolTable::attachDwfl(void *arg) void PerfSymbolTable::clearCache() { m_addressCache.clearInvalid(); - m_dieRangeMaps.clear(); + m_cuDieRanges.clear(); m_perfMap.clear(); if (m_perfMapFile.isOpen()) m_perfMapFile.reset(); diff --git a/app/perfsymboltable.h b/app/perfsymboltable.h index 4d9318b..9687763 100644 --- a/app/perfsymboltable.h +++ b/app/perfsymboltable.h @@ -34,7 +34,7 @@ #include <QObject> -class DieRangeMaps; +class CuDieRanges; class PerfSymbolTable { @@ -111,7 +111,7 @@ private: PerfElfMap m_elfs; PerfAddressCache m_addressCache; - QHash<Dwfl_Module*, DieRangeMaps> m_dieRangeMaps; + QHash<Dwfl_Module*, CuDieRanges> m_cuDieRanges; Dwfl_Callbacks *m_callbacks; qint32 m_pid; diff --git a/tests/auto/shared/perfparsertestclient.cpp b/tests/auto/shared/perfparsertestclient.cpp index ac45c28..82eed31 100644 --- a/tests/auto/shared/perfparsertestclient.cpp +++ b/tests/auto/shared/perfparsertestclient.cpp @@ -20,6 +20,7 @@ #include "perffeatures.h" #include "perfparsertestclient.h" +#include <QTextStream> #include <QtEndian> #ifdef MANUAL_TEST @@ -187,3 +188,45 @@ void PerfParserTestClient::extractTrace(QIODevice *device) QVERIFY(stream.atEnd()); } } + +void PerfParserTestClient::convertToText(QTextStream &out) const +{ + for (const auto &sample : samples()) { + out << string(command(sample.pid).name) << '\t' + << sample.pid << '\t' << sample.tid << '\t' + << sample.time / 1000000000 << '.' << qSetFieldWidth(9) << qSetPadChar(QLatin1Char('0')) + << sample.time % 1000000000 << qSetFieldWidth(0) << qSetPadChar(QLatin1Char(' ')) << '\n'; + for (const auto &value : sample.values) { + const auto attribute = this->attribute(value.first); + const auto cost = attribute.usesFrequency ? value.second : attribute.frequencyOrPeriod; + out << '\t' << string(attribute.name) << ": "; + if (attribute.type == 2) { + const auto format = tracePointFormat(static_cast<qint32>(attribute.config)); + out << string(format.system) << ' ' << string(format.name) << ' ' << hex << format.flags << dec << '\n'; + for (auto it = sample.tracePointData.begin(); it != sample.tracePointData.end(); ++it) { + out << "\t\t" << string(it.key()) << '=' << it.value().toString() << '\n'; + } + } else { + out << cost << '\n'; + } + } + out << '\n'; + auto printFrame = [&out, this](qint32 locationId) -> qint32 { + const auto location = this->location(locationId); + out << '\t' << hex << location.address << dec; + const auto symbol = this->symbol(locationId); + if (location.file != -1) + out << '\t' << string(location.file) << ':' << location.line << ':' << location.column; + if (symbol.path != -1) + out << '\t' << string(symbol.name) << ' ' << string(symbol.binary) << ' ' << string(symbol.path) << ' ' << (symbol.isKernel ? "[kernel]" : ""); + out << '\n'; + return location.parentLocationId; + }; + for (const auto &frame : sample.frames) { + auto locationId = printFrame(frame); + while (locationId != -1) + locationId = printFrame(locationId); + } + out << '\n'; + } +} diff --git a/tests/auto/shared/perfparsertestclient.h b/tests/auto/shared/perfparsertestclient.h index 8e6b7d5..468d7ae 100644 --- a/tests/auto/shared/perfparsertestclient.h +++ b/tests/auto/shared/perfparsertestclient.h @@ -25,6 +25,8 @@ #include <QVariant> #include <QVector> +class QTextStream; + class PerfParserTestClient : public QObject { Q_OBJECT @@ -113,7 +115,9 @@ public: LocationEvent location(qint32 id) const { return m_locations.value(id); } SymbolEvent symbol(qint32 id) const { return m_symbols.value(id); } - TracePointFormatEvent tracePointFormat(qint32 id) { return m_tracePointFormats.value(id); } + TracePointFormatEvent tracePointFormat(qint32 id) const { return m_tracePointFormats.value(id); } + + void convertToText(QTextStream &output) const; private: QVector<QByteArray> m_strings; diff --git a/tests/manual/perf2text/perf2text.cpp b/tests/manual/perf2text/perf2text.cpp index e57082f..d77f518 100644 --- a/tests/manual/perf2text/perf2text.cpp +++ b/tests/manual/perf2text/perf2text.cpp @@ -39,7 +39,6 @@ int main(int argc, char **argv) args.removeFirst(); QProcess process; - PerfParserTestClient client; process.setProcessChannelMode(QProcess::ForwardedErrorChannel); QObject::connect(&process, &QProcess::errorOccurred, &app, [&process](QProcess::ProcessError error) { qWarning() << "perfparser process error:" << error << process.errorString(); @@ -53,47 +52,11 @@ int main(int argc, char **argv) if (!process.waitForStarted() || !process.waitForFinished()) return 1; - client.extractTrace(&process); - QTextStream out(stdout); - for (const auto &sample : client.samples()) { - out << client.string(client.command(sample.pid).name) << '\t' - << sample.pid << '\t' << sample.tid << '\t' - << sample.time / 1000000000 << '.' << qSetFieldWidth(9) << qSetPadChar(QLatin1Char('0')) - << sample.time % 1000000000 << qSetFieldWidth(0) << qSetPadChar(QLatin1Char(' ')) << '\n'; - for (const auto &value : sample.values) { - const auto attribute = client.attribute(value.first); - const auto cost = attribute.usesFrequency ? value.second : attribute.frequencyOrPeriod; - out << '\t' << client.string(attribute.name) << ": "; - if (attribute.type == 2) { - const auto format = client.tracePointFormat(static_cast<qint32>(attribute.config)); - out << client.string(format.system) << ' ' << client.string(format.name) << ' ' << hex << format.flags << dec << '\n'; - for (auto it = sample.tracePointData.begin(); it != sample.tracePointData.end(); ++it) { - out << "\t\t" << client.string(it.key()) << '=' << it.value().toString() << '\n'; - } - } else { - out << cost << '\n'; - } - } - out << '\n'; - auto printFrame = [&out, &client](qint32 locationId) -> qint32 { - const auto location = client.location(locationId); - out << '\t' << hex << location.address << dec; - const auto symbol = client.symbol(locationId); - if (location.file != -1) - out << '\t' << client.string(location.file) << ':' << location.line << ':' << location.column; - if (symbol.path != -1) - out << '\t' << client.string(symbol.name) << ' ' << client.string(symbol.binary) << ' ' << client.string(symbol.path) << ' ' << (symbol.isKernel ? "[kernel]" : ""); - out << '\n'; - return location.parentLocationId; - }; - for (const auto &frame : sample.frames) { - auto locationId = printFrame(frame); - while (locationId != -1) - locationId = printFrame(locationId); - } - out << '\n'; - } + + PerfParserTestClient client; + client.extractTrace(&process); + client.convertToText(out); return 0; } |