Added kprof to perf converter
[akaros.git] / tools / profile / kprof2perf / kprof2perf.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 <sys/types.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stdint.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <stdarg.h>
23 #include <ros/profiler_records.h>
24 #include "perf_format.h"
25
26 #define MAX_PERF_RECORD_SIZE (32 * 1024 * 1024)
27 #define PERF_RECORD_BUFFER_SIZE 1024
28 #define OFFSET_NORELOC ((uint64_t) -1)
29
30 #define ID_PERF_SAMPLE 0
31
32 #define MBWR_SOLID (1 << 0)
33
34 #define min(a, b)                                                               \
35         ({ __typeof__(a) _a = (a);                                      \
36                 __typeof__(b) _b = (b);                                 \
37                 _a < _b ? _a : _b; })
38 #define max(a, b)                                                               \
39         ({ __typeof__(a) _a = (a);                                      \
40                 __typeof__(b) _b = (b);                                 \
41                 _a > _b ? _a : _b; })
42 #define always_assert(c)                                                                                                \
43         do {                                                                                                                            \
44                 if (!(c))                                                                                                               \
45                         fprintf(stderr, "%s: %d: Assertion failed: " #c "\n",           \
46                                         __FILE__, __LINE__);                                                            \
47         } while (0)
48
49 struct perf_record {
50         uint64_t type;
51         uint64_t size;
52         char *data;
53         char buffer[PERF_RECORD_BUFFER_SIZE];
54 };
55
56 struct mem_file_reloc {
57         struct mem_file_reloc *next;
58         uint64_t *ptr;
59 };
60
61 struct mem_block {
62         struct mem_block *next;
63         char *base;
64         char *top;
65         char *wptr;
66 };
67
68 struct mem_file {
69         size_t size;
70         struct mem_block *head;
71         struct mem_block *tail;
72         struct mem_file_reloc *relocs;
73 };
74
75 struct perf_headers {
76         struct mem_block *headers[HEADER_FEAT_BITS];
77 };
78
79 struct static_mmap64 {
80         struct static_mmap64 *next;
81         uint64_t addr;
82         uint64_t size;
83         uint64_t offset;
84         uint32_t pid;
85         char *path;
86 };
87
88 static int debug_level;
89 static uint64_t kernel_load_address = 0xffffffffc0000000;
90 static size_t std_block_size = 64 * 1024;
91 static struct static_mmap64 *static_mmaps;
92
93 static inline void set_bitno(void *data, size_t bitno)
94 {
95         ((char *) data)[bitno / 8] |= 1 << (bitno % 8);
96 }
97
98 static inline const char* vb_decode_uint64(const char *data, uint64_t *pval)
99 {
100         unsigned int i;
101         uint64_t val = 0;
102
103         for (i = 0; (*data & 0x80) != 0; i += 7, data++)
104                 val |= (((uint64_t) *data) & 0x7f) << i;
105         *pval = val | ((uint64_t) *data) << i;
106
107         return data + 1;
108 }
109
110 static inline int vb_fdecode_uint64(FILE *file, uint64_t *pval)
111 {
112         unsigned int i = 0;
113         uint64_t val = 0;
114
115         for (;;) {
116                 int c = fgetc(file);
117
118                 if (c == EOF)
119                         return EOF;
120                 val |= (((uint64_t) c) & 0x7f) << i;
121                 i += 7;
122                 if ((c & 0x80) == 0)
123                         break;
124         }
125         *pval = val;
126
127         return i / 7;
128 }
129
130 static void dbg_print(int level, FILE *file, const char *fmt, ...)
131 {
132         if (debug_level >= level) {
133                 va_list args;
134
135                 va_start(args, fmt);
136                 vfprintf(file, fmt, args);
137                 va_end(args);
138         }
139 }
140
141 static void *xmalloc(size_t size)
142 {
143         void *data = malloc(size);
144
145         if (!data) {
146                 fprintf(stderr, "Unable to allocate %lu bytes: %s\n", size,
147                                 strerror(errno));
148                 exit(1);
149         }
150
151         return data;
152 }
153
154 static void *xzmalloc(size_t size)
155 {
156         void *data = xmalloc(size);
157
158         memset(data, 0, size);
159
160         return data;
161 }
162
163 static FILE *xfopen(const char *path, const char *mode)
164 {
165         FILE *file = fopen(path, mode);
166
167         if (!file) {
168                 fprintf(stderr, "Unable to open file '%s' for mode '%s': %s\n",
169                                 path, mode, strerror(errno));
170                 exit(1);
171         }
172
173         return file;
174 }
175
176 static void xfwrite(const void *data, size_t size, FILE *file)
177 {
178         if (fwrite(data, 1, size, file) != size) {
179                 fprintf(stderr, "Unable to write %lu bytes: %s\n", size,
180                                 strerror(errno));
181                 exit(1);
182         }
183 }
184
185 static void xfseek(FILE *file, long offset, int whence)
186 {
187         if (fseek(file, offset, whence)) {
188                 int error = errno;
189
190                 fprintf(stderr, "Unable to seek at offset %ld from %s (fpos=%ld): %s\n",
191                                 offset, whence == SEEK_SET ? "beginning of file" :
192                                 (whence == SEEK_END ? "end of file" : "current position"),
193                                 ftell(file), strerror(error));
194                 exit(1);
195         }
196 }
197
198 static void free_record(struct perf_record *pr)
199 {
200         if (pr->data != pr->buffer)
201                 free(pr->data);
202         pr->data = NULL;
203 }
204
205 static int read_record(FILE *file, struct perf_record *pr)
206 {
207         if (vb_fdecode_uint64(file, &pr->type) == EOF ||
208                 vb_fdecode_uint64(file, &pr->size) == EOF)
209                 return EOF;
210         if (pr->size > MAX_PERF_RECORD_SIZE) {
211                 fprintf(stderr, "Invalid record size: type=%lu size=%lu\n", pr->type,
212                                 pr->size);
213                 exit(1);
214         }
215         if (pr->size > sizeof(pr->buffer))
216                 pr->data = xmalloc((size_t) pr->size);
217         else
218                 pr->data = pr->buffer;
219         if (fread(pr->data, 1, (size_t) pr->size, file) != (size_t) pr->size) {
220                 fprintf(stderr, "Unable to read record memory: size=%lu\n",
221                                 pr->size);
222                 return EOF;
223         }
224
225         return 0;
226 }
227
228 static struct mem_block *mem_block_alloc(size_t size)
229 {
230         struct mem_block *mb = xmalloc(sizeof(struct mem_block) + size);
231
232         mb->next = NULL;
233         mb->base = mb->wptr = (char *) mb + sizeof(struct mem_block);
234         mb->top = mb->base + size;
235
236         return mb;
237 }
238
239 static char *mem_block_write(struct mem_block *mb, const void *data,
240                                                          size_t size)
241 {
242         char *wrbase = mb->wptr;
243
244         always_assert(size <= mb->top - mb->wptr);
245
246         memcpy(mb->wptr, data, size);
247         mb->wptr += size;
248
249         return wrbase;
250 }
251
252 static void mem_file_init(struct mem_file *mf)
253 {
254         memset(mf, 0, sizeof(*mf));
255 }
256
257 static int mem_block_can_write(struct mem_block *mb, size_t size, int flags)
258 {
259         size_t space = mb->top - mb->wptr;
260
261         return (flags & MBWR_SOLID) ? (space >= size) : (space > 0);
262 }
263
264 static void *mem_file_write(struct mem_file *mf, const void *data, size_t size,
265                                                         int flags)
266 {
267         void *wrbase = NULL;
268
269         while (size > 0) {
270                 size_t space, csize;
271                 struct mem_block *mb = mf->tail;
272
273                 if (!mb || !mem_block_can_write(mb, size, flags)) {
274                         mb = mem_block_alloc(max(std_block_size, size));
275                         if (!mf->tail)
276                                 mf->head = mb;
277                         else
278                                 mf->tail->next = mb;
279                         mf->tail = mb;
280                 }
281                 space = mb->top - mb->wptr;
282                 csize = min(size, space);
283
284                 wrbase = mem_block_write(mb, data, csize);
285                 mf->size += csize;
286
287                 size -= csize;
288                 data = (const char *) data + csize;
289         }
290
291         return wrbase;
292 }
293
294 static void mem_file_sync(struct mem_file *mf, FILE *file, uint64_t rel_offset)
295 {
296         struct mem_block *mb;
297
298         if (rel_offset != 0) {
299                 struct mem_file_reloc *rel;
300
301                 always_assert(!mf->relocs || rel_offset != OFFSET_NORELOC);
302
303                 for (rel = mf->relocs; rel; rel = rel->next)
304                         *rel->ptr += rel_offset;
305         }
306
307         for (mb = mf->head; mb; mb = mb->next)
308                 xfwrite(mb->base, mb->wptr - mb->base, file);
309 }
310
311 static struct mem_file_reloc *mem_file_add_reloc(struct mem_file *mf,
312                                                                                                  uint64_t *ptr)
313 {
314         struct mem_file_reloc *rel = xzmalloc(sizeof(struct mem_file_reloc));
315
316         rel->ptr = ptr;
317         rel->next = mf->relocs;
318         mf->relocs = rel;
319
320         return rel;
321 }
322
323 static void add_static_mmap(const char *path, uint64_t addr, uint64_t offset,
324                                                         uint32_t pid)
325 {
326         struct static_mmap64 *mm;
327         struct stat stb;
328
329         if (stat(path, &stb)) {
330                 fprintf(stderr, "Unable to stat mmapped file '%s': %s\n",
331                                 path, strerror(errno));
332                 exit(1);
333         }
334
335         mm = xzmalloc(sizeof(struct static_mmap64));
336         mm->pid = pid;
337         mm->addr = addr;
338         mm->size = stb.st_size;
339         mm->offset = offset;
340         mm->path = strdup(path);
341
342         mm->next = static_mmaps;
343         static_mmaps = mm;
344 }
345
346 static void add_kernel_mmap(const char *path)
347 {
348         add_static_mmap(path, kernel_load_address, kernel_load_address, 0);
349 }
350
351 static void headers_init(struct perf_headers *hdrs)
352 {
353         memset(hdrs, 0, sizeof(*hdrs));
354 }
355
356 static void headers_add_header(struct perf_headers *hdrs, size_t nhdr,
357                                                            struct mem_block *mb)
358 {
359         always_assert(nhdr < HEADER_FEAT_BITS);
360
361         hdrs->headers[nhdr] = mb;
362 }
363
364 static void headers_write(struct perf_headers *hdrs, struct perf_header *ph,
365                                                   struct mem_file *mf)
366 {
367         size_t i;
368
369         for (i = 0; i < HEADER_FEAT_BITS; i++) {
370                 struct mem_block *mb = hdrs->headers[i];
371
372                 if (mb) {
373                         mem_file_write(mf, mb->base, mb->wptr - mb->base, 0);
374                         set_bitno(ph->adds_features, i);
375                 }
376         }
377 }
378
379 static void perf_header_init(struct perf_header *ph)
380 {
381         memset(ph, 0, sizeof(*ph));
382         ph->magic = PERF_MAGIC2;
383         ph->size = sizeof(*ph);
384         ph->attr_size = sizeof(struct perf_event_attr);
385 }
386
387 static void emit_static_mmaps(struct mem_file *mf)
388 {
389         struct static_mmap64 *mm;
390
391         for (mm = static_mmaps; mm; mm = mm->next) {
392                 size_t size = sizeof(struct perf_record_mmap) + strlen(mm->path) + 1;
393                 struct perf_record_mmap *xrec = xzmalloc(size);
394
395                 xrec->header.type = PERF_RECORD_MMAP;
396                 xrec->header.misc = PERF_RECORD_MISC_USER;
397                 xrec->header.size = size;
398                 xrec->pid = xrec->tid = mm->pid;
399                 xrec->addr = mm->addr;
400                 xrec->len = mm->size;
401                 xrec->pgoff = mm->offset;
402                 strcpy(xrec->filename, mm->path);
403
404                 mem_file_write(mf, xrec, size, 0);
405
406                 free(xrec);
407         }
408 }
409
410 static void emit_comm(uint32_t pid, const char *comm, struct mem_file *mf)
411 {
412         size_t size = sizeof(struct perf_record_comm) + strlen(comm) + 1;
413         struct perf_record_comm *xrec = xzmalloc(size);
414
415         xrec->header.type = PERF_RECORD_COMM;
416         xrec->header.misc = PERF_RECORD_MISC_USER;
417         xrec->header.size = size;
418         xrec->pid = xrec->tid = pid;
419         strcpy(xrec->comm, comm);
420
421         mem_file_write(mf, xrec, size, 0);
422
423         free(xrec);
424 }
425
426 static void emit_pid_mmap64(struct perf_record *pr, struct mem_file *mf)
427 {
428         struct proftype_pid_mmap64 *rec = (struct proftype_pid_mmap64 *) pr->data;
429         size_t size = sizeof(struct perf_record_mmap) + strlen(rec->path) + 1;
430         struct perf_record_mmap *xrec = xzmalloc(size);
431
432         xrec->header.type = PERF_RECORD_MMAP;
433         xrec->header.misc = PERF_RECORD_MISC_USER;
434         xrec->header.size = size;
435         xrec->pid = xrec->tid = rec->pid;
436         xrec->addr = rec->addr;
437         xrec->len = rec->size;
438         xrec->pgoff = rec->offset;
439         strcpy(xrec->filename, rec->path);
440
441         mem_file_write(mf, xrec, size, 0);
442
443         free(xrec);
444 }
445
446 static void emit_kernel_trace64(struct perf_record *pr, struct mem_file *mf)
447 {
448         struct proftype_kern_trace64 *rec = (struct proftype_kern_trace64 *)
449                 pr->data;
450         size_t size = sizeof(struct perf_record_sample) +
451                 (rec->num_traces - 1) * sizeof(uint64_t);
452         struct perf_record_sample *xrec = xzmalloc(size);
453
454         xrec->header.type = PERF_RECORD_SAMPLE;
455         xrec->header.misc = PERF_RECORD_MISC_USER;
456         xrec->header.size = size;
457         xrec->ip = rec->trace[0];
458         xrec->time = rec->tstamp;
459         xrec->addr = rec->trace[0];
460         xrec->id = ID_PERF_SAMPLE;
461         xrec->cpu = rec->cpu;
462         xrec->nr = rec->num_traces - 1;
463         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
464
465         mem_file_write(mf, xrec, size, 0);
466
467         free(xrec);
468 }
469
470 static void emit_user_trace64(struct perf_record *pr, struct mem_file *mf)
471 {
472         struct proftype_user_trace64 *rec = (struct proftype_user_trace64 *)
473                 pr->data;
474         size_t size = sizeof(struct perf_record_sample) +
475                 (rec->num_traces - 1) * sizeof(uint64_t);
476         struct perf_record_sample *xrec = xzmalloc(size);
477
478         xrec->header.type = PERF_RECORD_SAMPLE;
479         xrec->header.misc = PERF_RECORD_MISC_USER;
480         xrec->header.size = size;
481         xrec->ip = rec->trace[0];
482         xrec->pid = xrec->tid = rec->pid;
483         xrec->time = rec->tstamp;
484         xrec->addr = rec->trace[0];
485         xrec->id = ID_PERF_SAMPLE;
486         xrec->cpu = rec->cpu;
487         xrec->nr = rec->num_traces - 1;
488         memcpy(xrec->ips, rec->trace + 1, (rec->num_traces - 1) * sizeof(uint64_t));
489
490         mem_file_write(mf, xrec, size, 0);
491
492         free(xrec);
493 }
494
495 static void emit_new_process(struct perf_record *pr, struct mem_file *mf)
496 {
497         struct proftype_new_process *rec = (struct proftype_new_process *) pr->data;
498         const char *comm = strrchr(rec->path, '/');
499
500         if (!comm)
501                 comm = rec->path;
502         else
503                 comm++;
504         emit_comm(rec->pid, comm, mf);
505 }
506
507 static void add_attribute(struct mem_file *amf, struct mem_file *mmf,
508                                                   const struct perf_event_attr *attr,
509                                                   const uint64_t *ids, size_t nids)
510 {
511         struct perf_file_section *psids;
512         struct perf_file_section sids;
513
514         mem_file_write(amf, attr, sizeof(*attr), 0);
515
516         sids.offset = mmf->size;
517         sids.size = nids * sizeof(uint64_t);
518
519         mem_file_write(mmf, ids, nids * sizeof(uint64_t), 0);
520
521         psids = mem_file_write(amf, &sids, sizeof(sids), MBWR_SOLID);
522
523         mem_file_add_reloc(amf, &psids->offset);
524 }
525
526 static void add_default_attribute(struct mem_file *amf, struct mem_file *mmf,
527                                                                   uint64_t event_id, uint64_t id)
528 {
529         struct perf_event_attr attr;
530
531         memset(&attr, 0, sizeof(attr));
532         attr.type = PERF_TYPE_HARDWARE;
533         attr.size = sizeof(attr);
534         attr.config = event_id;
535         attr.mmap = 1;
536         attr.comm = 1;
537         attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME |
538                 PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_CPU |
539                 PERF_SAMPLE_CALLCHAIN;
540
541         add_attribute(amf, mmf, &attr, &id, 1);
542 }
543
544 static void add_event_type(struct mem_file *mf, uint64_t id, const char *name)
545 {
546         struct perf_trace_event_type evt;
547
548         memset(&evt, 0, sizeof(evt));
549         evt.event_id = id;
550         strncpy(evt.name, name, sizeof(evt.name));
551         evt.name[sizeof(evt.name) - 1] = 0;
552
553         mem_file_write(mf, &evt, sizeof(evt), 0);
554 }
555
556 static void process_input(FILE *input, FILE *output)
557 {
558         size_t processed_records = 0;
559         uint64_t offset, rel_offset;
560         struct perf_record pr;
561         struct perf_header ph;
562         struct perf_headers hdrs;
563         struct mem_file fhdrs, misc, attrs, data, event_types;
564
565         perf_header_init(&ph);
566         headers_init(&hdrs);
567         mem_file_init(&fhdrs);
568         mem_file_init(&misc);
569         mem_file_init(&attrs);
570         mem_file_init(&data);
571         mem_file_init(&event_types);
572
573         add_event_type(&event_types, PERF_COUNT_HW_CPU_CYCLES, "cycles");
574         add_default_attribute(&attrs, &misc, PERF_COUNT_HW_CPU_CYCLES,
575                                                   ID_PERF_SAMPLE);
576
577         emit_comm(0, "[kernel]", &data);
578         emit_static_mmaps(&data);
579
580         while (read_record(input, &pr) == 0) {
581                 dbg_print(8, stderr, "Valid record: type=%lu size=%lu\n",
582                                   pr.type, pr.size);
583
584                 processed_records++;
585
586                 switch (pr.type) {
587                 case PROFTYPE_KERN_TRACE64:
588                         emit_kernel_trace64(&pr, &data);
589                         break;
590                 case PROFTYPE_USER_TRACE64:
591                         emit_user_trace64(&pr, &data);
592                         break;
593                 case PROFTYPE_PID_MMAP64:
594                         emit_pid_mmap64(&pr, &data);
595                         break;
596                 case PROFTYPE_NEW_PROCESS:
597                         emit_new_process(&pr, &data);
598                         break;
599                 default:
600                         fprintf(stderr, "Unknown record: type=%lu size=%lu\n", pr.type,
601                                         pr.size);
602                         processed_records--;
603                 }
604
605                 free_record(&pr);
606         }
607
608         headers_write(&hdrs, &ph, &fhdrs);
609         offset = sizeof(ph) + fhdrs.size + misc.size;
610
611         if (event_types.size > 0) {
612                 ph.event_types.offset = offset;
613                 ph.event_types.size = event_types.size;
614                 offset += event_types.size;
615         }
616         if (attrs.size > 0) {
617                 ph.attrs.offset = offset;
618                 ph.attrs.size = attrs.size;
619                 offset += attrs.size;
620         }
621         if (data.size > 0) {
622                 ph.data.offset = offset;
623                 ph.data.size = data.size;
624                 offset += data.size;
625         }
626
627         xfwrite(&ph, sizeof(ph), output);
628         mem_file_sync(&fhdrs, output, OFFSET_NORELOC);
629
630         rel_offset = (uint64_t) ftell(output);
631         mem_file_sync(&misc, output, rel_offset);
632
633         mem_file_sync(&event_types, output, rel_offset);
634         mem_file_sync(&attrs, output, rel_offset);
635         mem_file_sync(&data, output, rel_offset);
636
637         fprintf(stderr, "Conversion succeeded: %lu records converted\n",
638                         processed_records);
639 }
640
641 static void usage(const char *prg)
642 {
643         fprintf(stderr, "Use: %s [-ioskDh]\n"
644                         "\t-i INPUT_FILE                  : Sets the input file path (STDIN).\n"
645                         "\t-o OUTPUT_FILE                 : Sets the output file path (STDOUT).\n"
646                         "\t-k KERN_ELF_FILE               : Sets the kernel file path.\n"
647                         "\t-s SIZE                                : Sets the default memory block size (%lu).\n"
648                         "\t-D DBG_LEVEL                   : Sets the debug level for messages.\n"
649                         "\t-h                                     : Displays this help screen.\n",
650                         prg, std_block_size);
651         exit(1);
652 }
653
654 int main(int argc, const char **argv)
655 {
656         int i;
657         const char *inpath = NULL, *outpath = NULL;
658         FILE *input = stdin, *output = stdout;
659
660         for (i = 1; i < argc; i++) {
661                 if (strcmp(argv[i], "-i") == 0) {
662                         if (++i < argc)
663                                 inpath = argv[i];
664                 } else if (strcmp(argv[i], "-o") == 0) {
665                         if (++i < argc)
666                                 outpath = argv[i];
667                 } else if (strcmp(argv[i], "-s") == 0) {
668                         if (++i < argc)
669                                 std_block_size = atol(argv[i]);
670                 } else if (strcmp(argv[i], "-k") == 0) {
671                         if (++i < argc)
672                                 add_kernel_mmap(argv[i]);
673                 } else if (strcmp(argv[i], "-D") == 0) {
674                         if (++i < argc)
675                                 debug_level = atoi(argv[i]);
676                 } else
677                         usage(argv[0]);
678         }
679         if (inpath)
680                 input = xfopen(inpath, "rb");
681         if (outpath)
682                 output = xfopen(outpath, "wb");
683
684         process_input(input, output);
685
686         fflush(output);
687
688         return 0;
689 }