summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilian Wolff <milian.wolff@kdab.com>2017-03-22 16:05:57 +0100
committerMilian Wolff <milian.wolff@kdab.com>2017-03-29 14:52:34 +0000
commita374c21359bee141f1635c7798b085cf8e7c516b (patch)
tree14e744054746c7ecdc820508ebd0352a20f7d844
parent4296e642906a540efd5502c65c760f09b0a49ed7 (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.cpp8
-rw-r--r--app/perfunwind.cpp51
-rw-r--r--app/perfunwind.h23
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);