1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
6 **
7 ** This file is part of the Qt Enterprise Perf Profiler Add-on.
8 **
9 ** GNU General Public License Usage
10 ** This file may be used under the terms of the GNU General Public License
11 ** version 3 as published by the Free Software Foundation and appearing in
12 ** the file LICENSE.GPLv3 included in the packaging of this file. Please
13 ** review the following information to ensure the GNU General Public License
14 ** requirements will be met: https://www.gnu.org/licenses/gpl.html.
15 **
16 ** If you have questions regarding the use of this file, please use
17 ** contact form at http://www.qt.io/contact-us
18 **
19 ****************************************************************************/
20
21 #include "perfdata.h"
22 #include "perftracingdata.h"
23 #include "perfunwind.h"
24
25 #include <QDebug>
26
27 #include <limits>
28
29 static const int intMax = std::numeric_limits<int>::max();
30
PerfData(PerfUnwind * destination,const PerfHeader * header,PerfAttributes * attributes)31 PerfData::PerfData(PerfUnwind *destination, const PerfHeader *header, PerfAttributes *attributes) :
32 m_source(nullptr), m_destination(destination), m_header(header), m_attributes(attributes)
33 {
34 }
35
~PerfData()36 PerfData::~PerfData()
37 {
38 #ifdef HAVE_ZSTD
39 if (m_zstdDstream)
40 ZSTD_freeDStream(m_zstdDstream);
41 #endif
42 }
43
setSource(QIODevice * source)44 void PerfData::setSource(QIODevice *source)
45 {
46 m_source = source;
47 }
48
setCompressed(const PerfCompressed & compressed)49 bool PerfData::setCompressed(const PerfCompressed &compressed)
50 {
51 if (compressed.version != 0) {
52 qWarning() << "unsupported compression version" << compressed.version;
53 return false;
54 } else if (compressed.type != PerfCompressed::PERF_COMP_ZSTD) {
55 qWarning() << "unsupported compression type" << compressed.type;
56 return false;
57 } else if (!CAN_DECOMPRESS_ZSTD) {
58 qWarning() << "zstd decompression support not available";
59 return false;
60 } else if (!compressed.mmap_len) {
61 qWarning() << "invalid compression information" << compressed.mmap_len;
62 return false;
63 }
64 m_compressed = compressed;
65 return true;
66 }
67
perfEventToString(qint32 type)68 const char * perfEventToString(qint32 type)
69 {
70 switch (type) {
71 case PERF_RECORD_MMAP: return "PERF_RECORD_MMAP";
72 case PERF_RECORD_LOST: return "PERF_RECORD_LOST";
73 case PERF_RECORD_COMM: return "PERF_RECORD_COMM";
74 case PERF_RECORD_EXIT: return "PERF_RECORD_EXIT";
75 case PERF_RECORD_THROTTLE: return "PERF_RECORD_THROTTLE";
76 case PERF_RECORD_UNTHROTTLE: return "PERF_RECORD_UNTHROTTLE";
77 case PERF_RECORD_FORK: return "PERF_RECORD_FORK";
78 case PERF_RECORD_READ: return "PERF_RECORD_READ";
79 case PERF_RECORD_SAMPLE: return "PERF_RECORD_SAMPLE";
80 case PERF_RECORD_MMAP2: return "PERF_RECORD_MMAP2";
81 case PERF_RECORD_SWITCH: return "PERF_RECORD_SWITCH";
82 case PERF_RECORD_SWITCH_CPU_WIDE: return "PERF_RECORD_SWITCH_CPU_WIDE";
83 case PERF_RECORD_NAMESPACES: return "PERF_RECORD_NAMESPACES";
84 case PERF_RECORD_KSYMBOL: return "PERF_RECORD_KSYMBOL";
85 case PERF_RECORD_BPF_EVENT: return "PERF_RECORD_BPF_EVENT";
86 case PERF_RECORD_CGROUP: return "PERF_RECORD_CGROUP";
87 case PERF_RECORD_HEADER_ATTR: return "PERF_RECORD_HEADER_ATTR";
88 case PERF_RECORD_HEADER_EVENT_TYPE: return "PERF_RECORD_HEADER_EVENT_TYPE";
89 case PERF_RECORD_HEADER_TRACING_DATA: return "PERF_RECORD_HEADER_TRACING_DATA";
90 case PERF_RECORD_HEADER_BUILD_ID: return "PERF_RECORD_HEADER_BUILD_ID";
91 case PERF_RECORD_FINISHED_ROUND: return "PERF_RECORD_FINISHED_ROUND";
92 case PERF_RECORD_ID_INDEX: return "PERF_RECORD_ID_INDEX";
93 case PERF_RECORD_AUXTRACE_INFO: return "PERF_RECORD_AUXTRACE_INFO";
94 case PERF_RECORD_AUXTRACE: return "PERF_RECORD_AUXTRACE";
95 case PERF_RECORD_AUXTRACE_ERROR: return "PERF_RECORD_AUXTRACE_ERROR";
96 case PERF_RECORD_THREAD_MAP: return "PERF_RECORD_THREAD_MAP";
97 case PERF_RECORD_CPU_MAP: return "PERF_RECORD_CPU_MAP";
98 case PERF_RECORD_STAT_CONFIG: return "PERF_RECORD_STAT_CONFIG";
99 case PERF_RECORD_STAT: return "PERF_RECORD_STAT";
100 case PERF_RECORD_STAT_ROUND: return "PERF_RECORD_STAT_ROUND";
101 case PERF_RECORD_EVENT_UPDATE: return "PERF_RECORD_EVENT_UPDATE";
102 case PERF_RECORD_TIME_CONV: return "PERF_RECORD_TIME_CONV";
103 case PERF_RECORD_HEADER_FEATURE: return "PERF_RECORD_HEADER_FEATURE";
104 case PERF_RECORD_COMPRESSED: return "PERF_RECORD_COMPRESSED";
105 }
106 return "uknown type";
107 }
108
processEvents(QDataStream & stream)109 PerfData::ReadStatus PerfData::processEvents(QDataStream &stream)
110 {
111 const quint16 headerSize = PerfEventHeader::fixedLength();
112
113 if (m_eventHeader.size == 0) {
114 const qint64 available = stream.device()->bytesAvailable();
115 if (available < 0)
116 return SignalError;
117 if (available < headerSize)
118 return Rerun;
119
120 stream >> m_eventHeader;
121
122 if (m_eventHeader.size < headerSize) {
123 qWarning() << "bad event header size" << m_eventHeader.size << m_eventHeader.type
124 << m_eventHeader.misc;
125 return SignalError;
126 }
127 }
128
129 const quint16 contentSize = m_eventHeader.size - headerSize;
130 qint64 expectedParsedContentSize = contentSize;
131 if (stream.device()->bytesAvailable() < contentSize)
132 return Rerun;
133
134 const PerfEventAttributes &attrs = m_attributes->globalAttributes();
135 int idOffset = attrs.sampleIdOffset();
136 bool sampleIdAll = attrs.sampleIdAll();
137 quint64 sampleType = attrs.sampleType();
138
139 const auto oldPos = stream.device()->isSequential() ? 0 : stream.device()->pos();
140
141 switch (m_eventHeader.type) {
142 case PERF_RECORD_MMAP: {
143 PerfRecordMmap mmap(&m_eventHeader, sampleType, sampleIdAll);
144 stream >> mmap;
145 m_destination->registerElf(mmap);
146 break;
147 }
148 case PERF_RECORD_LOST: {
149 PerfRecordLost lost(&m_eventHeader, sampleType, sampleIdAll);
150 stream >> lost;
151 m_destination->lost(lost);
152 break;
153 }
154 case PERF_RECORD_COMM: {
155 PerfRecordComm comm(&m_eventHeader, sampleType, sampleIdAll);
156 stream >> comm;
157 m_destination->comm(comm);
158 break;
159 }
160 case PERF_RECORD_SAMPLE: {
161 if (sampleIdAll && idOffset >= 0) {
162 QByteArray buffer(contentSize, Qt::Uninitialized);
163 stream.readRawData(buffer.data(), contentSize);
164 QDataStream contentStream(buffer);
165 contentStream.setByteOrder(stream.byteOrder());
166
167 Q_ASSERT(!contentStream.device()->isSequential());
168
169 // peek into the data structure to find the actual ID. Horrible.
170 quint64 id;
171 qint64 prevPos = contentStream.device()->pos();
172 contentStream.device()->seek(prevPos + idOffset);
173 contentStream >> id;
174 contentStream.device()->seek(prevPos);
175
176 PerfRecordSample sample(&m_eventHeader, &m_attributes->attributes(id));
177 contentStream >> sample;
178 m_destination->sample(sample);
179 } else {
180 PerfRecordSample sample(&m_eventHeader, &attrs);
181 stream >> sample;
182 m_destination->sample(sample);
183 }
184
185 break;
186 }
187 case PERF_RECORD_MMAP2: {
188 PerfRecordMmap2 mmap2(&m_eventHeader, sampleType, sampleIdAll);
189 stream >> mmap2;
190 m_destination->registerElf(mmap2); // Throw out the extra data for now.
191 break;
192 }
193 case PERF_RECORD_HEADER_ATTR: {
194 PerfRecordAttr attr(&m_eventHeader, sampleType, sampleIdAll);
195 stream >> attr;
196 m_destination->attr(attr);
197 if (m_attributes->globalAttributes().size() == 0)
198 m_attributes->setGlobalAttributes(attr.attr());
199
200 foreach (quint64 id, attr.ids())
201 m_attributes->addAttributes(id, attr.attr());
202
203 break;
204 }
205 case PERF_RECORD_FORK: {
206 PerfRecordFork fork(&m_eventHeader, sampleType, sampleIdAll);
207 stream >> fork;
208 m_destination->fork(fork);
209 break;
210 }
211 case PERF_RECORD_EXIT: {
212 PerfRecordFork exit(&m_eventHeader, sampleType, sampleIdAll);
213 stream >> exit;
214 m_destination->exit(exit);
215 break;
216 }
217 case PERF_RECORD_HEADER_TRACING_DATA: {
218 if (contentSize == 4) {
219 // The content is actually another 4 byte integer,
220 // describing the size of the real content that follows.
221 if (m_tracingData.size() == 0) {
222 quint32 size;
223 stream >> size;
224 m_tracingData.setSize(size);
225 expectedParsedContentSize += size;
226 }
227 if (stream.device()->bytesAvailable() >= m_tracingData.size()) {
228 stream >> m_tracingData;
229 m_destination->tracing(m_tracingData);
230 m_tracingData = PerfTracingData();
231 } else {
232 return Rerun;
233 }
234 } else {
235 // contentSize is only 16bit. The tracing data frequently exceeds 2^16 bytes.
236 qWarning() << "HEADER_TRACING_DATA with unexpected contentSize" << contentSize;
237 stream.skipRawData(contentSize);
238 }
239 break;
240 }
241 case PERF_RECORD_FINISHED_ROUND: {
242 m_destination->finishedRound();
243 if (contentSize != 0) {
244 qWarning() << "FINISHED_ROUND with non-zero content size detected"
245 << contentSize;
246 stream.skipRawData(contentSize);
247 }
248 break;
249 }
250 case PERF_RECORD_SWITCH: {
251 PerfRecordContextSwitch switchEvent(&m_eventHeader, sampleType, sampleIdAll);
252 stream >> switchEvent;
253 m_destination->contextSwitch(switchEvent);
254 break;
255 }
256 case PERF_RECORD_SWITCH_CPU_WIDE: {
257 PerfRecordContextSwitchCpuWide switchEvent(&m_eventHeader, sampleType, sampleIdAll);
258 stream >> switchEvent;
259 // TODO: also send prevNext{T,P}id, that would allow switch markers in the GUI to
260 // show where a switch comes from/goes to
261 m_destination->contextSwitch(switchEvent);
262 break;
263 }
264
265 #ifdef HAVE_ZSTD
266 case PERF_RECORD_COMPRESSED: {
267 if (!m_zstdDstream) {
268 if (!m_header->hasFeature(PerfHeader::COMPRESSED)) {
269 qWarning() << "encountered PERF_RECORD_COMPRESSED without HEADER_COMPRESSED information";
270 return SignalError;
271 }
272
273 m_zstdDstream = ZSTD_createDStream();
274 ZSTD_initDStream(m_zstdDstream);
275
276 // preallocate a buffer to hold the compressed data
277 m_compressedBuffer.resize(std::numeric_limits<quint16>::max());
278 }
279
280 // load compressed data into contiguous array
281 stream.readRawData(m_compressedBuffer.data(), contentSize);
282 ZSTD_inBuffer in = {m_compressedBuffer.constData(), static_cast<size_t>(contentSize), 0};
283
284 // setup decompression buffer which may contain data from a previous compressed record
285 // i.e. one where we had to Rerun. the decompression can add at most mmap_len data on top
286 m_decompressBuffer.resize(m_compressed.mmap_len + m_remaininingDecompressedDataSize);
287 auto outBuffer = m_decompressBuffer.data() + m_remaininingDecompressedDataSize;
288 auto outBufferSize = static_cast<size_t>(m_decompressBuffer.size() - m_remaininingDecompressedDataSize);
289 ZSTD_outBuffer out = {outBuffer, outBufferSize, 0};
290
291 // now actually decompress the record data
292 while (in.pos < in.size) {
293 const auto err = ZSTD_decompressStream(m_zstdDstream, &out, &in);
294 if (ZSTD_isError(err)) {
295 qWarning() << "ZSTD decompression failed:" << ZSTD_getErrorName(err);
296 return SignalError;
297 }
298 out.dst = outBuffer + out.pos;
299 out.size = outBufferSize - out.pos;
300 }
301
302 // then resize the buffer to final size, which may be less than mmap_len
303 m_decompressBuffer.resize(out.pos + m_remaininingDecompressedDataSize);
304 // reset this now that we start to parse from the start of the buffer again
305 m_remaininingDecompressedDataSize = 0;
306
307 QDataStream uncompressedStream(m_decompressBuffer);
308 uncompressedStream.setByteOrder(m_header->byteOrder());
309 // we have to set the size to zero here otherwise processEvents gets confused
310 m_eventHeader.size = 0;
311 auto status = SignalFinished;
312 while (status == SignalFinished) {
313 // position in the decompressed buffer that corresponds to a start of the next record
314 // when we encounter a Rerun scenario, we have to start again at this position the next time
315 const auto oldPos = uncompressedStream.device()->pos();
316 status = processEvents(uncompressedStream);
317 switch (status) {
318 case SignalFinished:
319 break;
320 case SignalError:
321 return SignalError;
322 case Rerun:
323 // unset the device to prevent the m_decompressBuffer from being shared
324 // we don't want to copy the data when we call .begin() below
325 uncompressedStream.setDevice(nullptr);
326 // remaining decompressed data that needs to be parsed the next time
327 // we handle an uncompressed record
328 m_remaininingDecompressedDataSize = m_decompressBuffer.size() - oldPos;
329 // move that data up front in the buffer and continue appending data
330 std::move(m_decompressBuffer.begin() + oldPos, m_decompressBuffer.end(),
331 m_decompressBuffer.begin());
332 break;
333 }
334 };
335
336 break;
337 }
338 #endif
339
340 default:
341 qWarning() << "unhandled event type" << m_eventHeader.type << " " << perfEventToString(m_eventHeader.type);
342 stream.skipRawData(contentSize);
343 break;
344 }
345
346 if (!stream.device()->isSequential()) {
347 const auto parsedContentSize = stream.device()->pos() - oldPos;
348 if (parsedContentSize != expectedParsedContentSize) {
349 qWarning() << "Event not fully parsed" << m_eventHeader.type << expectedParsedContentSize
350 << parsedContentSize;
351 stream.skipRawData(contentSize - parsedContentSize);
352 }
353 }
354
355 m_eventHeader.size = 0;
356
357 return SignalFinished;
358 }
359
doRead()360 PerfData::ReadStatus PerfData::doRead()
361 {
362 QDataStream stream(m_source);
363 stream.setByteOrder(m_header->byteOrder());
364 ReadStatus returnCode = SignalFinished;
365
366 if (m_header->isPipe()) {
367 if (m_source->isSequential()) {
368 while (m_source->bytesAvailable() > 0) {
369 returnCode = processEvents(stream);
370 if (returnCode == SignalError || returnCode == Rerun)
371 break;
372 }
373 if (returnCode != SignalError) {
374 if (m_source->isOpen()) {
375 // finished some event, but not the whole stream
376 returnCode = Rerun;
377 } else {
378 // if there is a half event left when the stream finishes, that's bad
379 returnCode = m_eventHeader.size != 0 ? SignalError : SignalFinished;
380 }
381 }
382 } else {
383 while (!m_source->atEnd()) {
384 if (processEvents(stream) != SignalFinished) {
385 returnCode = SignalError;
386 break;
387 }
388 }
389 }
390 } else if (m_source->isSequential()) {
391 qWarning() << "cannot read non-stream format from stream";
392 returnCode = SignalError;
393 } else if (!m_source->seek(m_header->dataOffset())) {
394 qWarning() << "cannot seek to" << m_header->dataOffset();
395 returnCode = SignalError;
396 } else {
397 const auto dataOffset = m_header->dataOffset();
398 const auto dataSize = m_header->dataSize();
399 const auto endOfDataSection = dataOffset + dataSize;
400
401 m_destination->sendProgress(float(m_source->pos() - dataOffset) / dataSize);
402 const qint64 posDeltaBetweenProgress = dataSize / 100;
403 qint64 nextProgressAt = m_source->pos() + posDeltaBetweenProgress;
404
405 while (m_source->pos() < endOfDataSection) {
406 if (processEvents(stream) != SignalFinished) {
407 returnCode = SignalError;
408 break;
409 }
410 if (m_source->pos() >= nextProgressAt) {
411 m_destination->sendProgress(float(m_source->pos() - dataOffset) / dataSize);
412 nextProgressAt += posDeltaBetweenProgress;
413 }
414 }
415 }
416
417 return returnCode;
418 }
419
read()420 void PerfData::read()
421 {
422 ReadStatus returnCode = doRead();
423 switch (returnCode) {
424 case SignalFinished:
425 disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
426 disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
427 emit finished();
428 break;
429 case SignalError:
430 disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
431 disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
432 emit error();
433 break;
434 case Rerun:
435 break;
436 }
437 }
438
finishReading()439 void PerfData::finishReading()
440 {
441 disconnect(m_source, &QIODevice::readyRead, this, &PerfData::read);
442 disconnect(m_source, &QIODevice::aboutToClose, this, &PerfData::finishReading);
443
444 ReadStatus returnCode = doRead();
445 switch (returnCode) {
446 case SignalFinished:
447 emit finished();
448 break;
449 case SignalError:
450 emit error();
451 break;
452 case Rerun:
453 if (m_eventHeader.size == 0)
454 emit finished();
455 else
456 emit error();
457 break;
458 }
459 }
460
PerfRecordMmap(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)461 PerfRecordMmap::PerfRecordMmap(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
462 PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_tid(0), m_addr(0), m_len(0), m_pgoff(0)
463 {
464 }
465
readNumbers(QDataStream & stream)466 QDataStream &PerfRecordMmap::readNumbers(QDataStream &stream)
467 {
468 return stream >> m_pid >> m_tid >> m_addr >> m_len >> m_pgoff;
469 }
470
readFilename(QDataStream & stream,quint16 filenameLength)471 QDataStream &PerfRecordMmap::readFilename(QDataStream &stream, quint16 filenameLength)
472 {
473 m_filename.resize(filenameLength);
474 stream.readRawData(m_filename.data(), filenameLength);
475 int null = m_filename.indexOf('\0');
476 if (null != -1)
477 m_filename.truncate(null);
478 return stream;
479 }
480
readSampleId(QDataStream & stream)481 QDataStream &PerfRecordMmap::readSampleId(QDataStream &stream)
482 {
483 stream >> m_sampleId;
484 return stream;
485 }
486
fixedLength() const487 quint16 PerfRecordMmap::fixedLength() const
488 {
489 return sizeof(m_pid) + sizeof(m_tid) + sizeof(m_addr) + sizeof(m_len) + sizeof(m_pgoff)
490 + m_header.fixedLength() + m_sampleId.fixedLength();
491 }
492
operator >>(QDataStream & stream,PerfRecordMmap & record)493 QDataStream &operator>>(QDataStream &stream, PerfRecordMmap &record)
494 {
495 record.readNumbers(stream);
496 record.readFilename(stream, record.m_header.size - record.fixedLength());
497 record.readSampleId(stream);
498 return stream;
499 }
500
PerfRecordMmap2(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)501 PerfRecordMmap2::PerfRecordMmap2(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
502 PerfRecordMmap(header, sampleType, sampleIdAll), m_maj(0), m_min(0), m_ino(0),
503 m_ino_generation(0), m_prot(0), m_flags(0)
504 {
505 }
506
readNumbers(QDataStream & stream)507 QDataStream &PerfRecordMmap2::readNumbers(QDataStream &stream)
508 {
509 PerfRecordMmap::readNumbers(stream);
510 return stream >> m_maj >> m_min >> m_ino >> m_ino_generation >> m_prot >> m_flags;
511 }
512
fixedLength() const513 quint16 PerfRecordMmap2::fixedLength() const
514 {
515 return PerfRecordMmap::fixedLength() + sizeof(m_maj) + sizeof(m_min) + sizeof(m_ino)
516 + sizeof(m_ino_generation) + sizeof(m_prot) + sizeof(m_flags);
517 }
518
operator >>(QDataStream & stream,PerfRecordMmap2 & record)519 QDataStream &operator>>(QDataStream &stream, PerfRecordMmap2 &record)
520 {
521 record.readNumbers(stream);
522 record.readFilename(stream, record.m_header.size - record.fixedLength());
523 record.readSampleId(stream);
524 return stream;
525 }
526
PerfRecordComm(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)527 PerfRecordComm::PerfRecordComm(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
528 PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_tid(0)
529 {
530 }
531
operator >>(QDataStream & stream,PerfRecordComm & record)532 QDataStream &operator>>(QDataStream &stream, PerfRecordComm &record)
533 {
534 stream >> record.m_pid >> record.m_tid;
535 const quint16 commLength = record.m_header.size - record.fixedLength();
536
537 record.m_comm.resize(commLength);
538 stream.readRawData(record.m_comm.data(), commLength);
539 int null = record.m_comm.indexOf('\0');
540 if (null != -1)
541 record.m_comm.truncate(null);
542
543 stream >> record.m_sampleId;
544
545 return stream;
546 }
547
548
PerfRecordLost(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)549 PerfRecordLost::PerfRecordLost(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
550 PerfRecord(header, sampleType, sampleIdAll), m_id(0), m_lost(0)
551 {
552 }
553
554
operator >>(QDataStream & stream,PerfRecordLost & record)555 QDataStream &operator>>(QDataStream &stream, PerfRecordLost &record)
556 {
557 stream >> record.m_id >> record.m_lost >> record.m_sampleId;
558 return stream;
559 }
560
operator >>(QDataStream & stream,PerfSampleId & sampleId)561 QDataStream &operator>>(QDataStream &stream, PerfSampleId &sampleId)
562 {
563 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_ID_ALL) {
564 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_TID)
565 stream >> sampleId.m_pid >> sampleId.m_tid;
566 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_TIME)
567 stream >> sampleId.m_time;
568 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_ID)
569 stream >> sampleId.m_id;
570 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
571 stream >> sampleId.m_streamId;
572 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_CPU)
573 stream >> sampleId.m_cpu >> sampleId.m_res;
574 if (sampleId.m_sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
575 stream.skipRawData(sizeof(sampleId.m_ignoredDuplicateId));
576 }
577 return stream;
578 }
579
580
fixedLength() const581 quint16 PerfSampleId::fixedLength() const
582 {
583 quint16 ret = 0;
584 if (m_sampleType & PerfEventAttributes::SAMPLE_ID_ALL) {
585 if (m_sampleType & PerfEventAttributes::SAMPLE_TID)
586 ret += sizeof(m_pid) + sizeof(m_tid);
587 if (m_sampleType & PerfEventAttributes::SAMPLE_TIME)
588 ret += sizeof(m_time);
589 if (m_sampleType & PerfEventAttributes::SAMPLE_ID)
590 ret += sizeof(m_id);
591 if (m_sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
592 ret += sizeof(m_streamId);
593 if (m_sampleType & PerfEventAttributes::SAMPLE_CPU)
594 ret += sizeof(m_res) + sizeof(m_cpu);
595 if (m_sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
596 ret += sizeof(m_ignoredDuplicateId);
597 }
598 return ret;
599 }
600
601
PerfRecord(const PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)602 PerfRecord::PerfRecord(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
603 m_header(header ? *header : PerfEventHeader()), m_sampleId(sampleType, sampleIdAll)
604 {
605 }
606
607
PerfRecordSample(const PerfEventHeader * header,const PerfEventAttributes * attributes)608 PerfRecordSample::PerfRecordSample(const PerfEventHeader *header,
609 const PerfEventAttributes *attributes)
610 : PerfRecord(header, attributes->sampleType(), false), m_readFormat(attributes->readFormat()),
611 m_registerMask(attributes->sampleRegsUser()), m_ip(0), m_addr(0), m_period(0),
612 m_timeEnabled(0), m_timeRunning(0), m_registerAbi(0), m_weight(0)
613 {
614 }
615
registerValue(int reg) const616 quint64 PerfRecordSample::registerValue(int reg) const
617 {
618 Q_ASSERT(reg >= 0);
619 Q_ASSERT(m_registerAbi && m_registerMask & (1ull << reg));
620
621 int index = 0;
622 for (int i = 0; i < reg; i++) {
623 if (m_registerMask & (1ull << i))
624 index++;
625 }
626
627 if (index < m_registers.length()) {
628 return m_registers[index];
629 } else {
630 qWarning() << "invalid register offset" << index;
631 return std::numeric_limits<quint64>::max();
632 }
633 }
634
operator >>(QDataStream & stream,PerfRecordSample & record)635 QDataStream &operator>>(QDataStream &stream, PerfRecordSample &record)
636 {
637 quint32 waste32;
638
639 const quint64 sampleType = record.m_sampleId.sampleType();
640
641 if (sampleType & PerfEventAttributes::SAMPLE_IDENTIFIER)
642 stream >> record.m_sampleId.m_id;
643 if (sampleType & PerfEventAttributes::SAMPLE_IP)
644 stream >> record.m_ip;
645 if (sampleType & PerfEventAttributes::SAMPLE_TID)
646 stream >> record.m_sampleId.m_pid >> record.m_sampleId.m_tid;
647 if (sampleType & PerfEventAttributes::SAMPLE_TIME)
648 stream >> record.m_sampleId.m_time;
649 if (sampleType & PerfEventAttributes::SAMPLE_ADDR)
650 stream >> record.m_addr;
651 if (sampleType & PerfEventAttributes::SAMPLE_ID)
652 stream >> record.m_sampleId.m_id; // It's the same as identifier
653 if (sampleType & PerfEventAttributes::SAMPLE_STREAM_ID)
654 stream >> record.m_sampleId.m_streamId;
655 if (sampleType & PerfEventAttributes::SAMPLE_CPU)
656 stream >> record.m_sampleId.m_cpu >> waste32;
657 if (sampleType & PerfEventAttributes::SAMPLE_PERIOD)
658 stream >> record.m_period;
659
660 if (sampleType & PerfEventAttributes::SAMPLE_READ) {
661 quint64 numFormats = 1;
662 PerfRecordSample::ReadFormat format;
663 if (record.m_readFormat & PerfEventAttributes::FORMAT_GROUP)
664 stream >> numFormats;
665 else
666 stream >> format.value;
667
668 if (record.m_readFormat & PerfEventAttributes::FORMAT_TOTAL_TIME_ENABLED)
669 stream >> record.m_timeEnabled;
670 if (record.m_readFormat & PerfEventAttributes::FORMAT_TOTAL_TIME_RUNNING)
671 stream >> record.m_timeRunning;
672
673 if (record.m_readFormat & PerfEventAttributes::FORMAT_GROUP) {
674 while (numFormats-- > 0) {
675 stream >> format.value >> format.id;
676 record.m_readFormats << format;
677 }
678 } else {
679 stream >> format.id;
680 record.m_readFormats << format;
681 }
682 }
683
684 if (sampleType & PerfEventAttributes::SAMPLE_CALLCHAIN) {
685 quint64 numIps;
686 quint64 ip;
687 stream >> numIps;
688 while (numIps-- > 0) {
689 stream >> ip;
690 record.m_callchain << ip;
691 }
692 }
693
694 if (sampleType & PerfEventAttributes::SAMPLE_RAW) {
695 quint32 rawSize;
696 stream >> rawSize;
697 if (rawSize > intMax) {
698 qWarning() << "Excessively long raw data section" << rawSize;
699 stream.skipRawData(intMax);
700 stream.skipRawData(static_cast<int>(rawSize - intMax));
701 } else {
702 record.m_rawData.resize(static_cast<int>(rawSize));
703 stream.readRawData(record.m_rawData.data(), record.m_rawData.length());
704 }
705 }
706
707 if (sampleType & PerfEventAttributes::SAMPLE_BRANCH_STACK) {
708 quint64 numBranches;
709 stream >> numBranches;
710 PerfRecordSample::BranchEntry entry;
711 while (numBranches-- > 0) {
712 stream >> entry.from >> entry.to;
713 stream.readRawData(reinterpret_cast<char*>(&entry.flags), sizeof(entry.flags));
714 record.m_branchStack << entry;
715 }
716 }
717
718 if (sampleType & PerfEventAttributes::SAMPLE_REGS_USER) {
719 quint64 reg;
720 stream >> record.m_registerAbi;
721 if (record.m_registerAbi) {
722 for (uint i = qPopulationCount(record.m_registerMask); i > 0; --i) {
723 stream >> reg;
724 record.m_registers << reg;
725 }
726 }
727 }
728
729 if (sampleType & PerfEventAttributes::SAMPLE_STACK_USER) {
730 quint64 sectionSize;
731 stream >> sectionSize;
732
733 if (sectionSize > intMax) {
734 // We don't accept stack samples of > 2G, sorry ...
735 qWarning() << "Excessively large stack snapshot" << sectionSize;
736 do {
737 stream.skipRawData(intMax);
738 sectionSize -= intMax;
739 } while (sectionSize > intMax);
740 stream.skipRawData(static_cast<int>(sectionSize));
741 sectionSize = 0;
742 stream.skipRawData(sizeof(quint64)); // skip contentSize
743 } else if (sectionSize > 0) {
744 record.m_userStack.resize(static_cast<int>(sectionSize));
745 stream.readRawData(record.m_userStack.data(), record.m_userStack.size());
746
747 quint64 contentSize;
748 stream >> contentSize;
749 if (contentSize > sectionSize)
750 qWarning() << "Truncated stack snapshot" << contentSize << sectionSize;
751 else
752 record.m_userStack.resize(static_cast<int>(contentSize));
753 }
754 }
755
756 if (sampleType & PerfEventAttributes::SAMPLE_WEIGHT)
757 stream >> record.m_weight;
758
759 if (sampleType & PerfEventAttributes::SAMPLE_DATA_SRC)
760 stream >> record.m_dataSrc;
761
762 if (sampleType & PerfEventAttributes::SAMPLE_TRANSACTION)
763 stream >> record.m_transaction;
764
765 return stream;
766 }
767
768
PerfRecordAttr(const PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)769 PerfRecordAttr::PerfRecordAttr(const PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
770 PerfRecord(header, sampleType, sampleIdAll)
771 {
772 }
773
PerfRecordAttr(const PerfEventAttributes & attributes,const QList<quint64> & ids)774 PerfRecordAttr::PerfRecordAttr(const PerfEventAttributes &attributes, const QList<quint64> &ids) :
775 PerfRecord(nullptr, 0, false), m_attr(attributes), m_ids(ids)
776 {
777 }
778
operator >>(QDataStream & stream,PerfRecordAttr & record)779 QDataStream &operator>>(QDataStream &stream, PerfRecordAttr &record)
780 {
781 stream >> record.m_attr;
782 quint32 read = record.m_attr.size() + PerfEventHeader::fixedLength();
783 quint64 id = 0;
784 for (quint32 i = 0; i < (record.m_header.size - read) / sizeof(quint64); ++i) {
785 stream >> id;
786 record.m_ids << id;
787 }
788 return stream;
789 }
790
791
PerfRecordFork(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)792 PerfRecordFork::PerfRecordFork(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
793 PerfRecord(header, sampleType, sampleIdAll), m_pid(0), m_ppid(0), m_tid(0), m_ptid(0), m_time(0)
794 {
795 }
796
operator >>(QDataStream & stream,PerfRecordFork & record)797 QDataStream &operator>>(QDataStream &stream, PerfRecordFork &record)
798 {
799 return stream >> record.m_pid >> record.m_ppid >> record.m_tid >> record.m_ptid >> record.m_time
800 >> record.m_sampleId;
801 }
802
PerfRecordContextSwitch(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)803 PerfRecordContextSwitch::PerfRecordContextSwitch(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
804 PerfRecord(header, sampleType, sampleIdAll)
805 {
806 }
807
operator >>(QDataStream & stream,PerfRecordContextSwitch & record)808 QDataStream &operator>>(QDataStream &stream, PerfRecordContextSwitch &record)
809 {
810 return stream >> record.m_sampleId;
811 }
812
PerfRecordContextSwitchCpuWide(PerfEventHeader * header,quint64 sampleType,bool sampleIdAll)813 PerfRecordContextSwitchCpuWide::PerfRecordContextSwitchCpuWide(PerfEventHeader *header, quint64 sampleType, bool sampleIdAll) :
814 PerfRecordContextSwitch(header, sampleType, sampleIdAll)
815 {
816 }
817
operator >>(QDataStream & stream,PerfRecordContextSwitchCpuWide & record)818 QDataStream &operator>>(QDataStream &stream, PerfRecordContextSwitchCpuWide &record)
819 {
820 return stream >> record.m_nextPrevPid >> record.m_nextPrevTid >> record.m_sampleId;
821 }
822