2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 #include <unordered_map>
23 #include <android-base/logging.h>
24 #include <android-base/stringprintf.h>
26 #include "environment.h"
27 #include "perf_regs.h"
30 static std::string RecordTypeToString(int record_type) {
31 static std::unordered_map<int, std::string> record_type_names = {
32 {PERF_RECORD_MMAP, "mmap"}, {PERF_RECORD_LOST, "lost"},
33 {PERF_RECORD_COMM, "comm"}, {PERF_RECORD_EXIT, "exit"},
34 {PERF_RECORD_THROTTLE, "throttle"}, {PERF_RECORD_UNTHROTTLE, "unthrottle"},
35 {PERF_RECORD_FORK, "fork"}, {PERF_RECORD_READ, "read"},
36 {PERF_RECORD_SAMPLE, "sample"}, {PERF_RECORD_BUILD_ID, "build_id"},
37 {PERF_RECORD_MMAP2, "mmap2"},
40 auto it = record_type_names.find(record_type);
41 if (it != record_type_names.end()) {
44 return android::base::StringPrintf("unknown(%d)", record_type);
48 void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) {
49 size_t size = n * sizeof(T);
50 memcpy(data_p, p, size);
55 void MoveToBinaryFormat(const T& data, char*& p) {
56 *reinterpret_cast<T*>(p) = data;
61 void MoveToBinaryFormat(const T* data_p, size_t n, char*& p) {
62 size_t size = n * sizeof(T);
63 memcpy(p, data_p, size);
67 SampleId::SampleId() {
68 memset(this, 0, sizeof(SampleId));
71 // Return sample_id size in binary format.
72 size_t SampleId::CreateContent(const perf_event_attr& attr) {
73 sample_id_all = attr.sample_id_all;
74 sample_type = attr.sample_type;
75 // Other data are not necessary. TODO: Set missing SampleId data.
78 if (sample_type & PERF_SAMPLE_TID) {
79 size += sizeof(PerfSampleTidType);
81 if (sample_type & PERF_SAMPLE_TIME) {
82 size += sizeof(PerfSampleTimeType);
84 if (sample_type & PERF_SAMPLE_ID) {
85 size += sizeof(PerfSampleIdType);
87 if (sample_type & PERF_SAMPLE_STREAM_ID) {
88 size += sizeof(PerfSampleStreamIdType);
90 if (sample_type & PERF_SAMPLE_CPU) {
91 size += sizeof(PerfSampleCpuType);
97 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
98 sample_id_all = attr.sample_id_all;
99 sample_type = attr.sample_type;
101 if (sample_type & PERF_SAMPLE_TID) {
102 MoveFromBinaryFormat(tid_data, p);
104 if (sample_type & PERF_SAMPLE_TIME) {
105 MoveFromBinaryFormat(time_data, p);
107 if (sample_type & PERF_SAMPLE_ID) {
108 MoveFromBinaryFormat(id_data, p);
110 if (sample_type & PERF_SAMPLE_STREAM_ID) {
111 MoveFromBinaryFormat(stream_id_data, p);
113 if (sample_type & PERF_SAMPLE_CPU) {
114 MoveFromBinaryFormat(cpu_data, p);
116 // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER.
120 LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n";
124 void SampleId::WriteToBinaryFormat(char*& p) const {
126 if (sample_type & PERF_SAMPLE_TID) {
127 MoveToBinaryFormat(tid_data, p);
129 if (sample_type & PERF_SAMPLE_TIME) {
130 MoveToBinaryFormat(time_data, p);
132 if (sample_type & PERF_SAMPLE_ID) {
133 MoveToBinaryFormat(id_data, p);
135 if (sample_type & PERF_SAMPLE_STREAM_ID) {
136 MoveToBinaryFormat(stream_id_data, p);
138 if (sample_type & PERF_SAMPLE_CPU) {
139 MoveToBinaryFormat(cpu_data, p);
144 void SampleId::Dump(size_t indent) const {
146 if (sample_type & PERF_SAMPLE_TID) {
147 PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid);
149 if (sample_type & PERF_SAMPLE_TIME) {
150 PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
152 if (sample_type & PERF_SAMPLE_ID) {
153 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id);
155 if (sample_type & PERF_SAMPLE_STREAM_ID) {
156 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id);
158 if (sample_type & PERF_SAMPLE_CPU) {
159 PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
165 memset(&header, 0, sizeof(header));
168 Record::Record(const perf_event_header* pheader) {
172 void Record::Dump(size_t indent) const {
173 PrintIndented(indent, "record %s: type %u, misc %u, size %u\n",
174 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size);
175 DumpData(indent + 1);
176 sample_id.Dump(indent + 1);
179 uint64_t Record::Timestamp() const {
180 return sample_id.time_data.time;
183 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
185 const char* p = reinterpret_cast<const char*>(pheader + 1);
186 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
187 MoveFromBinaryFormat(data, p);
189 p += ALIGN(filename.size() + 1, 8);
191 sample_id.ReadFromBinaryFormat(attr, p, end);
194 std::vector<char> MmapRecord::BinaryFormat() const {
195 std::vector<char> buf(header.size);
196 char* p = buf.data();
197 MoveToBinaryFormat(header, p);
198 MoveToBinaryFormat(data, p);
199 strcpy(p, filename.c_str());
200 p += ALIGN(filename.size() + 1, 8);
201 sample_id.WriteToBinaryFormat(p);
205 void MmapRecord::DumpData(size_t indent) const {
206 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
207 data.tid, data.addr, data.len);
208 PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
211 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader)
213 const char* p = reinterpret_cast<const char*>(pheader + 1);
214 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
215 MoveFromBinaryFormat(data, p);
217 p += ALIGN(filename.size() + 1, 8);
219 sample_id.ReadFromBinaryFormat(attr, p, end);
222 std::vector<char> Mmap2Record::BinaryFormat() const {
223 std::vector<char> buf(header.size);
224 char* p = buf.data();
225 MoveToBinaryFormat(header, p);
226 MoveToBinaryFormat(data, p);
227 strcpy(p, filename.c_str());
228 p += ALIGN(filename.size() + 1, 8);
229 sample_id.WriteToBinaryFormat(p);
233 void Mmap2Record::DumpData(size_t indent) const {
234 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
235 data.tid, data.addr, data.len);
236 PrintIndented(indent,
237 "pgoff 0x" PRIx64 ", maj %u, min %u, ino %" PRId64 ", ino_generation %" PRIu64 "\n",
238 data.pgoff, data.maj, data.min, data.ino, data.ino_generation);
239 PrintIndented(indent, "prot %u, flags %u, filenames %s\n", data.prot, data.flags,
243 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader)
245 const char* p = reinterpret_cast<const char*>(pheader + 1);
246 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
247 MoveFromBinaryFormat(data, p);
249 p += ALIGN(strlen(p) + 1, 8);
251 sample_id.ReadFromBinaryFormat(attr, p, end);
254 std::vector<char> CommRecord::BinaryFormat() const {
255 std::vector<char> buf(header.size);
256 char* p = buf.data();
257 MoveToBinaryFormat(header, p);
258 MoveToBinaryFormat(data, p);
259 strcpy(p, comm.c_str());
260 p += ALIGN(comm.size() + 1, 8);
261 sample_id.WriteToBinaryFormat(p);
265 void CommRecord::DumpData(size_t indent) const {
266 PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
269 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
271 const char* p = reinterpret_cast<const char*>(pheader + 1);
272 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
273 MoveFromBinaryFormat(data, p);
275 sample_id.ReadFromBinaryFormat(attr, p, end);
278 std::vector<char> ExitOrForkRecord::BinaryFormat() const {
279 std::vector<char> buf(header.size);
280 char* p = buf.data();
281 MoveToBinaryFormat(header, p);
282 MoveToBinaryFormat(data, p);
283 sample_id.WriteToBinaryFormat(p);
287 void ExitOrForkRecord::DumpData(size_t indent) const {
288 PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
292 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
294 const char* p = reinterpret_cast<const char*>(pheader + 1);
295 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
296 sample_type = attr.sample_type;
298 if (sample_type & PERF_SAMPLE_IP) {
299 MoveFromBinaryFormat(ip_data, p);
301 if (sample_type & PERF_SAMPLE_TID) {
302 MoveFromBinaryFormat(tid_data, p);
304 if (sample_type & PERF_SAMPLE_TIME) {
305 MoveFromBinaryFormat(time_data, p);
307 if (sample_type & PERF_SAMPLE_ADDR) {
308 MoveFromBinaryFormat(addr_data, p);
310 if (sample_type & PERF_SAMPLE_ID) {
311 MoveFromBinaryFormat(id_data, p);
313 if (sample_type & PERF_SAMPLE_STREAM_ID) {
314 MoveFromBinaryFormat(stream_id_data, p);
316 if (sample_type & PERF_SAMPLE_CPU) {
317 MoveFromBinaryFormat(cpu_data, p);
319 if (sample_type & PERF_SAMPLE_PERIOD) {
320 MoveFromBinaryFormat(period_data, p);
322 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
324 MoveFromBinaryFormat(nr, p);
325 callchain_data.ips.resize(nr);
326 MoveFromBinaryFormat(callchain_data.ips.data(), nr, p);
328 if (sample_type & PERF_SAMPLE_RAW) {
330 MoveFromBinaryFormat(size, p);
331 raw_data.data.resize(size);
332 MoveFromBinaryFormat(raw_data.data.data(), size, p);
334 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
336 MoveFromBinaryFormat(nr, p);
337 branch_stack_data.stack.resize(nr);
338 MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
340 if (sample_type & PERF_SAMPLE_REGS_USER) {
341 MoveFromBinaryFormat(regs_user_data.abi, p);
342 if (regs_user_data.abi == 0) {
343 regs_user_data.reg_mask = 0;
345 regs_user_data.reg_mask = attr.sample_regs_user;
347 for (size_t i = 0; i < 64; ++i) {
348 if ((regs_user_data.reg_mask >> i) & 1) {
352 regs_user_data.regs.resize(bit_nr);
353 MoveFromBinaryFormat(regs_user_data.regs.data(), bit_nr, p);
356 if (sample_type & PERF_SAMPLE_STACK_USER) {
358 MoveFromBinaryFormat(size, p);
360 stack_user_data.dyn_size = 0;
362 stack_user_data.data.resize(size);
363 MoveFromBinaryFormat(stack_user_data.data.data(), size, p);
364 MoveFromBinaryFormat(stack_user_data.dyn_size, p);
367 // TODO: Add parsing of other PERF_SAMPLE_*.
370 LOG(DEBUG) << "Record has " << end - p << " bytes left\n";
374 std::vector<char> SampleRecord::BinaryFormat() const {
375 std::vector<char> buf(header.size);
376 char* p = buf.data();
377 MoveToBinaryFormat(header, p);
378 if (sample_type & PERF_SAMPLE_IP) {
379 MoveToBinaryFormat(ip_data, p);
381 if (sample_type & PERF_SAMPLE_TID) {
382 MoveToBinaryFormat(tid_data, p);
384 if (sample_type & PERF_SAMPLE_TIME) {
385 MoveToBinaryFormat(time_data, p);
387 if (sample_type & PERF_SAMPLE_ADDR) {
388 MoveToBinaryFormat(addr_data, p);
390 if (sample_type & PERF_SAMPLE_ID) {
391 MoveToBinaryFormat(id_data, p);
393 if (sample_type & PERF_SAMPLE_STREAM_ID) {
394 MoveToBinaryFormat(stream_id_data, p);
396 if (sample_type & PERF_SAMPLE_CPU) {
397 MoveToBinaryFormat(cpu_data, p);
399 if (sample_type & PERF_SAMPLE_PERIOD) {
400 MoveToBinaryFormat(period_data, p);
402 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
403 uint64_t nr = callchain_data.ips.size();
404 MoveToBinaryFormat(nr, p);
405 MoveToBinaryFormat(callchain_data.ips.data(), nr, p);
407 if (sample_type & PERF_SAMPLE_RAW) {
408 uint32_t size = raw_data.data.size();
409 MoveToBinaryFormat(size, p);
410 MoveToBinaryFormat(raw_data.data.data(), size, p);
412 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
413 uint64_t nr = branch_stack_data.stack.size();
414 MoveToBinaryFormat(nr, p);
415 MoveToBinaryFormat(branch_stack_data.stack.data(), nr, p);
417 if (sample_type & PERF_SAMPLE_REGS_USER) {
418 MoveToBinaryFormat(regs_user_data.abi, p);
419 if (regs_user_data.abi != 0) {
420 MoveToBinaryFormat(regs_user_data.regs.data(), regs_user_data.regs.size(), p);
423 if (sample_type & PERF_SAMPLE_STACK_USER) {
424 uint64_t size = stack_user_data.data.size();
425 MoveToBinaryFormat(size, p);
427 MoveToBinaryFormat(stack_user_data.data.data(), size, p);
428 MoveToBinaryFormat(stack_user_data.dyn_size, p);
432 // If record command does stack unwinding, sample records' size may be decreased.
433 // So we can't trust header.size here, and should adjust buffer size based on real need.
434 buf.resize(p - buf.data());
438 void SampleRecord::AdjustSizeBasedOnData() {
439 size_t size = BinaryFormat().size();
440 LOG(DEBUG) << "SampleRecord size is changed from " << header.size << " to " << size;
444 void SampleRecord::DumpData(size_t indent) const {
445 PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
446 if (sample_type & PERF_SAMPLE_IP) {
447 PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip));
449 if (sample_type & PERF_SAMPLE_TID) {
450 PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid);
452 if (sample_type & PERF_SAMPLE_TIME) {
453 PrintIndented(indent, "time %" PRId64 "\n", time_data.time);
455 if (sample_type & PERF_SAMPLE_ADDR) {
456 PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr));
458 if (sample_type & PERF_SAMPLE_ID) {
459 PrintIndented(indent, "id %" PRId64 "\n", id_data.id);
461 if (sample_type & PERF_SAMPLE_STREAM_ID) {
462 PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id);
464 if (sample_type & PERF_SAMPLE_CPU) {
465 PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
467 if (sample_type & PERF_SAMPLE_PERIOD) {
468 PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
470 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
471 PrintIndented(indent, "callchain nr=%" PRIu64 "\n", callchain_data.ips.size());
472 for (auto& ip : callchain_data.ips) {
473 PrintIndented(indent + 1, "0x%" PRIx64 "\n", ip);
476 if (sample_type & PERF_SAMPLE_RAW) {
477 PrintIndented(indent, "raw size=%zu\n", raw_data.data.size());
478 const uint32_t* data = reinterpret_cast<const uint32_t*>(raw_data.data.data());
479 size_t size = raw_data.data.size() / sizeof(uint32_t);
480 for (size_t i = 0; i < size; ++i) {
481 PrintIndented(indent + 1, "0x%08x (%zu)\n", data[i], data[i]);
484 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
485 PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
486 for (auto& item : branch_stack_data.stack) {
487 PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
488 item.from, item.to, item.flags);
491 if (sample_type & PERF_SAMPLE_REGS_USER) {
492 PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi);
493 for (size_t i = 0, pos = 0; i < 64; ++i) {
494 if ((regs_user_data.reg_mask >> i) & 1) {
495 PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n", GetRegName(i).c_str(),
496 regs_user_data.regs[pos++]);
500 if (sample_type & PERF_SAMPLE_STACK_USER) {
501 PrintIndented(indent, "user stack: size %zu dyn_size %" PRIu64 "\n",
502 stack_user_data.data.size(), stack_user_data.dyn_size);
503 const uint64_t* p = reinterpret_cast<const uint64_t*>(stack_user_data.data.data());
504 const uint64_t* end = p + (stack_user_data.data.size() / sizeof(uint64_t));
506 PrintIndented(indent + 1, "");
507 for (size_t i = 0; i < 4 && p < end; ++i, ++p) {
508 printf(" %016" PRIx64, *p);
516 uint64_t SampleRecord::Timestamp() const {
517 return time_data.time;
520 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
521 const char* p = reinterpret_cast<const char*>(pheader + 1);
522 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
523 MoveFromBinaryFormat(pid, p);
524 build_id = BuildId(p);
525 p += ALIGN(build_id.Size(), 8);
527 p += ALIGN(filename.size() + 1, 64);
531 std::vector<char> BuildIdRecord::BinaryFormat() const {
532 std::vector<char> buf(header.size);
533 char* p = buf.data();
534 MoveToBinaryFormat(header, p);
535 MoveToBinaryFormat(pid, p);
536 memcpy(p, build_id.Data(), build_id.Size());
537 p += ALIGN(build_id.Size(), 8);
538 strcpy(p, filename.c_str());
539 p += ALIGN(filename.size() + 1, 64);
543 void BuildIdRecord::DumpData(size_t indent) const {
544 PrintIndented(indent, "pid %u\n", pid);
545 PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str());
546 PrintIndented(indent, "filename %s\n", filename.c_str());
549 UnknownRecord::UnknownRecord(const perf_event_header* pheader) : Record(pheader) {
550 const char* p = reinterpret_cast<const char*>(pheader + 1);
551 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
552 data.insert(data.end(), p, end);
555 std::vector<char> UnknownRecord::BinaryFormat() const {
556 std::vector<char> buf(header.size);
557 char* p = buf.data();
558 MoveToBinaryFormat(header, p);
559 MoveToBinaryFormat(data.data(), data.size(), p);
563 void UnknownRecord::DumpData(size_t) const {
566 static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
567 const perf_event_header* pheader) {
568 switch (pheader->type) {
569 case PERF_RECORD_MMAP:
570 return std::unique_ptr<Record>(new MmapRecord(attr, pheader));
571 case PERF_RECORD_MMAP2:
572 return std::unique_ptr<Record>(new Mmap2Record(attr, pheader));
573 case PERF_RECORD_COMM:
574 return std::unique_ptr<Record>(new CommRecord(attr, pheader));
575 case PERF_RECORD_EXIT:
576 return std::unique_ptr<Record>(new ExitRecord(attr, pheader));
577 case PERF_RECORD_FORK:
578 return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
579 case PERF_RECORD_SAMPLE:
580 return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
582 return std::unique_ptr<Record>(new UnknownRecord(pheader));
586 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr,
587 const char* buf, size_t buf_size) {
588 std::vector<std::unique_ptr<Record>> result;
590 const char* end = buf + buf_size;
592 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
593 CHECK_LE(p + header->size, end);
594 CHECK_NE(0u, header->size);
595 result.push_back(ReadRecordFromBuffer(attr, header));
601 std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) {
602 std::vector<char> buf(sizeof(perf_event_header));
603 perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
604 if (fread(header, sizeof(perf_event_header), 1, fp) != 1) {
605 PLOG(ERROR) << "Failed to read record file";
608 buf.resize(header->size);
609 header = reinterpret_cast<perf_event_header*>(&buf[0]);
610 if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) {
611 PLOG(ERROR) << "Failed to read record file";
614 return ReadRecordFromBuffer(attr, header);
617 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
618 uint64_t addr, uint64_t len, uint64_t pgoff,
619 const std::string& filename) {
621 record.header.type = PERF_RECORD_MMAP;
622 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
623 record.data.pid = pid;
624 record.data.tid = tid;
625 record.data.addr = addr;
626 record.data.len = len;
627 record.data.pgoff = pgoff;
628 record.filename = filename;
629 size_t sample_id_size = record.sample_id.CreateContent(attr);
630 record.header.size = sizeof(record.header) + sizeof(record.data) +
631 ALIGN(record.filename.size() + 1, 8) + sample_id_size;
635 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
636 const std::string& comm) {
638 record.header.type = PERF_RECORD_COMM;
639 record.header.misc = 0;
640 record.data.pid = pid;
641 record.data.tid = tid;
643 size_t sample_id_size = record.sample_id.CreateContent(attr);
644 record.header.size = sizeof(record.header) + sizeof(record.data) +
645 ALIGN(record.comm.size() + 1, 8) + sample_id_size;
649 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
652 record.header.type = PERF_RECORD_FORK;
653 record.header.misc = 0;
654 record.data.pid = pid;
655 record.data.ppid = ppid;
656 record.data.tid = tid;
657 record.data.ptid = ptid;
658 record.data.time = 0;
659 size_t sample_id_size = record.sample_id.CreateContent(attr);
660 record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size;
664 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
665 const std::string& filename) {
666 BuildIdRecord record;
667 record.header.type = PERF_RECORD_BUILD_ID;
668 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
670 record.build_id = build_id;
671 record.filename = filename;
672 record.header.size = sizeof(record.header) + sizeof(record.pid) +
673 ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64);
677 bool RecordCache::RecordWithSeq::IsHappensBefore(const RecordWithSeq& other) const {
678 bool is_sample = (record->header.type == PERF_RECORD_SAMPLE);
679 bool is_other_sample = (other.record->header.type == PERF_RECORD_SAMPLE);
680 uint64_t time = record->Timestamp();
681 uint64_t other_time = other.record->Timestamp();
682 // The record with smaller time happens first.
683 if (time != other_time) {
684 return time < other_time;
686 // If happening at the same time, make non-sample records before sample records,
687 // because non-sample records may contain useful information to parse sample records.
688 if (is_sample != is_other_sample) {
689 return is_sample ? false : true;
691 // Otherwise, use the same order as they enter the cache.
692 return seq < other.seq;
695 bool RecordCache::RecordComparator::operator()(const RecordWithSeq& r1,
696 const RecordWithSeq& r2) {
697 return r2.IsHappensBefore(r1);
700 RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size,
701 uint64_t min_time_diff_in_ns)
703 has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)),
704 min_cache_size_(min_cache_size),
705 min_time_diff_in_ns_(min_time_diff_in_ns),
708 queue_(RecordComparator()) {
711 RecordCache::~RecordCache() {
715 void RecordCache::Push(const char* data, size_t size) {
716 std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size);
717 if (has_timestamp_) {
718 for (const auto& r : records) {
719 last_time_ = std::max(last_time_, r->Timestamp());
722 for (auto& r : records) {
723 queue_.push(CreateRecordWithSeq(r.release()));
727 void RecordCache::Push(std::unique_ptr<Record> record) {
728 queue_.push(CreateRecordWithSeq(record.release()));
731 std::unique_ptr<Record> RecordCache::Pop() {
732 if (queue_.size() < min_cache_size_) {
735 Record* r = queue_.top().record;
736 if (has_timestamp_) {
737 if (r->Timestamp() + min_time_diff_in_ns_ > last_time_) {
742 return std::unique_ptr<Record>(r);
745 std::vector<std::unique_ptr<Record>> RecordCache::PopAll() {
746 std::vector<std::unique_ptr<Record>> result;
747 while (!queue_.empty()) {
748 result.emplace_back(queue_.top().record);
754 RecordCache::RecordWithSeq RecordCache::CreateRecordWithSeq(Record *r) {
755 RecordWithSeq result;
756 result.seq = cur_seq_++;