diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2017-03-22 16:05:57 +0100 |
---|---|---|
committer | Milian Wolff <milian.wolff@kdab.com> | 2017-03-29 14:52:34 +0000 |
commit | a374c21359bee141f1635c7798b085cf8e7c516b (patch) | |
tree | 14e744054746c7ecdc820508ebd0352a20f7d844 | |
parent | 4296e642906a540efd5502c65c760f09b0a49ed7 (diff) |
Add mode to print statistics for a given perf data file
To find fitting values for some magic heuristic values we will need
in the follow-up commits to sort events by time, this mode analyzes
a data file and computes some statistics. Unwinding and similarly
expensive operations are disabled in the `--print-stats` mode. The
output of this mode is e.g.:
~~~~~~~~~~~~~~
samples: 20210
mmaps: 24330
max buffer size: 1057128
max time: 628782799569406
max reorder time: 376374129
~~~~~~~~~~~~~~
Change-Id: I5d1344618925502b08ba303239a75d9945d965e7
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r-- | app/main.cpp | 8 | ||||
-rw-r--r-- | app/perfunwind.cpp | 51 | ||||
-rw-r--r-- | app/perfunwind.h | 23 |
3 files changed, 74 insertions, 8 deletions
diff --git a/app/main.cpp b/app/main.cpp index e679f0c..8dc878c 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -152,6 +152,11 @@ int main(int argc, char *argv[]) QLatin1String("/proc/kallsyms")); parser.addOption(kallsymsPath); + QCommandLineOption printStats(QLatin1String("print-stats"), + QCoreApplication::translate( + "main", "Print statistics instead of converting the data.")); + parser.addOption(printStats); + parser.process(app); QScopedPointer<QFile> outfile; @@ -183,7 +188,8 @@ int main(int argc, char *argv[]) parser.value(debug) : parser.value(sysroot) + parser.value(debug), parser.value(extra), parser.value(appPath), parser.isSet(kallsymsPath) ? parser.value(kallsymsPath) - : parser.value(sysroot) + parser.value(kallsymsPath)); + : parser.value(sysroot) + parser.value(kallsymsPath), + parser.isSet(printStats)); PerfHeader header(infile.data()); PerfAttributes attributes; diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp index 8dfcc7f..006df5e 100644 --- a/app/perfunwind.cpp +++ b/app/perfunwind.cpp @@ -44,13 +44,22 @@ bool operator==(const PerfUnwind::Location &a, const PerfUnwind::Location &b) && a.column == b.column; } +void PerfUnwind::Stats::addEventTime(quint64 time) +{ + if (time && time < maxTime) + maxReorderTime = std::max(maxReorderTime, maxTime - time); + else + maxTime = time; +} + PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugPath, const QString &extraLibsPath, const QString &appPath, - const QString &kallsymsPath) : + const QString &kallsymsPath, bool printStats) : m_output(output), m_architecture(PerfRegisterInfo::ARCH_INVALID), m_systemRoot(systemRoot), m_extraLibsPath(extraLibsPath), m_appPath(appPath), m_kallsyms(kallsymsPath), m_sampleBufferSize(0) { + m_stats.enabled = printStats; m_currentUnwind.unwind = this; m_offlineCallbacks.find_elf = dwfl_build_id_find_elf; m_offlineCallbacks.find_debuginfo = dwfl_standard_find_debuginfo; @@ -63,11 +72,13 @@ PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QStri std::memcpy(m_debugInfoPath, newDebugInfo.data(), newDebugInfo.length()); m_offlineCallbacks.debuginfo_path = &m_debugInfoPath; - // Write minimal header, consisting of magic and data stream version we're going to use. - const char magic[] = "QPERFSTREAM"; - output->write(magic, sizeof(magic)); - qint32 dataStreamVersion = qToLittleEndian(QDataStream::Qt_DefaultCompiledVersion); - output->write(reinterpret_cast<const char *>(&dataStreamVersion), sizeof(qint32)); + if (!printStats) { + // Write minimal header, consisting of magic and data stream version we're going to use. + const char magic[] = "QPERFSTREAM"; + output->write(magic, sizeof(magic)); + qint32 dataStreamVersion = qToLittleEndian(QDataStream::Qt_DefaultCompiledVersion); + output->write(reinterpret_cast<const char *>(&dataStreamVersion), sizeof(qint32)); + } } PerfUnwind::~PerfUnwind() @@ -77,6 +88,15 @@ PerfUnwind::~PerfUnwind() delete[] m_debugInfoPath; qDeleteAll(m_symbolTables); + + if (m_stats.enabled) { + QTextStream out(m_output); + out << "samples: " << m_stats.numSamples << "\n"; + out << "mmaps: " << m_stats.numMmaps << "\n"; + out << "max buffer size: " << m_stats.maxBufferSize << "\n"; + out << "max time: " << m_stats.maxTime << "\n"; + out << "max reorder time: " << m_stats.maxReorderTime << "\n"; + } } PerfSymbolTable *PerfUnwind::symbolTable(quint32 pid) @@ -94,11 +114,20 @@ Dwfl *PerfUnwind::dwfl(quint32 pid, quint64 timestamp) void PerfUnwind::registerElf(const PerfRecordMmap &mmap) { + if (m_stats.enabled) { + m_stats.addEventTime(mmap.time()); + ++m_stats.numMmaps; + return; + } + symbolTable(mmap.pid())->registerElf(mmap, m_appPath, m_systemRoot, m_extraLibsPath); } void PerfUnwind::sendBuffer(const QByteArray &buffer) { + if (m_stats.enabled) + return; + quint32 size = qToLittleEndian(buffer.length()); m_output->write(reinterpret_cast<char *>(&size), sizeof(quint32)); m_output->write(buffer); @@ -312,6 +341,13 @@ void PerfUnwind::sample(const PerfRecordSample &sample) m_sampleBuffer.append(sample); m_sampleBufferSize += sample.size(); + if (m_stats.enabled) { + m_stats.maxBufferSize = std::max(m_sampleBufferSize, m_stats.maxBufferSize); + m_stats.addEventTime(sample.time()); + ++m_stats.numSamples; + // don't return early, stats should include our buffer behavior + } + while (m_sampleBufferSize > s_maxSampleBufferSize) { const PerfRecordSample &sample = m_sampleBuffer.front(); m_sampleBufferSize -= sample.size(); @@ -322,6 +358,9 @@ void PerfUnwind::sample(const PerfRecordSample &sample) void PerfUnwind::analyze(const PerfRecordSample &sample) { + if (m_stats.enabled) // don't do any time intensive work in stats mode + return; + m_currentUnwind.isInterworking = false; m_currentUnwind.firstGuessedFrame = -1; m_currentUnwind.sample = &sample; diff --git a/app/perfunwind.h b/app/perfunwind.h index 931a552..b1e5599 100644 --- a/app/perfunwind.h +++ b/app/perfunwind.h @@ -104,7 +104,7 @@ public: PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugInfo, const QString &extraLibs, const QString &appPath, - const QString &kallsymsPath); + const QString &kallsymsPath, bool printStats); ~PerfUnwind(); PerfRegisterInfo::Architecture architecture() const { return m_architecture; } @@ -191,6 +191,27 @@ private: static const uint s_maxSampleBufferSize = 1024 * 1024; + struct Stats + { + Stats() + : numSamples(0), numMmaps(0), + maxBufferSize(0), + maxTime(0), maxReorderTime(0), + enabled(false) + {} + + void addEventTime(quint64 time); + void finishedRound(); + + quint64 numSamples; + quint64 numMmaps; + uint maxBufferSize; + quint64 maxTime; + quint64 maxReorderTime; + bool enabled; + }; + Stats m_stats; + void unwindStack(Dwfl *dwfl); void resolveCallchain(); void analyze(const PerfRecordSample &sample); |