summaryrefslogtreecommitdiffstats
path: root/app/perfdata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/perfdata.cpp')
-rw-r--r--app/perfdata.cpp163
1 files changed, 162 insertions, 1 deletions
diff --git a/app/perfdata.cpp b/app/perfdata.cpp
index 5b10eaf..27a2095 100644
--- a/app/perfdata.cpp
+++ b/app/perfdata.cpp
@@ -33,11 +33,79 @@ PerfData::PerfData(PerfUnwind *destination, const PerfHeader *header, PerfAttrib
{
}
+PerfData::~PerfData()
+{
+#ifdef HAVE_ZSTD
+ if (m_zstdDstream)
+ ZSTD_freeDStream(m_zstdDstream);
+#endif
+}
+
void PerfData::setSource(QIODevice *source)
{
m_source = source;
}
+bool PerfData::setCompressed(const PerfCompressed &compressed)
+{
+ if (compressed.version != 0) {
+ qWarning() << "unsupported compression version" << compressed.version;
+ return false;
+ } else if (compressed.type != PerfCompressed::PERF_COMP_ZSTD) {
+ qWarning() << "unsupported compression type" << compressed.type;
+ return false;
+ } else if (!CAN_DECOMPRESS_ZSTD) {
+ qWarning() << "zstd decompression support not available";
+ return false;
+ } else if (!compressed.mmap_len) {
+ qWarning() << "invalid compression information" << compressed.mmap_len;
+ return false;
+ }
+ m_compressed = compressed;
+ return true;
+}
+
+const char * perfEventToString(qint32 type)
+{
+ switch (type) {
+ case PERF_RECORD_MMAP: return "PERF_RECORD_MMAP";
+ case PERF_RECORD_LOST: return "PERF_RECORD_LOST";
+ case PERF_RECORD_COMM: return "PERF_RECORD_COMM";
+ case PERF_RECORD_EXIT: return "PERF_RECORD_EXIT";
+ case PERF_RECORD_THROTTLE: return "PERF_RECORD_THROTTLE";
+ case PERF_RECORD_UNTHROTTLE: return "PERF_RECORD_UNTHROTTLE";
+ case PERF_RECORD_FORK: return "PERF_RECORD_FORK";
+ case PERF_RECORD_READ: return "PERF_RECORD_READ";
+ case PERF_RECORD_SAMPLE: return "PERF_RECORD_SAMPLE";
+ case PERF_RECORD_MMAP2: return "PERF_RECORD_MMAP2";
+ case PERF_RECORD_SWITCH: return "PERF_RECORD_SWITCH";
+ case PERF_RECORD_SWITCH_CPU_WIDE: return "PERF_RECORD_SWITCH_CPU_WIDE";
+ case PERF_RECORD_NAMESPACES: return "PERF_RECORD_NAMESPACES";
+ case PERF_RECORD_KSYMBOL: return "PERF_RECORD_KSYMBOL";
+ case PERF_RECORD_BPF_EVENT: return "PERF_RECORD_BPF_EVENT";
+ case PERF_RECORD_CGROUP: return "PERF_RECORD_CGROUP";
+ case PERF_RECORD_HEADER_ATTR: return "PERF_RECORD_HEADER_ATTR";
+ case PERF_RECORD_HEADER_EVENT_TYPE: return "PERF_RECORD_HEADER_EVENT_TYPE";
+ case PERF_RECORD_HEADER_TRACING_DATA: return "PERF_RECORD_HEADER_TRACING_DATA";
+ case PERF_RECORD_HEADER_BUILD_ID: return "PERF_RECORD_HEADER_BUILD_ID";
+ case PERF_RECORD_FINISHED_ROUND: return "PERF_RECORD_FINISHED_ROUND";
+ case PERF_RECORD_ID_INDEX: return "PERF_RECORD_ID_INDEX";
+ case PERF_RECORD_AUXTRACE_INFO: return "PERF_RECORD_AUXTRACE_INFO";
+ case PERF_RECORD_AUXTRACE: return "PERF_RECORD_AUXTRACE";
+ case PERF_RECORD_AUXTRACE_ERROR: return "PERF_RECORD_AUXTRACE_ERROR";
+ case PERF_RECORD_THREAD_MAP: return "PERF_RECORD_THREAD_MAP";
+ case PERF_RECORD_CPU_MAP: return "PERF_RECORD_CPU_MAP";
+ case PERF_RECORD_STAT_CONFIG: return "PERF_RECORD_STAT_CONFIG";
+ case PERF_RECORD_STAT: return "PERF_RECORD_STAT";
+ case PERF_RECORD_STAT_ROUND: return "PERF_RECORD_STAT_ROUND";
+ case PERF_RECORD_EVENT_UPDATE: return "PERF_RECORD_EVENT_UPDATE";
+ case PERF_RECORD_TIME_CONV: return "PERF_RECORD_TIME_CONV";
+ case PERF_RECORD_HEADER_FEATURE: return "PERF_RECORD_HEADER_FEATURE";
+ case PERF_RECORD_COMPRESSED: return "PERF_RECORD_COMPRESSED";
+ }
+ return "uknown type";
+}
+
PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
{
const quint16 headerSize = PerfEventHeader::fixedLength();
@@ -185,9 +253,92 @@ PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
m_destination->contextSwitch(switchEvent);
break;
}
+ case PERF_RECORD_SWITCH_CPU_WIDE: {
+ PerfRecordContextSwitchCpuWide switchEvent(&m_eventHeader, sampleType, sampleIdAll);
+ stream >> switchEvent;
+ // TODO: also send prevNext{T,P}id, that would allow switch markers in the GUI to
+ // show where a switch comes from/goes to
+ m_destination->contextSwitch(switchEvent);
+ break;
+ }
+
+#ifdef HAVE_ZSTD
+ case PERF_RECORD_COMPRESSED: {
+ if (!m_zstdDstream) {
+ if (!m_header->hasFeature(PerfHeader::COMPRESSED)) {
+ qWarning() << "encountered PERF_RECORD_COMPRESSED without HEADER_COMPRESSED information";
+ return SignalError;
+ }
+
+ m_zstdDstream = ZSTD_createDStream();
+ ZSTD_initDStream(m_zstdDstream);
+
+ // preallocate a buffer to hold the compressed data
+ m_compressedBuffer.resize(std::numeric_limits<quint16>::max());
+ }
+
+ // load compressed data into contiguous array
+ stream.readRawData(m_compressedBuffer.data(), contentSize);
+ ZSTD_inBuffer in = {m_compressedBuffer.constData(), static_cast<size_t>(contentSize), 0};
+
+ // setup decompression buffer which may contain data from a previous compressed record
+ // i.e. one where we had to Rerun. the decompression can add at most mmap_len data on top
+ m_decompressBuffer.resize(m_compressed.mmap_len + m_remaininingDecompressedDataSize);
+ auto outBuffer = m_decompressBuffer.data() + m_remaininingDecompressedDataSize;
+ auto outBufferSize = static_cast<size_t>(m_decompressBuffer.size() - m_remaininingDecompressedDataSize);
+ ZSTD_outBuffer out = {outBuffer, outBufferSize, 0};
+
+ // now actually decompress the record data
+ while (in.pos < in.size) {
+ const auto err = ZSTD_decompressStream(m_zstdDstream, &out, &in);
+ if (ZSTD_isError(err)) {
+ qWarning() << "ZSTD decompression failed:" << ZSTD_getErrorName(err);
+ return SignalError;
+ }
+ out.dst = outBuffer + out.pos;
+ out.size = outBufferSize - out.pos;
+ }
+
+ // then resize the buffer to final size, which may be less than mmap_len
+ m_decompressBuffer.resize(out.pos + m_remaininingDecompressedDataSize);
+ // reset this now that we start to parse from the start of the buffer again
+ m_remaininingDecompressedDataSize = 0;
+
+ QDataStream uncompressedStream(m_decompressBuffer);
+ uncompressedStream.setByteOrder(m_header->byteOrder());
+ // we have to set the size to zero here otherwise processEvents gets confused
+ m_eventHeader.size = 0;
+ auto status = SignalFinished;
+ while (status == SignalFinished) {
+ // position in the decompressed buffer that corresponds to a start of the next record
+ // when we encounter a Rerun scenario, we have to start again at this position the next time
+ const auto oldPos = uncompressedStream.device()->pos();
+ status = processEvents(uncompressedStream);
+ switch (status) {
+ case SignalFinished:
+ break;
+ case SignalError:
+ return SignalError;
+ case Rerun:
+ // unset the device to prevent the m_decompressBuffer from being shared
+ // we don't want to copy the data when we call .begin() below
+ uncompressedStream.setDevice(nullptr);
+ // remaining decompressed data that needs to be parsed the next time
+ // we handle an uncompressed record
+ m_remaininingDecompressedDataSize = m_decompressBuffer.size() - oldPos;
+ // move that data up front in the buffer and continue appending data
+ std::move(m_decompressBuffer.begin() + oldPos, m_decompressBuffer.end(),
+ m_decompressBuffer.begin());
+ break;
+ }
+ };
+
+ break;
+ }
+#endif
default:
- qWarning() << "unhandled event type" << m_eventHeader.type;
+ qWarning() << "unhandled event type" << m_eventHeader.type << " " << perfEventToString(m_eventHeader.type);
stream.skipRawData(contentSize);
break;
}
@@ -658,3 +809,13 @@ QDataStream &operator>>(QDataStream &stream, PerfRecordContextSwitch &record)
{
return stream >> record.m_sampleId;
}
+
+PerfRecordContextSwitchCpuWide::PerfRecordContextSwitchCpuWide(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
+ PerfRecordContextSwitch(header, sampleType, sampleIdAll)
+{
+}
+
+QDataStream &operator>>(QDataStream &stream, PerfRecordContextSwitchCpuWide &record)
+{
+ return stream >> record.m_nextPrevPid >> record.m_nextPrevTid >> record.m_sampleId;
+}