1 /*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 #include "record.h"
18
19 #include <inttypes.h>
20 #include <algorithm>
21 #include <unordered_map>
22
23 #include <android-base/logging.h>
24 #include <android-base/stringprintf.h>
25
26 #include "environment.h"
27 #include "perf_regs.h"
28 #include "utils.h"
29
RecordTypeToString(int record_type)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"},
38 };
39
40 auto it = record_type_names.find(record_type);
41 if (it != record_type_names.end()) {
42 return it->second;
43 }
44 return android::base::StringPrintf("unknown(%d)", record_type);
45 }
46
47 template <class T>
MoveFromBinaryFormat(T * data_p,size_t n,const char * & p)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);
51 p += size;
52 }
53
54 template <class T>
MoveToBinaryFormat(const T & data,char * & p)55 void MoveToBinaryFormat(const T& data, char*& p) {
56 *reinterpret_cast<T*>(p) = data;
57 p += sizeof(T);
58 }
59
60 template <class T>
MoveToBinaryFormat(const T * data_p,size_t n,char * & p)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);
64 p += size;
65 }
66
SampleId()67 SampleId::SampleId() {
68 memset(this, 0, sizeof(SampleId));
69 }
70
71 // Return sample_id size in binary format.
CreateContent(const perf_event_attr & attr)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.
76 return Size();
77 }
78
ReadFromBinaryFormat(const perf_event_attr & attr,const char * p,const char * end)79 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
80 sample_id_all = attr.sample_id_all;
81 sample_type = attr.sample_type;
82 if (sample_id_all) {
83 if (sample_type & PERF_SAMPLE_TID) {
84 MoveFromBinaryFormat(tid_data, p);
85 }
86 if (sample_type & PERF_SAMPLE_TIME) {
87 MoveFromBinaryFormat(time_data, p);
88 }
89 if (sample_type & PERF_SAMPLE_ID) {
90 MoveFromBinaryFormat(id_data, p);
91 }
92 if (sample_type & PERF_SAMPLE_STREAM_ID) {
93 MoveFromBinaryFormat(stream_id_data, p);
94 }
95 if (sample_type & PERF_SAMPLE_CPU) {
96 MoveFromBinaryFormat(cpu_data, p);
97 }
98 // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER.
99 }
100 CHECK_LE(p, end);
101 if (p < end) {
102 LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n";
103 }
104 }
105
WriteToBinaryFormat(char * & p) const106 void SampleId::WriteToBinaryFormat(char*& p) const {
107 if (sample_id_all) {
108 if (sample_type & PERF_SAMPLE_TID) {
109 MoveToBinaryFormat(tid_data, p);
110 }
111 if (sample_type & PERF_SAMPLE_TIME) {
112 MoveToBinaryFormat(time_data, p);
113 }
114 if (sample_type & PERF_SAMPLE_ID) {
115 MoveToBinaryFormat(id_data, p);
116 }
117 if (sample_type & PERF_SAMPLE_STREAM_ID) {
118 MoveToBinaryFormat(stream_id_data, p);
119 }
120 if (sample_type & PERF_SAMPLE_CPU) {
121 MoveToBinaryFormat(cpu_data, p);
122 }
123 }
124 }
125
Dump(size_t indent) const126 void SampleId::Dump(size_t indent) const {
127 if (sample_id_all) {
128 if (sample_type & PERF_SAMPLE_TID) {
129 PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid);
130 }
131 if (sample_type & PERF_SAMPLE_TIME) {
132 PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
133 }
134 if (sample_type & PERF_SAMPLE_ID) {
135 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id);
136 }
137 if (sample_type & PERF_SAMPLE_STREAM_ID) {
138 PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id);
139 }
140 if (sample_type & PERF_SAMPLE_CPU) {
141 PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
142 }
143 }
144 }
145
Size() const146 size_t SampleId::Size() const {
147 size_t size = 0;
148 if (sample_id_all) {
149 if (sample_type & PERF_SAMPLE_TID) {
150 size += sizeof(PerfSampleTidType);
151 }
152 if (sample_type & PERF_SAMPLE_TIME) {
153 size += sizeof(PerfSampleTimeType);
154 }
155 if (sample_type & PERF_SAMPLE_ID) {
156 size += sizeof(PerfSampleIdType);
157 }
158 if (sample_type & PERF_SAMPLE_STREAM_ID) {
159 size += sizeof(PerfSampleStreamIdType);
160 }
161 if (sample_type & PERF_SAMPLE_CPU) {
162 size += sizeof(PerfSampleCpuType);
163 }
164 }
165 return size;
166 }
167
Record()168 Record::Record() {
169 memset(&header, 0, sizeof(header));
170 }
171
Record(const perf_event_header * pheader)172 Record::Record(const perf_event_header* pheader) {
173 header = *pheader;
174 }
175
Dump(size_t indent) const176 void Record::Dump(size_t indent) const {
177 PrintIndented(indent, "record %s: type %u, misc %u, size %u\n",
178 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size);
179 DumpData(indent + 1);
180 sample_id.Dump(indent + 1);
181 }
182
Timestamp() const183 uint64_t Record::Timestamp() const {
184 return sample_id.time_data.time;
185 }
186
MmapRecord(const perf_event_attr & attr,const perf_event_header * pheader)187 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
188 : Record(pheader) {
189 const char* p = reinterpret_cast<const char*>(pheader + 1);
190 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
191 MoveFromBinaryFormat(data, p);
192 filename = p;
193 p += ALIGN(filename.size() + 1, 8);
194 CHECK_LE(p, end);
195 sample_id.ReadFromBinaryFormat(attr, p, end);
196 }
197
BinaryFormat() const198 std::vector<char> MmapRecord::BinaryFormat() const {
199 std::vector<char> buf(header.size);
200 char* p = buf.data();
201 MoveToBinaryFormat(header, p);
202 MoveToBinaryFormat(data, p);
203 strcpy(p, filename.c_str());
204 p += ALIGN(filename.size() + 1, 8);
205 sample_id.WriteToBinaryFormat(p);
206 return buf;
207 }
208
AdjustSizeBasedOnData()209 void MmapRecord::AdjustSizeBasedOnData() {
210 header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
211 }
212
DumpData(size_t indent) const213 void MmapRecord::DumpData(size_t indent) const {
214 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
215 data.tid, data.addr, data.len);
216 PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
217 }
218
Mmap2Record(const perf_event_attr & attr,const perf_event_header * pheader)219 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader)
220 : Record(pheader) {
221 const char* p = reinterpret_cast<const char*>(pheader + 1);
222 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
223 MoveFromBinaryFormat(data, p);
224 filename = p;
225 p += ALIGN(filename.size() + 1, 8);
226 CHECK_LE(p, end);
227 sample_id.ReadFromBinaryFormat(attr, p, end);
228 }
229
BinaryFormat() const230 std::vector<char> Mmap2Record::BinaryFormat() const {
231 std::vector<char> buf(header.size);
232 char* p = buf.data();
233 MoveToBinaryFormat(header, p);
234 MoveToBinaryFormat(data, p);
235 strcpy(p, filename.c_str());
236 p += ALIGN(filename.size() + 1, 8);
237 sample_id.WriteToBinaryFormat(p);
238 return buf;
239 }
240
AdjustSizeBasedOnData()241 void Mmap2Record::AdjustSizeBasedOnData() {
242 header.size = sizeof(header) + sizeof(data) + ALIGN(filename.size() + 1, 8) + sample_id.Size();
243 }
244
DumpData(size_t indent) const245 void Mmap2Record::DumpData(size_t indent) const {
246 PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
247 data.tid, data.addr, data.len);
248 PrintIndented(indent,
249 "pgoff 0x" PRIx64 ", maj %u, min %u, ino %" PRId64 ", ino_generation %" PRIu64 "\n",
250 data.pgoff, data.maj, data.min, data.ino, data.ino_generation);
251 PrintIndented(indent, "prot %u, flags %u, filenames %s\n", data.prot, data.flags,
252 filename.c_str());
253 }
254
CommRecord(const perf_event_attr & attr,const perf_event_header * pheader)255 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader)
256 : Record(pheader) {
257 const char* p = reinterpret_cast<const char*>(pheader + 1);
258 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
259 MoveFromBinaryFormat(data, p);
260 comm = p;
261 p += ALIGN(strlen(p) + 1, 8);
262 CHECK_LE(p, end);
263 sample_id.ReadFromBinaryFormat(attr, p, end);
264 }
265
BinaryFormat() const266 std::vector<char> CommRecord::BinaryFormat() const {
267 std::vector<char> buf(header.size);
268 char* p = buf.data();
269 MoveToBinaryFormat(header, p);
270 MoveToBinaryFormat(data, p);
271 strcpy(p, comm.c_str());
272 p += ALIGN(comm.size() + 1, 8);
273 sample_id.WriteToBinaryFormat(p);
274 return buf;
275 }
276
DumpData(size_t indent) const277 void CommRecord::DumpData(size_t indent) const {
278 PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
279 }
280
ExitOrForkRecord(const perf_event_attr & attr,const perf_event_header * pheader)281 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
282 : Record(pheader) {
283 const char* p = reinterpret_cast<const char*>(pheader + 1);
284 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
285 MoveFromBinaryFormat(data, p);
286 CHECK_LE(p, end);
287 sample_id.ReadFromBinaryFormat(attr, p, end);
288 }
289
BinaryFormat() const290 std::vector<char> ExitOrForkRecord::BinaryFormat() const {
291 std::vector<char> buf(header.size);
292 char* p = buf.data();
293 MoveToBinaryFormat(header, p);
294 MoveToBinaryFormat(data, p);
295 sample_id.WriteToBinaryFormat(p);
296 return buf;
297 }
298
DumpData(size_t indent) const299 void ExitOrForkRecord::DumpData(size_t indent) const {
300 PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
301 data.ptid);
302 }
303
SampleRecord(const perf_event_attr & attr,const perf_event_header * pheader)304 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
305 : Record(pheader) {
306 const char* p = reinterpret_cast<const char*>(pheader + 1);
307 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
308 sample_type = attr.sample_type;
309
310 if (sample_type & PERF_SAMPLE_IP) {
311 MoveFromBinaryFormat(ip_data, p);
312 }
313 if (sample_type & PERF_SAMPLE_TID) {
314 MoveFromBinaryFormat(tid_data, p);
315 }
316 if (sample_type & PERF_SAMPLE_TIME) {
317 MoveFromBinaryFormat(time_data, p);
318 }
319 if (sample_type & PERF_SAMPLE_ADDR) {
320 MoveFromBinaryFormat(addr_data, p);
321 }
322 if (sample_type & PERF_SAMPLE_ID) {
323 MoveFromBinaryFormat(id_data, p);
324 }
325 if (sample_type & PERF_SAMPLE_STREAM_ID) {
326 MoveFromBinaryFormat(stream_id_data, p);
327 }
328 if (sample_type & PERF_SAMPLE_CPU) {
329 MoveFromBinaryFormat(cpu_data, p);
330 }
331 if (sample_type & PERF_SAMPLE_PERIOD) {
332 MoveFromBinaryFormat(period_data, p);
333 }
334 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
335 uint64_t nr;
336 MoveFromBinaryFormat(nr, p);
337 callchain_data.ips.resize(nr);
338 MoveFromBinaryFormat(callchain_data.ips.data(), nr, p);
339 }
340 if (sample_type & PERF_SAMPLE_RAW) {
341 uint32_t size;
342 MoveFromBinaryFormat(size, p);
343 raw_data.data.resize(size);
344 MoveFromBinaryFormat(raw_data.data.data(), size, p);
345 }
346 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
347 uint64_t nr;
348 MoveFromBinaryFormat(nr, p);
349 branch_stack_data.stack.resize(nr);
350 MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
351 }
352 if (sample_type & PERF_SAMPLE_REGS_USER) {
353 MoveFromBinaryFormat(regs_user_data.abi, p);
354 if (regs_user_data.abi == 0) {
355 regs_user_data.reg_mask = 0;
356 } else {
357 regs_user_data.reg_mask = attr.sample_regs_user;
358 size_t bit_nr = 0;
359 for (size_t i = 0; i < 64; ++i) {
360 if ((regs_user_data.reg_mask >> i) & 1) {
361 bit_nr++;
362 }
363 }
364 regs_user_data.regs.resize(bit_nr);
365 MoveFromBinaryFormat(regs_user_data.regs.data(), bit_nr, p);
366 }
367 }
368 if (sample_type & PERF_SAMPLE_STACK_USER) {
369 uint64_t size;
370 MoveFromBinaryFormat(size, p);
371 if (size == 0) {
372 stack_user_data.dyn_size = 0;
373 } else {
374 stack_user_data.data.resize(size);
375 MoveFromBinaryFormat(stack_user_data.data.data(), size, p);
376 MoveFromBinaryFormat(stack_user_data.dyn_size, p);
377 }
378 }
379 // TODO: Add parsing of other PERF_SAMPLE_*.
380 CHECK_LE(p, end);
381 if (p < end) {
382 LOG(DEBUG) << "Record has " << end - p << " bytes left\n";
383 }
384 }
385
BinaryFormat() const386 std::vector<char> SampleRecord::BinaryFormat() const {
387 std::vector<char> buf(header.size);
388 char* p = buf.data();
389 MoveToBinaryFormat(header, p);
390 if (sample_type & PERF_SAMPLE_IP) {
391 MoveToBinaryFormat(ip_data, p);
392 }
393 if (sample_type & PERF_SAMPLE_TID) {
394 MoveToBinaryFormat(tid_data, p);
395 }
396 if (sample_type & PERF_SAMPLE_TIME) {
397 MoveToBinaryFormat(time_data, p);
398 }
399 if (sample_type & PERF_SAMPLE_ADDR) {
400 MoveToBinaryFormat(addr_data, p);
401 }
402 if (sample_type & PERF_SAMPLE_ID) {
403 MoveToBinaryFormat(id_data, p);
404 }
405 if (sample_type & PERF_SAMPLE_STREAM_ID) {
406 MoveToBinaryFormat(stream_id_data, p);
407 }
408 if (sample_type & PERF_SAMPLE_CPU) {
409 MoveToBinaryFormat(cpu_data, p);
410 }
411 if (sample_type & PERF_SAMPLE_PERIOD) {
412 MoveToBinaryFormat(period_data, p);
413 }
414 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
415 uint64_t nr = callchain_data.ips.size();
416 MoveToBinaryFormat(nr, p);
417 MoveToBinaryFormat(callchain_data.ips.data(), nr, p);
418 }
419 if (sample_type & PERF_SAMPLE_RAW) {
420 uint32_t size = raw_data.data.size();
421 MoveToBinaryFormat(size, p);
422 MoveToBinaryFormat(raw_data.data.data(), size, p);
423 }
424 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
425 uint64_t nr = branch_stack_data.stack.size();
426 MoveToBinaryFormat(nr, p);
427 MoveToBinaryFormat(branch_stack_data.stack.data(), nr, p);
428 }
429 if (sample_type & PERF_SAMPLE_REGS_USER) {
430 MoveToBinaryFormat(regs_user_data.abi, p);
431 if (regs_user_data.abi != 0) {
432 MoveToBinaryFormat(regs_user_data.regs.data(), regs_user_data.regs.size(), p);
433 }
434 }
435 if (sample_type & PERF_SAMPLE_STACK_USER) {
436 uint64_t size = stack_user_data.data.size();
437 MoveToBinaryFormat(size, p);
438 if (size != 0) {
439 MoveToBinaryFormat(stack_user_data.data.data(), size, p);
440 MoveToBinaryFormat(stack_user_data.dyn_size, p);
441 }
442 }
443
444 // If record command does stack unwinding, sample records' size may be decreased.
445 // So we can't trust header.size here, and should adjust buffer size based on real need.
446 buf.resize(p - buf.data());
447 return buf;
448 }
449
AdjustSizeBasedOnData()450 void SampleRecord::AdjustSizeBasedOnData() {
451 size_t size = BinaryFormat().size();
452 LOG(DEBUG) << "Record (type " << RecordTypeToString(header.type) << ") size is changed from "
453 << header.size << " to " << size;
454 header.size = size;
455 }
456
DumpData(size_t indent) const457 void SampleRecord::DumpData(size_t indent) const {
458 PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
459 if (sample_type & PERF_SAMPLE_IP) {
460 PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip));
461 }
462 if (sample_type & PERF_SAMPLE_TID) {
463 PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid);
464 }
465 if (sample_type & PERF_SAMPLE_TIME) {
466 PrintIndented(indent, "time %" PRId64 "\n", time_data.time);
467 }
468 if (sample_type & PERF_SAMPLE_ADDR) {
469 PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr));
470 }
471 if (sample_type & PERF_SAMPLE_ID) {
472 PrintIndented(indent, "id %" PRId64 "\n", id_data.id);
473 }
474 if (sample_type & PERF_SAMPLE_STREAM_ID) {
475 PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id);
476 }
477 if (sample_type & PERF_SAMPLE_CPU) {
478 PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
479 }
480 if (sample_type & PERF_SAMPLE_PERIOD) {
481 PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
482 }
483 if (sample_type & PERF_SAMPLE_CALLCHAIN) {
484 PrintIndented(indent, "callchain nr=%" PRIu64 "\n", callchain_data.ips.size());
485 for (auto& ip : callchain_data.ips) {
486 PrintIndented(indent + 1, "0x%" PRIx64 "\n", ip);
487 }
488 }
489 if (sample_type & PERF_SAMPLE_RAW) {
490 PrintIndented(indent, "raw size=%zu\n", raw_data.data.size());
491 const uint32_t* data = reinterpret_cast<const uint32_t*>(raw_data.data.data());
492 size_t size = raw_data.data.size() / sizeof(uint32_t);
493 for (size_t i = 0; i < size; ++i) {
494 PrintIndented(indent + 1, "0x%08x (%zu)\n", data[i], data[i]);
495 }
496 }
497 if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
498 PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
499 for (auto& item : branch_stack_data.stack) {
500 PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
501 item.from, item.to, item.flags);
502 }
503 }
504 if (sample_type & PERF_SAMPLE_REGS_USER) {
505 PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi);
506 for (size_t i = 0, pos = 0; i < 64; ++i) {
507 if ((regs_user_data.reg_mask >> i) & 1) {
508 PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n",
509 GetRegName(i, ScopedCurrentArch::GetCurrentArch()).c_str(),
510 regs_user_data.regs[pos++]);
511 }
512 }
513 }
514 if (sample_type & PERF_SAMPLE_STACK_USER) {
515 PrintIndented(indent, "user stack: size %zu dyn_size %" PRIu64 "\n",
516 stack_user_data.data.size(), stack_user_data.dyn_size);
517 const uint64_t* p = reinterpret_cast<const uint64_t*>(stack_user_data.data.data());
518 const uint64_t* end = p + (stack_user_data.data.size() / sizeof(uint64_t));
519 while (p < end) {
520 PrintIndented(indent + 1, "");
521 for (size_t i = 0; i < 4 && p < end; ++i, ++p) {
522 printf(" %016" PRIx64, *p);
523 }
524 printf("\n");
525 }
526 printf("\n");
527 }
528 }
529
Timestamp() const530 uint64_t SampleRecord::Timestamp() const {
531 return time_data.time;
532 }
533
BuildIdRecord(const perf_event_header * pheader)534 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
535 const char* p = reinterpret_cast<const char*>(pheader + 1);
536 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
537 MoveFromBinaryFormat(pid, p);
538 build_id = BuildId(p, BUILD_ID_SIZE);
539 p += ALIGN(build_id.Size(), 8);
540 filename = p;
541 p += ALIGN(filename.size() + 1, 64);
542 CHECK_EQ(p, end);
543 }
544
BinaryFormat() const545 std::vector<char> BuildIdRecord::BinaryFormat() const {
546 std::vector<char> buf(header.size);
547 char* p = buf.data();
548 MoveToBinaryFormat(header, p);
549 MoveToBinaryFormat(pid, p);
550 memcpy(p, build_id.Data(), build_id.Size());
551 p += ALIGN(build_id.Size(), 8);
552 strcpy(p, filename.c_str());
553 p += ALIGN(filename.size() + 1, 64);
554 return buf;
555 }
556
DumpData(size_t indent) const557 void BuildIdRecord::DumpData(size_t indent) const {
558 PrintIndented(indent, "pid %u\n", pid);
559 PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str());
560 PrintIndented(indent, "filename %s\n", filename.c_str());
561 }
562
UnknownRecord(const perf_event_header * pheader)563 UnknownRecord::UnknownRecord(const perf_event_header* pheader) : Record(pheader) {
564 const char* p = reinterpret_cast<const char*>(pheader + 1);
565 const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
566 data.insert(data.end(), p, end);
567 }
568
BinaryFormat() const569 std::vector<char> UnknownRecord::BinaryFormat() const {
570 std::vector<char> buf(header.size);
571 char* p = buf.data();
572 MoveToBinaryFormat(header, p);
573 MoveToBinaryFormat(data.data(), data.size(), p);
574 return buf;
575 }
576
DumpData(size_t) const577 void UnknownRecord::DumpData(size_t) const {
578 }
579
ReadRecordFromBuffer(const perf_event_attr & attr,const perf_event_header * pheader)580 static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
581 const perf_event_header* pheader) {
582 switch (pheader->type) {
583 case PERF_RECORD_MMAP:
584 return std::unique_ptr<Record>(new MmapRecord(attr, pheader));
585 case PERF_RECORD_MMAP2:
586 return std::unique_ptr<Record>(new Mmap2Record(attr, pheader));
587 case PERF_RECORD_COMM:
588 return std::unique_ptr<Record>(new CommRecord(attr, pheader));
589 case PERF_RECORD_EXIT:
590 return std::unique_ptr<Record>(new ExitRecord(attr, pheader));
591 case PERF_RECORD_FORK:
592 return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
593 case PERF_RECORD_SAMPLE:
594 return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
595 default:
596 return std::unique_ptr<Record>(new UnknownRecord(pheader));
597 }
598 }
599
ReadRecordsFromBuffer(const perf_event_attr & attr,const char * buf,size_t buf_size)600 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr,
601 const char* buf, size_t buf_size) {
602 std::vector<std::unique_ptr<Record>> result;
603 const char* p = buf;
604 const char* end = buf + buf_size;
605 while (p < end) {
606 const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
607 CHECK_LE(p + header->size, end);
608 CHECK_NE(0u, header->size);
609 result.push_back(ReadRecordFromBuffer(attr, header));
610 p += header->size;
611 }
612 return result;
613 }
614
ReadRecordFromFile(const perf_event_attr & attr,FILE * fp)615 std::unique_ptr<Record> ReadRecordFromFile(const perf_event_attr& attr, FILE* fp) {
616 std::vector<char> buf(sizeof(perf_event_header));
617 perf_event_header* header = reinterpret_cast<perf_event_header*>(&buf[0]);
618 if (fread(header, sizeof(perf_event_header), 1, fp) != 1) {
619 PLOG(ERROR) << "Failed to read record file";
620 return nullptr;
621 }
622 buf.resize(header->size);
623 header = reinterpret_cast<perf_event_header*>(&buf[0]);
624 if (fread(&buf[sizeof(perf_event_header)], buf.size() - sizeof(perf_event_header), 1, fp) != 1) {
625 PLOG(ERROR) << "Failed to read record file";
626 return nullptr;
627 }
628 return ReadRecordFromBuffer(attr, header);
629 }
630
CreateMmapRecord(const perf_event_attr & attr,bool in_kernel,uint32_t pid,uint32_t tid,uint64_t addr,uint64_t len,uint64_t pgoff,const std::string & filename)631 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
632 uint64_t addr, uint64_t len, uint64_t pgoff,
633 const std::string& filename) {
634 MmapRecord record;
635 record.header.type = PERF_RECORD_MMAP;
636 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
637 record.data.pid = pid;
638 record.data.tid = tid;
639 record.data.addr = addr;
640 record.data.len = len;
641 record.data.pgoff = pgoff;
642 record.filename = filename;
643 size_t sample_id_size = record.sample_id.CreateContent(attr);
644 record.header.size = sizeof(record.header) + sizeof(record.data) +
645 ALIGN(record.filename.size() + 1, 8) + sample_id_size;
646 return record;
647 }
648
CreateCommRecord(const perf_event_attr & attr,uint32_t pid,uint32_t tid,const std::string & comm)649 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
650 const std::string& comm) {
651 CommRecord record;
652 record.header.type = PERF_RECORD_COMM;
653 record.header.misc = 0;
654 record.data.pid = pid;
655 record.data.tid = tid;
656 record.comm = comm;
657 size_t sample_id_size = record.sample_id.CreateContent(attr);
658 record.header.size = sizeof(record.header) + sizeof(record.data) +
659 ALIGN(record.comm.size() + 1, 8) + sample_id_size;
660 return record;
661 }
662
CreateForkRecord(const perf_event_attr & attr,uint32_t pid,uint32_t tid,uint32_t ppid,uint32_t ptid)663 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
664 uint32_t ptid) {
665 ForkRecord record;
666 record.header.type = PERF_RECORD_FORK;
667 record.header.misc = 0;
668 record.data.pid = pid;
669 record.data.ppid = ppid;
670 record.data.tid = tid;
671 record.data.ptid = ptid;
672 record.data.time = 0;
673 size_t sample_id_size = record.sample_id.CreateContent(attr);
674 record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size;
675 return record;
676 }
677
CreateBuildIdRecord(bool in_kernel,pid_t pid,const BuildId & build_id,const std::string & filename)678 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
679 const std::string& filename) {
680 BuildIdRecord record;
681 record.header.type = PERF_RECORD_BUILD_ID;
682 record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
683 record.pid = pid;
684 record.build_id = build_id;
685 record.filename = filename;
686 record.header.size = sizeof(record.header) + sizeof(record.pid) +
687 ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64);
688 return record;
689 }
690
IsHappensBefore(const RecordWithSeq & other) const691 bool RecordCache::RecordWithSeq::IsHappensBefore(const RecordWithSeq& other) const {
692 bool is_sample = (record->header.type == PERF_RECORD_SAMPLE);
693 bool is_other_sample = (other.record->header.type == PERF_RECORD_SAMPLE);
694 uint64_t time = record->Timestamp();
695 uint64_t other_time = other.record->Timestamp();
696 // The record with smaller time happens first.
697 if (time != other_time) {
698 return time < other_time;
699 }
700 // If happening at the same time, make non-sample records before sample records,
701 // because non-sample records may contain useful information to parse sample records.
702 if (is_sample != is_other_sample) {
703 return is_sample ? false : true;
704 }
705 // Otherwise, use the same order as they enter the cache.
706 return seq < other.seq;
707 }
708
operator ()(const RecordWithSeq & r1,const RecordWithSeq & r2)709 bool RecordCache::RecordComparator::operator()(const RecordWithSeq& r1,
710 const RecordWithSeq& r2) {
711 return r2.IsHappensBefore(r1);
712 }
713
RecordCache(const perf_event_attr & attr,size_t min_cache_size,uint64_t min_time_diff_in_ns)714 RecordCache::RecordCache(const perf_event_attr& attr, size_t min_cache_size,
715 uint64_t min_time_diff_in_ns)
716 : attr_(attr),
717 has_timestamp_(attr.sample_id_all && (attr.sample_type & PERF_SAMPLE_TIME)),
718 min_cache_size_(min_cache_size),
719 min_time_diff_in_ns_(min_time_diff_in_ns),
720 last_time_(0),
721 cur_seq_(0),
722 queue_(RecordComparator()) {
723 }
724
~RecordCache()725 RecordCache::~RecordCache() {
726 PopAll();
727 }
728
Push(const char * data,size_t size)729 void RecordCache::Push(const char* data, size_t size) {
730 std::vector<std::unique_ptr<Record>> records = ReadRecordsFromBuffer(attr_, data, size);
731 if (has_timestamp_) {
732 for (const auto& r : records) {
733 last_time_ = std::max(last_time_, r->Timestamp());
734 }
735 }
736 for (auto& r : records) {
737 queue_.push(CreateRecordWithSeq(r.release()));
738 }
739 }
740
Push(std::unique_ptr<Record> record)741 void RecordCache::Push(std::unique_ptr<Record> record) {
742 queue_.push(CreateRecordWithSeq(record.release()));
743 }
744
Pop()745 std::unique_ptr<Record> RecordCache::Pop() {
746 if (queue_.size() < min_cache_size_) {
747 return nullptr;
748 }
749 Record* r = queue_.top().record;
750 if (has_timestamp_) {
751 if (r->Timestamp() + min_time_diff_in_ns_ > last_time_) {
752 return nullptr;
753 }
754 }
755 queue_.pop();
756 return std::unique_ptr<Record>(r);
757 }
758
PopAll()759 std::vector<std::unique_ptr<Record>> RecordCache::PopAll() {
760 std::vector<std::unique_ptr<Record>> result;
761 while (!queue_.empty()) {
762 result.emplace_back(queue_.top().record);
763 queue_.pop();
764 }
765 return result;
766 }
767
CreateRecordWithSeq(Record * r)768 RecordCache::RecordWithSeq RecordCache::CreateRecordWithSeq(Record *r) {
769 RecordWithSeq result;
770 result.seq = cur_seq_++;
771 result.record = r;
772 return result;
773 }
774