summaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-02-23 15:34:11 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-03-04 11:18:30 +0200
commitcf23d7740f54b8c53409e70eea244d984a27e537 (patch)
tree5b271f50c2a77822c8aee5cb05520f9ddbe5073a /app
parent7b6a8e42b5428038614faa2f44fc252f4bfd5f0a (diff)
Add elfutils to source tree and integrate build system
We only import the relevant parts of elfutils, leaving out the original build system, support for bzip2 and lzma compression, source code of the elfutils command line utilities, tests, and libcpu. Change-Id: I7c15cff880a7ab6d84e272c8b18f854f97539a1f Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'app')
-rw-r--r--app/app.pro52
-rw-r--r--app/main.cpp249
-rw-r--r--app/perfattributes.cpp155
-rw-r--r--app/perfattributes.h190
-rw-r--r--app/perfdata.cpp554
-rw-r--r--app/perfdata.h474
-rw-r--r--app/perffeatures.cpp298
-rw-r--r--app/perffeatures.h180
-rw-r--r--app/perffilesection.cpp29
-rw-r--r--app/perffilesection.h34
-rw-r--r--app/perfheader.cpp110
-rw-r--r--app/perfheader.h100
-rw-r--r--app/perfregisterinfo.cpp64
-rw-r--r--app/perfregisterinfo.h56
-rw-r--r--app/perfstdin.cpp63
-rw-r--r--app/perfstdin.h38
-rw-r--r--app/perfunwind.cpp366
-rw-r--r--app/perfunwind.h132
18 files changed, 3144 insertions, 0 deletions
diff --git a/app/app.pro b/app/app.pro
new file mode 100644
index 0000000..36bf0fe
--- /dev/null
+++ b/app/app.pro
@@ -0,0 +1,52 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2014-08-14T10:44:20
+#
+#-------------------------------------------------
+
+QT += core network
+
+QT -= gui
+
+QMAKE_LFLAGS += -Wl,-rpath,\'\$\$ORIGIN/elfutils/backends\'
+LIBS += -Wl,--start-group \
+ ../3rdparty/elfutils/libdw.a \
+ ../3rdparty/elfutils/libdwfl.a \
+ ../3rdparty/elfutils/libelf.a \
+ ../3rdparty/elfutils/libelf32.a \
+ ../3rdparty/elfutils/libelf64.a \
+ ../3rdparty/elfutils/libebl.a \
+ ../3rdparty/elfutils/libdwelf.a \
+ -Wl,--end-group \
+ -Wl,-Bstatic -lbfd -Wl,-Bdynamic -lz -ldl -liberty
+
+TARGET = ../perfparser
+CONFIG += console c++11
+CONFIG -= app_bundle
+
+TEMPLATE = app
+
+SOURCES += main.cpp \
+ perfattributes.cpp \
+ perfheader.cpp \
+ perffilesection.cpp \
+ perffeatures.cpp \
+ perfdata.cpp \
+ perfunwind.cpp \
+ perfregisterinfo.cpp \
+ perfstdin.cpp
+
+HEADERS += \
+ perfattributes.h \
+ perfheader.h \
+ perffilesection.h \
+ perffeatures.h \
+ perfdata.h \
+ perfunwind.h \
+ perfregisterinfo.h \
+ perfstdin.h
+
+include(../3rdparty/elfutils/elfutils.pri)
+include(../3rdparty/elfutils/libdwfl/dwflheaders.pri)
+include(../3rdparty/elfutils/libebl/eblheaders.pri)
+include(../3rdparty/elfutils/libdwelf/dwelfheaders.pri)
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644
index 0000000..7a8310e
--- /dev/null
+++ b/app/main.cpp
@@ -0,0 +1,249 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfheader.h"
+#include "perfattributes.h"
+#include "perffeatures.h"
+#include "perfdata.h"
+#include "perfunwind.h"
+#include "perfregisterinfo.h"
+#include "perfstdin.h"
+
+#include <QFile>
+#include <QDebug>
+#include <QtEndian>
+#include <QCoreApplication>
+#include <QCommandLineParser>
+#include <QPointer>
+#include <QAbstractSocket>
+#include <QTcpSocket>
+#include <limits>
+
+enum ErrorCodes {
+ NoError,
+ TcpSocketError,
+ CannotOpen,
+ BadMagic,
+ HeaderError,
+ DataError,
+ MissingData,
+};
+
+class PerfTcpSocket : public QTcpSocket {
+ Q_OBJECT
+public:
+ PerfTcpSocket(QCoreApplication *app);
+
+public slots:
+ void readingFinished();
+ void processError(QAbstractSocket::SocketError error);
+
+private:
+ bool reading;
+};
+
+int main(int argc, char *argv[])
+{
+ int exitCode = -1;
+
+ QCoreApplication app(argc, argv);
+ app.setApplicationName(QLatin1String("perfparser"));
+ app.setApplicationVersion(QLatin1String("1.0"));
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription(QLatin1String("Perf data parser and unwinder."));
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ QCommandLineOption input(QLatin1String("input"),
+ QCoreApplication::translate(
+ "main", "Read perf data from <file> instead of stdin."),
+ QLatin1String("file"));
+ parser.addOption(input);
+
+ QCommandLineOption host(QLatin1String("host"),
+ QCoreApplication::translate(
+ "main", "Read perf data from remote <host> instead of stdin."),
+ QLatin1String("host"));
+ parser.addOption(host);
+
+ QCommandLineOption port(QLatin1String("port"),
+ QCoreApplication::translate(
+ "main", "When reading from remote host, connect to <port> (default: "
+ "9327)."),
+ QLatin1String("port"),
+ QLatin1String("9327"));
+ parser.addOption(port);
+
+ QCommandLineOption output(QLatin1String("output"),
+ QCoreApplication::translate(
+ "main", "Write b2qt data to <file> instead of stdout."),
+ QLatin1String("file"));
+ parser.addOption(output);
+
+ QCommandLineOption sysroot(QLatin1String("sysroot"),
+ QCoreApplication::translate(
+ "main", "Look for system libraries in <path> (default: /)."),
+ QLatin1String("path"),
+ QLatin1String("/"));
+ parser.addOption(sysroot);
+
+ QCommandLineOption debug(QLatin1String("debug"),
+ QCoreApplication::translate(
+ "main",
+ "Look for debug information in <path>. "
+ "You can specify multiple paths separated by ':'. "
+ "Relative paths are relative to the original file's path. "
+ "The default is: <sysroot>/usr/lib/debug:.debug/ ."),
+ QLatin1String("path"),
+ QLatin1String("/usr/lib/debug:.debug/"));
+ parser.addOption(debug);
+
+ QCommandLineOption extra(QLatin1String("extra"),
+ QCoreApplication::translate(
+ "main", "Look for additional libraries in <path> (default: .)."),
+ QLatin1String("path"),
+ QLatin1String("."));
+ parser.addOption(extra);
+
+ QCommandLineOption appPath(QLatin1String("app"),
+ QCoreApplication::translate(
+ "main", "Look for application binary in <path> (default: .)."),
+ QLatin1String("path"),
+ QLatin1String("."));
+ parser.addOption(appPath);
+
+ QCommandLineOption arch(QLatin1String("arch"),
+ QCoreApplication::translate(
+ "main",
+ "Set the fallback architecture, in case the architecture is not "
+ "given in the data itself, to <arch>."),
+ QLatin1String("arch"));
+ parser.addOption(arch);
+
+ parser.process(app);
+
+ QPointer<QFile> outfile;
+ if (parser.isSet(output)) {
+ outfile = new QFile(parser.value(output));
+ if (!outfile->open(QIODevice::WriteOnly))
+ return CannotOpen;
+ } else {
+ outfile = new QFile;
+ if (!outfile->open(stdout, QIODevice::WriteOnly))
+ return CannotOpen;
+ }
+
+ QPointer<QIODevice> infile;
+ if (parser.isSet(host)) {
+ PerfTcpSocket *socket = new PerfTcpSocket(&app);
+ infile = socket;
+ } else {
+ if (parser.isSet(input))
+ infile = new QFile(parser.value(input));
+ else
+ infile = new PerfStdin;
+ }
+
+ PerfUnwind unwind(outfile, parser.value(sysroot), parser.isSet(debug) ?
+ parser.value(debug) : parser.value(sysroot) + parser.value(debug),
+ parser.value(extra), parser.value(appPath));
+ PerfHeader header(infile);
+ PerfAttributes attributes;
+ PerfFeatures features;
+ PerfData data(infile, &unwind, &header, &attributes);
+
+ if (parser.isSet(arch))
+ features.setArchitecture(parser.value(arch).toLatin1());
+
+ QObject::connect(&header, &PerfHeader::finished, [&]() {
+ if (!header.isPipe()) {
+ attributes.read(infile, &header);
+ features.read(infile, &header);
+ }
+
+ const QByteArray &featureArch = features.architecture();
+ for (uint i = 0; i < PerfRegisterInfo::ARCH_INVALID; ++i) {
+ if (featureArch.startsWith(PerfRegisterInfo::s_archNames[i])) {
+ unwind.setArchitecture(static_cast<PerfRegisterInfo::Architecture>(i));
+ break;
+ }
+ }
+
+ if (unwind.architecture() == PerfRegisterInfo::ARCH_INVALID) {
+ qWarning() << "No information about CPU architecture found. Cannot unwind.";
+ app.exit(MissingData);
+ }
+
+ QObject::connect(infile.data(), &QIODevice::readyRead, &data, &PerfData::read);
+ if (infile->bytesAvailable() > 0)
+ data.read();
+ });
+
+ QObject::connect(&header, &PerfHeader::error, [&]() {
+ app.exit(HeaderError);
+ });
+
+ QObject::connect(&data, &PerfData::finished, [&]() {
+ exitCode = NoError;
+ app.exit(NoError);
+ });
+
+ QObject::connect(&data, &PerfData::error, [&]() {
+ app.exit(DataError);
+ });
+
+ if (parser.isSet(host)) {
+ PerfTcpSocket *socket = static_cast<PerfTcpSocket *>(infile.data());
+ QObject::connect(socket, &QTcpSocket::disconnected, &data, &PerfData::finishReading);
+ QObject::connect(&data, &PerfData::finished, socket, &PerfTcpSocket::readingFinished);
+ socket->connectToHost(parser.value(host), parser.value(port).toUShort(),
+ QIODevice::ReadOnly);
+ } else {
+ if (!infile->open(QIODevice::ReadOnly))
+ return CannotOpen;
+ header.read();
+ }
+
+ return exitCode == -1 ? app.exec() : NoError;
+}
+
+
+void PerfTcpSocket::processError(QAbstractSocket::SocketError error)
+{
+ if (reading) {
+ qWarning() << "socket error" << error << errorString();
+ QCoreApplication::instance()->exit(TcpSocketError);
+ } // Otherwise ignore the error. We don't need the socket anymore
+}
+
+
+PerfTcpSocket::PerfTcpSocket(QCoreApplication *app) : QTcpSocket(app), reading(true)
+{
+ connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(processError(QAbstractSocket::SocketError)));
+}
+
+void PerfTcpSocket::readingFinished()
+{
+ reading = false;
+}
+
+#include "main.moc"
diff --git a/app/perfattributes.cpp b/app/perfattributes.cpp
new file mode 100644
index 0000000..94d59fc
--- /dev/null
+++ b/app/perfattributes.cpp
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfattributes.h"
+#include "perfdata.h"
+#include <QDebug>
+
+PerfEventAttributes::PerfEventAttributes()
+{
+ memset(this, 0, sizeof(PerfEventAttributes));
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs)
+{
+ quint64 flags;
+ stream >> attrs.m_type >> attrs.m_size;
+
+ if (attrs.m_size < sizeof(PerfEventAttributes)) {
+ qWarning() << "unsupported file format";
+ return stream;
+ }
+
+ stream >> attrs.m_config >> attrs.m_samplePeriod >> attrs.m_sampleType >> attrs.m_readFormat
+ >> flags >> attrs.m_wakeupEvents >> attrs.m_bpType >> attrs.m_bpAddr >> attrs.m_bpLen
+ >> attrs.m_branchSampleType >> attrs.m_sampleRegsUser >> attrs.m_sampleStackUser
+ >> attrs.m_reserved2;
+
+ if (static_cast<QSysInfo::Endian>(stream.byteOrder()) != QSysInfo::ByteOrder) {
+ // bit fields are saved in byte order; who came up with that BS?
+ quint64 newFlags = 0;
+ for (int i = 0; i < 64; ++i) {
+ if ((flags & (1ull << i)) != 0)
+ newFlags |= (1ull << (i / 8 + 7 - (i % 8)));
+ }
+ flags = newFlags;
+ }
+
+ *(&attrs.m_readFormat + 1) = flags;
+
+ stream.skipRawData(attrs.m_size - sizeof(PerfEventAttributes));
+
+ return stream;
+}
+
+int PerfEventAttributes::sampleIdOffset() const
+{
+ int offset = 0;
+
+ if (m_sampleType & SAMPLE_IDENTIFIER)
+ return 0;
+
+ if (!(m_sampleType & SAMPLE_ID))
+ return -1;
+
+ if (m_sampleType & SAMPLE_IP)
+ offset += sizeof(quint64); // PerfRecordSample::m_ip
+
+ if (m_sampleType & SAMPLE_TID)
+ offset += sizeof(quint32) + sizeof(quint32); // PerfRecordSampleId::{m_pid|m_tid}
+
+ if (m_sampleType & SAMPLE_TIME)
+ offset += sizeof(quint64); // PerfSampleId::m_time
+
+ if (m_sampleType & SAMPLE_ADDR)
+ offset += sizeof(quint64); // PerfRecordSample::m_addr
+
+ return offset;
+}
+
+
+
+
+bool PerfAttributes::read(QIODevice *device, PerfHeader *header)
+{
+ if (header->attrSize() < sizeof(PerfEventAttributes) + sizeof(PerfFileSection)) {
+ qWarning() << "unsupported file format";
+ return false;
+ }
+
+ PerfEventAttributes attrs;
+ PerfFileSection ids;
+
+ for (uint i = 0; i < header->numAttrs(); ++i) {
+
+ if (!device->seek(header->attrs().offset + header->attrSize() * i)) {
+ qWarning() << "cannot seek to attribute section" << i
+ << header->attrs().offset + header->attrSize() * i;
+ return false;
+ }
+
+ QDataStream stream(device);
+ stream.setByteOrder(header->byteOrder());
+ stream >> attrs;
+
+ if (attrs.size() < sizeof(PerfEventAttributes))
+ return false;
+
+ if (i == 0)
+ m_globalAttributes = attrs;
+
+ stream >> ids;
+ if (ids.size > 0) {
+ if (!device->seek(ids.offset)) {
+ qWarning() << "cannot seek to attribute ID section";
+ return false;
+ }
+
+ QDataStream idStream(device);
+ stream.setByteOrder(header->byteOrder());
+ quint64 id;
+ for (uint i = 0; i < ids.size / sizeof(quint64); ++i) {
+ idStream >> id;
+ m_attributes[id] = attrs;
+ }
+ }
+
+ }
+ return true;
+}
+
+void PerfAttributes::setGlobalAttributes(const PerfEventAttributes &attributes)
+{
+ m_globalAttributes = attributes;
+}
+
+void PerfAttributes::addAttributes(quint64 id, const PerfEventAttributes &attributes)
+{
+ m_attributes[id] = attributes;
+}
+
+const PerfEventAttributes &PerfAttributes::attributes(quint64 id) const
+{
+ QHash<quint64, PerfEventAttributes>::ConstIterator i = m_attributes.find(id);
+ if (i != m_attributes.end())
+ return i.value();
+ else
+ return m_globalAttributes;
+}
diff --git a/app/perfattributes.h b/app/perfattributes.h
new file mode 100644
index 0000000..f0c244e
--- /dev/null
+++ b/app/perfattributes.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFATTRIBUTES_H
+#define PERFATTRIBUTES_H
+
+#include "perffilesection.h"
+#include "perfheader.h"
+
+#include <QIODevice>
+#include <QDataStream>
+#include <QHash>
+
+class PerfEventAttributes {
+public:
+ PerfEventAttributes();
+
+ bool sampleIdAll() const { return m_sampleIdAll; }
+ quint64 sampleType() const { return m_sampleType; }
+ quint64 readFormat() const { return m_readFormat; }
+ quint64 sampleRegsUser() const { return m_sampleRegsUser; }
+ quint32 size() const { return m_size; }
+ int sampleIdOffset() const;
+
+ enum ReadFormat {
+ FORMAT_TOTAL_TIME_ENABLED = 1U << 0,
+ FORMAT_TOTAL_TIME_RUNNING = 1U << 1,
+ FORMAT_ID = 1U << 2,
+ FORMAT_GROUP = 1U << 3,
+
+ FORMAT_MAX = 1U << 4
+ };
+
+ /*
+ * Bits that can be set in sampleType to request information
+ * in the overflow packets.
+ */
+ enum SampleFormat {
+ SAMPLE_IP = 1U << 0,
+ SAMPLE_TID = 1U << 1,
+ SAMPLE_TIME = 1U << 2,
+ SAMPLE_ADDR = 1U << 3,
+ SAMPLE_READ = 1U << 4,
+ SAMPLE_CALLCHAIN = 1U << 5,
+ SAMPLE_ID = 1U << 6,
+ SAMPLE_CPU = 1U << 7,
+ SAMPLE_PERIOD = 1U << 8,
+ SAMPLE_STREAM_ID = 1U << 9,
+ SAMPLE_RAW = 1U << 10,
+ SAMPLE_BRANCH_STACK = 1U << 11,
+ SAMPLE_REGS_USER = 1U << 12,
+ SAMPLE_STACK_USER = 1U << 13,
+ SAMPLE_WEIGHT = 1U << 14,
+ SAMPLE_DATA_SRC = 1U << 15,
+ SAMPLE_IDENTIFIER = 1U << 16,
+ SAMPLE_TRANSACTION = 1U << 17,
+
+ SAMPLE_MAX = 1U << 18,
+ SAMPLE_ID_ALL = 1U << 31 // extra flag, to check if the sample has a sample ID at all
+ };
+
+private:
+
+ /*
+ * Major type: hardware/software/tracepoint/etc.
+ */
+ quint32 m_type;
+
+ /*
+ * Size of the attr structure, for fwd/bwd compat.
+ */
+ quint32 m_size;
+
+ /*
+ * Type specific configuration information.
+ */
+ quint64 m_config;
+
+ union {
+ quint64 m_samplePeriod;
+ quint64 m_sampleFreq;
+ };
+
+ quint64 m_sampleType;
+ quint64 m_readFormat;
+
+ quint64 m_disabled : 1, /* off by default */
+ m_inherit : 1, /* children inherit it */
+ m_pinned : 1, /* must always be on PMU */
+ m_exclusive : 1, /* only group on PMU */
+ m_excludeUser : 1, /* don't count user */
+ m_excludeKernel : 1, /* ditto kernel */
+ m_excludeHv : 1, /* ditto hypervisor */
+ m_excludeIdle : 1, /* don't count when idle */
+ m_mmap : 1, /* include mmap data */
+ m_comm : 1, /* include comm data */
+ m_freq : 1, /* use freq, not period */
+ m_inheritStat : 1, /* per task counts */
+ m_enableOnExec : 1, /* next exec enables */
+ m_task : 1, /* trace fork/exit */
+ m_watermark : 1, /* wakeup_watermark */
+ /*
+ * m_preciseIp:
+ *
+ * 0 - SAMPLE_IP can have arbitrary skid
+ * 1 - SAMPLE_IP must have constant skid
+ * 2 - SAMPLE_IP requested to have 0 skid
+ * 3 - SAMPLE_IP must have 0 skid
+ *
+ * See also PERF_RECORD_MISC_EXACT_IP
+ */
+ m_preciseIp : 2, /* skid constraint */
+ m_mmapData : 1, /* non-exec mmap data */
+ m_sampleIdAll : 1, /* sample_type all events */
+
+ m_excludeHost : 1, /* don't count in host */
+ m_excludeGuest : 1, /* don't count in guest */
+
+ m_reserved1 : 43;
+
+ union {
+ quint32 m_wakeupEvents; /* wakeup every n events */
+ quint32 m_wakeupWatermark; /* bytes before wakeup */
+ };
+
+ quint32 m_bpType;
+ union {
+ quint64 m_bpAddr;
+ quint64 m_config1; /* extension of config */
+ };
+
+ union {
+ quint64 m_bpLen;
+ quint64 m_config2; /* extension of config1 */
+ };
+
+ quint64 m_branchSampleType; /* enum perf_branch_sample_type */
+
+ /*
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ quint64 m_sampleRegsUser;
+
+ /*
+ * Defines size of the user stack to dump on samples.
+ */
+ quint32 m_sampleStackUser;
+
+ /* Align to u64. */
+ quint32 m_reserved2;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfEventAttributes &attrs);
+
+
+class PerfAttributes {
+public:
+ bool read(QIODevice *device, PerfHeader *header);
+ void addAttributes(quint64 id, const PerfEventAttributes &attributes);
+ void setGlobalAttributes(const PerfEventAttributes &attributes);
+
+ const PerfEventAttributes &attributes(quint64 id) const;
+ const PerfEventAttributes &globalAttributes() const { return m_globalAttributes; }
+
+private:
+ PerfEventAttributes m_globalAttributes;
+ QHash<quint64, PerfEventAttributes> m_attributes;
+};
+
+#endif // PERFATTRIBUTES_H
diff --git a/app/perfdata.cpp b/app/perfdata.cpp
new file mode 100644
index 0000000..6a71b3f
--- /dev/null
+++ b/app/perfdata.cpp
@@ -0,0 +1,554 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfdata.h"
+#include "perfunwind.h"
+
+#include <QDebug>
+#include <limits>
+
+PerfData::PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader *header,
+ PerfAttributes *attributes) :
+ m_source(source), m_destination(destination), m_header(header), m_attributes(attributes)
+{
+}
+
+PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
+{
+ qint64 headerSize = sizeof(PerfEventHeader);
+
+ if (m_eventHeader.size == 0) {
+ if (stream.device()->bytesAvailable() < headerSize)
+ return Rerun;
+
+ stream >> m_eventHeader;
+
+ if (m_eventHeader.size < headerSize) {
+ qWarning() << "bad event header size" << m_eventHeader.size << m_eventHeader.type
+ << m_eventHeader.misc;
+ return SignalError;
+ }
+ }
+
+ if (stream.device()->bytesAvailable() < m_eventHeader.size - headerSize)
+ return Rerun;
+
+ const PerfEventAttributes &attrs = m_attributes->globalAttributes();
+ int idOffset = attrs.sampleIdOffset();
+ bool sampleIdAll = attrs.sampleIdAll();
+ quint64 sampleType = attrs.sampleType();
+
+ switch (m_eventHeader.type) {
+ case PERF_RECORD_MMAP: {
+ PerfRecordMmap mmap(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> mmap;
+ m_destination->registerElf(mmap);
+ break;
+ }
+ case PERF_RECORD_LOST:
+ m_lostRecords << PerfRecordLost(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> m_lostRecords.last();
+ break;
+ case PERF_RECORD_COMM: {
+ PerfRecordComm comm(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> comm;
+ m_destination->registerThread(comm.tid(), comm.comm());
+ break;
+ }
+ case PERF_RECORD_SAMPLE: {
+ const PerfEventAttributes *sampleAttrs = &attrs;
+
+ if (sampleIdAll && idOffset >= 0) {
+ if (stream.device()->isSequential()) {
+ qWarning() << "trying to forward-peek into sample on stream device.";
+ return SignalError;
+ }
+
+ // peek into the data structure to find the actual ID. Horrible.
+ quint64 id;
+ qint64 prevPos = stream.device()->pos();
+ stream.device()->seek(prevPos + idOffset);
+ stream >> id;
+ stream.device()->seek(prevPos);
+ sampleAttrs = &m_attributes->attributes(id);
+ }
+
+ // TODO: for this we have to find the right attribute by some kind of hash and id ...
+ PerfRecordSample sample(&m_eventHeader, sampleAttrs);
+ stream >> sample;
+ m_destination->analyze(sample);
+ break;
+ }
+ case PERF_RECORD_MMAP2: {
+ PerfRecordMmap2 mmap2(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> mmap2;
+ m_destination->registerElf(mmap2); // Throw out the extra data for now.
+ break;
+ }
+ case PERF_RECORD_HEADER_ATTR: {
+ PerfRecordAttr attr(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> attr;
+ if (m_attributes->globalAttributes().size() == 0)
+ m_attributes->setGlobalAttributes(attr.attr());
+
+ foreach (quint64 id, attr.ids())
+ m_attributes->addAttributes(id, attr.attr());
+
+ break;
+ }
+ case PERF_RECORD_FORK: {
+ PerfRecordFork fork(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> fork;
+ m_destination->fork(fork);
+ break;
+ }
+ case PERF_RECORD_EXIT: {
+ PerfRecordFork exit(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> exit;
+ m_destination->exit(exit);
+ break;
+ }
+
+ default:
+ qWarning() << "unhandled event type" << m_eventHeader.type;
+ stream.skipRawData(m_eventHeader.size - headerSize);
+ break;
+ }
+
+ m_eventHeader.size = 0;
+
+ return SignalFinished;
+}
+
+PerfData::ReadStatus PerfData::doRead()
+{
+ QDataStream stream(m_source);
+ stream.setByteOrder(m_header->byteOrder());
+ ReadStatus returnCode = SignalFinished;
+
+ if (m_header->isPipe()) {
+ if (m_source->isSequential()) {
+ while (m_source->bytesAvailable() > 0) {
+ returnCode = processEvents(stream);
+ if (returnCode == SignalError || returnCode == Rerun)
+ break;
+ }
+ if (returnCode != SignalError) {
+ if (m_source->isOpen()) {
+ // finished some event, but not the whole stream
+ returnCode = Rerun;
+ } else {
+ // if there is a half event left when the stream finishes, that's bad
+ returnCode = m_eventHeader.size != 0 ? SignalError : SignalFinished;
+ }
+ }
+ } else {
+ while (!m_source->atEnd()) {
+ if (processEvents(stream) != SignalFinished) {
+ returnCode = SignalError;
+ break;
+ }
+ }
+ }
+ } else if (m_source->isSequential()) {
+ qWarning() << "cannot read non-stream format from stream";
+ returnCode = SignalError;
+ } else if (!m_source->seek(m_header->dataOffset())) {
+ qWarning() << "cannot seek to" << m_header->dataOffset();
+ returnCode = SignalError;
+ } else {
+ while (static_cast<quint64>(m_source->pos()) <
+ m_header->dataOffset() + m_header->dataSize()) {
+ if (processEvents(stream) != SignalFinished) {
+ returnCode = SignalError;
+ break;
+ }
+ }
+ }
+
+ return returnCode;
+}
+
+void PerfData::read()
+{
+ ReadStatus returnCode = doRead();
+ switch (returnCode) {
+ case SignalFinished:
+ disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
+ disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
+ emit finished();
+ break;
+ case SignalError:
+ disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
+ disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
+ emit error();
+ break;
+ case Rerun:
+ break;
+ }
+}
+
+void PerfData::finishReading()
+{
+ disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
+ disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
+
+ ReadStatus returnCode = doRead();
+ switch (returnCode) {
+ case SignalFinished:
+ emit finished();
+ break;
+ case SignalError:
+ emit error();
+ break;
+ case Rerun:
+ if (m_eventHeader.size == 0)
+ emit finished();
+ else
+ emit error();
+ break;
+ }
+}
+
+PerfRecordMmap::PerfRecordMmap(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_tid(0), m_addr(0), m_len(0), m_pgoff(0)
+{
+}
+
+QDataStream &PerfRecordMmap::readNumbers(QDataStream &stream)
+{
+ return stream >> m_pid >> m_tid >> m_addr >> m_len >> m_pgoff;
+}
+
+QDataStream &PerfRecordMmap::readFilename(QDataStream &stream, quint64 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);
+ return stream;
+}
+
+QDataStream &PerfRecordMmap::readSampleId(QDataStream &stream)
+{
+ stream >> m_sampleId;
+
+ if (m_sampleId.sampleType() &
+ (PerfEventAttributes::SAMPLE_ID_ALL | PerfEventAttributes::SAMPLE_TID)) {
+ if (m_sampleId.pid() != m_pid)
+ qWarning() << "ambiguous pids in mmap event" << m_sampleId.pid() << m_pid;
+ if (m_sampleId.tid() != m_tid)
+ qWarning() << "ambiguous tids in mmap event" << m_sampleId.tid() << m_tid;
+ }
+
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordMmap &record)
+{
+ record.readNumbers(stream);
+ quint64 filenameLength = record.m_header.size - sizeof(PerfRecordMmap) + sizeof(PerfSampleId) +
+ sizeof(record.m_filename) - record.m_sampleId.length();
+ record.readFilename(stream, filenameLength);
+ record.readSampleId(stream);
+ return stream;
+}
+
+PerfRecordMmap2::PerfRecordMmap2(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecordMmap(header, sampleType, sampleIdAll), m_maj(0), m_min(0), m_ino(0),
+ m_ino_generation(0), m_prot(0), m_flags(0)
+{
+}
+
+QDataStream &PerfRecordMmap2::readNumbers(QDataStream &stream)
+{
+ PerfRecordMmap::readNumbers(stream);
+ return stream >> m_maj >> m_min >> m_ino >> m_ino_generation >> m_prot >> m_flags;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record)
+{
+ record.readNumbers(stream);
+ quint64 filenameLength = record.m_header.size - sizeof(PerfRecordMmap2) + sizeof(PerfSampleId) +
+ sizeof(record.m_filename) - record.m_sampleId.length();
+ record.readFilename(stream, filenameLength);
+ record.readSampleId(stream);
+ return stream;
+}
+
+PerfRecordComm::PerfRecordComm(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_tid(0)
+{
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record)
+{
+ stream >> record.m_pid >> record.m_tid;
+ quint64 commLength = record.m_header.size - sizeof(PerfRecordComm) + sizeof(PerfSampleId) +
+ sizeof(record.m_comm) - record.m_sampleId.length();
+
+ 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);
+ stream >> record.m_sampleId;
+
+ return stream;
+}
+
+
+PerfRecordLost::PerfRecordLost(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecord(header, sampleType, sampleIdAll), m_id(0), m_lost(0)
+{
+}
+
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordLost &record)
+{
+ stream >> record.m_id >> record.m_lost >> record.m_sampleId;
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId)
+{
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_ID_ALL) {
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_TID)
+ stream >> sampleId.m_pid >> sampleId.m_tid;
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_TIME)
+ stream >> sampleId.m_time;
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_ID)
+ stream >> sampleId.m_id;
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
+ stream >> sampleId.m_streamId;
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_CPU)
+ stream >> sampleId.m_res >> sampleId.m_cpu;
+ if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
+ stream.skipRawData(sizeof(sampleId.m_ignoredDuplicateId));
+ }
+ return stream;
+}
+
+
+quint64 PerfSampleId::length() const
+{
+ quint64 ret = 0;
+ if (m_sampleType & PerfEventAttributes::SAMPLE_ID_ALL) {
+ if (m_sampleType & PerfEventAttributes::SAMPLE_TID)
+ ret += sizeof(m_pid) + sizeof(m_tid);
+ if (m_sampleType & PerfEventAttributes::SAMPLE_TIME)
+ ret += sizeof(m_time);
+ if (m_sampleType & PerfEventAttributes::SAMPLE_ID)
+ ret += sizeof(m_id);
+ if (m_sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
+ ret += sizeof(m_streamId);
+ if (m_sampleType & PerfEventAttributes::SAMPLE_CPU)
+ ret += sizeof(m_res) + sizeof(m_cpu);
+ if (m_sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
+ ret += sizeof(m_ignoredDuplicateId);
+ }
+ return ret;
+}
+
+
+PerfRecord::PerfRecord(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ m_header(header ? *header : PerfEventHeader()), m_sampleId(sampleType, sampleIdAll)
+{
+}
+
+
+PerfRecordSample::PerfRecordSample(const PerfEventHeader *header,
+ const PerfEventAttributes *attributes)
+ : PerfRecord(header, attributes->sampleType(), false), m_readFormat(attributes->readFormat()),
+ m_registerMask(attributes->sampleRegsUser()), m_ip(0), m_addr(0), m_period(0),
+ m_timeEnabled(0), m_timeRunning(0), m_registerAbi(0)
+{
+}
+
+quint64 PerfRecordSample::registerValue(uint reg) const
+{
+ Q_ASSERT(m_registerAbi && m_registerMask & (1 << reg));
+
+ int index = 0;
+ for (uint i = 0; i < reg; i++) {
+ if (m_registerMask & (1 << i))
+ index++;
+ }
+
+ if (index < m_registers.length()) {
+ return m_registers[index];
+ } else {
+ qWarning() << "invalid register offset" << index;
+ return -1;
+ }
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record)
+{
+ quint32 waste32;
+
+ const quint64 sampleType = record.m_sampleId.sampleType();
+
+ if (sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
+ stream >> record.m_sampleId.m_id;
+ if (sampleType & PerfEventAttributes::SAMPLE_IP)
+ stream >> record.m_ip;
+ if (sampleType & PerfEventAttributes::SAMPLE_TID)
+ stream >> record.m_sampleId.m_pid >> record.m_sampleId.m_tid;
+ if (sampleType & PerfEventAttributes::SAMPLE_TIME)
+ stream >> record.m_sampleId.m_time;
+ if (sampleType & PerfEventAttributes::SAMPLE_ADDR)
+ stream >> record.m_addr;
+ if (sampleType & PerfEventAttributes::SAMPLE_ID)
+ stream >> record.m_sampleId.m_id; // It's the same as identifier
+ if (sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
+ stream >> record.m_sampleId.m_streamId;
+ if (sampleType & PerfEventAttributes::SAMPLE_CPU)
+ stream >> record.m_sampleId.m_cpu >> waste32;
+ if (sampleType & PerfEventAttributes::SAMPLE_PERIOD)
+ stream >> record.m_period;
+
+ if (sampleType & PerfEventAttributes::SAMPLE_READ) {
+ quint64 numFormats = 1;
+ PerfRecordSample::ReadFormat format;
+ if (record.m_readFormat & PerfEventAttributes::FORMAT_GROUP)
+ stream >> numFormats;
+ else
+ stream >> format.value;
+
+ if (record.m_readFormat & PerfEventAttributes::FORMAT_TOTAL_TIME_ENABLED)
+ stream >> record.m_timeEnabled;
+ if (record.m_readFormat & PerfEventAttributes::FORMAT_TOTAL_TIME_RUNNING)
+ stream >> record.m_timeRunning;
+
+ if (record.m_readFormat & PerfEventAttributes::FORMAT_GROUP) {
+ while (numFormats-- > 0) {
+ stream >> format.value >> format.id;
+ record.m_readFormats << format;
+ }
+ } else {
+ stream >> format.id;
+ record.m_readFormats << format;
+ }
+ }
+
+ if (sampleType & PerfEventAttributes::SAMPLE_CALLCHAIN) {
+ quint64 numIps;
+ quint64 ip;
+ stream >> numIps;
+ while (numIps-- > 0) {
+ stream >> ip;
+ record.m_callchain << ip;
+ }
+ }
+
+ 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;
+ }
+ record.m_rawData.resize(rawSize);
+ stream.readRawData(record.m_rawData.data(), rawSize);
+ }
+
+ if (sampleType & PerfEventAttributes::SAMPLE_BRANCH_STACK) {
+ quint64 numBranches;
+ stream >> numBranches;
+ PerfRecordSample::BranchEntry entry;
+ while (numBranches-- > 0) {
+ stream >> entry.from >> entry.to;
+ record.m_branchStack << entry;
+ }
+ }
+
+ if (sampleType & PerfEventAttributes::SAMPLE_REGS_USER) {
+ quint64 reg;
+ stream >> record.m_registerAbi;
+ if (record.m_registerAbi) {
+ for (uint i = qPopulationCount(record.m_registerMask); i > 0; --i) {
+ stream >> reg;
+ record.m_registers << reg;
+ }
+ }
+ }
+
+ if (sampleType & PerfEventAttributes::SAMPLE_STACK_USER) {
+ quint64 size;
+ stream >> size;
+
+ if (size > static_cast<quint64>(std::numeric_limits<int>::max())) {
+ // 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;
+ }
+ }
+
+ if (sampleType & PerfEventAttributes::SAMPLE_WEIGHT)
+ stream >> record.m_weight;
+
+ if (sampleType & PerfEventAttributes::SAMPLE_DATA_SRC)
+ stream >> record.m_dataSrc;
+
+ if (sampleType & PerfEventAttributes::SAMPLE_TRANSACTION)
+ stream >> record.m_transaction;
+
+ return stream;
+}
+
+
+PerfRecordAttr::PerfRecordAttr(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecord(header, sampleType, sampleIdAll)
+{
+}
+
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordAttr &record)
+{
+ stream >> record.m_attr;
+ qint64 read = record.m_attr.size() + sizeof(PerfEventHeader);
+ quint64 id = 0;
+ for (quint64 i = 0; i < (record.m_header.size - read) / sizeof(quint64); ++i) {
+ stream >> id;
+ record.m_ids << id;
+ }
+ return stream;
+}
+
+
+PerfRecordFork::PerfRecordFork(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_ppid(0), m_tid(0), m_ptid(0), m_time(0)
+{
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordFork &record)
+{
+ return stream >> record.m_pid >> record.m_ppid >> record.m_tid >> record.m_ptid >> record.m_time
+ >> record.m_sampleId;
+}
diff --git a/app/perfdata.h b/app/perfdata.h
new file mode 100644
index 0000000..3c85390
--- /dev/null
+++ b/app/perfdata.h
@@ -0,0 +1,474 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFDATA_H
+#define PERFDATA_H
+
+#include "perfheader.h"
+#include "perfattributes.h"
+#include "perffeatures.h"
+
+#include <QIODevice>
+
+enum PerfEventType {
+
+ /*
+ * If perf_event_attr.sample_id_all is set then all event types will
+ * have the sample_type selected fields related to where/when
+ * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU,
+ * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed
+ * just after the perf_event_header and the fields already present for
+ * the existing fields, i.e. at the end of the payload. That way a newer
+ * perf.data file will be supported by older perf tools, with these new
+ * optional fields being ignored.
+ *
+ * struct sample_id {
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * } && perf_event_attr::sample_id_all
+ *
+ * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The
+ * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed
+ * relative to header.size.
+ */
+
+ /*
+ * The MMAP events record the PROT_EXEC mappings so that we can
+ * correlate userspace IPs to code. They have the following structure:
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP = 1,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 id;
+ * u64 lost;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_LOST = 2,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * char comm[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_COMM = 3,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_EXIT = 4,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u64 time;
+ * u64 id;
+ * u64 stream_id;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_THROTTLE = 5,
+ PERF_RECORD_UNTHROTTLE = 6,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, ppid;
+ * u32 tid, ptid;
+ * u64 time;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_FORK = 7,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ * u32 pid, tid;
+ *
+ * struct read_format values;
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_READ = 8,
+
+ /*
+ * struct {
+ * struct perf_event_header header;
+ *
+ * #
+ * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
+ * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position
+ * # is fixed relative to header.
+ * #
+ *
+ * { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ * { u64 ip; } && PERF_SAMPLE_IP
+ * { u32 pid, tid; } && PERF_SAMPLE_TID
+ * { u64 time; } && PERF_SAMPLE_TIME
+ * { u64 addr; } && PERF_SAMPLE_ADDR
+ * { u64 id; } && PERF_SAMPLE_ID
+ * { u64 stream_id; } && PERF_SAMPLE_STREAM_ID
+ * { u32 cpu, res; } && PERF_SAMPLE_CPU
+ * { u64 period; } && PERF_SAMPLE_PERIOD
+ *
+ * { struct read_format values;} && PERF_SAMPLE_READ
+ *
+ * { u64 nr,
+ * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ *
+ * #
+ * # The RAW record below is opaque data wrt the ABI
+ * #
+ * # That is, the ABI doesn't make any promises wrt to
+ * # the stability of its content, it may vary depending
+ * # on event, hardware, kernel version and phase of
+ * # the moon.
+ * #
+ * # In other words, PERF_SAMPLE_RAW contents are not an ABI.
+ * #
+ *
+ * { u32 size;
+ * char data[size];} && PERF_SAMPLE_RAW
+ *
+ * { u64 nr;
+ * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
+ *
+ * { u64 weight; } && PERF_SAMPLE_WEIGHT
+ * { u64 data_src; } && PERF_SAMPLE_DATA_SRC
+ * { u64 transaction; } && PERF_SAMPLE_TRANSACTION
+ * };
+ */
+ PERF_RECORD_SAMPLE = 9,
+
+ /*
+ * The MMAP2 records are an augmented version of MMAP, they add
+ * maj, min, ino numbers to be used to uniquely identify each mapping
+ *
+ * struct {
+ * struct perf_event_header header;
+ *
+ * u32 pid, tid;
+ * u64 addr;
+ * u64 len;
+ * u64 pgoff;
+ * u32 maj;
+ * u32 min;
+ * u64 ino;
+ * u64 ino_generation;
+ * u32 prot, flags;
+ * char filename[];
+ * struct sample_id sample_id;
+ * };
+ */
+ PERF_RECORD_MMAP2 = 10,
+
+ PERF_RECORD_MAX, /* non-ABI */
+
+ PERF_RECORD_USER_TYPE_START = 64,
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65, /* depreceated */
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_HEADER_MAX
+};
+
+class PerfRecordSample;
+
+// Use first attribute for deciding if this is present, not the header!
+// Why the first?!? idiots ... => encoded in sampleType via sampleIdAll
+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))
+ {}
+
+ quint32 pid() const { return m_pid; }
+ quint32 tid() const { return m_tid; }
+ quint64 time() const { return m_time; }
+ quint64 length() const;
+ quint64 sampleType() const { return m_sampleType; }
+
+private:
+ quint32 m_pid;
+ quint32 m_tid;
+ quint64 m_time;
+ quint64 m_id;
+ quint64 m_streamId;
+ quint32 m_cpu;
+ quint32 m_res;
+
+ union {
+ quint64 m_ignoredDuplicateId; // In the file format this is the same as id above
+ quint64 m_sampleType; // As the id is ignored we can reuse the space for saving the flags
+ };
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId);
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId);
+
+class PerfRecord {
+public:
+ quint32 pid() const { return m_sampleId.pid(); }
+ quint32 tid() const { return m_sampleId.tid(); }
+ quint64 time() const { return m_sampleId.time(); }
+
+protected:
+ PerfRecord(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll);
+ PerfEventHeader m_header;
+ PerfSampleId m_sampleId;
+};
+
+class PerfRecordMmap2;
+class PerfRecordMmap : public PerfRecord {
+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; }
+
+ quint64 addr() const { return m_addr; }
+ quint64 len() const { return m_len; }
+ quint64 pgoff() const { return m_pgoff; }
+ const QByteArray &filename() const { return m_filename; }
+
+protected:
+ QDataStream &readNumbers(QDataStream &stream);
+ QDataStream &readFilename(QDataStream &stream, quint64 filenameLength);
+ QDataStream &readSampleId(QDataStream &stream);
+
+private:
+ quint32 m_pid;
+ quint32 m_tid;
+ quint64 m_addr;
+ quint64 m_len;
+ quint64 m_pgoff;
+ QByteArray m_filename;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordMmap &record);
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordMmap &record);
+
+class PerfRecordMmap2 : public PerfRecordMmap
+{
+public:
+ PerfRecordMmap2(PerfEventHeader *header = 0, quint64 sampleType = 0, bool sampleIdAll = false);
+
+protected:
+ QDataStream &readNumbers(QDataStream &stream);
+
+private:
+ quint32 m_maj;
+ quint32 m_min;
+ quint64 m_ino;
+ quint64 m_ino_generation;
+ quint32 m_prot;
+ quint32 m_flags;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record);
+
+class PerfRecordLost : public PerfRecord {
+public:
+ PerfRecordLost(PerfEventHeader *header = 0, quint64 sampleType = 0, bool sampleIdAll = false);
+private:
+ quint64 m_id;
+ quint64 m_lost;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordLost &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordLost &record);
+
+class PerfRecordComm : public PerfRecord {
+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;
+ QByteArray m_comm;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record);
+
+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 ip() const { return m_ip; }
+ const QByteArray &userStack() const { return m_userStack; }
+ const QList<quint64> &callchain() const { return m_callchain; }
+
+private:
+ struct ReadFormat {
+ quint64 value;
+ quint64 id;
+ };
+
+ struct BranchEntry {
+ quint64 from;
+ quint64 to;
+ };
+
+ quint64 m_readFormat;
+ quint64 m_registerMask;
+
+ quint64 m_ip;
+ quint64 m_addr;
+ quint64 m_period;
+ quint64 m_timeEnabled;
+ quint64 m_timeRunning;
+
+ quint64 m_registerAbi;
+ quint64 m_weight;
+ quint64 m_dataSrc;
+ quint64 m_transaction;
+
+ QList<ReadFormat> m_readFormats;
+ QList<quint64> m_callchain;
+ QByteArray m_rawData;
+ QList<BranchEntry> m_branchStack;
+ QList<quint64> m_registers;
+ QByteArray m_userStack;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record);
+
+class PerfRecordAttr : PerfRecord
+{
+public:
+ PerfRecordAttr(const PerfEventHeader *header = 0, quint64 sampleType = 0,
+ bool sampleIdAll = false);
+
+ const PerfEventAttributes &attr() { return m_attr; }
+ const QList<quint64> &ids() { return m_ids; }
+
+private:
+ PerfEventAttributes m_attr;
+ QList<quint64> m_ids;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordAttr &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordAttr &record);
+
+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; }
+private:
+ quint32 m_pid, m_ppid;
+ quint32 m_tid, m_ptid;
+ quint64 m_time;
+
+ friend QDataStream &operator>>(QDataStream &stream, PerfRecordFork &record);
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordFork &record);
+
+typedef PerfRecordFork PerfRecordExit;
+
+class PerfUnwind;
+class PerfData : public QObject
+{
+ Q_OBJECT
+public:
+ PerfData(QIODevice *source, PerfUnwind *destination, const PerfHeader *header,
+ PerfAttributes *attributes);
+
+public slots:
+ void read();
+ void finishReading();
+
+signals:
+ void finished();
+ void error();
+
+private:
+
+ enum ReadStatus {
+ Rerun,
+ SignalError,
+ SignalFinished
+ };
+
+ QIODevice *m_source;
+ PerfUnwind *m_destination;
+
+ const PerfHeader *m_header;
+ PerfAttributes *m_attributes;
+ PerfEventHeader m_eventHeader;
+
+ QList<PerfRecordLost> m_lostRecords;
+ ReadStatus processEvents(QDataStream &stream);
+ ReadStatus doRead();
+};
+
+#endif // PERFDATA_H
diff --git a/app/perffeatures.cpp b/app/perffeatures.cpp
new file mode 100644
index 0000000..f9f26bb
--- /dev/null
+++ b/app/perffeatures.cpp
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perffeatures.h"
+#include <QDebug>
+#include <limits>
+
+// TODO: What to do if feature flags are set but features don't really exist in the file?
+
+void PerfFeatures::createFeature(QIODevice *device, QDataStream::ByteOrder byteOrder,
+ const PerfFileSection &section, PerfHeader::Feature featureId)
+{
+ device->seek(section.offset);
+ QDataStream stream(device);
+ stream.setByteOrder(byteOrder);
+
+ switch (featureId) {
+ case PerfHeader::BUILD_ID:
+ m_buildId.size = section.size;
+ stream >> m_buildId;
+ break;
+ case PerfHeader::HOSTNAME:
+ stream >> m_hostName;
+ break;
+ case PerfHeader::OSRELEASE:
+ stream >> m_osRelease;
+ break;
+ case PerfHeader::VERSION:
+ stream >> m_version;
+ break;
+ case PerfHeader::ARCH:
+ stream >> m_arch;
+ break;
+ case PerfHeader::CPUDESC:
+ stream >> m_cpuDesc;
+ break;
+ case PerfHeader::CPUID:
+ stream >> m_cpuId;
+ break;
+ case PerfHeader::NRCPUS:
+ stream >> m_nrCpus;
+ break;
+ case PerfHeader::TOTAL_MEM:
+ stream >> m_totalMem;
+ break;
+ case PerfHeader::CMDLINE:
+ stream >> m_cmdline;
+ break;
+ case PerfHeader::EVENT_DESC:
+ stream >> m_eventDesc;
+ break;
+ case PerfHeader::CPU_TOPOLOGY:
+ stream >> m_cpuTopology;
+ break;
+ case PerfHeader::NUMA_TOPOLOGY:
+ stream >> m_numaToplogy;
+ break;
+ case PerfHeader::BRANCH_STACK:
+ stream >> m_branchStack;
+ break;
+ case PerfHeader::PMU_MAPPINGS:
+ stream >> m_pmuMappings;
+ break;
+ case PerfHeader::GROUP_DESC:
+ stream >> m_groupDesc;
+ break;
+ default:
+ break;
+ }
+
+ quint64 readSize = device->pos() - section.offset;
+ if (section.size != readSize) {
+ qWarning() << "feature not properly read" << featureId << section.size << readSize;
+ QByteArray data;
+ data.resize(readSize);
+ stream.readRawData(data.data(), readSize);
+ }
+}
+
+PerfFeatures::PerfFeatures()
+{
+}
+
+PerfFeatures::~PerfFeatures()
+{
+}
+
+bool PerfFeatures::read(QIODevice *device, const PerfHeader *header)
+{
+ if (!device->seek(header->featureOffset())) {
+ qWarning() << "cannot seek to" << header->featureOffset();
+ return false;
+ }
+ QDataStream stream(device);
+ stream.setByteOrder(header->byteOrder());
+
+ QHash<PerfHeader::Feature, PerfFileSection> featureSections;
+ PerfFileSection section;
+ for (uint i = 0; i < PerfHeader::LAST_FEATURE; ++i) {
+ PerfHeader::Feature feature = (PerfHeader::Feature)i;
+ if (header->hasFeature(feature)) {
+ stream >> section;
+ if (section.size > 0)
+ featureSections[feature] = section;
+ else
+ qWarning() << "Feature announced in header but not present:" << feature;
+ }
+ }
+
+ QHash<PerfHeader::Feature, PerfFileSection>::ConstIterator i;
+ for (i = featureSections.begin(); i != featureSections.end(); ++i)
+ createFeature(device, stream.byteOrder(), i.value(), i.key());
+
+ return true;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId)
+{
+ quint64 next = 0;
+ while (next < buildId.size) {
+ PerfBuildId::BuildId build;
+ stream >> build.header;
+ stream >> build.pid;
+
+ build.id.resize(PerfBuildId::s_idLength);
+ stream.readRawData(build.id.data(), PerfBuildId::s_idLength);
+ stream.skipRawData(PerfBuildId::s_idPadding);
+
+ uint fileNameLength = build.header.size - sizeof(build.header) - 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);
+ next += build.header.size;
+ buildId.buildIds << build;
+ }
+ return stream;
+}
+
+
+QDataStream &operator>>(QDataStream &stream, PerfEventHeader &header)
+{
+ return stream >> header.type >> header.misc >> header.size;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfStringFeature &string)
+{
+ quint32 length;
+ stream >> length;
+ if (length > static_cast<quint32>(std::numeric_limits<int>::max())) {
+ qWarning() << "bad string length";
+ return stream;
+ }
+ string.value.resize(length);
+ stream.readRawData(string.value.data(), length);
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfNrCpus &nrCpus)
+{
+ return stream >> nrCpus.online >> nrCpus.available;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfTotalMem &totalMem)
+{
+ return stream >> totalMem.totalMem;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfCmdline &cmdline)
+{
+ quint32 numParts;
+ stream >> numParts;
+ PerfStringFeature stringFeature;
+ while (numParts-- > 0) {
+ stream >> stringFeature;
+ cmdline.cmdline << stringFeature.value;
+ }
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfEventDesc &eventDesc)
+{
+ quint32 numEvents;
+ quint32 eventSize;
+ quint32 numIds;
+ quint64 id;
+ stream >> numEvents >> eventSize;
+ PerfStringFeature stringFeature;
+ while (numEvents-- > 0) {
+ eventDesc.eventDescs << PerfEventDesc::EventDesc();
+ PerfEventDesc::EventDesc &currentEvent = eventDesc.eventDescs.last();
+ stream >> currentEvent.attrs;
+ stream >> numIds;
+ stream >> stringFeature;
+ currentEvent.name = stringFeature.value;
+ while (numIds-- > 0) {
+ stream >> id;
+ currentEvent.ids << id;
+ }
+ }
+ // There is some additional length-encoded stuff after this, but perf doesn't read that, either.
+ // On top of that perf is only interested in the event name and throws everything else away
+ // after reading.
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfCpuTopology &cpuTopology)
+{
+ quint32 numSiblings;
+ PerfStringFeature stringFeature;
+ stream >> numSiblings;
+
+ while (numSiblings-- > 0) {
+ stream >> stringFeature;
+ cpuTopology.siblingCores << stringFeature.value;
+ }
+
+ stream >> numSiblings;
+ while (numSiblings-- > 0) {
+ stream >> stringFeature;
+ cpuTopology.siblingThreads << stringFeature.value;
+ }
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfNumaTopology &numaTopology)
+{
+ quint32 numNodes;
+ stream >> numNodes;
+
+ PerfStringFeature stringFeature;
+ while (numNodes-- > 0) {
+ PerfNumaTopology::NumaNode node;
+ stream >> node.nodeId >> node.memTotal >> node.memFree >> stringFeature;
+ node.topology = stringFeature.value;
+ numaTopology.nodes << node;
+ }
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfBranchStack &branchStack)
+{
+ // Doesn't really exist.
+ Q_UNUSED(stream);
+ Q_UNUSED(branchStack);
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfPmuMappings &pmuMappings)
+{
+ quint32 numPmus;
+ stream >> numPmus;
+
+ PerfStringFeature stringFeature;
+ while (numPmus-- > 0) {
+ PerfPmuMappings::Pmu pmu;
+ stream >> pmu.type >> stringFeature;
+ pmu.name = stringFeature.value;
+ pmuMappings.pmus << pmu;
+ }
+ return stream;
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfGroupDesc &groupDesc)
+{
+ quint32 numGroups;
+ stream >> numGroups;
+
+ PerfStringFeature stringFeature;
+ while (numGroups-- > 0) {
+ PerfGroupDesc::GroupDesc desc;
+ stream >> stringFeature;
+ desc.name = stringFeature.value;
+ stream >> desc.leaderIndex >> desc.numMembers;
+ groupDesc.groupDescs << desc;
+ }
+ return stream;
+}
diff --git a/app/perffeatures.h b/app/perffeatures.h
new file mode 100644
index 0000000..c63bea2
--- /dev/null
+++ b/app/perffeatures.h
@@ -0,0 +1,180 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFFEATURE_H
+#define PERFFEATURE_H
+
+#include "perfheader.h"
+#include "perfattributes.h"
+
+#include <QHash>
+#include <QVector>
+
+struct PerfEventHeader {
+ PerfEventHeader() : type(0), misc(0), size(0) {}
+ quint32 type;
+ quint16 misc;
+ quint16 size;
+};
+
+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;
+
+ struct BuildId {
+ PerfEventHeader header;
+ quint32 pid;
+ QByteArray id;
+ QByteArray fileName;
+ };
+
+ quint64 size;
+ QList<BuildId> buildIds;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfBuildId &buildId);
+
+struct PerfStringFeature {
+ QByteArray value;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfStringFeature &stringFeature);
+
+struct PerfNrCpus {
+ quint32 online;
+ quint32 available;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfNrCpus &numCpus);
+
+struct PerfTotalMem {
+ quint64 totalMem;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfTotalMem &totalMem);
+
+struct PerfCmdline {
+ QList<QByteArray> cmdline;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfCmdline &cmdline);
+
+struct PerfEventDesc {
+ struct EventDesc {
+ PerfEventAttributes attrs;
+ QByteArray name;
+ QList<quint64> ids;
+ };
+
+ QList<EventDesc> eventDescs;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfEventDesc &eventDesc);
+
+struct PerfCpuTopology {
+
+ // Some kind of bitmask. Not so important for now
+ QList<QByteArray> siblingCores;
+ QList<QByteArray> siblingThreads;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfCpuTopology &cpuTopology);
+
+struct PerfNumaTopology {
+
+ struct NumaNode {
+ quint32 nodeId;
+ quint64 memTotal;
+ quint64 memFree;
+ QByteArray topology;
+ };
+
+ QList<NumaNode> nodes;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfNumaTopology &numaTopology);
+
+struct PerfBranchStack {
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfBranchStack &branchStack);
+
+struct PerfPmuMappings {
+
+ struct Pmu {
+ quint32 type;
+ QByteArray name;
+ };
+ QList<Pmu> pmus;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfPmuMappings &pmuMappings);
+
+struct PerfGroupDesc {
+
+ struct GroupDesc {
+ QByteArray name;
+ quint32 leaderIndex;
+ quint32 numMembers;
+ };
+
+ QList<GroupDesc> groupDescs;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfGroupDesc &groupDesc);
+
+class PerfFeatures
+{
+public:
+ PerfFeatures();
+ ~PerfFeatures();
+
+ bool read(QIODevice *device, const PerfHeader *header);
+ const QByteArray &architecture() const { return m_arch.value; }
+ void setArchitecture(const QByteArray &arch) { m_arch.value = arch; }
+
+private:
+ void createFeature(QIODevice *device, QDataStream::ByteOrder byteOrder,
+ const PerfFileSection &section, PerfHeader::Feature featureId);
+
+ PerfBuildId m_buildId;
+ PerfStringFeature m_hostName;
+ PerfStringFeature m_osRelease;
+ PerfStringFeature m_version;
+ PerfStringFeature m_arch;
+ PerfNrCpus m_nrCpus;
+ PerfStringFeature m_cpuDesc;
+ PerfStringFeature m_cpuId;
+ PerfTotalMem m_totalMem;
+ PerfCmdline m_cmdline;
+ PerfEventDesc m_eventDesc;
+ PerfCpuTopology m_cpuTopology;
+ PerfNumaTopology m_numaToplogy;
+ PerfBranchStack m_branchStack;
+ PerfPmuMappings m_pmuMappings;
+ PerfGroupDesc m_groupDesc;
+};
+
+#endif // PERFFEATURE_H
diff --git a/app/perffilesection.cpp b/app/perffilesection.cpp
new file mode 100644
index 0000000..4a6d3e4
--- /dev/null
+++ b/app/perffilesection.cpp
@@ -0,0 +1,29 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perffilesection.h"
+
+QDataStream &operator>>(QDataStream &stream, PerfFileSection &section)
+{
+ return stream >> section.offset >> section.size;
+}
+
+
+PerfFileSection::PerfFileSection() : offset(0), size(0) {}
diff --git a/app/perffilesection.h b/app/perffilesection.h
new file mode 100644
index 0000000..b375cef
--- /dev/null
+++ b/app/perffilesection.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFFILESECTION_H
+#define PERFFILESECTION_H
+
+#include <QDataStream>
+
+struct PerfFileSection {
+ PerfFileSection();
+ quint64 offset;
+ quint64 size;
+};
+
+QDataStream &operator>>(QDataStream &stream, PerfFileSection &section);
+
+#endif // PERFFILESECTION_H
diff --git a/app/perfheader.cpp b/app/perfheader.cpp
new file mode 100644
index 0000000..e2abef0
--- /dev/null
+++ b/app/perfheader.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfheader.h"
+#include <QDebug>
+
+PerfHeader::PerfHeader(QIODevice *source) :
+ m_source(source), m_magic(0), m_size(0), m_attrSize(0)
+{
+ connect(source, &QIODevice::readyRead, this, &PerfHeader::read);
+ for (uint i = 0; i < sizeof(m_features) / sizeof(quint64); ++i)
+ m_features[i] = 0;
+}
+
+void PerfHeader::read()
+{
+ const uint featureParts = sizeof(m_features) / sizeof(quint64);
+
+ QDataStream stream(m_source);
+ if (m_size == 0) {
+ if (m_source->bytesAvailable() < static_cast<qint64>(sizeof(m_magic) + sizeof(m_size)))
+ return;
+
+ stream >> m_magic;
+ if (m_magic != s_magicSame && m_magic != s_magicSwitched) {
+ qWarning() << "invalid magic:" << m_magic;
+ qWarning() << "we don't support V1 perf data";
+ emit error();
+ return;
+ } else {
+ stream.setByteOrder(byteOrder());
+ }
+
+ stream >> m_size;
+ }
+
+ if (m_size == s_perfHeaderSize) {
+ if (m_source->bytesAvailable() < static_cast<qint64>(m_size - sizeof(m_magic) -
+ sizeof(m_size)))
+ return;
+
+ // file header
+ stream >> m_attrSize >> m_attrs >> m_data >> m_eventTypes;
+ for (uint i = 0; i < featureParts; ++i)
+ stream >> m_features[i];
+
+ if (m_magic == s_magicSwitched && !hasFeature(HOSTNAME)) {
+
+ quint32 *features32 = reinterpret_cast<quint32 *>(&m_features[0]);
+ for (uint i = 0; i < featureParts; ++i)
+ qSwap(features32[i * 2], features32[i * 2 + 1]);
+
+ if (!hasFeature(HOSTNAME)) {
+ // It borked: blank it all
+ qWarning() << "bad feature data:" << m_features;
+ for (uint i = 0; i < featureParts; ++i)
+ m_features[i] = 0;
+ setFeature(BUILD_ID);
+ }
+ }
+ } else {
+ // pipe header, anything to do here?
+ }
+
+ disconnect(m_source, &QIODevice::readyRead, this, &PerfHeader::read);
+ emit finished();
+}
+
+QDataStream::ByteOrder PerfHeader::byteOrder() const
+{
+ // magic is read in QDataStream's default big endian mode
+ return m_magic == s_magicSame ? QDataStream::BigEndian : QDataStream::LittleEndian;
+}
+
+void PerfHeader::setFeature(PerfHeader::Feature feature)
+{
+ Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64));
+ m_features[feature / 64] |= (1ULL << (feature % 64));
+}
+
+void PerfHeader::clearFeature(PerfHeader::Feature feature)
+{
+ Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64));
+ m_features[feature / 64] &= ~(1ULL << (feature % 64));
+}
+
+bool PerfHeader::hasFeature(PerfHeader::Feature feature) const
+{
+ Q_ASSERT(feature >= 0 && feature < sizeof(m_features) * sizeof(quint64));
+ return (m_features[feature / 64] & (1ULL << (feature % 64))) != 0;
+}
+
+
diff --git a/app/perfheader.h b/app/perfheader.h
new file mode 100644
index 0000000..986a18b
--- /dev/null
+++ b/app/perfheader.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFHEADER_H
+#define PERFHEADER_H
+
+#include "perffilesection.h"
+
+#include <QIODevice>
+#include <QDataStream>
+
+class PerfHeader : public QObject {
+ Q_OBJECT
+public:
+ PerfHeader(QIODevice *source);
+
+ enum Feature {
+ RESERVED = 0, /* always cleared */
+ FIRST_FEATURE = 1,
+ TRACING_DATA = 1,
+ BUILD_ID,
+
+ HOSTNAME,
+ OSRELEASE,
+ VERSION,
+ ARCH,
+ NRCPUS,
+ CPUDESC,
+ CPUID,
+ TOTAL_MEM,
+ CMDLINE,
+ EVENT_DESC,
+ CPU_TOPOLOGY,
+ NUMA_TOPOLOGY,
+ BRANCH_STACK,
+ PMU_MAPPINGS,
+ GROUP_DESC,
+ LAST_FEATURE,
+ FEAT_BITS = 256,
+ };
+
+ QDataStream::ByteOrder byteOrder() const;
+
+ bool hasFeature(Feature feature) const;
+ 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; }
+ 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; }
+ bool isPipe() const { return m_size == s_pipeHeaderSize; }
+
+public slots:
+ void read();
+
+signals:
+ void finished();
+ void error();
+
+private:
+ QIODevice *m_source;
+
+ quint64 m_magic;
+ quint64 m_size;
+ quint64 m_attrSize;
+
+ PerfFileSection m_attrs;
+ PerfFileSection m_data;
+ PerfFileSection m_eventTypes;
+
+ 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;
+};
+
+#endif // PERFHEADER_H
diff --git a/app/perfregisterinfo.cpp b/app/perfregisterinfo.cpp
new file mode 100644
index 0000000..0703881
--- /dev/null
+++ b/app/perfregisterinfo.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfregisterinfo.h"
+
+const char *PerfRegisterInfo::s_archNames[] = {
+ "arm", "arm64", "powerpc", "s390", "sh", "sparc", "x86"
+};
+
+const uint PerfRegisterInfo::s_numRegisters[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
+ {16, 16},
+ {33, 33},
+ { 0, 0},
+ { 0, 0},
+ { 0, 0},
+ { 0, 0},
+ { 9, 17},
+};
+
+// 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 arm64[] = { 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 uint none[] = {};
+
+const uint *PerfRegisterInfo::s_perfToDwarf[PerfRegisterInfo::ARCH_INVALID][PerfRegisterInfo::s_numAbis] = {
+ {arm, arm },
+ {arm64, arm64 },
+ {none, none },
+ {none, none },
+ {none, none },
+ {none, none },
+ {x86, x86_64}
+};
+
+const uint PerfRegisterInfo::s_perfIp[ARCH_INVALID] = {
+ 15, 32, 0xffff, 0xffff, 0xffff, 0xffff, 8
+};
+
+const uint PerfRegisterInfo::s_perfSp[ARCH_INVALID] = {
+ 13, 31, 0xffff, 0xffff, 0xffff, 0xffff, 7
+};
diff --git a/app/perfregisterinfo.h b/app/perfregisterinfo.h
new file mode 100644
index 0000000..c834694
--- /dev/null
+++ b/app/perfregisterinfo.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFREGISTERINFO_H
+#define PERFREGISTERINFO_H
+
+#include <QtGlobal>
+
+class PerfRegisterInfo
+{
+public:
+ enum Architecture {
+ ARCH_ARM = 0,
+ ARCH_ARM64,
+ ARCH_POWERPC,
+ ARCH_S390,
+ ARCH_SH,
+ ARCH_SPARC,
+ ARCH_X86,
+ ARCH_INVALID
+ };
+
+ static const uint s_numAbis = 2; // maybe more for some archs?
+
+ static const char *s_archNames[ARCH_INVALID];
+ static const uint s_numRegisters[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];
+
+ // 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];
+ // location of SP register or equivalent in perf register layout for each arch/abi
+ static const uint s_perfSp[ARCH_INVALID];
+};
+
+#endif // PERFREGISTERINFO_H
diff --git a/app/perfstdin.cpp b/app/perfstdin.cpp
new file mode 100644
index 0000000..5a70976
--- /dev/null
+++ b/app/perfstdin.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfstdin.h"
+#include <cstdio>
+#include <limits>
+
+bool PerfStdin::open(QIODevice::OpenMode mode)
+{
+ if (!(mode & QIODevice::ReadOnly) || (mode & QIODevice::WriteOnly))
+ return false;
+ char cReadMode[3] = {'r', (mode & QIODevice::Text) == 0 ? 'b' : '\0', '\0'};
+ if (!freopen(0, cReadMode, stdin))
+ return false;
+
+ return QIODevice::open(mode);
+}
+
+qint64 PerfStdin::readData(char *data, qint64 maxlen)
+{
+ size_t read = fread(data, 1, maxlen, stdin);
+ if (feof(stdin) || ferror(stdin))
+ close();
+ if (read == 0 && maxlen > 0) {
+ return -1;
+ } else {
+ return read;
+ }
+}
+
+qint64 PerfStdin::writeData(const char *data, qint64 len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ return -1;
+}
+
+bool PerfStdin::isSequential() const
+{
+ return true;
+}
+
+qint64 PerfStdin::bytesAvailable() const
+{
+ return isOpen() ? std::numeric_limits<qint64>::max() : 0;
+}
diff --git a/app/perfstdin.h b/app/perfstdin.h
new file mode 100644
index 0000000..2633c84
--- /dev/null
+++ b/app/perfstdin.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFSTDIN_H
+#define PERFSTDIN_H
+
+#include <QIODevice>
+
+class PerfStdin : public QIODevice
+{
+public:
+ bool open(OpenMode mode);
+ bool isSequential() const;
+ qint64 bytesAvailable() const;
+
+protected:
+ qint64 readData(char *data, qint64 maxlen);
+ qint64 writeData(const char *data, qint64 len);
+};
+
+#endif // PERFSTDIN_H
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
new file mode 100644
index 0000000..b79a868
--- /dev/null
+++ b/app/perfunwind.cpp
@@ -0,0 +1,366 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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 "perfunwind.h"
+#include "perfregisterinfo.h"
+
+#include <bfd.h>
+
+#include <QDir>
+#include <QDebug>
+
+#include <limits>
+
+PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugPath,
+ const QString &extraLibsPath, const QString &appPath) :
+ output(output), lastPid(0), registerArch(PerfRegisterInfo::ARCH_INVALID),
+ systemRoot(systemRoot), extraLibsPath(extraLibsPath), appPath(appPath)
+{
+ currentUnwind.unwind = this;
+ offlineCallbacks.find_elf = dwfl_build_id_find_elf;
+ offlineCallbacks.find_debuginfo = dwfl_standard_find_debuginfo;
+ offlineCallbacks.section_address = dwfl_offline_section_address;
+ QByteArray newDebugInfo = (":" + debugPath + ":" + appPath + ":" + extraLibsPath + ":" +
+ systemRoot).toUtf8();
+ debugInfoPath = new char[newDebugInfo.length() + 1];
+ debugInfoPath[newDebugInfo.length()] = 0;
+ memcpy(debugInfoPath, newDebugInfo.data(), newDebugInfo.length());
+ offlineCallbacks.debuginfo_path = &debugInfoPath;
+ dwfl = dwfl_begin(&offlineCallbacks);
+}
+
+PerfUnwind::~PerfUnwind()
+{
+ delete[] debugInfoPath;
+}
+
+bool findInExtraPath(QFileInfo &path, const QString &fileName)
+{
+ path.setFile(path.absoluteFilePath() + "/" + fileName);
+ if (path.exists())
+ return true;
+
+ QDir absDir = path.absoluteDir();
+ foreach (const QString &entry, absDir.entryList(QStringList(),
+ QDir::Dirs | QDir::NoDotAndDotDot)) {
+ path.setFile(absDir, entry);
+ if (findInExtraPath(path, fileName))
+ return true;
+ }
+ return false;
+}
+
+void PerfUnwind::registerElf(const PerfRecordMmap &mmap)
+{
+ QMap<quint64, ElfInfo> &procElfs = elfs[mmap.pid()];
+
+ // No point in mapping the same file twice
+ if (procElfs.find(mmap.addr()) != procElfs.end())
+ return;
+
+ QString fileName = QFileInfo(mmap.filename()).fileName();
+ QFileInfo path;
+ if (mmap.pid() != s_kernelPid) {
+ path.setFile(appPath + "/" + fileName);
+ if (!path.isFile()) {
+ path.setFile(extraLibsPath);
+ if (!findInExtraPath(path, fileName))
+ path.setFile(systemRoot + mmap.filename());
+ }
+ } else { // kernel
+ path.setFile(systemRoot + mmap.filename());
+ }
+
+ if (path.isFile())
+ procElfs[mmap.addr()] = ElfInfo(path, mmap.len());
+ else
+ qWarning() << "cannot find file to report for" << QString::fromLocal8Bit(mmap.filename());
+
+}
+
+void PerfUnwind::registerThread(quint32 tid, const QString &name)
+{
+ threads[tid] = name;
+}
+
+Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid) const
+{
+ QHash<quint32, QMap<quint64, ElfInfo> >::ConstIterator elfsIt = elfs.find(pid);
+ if (elfsIt == elfs.end()) {
+ qWarning() << "Process" << pid << "has no elfs";
+ return 0;
+ }
+ const QMap<quint64, ElfInfo> &procElfs = elfsIt.value();
+ QMap<quint64, ElfInfo>::ConstIterator i = procElfs.upperBound(ip);
+ if (i == procElfs.end() || i.key() != ip) {
+ if (i != procElfs.begin())
+ --i;
+ else
+ i = procElfs.end();
+ }
+
+// /* On ARM, symbols for thumb functions have 1 added to
+// * the symbol address as a flag - remove it */
+// if ((ehdr.e_machine == EM_ARM) &&
+// (map->type == MAP__FUNCTION) &&
+// (sym.st_value & 1))
+// --sym.st_value;
+//
+// ^ We don't have to do this here as libdw is supposed to handle it from version 0.160.
+
+ if (i != procElfs.end() && i.key() + i.value().length > ip) {
+ Dwfl_Module *ret = dwfl_report_elf(
+ dwfl, i.value().file.fileName().toLocal8Bit().constData(),
+ i.value().file.absoluteFilePath().toLocal8Bit().constData(), -1, i.key(),
+ false);
+ if (!ret)
+ qWarning() << "failed to report" << i.value().file.absoluteFilePath() << "for"
+ << QString("0x%1").arg(i.key(), 0, 16).toLocal8Bit().constData() << ":"
+ << dwfl_errmsg(dwfl_errno());
+ return ret;
+ } else {
+ qWarning() << "no elf found for IP"
+ << QString("0x%1").arg(ip, 0, 16).toLocal8Bit().constData();
+ return 0;
+ }
+}
+
+QDataStream &operator<<(QDataStream &stream, const PerfUnwind::Frame &frame)
+{
+ return stream << frame.frame << frame.isKernel << frame.symbol << frame.file;
+}
+
+static pid_t nextThread(Dwfl *dwfl, void *arg, void **threadArg)
+{
+ /* Stop after first thread. */
+ if (*threadArg != 0)
+ return 0;
+
+ *threadArg = arg;
+ return dwfl_pid(dwfl);
+}
+
+
+static bool memoryRead(Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Word *result, void *arg)
+{
+ Q_UNUSED(dwfl)
+ PerfUnwind::UnwindInfo *ui = static_cast<PerfUnwind::UnwindInfo *>(arg);
+
+ /* Check overflow. */
+ if (addr + sizeof(Dwarf_Word) < addr) {
+ qWarning() << "Invalid memory read requested by dwfl" << addr;
+ ui->broken = true;
+ return false;
+ }
+
+ const QByteArray &stack = ui->sample->userStack();
+
+ quint64 start = ui->sample->registerValue(PerfRegisterInfo::s_perfSp[ui->unwind->architecture()]);
+ quint64 end = start + stack.size();
+
+ if (addr < start || addr + sizeof(Dwarf_Word) > end) {
+ qWarning() << "Cannot read memory at" << addr;
+ qWarning() << "dwfl should only read stack state (" << start << "to" << end
+ << ") with memoryRead().";
+ ui->broken = true;
+ return false;
+ }
+
+ *result = *(Dwarf_Word *)(&stack.data()[addr - start]);
+ return true;
+}
+
+bool setInitialRegisters(Dwfl_Thread *thread, void *arg)
+{
+ const PerfUnwind::UnwindInfo *ui = static_cast<PerfUnwind::UnwindInfo *>(arg);
+ quint64 abi = ui->sample->registerAbi() - 1; // ABI 0 means "no registers"
+ Q_ASSERT(abi < PerfRegisterInfo::s_numAbis);
+ uint architecture = ui->unwind->architecture();
+ uint numRegs = PerfRegisterInfo::s_numRegisters[architecture][abi];
+ Dwarf_Word dwarfRegs[numRegs];
+ for (uint i = 0; i < numRegs; ++i)
+ dwarfRegs[i] = ui->sample->registerValue(
+ PerfRegisterInfo::s_perfToDwarf[architecture][abi][i]);
+
+ return dwfl_thread_state_registers(thread, 0, numRegs, dwarfRegs);
+}
+
+static const Dwfl_Thread_Callbacks callbacks = {
+ nextThread, NULL, memoryRead, setInitialRegisters, NULL, NULL
+};
+
+static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dwarf_Addr ip,
+ bool isKernel)
+{
+ Dwfl_Module *mod = dwfl_addrmodule (dwfl, ip);
+ const char *symname = NULL;
+ const char *demangled = NULL;
+ if (!mod)
+ mod = ui->unwind->reportElf(ip, isKernel ? PerfUnwind::s_kernelPid : ui->unwind->pid());
+
+ const char *filename = NULL;
+ GElf_Sym sym;
+ GElf_Off off;
+
+ bool do_adjust = (ui->unwind->architecture() == PerfRegisterInfo::ARCH_ARM);
+ if (mod) {
+ // For addrinfo we need the raw pointer into symtab, so we need to adjust ourselves.
+ symname = dwfl_module_addrinfo(mod, (!do_adjust || (ip & 1)) ? ip : ip + 1, &off, &sym, 0,
+ 0, 0);
+ filename = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ if (symname) {
+ demangled = bfd_demangle(NULL, symname, 0x3);
+ // Adjust it back. The symtab entries are 1 off for all practical purposes.
+ return PerfUnwind::Frame((do_adjust && (sym.st_value & 1)) ? sym.st_value - 1 :
+ sym.st_value,
+ isKernel, demangled ? demangled : symname, filename);
+ } else {
+ qWarning() << "no symbol found for" << ip << "in" << filename;
+ ui->broken = !isKernel;
+ return PerfUnwind::Frame(ip, isKernel, symname, filename);
+ }
+}
+
+static int frameCallback(Dwfl_Frame *state, void *arg)
+{
+ Dwarf_Addr pc;
+ PerfUnwind::UnwindInfo *ui = static_cast<PerfUnwind::UnwindInfo *>(arg);
+
+ bool isactivation;
+ if (!dwfl_frame_pc(state, &pc, &isactivation)) {
+ qWarning() << dwfl_errmsg(dwfl_errno());
+ ui->broken = true;
+ return DWARF_CB_ABORT;
+ }
+
+ Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1);
+
+ /* Get PC->SYMNAME. */
+ Dwfl_Thread *thread = dwfl_frame_thread (state);
+ Dwfl *dwfl = dwfl_thread_dwfl (thread);
+
+ // isKernel = false as unwinding generally only works on user code
+ ui->frames.append(lookupSymbol(ui, dwfl, pc_adjusted, false));
+ return DWARF_CB_OK;
+}
+
+void PerfUnwind::unwindStack()
+{
+ if (dwfl_getthread_frames(dwfl, currentUnwind.sample->pid(), frameCallback, &currentUnwind))
+ qWarning() << "failed to get some frames:" << currentUnwind.sample->tid()
+ << dwfl_errmsg(dwfl_errno());
+}
+
+void PerfUnwind::resolveCallchain()
+{
+ bool isKernel = false;
+ for (int i = 0; i < currentUnwind.sample->callchain().length(); ++i) {
+ quint64 ip = currentUnwind.sample->callchain()[i];
+ if (ip > PERF_CONTEXT_MAX) {
+ switch (ip) {
+ case PERF_CONTEXT_HV: // hypervisor
+ case PERF_CONTEXT_KERNEL:
+ isKernel = true;
+ break;
+ case PERF_CONTEXT_USER:
+ isKernel = false;
+ break;
+ default:
+ qWarning() << "invalid callchain context" << ip;
+ return;
+ }
+ }
+
+ // sometimes it skips the first user frame.
+ if (i == 0 && !isKernel && ip != currentUnwind.sample->ip())
+ currentUnwind.frames.append(lookupSymbol(&currentUnwind, dwfl,
+ currentUnwind.sample->ip(), false));
+
+ if (ip <= PERF_CONTEXT_MAX)
+ currentUnwind.frames.append(lookupSymbol(&currentUnwind, dwfl, ip, isKernel));
+ }
+}
+
+void sendBuffer(QIODevice *output, const QByteArray &buffer)
+{
+ quint32 size = buffer.length();
+ output->write(reinterpret_cast<char *>(&size), sizeof(quint32));
+ output->write(buffer);
+}
+
+void PerfUnwind::analyze(const PerfRecordSample &sample)
+{
+ if (sample.pid() != lastPid) {
+ dwfl_end(dwfl);
+ dwfl = dwfl_begin(&offlineCallbacks);
+ lastPid = sample.pid();
+ reportElf(sample.ip(), lastPid);
+
+ if (!dwfl) {
+ qWarning() << "failed to initialize dwfl" << dwfl_errmsg(dwfl_errno());
+ lastPid = -1;
+ return;
+ }
+ if (!dwfl_attach_state(dwfl, 0, sample.pid(), &callbacks, &currentUnwind)) {
+ qWarning() << "failed to attach state:" << dwfl_errmsg(dwfl_errno());
+ lastPid = -1;
+ return;
+ }
+ } else {
+ if (!dwfl_addrmodule (dwfl, sample.ip()))
+ reportElf(sample.ip(), lastPid);
+ }
+
+ currentUnwind.broken = false;
+ currentUnwind.sample = &sample;
+ currentUnwind.frames.clear();
+ if (sample.callchain().length() > 0)
+ resolveCallchain();
+ if (sample.registerAbi() != 0 && sample.userStack().length() > 0)
+ unwindStack();
+
+ QByteArray buffer;
+ QDataStream(&buffer, QIODevice::WriteOnly)
+ << static_cast<quint8>(currentUnwind.broken ? BadStack : GoodStack) << lastPid
+ << sample.tid() << threads[sample.tid()] << sample.time() << currentUnwind.frames;
+ sendBuffer(output, buffer);
+}
+
+void PerfUnwind::fork(const PerfRecordFork &sample)
+{
+ QByteArray buffer;
+ QDataStream(&buffer, QIODevice::WriteOnly) << static_cast<quint8>(ThreadStart)
+ << sample.childPid() << sample.childTid()
+ << threads[sample.childTid()] << sample.time()
+ << QVector<PerfUnwind::Frame>();
+ sendBuffer(output, buffer);
+}
+
+void PerfUnwind::exit(const PerfRecordExit &sample)
+{
+ QByteArray buffer;
+ QDataStream(&buffer, QIODevice::WriteOnly) << static_cast<quint8>(ThreadEnd)
+ << sample.childPid() << sample.childTid()
+ << threads[sample.childTid()] << sample.time()
+ << QVector<PerfUnwind::Frame>();
+ sendBuffer(output, buffer);
+}
diff --git a/app/perfunwind.h b/app/perfunwind.h
new file mode 100644
index 0000000..12b627e
--- /dev/null
+++ b/app/perfunwind.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** 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
+**
+****************************************************************************/
+
+#ifndef PERFUNWIND_H
+#define PERFUNWIND_H
+
+#include "perfdata.h"
+#include "perfregisterinfo.h"
+#include <libdwfl.h>
+#include <QFileInfo>
+#include <QMap>
+#include <QHash>
+
+class PerfUnwind
+{
+public:
+ enum EventType {
+ GoodStack,
+ BadStack,
+ ThreadStart,
+ ThreadEnd,
+ InvalidType
+ };
+
+ struct Frame {
+ Frame(quint64 frame = 0, bool isKernel = false, const QByteArray &symbol = QByteArray(),
+ const QByteArray &file = QByteArray()) :
+ frame(frame), isKernel(isKernel), symbol(symbol), file(file) {}
+ quint64 frame;
+ bool isKernel;
+ QByteArray symbol;
+ QByteArray file;
+ };
+
+ struct UnwindInfo {
+ UnwindInfo() : frames(0), unwind(0), sample(0), broken(false) {}
+ QVector<PerfUnwind::Frame> frames;
+ const PerfUnwind *unwind;
+ const PerfRecordSample *sample;
+ bool broken;
+ };
+
+ static const quint32 s_kernelPid = std::numeric_limits<quint32>::max();
+
+ PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugInfo,
+ const QString &extraLibs, const QString &appPath);
+ ~PerfUnwind();
+
+ PerfRegisterInfo::Architecture architecture() const { return registerArch; }
+ void setArchitecture(PerfRegisterInfo::Architecture architecture)
+ {
+ registerArch = architecture;
+ }
+
+ void registerElf(const PerfRecordMmap &mmap);
+ void registerThread(quint32 tid, const QString &name);
+ quint32 pid() const { return lastPid; }
+
+ Dwfl_Module *reportElf(quint64 ip, quint32 pid) const;
+
+ void analyze(const PerfRecordSample &sample);
+ void fork(const PerfRecordFork &sample);
+ void exit(const PerfRecordExit &sample);
+
+private:
+
+ enum CallchainContext {
+ PERF_CONTEXT_HV = (quint64)-32,
+ PERF_CONTEXT_KERNEL = (quint64)-128,
+ PERF_CONTEXT_USER = (quint64)-512,
+
+ PERF_CONTEXT_GUEST = (quint64)-2048,
+ PERF_CONTEXT_GUEST_KERNEL = (quint64)-2176,
+ PERF_CONTEXT_GUEST_USER = (quint64)-2560,
+
+ PERF_CONTEXT_MAX = (quint64)-4095,
+ };
+
+ UnwindInfo currentUnwind;
+ QIODevice *output;
+ quint32 lastPid;
+
+ QHash<quint32, QString> threads;
+ Dwfl *dwfl;
+ Dwfl_Callbacks offlineCallbacks;
+ char *debugInfoPath;
+
+ PerfRegisterInfo::Architecture registerArch;
+
+
+ // Root of the file system of the machine that recorded the data. Any binaries and debug
+ // symbols not found in appPath or extraLibsPath have to appear here.
+ QString systemRoot;
+
+ // Extra path to search for binaries and debug symbols before considering the system root
+ QString extraLibsPath;
+
+ // Path where the application being profiled resides. This is the first path to look for
+ // binaries and debug symbols.
+ QString appPath;
+
+ struct ElfInfo {
+ ElfInfo(const QFileInfo &file = QFileInfo(), quint64 length = 0) :
+ file(file), length(length) {}
+ QFileInfo file;
+ quint64 length;
+ };
+
+ QHash<quint32, QMap<quint64, ElfInfo> > elfs; // The inner map needs to be sorted
+
+ void unwindStack();
+ void resolveCallchain();
+};
+
+#endif // PERFUNWIND_H