perf: Add infrastructure for creating headers
[akaros.git] / tools / profile / perf / perfconv.c
1 /* Copyright (c) 2015-2016 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * Barret Rhoden <brho@cs.berkeley.edu>
4  *
5  * See LICENSE for details.
6  *
7  * Converts kprof profiler files into Linux perf ones. The Linux Perf file
8  * format has bee illustrated here:
9  *
10  *       https://lwn.net/Articles/644919/
11  *       https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
12  *                       3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
13  *
14  */
15
16 #include <ros/common.h>
17 #include <ros/memops.h>
18 #include <ros/profiler_records.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdarg.h>
28 #include "perf_format.h"
29 #include "xlib.h"
30 #include "perf_core.h"
31 #include "perfconv.h"
32
33 #define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
34 #define PERF_RECORD_BUFFER_SIZE 1024
35 #define OFFSET_NORELOC ((uint64_t) -1)
36 #define MEMFILE_BLOCK_SIZE (64 * 1024)
37
38 #define MBWR_SOLID (1 << 0)
39
40 struct perf_record {
41         uint64_t type;
42         uint64_t size;
43         char *data;
44         char buffer[PERF_RECORD_BUFFER_SIZE];
45 };
46
47 static void dbg_print(struct perfconv_context *cctx, int level, FILE *file,
48                                           const char *fmt, ...)
49 {
50         if (cctx->debug_level >= level) {
51                 va_list args;
52
53                 va_start(args, fmt);
54                 vfprintf(file, fmt, args);
55                 va_end(args);
56         }
57 }
58
59 void perfconv_set_dbglevel(int level, struct perfconv_context *cctx)
60 {
61         cctx->debug_level = level;
62 }
63
64 static void free_record(struct perf_record *pr)
65 {
66         if (pr->data != pr->buffer)
67                 free(pr->data);
68         pr->data = NULL;
69 }
70
71 static int read_record(FILE *file, struct perf_record *pr)
72 {
73         if (vb_fdecode_uint64(file, &pr->type) == EOF ||
74                 vb_fdecode_uint64(file, &pr->size) == EOF)
75                 return EOF;
76         if (pr->size > MAX_PERF_RECORD_SIZE) {
77                 fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
78                                 pr->size);
79                 exit(1);
80         }
81         if (pr->size > sizeof(pr->buffer))
82                 pr->data = xmalloc((size_t) pr->size);
83         else
84                 pr->data = pr->buffer;
85         if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
86                 fprintf(stderr, "Unable to read record memory: size=%lu\n",
87                                 pr->size);
88                 return EOF;
89         }
90
91         return 0;
92 }
93
94 static struct mem_block *mem_block_alloc(size_t size)
95 {
96         struct mem_block *mb = xmalloc(sizeof(struct mem_block) + size);
97
98         mb->next = NULL;
99         mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
100         mb->top = mb->base + size;
101
102         return mb;
103 }
104
105 static char *mem_block_write(struct mem_block *mb, const void *data,
106                                                          size_t size)
107 {
108         char *wrbase = mb->wptr;
109
110         always_assert(size <= mb->top - mb->wptr);
111
112         memcpy(mb->wptr, data, size);
113         mb->wptr += size;
114
115         return wrbase;
116 }
117
118 static void mem_file_init(struct mem_file *mf)
119 {
120         ZERO_DATA(*mf);
121 }
122
123 static int mem_block_can_write(struct mem_block *mb, size_t size, int flags)
124 {
125         size_t space = mb->top - mb->wptr;
126
127         return (flags & MBWR_SOLID) ? (space >= size) : (space > 0);
128 }
129
130 static void *mem_file_write(struct mem_file *mf, const void *data, size_t size,
131                                                         int flags)
132 {
133         void *wrbase = NULL;
134
135         while (size > 0) {
136                 size_t space, csize;
137                 struct mem_block *mb = mf->tail;
138
139                 if (!mb || !mem_block_can_write(mb, size, flags)) {
140                         mb = mem_block_alloc(max(MEMFILE_BLOCK_SIZE, size));
141                         if (!mf->tail)
142                                 mf->head = mb;
143                         else
144                                 mf->tail->next = mb;
145                         mf->tail = mb;
146                 }
147                 space = mb->top - mb->wptr;
148                 csize = min(size, space);
149
150                 wrbase = mem_block_write(mb, data, csize);
151                 mf->size += csize;
152
153                 size -= csize;
154                 data = (const char *) data + csize;
155         }
156
157         return wrbase;
158 }
159
160 static void mem_file_sync(struct mem_file *mf, FILE *file, uint64_t rel_offset)
161 {
162         struct mem_block *mb;
163
164         if (rel_offset != 0) {
165                 struct mem_file_reloc *rel;
166
167                 always_assert(!mf->relocs || rel_offset != OFFSET_NORELOC);
168
169                 for (rel = mf->relocs; rel; rel = rel->next)
170                         *rel->ptr += rel_offset;
171         }
172
173         for (mb = mf->head; mb; mb = mb->next)
174                 xfwrite(mb->base, mb->wptr - mb->base, file);
175 }
176
177 static struct mem_file_reloc *mem_file_add_reloc(struct mem_file *mf,
178                                                                                                  uint64_t *ptr)
179 {
180         struct mem_file_reloc *rel = xzmalloc(sizeof(struct mem_file_reloc));
181
182         rel->ptr = ptr;
183         rel->next = mf->relocs;
184         mf->relocs = rel;
185
186         return rel;
187 }
188
189 void perfconv_add_kernel_mmap(struct perfconv_context *cctx)
190 {
191         char path[] = "[kernel.kallsyms]";
192         struct static_mmap64 *mm;
193
194         mm = xzmalloc(sizeof(struct static_mmap64));
195         mm->pid = -1;                           /* Linux HOST_KERNEL_ID == -1 */
196         mm->tid = 0;                            /* Default thread: swapper */
197         mm->header_misc = PERF_RECORD_MISC_KERNEL;
198         /* Linux sets addr = 0, size = 0xffffffff9fffffff, off = 0xffffffff81000000
199          * Their mmap record is also called [kernel.kallsyms]_text (I think).  They
200          * also have a _text symbol in kallsyms at ffffffff81000000 (equiv to our
201          * KERN_LOAD_ADDR (which is 0xffffffffc0000000)).  Either way, this seems to
202          * work for us; we'll see.  It's also arch-independent (for now). */
203         mm->addr = 0;
204         mm->size = 0xffffffffffffffff;
205         mm->offset = 0x0;
206         mm->path = xstrdup(path);
207
208         mm->next = cctx->static_mmaps;
209         cctx->static_mmaps = mm;
210 }
211
212 static void headers_init(struct perf_headers *hdrs)
213 {
214         ZERO_DATA(*hdrs);
215 }
216
217 /* Prepends a header (the mem_block) to the list of memblocks for the given
218  * HEADER type.  They will all be concatted together during finalization. */
219 static void headers_add_header(struct perf_header *ph,
220                                struct perf_headers *hdrs, size_t nhdr,
221                                                            struct mem_block *mb)
222 {
223         always_assert(nhdr < HEADER_FEAT_BITS);
224
225         mb->next = hdrs->headers[nhdr];
226         hdrs->headers[nhdr] = mb;
227         set_bitno(ph->adds_features, nhdr);
228 }
229
230 /* Emits the headers contents to the mem_file */
231 static void headers_finalize(struct perf_headers *hdrs, struct mem_file *mf)
232 {
233         struct perf_file_section *header_file_secs, *file_sec;
234         struct perf_file_section *file_sec_reloc;
235         size_t nr_hdrs = 0;
236         size_t hdr_off;
237         struct mem_block *mb;
238         size_t mb_sz;
239
240         /* For each header, we need a perf_file_section.  These header file sections
241          * are right after the main perf header, and they point to actual header. */
242         for (int i = 0; i < HEADER_FEAT_BITS; i++)
243                 if (hdrs->headers[i])
244                         nr_hdrs++;
245         if (!nr_hdrs)
246                 return;
247         header_file_secs = xmalloc(sizeof(struct perf_file_section) * nr_hdrs);
248
249         hdr_off = sizeof(struct perf_file_section) * nr_hdrs;
250         file_sec = header_file_secs;
251
252         /* Spit out the perf_file_sections first and track relocations for all of
253          * the offsets. */
254         for (int i = 0; i < HEADER_FEAT_BITS; i++) {
255                 mb = hdrs->headers[i];
256                 if (!mb)
257                         continue;
258                 mb_sz = mb->wptr - mb->base;
259                 /* headers[i] is a chain of memblocks */
260                 while (mb->next) {
261                         mb = mb->next;
262                         mb_sz += mb->wptr - mb->base;
263                 }
264                 file_sec->size = mb_sz;
265                 file_sec->offset = hdr_off;             /* offset rel to this memfile */
266                 /* When we sync the memfile, we'll need to relocate each of the offsets
267                  * so that they are relative to the final file.   mem_file_write()
268                  * should be returning the location of where it wrote our file section
269                  * within the memfile.  that's the offset that we'll reloc later. */
270                 file_sec_reloc = mem_file_write(mf, file_sec,
271                                                 sizeof(struct perf_file_section), 0);
272                 assert(file_sec->size == file_sec_reloc->size);
273                 assert(file_sec->offset == file_sec_reloc->offset);
274                 mem_file_add_reloc(mf, &file_sec_reloc->offset);
275
276                 hdr_off += mb_sz;
277                 file_sec++;
278         }
279         free(header_file_secs);
280
281         /* Spit out the actual headers */
282         for (int i = 0; i < HEADER_FEAT_BITS; i++) {
283                 mb = hdrs->headers[i];
284                 while (mb) {
285                         mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
286                         mb = mb->next;
287                 }
288         }
289 }
290
291 /* Builds a struct perf_header_string from str and returns it in a mem_block */
292 static struct mem_block *header_make_string(const char *str)
293 {
294         struct perf_header_string *hdr;
295         struct mem_block *mb;
296         size_t str_sz = strlen(str) + 1;
297         size_t hdr_sz = ROUNDUP(str_sz + sizeof(struct perf_header_string),
298                                 PERF_STRING_ALIGN);
299
300         mb = mem_block_alloc(hdr_sz);
301         /* Manually writing to the block to avoid another alloc.  I guess I could do
302          * two writes (len and string) and try to not screw it up, but that'd be a
303          * mess. */
304         hdr = (struct perf_header_string*)mb->wptr;
305         mb->wptr += hdr_sz;
306         hdr->len = str_sz;
307         memcpy(hdr->string, str, str_sz);
308         return mb;
309 }
310
311 /* Opens and reads filename, returning the contents.  Free the ret. */
312 static char *get_str_from_os(const char *filename)
313 {
314         int fd, ret;
315         struct stat fd_stat;
316         char *buf;
317         size_t buf_sz;
318
319         fd = open(filename, O_RDONLY);
320         if (fd < 0)
321                 return 0;
322         ret = fstat(fd, &fd_stat);
323         if (ret) {
324                 close(fd);
325                 return 0;
326         }
327         buf_sz = fd_stat.st_size + 1;
328         buf = xmalloc(buf_sz);
329         ret = read(fd, buf, buf_sz - 1);
330         if (ret <= 0) {
331                 free(buf);
332                 close(fd);
333                 return 0;
334         }
335         close(fd);
336         /* OS strings should be null terminated, but let's be defensive */
337         buf[ret] = 0;
338         return buf;
339 }
340
341 static void hdr_do_osrelease(struct perf_header *ph, struct perf_headers *hdrs)
342 {
343         char *str;
344
345         str = get_str_from_os("#version/version_name");
346         if (!str)
347                 return;
348         headers_add_header(ph, hdrs, HEADER_OSRELEASE, header_make_string(str));
349         free(str);
350 }
351
352 static void hdr_do_nrcpus(struct perf_header *ph, struct perf_headers *hdrs)
353 {
354         char *str;
355         uint32_t nr_cores;
356         struct mem_block *mb;
357         struct nr_cpus *hdr;
358
359         str = get_str_from_os("#vars/num_cores!dw");
360         if (!str)
361                 return;
362         nr_cores = atoi(str);
363         free(str);
364
365         mb = mem_block_alloc(sizeof(struct nr_cpus));
366         hdr = (struct nr_cpus*)mb->wptr;
367         mb->wptr += sizeof(struct nr_cpus);
368
369         hdr->nr_cpus_online = nr_cores;
370         hdr->nr_cpus_available = nr_cores;
371
372         headers_add_header(ph, hdrs, HEADER_NRCPUS, mb);
373 }
374
375 /* Helper: adds all the headers, marking them in PH and storing them in
376  * feat_hdrs. */
377 static void headers_build(struct perf_header *ph, struct perf_headers *hdrs,
378                           struct mem_file *feat_hdrs)
379 {
380         hdr_do_osrelease(ph, hdrs);
381         hdr_do_nrcpus(ph, hdrs);
382
383         headers_finalize(hdrs, feat_hdrs);
384 }
385
386 static void perf_header_init(struct perf_header *ph)
387 {
388         ZERO_DATA(*ph);
389         ph->magic = PERF_MAGIC2;
390         ph->size = sizeof(*ph);
391         ph->attr_size = sizeof(struct perf_event_attr);
392 }
393
394 /* For each attr we emit, we push out the attr, then a perf_file_section for the
395  * id(s) for that attr.  This wasn't mentioned in
396  * https://lwn.net/Articles/644919/, but it's what Linux perf expects
397  * (util/header.c).  It looks like you can have multiple IDs per event attr.
398  * We'll only emit one.  The *contents* of the perf_file_section for the ids
399  * aren't in the attr perf_file_section, they are in a separate one
400  * (attr_id_mf).
401  *
402  * Note that *attr_mf*'s relocs are relative to the base of *attr_id_mf*, which
403  * we'll need to sort out later. */
404 static void emit_attr(struct mem_file *attr_mf, struct mem_file *attr_id_mf,
405                       const struct perf_event_attr *attr, uint64_t id)
406 {
407         struct perf_file_section *psids;
408         struct perf_file_section sids;
409
410         mem_file_write(attr_mf, attr, sizeof(*attr), 0);
411
412         sids.offset = attr_id_mf->size;
413         sids.size = sizeof(uint64_t);
414         mem_file_write(attr_id_mf, &id, sizeof(uint64_t), 0);
415
416         psids = mem_file_write(attr_mf, &sids, sizeof(sids), MBWR_SOLID);
417         mem_file_add_reloc(attr_mf, &psids->offset);
418 }
419
420 /* Given raw_info, which is what the kernel sends as user_data for a particular
421  * sample, look up the 'id' for the event/sample.  The 'id' identifies the event
422  * stream that the sample is a part of.  There are many samples per event
423  * stream, all identified by 'id.'  It doesn't matter what 'id', so long as it
424  * is unique.  We happen to use the pointer to the sample's eventsel.
425  *
426  * If this is the first time we've seen 'raw_info', we'll also add an attribute
427  * to the perf ctx.  There is one attr per 'id' / event stream. */
428 static uint64_t perfconv_get_event_id(struct perfconv_context *cctx,
429                                                                           uint64_t raw_info)
430 {
431         struct perf_eventsel *sel = (struct perf_eventsel*)raw_info;
432         struct perf_event_attr attr;
433         uint64_t raw_event;
434
435         assert(sel);
436         if (sel->attr_emitted)
437                 return raw_info;
438         raw_event = sel->ev.event;
439         ZERO_DATA(attr);
440         attr.size = sizeof(attr);
441         attr.mmap = 1;
442         attr.comm = 1;
443         attr.sample_period = sel->ev.trigger_count;
444         /* Closely coupled with struct perf_record_sample */
445         attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
446                            PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_CPU |
447                            PERF_SAMPLE_CALLCHAIN;
448         attr.exclude_guest = 1; /* we can't trace VMs yet */
449         attr.exclude_hv = 1;    /* we aren't tracing our hypervisor, AFAIK */
450         attr.exclude_user = !PMEV_GET_USR(raw_event);
451         attr.exclude_kernel = !PMEV_GET_OS(raw_event);
452         attr.type = sel->type;
453         attr.config = sel->config;
454         emit_attr(&cctx->attrs, &cctx->attr_ids, &attr, raw_info);
455         sel->attr_emitted = TRUE;
456         return raw_info;
457 }
458
459 static void emit_static_mmaps(struct perfconv_context *cctx)
460 {
461         struct static_mmap64 *mm;
462
463         for (mm = cctx->static_mmaps; mm; mm = mm->next) {
464                 size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
465                 struct perf_record_mmap *xrec = xzmalloc(size);
466
467                 xrec->header.type = PERF_RECORD_MMAP;
468                 xrec->header.misc = mm->header_misc;
469                 xrec->header.size = size;
470                 xrec->pid = mm->pid;
471                 xrec->tid = mm->tid;
472                 xrec->addr = mm->addr;
473                 xrec->len = mm->size;
474                 xrec->pgoff = mm->offset;
475                 strcpy(xrec->filename, mm->path);
476
477                 mem_file_write(&cctx->data, xrec, size, 0);
478
479                 free(xrec);
480         }
481 }
482
483 static void emit_comm(uint32_t pid, const char *comm,
484                                           struct perfconv_context *cctx)
485 {
486         size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
487         struct perf_record_comm *xrec = xzmalloc(size);
488
489         xrec->header.type = PERF_RECORD_COMM;
490         xrec->header.misc = PERF_RECORD_MISC_USER;
491         xrec->header.size = size;
492         xrec->pid = xrec->tid = pid;
493         strcpy(xrec->comm, comm);
494
495         mem_file_write(&cctx->data, xrec, size, 0);
496
497         free(xrec);
498 }
499
500 static void emit_pid_mmap64(struct perf_record *pr,
501                                                         struct perfconv_context *cctx)
502 {
503         struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
504         size_t size = sizeof(struct perf_record_mmap) +
505                       strlen((char*)rec->path) + 1;
506         struct perf_record_mmap *xrec = xzmalloc(size);
507
508         xrec->header.type = PERF_RECORD_MMAP;
509         xrec->header.misc = PERF_RECORD_MISC_USER;
510         xrec->header.size = size;
511         xrec->pid = xrec->tid = rec->pid;
512         xrec->addr = rec->addr;
513         xrec->len = rec->size;
514         xrec->pgoff = rec->offset;
515         strcpy(xrec->filename, (char*)rec->path);
516
517         mem_file_write(&cctx->data, xrec, size, 0);
518
519         free(xrec);
520 }
521
522 static void emit_kernel_trace64(struct perf_record *pr,
523                                                                 struct perfconv_context *cctx)
524 {
525         struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
526                 pr->data;
527         size_t size = sizeof(struct perf_record_sample) +
528                 (rec->num_traces - 1) * sizeof(uint64_t);
529         struct perf_record_sample *xrec = xzmalloc(size);
530
531         xrec->header.type = PERF_RECORD_SAMPLE;
532         xrec->header.misc = PERF_RECORD_MISC_KERNEL;
533         xrec->header.size = size;
534         xrec->ip = rec->trace[0];
535         /* TODO: We could have pid/tid for kernel tasks during their lifetime.
536          * During syscalls, we could use the pid of the process.  For the kernel
537          * itself, -1 seems to be generic kernel stuff, and tid == 0 is 'swapper'.
538          *
539          * Right now, the kernel doesn't even tell us the pid, so we have no way of
540          * knowing from userspace. */
541         xrec->pid = -1;
542         xrec->tid = 0;
543         xrec->time = rec->tstamp;
544         xrec->addr = rec->trace[0];
545         xrec->id = perfconv_get_event_id(cctx, rec->info);
546         xrec->cpu = rec->cpu;
547         xrec->nr = rec->num_traces - 1;
548         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
549
550         mem_file_write(&cctx->data, xrec, size, 0);
551
552         free(xrec);
553 }
554
555 static void emit_user_trace64(struct perf_record *pr,
556                                                           struct perfconv_context *cctx)
557 {
558         struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
559                 pr->data;
560         size_t size = sizeof(struct perf_record_sample) +
561                 (rec->num_traces - 1) * sizeof(uint64_t);
562         struct perf_record_sample *xrec = xzmalloc(size);
563
564         xrec->header.type = PERF_RECORD_SAMPLE;
565         xrec->header.misc = PERF_RECORD_MISC_USER;
566         xrec->header.size = size;
567         xrec->ip = rec->trace[0];
568         xrec->pid = xrec->tid = rec->pid;
569         xrec->time = rec->tstamp;
570         xrec->addr = rec->trace[0];
571         xrec->id = perfconv_get_event_id(cctx, rec->info);
572         xrec->cpu = rec->cpu;
573         xrec->nr = rec->num_traces - 1;
574         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
575
576         mem_file_write(&cctx->data, xrec, size, 0);
577
578         free(xrec);
579 }
580
581 static void emit_new_process(struct perf_record *pr,
582                                                          struct perfconv_context *cctx)
583 {
584         struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
585         const char *comm = strrchr((char*)rec->path, '/');
586
587         if (!comm)
588                 comm = (char*)rec->path;
589         else
590                 comm++;
591         emit_comm(rec->pid, comm, cctx);
592 }
593
594 struct perfconv_context *perfconv_create_context(struct perf_context *pctx)
595 {
596         struct perfconv_context *cctx = xzmalloc(sizeof(struct perfconv_context));
597
598         cctx->pctx = pctx;
599         perf_header_init(&cctx->ph);
600         headers_init(&cctx->hdrs);
601         mem_file_init(&cctx->fhdrs);
602         mem_file_init(&cctx->attr_ids);
603         mem_file_init(&cctx->attrs);
604         mem_file_init(&cctx->data);
605         /* event_types is ignored in newer versions of perf */
606         mem_file_init(&cctx->event_types);
607
608         return cctx;
609 }
610
611 void perfconv_free_context(struct perfconv_context *cctx)
612 {
613         if (cctx)
614                 free(cctx);
615 }
616
617 void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
618                                                         FILE *output)
619 {
620         size_t processed_records = 0;
621         uint64_t offset;
622         uint64_t attr_ids_off = sizeof(cctx->ph);
623         struct perf_record pr;
624
625         emit_static_mmaps(cctx);
626
627         while (read_record(input, &pr) == 0) {
628                 dbg_print(cctx, 8, stderr, "Valid record: type=%lu size=%lu\n",
629                                   pr.type, pr.size);
630
631                 processed_records++;
632
633                 switch (pr.type) {
634                 case PROFTYPE_KERN_TRACE64:
635                         emit_kernel_trace64(&pr, cctx);
636                         break;
637                 case PROFTYPE_USER_TRACE64:
638                         emit_user_trace64(&pr, cctx);
639                         break;
640                 case PROFTYPE_PID_MMAP64:
641                         emit_pid_mmap64(&pr, cctx);
642                         break;
643                 case PROFTYPE_NEW_PROCESS:
644                         emit_new_process(&pr, cctx);
645                         break;
646                 default:
647                         fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
648                                         pr.size);
649                         processed_records--;
650                 }
651
652                 free_record(&pr);
653         }
654
655         /* Add all of the headers before outputting ph */
656         headers_build(&cctx->ph, &cctx->hdrs, &cctx->fhdrs);
657
658         /* attrs, events, and data will come after attr_ids. */
659         offset = sizeof(cctx->ph) + cctx->attr_ids.size;
660
661         /* These are the perf_file_sections in the main perf header.  We need this
662          * sorted out before we emit the PH. */
663         cctx->ph.event_types.offset = offset;
664         cctx->ph.event_types.size = cctx->event_types.size;
665         offset += cctx->event_types.size;
666
667         cctx->ph.attrs.offset = offset;
668         cctx->ph.attrs.size = cctx->attrs.size;
669         offset += cctx->attrs.size;
670
671         cctx->ph.data.offset = offset;
672         cctx->ph.data.size = cctx->data.size;
673         offset += cctx->data.size;
674
675         xfwrite(&cctx->ph, sizeof(cctx->ph), output);
676
677         /* attr_ids comes right after the cctx->ph.  We need to put it before
678          * attrs, since attrs needs to know the offset of the base of attrs_ids for
679          * its relocs. */
680         assert(ftell(output) == attr_ids_off);
681         mem_file_sync(&cctx->attr_ids, output, OFFSET_NORELOC);
682         mem_file_sync(&cctx->event_types, output, OFFSET_NORELOC);
683         /* reloc is based off *attr_ids* base */
684         mem_file_sync(&cctx->attrs, output, attr_ids_off);
685         /* Keep data last, so we can append the feature headers.*/
686         mem_file_sync(&cctx->data, output, OFFSET_NORELOC);
687         /* The feature headers must be right after the data section.  I didn't see
688          * anything in the ABI about this, but Linux's perf has this line:
689          *
690          *              ph->feat_offset  = header->data.offset + header->data.size;
691          */
692         mem_file_sync(&cctx->fhdrs, output,
693                       cctx->ph.data.offset + cctx->ph.data.size);
694
695         dbg_print(cctx, 2, stderr, "Conversion succeeded: %lu records converted\n",
696                           processed_records);
697 }