OSDN Git Service

am eb2e4a48: (-s ours) am 5c9978ce: Merge "Simpleperf: use ThreadTree when getting...
[android-x86/system-extras.git] / simpleperf / record.cpp
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 <base/logging.h>
24 #include <base/stringprintf.h>
25
26 #include "environment.h"
27 #include "perf_regs.h"
28 #include "utils.h"
29
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>
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>
55 void MoveToBinaryFormat(const T& data, char*& p) {
56   *reinterpret_cast<T*>(p) = data;
57   p += sizeof(T);
58 }
59
60 SampleId::SampleId() {
61   memset(this, 0, sizeof(SampleId));
62 }
63
64 // Return sample_id size in binary format.
65 size_t SampleId::CreateContent(const perf_event_attr& attr) {
66   sample_id_all = attr.sample_id_all;
67   sample_type = attr.sample_type;
68   // Other data are not necessary. TODO: Set missing SampleId data.
69   size_t size = 0;
70   if (sample_id_all) {
71     if (sample_type & PERF_SAMPLE_TID) {
72       size += sizeof(PerfSampleTidType);
73     }
74     if (sample_type & PERF_SAMPLE_TIME) {
75       size += sizeof(PerfSampleTimeType);
76     }
77     if (sample_type & PERF_SAMPLE_ID) {
78       size += sizeof(PerfSampleIdType);
79     }
80     if (sample_type & PERF_SAMPLE_STREAM_ID) {
81       size += sizeof(PerfSampleStreamIdType);
82     }
83     if (sample_type & PERF_SAMPLE_CPU) {
84       size += sizeof(PerfSampleCpuType);
85     }
86   }
87   return size;
88 }
89
90 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
91   sample_id_all = attr.sample_id_all;
92   sample_type = attr.sample_type;
93   if (sample_id_all) {
94     if (sample_type & PERF_SAMPLE_TID) {
95       MoveFromBinaryFormat(tid_data, p);
96     }
97     if (sample_type & PERF_SAMPLE_TIME) {
98       MoveFromBinaryFormat(time_data, p);
99     }
100     if (sample_type & PERF_SAMPLE_ID) {
101       MoveFromBinaryFormat(id_data, p);
102     }
103     if (sample_type & PERF_SAMPLE_STREAM_ID) {
104       MoveFromBinaryFormat(stream_id_data, p);
105     }
106     if (sample_type & PERF_SAMPLE_CPU) {
107       MoveFromBinaryFormat(cpu_data, p);
108     }
109     // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER.
110   }
111   CHECK_LE(p, end);
112   if (p < end) {
113     LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n";
114   }
115 }
116
117 void SampleId::WriteToBinaryFormat(char*& p) const {
118   if (sample_id_all) {
119     if (sample_type & PERF_SAMPLE_TID) {
120       MoveToBinaryFormat(tid_data, p);
121     }
122     if (sample_type & PERF_SAMPLE_TIME) {
123       MoveToBinaryFormat(time_data, p);
124     }
125     if (sample_type & PERF_SAMPLE_ID) {
126       MoveToBinaryFormat(id_data, p);
127     }
128     if (sample_type & PERF_SAMPLE_STREAM_ID) {
129       MoveToBinaryFormat(stream_id_data, p);
130     }
131     if (sample_type & PERF_SAMPLE_CPU) {
132       MoveToBinaryFormat(cpu_data, p);
133     }
134   }
135 }
136
137 void SampleId::Dump(size_t indent) const {
138   if (sample_id_all) {
139     if (sample_type & PERF_SAMPLE_TID) {
140       PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid);
141     }
142     if (sample_type & PERF_SAMPLE_TIME) {
143       PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
144     }
145     if (sample_type & PERF_SAMPLE_ID) {
146       PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id);
147     }
148     if (sample_type & PERF_SAMPLE_STREAM_ID) {
149       PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id);
150     }
151     if (sample_type & PERF_SAMPLE_CPU) {
152       PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
153     }
154   }
155 }
156
157 Record::Record() {
158   memset(&header, 0, sizeof(header));
159 }
160
161 Record::Record(const perf_event_header* pheader) {
162   header = *pheader;
163 }
164
165 void Record::Dump(size_t indent) const {
166   PrintIndented(indent, "record %s: type %u, misc %u, size %u\n",
167                 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size);
168   DumpData(indent + 1);
169   sample_id.Dump(indent + 1);
170 }
171
172 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
173     : Record(pheader) {
174   const char* p = reinterpret_cast<const char*>(pheader + 1);
175   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
176   MoveFromBinaryFormat(data, p);
177   filename = p;
178   p += ALIGN(filename.size() + 1, 8);
179   CHECK_LE(p, end);
180   sample_id.ReadFromBinaryFormat(attr, p, end);
181 }
182
183 void MmapRecord::DumpData(size_t indent) const {
184   PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
185                 data.tid, data.addr, data.len);
186   PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
187 }
188
189 std::vector<char> MmapRecord::BinaryFormat() const {
190   std::vector<char> buf(header.size);
191   char* p = buf.data();
192   MoveToBinaryFormat(header, p);
193   MoveToBinaryFormat(data, p);
194   strcpy(p, filename.c_str());
195   p += ALIGN(filename.size() + 1, 8);
196   sample_id.WriteToBinaryFormat(p);
197   return buf;
198 }
199
200 Mmap2Record::Mmap2Record(const perf_event_attr& attr, const perf_event_header* pheader)
201     : Record(pheader) {
202   const char* p = reinterpret_cast<const char*>(pheader + 1);
203   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
204   MoveFromBinaryFormat(data, p);
205   filename = p;
206   p += ALIGN(filename.size() + 1, 8);
207   CHECK_LE(p, end);
208   sample_id.ReadFromBinaryFormat(attr, p, end);
209 }
210
211 void Mmap2Record::DumpData(size_t indent) const {
212   PrintIndented(indent, "pid %u, tid %u, addr 0x%" PRIx64 ", len 0x%" PRIx64 "\n", data.pid,
213                 data.tid, data.addr, data.len);
214   PrintIndented(indent,
215                 "pgoff 0x" PRIx64 ", maj %u, min %u, ino %" PRId64 ", ino_generation %" PRIu64 "\n",
216                 data.pgoff, data.maj, data.min, data.ino, data.ino_generation);
217   PrintIndented(indent, "prot %u, flags %u, filenames %s\n", data.prot, data.flags,
218                 filename.c_str());
219 }
220
221 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader)
222     : Record(pheader) {
223   const char* p = reinterpret_cast<const char*>(pheader + 1);
224   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
225   MoveFromBinaryFormat(data, p);
226   comm = p;
227   p += ALIGN(strlen(p) + 1, 8);
228   CHECK_LE(p, end);
229   sample_id.ReadFromBinaryFormat(attr, p, end);
230 }
231
232 void CommRecord::DumpData(size_t indent) const {
233   PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
234 }
235
236 std::vector<char> CommRecord::BinaryFormat() const {
237   std::vector<char> buf(header.size);
238   char* p = buf.data();
239   MoveToBinaryFormat(header, p);
240   MoveToBinaryFormat(data, p);
241   strcpy(p, comm.c_str());
242   p += ALIGN(comm.size() + 1, 8);
243   sample_id.WriteToBinaryFormat(p);
244   return buf;
245 }
246
247 ExitOrForkRecord::ExitOrForkRecord(const perf_event_attr& attr, const perf_event_header* pheader)
248     : Record(pheader) {
249   const char* p = reinterpret_cast<const char*>(pheader + 1);
250   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
251   MoveFromBinaryFormat(data, p);
252   CHECK_LE(p, end);
253   sample_id.ReadFromBinaryFormat(attr, p, end);
254 }
255
256 void ExitOrForkRecord::DumpData(size_t indent) const {
257   PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
258                 data.ptid);
259 }
260
261 std::vector<char> ForkRecord::BinaryFormat() const {
262   std::vector<char> buf(header.size);
263   char* p = buf.data();
264   MoveToBinaryFormat(header, p);
265   MoveToBinaryFormat(data, p);
266   sample_id.WriteToBinaryFormat(p);
267   return buf;
268 }
269
270 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
271     : Record(pheader) {
272   const char* p = reinterpret_cast<const char*>(pheader + 1);
273   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
274   sample_type = attr.sample_type;
275
276   if (sample_type & PERF_SAMPLE_IP) {
277     MoveFromBinaryFormat(ip_data, p);
278   }
279   if (sample_type & PERF_SAMPLE_TID) {
280     MoveFromBinaryFormat(tid_data, p);
281   }
282   if (sample_type & PERF_SAMPLE_TIME) {
283     MoveFromBinaryFormat(time_data, p);
284   }
285   if (sample_type & PERF_SAMPLE_ADDR) {
286     MoveFromBinaryFormat(addr_data, p);
287   }
288   if (sample_type & PERF_SAMPLE_ID) {
289     MoveFromBinaryFormat(id_data, p);
290   }
291   if (sample_type & PERF_SAMPLE_STREAM_ID) {
292     MoveFromBinaryFormat(stream_id_data, p);
293   }
294   if (sample_type & PERF_SAMPLE_CPU) {
295     MoveFromBinaryFormat(cpu_data, p);
296   }
297   if (sample_type & PERF_SAMPLE_PERIOD) {
298     MoveFromBinaryFormat(period_data, p);
299   }
300   if (sample_type & PERF_SAMPLE_CALLCHAIN) {
301     uint64_t nr;
302     MoveFromBinaryFormat(nr, p);
303     callchain_data.ips.resize(nr);
304     MoveFromBinaryFormat(callchain_data.ips.data(), nr, p);
305   }
306   if (sample_type & PERF_SAMPLE_RAW) {
307     uint32_t size;
308     MoveFromBinaryFormat(size, p);
309     raw_data.data.resize(size);
310     MoveFromBinaryFormat(raw_data.data.data(), size, p);
311   }
312   if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
313     uint64_t nr;
314     MoveFromBinaryFormat(nr, p);
315     branch_stack_data.stack.resize(nr);
316     MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
317   }
318   if (sample_type & PERF_SAMPLE_REGS_USER) {
319     MoveFromBinaryFormat(regs_user_data.abi, p);
320     if (regs_user_data.abi == 0) {
321       regs_user_data.reg_mask = 0;
322     } else {
323       regs_user_data.reg_mask = attr.sample_regs_user;
324       size_t bit_nr = 0;
325       for (size_t i = 0; i < 64; ++i) {
326         if ((regs_user_data.reg_mask >> i) & 1) {
327           bit_nr++;
328         }
329       }
330       regs_user_data.regs.resize(bit_nr);
331       MoveFromBinaryFormat(regs_user_data.regs.data(), bit_nr, p);
332     }
333   }
334   if (sample_type & PERF_SAMPLE_STACK_USER) {
335     uint64_t size;
336     MoveFromBinaryFormat(size, p);
337     if (size == 0) {
338       stack_user_data.dyn_size = 0;
339     } else {
340       stack_user_data.data.resize(size);
341       MoveFromBinaryFormat(stack_user_data.data.data(), size, p);
342       MoveFromBinaryFormat(stack_user_data.dyn_size, p);
343     }
344   }
345   // TODO: Add parsing of other PERF_SAMPLE_*.
346   CHECK_LE(p, end);
347   if (p < end) {
348     LOG(DEBUG) << "Record has " << end - p << " bytes left\n";
349   }
350 }
351
352 void SampleRecord::DumpData(size_t indent) const {
353   PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
354   if (sample_type & PERF_SAMPLE_IP) {
355     PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip));
356   }
357   if (sample_type & PERF_SAMPLE_TID) {
358     PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid);
359   }
360   if (sample_type & PERF_SAMPLE_TIME) {
361     PrintIndented(indent, "time %" PRId64 "\n", time_data.time);
362   }
363   if (sample_type & PERF_SAMPLE_ADDR) {
364     PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr));
365   }
366   if (sample_type & PERF_SAMPLE_ID) {
367     PrintIndented(indent, "id %" PRId64 "\n", id_data.id);
368   }
369   if (sample_type & PERF_SAMPLE_STREAM_ID) {
370     PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id);
371   }
372   if (sample_type & PERF_SAMPLE_CPU) {
373     PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
374   }
375   if (sample_type & PERF_SAMPLE_PERIOD) {
376     PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
377   }
378   if (sample_type & PERF_SAMPLE_CALLCHAIN) {
379     PrintIndented(indent, "callchain nr=%" PRIu64 "\n", callchain_data.ips.size());
380     for (auto& ip : callchain_data.ips) {
381       PrintIndented(indent + 1, "0x%" PRIx64 "\n", ip);
382     }
383   }
384   if (sample_type & PERF_SAMPLE_RAW) {
385     PrintIndented(indent, "raw size=%zu\n", raw_data.data.size());
386     const uint32_t* data = reinterpret_cast<const uint32_t*>(raw_data.data.data());
387     size_t size = raw_data.data.size() / sizeof(uint32_t);
388     for (size_t i = 0; i < size; ++i) {
389       PrintIndented(indent + 1, "0x%08x (%zu)\n", data[i], data[i]);
390     }
391   }
392   if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
393     PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
394     for (auto& item : branch_stack_data.stack) {
395       PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
396                     item.from, item.to, item.flags);
397     }
398   }
399   if (sample_type & PERF_SAMPLE_REGS_USER) {
400     PrintIndented(indent, "user regs: abi=%" PRId64 "\n", regs_user_data.abi);
401     for (size_t i = 0, pos = 0; i < 64; ++i) {
402       if ((regs_user_data.reg_mask >> i) & 1) {
403         PrintIndented(indent + 1, "reg (%s) 0x%016" PRIx64 "\n", GetRegName(i).c_str(),
404                       regs_user_data.regs[pos++]);
405       }
406     }
407   }
408   if (sample_type & PERF_SAMPLE_STACK_USER) {
409     PrintIndented(indent, "user stack: size %zu dyn_size %" PRIu64 "\n",
410                   stack_user_data.data.size(), stack_user_data.dyn_size);
411     const uint64_t* p = reinterpret_cast<const uint64_t*>(stack_user_data.data.data());
412     const uint64_t* end = p + (stack_user_data.data.size() / sizeof(uint64_t));
413     while (p < end) {
414       PrintIndented(indent + 1, "");
415       for (size_t i = 0; i < 4 && p < end; ++i, ++p) {
416         printf(" %016" PRIx64, *p);
417       }
418       printf("\n");
419     }
420     printf("\n");
421   }
422 }
423
424 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
425   const char* p = reinterpret_cast<const char*>(pheader + 1);
426   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
427   MoveFromBinaryFormat(pid, p);
428   build_id = BuildId(p);
429   p += ALIGN(build_id.Size(), 8);
430   filename = p;
431   p += ALIGN(filename.size() + 1, 64);
432   CHECK_EQ(p, end);
433 }
434
435 void BuildIdRecord::DumpData(size_t indent) const {
436   PrintIndented(indent, "pid %u\n", pid);
437   PrintIndented(indent, "build_id %s\n", build_id.ToString().c_str());
438   PrintIndented(indent, "filename %s\n", filename.c_str());
439 }
440
441 std::vector<char> BuildIdRecord::BinaryFormat() const {
442   std::vector<char> buf(header.size);
443   char* p = buf.data();
444   MoveToBinaryFormat(header, p);
445   MoveToBinaryFormat(pid, p);
446   memcpy(p, build_id.Data(), build_id.Size());
447   p += ALIGN(build_id.Size(), 8);
448   strcpy(p, filename.c_str());
449   p += ALIGN(filename.size() + 1, 64);
450   return buf;
451 }
452
453 static std::unique_ptr<Record> ReadRecordFromBuffer(const perf_event_attr& attr,
454                                                     const perf_event_header* pheader) {
455   switch (pheader->type) {
456     case PERF_RECORD_MMAP:
457       return std::unique_ptr<Record>(new MmapRecord(attr, pheader));
458     case PERF_RECORD_MMAP2:
459       return std::unique_ptr<Record>(new Mmap2Record(attr, pheader));
460     case PERF_RECORD_COMM:
461       return std::unique_ptr<Record>(new CommRecord(attr, pheader));
462     case PERF_RECORD_EXIT:
463       return std::unique_ptr<Record>(new ExitRecord(attr, pheader));
464     case PERF_RECORD_FORK:
465       return std::unique_ptr<Record>(new ForkRecord(attr, pheader));
466     case PERF_RECORD_SAMPLE:
467       return std::unique_ptr<Record>(new SampleRecord(attr, pheader));
468     default:
469       return std::unique_ptr<Record>(new Record(pheader));
470   }
471 }
472
473 static bool IsRecordHappensBefore(const std::unique_ptr<Record>& r1,
474                                   const std::unique_ptr<Record>& r2) {
475   bool is_r1_sample = (r1->header.type == PERF_RECORD_SAMPLE);
476   bool is_r2_sample = (r2->header.type == PERF_RECORD_SAMPLE);
477   uint64_t time1 = (is_r1_sample ? static_cast<const SampleRecord*>(r1.get())->time_data.time
478                                  : r1->sample_id.time_data.time);
479   uint64_t time2 = (is_r2_sample ? static_cast<const SampleRecord*>(r2.get())->time_data.time
480                                  : r2->sample_id.time_data.time);
481   // The record with smaller time happens first.
482   if (time1 != time2) {
483     return time1 < time2;
484   }
485   // If happening at the same time, make non-sample records before sample records,
486   // because non-sample records may contain useful information to parse sample records.
487   if (is_r1_sample != is_r2_sample) {
488     return is_r1_sample ? false : true;
489   }
490   // Otherwise, don't care of the order.
491   return false;
492 }
493
494 std::vector<std::unique_ptr<Record>> ReadRecordsFromBuffer(const perf_event_attr& attr,
495                                                            const char* buf, size_t buf_size) {
496   std::vector<std::unique_ptr<Record>> result;
497   const char* p = buf;
498   const char* end = buf + buf_size;
499   while (p < end) {
500     const perf_event_header* header = reinterpret_cast<const perf_event_header*>(p);
501     if (p + header->size <= end) {
502       result.push_back(ReadRecordFromBuffer(attr, header));
503     }
504     p += header->size;
505   }
506   if ((attr.sample_type & PERF_SAMPLE_TIME) && attr.sample_id_all) {
507     std::sort(result.begin(), result.end(), IsRecordHappensBefore);
508   }
509   return result;
510 }
511
512 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
513                             uint64_t addr, uint64_t len, uint64_t pgoff,
514                             const std::string& filename) {
515   MmapRecord record;
516   record.header.type = PERF_RECORD_MMAP;
517   record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
518   record.data.pid = pid;
519   record.data.tid = tid;
520   record.data.addr = addr;
521   record.data.len = len;
522   record.data.pgoff = pgoff;
523   record.filename = filename;
524   size_t sample_id_size = record.sample_id.CreateContent(attr);
525   record.header.size = sizeof(record.header) + sizeof(record.data) +
526                        ALIGN(record.filename.size() + 1, 8) + sample_id_size;
527   return record;
528 }
529
530 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
531                             const std::string& comm) {
532   CommRecord record;
533   record.header.type = PERF_RECORD_COMM;
534   record.header.misc = 0;
535   record.data.pid = pid;
536   record.data.tid = tid;
537   record.comm = comm;
538   size_t sample_id_size = record.sample_id.CreateContent(attr);
539   record.header.size = sizeof(record.header) + sizeof(record.data) +
540                        ALIGN(record.comm.size() + 1, 8) + sample_id_size;
541   return record;
542 }
543
544 ForkRecord CreateForkRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid, uint32_t ppid,
545                             uint32_t ptid) {
546   ForkRecord record;
547   record.header.type = PERF_RECORD_FORK;
548   record.header.misc = 0;
549   record.data.pid = pid;
550   record.data.ppid = ppid;
551   record.data.tid = tid;
552   record.data.ptid = ptid;
553   record.data.time = 0;
554   size_t sample_id_size = record.sample_id.CreateContent(attr);
555   record.header.size = sizeof(record.header) + sizeof(record.data) + sample_id_size;
556   return record;
557 }
558
559 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
560                                   const std::string& filename) {
561   BuildIdRecord record;
562   record.header.type = PERF_RECORD_BUILD_ID;
563   record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
564   record.pid = pid;
565   record.build_id = build_id;
566   record.filename = filename;
567   record.header.size = sizeof(record.header) + sizeof(record.pid) +
568                        ALIGN(record.build_id.Size(), 8) + ALIGN(filename.size() + 1, 64);
569   return record;
570 }