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