summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/app.pro19
-rw-r--r--app/main.cpp20
-rw-r--r--app/perfattributes.cpp13
-rw-r--r--app/perfattributes.h2
-rw-r--r--app/perfdata.cpp97
-rw-r--r--app/perfdata.h51
-rw-r--r--app/perfelfmap.cpp2
-rw-r--r--app/perffeatures.cpp23
-rw-r--r--app/perffeatures.h12
-rw-r--r--app/perffilesection.h6
-rw-r--r--app/perfheader.cpp4
-rw-r--r--app/perfheader.h26
-rw-r--r--app/perfregisterinfo.cpp28
-rw-r--r--app/perfregisterinfo.h18
-rw-r--r--app/perfstdin.cpp9
-rw-r--r--app/perfstdin.h10
-rw-r--r--app/perfsymboltable.cpp47
-rw-r--r--app/perfsymboltable.h6
-rw-r--r--app/perfunwind.cpp67
-rw-r--r--app/perfunwind.h130
-rw-r--r--elfutils.pri17
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/auto.qbs2
-rw-r--r--tests/auto/perfdata/perfdata.pro38
-rw-r--r--tests/auto/perfdata/perfdata.qbs33
-rw-r--r--tests/auto/perfdata/perfdata.qrc5
-rw-r--r--tests/auto/perfdata/probe.data.streambin0 -> 18676 bytes
-rw-r--r--tests/auto/perfdata/tst_perfdata.cpp69
28 files changed, 494 insertions, 263 deletions
diff --git a/app/app.pro b/app/app.pro
index b0df268..2811982 100644
--- a/app/app.pro
+++ b/app/app.pro
@@ -9,24 +9,7 @@ CONFIG += c++11 console
CONFIG -= app_bundle
include(../paths.pri)
-
-!isEmpty(ELFUTILS_INSTALL_DIR) {
- INCLUDEPATH += $$ELFUTILS_INSTALL_DIR/include $$ELFUTILS_INSTALL_DIR/include/elfutils
- LIBS += -L$$ELFUTILS_INSTALL_DIR/lib
-} else:unix {
- INCLUDEPATH += /usr/include/elfutils
-}
-
-LIBS += -ldw -lelf
-
-win32 {
- LIBS += -leu_compat
-}
-
-linux-g++*:!isEmpty(PERFPARSER_ELFUTILS_INSTALLDIR) {
- RPATH = $$relative_path($$PERFPARSER_ELFUTILS_INSTALLDIR, $$PERFPARSER_APP_INSTALLDIR)
- QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/$$RPATH\'
-}
+include(../elfutils.pri)
DESTDIR = $$PERFPARSER_APP_DESTDIR
target.path = $$PERFPARSER_APP_INSTALLDIR
diff --git a/app/main.cpp b/app/main.cpp
index 491b097..26e4183 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -116,8 +116,7 @@ int main(int argc, char *argv[])
QDir::rootPath());
parser.addOption(sysroot);
- const auto defaultDebug = QString::fromLatin1("%1usr%1lib%1debug%2%3%1.debug%2.debug")
- .arg(QDir::separator(), QDir::listSeparator(), QDir::homePath());
+ const auto defaultDebug = PerfUnwind::defaultDebugInfoPath();
QCommandLineOption debug(QLatin1String("debug"),
QCoreApplication::translate(
"main",
@@ -155,7 +154,7 @@ int main(int argc, char *argv[])
defaultArch);
parser.addOption(arch);
- const auto defaultKallsyms = QString::fromLatin1("%1proc%1kallsyms").arg(QDir::separator());
+ const auto defaultKallsyms = PerfUnwind::defaultKallsymsPath();
QCommandLineOption kallsymsPath(QLatin1String("kallsyms"),
QCoreApplication::translate(
"main", "Path to kallsyms mapping to resolve kernel "
@@ -233,11 +232,16 @@ int main(int argc, char *argv[])
PerfUnwind unwind(outfile.data(), parser.value(sysroot), parser.isSet(debug) ?
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.isSet(kallsymsPath), parser.isSet(printStats),
- maxEventBufferSize, maxFramesValue);
+ parser.value(extra), parser.value(appPath), parser.isSet(printStats));
+
+ unwind.setKallsymsPath(parser.isSet(kallsymsPath)
+ ? parser.value(kallsymsPath)
+ : (parser.value(sysroot) + parser.value(kallsymsPath)));
+
+ unwind.setIgnoreKallsymsBuildId(parser.isSet(kallsymsPath));
+
+ unwind.setMaxEventBufferSize(maxEventBufferSize);
+ unwind.setMaxUnwindFrames(maxFramesValue);
PerfHeader header(infile.data());
PerfAttributes attributes;
diff --git a/app/perfattributes.cpp b/app/perfattributes.cpp
index daa7396..e9ea7df 100644
--- a/app/perfattributes.cpp
+++ b/app/perfattributes.cpp
@@ -54,7 +54,13 @@ QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs)
*(&attrs.m_readFormat + 1) = flags;
- stream.skipRawData(attrs.m_size - PerfEventAttributes::fixedLength());
+ static const int intMax = std::numeric_limits<int>::max();
+ quint32 skip = attrs.m_size - PerfEventAttributes::fixedLength();
+ if (skip > intMax) {
+ stream.skipRawData(intMax);
+ skip -= intMax;
+ }
+ stream.skipRawData(static_cast<int>(skip));
return stream;
}
@@ -154,7 +160,7 @@ QByteArray PerfEventAttributes::name() const
}
}
-quint64 PerfEventAttributes::fixedLength()
+quint16 PerfEventAttributes::fixedLength()
{
return sizeof(m_type) + sizeof(m_type) + sizeof(m_config) + sizeof(m_sampleFreq)
+ sizeof(m_sampleType) + sizeof(m_readFormat) + sizeof(quint64) // flags
@@ -281,7 +287,8 @@ bool PerfAttributes::read(QIODevice *device, PerfHeader *header)
QDataStream idStream(device);
idStream.setByteOrder(header->byteOrder());
quint64 id;
- for (uint i = 0; i < ids.size / sizeof(quint64); ++i) {
+ for (qint64 i = 0, num = ids.size / static_cast<qint64>(sizeof(quint64));
+ i < num; ++i) {
idStream >> id;
m_attributes[id] = attrs;
}
diff --git a/app/perfattributes.h b/app/perfattributes.h
index 1be3cc4..db4f4f2 100644
--- a/app/perfattributes.h
+++ b/app/perfattributes.h
@@ -171,7 +171,7 @@ public:
SOFTWARE_MAX, /* non-ABI */
};
- static quint64 fixedLength();
+ static quint16 fixedLength();
bool operator==(const PerfEventAttributes &rhs) const;
diff --git a/app/perfdata.cpp b/app/perfdata.cpp
index d73f31c..1305d21 100644
--- a/app/perfdata.cpp
+++ b/app/perfdata.cpp
@@ -24,6 +24,8 @@
#include <QDebug>
#include <limits>
+static const int intMax = std::numeric_limits<int>::max();
+
PerfData::PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader *header,
PerfAttributes *attributes) :
m_source(source), m_destination(destination), m_header(header), m_attributes(attributes)
@@ -32,10 +34,13 @@ PerfData::PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader
PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
{
- qint64 headerSize = PerfEventHeader::fixedLength();
+ const quint16 headerSize = PerfEventHeader::fixedLength();
if (m_eventHeader.size == 0) {
- if (stream.device()->bytesAvailable() < headerSize)
+ const qint64 available = stream.device()->bytesAvailable();
+ if (available < 0)
+ return SignalError;
+ if (available < headerSize)
return Rerun;
stream >> m_eventHeader;
@@ -47,7 +52,7 @@ PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
}
}
- const qint64 contentSize = m_eventHeader.size - headerSize;
+ const quint16 contentSize = m_eventHeader.size - headerSize;
if (stream.device()->bytesAvailable() < contentSize)
return Rerun;
@@ -132,6 +137,21 @@ PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
m_destination->exit(exit);
break;
}
+ case PERF_RECORD_HEADER_TRACING_DATA: {
+ if (contentSize == 4) {
+ // The content is actually another 4 byte integer,
+ // describing the size of the real content that follows.
+ quint32 content;
+ stream >> content;
+ stream.skipRawData(content);
+ } else {
+ // Maybe someone with a brain will fix this eventually ...
+ // then we'll hit this branch.
+ qWarning() << "HEADER_TRACING_DATA with unexpected contentSize" << contentSize;
+ stream.skipRawData(contentSize);
+ }
+ break;
+ }
case PERF_RECORD_FINISHED_ROUND: {
m_destination->finishedRound();
if (contentSize != 0) {
@@ -198,7 +218,7 @@ PerfData::ReadStatus PerfData::doRead()
const qint64 posDeltaBetweenProgress = dataSize / 100;
qint64 nextProgressAt = m_source->pos() + posDeltaBetweenProgress;
- while (static_cast<quint64>(m_source->pos()) < endOfDataSection) {
+ while (m_source->pos() < endOfDataSection) {
if (processEvents(stream) != SignalFinished) {
returnCode = SignalError;
break;
@@ -264,12 +284,8 @@ QDataStream &PerfRecordMmap::readNumbers(QDataStream &stream)
return stream >> m_pid >> m_tid >> m_addr >> m_len >> m_pgoff;
}
-QDataStream &PerfRecordMmap::readFilename(QDataStream &stream, quint64 filenameLength)
+QDataStream &PerfRecordMmap::readFilename(QDataStream &stream, quint16 filenameLength)
{
- if (filenameLength > static_cast<quint64>(std::numeric_limits<int>::max())) {
- qWarning() << "bad filename length";
- return stream;
- }
m_filename.resize(filenameLength);
stream.readRawData(m_filename.data(), filenameLength);
int null = m_filename.indexOf('\0');
@@ -284,7 +300,7 @@ QDataStream &PerfRecordMmap::readSampleId(QDataStream &stream)
return stream;
}
-quint64 PerfRecordMmap::fixedLength() const
+quint16 PerfRecordMmap::fixedLength() const
{
return sizeof(m_pid) + sizeof(m_tid) + sizeof(m_addr) + sizeof(m_len) + sizeof(m_pgoff)
+ m_header.fixedLength() + m_sampleId.fixedLength();
@@ -310,7 +326,7 @@ QDataStream &PerfRecordMmap2::readNumbers(QDataStream &stream)
return stream >> m_maj >> m_min >> m_ino >> m_ino_generation >> m_prot >> m_flags;
}
-quint64 PerfRecordMmap2::fixedLength() const
+quint16 PerfRecordMmap2::fixedLength() const
{
return PerfRecordMmap::fixedLength() + sizeof(m_maj) + sizeof(m_min) + sizeof(m_ino)
+ sizeof(m_ino_generation) + sizeof(m_prot) + sizeof(m_flags);
@@ -332,12 +348,8 @@ PerfRecordComm::PerfRecordComm(PerfEventHeader *header, quint64 sampleType, bool
QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record)
{
stream >> record.m_pid >> record.m_tid;
- const quint64 commLength = record.m_header.size - record.fixedLength();
+ const quint16 commLength = record.m_header.size - record.fixedLength();
- if (commLength > static_cast<quint64>(std::numeric_limits<int>::max())) {
- qWarning() << "bad comm length";
- return stream;
- }
record.m_comm.resize(commLength);
stream.readRawData(record.m_comm.data(), commLength);
int null = record.m_comm.indexOf('\0');
@@ -382,9 +394,9 @@ QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId)
}
-quint64 PerfSampleId::fixedLength() const
+quint16 PerfSampleId::fixedLength() const
{
- quint64 ret = 0;
+ quint16 ret = 0;
if (m_sampleType & PerfEventAttributes::SAMPLE_ID_ALL) {
if (m_sampleType & PerfEventAttributes::SAMPLE_TID)
ret += sizeof(m_pid) + sizeof(m_tid);
@@ -417,12 +429,13 @@ PerfRecordSample::PerfRecordSample(const PerfEventHeader *header,
{
}
-quint64 PerfRecordSample::registerValue(uint reg) const
+quint64 PerfRecordSample::registerValue(int reg) const
{
+ Q_ASSERT(reg >= 0);
Q_ASSERT(m_registerAbi && m_registerMask & (1ull << reg));
int index = 0;
- for (uint i = 0; i < reg; i++) {
+ for (int i = 0; i < reg; i++) {
if (m_registerMask & (1ull << i))
index++;
}
@@ -431,7 +444,7 @@ quint64 PerfRecordSample::registerValue(uint reg) const
return m_registers[index];
} else {
qWarning() << "invalid register offset" << index;
- return -1;
+ return std::numeric_limits<quint64>::max();
}
}
@@ -497,12 +510,14 @@ QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record)
if (sampleType & PerfEventAttributes::SAMPLE_RAW) {
quint32 rawSize;
stream >> rawSize;
- if (rawSize > static_cast<quint32>(std::numeric_limits<int>::max())) {
- qWarning() << "bad raw data section";
- return stream;
+ if (rawSize > intMax) {
+ qWarning() << "Excessively long raw data section" << rawSize;
+ stream.skipRawData(intMax);
+ stream.skipRawData(static_cast<int>(rawSize - intMax));
+ } else {
+ record.m_rawData.resize(static_cast<int>(rawSize));
+ stream.readRawData(record.m_rawData.data(), record.m_rawData.length());
}
- record.m_rawData.resize(rawSize);
- stream.readRawData(record.m_rawData.data(), rawSize);
}
if (sampleType & PerfEventAttributes::SAMPLE_BRANCH_STACK) {
@@ -527,19 +542,29 @@ QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record)
}
if (sampleType & PerfEventAttributes::SAMPLE_STACK_USER) {
- quint64 size;
- stream >> size;
+ quint64 sectionSize;
+ stream >> sectionSize;
- if (size > static_cast<quint64>(std::numeric_limits<int>::max())) {
+ if (sectionSize > intMax) {
// We don't accept stack samples of > 2G, sorry ...
- qWarning() << "bad stack size";
- return stream;
- }
- if (size > 0) {
- record.m_userStack.resize(size);
- stream.readRawData(record.m_userStack.data(), size);
- stream >> size;
+ qWarning() << "Excessively large stack snapshot" << sectionSize;
+ do {
+ stream.skipRawData(intMax);
+ sectionSize -= intMax;
+ } while (sectionSize > intMax);
+ stream.skipRawData(static_cast<int>(sectionSize));
+ sectionSize = 0;
+ } else if (sectionSize > 0) {
+ record.m_userStack.resize(static_cast<int>(sectionSize));
+ stream.readRawData(record.m_userStack.data(), record.m_userStack.size());
}
+
+ quint64 contentSize;
+ stream >> contentSize;
+ if (contentSize > sectionSize)
+ qWarning() << "Truncated stack snapshot" << contentSize << sectionSize;
+ else
+ record.m_userStack.resize(static_cast<int>(contentSize));
}
if (sampleType & PerfEventAttributes::SAMPLE_WEIGHT)
diff --git a/app/perfdata.h b/app/perfdata.h
index dd8297a..572d318 100644
--- a/app/perfdata.h
+++ b/app/perfdata.h
@@ -232,19 +232,20 @@ class PerfRecordSample;
struct PerfSampleId {
PerfSampleId(quint64 sampleType = 0, bool sampleIdAll = false) : m_pid(0), m_tid(0), m_time(0),
m_id(0), m_streamId(0), m_cpu(0), m_res(0),
- m_sampleType(sampleType | (sampleIdAll ? (quint64)PerfEventAttributes::SAMPLE_ID_ALL : 0))
+ m_sampleType(sampleType
+ | (sampleIdAll ? static_cast<quint64>(PerfEventAttributes::SAMPLE_ID_ALL) : 0))
{}
- quint32 pid() const { return m_pid; }
- quint32 tid() const { return m_tid; }
+ qint32 pid() const { return m_pid; }
+ qint32 tid() const { return m_tid; }
quint64 time() const { return m_time; }
quint64 id() const { return m_id; }
- quint64 fixedLength() const;
+ quint16 fixedLength() const;
quint64 sampleType() const { return m_sampleType; }
private:
- quint32 m_pid;
- quint32 m_tid;
+ qint32 m_pid;
+ qint32 m_tid;
quint64 m_time;
quint64 m_id;
quint64 m_streamId;
@@ -264,18 +265,18 @@ QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId);
class PerfRecord {
public:
- quint32 pid() const { return m_sampleId.pid(); }
- quint32 tid() const { return m_sampleId.tid(); }
+ qint32 pid() const { return m_sampleId.pid(); }
+ qint32 tid() const { return m_sampleId.tid(); }
quint64 time() const { return m_sampleId.time(); }
quint64 id() const { return m_sampleId.id(); }
- uint size() const { return m_header.size; }
+ quint16 size() const { return m_header.size; }
protected:
PerfRecord(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll);
PerfEventHeader m_header;
PerfSampleId m_sampleId;
- quint64 fixedLength() const { return m_header.fixedLength() + m_sampleId.fixedLength(); }
+ quint16 fixedLength() const { return m_header.fixedLength() + m_sampleId.fixedLength(); }
};
class PerfRecordMmap2;
@@ -284,8 +285,8 @@ public:
PerfRecordMmap(PerfEventHeader *header = 0, quint64 sampleType = 0, bool sampleIdAll = false);
// The pids and tids in the sampleId are always 0 in this case. Go figure ...
- quint32 pid() const { return m_pid; }
- quint32 tid() const { return m_tid; }
+ qint32 pid() const { return m_pid; }
+ qint32 tid() const { return m_tid; }
quint64 addr() const { return m_addr; }
quint64 len() const { return m_len; }
@@ -294,13 +295,13 @@ public:
protected:
QDataStream &readNumbers(QDataStream &stream);
- QDataStream &readFilename(QDataStream &stream, quint64 filenameLength);
+ QDataStream &readFilename(QDataStream &stream, quint16 filenameLength);
QDataStream &readSampleId(QDataStream &stream);
- quint64 fixedLength() const;
+ quint16 fixedLength() const;
private:
- quint32 m_pid;
- quint32 m_tid;
+ qint32 m_pid;
+ qint32 m_tid;
quint64 m_addr;
quint64 m_len;
quint64 m_pgoff;
@@ -330,7 +331,7 @@ private:
quint32 m_prot;
quint32 m_flags;
- quint64 fixedLength() const;
+ quint16 fixedLength() const;
friend QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record);
};
@@ -354,11 +355,11 @@ public:
PerfRecordComm(PerfEventHeader *header = 0, quint64 sampleType = 0, bool sampleIdAll = false);
const QByteArray &comm() const { return m_comm; }
private:
- quint32 m_pid;
- quint32 m_tid;
+ qint32 m_pid;
+ qint32 m_tid;
QByteArray m_comm;
- quint64 fixedLength() const { return PerfRecord::fixedLength() + sizeof(m_pid) + sizeof(m_tid); }
+ quint16 fixedLength() const { return PerfRecord::fixedLength() + sizeof(m_pid) + sizeof(m_tid); }
friend QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record);
};
@@ -369,7 +370,7 @@ class PerfRecordSample : public PerfRecord {
public:
PerfRecordSample(const PerfEventHeader *header = 0, const PerfEventAttributes *attributes = 0);
quint64 registerAbi() const { return m_registerAbi; }
- quint64 registerValue(uint reg) const;
+ quint64 registerValue(int reg) const;
quint64 ip() const { return m_ip; }
const QByteArray &userStack() const { return m_userStack; }
const QList<quint64> &callchain() const { return m_callchain; }
@@ -437,11 +438,11 @@ class PerfRecordFork : public PerfRecord
{
public:
PerfRecordFork(PerfEventHeader *header = 0, quint64 sampleType = 0, bool sampleIdAll = false);
- quint32 childTid() const { return m_tid; }
- quint32 childPid() const { return m_pid; }
+ qint32 childTid() const { return m_tid; }
+ qint32 childPid() const { return m_pid; }
private:
- quint32 m_pid, m_ppid;
- quint32 m_tid, m_ptid;
+ qint32 m_pid, m_ppid;
+ qint32 m_tid, m_ptid;
quint64 m_time;
friend QDataStream &operator>>(QDataStream &stream, PerfRecordFork &record);
diff --git a/app/perfelfmap.cpp b/app/perfelfmap.cpp
index a55bfd5..545f362 100644
--- a/app/perfelfmap.cpp
+++ b/app/perfelfmap.cpp
@@ -80,7 +80,7 @@ bool PerfElfMap::registerElf(const quint64 addr, const quint64 len, quint64 pgof
i->originalFileName, i->originalPath));
}
- removedElfs.push_back(std::distance(m_elfs.begin(), i));
+ removedElfs.push_back(static_cast<int>(std::distance(m_elfs.begin(), i)));
// Overlapping module. Clear the cache, but only when the section is actually backed by a
// file. Otherwise, we will see tons of overlapping heap/anon sections which don't actually
diff --git a/app/perffeatures.cpp b/app/perffeatures.cpp
index ceb0324..537773c 100644
--- a/app/perffeatures.cpp
+++ b/app/perffeatures.cpp
@@ -94,7 +94,7 @@ void PerfFeatures::createFeature(QIODevice *device, QDataStream::ByteOrder byteO
break;
}
- quint64 readSize = device->pos() - section.offset;
+ qint64 readSize = device->pos() - section.offset;
if (section.size != readSize)
qWarning() << "feature not properly read" << featureId << section.size << readSize;
}
@@ -119,7 +119,7 @@ bool PerfFeatures::read(QIODevice *device, const PerfHeader *header)
QHash<PerfHeader::Feature, PerfFileSection> featureSections;
PerfFileSection section;
for (uint i = 0; i < PerfHeader::LAST_FEATURE; ++i) {
- PerfHeader::Feature feature = (PerfHeader::Feature)i;
+ PerfHeader::Feature feature = static_cast<PerfHeader::Feature>(i);
if (header->hasFeature(feature)) {
stream >> section;
if (section.size > 0)
@@ -138,7 +138,7 @@ bool PerfFeatures::read(QIODevice *device, const PerfHeader *header)
QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId)
{
- quint64 next = 0;
+ qint64 next = 0;
while (next < buildId.size) {
PerfEventHeader header;
stream >> header;
@@ -150,12 +150,8 @@ QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId)
stream.readRawData(build.id.data(), PerfBuildId::s_idLength);
stream.skipRawData(PerfBuildId::s_idPadding);
- uint fileNameLength = header.size - PerfEventHeader::fixedLength() - sizeof(build.pid)
+ quint16 fileNameLength = header.size - PerfEventHeader::fixedLength() - sizeof(build.pid)
- PerfBuildId::s_idPadding - PerfBuildId::s_idLength;
- if (fileNameLength > static_cast<uint>(std::numeric_limits<int>::max())) {
- qWarning() << "bad file name length";
- return stream;
- }
build.fileName.resize(fileNameLength);
stream.readRawData(build.fileName.data(), fileNameLength);
removeTrailingZeros(&build.fileName);
@@ -185,12 +181,15 @@ QDataStream &operator>>(QDataStream &stream, PerfStringFeature &string)
{
quint32 length;
stream >> length;
- if (length > static_cast<quint32>(std::numeric_limits<int>::max())) {
- qWarning() << "bad string length";
+ static const int intMax = std::numeric_limits<int>::max();
+ if (length > intMax) {
+ qWarning() << "Excessively long string" << length;
+ stream.skipRawData(intMax);
+ stream.skipRawData(static_cast<int>(length - intMax));
return stream;
}
- string.value.resize(length);
- stream.readRawData(string.value.data(), length);
+ string.value.resize(static_cast<int>(length));
+ stream.readRawData(string.value.data(), string.value.length());
removeTrailingZeros(&string.value);
return stream;
}
diff --git a/app/perffeatures.h b/app/perffeatures.h
index d9c197c..da25609 100644
--- a/app/perffeatures.h
+++ b/app/perffeatures.h
@@ -33,7 +33,7 @@ struct PerfEventHeader {
quint16 misc;
quint16 size;
- static quint64 fixedLength() { return sizeof(type) + sizeof(misc) + sizeof(size); }
+ static quint16 fixedLength() { return sizeof(type) + sizeof(misc) + sizeof(size); }
};
QDataStream &operator>>(QDataStream &stream, PerfEventHeader &header);
@@ -41,17 +41,17 @@ QDataStream &operator>>(QDataStream &stream, PerfEventHeader &header);
struct PerfBuildId {
PerfBuildId() : size(0) {}
- static const uint s_idLength = 20;
- static const uint s_idPadding = 4; // 20 aligned to 8 gives 24 => 4 unused bytes
- static const uint s_pathMax = 4096;
+ static const quint16 s_idLength = 20;
+ static const quint16 s_idPadding = 4; // 20 aligned to 8 gives 24 => 4 unused bytes
+ static const quint16 s_pathMax = 4096;
struct BuildId {
- quint32 pid;
+ qint32 pid;
QByteArray id; // raw id, use .toHex() to get something human-readable
QByteArray fileName;
};
- quint64 size;
+ qint64 size;
QList<BuildId> buildIds;
};
diff --git a/app/perffilesection.h b/app/perffilesection.h
index 9615d26..78b706f 100644
--- a/app/perffilesection.h
+++ b/app/perffilesection.h
@@ -25,10 +25,10 @@
struct PerfFileSection {
PerfFileSection();
- quint64 offset;
- quint64 size;
+ qint64 offset;
+ qint64 size;
- static quint64 fixedLength() { return sizeof(offset) + sizeof(size); }
+ static quint16 fixedLength() { return sizeof(offset) + sizeof(size); }
};
QDataStream &operator>>(QDataStream &stream, PerfFileSection &section);
diff --git a/app/perfheader.cpp b/app/perfheader.cpp
index 8252ad4..99a738f 100644
--- a/app/perfheader.cpp
+++ b/app/perfheader.cpp
@@ -65,8 +65,8 @@ void PerfHeader::read()
return;
}
- if (m_source->bytesAvailable() < static_cast<qint64>(m_size - sizeof(m_magic) -
- sizeof(m_size)))
+ if (m_source->bytesAvailable()
+ < m_size - static_cast<qint64>(sizeof(m_magic) + sizeof(m_size)))
return;
// file header
diff --git a/app/perfheader.h b/app/perfheader.h
index 4138337..fdc01ec 100644
--- a/app/perfheader.h
+++ b/app/perfheader.h
@@ -55,7 +55,7 @@ public:
LAST_FEATURE,
FEAT_BITS = 256,
};
- Q_ENUM(Feature);
+ Q_ENUM(Feature)
QDataStream::ByteOrder byteOrder() const;
@@ -63,13 +63,13 @@ public:
void setFeature(Feature feature);
void clearFeature(Feature feature);
- uint numAttrs() const { return m_attrs.size > 0 ? m_attrs.size / m_attrSize : 0; }
- quint64 attrSize() const { return m_attrSize; }
+ qint64 numAttrs() const { return m_attrs.size > 0 ? m_attrs.size / m_attrSize : 0ll; }
+ qint64 attrSize() const { return m_attrSize; }
const PerfFileSection &attrs() const { return m_attrs; }
- quint64 featureOffset() const { return m_data.offset + m_data.size; }
- quint64 dataOffset() const { return m_data.offset; }
- quint64 dataSize() const { return m_data.size; }
+ qint64 featureOffset() const { return m_data.offset + m_data.size; }
+ qint64 dataOffset() const { return m_data.offset; }
+ qint64 dataSize() const { return m_data.size; }
bool isPipe() const { return m_size == s_pipeHeaderSize; }
public slots:
@@ -82,9 +82,9 @@ signals:
private:
QIODevice *m_source;
- quint64 m_magic;
- quint64 m_size;
- quint64 m_attrSize;
+ qint64 m_magic;
+ qint64 m_size;
+ qint64 m_attrSize;
PerfFileSection m_attrs;
PerfFileSection m_data;
@@ -92,10 +92,10 @@ private:
quint64 m_features[FEAT_BITS / 64 + ((FEAT_BITS % 64) > 0 ? 1 : 0)];
- static const quint64 s_magicSame = 0x32454c4946524550ULL;
- static const quint64 s_magicSwitched = 0x50455246494c4532ULL;
- static const quint64 s_pipeHeaderSize = 16ULL;
- static const quint64 s_perfHeaderSize = 104;
+ static const qint64 s_magicSame = 0x32454c4946524550LL;
+ static const qint64 s_magicSwitched = 0x50455246494c4532LL;
+ static const qint64 s_pipeHeaderSize = 16LL;
+ static const qint64 s_perfHeaderSize = 104LL;
};
#endif // PERFHEADER_H
diff --git a/app/perfregisterinfo.cpp b/app/perfregisterinfo.cpp
index 1c3d3cf..b5385bb 100644
--- a/app/perfregisterinfo.cpp
+++ b/app/perfregisterinfo.cpp
@@ -24,7 +24,7 @@ const char *PerfRegisterInfo::s_archNames[] = {
"arm", "aarch64", "powerpc", "s390", "sh", "sparc", "x86"
};
-const uint PerfRegisterInfo::s_numRegisters[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
+const int PerfRegisterInfo::s_numRegisters[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
{16, 16},
{33, 33},
{ 0, 0},
@@ -34,7 +34,7 @@ const uint PerfRegisterInfo::s_numRegisters[PerfRegisterInfo::ARCH_INVALID][Perf
{ 9, 17},
};
-const uint PerfRegisterInfo::s_wordWidth[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
+const int PerfRegisterInfo::s_wordWidth[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
{4, 4},
{8, 8},
{0, 0},
@@ -45,17 +45,17 @@ const uint PerfRegisterInfo::s_wordWidth[PerfRegisterInfo::ARCH_INVALID][PerfReg
};
// Perf and Dwarf register layouts are the same for ARM and ARM64
-static uint arm[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-static uint aarch64[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
- 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
+static int arm[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+static int aarch64[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
// X86 is a mess
-static uint x86[] = {0, 2, 3, 1, 7, 6, 4, 5, 8};
-static uint x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 8};
+static int x86[] = {0, 2, 3, 1, 7, 6, 4, 5, 8};
+static int x86_64[] = {0, 3, 2, 1, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23, 8};
-static uint none[] = {0};
+static int none[] = {0};
-const uint *PerfRegisterInfo::s_perfToDwarf[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
+const int *PerfRegisterInfo::s_perfToDwarf[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
{arm, arm },
{aarch64, aarch64},
{none, none },
@@ -65,15 +65,15 @@ const uint *PerfRegisterInfo::s_perfToDwarf[PerfRegisterInfo::ARCH_INVALID][Perf
{x86, x86_64 }
};
-const uint PerfRegisterInfo::s_perfIp[ARCH_INVALID] = {
+const int PerfRegisterInfo::s_perfIp[ARCH_INVALID] = {
15, 32, 0xffff, 0xffff, 0xffff, 0xffff, 8
};
-const uint PerfRegisterInfo::s_perfSp[ARCH_INVALID] = {
+const int PerfRegisterInfo::s_perfSp[ARCH_INVALID] = {
13, 31, 0xffff, 0xffff, 0xffff, 0xffff, 7
};
-const uint PerfRegisterInfo::s_dwarfLr[ARCH_INVALID][s_numAbis] = {
+const int PerfRegisterInfo::s_dwarfLr[ARCH_INVALID][s_numAbis] = {
{14, 14},
{30, 30},
{0xffff, 0xffff},
@@ -83,7 +83,7 @@ const uint PerfRegisterInfo::s_dwarfLr[ARCH_INVALID][s_numAbis] = {
{0xffff, 0xffff}
};
-const uint PerfRegisterInfo::s_dwarfIp[ARCH_INVALID][s_numAbis] = {
+const int PerfRegisterInfo::s_dwarfIp[ARCH_INVALID][s_numAbis] = {
{15, 15},
{32, 32},
{0xffff, 0xffff},
@@ -93,7 +93,7 @@ const uint PerfRegisterInfo::s_dwarfIp[ARCH_INVALID][s_numAbis] = {
{8, 16}
};
-const uint PerfRegisterInfo::s_dummyRegisters[ARCH_INVALID][2] = {
+const int PerfRegisterInfo::s_dummyRegisters[ARCH_INVALID][2] = {
{0, 0},
{72, 80},
{0, 0},
diff --git a/app/perfregisterinfo.h b/app/perfregisterinfo.h
index 3754073..88e6e58 100644
--- a/app/perfregisterinfo.h
+++ b/app/perfregisterinfo.h
@@ -37,29 +37,29 @@ public:
ARCH_INVALID
};
- static const uint s_numAbis = 2; // maybe more for some archs?
+ static const int s_numAbis = 2; // maybe more for some archs?
static const char *s_archNames[ARCH_INVALID];
- static const uint s_numRegisters[ARCH_INVALID][s_numAbis];
- static const uint s_wordWidth[ARCH_INVALID][s_numAbis];
+ static const int s_numRegisters[ARCH_INVALID][s_numAbis];
+ static const int s_wordWidth[ARCH_INVALID][s_numAbis];
// Translation table for converting perf register layout to dwarf register layout
// This is specific to ABI as the different ABIs may have different numbers of registers.
- static const uint *s_perfToDwarf[ARCH_INVALID][s_numAbis];
+ static const int *s_perfToDwarf[ARCH_INVALID][s_numAbis];
// location of IP register or equivalent in perf register layout for each arch/abi
// This is not specific to ABI as perf makes sure IP is always in the same spot
- static const uint s_perfIp[ARCH_INVALID];
+ static const int s_perfIp[ARCH_INVALID];
// location of SP register or equivalent in perf register layout for each arch/abi
- static const uint s_perfSp[ARCH_INVALID];
+ static const int s_perfSp[ARCH_INVALID];
// location of LR register or equivalent in dwarf register layout for each arch/abi
- static const uint s_dwarfLr[ARCH_INVALID][s_numAbis];
+ static const int s_dwarfLr[ARCH_INVALID][s_numAbis];
// location of IP register or equivalent in dwarf register layout for each arch/abi
- static const uint s_dwarfIp[ARCH_INVALID][s_numAbis];
+ static const int s_dwarfIp[ARCH_INVALID][s_numAbis];
// ranges of registers expected by libdw, but not provided by perf
- static const uint s_dummyRegisters[ARCH_INVALID][2];
+ static const int s_dummyRegisters[ARCH_INVALID][2];
// default architecture for the system which was used for compilation
static const Architecture s_defaultArchitecture;
diff --git a/app/perfstdin.cpp b/app/perfstdin.cpp
index 8d4d2b0..ecabe45 100644
--- a/app/perfstdin.cpp
+++ b/app/perfstdin.cpp
@@ -33,13 +33,16 @@ bool PerfStdin::open(QIODevice::OpenMode mode)
qint64 PerfStdin::readData(char *data, qint64 maxlen)
{
- size_t read = fread(data, 1, maxlen, stdin);
+ if (maxlen <= 0)
+ return 0;
+ size_t read = fread(data, 1, static_cast<size_t>(maxlen), stdin);
if (feof(stdin) || ferror(stdin))
QTimer::singleShot(0, this, &QIODevice::close);
- if (read == 0 && maxlen > 0) {
+ if (read == 0) {
return -1;
} else {
- return read;
+ Q_ASSERT(read <= static_cast<size_t>(maxlen));
+ return static_cast<qint64>(read);
}
}
diff --git a/app/perfstdin.h b/app/perfstdin.h
index 2633c84..e272b23 100644
--- a/app/perfstdin.h
+++ b/app/perfstdin.h
@@ -26,13 +26,13 @@
class PerfStdin : public QIODevice
{
public:
- bool open(OpenMode mode);
- bool isSequential() const;
- qint64 bytesAvailable() const;
+ bool open(OpenMode mode) override;
+ bool isSequential() const override;
+ qint64 bytesAvailable() const override;
protected:
- qint64 readData(char *data, qint64 maxlen);
- qint64 writeData(const char *data, qint64 len);
+ qint64 readData(char *data, qint64 maxlen) override;
+ qint64 writeData(const char *data, qint64 len) override;
};
#endif // PERFSTDIN_H
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp
index c306aa7..a718566 100644
--- a/app/perfsymboltable.cpp
+++ b/app/perfsymboltable.cpp
@@ -61,7 +61,7 @@ extern "C" {
#define O_BINARY 0
#endif
-PerfSymbolTable::PerfSymbolTable(quint32 pid, Dwfl_Callbacks *callbacks, PerfUnwind *parent) :
+PerfSymbolTable::PerfSymbolTable(qint32 pid, Dwfl_Callbacks *callbacks, PerfUnwind *parent) :
m_perfMapFile(QDir::tempPath() + QDir::separator()
+ QString::fromLatin1("perf-%1.map").arg(pid)),
m_cacheIsDirty(false),
@@ -91,7 +91,7 @@ static pid_t nextThread(Dwfl *dwfl, void *arg, void **threadArg)
return dwfl_pid(dwfl);
}
-static void *memcpyTarget(Dwarf_Word *result, uint wordWidth)
+static void *memcpyTarget(Dwarf_Word *result, int wordWidth)
{
if (wordWidth == 4)
return (uint32_t *)result;
@@ -100,22 +100,24 @@ static void *memcpyTarget(Dwarf_Word *result, uint wordWidth)
return result;
}
-static void doMemcpy(Dwarf_Word *result, const void *src, uint wordWidth)
+static void doMemcpy(Dwarf_Word *result, const void *src, int wordWidth)
{
+ Q_ASSERT(wordWidth > 0);
*result = 0; // initialize, as we might only overwrite half of it
- std::memcpy(memcpyTarget(result, wordWidth), src, wordWidth);
+ std::memcpy(memcpyTarget(result, wordWidth), src, static_cast<size_t>(wordWidth));
}
-static uint registerAbi(const PerfRecordSample *sample)
+static quint64 registerAbi(const PerfRecordSample *sample)
{
- const uint abi = sample->registerAbi();
+ const quint64 abi = sample->registerAbi();
Q_ASSERT(abi > 0); // ABI 0 means "no registers" - we shouldn't unwind in this case.
return abi - 1;
}
static bool accessDsoMem(const PerfUnwind::UnwindInfo *ui, Dwarf_Addr addr,
- Dwarf_Word *result, uint wordWidth)
+ Dwarf_Word *result, int wordWidth)
{
+ Q_ASSERT(wordWidth > 0);
// TODO: Take the pgoff into account? Or does elf_getdata do that already?
auto mod = ui->unwind->symbolTable(ui->sample->pid())->module(addr);
if (!mod)
@@ -138,7 +140,7 @@ static bool accessDsoMem(const PerfUnwind::UnwindInfo *ui, Dwarf_Addr addr,
static bool memoryRead(Dwfl *, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
{
PerfUnwind::UnwindInfo *ui = static_cast<PerfUnwind::UnwindInfo *>(arg);
- const uint wordWidth =
+ const int wordWidth =
PerfRegisterInfo::s_wordWidth[ui->unwind->architecture()][registerAbi(ui->sample)];
/* Check overflow. */
@@ -152,7 +154,8 @@ static bool memoryRead(Dwfl *, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
quint64 start = ui->sample->registerValue(
PerfRegisterInfo::s_perfSp[ui->unwind->architecture()]);
- quint64 end = start + stack.size();
+ Q_ASSERT(stack.size() >= 0);
+ quint64 end = start + static_cast<quint64>(stack.size());
if (addr < start || addr + sizeof(Dwarf_Word) > end) {
// not stack, try reading from ELF
@@ -181,32 +184,34 @@ static bool memoryRead(Dwfl *, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
static bool setInitialRegisters(Dwfl_Thread *thread, void *arg)
{
const PerfUnwind::UnwindInfo *ui = static_cast<PerfUnwind::UnwindInfo *>(arg);
- const uint abi = registerAbi(ui->sample);
+ const quint64 abi = registerAbi(ui->sample);
const uint architecture = ui->unwind->architecture();
- const uint numRegs = PerfRegisterInfo::s_numRegisters[architecture][abi];
+ const int numRegs = PerfRegisterInfo::s_numRegisters[architecture][abi];
+ Q_ASSERT(numRegs >= 0);
QVarLengthArray<Dwarf_Word, 64> dwarfRegs(numRegs);
- for (uint i = 0; i < numRegs; ++i) {
+ for (int i = 0; i < numRegs; ++i) {
dwarfRegs[i] = ui->sample->registerValue(
PerfRegisterInfo::s_perfToDwarf[architecture][abi][i]);
}
// Go one frame up to get the rest of the stack at interworking veneers.
if (ui->isInterworking) {
- dwarfRegs[PerfRegisterInfo::s_dwarfIp[architecture][abi]] =
- dwarfRegs[PerfRegisterInfo::s_dwarfLr[architecture][abi]];
+ dwarfRegs[static_cast<int>(PerfRegisterInfo::s_dwarfIp[architecture][abi])] =
+ dwarfRegs[static_cast<int>(PerfRegisterInfo::s_dwarfLr[architecture][abi])];
}
- uint dummyBegin = PerfRegisterInfo::s_dummyRegisters[architecture][0];
- uint dummyNum = PerfRegisterInfo::s_dummyRegisters[architecture][1] - dummyBegin;
+ int dummyBegin = PerfRegisterInfo::s_dummyRegisters[architecture][0];
+ int dummyNum = PerfRegisterInfo::s_dummyRegisters[architecture][1] - dummyBegin;
if (dummyNum > 0) {
QVarLengthArray<Dwarf_Word, 64> dummyRegs(dummyNum);
- std::memset(dummyRegs.data(), 0, dummyNum * sizeof(Dwarf_Word));
- if (!dwfl_thread_state_registers(thread, dummyBegin, dummyNum, dummyRegs.data()))
+ std::memset(dummyRegs.data(), 0, static_cast<size_t>(dummyNum) * sizeof(Dwarf_Word));
+ if (!dwfl_thread_state_registers(thread, dummyBegin, static_cast<uint>(dummyNum),
+ dummyRegs.data()))
return false;
}
- return dwfl_thread_state_registers(thread, 0, numRegs, dwarfRegs.data());
+ return dwfl_thread_state_registers(thread, 0, static_cast<uint>(numRegs), dwarfRegs.data());
}
static const Dwfl_Thread_Callbacks threadCallbacks = {
@@ -401,10 +406,10 @@ int PerfSymbolTable::parseDie(Dwarf_Die *top, qint32 binaryId, Dwarf_Files *file
location.file = m_unwind->resolveString(file);
location.line
= (dwarf_formudata(dwarf_attr(top, DW_AT_call_line, &attr), &val) == 0)
- ? val : -1;
+ ? static_cast<qint32>(val) : -1;
location.column
= (dwarf_formudata(dwarf_attr(top, DW_AT_call_column, &attr), &val) == 0)
- ? val : -1;
+ ? static_cast<qint32>(val) : -1;
auto it = stack.end();
--it;
diff --git a/app/perfsymboltable.h b/app/perfsymboltable.h
index fa90d93..80394ff 100644
--- a/app/perfsymboltable.h
+++ b/app/perfsymboltable.h
@@ -35,7 +35,7 @@
class PerfSymbolTable
{
public:
- PerfSymbolTable(quint32 pid, Dwfl_Callbacks *callbacks, PerfUnwind *parent);
+ PerfSymbolTable(qint32 pid, Dwfl_Callbacks *callbacks, PerfUnwind *parent);
~PerfSymbolTable();
struct PerfMapSymbol {
@@ -48,7 +48,7 @@ public:
struct DieAndLocation {
Dwarf_Die die;
- int locationId;
+ qint32 locationId;
};
// Announce an mmap. Invalidate the symbol and address cache and clear the dwfl if it overlaps
@@ -100,7 +100,7 @@ private:
PerfElfMap m_elfs;
Dwfl_Callbacks *m_callbacks;
- quint32 m_pid;
+ qint32 m_pid;
QByteArray symbolFromPerfMap(quint64 ip, GElf_Off *offset) const;
int parseDie(Dwarf_Die *top, qint32 binaryId, Dwarf_Files *files, Dwarf_Addr entry,
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
index 4ace26b..7823791 100644
--- a/app/perfunwind.cpp
+++ b/app/perfunwind.cpp
@@ -29,6 +29,8 @@
#include <cstring>
+const qint32 PerfUnwind::s_kernelPid = -1;
+
uint qHash(const PerfUnwind::Location &location, uint seed)
{
QtPrivate::QHashCombine hash;
@@ -85,28 +87,37 @@ static int find_debuginfo(Dwfl_Module *module, void **userData, const char *modu
return symbolTable->findDebugInfo(module, moduleName, base, file, debugLink, crc, debugInfoFilename);
}
+QString PerfUnwind::defaultDebugInfoPath()
+{
+ return QString::fromLatin1("%1usr%1lib%1debug%2%3%1.debug%2.debug")
+ .arg(QDir::separator(), QDir::listSeparator(), QDir::homePath());
+}
+
+QString PerfUnwind::defaultKallsymsPath()
+{
+ return QString::fromLatin1("%1proc%1kallsyms").arg(QDir::separator());
+}
+
PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugPath,
- const QString &extraLibsPath, const QString &appPath,
- const QString &kallsymsPath, bool ignoreKallsymsBuildId,
- bool printStats, uint maxEventBufferSize, int maxFrames) :
+ const QString &extraLibsPath, const QString &appPath, bool printStats) :
m_output(output), m_architecture(PerfRegisterInfo::ARCH_INVALID), m_systemRoot(systemRoot),
m_extraLibsPath(extraLibsPath), m_appPath(appPath), m_debugPath(debugPath),
- m_kallsymsPath(kallsymsPath), m_ignoreKallsymsBuildId(ignoreKallsymsBuildId),
- m_maxEventBufferSize(maxEventBufferSize), m_eventBufferSize(0),
- m_lastFlushMaxTime(0)
+ m_kallsymsPath(QDir::rootPath() + defaultKallsymsPath()), m_ignoreKallsymsBuildId(false),
+ m_maxEventBufferSize(10 * (1 << 20)), m_eventBufferSize(0), m_lastFlushMaxTime(0)
{
m_stats.enabled = printStats;
m_currentUnwind.unwind = this;
- m_currentUnwind.maxFrames = maxFrames;
m_offlineCallbacks.find_elf = dwfl_build_id_find_elf;
m_offlineCallbacks.find_debuginfo = find_debuginfo;
m_offlineCallbacks.section_address = dwfl_offline_section_address;
const QChar separator = QDir::listSeparator();
QByteArray newDebugInfo = (separator + debugPath + separator + appPath + separator
+ extraLibsPath + separator + systemRoot).toUtf8();
- m_debugInfoPath = new char[newDebugInfo.length() + 1];
- m_debugInfoPath[newDebugInfo.length()] = 0;
- std::memcpy(m_debugInfoPath, newDebugInfo.data(), newDebugInfo.length());
+ Q_ASSERT(newDebugInfo.length() >= 0);
+ const uint debugInfoLength = static_cast<uint>(newDebugInfo.length());
+ m_debugInfoPath = new char[debugInfoLength + 1];
+ m_debugInfoPath[debugInfoLength] = 0;
+ std::memcpy(m_debugInfoPath, newDebugInfo.data(), debugInfoLength);
m_offlineCallbacks.debuginfo_path = &m_debugInfoPath;
if (!printStats) {
@@ -146,7 +157,7 @@ PerfUnwind::~PerfUnwind()
}
}
-PerfSymbolTable *PerfUnwind::symbolTable(quint32 pid)
+PerfSymbolTable *PerfUnwind::symbolTable(qint32 pid)
{
PerfSymbolTable *&symbolTable = m_symbolTables[pid];
if (!symbolTable)
@@ -154,7 +165,7 @@ PerfSymbolTable *PerfUnwind::symbolTable(quint32 pid)
return symbolTable;
}
-Dwfl *PerfUnwind::dwfl(quint32 pid)
+Dwfl *PerfUnwind::dwfl(qint32 pid)
{
return symbolTable(pid)->attachDwfl(&m_currentUnwind);
}
@@ -169,7 +180,7 @@ void PerfUnwind::sendBuffer(const QByteArray &buffer)
if (m_stats.enabled)
return;
- quint32 size = qToLittleEndian(buffer.length());
+ qint32 size = qToLittleEndian(buffer.length());
m_output->write(reinterpret_cast<char *>(&size), sizeof(quint32));
m_output->write(buffer);
}
@@ -270,7 +281,7 @@ void PerfUnwind::features(const PerfFeatures &features)
}
}
-Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid)
+Dwfl_Module *PerfUnwind::reportElf(quint64 ip, qint32 pid)
{
auto symbols = symbolTable(pid);
return symbols->reportElf(symbols->findElf(ip));
@@ -278,7 +289,7 @@ Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid)
bool PerfUnwind::ipIsInKernelSpace(quint64 ip) const
{
- auto symbolTableIt = m_symbolTables.constFind(quint32(s_kernelPid));
+ auto symbolTableIt = m_symbolTables.constFind(s_kernelPid);
if (symbolTableIt == m_symbolTables.constEnd())
return false;
@@ -452,8 +463,16 @@ void PerfUnwind::analyze(const PerfRecordSample &sample)
&m_currentUnwind.isInterworking));
}
- const quint8 numGuessedFrames = (m_currentUnwind.firstGuessedFrame == -1)
- ? 0 : m_currentUnwind.frames.length() - m_currentUnwind.firstGuessedFrame;
+
+ quint8 numGuessedFrames = 0;
+ if (m_currentUnwind.firstGuessedFrame != -1) {
+ // Squeeze it into 8 bits.
+ int numGuessed = m_currentUnwind.frames.length() - m_currentUnwind.firstGuessedFrame;
+ Q_ASSERT(numGuessed >= 0);
+ numGuessedFrames
+ = static_cast<quint8>(qMin(static_cast<int>(std::numeric_limits<quint8>::max()),
+ numGuessed));
+ }
QByteArray buffer;
QDataStream(&buffer, QIODevice::WriteOnly)
<< static_cast<quint8>(Sample) << sample.pid()
@@ -611,7 +630,7 @@ void PerfUnwind::finishedRound()
}
template<typename Event>
-void PerfUnwind::bufferEvent(const Event &event, QList<Event> *buffer, int *eventCounter)
+void PerfUnwind::bufferEvent(const Event &event, QList<Event> *buffer, uint *eventCounter)
{
buffer->append(event);
m_eventBufferSize += event.size();
@@ -696,10 +715,14 @@ void PerfUnwind::flushEventBuffer(uint desiredBufferSize)
if (m_stats.enabled) {
++m_stats.numBufferFlushes;
- const int samples = std::distance(m_sampleBuffer.begin(), sampleIt);
- m_stats.maxSamplesPerFlush = std::max(samples, m_stats.maxSamplesPerFlush);
- const int mmaps = std::distance(m_mmapBuffer.begin(), mmapIt);
- m_stats.maxMmapsPerFlush = std::max(mmaps, m_stats.maxMmapsPerFlush);
+ const auto samples = std::distance(m_sampleBuffer.begin(), sampleIt);
+ Q_ASSERT(samples >= 0 && samples < std::numeric_limits<uint>::max());
+ m_stats.maxSamplesPerFlush = std::max(static_cast<uint>(samples),
+ m_stats.maxSamplesPerFlush);
+ const auto mmaps = std::distance(m_mmapBuffer.begin(), mmapIt);
+ Q_ASSERT(mmaps >= 0 && mmaps < std::numeric_limits<uint>::max());
+ m_stats.maxMmapsPerFlush = std::max(static_cast<uint>(mmaps),
+ m_stats.maxMmapsPerFlush);
}
m_sampleBuffer.erase(m_sampleBuffer.begin(), sampleIt);
diff --git a/app/perfunwind.h b/app/perfunwind.h
index e259676..7f4eae3 100644
--- a/app/perfunwind.h
+++ b/app/perfunwind.h
@@ -33,6 +33,7 @@
#include <QIODevice>
#include <QByteArray>
#include <QString>
+#include <QDir>
#include <limits>
@@ -97,15 +98,67 @@ public:
bool isInterworking;
};
- static const quint32 s_kernelPid = std::numeric_limits<quint32>::max();
+ struct Stats
+ {
+ Stats()
+ : numSamples(0), numMmaps(0), numRounds(0), numBufferFlushes(0),
+ numTimeViolatingSamples(0), numTimeViolatingMmaps(0),
+ numSamplesInRound(0), numMmapsInRound(0),
+ maxSamplesPerRound(0), maxMmapsPerRound(0),
+ maxSamplesPerFlush(0), maxMmapsPerFlush(0),
+ maxBufferSize(0), maxTotalEventSizePerRound(0),
+ maxTime(0), maxTimeBetweenRounds(0), maxReorderTime(0),
+ lastRoundTime(0), totalEventSizePerRound(0),
+ enabled(false)
+ {}
- PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugInfo,
- const QString &extraLibs, const QString &appPath,
- const QString &kallsymsPath, bool ignoreKallsymsBuildId,
- bool printStats, uint maxEventBufferSize,
- int maxFrames);
+ void addEventTime(quint64 time);
+ void finishedRound();
+
+ quint64 numSamples;
+ quint64 numMmaps;
+ quint64 numRounds;
+ quint64 numBufferFlushes;
+ quint64 numTimeViolatingSamples;
+ quint64 numTimeViolatingMmaps;
+ uint numSamplesInRound;
+ uint numMmapsInRound;
+ uint maxSamplesPerRound;
+ uint maxMmapsPerRound;
+ uint maxSamplesPerFlush;
+ uint maxMmapsPerFlush;
+ uint maxBufferSize;
+ uint maxTotalEventSizePerRound;
+ quint64 maxTime;
+ quint64 maxTimeBetweenRounds;
+ quint64 maxReorderTime;
+ quint64 lastRoundTime;
+ uint totalEventSizePerRound;
+ bool enabled;
+ };
+
+ static const qint32 s_kernelPid;
+ static QString defaultDebugInfoPath();
+ static QString defaultKallsymsPath();
+
+ PerfUnwind(QIODevice *output, const QString &systemRoot = QDir::rootPath(),
+ const QString &debugPath = defaultDebugInfoPath(),
+ const QString &extraLibs = QString(), const QString &appPath = QString(),
+ bool printStats = false);
~PerfUnwind();
+ QString kallsymsPath() const { return m_kallsymsPath; }
+ void setKallsymsPath(const QString &kallsymsPath) { m_kallsymsPath = kallsymsPath; }
+
+ bool ignoreKallsymsBuildId() const { return m_ignoreKallsymsBuildId; }
+ void setIgnoreKallsymsBuildId(bool ignore) { m_ignoreKallsymsBuildId = ignore; }
+
+ uint maxEventBufferSize() const { return m_maxEventBufferSize; }
+ void setMaxEventBufferSize(uint size) { m_maxEventBufferSize = size; }
+
+ int maxUnwindFrames() const { return m_currentUnwind.maxFrames; }
+ void setMaxUnwindFrames(int maxUnwindFrames) { m_currentUnwind.maxFrames = maxUnwindFrames; }
+
PerfRegisterInfo::Architecture architecture() const { return m_architecture; }
void setArchitecture(PerfRegisterInfo::Architecture architecture)
{
@@ -119,14 +172,14 @@ public:
void features(const PerfFeatures &features);
void finishedRound();
- Dwfl_Module *reportElf(quint64 ip, quint32 pid);
+ Dwfl_Module *reportElf(quint64 ip, qint32 pid);
bool ipIsInKernelSpace(quint64 ip) const;
void sample(const PerfRecordSample &sample);
void fork(const PerfRecordFork &sample);
void exit(const PerfRecordExit &sample);
- PerfSymbolTable *symbolTable(quint32 pid);
- Dwfl *dwfl(quint32 pid);
+ PerfSymbolTable *symbolTable(qint32 pid);
+ Dwfl *dwfl(qint32 pid);
qint32 resolveString(const QByteArray &string);
@@ -155,19 +208,22 @@ public:
QString extraLibsPath() const { return m_extraLibsPath; }
QString appPath() const { return m_appPath; }
QString debugPath() const { return m_debugPath; }
+ Stats stats() const { return m_stats; }
+
+ void flushEventBuffer() { flushEventBuffer(0); }
private:
enum CallchainContext {
- PERF_CONTEXT_HV = (quint64)-32,
- PERF_CONTEXT_KERNEL = (quint64)-128,
- PERF_CONTEXT_USER = (quint64)-512,
+ PERF_CONTEXT_HV = static_cast<quint64>(-32),
+ PERF_CONTEXT_KERNEL = static_cast<quint64>(-128),
+ PERF_CONTEXT_USER = static_cast<quint64>(-512),
- PERF_CONTEXT_GUEST = (quint64)-2048,
- PERF_CONTEXT_GUEST_KERNEL = (quint64)-2176,
- PERF_CONTEXT_GUEST_USER = (quint64)-2560,
+ PERF_CONTEXT_GUEST = static_cast<quint64>(-2048),
+ PERF_CONTEXT_GUEST_KERNEL = static_cast<quint64>(-2176),
+ PERF_CONTEXT_GUEST_USER = static_cast<quint64>(-2560),
- PERF_CONTEXT_MAX = (quint64)-4095,
+ PERF_CONTEXT_MAX = static_cast<quint64>(-4095),
};
UnwindInfo m_currentUnwind;
@@ -199,7 +255,7 @@ private:
QList<PerfRecordSample> m_sampleBuffer;
QList<PerfRecordMmap> m_mmapBuffer;
- QHash<quint32, PerfSymbolTable *> m_symbolTables;
+ QHash<qint32, PerfSymbolTable *> m_symbolTables;
PerfKallsyms m_kallsyms;
QHash<QByteArray, qint32> m_strings;
@@ -213,44 +269,6 @@ private:
uint m_eventBufferSize;
quint64 m_lastFlushMaxTime;
- struct Stats
- {
- Stats()
- : numSamples(0), numMmaps(0), numRounds(0), numBufferFlushes(0),
- numTimeViolatingSamples(0), numTimeViolatingMmaps(0),
- numSamplesInRound(0), numMmapsInRound(0),
- maxSamplesPerRound(0), maxMmapsPerRound(0),
- maxSamplesPerFlush(0), maxMmapsPerFlush(0),
- maxBufferSize(0), maxTotalEventSizePerRound(0),
- maxTime(0), maxTimeBetweenRounds(0), maxReorderTime(0),
- lastRoundTime(0), totalEventSizePerRound(0),
- enabled(false)
- {}
-
- void addEventTime(quint64 time);
- void finishedRound();
-
- quint64 numSamples;
- quint64 numMmaps;
- quint64 numRounds;
- quint64 numBufferFlushes;
- quint64 numTimeViolatingSamples;
- quint64 numTimeViolatingMmaps;
- int numSamplesInRound;
- int numMmapsInRound;
- int maxSamplesPerRound;
- int maxMmapsPerRound;
- int maxSamplesPerFlush;
- int maxMmapsPerFlush;
- uint maxBufferSize;
- uint maxTotalEventSizePerRound;
- quint64 maxTime;
- quint64 maxTimeBetweenRounds;
- quint64 maxReorderTime;
- quint64 lastRoundTime;
- uint totalEventSizePerRound;
- bool enabled;
- };
Stats m_stats;
void unwindStack(Dwfl *dwfl);
@@ -263,7 +281,7 @@ private:
void sendAttributes(qint32 id, const PerfEventAttributes &attributes, const QByteArray &name);
template<typename Event>
- void bufferEvent(const Event &event, QList<Event> *buffer, int *eventCounter);
+ void bufferEvent(const Event &event, QList<Event> *buffer, uint *eventCounter);
void flushEventBuffer(uint desiredBufferSize);
};
diff --git a/elfutils.pri b/elfutils.pri
new file mode 100644
index 0000000..37f2e0c
--- /dev/null
+++ b/elfutils.pri
@@ -0,0 +1,17 @@
+!isEmpty(ELFUTILS_INSTALL_DIR) {
+ INCLUDEPATH += $$ELFUTILS_INSTALL_DIR/include $$ELFUTILS_INSTALL_DIR/include/elfutils
+ LIBS += -L$$ELFUTILS_INSTALL_DIR/lib
+} else:unix {
+ INCLUDEPATH += /usr/include/elfutils
+}
+
+LIBS += -ldw -lelf
+
+win32 {
+ LIBS += -leu_compat
+}
+
+linux-g++*:!isEmpty(PERFPARSER_ELFUTILS_INSTALLDIR) {
+ RPATH = $$relative_path($$PERFPARSER_ELFUTILS_INSTALLDIR, $$PERFPARSER_APP_INSTALLDIR)
+ QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/$$RPATH\'
+}
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index a5dcc9b..bd5fb62 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,4 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = \
elfmap \
- kallsyms
+ kallsyms \
+ perfdata
diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs
index a815e4f..08ea2fe 100644
--- a/tests/auto/auto.qbs
+++ b/tests/auto/auto.qbs
@@ -4,6 +4,6 @@ Project {
name: "PerfParserAutotests"
condition: project.withAutotests
references: [
- "elfmap", "kallsyms"
+ "elfmap", "kallsyms", "perfdata"
]
}
diff --git a/tests/auto/perfdata/perfdata.pro b/tests/auto/perfdata/perfdata.pro
new file mode 100644
index 0000000..689a387
--- /dev/null
+++ b/tests/auto/perfdata/perfdata.pro
@@ -0,0 +1,38 @@
+include(../../../elfutils.pri)
+
+QT += testlib
+QT -= gui
+
+CONFIG += testcase strict_flags warn_on
+
+INCLUDEPATH += ../../../app
+
+TARGET = tst_perfdata
+
+SOURCES += \
+ tst_perfdata.cpp \
+ ../../../app/perfattributes.cpp \
+ ../../../app/perfdata.cpp \
+ ../../../app/perfelfmap.cpp \
+ ../../../app/perffeatures.cpp \
+ ../../../app/perffilesection.cpp \
+ ../../../app/perfheader.cpp \
+ ../../../app/perfkallsyms.cpp \
+ ../../../app/perfregisterinfo.cpp \
+ ../../../app/perfsymboltable.cpp \
+ ../../../app/perfunwind.cpp
+
+HEADERS += \
+ ../../../app/perfattributes.h \
+ ../../../app/perfdata.h \
+ ../../../app/perfelfmap.h \
+ ../../../app/perffeatures.h \
+ ../../../app/perffilesection.h \
+ ../../../app/perfheader.h \
+ ../../../app/perfkallsyms.h \
+ ../../../app/perfregisterinfo.h \
+ ../../../app/perfsymboltable.h \
+ ../../../app/perfunwind.h
+
+RESOURCES += \
+ perfdata.qrc
diff --git a/tests/auto/perfdata/perfdata.qbs b/tests/auto/perfdata/perfdata.qbs
new file mode 100644
index 0000000..8a9eba6
--- /dev/null
+++ b/tests/auto/perfdata/perfdata.qbs
@@ -0,0 +1,33 @@
+import qbs
+
+QtcAutotest {
+ name: "PerfData Autotest"
+
+ cpp.includePaths: ["/usr/include/elfutils", "../../../app"]
+ cpp.dynamicLibraries: ["dw", "elf"]
+
+ files: [
+ "perfdata.qrc",
+ "tst_perfdata.cpp",
+ "../../../app/perfattributes.cpp",
+ "../../../app/perfattributes.h",
+ "../../../app/perfdata.cpp",
+ "../../../app/perfdata.h",
+ "../../../app/perfelfmap.cpp",
+ "../../../app/perfelfmap.h",
+ "../../../app/perffeatures.cpp",
+ "../../../app/perffeatures.h",
+ "../../../app/perffilesection.cpp",
+ "../../../app/perffilesection.h",
+ "../../../app/perfheader.cpp",
+ "../../../app/perfheader.h",
+ "../../../app/perfkallsyms.cpp",
+ "../../../app/perfkallsyms.h",
+ "../../../app/perfregisterinfo.cpp",
+ "../../../app/perfregisterinfo.h",
+ "../../../app/perfsymboltable.cpp",
+ "../../../app/perfsymboltable.h",
+ "../../../app/perfunwind.cpp",
+ "../../../app/perfunwind.h"
+ ]
+}
diff --git a/tests/auto/perfdata/perfdata.qrc b/tests/auto/perfdata/perfdata.qrc
new file mode 100644
index 0000000..1b5d91a
--- /dev/null
+++ b/tests/auto/perfdata/perfdata.qrc
@@ -0,0 +1,5 @@
+<RCC>
+ <qresource prefix="/">
+ <file>probe.data.stream</file>
+ </qresource>
+</RCC>
diff --git a/tests/auto/perfdata/probe.data.stream b/tests/auto/perfdata/probe.data.stream
new file mode 100644
index 0000000..b8de1ee
--- /dev/null
+++ b/tests/auto/perfdata/probe.data.stream
Binary files differ
diff --git a/tests/auto/perfdata/tst_perfdata.cpp b/tests/auto/perfdata/tst_perfdata.cpp
new file mode 100644
index 0000000..820dae9
--- /dev/null
+++ b/tests/auto/perfdata/tst_perfdata.cpp
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Enterprise Perf Profiler Add-on.
+**
+** GNU General Public License Usage
+** This file may be used under the terms of the GNU General Public License
+** version 3 as published by the Free Software Foundation and appearing in
+** the file LICENSE.GPLv3 included in the packaging of this file. Please
+** review the following information to ensure the GNU General Public License
+** requirements will be met: https://www.gnu.org/licenses/gpl.html.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+#include <QObject>
+#include <QTest>
+#include <QBuffer>
+#include <QSignalSpy>
+#include <QDebug>
+
+#include "perfdata.h"
+#include "perfunwind.h"
+
+class TestPerfData : public QObject
+{
+ Q_OBJECT
+private slots:
+ void testTraceDataHeaderEvent();
+};
+
+void TestPerfData::testTraceDataHeaderEvent()
+{
+ QBuffer output;
+ QFile input(":/probe.data.stream");
+ QVERIFY(input.open(QIODevice::ReadOnly));
+ QVERIFY(output.open(QIODevice::WriteOnly));
+
+ PerfUnwind unwind(&output, QDir::rootPath(), PerfUnwind::defaultDebugInfoPath(), QString(),
+ QString(), true);
+ PerfHeader header(&input);
+ PerfAttributes attributes;
+ PerfData data(&input, &unwind, &header, &attributes);
+
+ QSignalSpy spy(&data, SIGNAL(finished()));
+ connect(&header, &PerfHeader::finished, &data, &PerfData::read);
+ header.read();
+ QCOMPARE(spy.count(), 1);
+
+ unwind.flushEventBuffer();
+
+ const PerfUnwind::Stats stats = unwind.stats();
+ QCOMPARE(stats.numSamples, 1u);
+ QCOMPARE(stats.numMmaps, 120u);
+ QCOMPARE(stats.numRounds, 1u);
+ QCOMPARE(stats.numBufferFlushes, 1u);
+ QCOMPARE(stats.numTimeViolatingSamples, 0u);
+ QCOMPARE(stats.numTimeViolatingMmaps, 0u);
+ QCOMPARE(stats.maxBufferSize, 15488u);
+ QCOMPARE(stats.maxTime, 13780586522722ull);
+}
+
+QTEST_GUILESS_MAIN(TestPerfData)
+
+#include "tst_perfdata.moc"