From f1454efdf6dacd52cb76fb44bb350bace70b9593 Mon Sep 17 00:00:00 2001 From: Lieven Hey Date: Mon, 18 Nov 2024 11:04:30 +0100 Subject: [PATCH] feat: tracepoint data on hover --- src/models/CMakeLists.txt | 1 + src/models/data.h | 23 ++++++++++++-- src/models/timelinedelegate.cpp | 34 +++++++++++++++++---- src/parsers/perf/perfparser.cpp | 54 +++++++++++++++++++++++++-------- tests/modeltests/CMakeLists.txt | 14 ++++++++- 5 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/models/CMakeLists.txt b/src/models/CMakeLists.txt index 1815f4ccf..d0c3a9aaf 100644 --- a/src/models/CMakeLists.txt +++ b/src/models/CMakeLists.txt @@ -23,6 +23,7 @@ add_library( timeaxisheaderview.cpp timelinedelegate.cpp topproxy.cpp + tracepointformat.cpp treemodel.cpp ) diff --git a/src/models/data.h b/src/models/data.h index fdb7187ad..8af1d0508 100644 --- a/src/models/data.h +++ b/src/models/data.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "../util.h" @@ -787,6 +788,9 @@ const constexpr auto INVALID_CPU_ID = std::numeric_limits::max(); const constexpr int INVALID_TID = -1; const constexpr int INVALID_PID = -1; +const constexpr auto INVALID_TRACEPOINTFORMAT = std::numeric_limits::max(); +const constexpr auto INVALID_TRACEPOINTDATA = std::numeric_limits::max(); + struct Event { quint64 time = 0; @@ -794,11 +798,13 @@ struct Event qint32 type = -1; qint32 stackId = -1; quint32 cpuId = INVALID_CPU_ID; + quint32 tracepointFormat = INVALID_TRACEPOINTFORMAT; + quint32 tracepointData = INVALID_TRACEPOINTDATA; bool operator==(const Event& rhs) const { - return std::tie(time, cost, type, stackId, cpuId) - == std::tie(rhs.time, rhs.cost, rhs.type, rhs.stackId, rhs.cpuId); + return std::tie(time, cost, type, stackId, cpuId, tracepointFormat, tracepointData) + == std::tie(rhs.time, rhs.cost, rhs.type, rhs.stackId, rhs.cpuId, rhs.tracepointFormat, rhs.tracepointData); } }; @@ -962,6 +968,17 @@ struct TracepointEvents } }; +struct TracePointFormat +{ + QString systemId; + QString nameId; + quint32 flags; + QString format; +}; + +#include +using TracePointData = QHash; + struct EventResults { QVector threads; @@ -969,6 +986,8 @@ struct EventResults QVector tracepoints; QVector> stacks; QVector totalCosts; + QHash tracePointFormats; + QVector tracePointData; qint32 offCpuTimeCostId = -1; qint32 lostEventCostId = -1; qint32 tracepointEventCostId = -1; diff --git a/src/models/timelinedelegate.cpp b/src/models/timelinedelegate.cpp index b4c1c8314..07033b064 100644 --- a/src/models/timelinedelegate.cpp +++ b/src/models/timelinedelegate.cpp @@ -19,6 +19,7 @@ #include "../util.h" #include "eventmodel.h" #include "filterandzoomstack.h" +#include "tracepointformat.h" #include @@ -314,7 +315,6 @@ bool TimeLineDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, con // check whether we are hovering an off-CPU area found = findSamples(results.offCpuTimeCostId, true); } - const auto formattedTime = Util::formatTimeString(time - data.time.start); const auto totalCosts = index.data(EventModel::TotalCostsRole).value>(); if (found.numLost > 0) { @@ -329,12 +329,34 @@ bool TimeLineDelegate::helpEvent(QHelpEvent* event, QAbstractItemView* view, con Util::formatTimeString(found.totalCost), Util::formatTimeString(found.maxCost))); } else if (found.numSamples > 0) { + qDebug() << m_eventType; if (m_eventType == results.tracepointEventCostId) { - // currently tracepoint cost is saying nothig, so don't show it - QToolTip::showText( - event->globalPos(), - tr("time: %1\n%3 samples: %2") - .arg(formattedTime, QString::number(found.numSamples), results.tracepoints[index.row()].name)); + qDebug() << "HERE" << found.numSamples; + if (found.numSamples != 1) { + QToolTip::showText(event->globalPos(), + tr("time: %1\n%3 samples: %2") + .arg(formattedTime, QString::number(found.numSamples), + results.tracepoints[index.row()].name)); + } else { + // we only hover over one tracepoint, find it + Data::Event tracepoint; + data.findSamples(mappedX, m_eventType, results.lostEventCostId, false, start, + [&tracepoint](const Data::Event& event, bool isLost) { + Q_UNUSED(isLost); + tracepoint = event; + }); + + const auto format = results.tracePointFormats[tracepoint.tracepointFormat]; + qDebug() << format.systemId << format.nameId << format.format; + qDebug() << results.tracePointData[tracepoint.tracepointData]; + + TracePointFormatter formatter(format.format); + + QToolTip::showText(event->globalPos(), + tr("time: %1\n%2:\n%3") + .arg(formattedTime, results.tracepoints[index.row()].name, + formatter.format(results.tracePointData[tracepoint.tracepointData]))); + } } else { QToolTip::showText(event->globalPos(), diff --git a/src/parsers/perf/perfparser.cpp b/src/parsers/perf/perfparser.cpp index 2e30149c4..8b6082fef 100644 --- a/src/parsers/perf/perfparser.cpp +++ b/src/parsers/perf/perfparser.cpp @@ -560,13 +560,24 @@ QDebug operator<<(QDebug stream, const TracePointFormat& format) return stream; } -using TracePointData = QHash; +struct TracePointData +{ + quint32 formatId; + QHash data; +}; + +QDataStream& operator>>(QDataStream& stream, TracePointData& traceData) +{ + stream >> traceData.formatId >> traceData.data; + return stream; +} QDebug operator<<(QDebug stream, const TracePointData& traceData) { auto s = stream.noquote().nospace(); s << "TracePointData{"; - for (auto it = traceData.cbegin(), end = traceData.cend(); it != end; it++) { + s << "eventId=" << traceData.formatId << ", "; + for (auto it = traceData.data.cbegin(), end = traceData.data.cend(); it != end; it++) { s << it.key() << "=" << it.value() << ", "; } s << "}"; @@ -795,12 +806,11 @@ class PerfParserPrivate : public QObject } if (static_cast(eventType) == EventType::TracePointSample) { - quint32 eventFormatId; TracePointData traceData; - stream >> eventFormatId >> traceData; - tracepointData[eventFormatId].push_back(traceData); - qCDebug(LOG_PERFPARSER) << "parsed:" << traceData; - sample.tracePointFormat = eventFormatId; + stream >> traceData; + tracepointData.push_back(traceData); + qCDebug(LOG_PERFPARSER) << "DATA parsed:" << traceData; + sample.tracePointFormat = traceData.formatId; sample.tracePointData = tracepointData.size() - 1; } @@ -921,7 +931,7 @@ class PerfParserPrivate : public QObject case EventType::TracePointFormat: { qint32 id; TracePointFormat format; - stream >> id >> format; + stream >> id >> format; // id is the tracepoint id, see /sys/kernel/tracing/system/tracepoint qCDebug(LOG_PERFPARSER) << "parsed:" << format; tracepointFormat[id] = format; break; @@ -954,10 +964,28 @@ class PerfParserPrivate : public QObject buildPerLibraryResult(); buildCallerCalleeResult(); - for (auto it = tracepoints.begin(), end = tracepoints.end(); it != end; it++) { + for (auto it = tracepoints.cbegin(), end = tracepoints.cend(); it != end; it++) { eventResult.tracepoints.push_back({strings[it.key()], {it.value()}}); } + eventResult.tracePointData.reserve(tracepointData.size()); + std::transform(tracepointData.cbegin(), tracepointData.cend(), std::back_inserter(eventResult.tracePointData), + [this](const TracePointData& data) -> Data::TracePointData { + QHash tracepointData; + + for (auto it = data.data.cbegin(), end = data.data.cend(); it != end; it++) { + tracepointData[strings.value(it.key())] = it.value(); + } + + return tracepointData; + }); + + for (auto it = tracepointFormat.cbegin(), end = tracepointFormat.cend(); it != end; it++) { + qDebug() << "FORMAT" << strings.value(it->format.id); + eventResult.tracePointFormats[it.key()] = {strings.value(it->systemId.id), strings.value(it->nameId.id), + it->flags, strings.value(it->format.id)}; + } + for (auto& thread : eventResult.threads) { thread.time.start = std::max(thread.time.start, applicationTime.start); thread.time.end = std::min(thread.time.end, applicationTime.end); @@ -1174,6 +1202,8 @@ class PerfParserPrivate : public QObject event.type = attributeIdsToCostIds.value(sampleCost.attributeId, -1); event.stackId = internStack(sample.frames); event.cpuId = sample.cpu; + event.tracepointFormat = sample.tracePointFormat; + event.tracepointData = sample.tracePointData; thread->events.push_back(event); cpu.events.push_back(event); @@ -1186,7 +1216,8 @@ class PerfParserPrivate : public QObject if (attribute.name.id != m_schedSwitchId) { auto& tracepointList = tracepoints[attribute.name.id]; - tracepointList.push_back({event.time, 0, eventResult.tracepointEventCostId}); + event.type = eventResult.tracepointEventCostId; + tracepointList.push_back(event); } } } @@ -1494,7 +1525,7 @@ class PerfParserPrivate : public QObject Settings::CostAggregation costAggregation; bool perfMapFileExists = false; QHash tracepointFormat; - QHash> tracepointData; + QVector tracepointData; // samples recorded without --call-graph have only one frame int m_numSamplesWithMoreThanOneFrame = 0; @@ -1526,7 +1557,6 @@ PerfParser::PerfParser(QObject* parent) qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); - qRegisterMetaType(); qRegisterMetaType(); qRegisterMetaType(); diff --git a/tests/modeltests/CMakeLists.txt b/tests/modeltests/CMakeLists.txt index 0f667a407..acef0fee4 100644 --- a/tests/modeltests/CMakeLists.txt +++ b/tests/modeltests/CMakeLists.txt @@ -35,6 +35,7 @@ set_target_properties( ecm_add_test( tst_disassemblyoutput.cpp + ../../src/settings.cpp LINK_LIBRARIES Qt::Core Qt::Test @@ -42,7 +43,6 @@ ecm_add_test( PrefixTickLabels TEST_NAME tst_disassemblyoutput - ../../src/settings.cpp ) set_target_properties( @@ -91,3 +91,15 @@ ecm_add_test( tst_formatting ) set_target_properties(tst_formatting PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${KDE_INSTALL_BINDIR}") + +ecm_add_test( + tst_tracepointformat.cpp + LINK_LIBRARIES + Qt::Test + models + TEST_NAME + tst_tracepointformat +) +set_target_properties( + tst_tracepointformat PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${KDE_INSTALL_BINDIR}" +)