perf: Set sample_period
[akaros.git] / tools / profile / perf / perfconv.c
1 /* Copyright (c) 2015 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * See LICENSE for details.
4  *
5  * Converts kprof profiler files into Linux perf ones. The Linux Perf file
6  * format has bee illustrated here:
7  *
8  *       https://lwn.net/Articles/644919/
9  *       https://openlab-mu-internal.web.cern.ch/openlab-mu-internal/03_Documents/
10  *                       3_Technical_Documents/Technical_Reports/2011/Urs_Fassler_report.pdf
11  *
12  */
13
14 #include <ros/common.h>
15 #include <ros/memops.h>
16 #include <ros/profiler_records.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include "perf_format.h"
27 #include "xlib.h"
28 #include "perf_core.h"
29 #include "perfconv.h"
30
31 #define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
32 #define PERF_RECORD_BUFFER_SIZE 1024
33 #define OFFSET_NORELOC ((uint64_t) -1)
34 #define MEMFILE_BLOCK_SIZE (64 * 1024)
35
36 #define MBWR_SOLID (1 << 0)
37
38 struct perf_record {
39         uint64_t type;
40         uint64_t size;
41         char *data;
42         char buffer[PERF_RECORD_BUFFER_SIZE];
43 };
44
45 static void dbg_print(struct perfconv_context *cctx, int level, FILE *file,
46                                           const char *fmt, ...)
47 {
48         if (cctx->debug_level >= level) {
49                 va_list args;
50
51                 va_start(args, fmt);
52                 vfprintf(file, fmt, args);
53                 va_end(args);
54         }
55 }
56
57 void perfconv_set_dbglevel(int level, struct perfconv_context *cctx)
58 {
59         cctx->debug_level = level;
60 }
61
62 static void free_record(struct perf_record *pr)
63 {
64         if (pr->data != pr->buffer)
65                 free(pr->data);
66         pr->data = NULL;
67 }
68
69 static int read_record(FILE *file, struct perf_record *pr)
70 {
71         if (vb_fdecode_uint64(file, &pr->type) == EOF ||
72                 vb_fdecode_uint64(file, &pr->size) == EOF)
73                 return EOF;
74         if (pr->size > MAX_PERF_RECORD_SIZE) {
75                 fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
76                                 pr->size);
77                 exit(1);
78         }
79         if (pr->size > sizeof(pr->buffer))
80                 pr->data = xmalloc((size_t) pr->size);
81         else
82                 pr->data = pr->buffer;
83         if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
84                 fprintf(stderr, "Unable to read record memory: size=%lu\n",
85                                 pr->size);
86                 return EOF;
87         }
88
89         return 0;
90 }
91
92 static struct mem_block *mem_block_alloc(struct mem_file *mf, size_t size)
93 {
94         struct mem_block *mb = xmem_arena_alloc(mf->ma,
95                                                                                         sizeof(struct mem_block) + size);
96
97         mb->next = NULL;
98         mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
99         mb->top = mb->base + size;
100
101         return mb;
102 }
103
104 static char *mem_block_write(struct mem_block *mb, const void *data,
105                                                          size_t size)
106 {
107         char *wrbase = mb->wptr;
108
109         always_assert(size <= mb->top - mb->wptr);
110
111         memcpy(mb->wptr, data, size);
112         mb->wptr += size;
113
114         return wrbase;
115 }
116
117 static void mem_file_init(struct mem_file *mf, struct mem_arena *ma)
118 {
119         ZERO_DATA(*mf);
120         mf->ma = ma;
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(mf, 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 =
181                 xmem_arena_zalloc(mf->ma, sizeof(struct mem_file_reloc));
182
183         rel->ptr = ptr;
184         rel->next = mf->relocs;
185         mf->relocs = rel;
186
187         return rel;
188 }
189
190 static uint64_t perfconv_make_config_id(bool is_raw, uint64_t type,
191                                                                                 uint64_t event_id)
192 {
193         uint64_t config = is_raw ? (uint64_t) 1 << 63 : 0;
194
195         return config | (type << 56) | (event_id & (((uint64_t) 1 << 56) - 1));
196 }
197
198 static uint64_t perfconv_get_config_type(uint64_t config)
199 {
200         return (config >> 56) & 0x7f;
201 }
202
203 static void add_static_mmap(const char *path, uint64_t addr, uint64_t offset,
204                                                         uint64_t size, uint32_t pid,
205                                                         struct perfconv_context *cctx)
206 {
207         struct static_mmap64 *mm;
208         struct stat stb;
209
210         if (size == 0) {
211                 if (stat(path, &stb)) {
212                         fprintf(stderr, "Unable to stat mmapped file '%s': %s\n",
213                                         path, strerror(errno));
214                         exit(1);
215                 }
216                 size = (uint64_t) stb.st_size;
217         }
218
219         mm = xmem_arena_zalloc(&cctx->ma, sizeof(struct static_mmap64));
220         mm->pid = pid;
221         mm->addr = addr;
222         mm->size = size;
223         mm->offset = offset;
224         mm->path = xmem_arena_strdup(&cctx->ma, path);
225
226         mm->next = cctx->static_mmaps;
227         cctx->static_mmaps = mm;
228 }
229
230 void perfconv_add_kernel_mmap(const char *path, size_t ksize,
231                                                           struct perfconv_context *cctx)
232 {
233         add_static_mmap(path, cctx->kernel_load_address, cctx->kernel_load_address,
234                                         (uint64_t) ksize, 0, cctx);
235 }
236
237 static void headers_init(struct perf_headers *hdrs)
238 {
239         ZERO_DATA(*hdrs);
240 }
241
242 static void headers_add_header(struct perf_headers *hdrs, size_t nhdr,
243                                                            struct mem_block *mb)
244 {
245         always_assert(nhdr < HEADER_FEAT_BITS);
246
247         hdrs->headers[nhdr] = mb;
248 }
249
250 static void headers_write(struct perf_headers *hdrs, struct perf_header *ph,
251                                                   struct mem_file *mf)
252 {
253         size_t i;
254
255         for (i = 0; i < HEADER_FEAT_BITS; i++) {
256                 struct mem_block *mb = hdrs->headers[i];
257
258                 if (mb) {
259                         mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
260                         set_bitno(ph->adds_features, i);
261                 }
262         }
263 }
264
265 static void perf_header_init(struct perf_header *ph)
266 {
267         ZERO_DATA(*ph);
268         ph->magic = PERF_MAGIC2;
269         ph->size = sizeof(*ph);
270         ph->attr_size = sizeof(struct perf_event_attr);
271 }
272
273 static void add_attribute(struct mem_file *amf, struct mem_file *mmf,
274                                                   const struct perf_event_attr *attr,
275                                                   const uint64_t *ids, size_t nids)
276 {
277         struct perf_file_section *psids;
278         struct perf_file_section sids;
279
280         mem_file_write(amf, attr, sizeof(*attr), 0);
281
282         sids.offset = mmf->size;
283         sids.size = nids * sizeof(uint64_t);
284
285         mem_file_write(mmf, ids, nids * sizeof(uint64_t), 0);
286
287         psids = mem_file_write(amf, &sids, sizeof(sids), MBWR_SOLID);
288
289         mem_file_add_reloc(amf, &psids->offset);
290 }
291
292 static void add_default_attribute(struct mem_file *amf, struct mem_file *mmf,
293                                                                   uint64_t config, uint64_t id)
294 {
295         struct perf_event_attr attr;
296
297         ZERO_DATA(attr);
298         attr.type = (uint32_t) perfconv_get_config_type(config);
299         attr.size = sizeof(attr);
300         attr.config = config;
301         attr.mmap = 1;
302         attr.comm = 1;
303         /* We don't know the actual sample_period at this point, but the perf report
304          * percentages are all relative. */
305         attr.sample_period = 1;
306         /* Closely coupled with struct perf_record_sample */
307         attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
308                            PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_CPU |
309                            PERF_SAMPLE_CALLCHAIN;
310         add_attribute(amf, mmf, &attr, &id, 1);
311 }
312
313 static uint64_t perfconv_get_event_id(struct perfconv_context *cctx,
314                                                                           uint64_t info)
315 {
316         size_t i, j, n, ipos;
317         uint64_t id, config;
318         struct perf_event_id *nevents;
319
320         for (;;) {
321                 ipos = (size_t) (info % cctx->alloced_events);
322                 for (i = cctx->alloced_events; (i > 0) &&
323                                  (cctx->events[ipos].event != 0);
324                          i--, ipos = (ipos + 1) % cctx->alloced_events) {
325                         if (cctx->events[ipos].event == info)
326                                 return cctx->events[ipos].id;
327                 }
328                 if (i != 0)
329                         break;
330                 /* Need to resize the hash ...
331                  */
332                 n = 2 * cctx->alloced_events;
333                 nevents = xmem_arena_zalloc(&cctx->ma, n * sizeof(*cctx->events));
334                 for (i = 0; i < cctx->alloced_events; i++) {
335                         if (cctx->events[i].event == 0)
336                                 continue;
337                         j = cctx->events[i].event % n;
338                         for (; nevents[j].event; j = (j + 1) % n)
339                                 ;
340                         nevents[j].event = cctx->events[i].event;
341                         nevents[j].id = cctx->events[i].id;
342                 }
343                 cctx->alloced_events = n;
344                 cctx->events = nevents;
345         }
346
347         cctx->events[ipos].event = info;
348         cctx->events[ipos].id = id = cctx->sqnr_id;
349         cctx->sqnr_id++;
350
351         switch ((int) PROF_INFO_DOM(info)) {
352         case PROF_DOM_PMU:
353                 config = perfconv_make_config_id(TRUE, PERF_TYPE_RAW,
354                                                                                  PROF_INFO_DATA(info));
355                 break;
356         case PROF_DOM_TIMER:
357         default:
358                 config = perfconv_make_config_id(FALSE, PERF_TYPE_HARDWARE,
359                                                                                  PERF_COUNT_HW_CPU_CYCLES);
360         }
361
362         add_default_attribute(&cctx->attrs, &cctx->misc, config, id);
363
364         return id;
365 }
366
367 static void emit_static_mmaps(struct perfconv_context *cctx)
368 {
369         struct static_mmap64 *mm;
370
371         for (mm = cctx->static_mmaps; mm; mm = mm->next) {
372                 size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
373                 struct perf_record_mmap *xrec = xzmalloc(size);
374
375                 xrec->header.type = PERF_RECORD_MMAP;
376                 xrec->header.misc = PERF_RECORD_MISC_USER;
377                 xrec->header.size = size;
378                 xrec->pid = xrec->tid = mm->pid;
379                 xrec->addr = mm->addr;
380                 xrec->len = mm->size;
381                 xrec->pgoff = mm->offset;
382                 strcpy(xrec->filename, mm->path);
383
384                 mem_file_write(&cctx->data, xrec, size, 0);
385
386                 free(xrec);
387         }
388 }
389
390 static void emit_comm(uint32_t pid, const char *comm,
391                                           struct perfconv_context *cctx)
392 {
393         size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
394         struct perf_record_comm *xrec = xzmalloc(size);
395
396         xrec->header.type = PERF_RECORD_COMM;
397         xrec->header.misc = PERF_RECORD_MISC_USER;
398         xrec->header.size = size;
399         xrec->pid = xrec->tid = pid;
400         strcpy(xrec->comm, comm);
401
402         mem_file_write(&cctx->data, xrec, size, 0);
403
404         free(xrec);
405 }
406
407 static void emit_pid_mmap64(struct perf_record *pr,
408                                                         struct perfconv_context *cctx)
409 {
410         struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
411         size_t size = sizeof(struct perf_record_mmap) + strlen(rec->path) + 1;
412         struct perf_record_mmap *xrec = xzmalloc(size);
413
414         xrec->header.type = PERF_RECORD_MMAP;
415         xrec->header.misc = PERF_RECORD_MISC_USER;
416         xrec->header.size = size;
417         xrec->pid = xrec->tid = rec->pid;
418         xrec->addr = rec->addr;
419         xrec->len = rec->size;
420         xrec->pgoff = rec->offset;
421         strcpy(xrec->filename, rec->path);
422
423         mem_file_write(&cctx->data, xrec, size, 0);
424
425         free(xrec);
426 }
427
428 static void emit_kernel_trace64(struct perf_record *pr,
429                                                                 struct perfconv_context *cctx)
430 {
431         struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
432                 pr->data;
433         size_t size = sizeof(struct perf_record_sample) +
434                 (rec->num_traces - 1) * sizeof(uint64_t);
435         struct perf_record_sample *xrec = xzmalloc(size);
436
437         xrec->header.type = PERF_RECORD_SAMPLE;
438         xrec->header.misc = PERF_RECORD_MISC_USER;
439         xrec->header.size = size;
440         xrec->ip = rec->trace[0];
441         xrec->time = rec->tstamp;
442         xrec->addr = rec->trace[0];
443         xrec->id = perfconv_get_event_id(cctx, rec->info);
444         xrec->cpu = rec->cpu;
445         xrec->nr = rec->num_traces - 1;
446         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
447
448         mem_file_write(&cctx->data, xrec, size, 0);
449
450         free(xrec);
451 }
452
453 static void emit_user_trace64(struct perf_record *pr,
454                                                           struct perfconv_context *cctx)
455 {
456         struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
457                 pr->data;
458         size_t size = sizeof(struct perf_record_sample) +
459                 (rec->num_traces - 1) * sizeof(uint64_t);
460         struct perf_record_sample *xrec = xzmalloc(size);
461
462         xrec->header.type = PERF_RECORD_SAMPLE;
463         xrec->header.misc = PERF_RECORD_MISC_USER;
464         xrec->header.size = size;
465         xrec->ip = rec->trace[0];
466         xrec->pid = xrec->tid = rec->pid;
467         xrec->time = rec->tstamp;
468         xrec->addr = rec->trace[0];
469         xrec->id = perfconv_get_event_id(cctx, rec->info);
470         xrec->cpu = rec->cpu;
471         xrec->nr = rec->num_traces - 1;
472         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
473
474         mem_file_write(&cctx->data, xrec, size, 0);
475
476         free(xrec);
477 }
478
479 static void emit_new_process(struct perf_record *pr,
480                                                          struct perfconv_context *cctx)
481 {
482         struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
483         const char *comm = strrchr(rec->path, '/');
484
485         if (!comm)
486                 comm = rec->path;
487         else
488                 comm++;
489         emit_comm(rec->pid, comm, cctx);
490 }
491
492 static void add_event_type(struct mem_file *mf, uint64_t id, const char *name)
493 {
494         struct perf_trace_event_type evt;
495
496         ZERO_DATA(evt);
497         evt.event_id = id;
498         strncpy(evt.name, name, sizeof(evt.name));
499         evt.name[sizeof(evt.name) - 1] = 0;
500
501         mem_file_write(mf, &evt, sizeof(evt), 0);
502 }
503
504 struct perfconv_context *perfconv_create_context(void)
505 {
506         struct perfconv_context *cctx = xzmalloc(sizeof(struct perfconv_context));
507
508         xmem_arena_init(&cctx->ma, 0);
509         cctx->kernel_load_address = 0xffffffffc0000000;
510         cctx->alloced_events = 128;
511         cctx->events = xmem_arena_zalloc(
512                 &cctx->ma, cctx->alloced_events * sizeof(*cctx->events));
513         perf_header_init(&cctx->ph);
514         headers_init(&cctx->hdrs);
515         mem_file_init(&cctx->fhdrs, &cctx->ma);
516         mem_file_init(&cctx->misc, &cctx->ma);
517         mem_file_init(&cctx->attrs, &cctx->ma);
518         mem_file_init(&cctx->data, &cctx->ma);
519         mem_file_init(&cctx->event_types, &cctx->ma);
520
521         emit_comm(0, "[kernel]", cctx);
522
523         return cctx;
524 }
525
526 void perfconv_free_context(struct perfconv_context *cctx)
527 {
528         if (cctx) {
529                 xmem_arena_destroy(&cctx->ma);
530                 free(cctx);
531         }
532 }
533
534 void perfconv_process_input(struct perfconv_context *cctx, FILE *input,
535                                                         FILE *output)
536 {
537         size_t processed_records = 0;
538         uint64_t offset, rel_offset;
539         struct perf_record pr;
540
541         emit_static_mmaps(cctx);
542
543         while (read_record(input, &pr) == 0) {
544                 dbg_print(cctx, 8, stderr, "Valid record: type=%lu size=%lu\n",
545                                   pr.type, pr.size);
546
547                 processed_records++;
548
549                 switch (pr.type) {
550                 case PROFTYPE_KERN_TRACE64:
551                         emit_kernel_trace64(&pr, cctx);
552                         break;
553                 case PROFTYPE_USER_TRACE64:
554                         emit_user_trace64(&pr, cctx);
555                         break;
556                 case PROFTYPE_PID_MMAP64:
557                         emit_pid_mmap64(&pr, cctx);
558                         break;
559                 case PROFTYPE_NEW_PROCESS:
560                         emit_new_process(&pr, cctx);
561                         break;
562                 default:
563                         fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
564                                         pr.size);
565                         processed_records--;
566                 }
567
568                 free_record(&pr);
569         }
570
571         headers_write(&cctx->hdrs, &cctx->ph, &cctx->fhdrs);
572         offset = sizeof(cctx->ph) + cctx->fhdrs.size + cctx->misc.size;
573
574         if (cctx->event_types.size > 0) {
575                 cctx->ph.event_types.offset = offset;
576                 cctx->ph.event_types.size = cctx->event_types.size;
577                 offset += cctx->event_types.size;
578         }
579         if (cctx->attrs.size > 0) {
580                 cctx->ph.attrs.offset = offset;
581                 cctx->ph.attrs.size = cctx->attrs.size;
582                 offset += cctx->attrs.size;
583         }
584         if (cctx->data.size > 0) {
585                 cctx->ph.data.offset = offset;
586                 cctx->ph.data.size = cctx->data.size;
587                 offset += cctx->data.size;
588         }
589
590         xfwrite(&cctx->ph, sizeof(cctx->ph), output);
591         mem_file_sync(&cctx->fhdrs, output, OFFSET_NORELOC);
592
593         rel_offset = (uint64_t) ftell(output);
594         mem_file_sync(&cctx->misc, output, rel_offset);
595
596         mem_file_sync(&cctx->event_types, output, rel_offset);
597         mem_file_sync(&cctx->attrs, output, rel_offset);
598         mem_file_sync(&cctx->data, output, rel_offset);
599
600         dbg_print(cctx, 2, stderr, "Conversion succeeded: %lu records converted\n",
601                           processed_records);
602 }