588ae48a0c81603067c639c36b9a30fcf6738f2e
[platform/kernel/u-boot.git] / tools / proftool.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2023 Google LLC
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6
7 /*
8  * Decode and dump U-Boot trace information into formats that can be used
9  * by trace-cmd or kernelshark
10  *
11  * See doc/develop/trace.rst for more information
12  */
13
14 #include <assert.h>
15 #include <ctype.h>
16 #include <limits.h>
17 #include <regex.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/param.h>
24 #include <sys/types.h>
25
26 #include <compiler.h>
27 #include <trace.h>
28 #include <abuf.h>
29
30 /* Set to 1 to emit version 7 file (currently this doesn't work) */
31 #define VERSION7        0
32
33 /* enable some debug features */
34 #define _DEBUG  0
35
36 /* from linux/kernel.h */
37 #define __ALIGN_MASK(x, mask)   (((x) + (mask)) & ~(mask))
38 #define ALIGN(x, a)             __ALIGN_MASK((x), (typeof(x))(a) - 1)
39
40 enum {
41         FUNCF_TRACE     = 1 << 0,       /* Include this function in trace */
42         TRACE_PAGE_SIZE = 4096,         /* Assumed page size for trace */
43         TRACE_PID       = 1,            /* PID to use for U-Boot */
44         LEN_STACK_SIZE  = 4,            /* number of nested length fix-ups */
45         TRACE_PAGE_MASK = TRACE_PAGE_SIZE - 1,
46         MAX_STACK_DEPTH = 50,           /* Max nested function calls */
47         MAX_LINE_LEN    = 500,          /* Max characters per line */
48 };
49
50 /**
51  * enum out_format_t - supported output formats
52  *
53  * @OUT_FMT_DEFAULT: Use the default for the output file
54  * @OUT_FMT_FUNCTION: Write ftrace 'function' records
55  * @OUT_FMT_FUNCGRAPH: Write ftrace funcgraph_entry and funcgraph_exit records
56  */
57 enum out_format_t {
58         OUT_FMT_DEFAULT,
59         OUT_FMT_FUNCTION,
60         OUT_FMT_FUNCGRAPH,
61 };
62
63 /* Section types for v7 format (trace-cmd format) */
64 enum {
65         SECTION_OPTIONS,
66 };
67
68 /* Option types (trace-cmd format) */
69 enum {
70         OPTION_DONE,
71         OPTION_DATE,
72         OPTION_CPUSTAT,
73         OPTION_BUFFER,
74         OPTION_TRACECLOCK,
75         OPTION_UNAME,
76         OPTION_HOOK,
77         OPTION_OFFSET,
78         OPTION_CPUCOUNT,
79         OPTION_VERSION,
80         OPTION_PROCMAPS,
81         OPTION_TRACEID,
82         OPTION_TIME_SHIFT,
83         OPTION_GUEST,
84         OPTION_TSC2NSEC,
85 };
86
87 /* types of trace records (trace-cmd format) */
88 enum trace_type {
89         __TRACE_FIRST_TYPE = 0,
90
91         TRACE_FN,
92         TRACE_CTX,
93         TRACE_WAKE,
94         TRACE_STACK,
95         TRACE_PRINT,
96         TRACE_BPRINT,
97         TRACE_MMIO_RW,
98         TRACE_MMIO_MAP,
99         TRACE_BRANCH,
100         TRACE_GRAPH_RET,
101         TRACE_GRAPH_ENT,
102 };
103
104 /**
105  * struct func_info - information recorded for each function
106  *
107  * @offset: Function offset in the image, measured from the text_base
108  * @name: Function name
109  * @code_size: Total code size of the function
110  * @flags: Either 0 or FUNCF_TRACE
111  */
112 struct func_info {
113         unsigned long offset;
114         const char *name;
115         unsigned long code_size;
116         unsigned flags;
117 };
118
119 /**
120  * enum trace_line_type - whether to include or exclude a function
121  *
122  * @TRACE_LINE_INCLUDE: Include the function
123  * @TRACE_LINE_EXCLUDE: Exclude the function
124  */
125 enum trace_line_type {
126         TRACE_LINE_INCLUDE,
127         TRACE_LINE_EXCLUDE,
128 };
129
130 /**
131  * struct trace_configline_info - information about a config-file line
132  *
133  * @next: Next line
134  * @type: Line type
135  * @name: identifier name / wildcard
136  * @regex: Regex to use if name starts with '/'
137  */
138 struct trace_configline_info {
139         struct trace_configline_info *next;
140         enum trace_line_type type;
141         const char *name;
142         regex_t regex;
143 };
144
145 /**
146  * struct tw_len - holds information about a length value that need fix-ups
147  *
148  * This is used to record a placeholder for a u32 or u64 length which is written
149  * to the output file but needs to be updated once the length is actually known
150  *
151  * This allows us to write tw->ptr - @len_base to position @ptr in the file
152  *
153  * @ptr: Position of the length value in the file
154  * @base: Base position for the calculation
155  * @size: Size of the length value, in bytes (4 or 8)
156  */
157 struct tw_len {
158         int ptr;
159         int base;
160         int size;
161 };
162
163 /**
164  * struct twriter - Writer for trace records
165  *
166  * Maintains state used when writing the output file in trace-cmd format
167  *
168  * @ptr: Current file position
169  * @len_stack: Stack of length values that need fixing up
170  * @len: Number of items on @len_stack
171  * @str_buf: Buffer of strings (for v7 format)
172  * @str_ptr: Current write-position in the buffer for strings
173  * @fout: Output file
174  */
175 struct twriter {
176         int ptr;
177         struct tw_len len_stack[LEN_STACK_SIZE];
178         int len_count;
179         struct abuf str_buf;
180         int str_ptr;
181         FILE *fout;
182 };
183
184 /* The contents of the trace config file */
185 struct trace_configline_info *trace_config_head;
186
187 /* list of all functions in System.map file, sorted by offset in the image */
188 struct func_info *func_list;
189
190 int func_count;                 /* number of functions */
191 struct trace_call *call_list;   /* list of all calls in the input trace file */
192 int call_count;                 /* number of calls */
193 int verbose;    /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */
194 ulong text_offset;              /* text address of first function */
195 ulong text_base;                /* CONFIG_TEXT_BASE from trace file */
196
197 /* debugging helpers */
198 static void outf(int level, const char *fmt, ...)
199                 __attribute__ ((format (__printf__, 2, 3)));
200 #define error(fmt, b...) outf(0, fmt, ##b)
201 #define warn(fmt, b...) outf(1, fmt, ##b)
202 #define notice(fmt, b...) outf(2, fmt, ##b)
203 #define info(fmt, b...) outf(3, fmt, ##b)
204 #define debug(fmt, b...) outf(4, fmt, ##b)
205
206 static void outf(int level, const char *fmt, ...)
207 {
208         if (verbose >= level) {
209                 va_list args;
210
211                 va_start(args, fmt);
212                 vfprintf(stderr, fmt, args);
213                 va_end(args);
214         }
215 }
216
217 static void usage(void)
218 {
219         fprintf(stderr,
220                 "Usage: proftool [-cmtv] <cmd> <profdata>\n"
221                 "\n"
222                 "Commands\n"
223                 "   dump-ftrace\t\tDump out records in ftrace format for use by trace-cmd\n"
224                 "\n"
225                 "Options:\n"
226                 "   -c <cfg>\tSpecify config file\n"
227                 "   -f <subtype>\tSpecify output subtype\n"
228                 "   -m <map>\tSpecify Systen.map file\n"
229                 "   -o <fname>\tSpecify output file\n"
230                 "   -t <fname>\tSpecify trace data file (from U-Boot 'trace calls')\n"
231                 "   -v <0-4>\tSpecify verbosity\n"
232                 "\n"
233                 "Subtypes for dump-ftrace:\n"
234                 "   function - write function-call records (caller/callee)\n"
235                 "   funcgraph - write function entry/exit records (graph)\n");
236         exit(EXIT_FAILURE);
237 }
238
239 /**
240  * h_cmp_offset - bsearch() function to compare two functions bny their offset
241  *
242  * @v1: Pointer to first function (struct func_info)
243  * @v2: Pointer to second function (struct func_info)
244  * Returns: < 0 if v1 offset < v2 offset, 0 if equal, > 0 otherwise
245  */
246 static int h_cmp_offset(const void *v1, const void *v2)
247 {
248         const struct func_info *f1 = v1, *f2 = v2;
249
250         return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE);
251 }
252
253 /**
254  * read_system_map() - read the System.map file to create a list of functions
255  *
256  * This also reads the text_offset value, since we assume that the first text
257  * symbol is at that address
258  *
259  * @fin: File to read
260  * Returns: 0 if OK, non-zero on error
261  */
262 static int read_system_map(FILE *fin)
263 {
264         unsigned long offset, start = 0;
265         struct func_info *func;
266         char buff[MAX_LINE_LEN];
267         char symtype;
268         char symname[MAX_LINE_LEN + 1];
269         int linenum;
270         int alloced;
271
272         for (linenum = 1, alloced = func_count = 0;; linenum++) {
273                 int fields = 0;
274
275                 if (fgets(buff, sizeof(buff), fin))
276                         fields = sscanf(buff, "%lx %c %100s\n", &offset,
277                                 &symtype, symname);
278                 if (fields == 2) {
279                         continue;
280                 } else if (feof(fin)) {
281                         break;
282                 } else if (fields < 2) {
283                         error("Map file line %d: invalid format\n", linenum);
284                         return 1;
285                 }
286
287                 /* Must be a text symbol */
288                 symtype = tolower(symtype);
289                 if (symtype != 't' && symtype != 'w')
290                         continue;
291
292                 if (func_count == alloced) {
293                         alloced += 256;
294                         func_list = realloc(func_list,
295                                         sizeof(struct func_info) * alloced);
296                         assert(func_list);
297                 }
298                 if (!func_count)
299                         start = offset;
300
301                 func = &func_list[func_count++];
302                 memset(func, '\0', sizeof(*func));
303                 func->offset = offset - start;
304                 func->name = strdup(symname);
305                 func->flags = FUNCF_TRACE;      /* trace by default */
306
307                 /* Update previous function's code size */
308                 if (func_count > 1)
309                         func[-1].code_size = func->offset - func[-1].offset;
310         }
311         notice("%d functions found in map file, start addr %lx\n", func_count,
312                start);
313         text_offset = start;
314
315         return 0;
316 }
317
318 static int read_data(FILE *fin, void *buff, int size)
319 {
320         int err;
321
322         err = fread(buff, 1, size, fin);
323         if (!err)
324                 return 1;
325         if (err != size) {
326                 error("Cannot read trace file at pos %lx\n", ftell(fin));
327                 return -1;
328         }
329         return 0;
330 }
331
332 /**
333  * find_func_by_offset() - Look up a function by its offset
334  *
335  * @offset: Offset to search for, from text_base
336  * Returns: function, if found, else NULL
337  *
338  * This does a fast search for a function given its offset from text_base
339  *
340  */
341 static struct func_info *find_func_by_offset(uint offset)
342 {
343         struct func_info key, *found;
344
345         key.offset = offset;
346         found = bsearch(&key, func_list, func_count, sizeof(struct func_info),
347                         h_cmp_offset);
348
349         return found;
350 }
351
352 /**
353  * find_caller_by_offset() - finds the function which contains the given offset
354  *
355  * @offset: Offset to search for, from text_base
356  * Returns: function, if found, else NULL
357  *
358  * If the offset falls between two functions, then it is assumed to belong to
359  * the first function (with the lowest offset). This is a way of figuring out
360  * which function owns code at a particular offset
361  */
362 static struct func_info *find_caller_by_offset(uint offset)
363 {
364         int low;        /* least function that could be a match */
365         int high;       /* greated function that could be a match */
366         struct func_info key;
367
368         low = 0;
369         high = func_count - 1;
370         key.offset = offset;
371         while (high > low + 1) {
372                 int mid = (low + high) / 2;
373                 int result;
374
375                 result = h_cmp_offset(&key, &func_list[mid]);
376                 if (result > 0)
377                         low = mid;
378                 else if (result < 0)
379                         high = mid;
380                 else
381                         return &func_list[mid];
382         }
383
384         return low >= 0 ? &func_list[low] : NULL;
385 }
386
387 /**
388  * read_calls() - Read the list of calls from the trace data
389  *
390  * The calls are stored consecutively in the trace output produced by U-Boot
391  *
392  * @fin: File to read from
393  * @count: Number of calls to read
394  * Returns: 0 if OK, -1 on error
395  */
396 static int read_calls(FILE *fin, size_t count)
397 {
398         struct trace_call *call_data;
399         int i;
400
401         notice("call count: %zu\n", count);
402         call_list = (struct trace_call *)calloc(count, sizeof(*call_data));
403         if (!call_list) {
404                 error("Cannot allocate call_list\n");
405                 return -1;
406         }
407         call_count = count;
408
409         call_data = call_list;
410         for (i = 0; i < count; i++, call_data++) {
411                 if (read_data(fin, call_data, sizeof(*call_data)))
412                         return -1;
413         }
414         return 0;
415 }
416
417 /**
418  * read_trace() - Read the U-Boot trace file
419  *
420  * Read in the calls from the trace file. The function list is ignored at
421  * present
422  *
423  * @fin: File to read
424  * Returns 0 if OK, non-zero on error
425  */
426 static int read_trace(FILE *fin)
427 {
428         struct trace_output_hdr hdr;
429
430         while (!feof(fin)) {
431                 int err;
432
433                 err = read_data(fin, &hdr, sizeof(hdr));
434                 if (err == 1)
435                         break; /* EOF */
436                 else if (err)
437                         return 1;
438                 text_base = hdr.text_base;
439
440                 switch (hdr.type) {
441                 case TRACE_CHUNK_FUNCS:
442                         /* Ignored at present */
443                         break;
444
445                 case TRACE_CHUNK_CALLS:
446                         if (read_calls(fin, hdr.rec_count))
447                                 return 1;
448                         break;
449                 }
450         }
451         return 0;
452 }
453
454 /**
455  * read_map_file() - Read the System.map file
456  *
457  * This reads the file into the func_list array
458  *
459  * @fname: Filename to read
460  * Returns 0 if OK, non-zero on error
461  */
462 static int read_map_file(const char *fname)
463 {
464         FILE *fmap;
465         int err = 0;
466
467         fmap = fopen(fname, "r");
468         if (!fmap) {
469                 error("Cannot open map file '%s'\n", fname);
470                 return 1;
471         }
472         if (fmap) {
473                 err = read_system_map(fmap);
474                 fclose(fmap);
475         }
476         return err;
477 }
478
479 /**
480  * read_trace_file() - Open and read the U-Boot trace file
481  *
482  * Read in the calls from the trace file. The function list is ignored at
483  * present
484  *
485  * @fin: File to read
486  * Returns 0 if OK, non-zero on error
487  */
488 static int read_trace_file(const char *fname)
489 {
490         FILE *fprof;
491         int err;
492
493         fprof = fopen(fname, "rb");
494         if (!fprof) {
495                 error("Cannot open trace data file '%s'\n",
496                       fname);
497                 return 1;
498         } else {
499                 err = read_trace(fprof);
500                 fclose(fprof);
501                 if (err)
502                         return err;
503         }
504         return 0;
505 }
506
507 static int regex_report_error(regex_t *regex, int err, const char *op,
508                               const char *name)
509 {
510         char buf[200];
511
512         regerror(err, regex, buf, sizeof(buf));
513         error("Regex error '%s' in %s '%s'\n", buf, op, name);
514         return -1;
515 }
516
517 static void check_trace_config_line(struct trace_configline_info *item)
518 {
519         struct func_info *func, *end;
520         int err;
521
522         debug("Checking trace config line '%s'\n", item->name);
523         for (func = func_list, end = func + func_count; func < end; func++) {
524                 err = regexec(&item->regex, func->name, 0, NULL, 0);
525                 debug("   - regex '%s', string '%s': %d\n", item->name,
526                       func->name, err);
527                 if (err == REG_NOMATCH)
528                         continue;
529
530                 if (err) {
531                         regex_report_error(&item->regex, err, "match",
532                                            item->name);
533                         break;
534                 }
535
536                 /* It matches, so perform the action */
537                 switch (item->type) {
538                 case TRACE_LINE_INCLUDE:
539                         info("      include %s at %lx\n", func->name,
540                              text_offset + func->offset);
541                         func->flags |= FUNCF_TRACE;
542                         break;
543
544                 case TRACE_LINE_EXCLUDE:
545                         info("      exclude %s at %lx\n", func->name,
546                              text_offset + func->offset);
547                         func->flags &= ~FUNCF_TRACE;
548                         break;
549                 }
550         }
551 }
552
553 /** check_trace_config() - Check trace-config file, reporting any problems */
554 static void check_trace_config(void)
555 {
556         struct trace_configline_info *line;
557
558         for (line = trace_config_head; line; line = line->next)
559                 check_trace_config_line(line);
560 }
561
562 /**
563  * read_trace_config() - read the trace-config file
564  *
565  * This file consists of lines like:
566  *
567  * include-func <regex>
568  * exclude-func <regex>
569  *
570  * where <regex> is a regular expression matched against function names. It
571  * allows some functions to be dropped from the trace when producing ftrace
572  * records
573  *
574  * @fin: File to process
575  * Returns: 0 if OK, -1 on error
576  */
577 static int read_trace_config(FILE *fin)
578 {
579         char buff[200];
580         int linenum = 0;
581         struct trace_configline_info **tailp = &trace_config_head;
582
583         while (fgets(buff, sizeof(buff), fin)) {
584                 int len = strlen(buff);
585                 struct trace_configline_info *line;
586                 char *saveptr;
587                 char *s, *tok;
588                 int err;
589
590                 linenum++;
591                 if (len && buff[len - 1] == '\n')
592                         buff[len - 1] = '\0';
593
594                 /* skip blank lines and comments */
595                 for (s = buff; *s == ' ' || *s == '\t'; s++)
596                         ;
597                 if (!*s || *s == '#')
598                         continue;
599
600                 line = (struct trace_configline_info *)calloc(1, sizeof(*line));
601                 if (!line) {
602                         error("Cannot allocate config line\n");
603                         return -1;
604                 }
605
606                 tok = strtok_r(s, " \t", &saveptr);
607                 if (!tok) {
608                         error("Invalid trace config data on line %d\n",
609                               linenum);
610                         return -1;
611                 }
612                 if (0 == strcmp(tok, "include-func")) {
613                         line->type = TRACE_LINE_INCLUDE;
614                 } else if (0 == strcmp(tok, "exclude-func")) {
615                         line->type = TRACE_LINE_EXCLUDE;
616                 } else {
617                         error("Unknown command in trace config data line %d\n",
618                               linenum);
619                         return -1;
620                 }
621
622                 tok = strtok_r(NULL, " \t", &saveptr);
623                 if (!tok) {
624                         error("Missing pattern in trace config data line %d\n",
625                               linenum);
626                         return -1;
627                 }
628
629                 err = regcomp(&line->regex, tok, REG_NOSUB);
630                 if (err) {
631                         int r = regex_report_error(&line->regex, err,
632                                                    "compile", tok);
633                         free(line);
634                         return r;
635                 }
636
637                 /* link this new one to the end of the list */
638                 line->name = strdup(tok);
639                 line->next = NULL;
640                 *tailp = line;
641                 tailp = &line->next;
642         }
643
644         if (!feof(fin)) {
645                 error("Cannot read from trace config file at position %ld\n",
646                       ftell(fin));
647                 return -1;
648         }
649         return 0;
650 }
651
652 static int read_trace_config_file(const char *fname)
653 {
654         FILE *fin;
655         int err;
656
657         fin = fopen(fname, "r");
658         if (!fin) {
659                 error("Cannot open trace_config file '%s'\n", fname);
660                 return -1;
661         }
662         err = read_trace_config(fin);
663         fclose(fin);
664         return err;
665 }
666
667 /**
668  * tputh() - Write a 16-bit little-endian value to a file
669  *
670  * @fout: File to write to
671  * @val: Value to write
672  * Returns: number of bytes written (2)
673  */
674 static int tputh(FILE *fout, unsigned int val)
675 {
676         fputc(val, fout);
677         fputc(val >> 8, fout);
678
679         return 2;
680 }
681
682 /**
683  * tputl() - Write a 32-bit little-endian value to a file
684  *
685  * @fout: File to write to
686  * @val: Value to write
687  * Returns: number of bytes written (4)
688  */
689 static int tputl(FILE *fout, ulong val)
690 {
691         fputc(val, fout);
692         fputc(val >> 8, fout);
693         fputc(val >> 16, fout);
694         fputc(val >> 24, fout);
695
696         return 4;
697 }
698
699 /**
700  * tputh() - Write a 64-bit little-endian value to a file
701  *
702  * @fout: File to write to
703  * @val: Value to write
704  * Returns: number of bytes written (8)
705  */
706 static int tputq(FILE *fout, unsigned long long val)
707 {
708         tputl(fout, val);
709         tputl(fout, val >> 32U);
710
711         return 8;
712 }
713
714 /**
715  * tputh() - Write a string to a file
716  *
717  * The string is written without its terminator
718  *
719  * @fout: File to write to
720  * @val: Value to write
721  * Returns: number of bytes written
722  */
723 static int tputs(FILE *fout, const char *str)
724 {
725         fputs(str, fout);
726
727         return strlen(str);
728 }
729
730 /**
731  * add_str() - add a name string to the string table
732  *
733  * This is used by the v7 format
734  *
735  * @tw: Writer context
736  * @name: String to write
737  * Returns: Updated value of string pointer, or -1 if out of memory
738  */
739 static int add_str(struct twriter *tw, const char *name)
740 {
741         int str_ptr;
742         int len;
743
744         len = strlen(name) + 1;
745         str_ptr = tw->str_ptr;
746         tw->str_ptr += len;
747
748         if (tw->str_ptr > abuf_size(&tw->str_buf)) {
749                 int new_size;
750
751                 new_size = ALIGN(tw->str_ptr, 4096);
752                 if (!abuf_realloc(&tw->str_buf, new_size))
753                         return -1;
754         }
755
756         return str_ptr;
757 }
758
759 /**
760  * push_len() - Push a new length request onto the stack
761  *
762  * @tw: Writer context
763  * @base: Base position of the length calculation
764  * @msg: Indicates the type of caller, for debugging
765  * @size: Size of the length value, either 4 bytes or 8
766  * Returns number of bytes written to the file (=@size on success), -ve on error
767  *
768  * This marks a place where a length must be written, covering data that is
769  * about to be written. It writes a placeholder value.
770  *
771  * Once the data is written, calling pop_len() will update the placeholder with
772  * the correct length based on how many bytes have been written
773  */
774 static int push_len(struct twriter *tw, int base, const char *msg, int size)
775 {
776         struct tw_len *lp;
777
778         if (tw->len_count >= LEN_STACK_SIZE) {
779                 fprintf(stderr, "Length-stack overflow: %s\n", msg);
780                 return -1;
781         }
782         if (size != 4 && size != 8) {
783                 fprintf(stderr, "Length-stack invalid size %d: %s\n", size,
784                         msg);
785                 return -1;
786         }
787
788         lp = &tw->len_stack[tw->len_count++];
789         lp->base = base;
790         lp->ptr = tw->ptr;
791         lp->size = size;
792
793         return size == 8 ? tputq(tw->fout, 0) : tputl(tw->fout, 0);
794 }
795
796 /**
797  * pop_len() - Update a length value once the length is known
798  *
799  * Pops a value of the length stack and updates the file at that position with
800  * the number of bytes written between now and then. Once done, the file is
801  * seeked to the current (tw->ptr) position again, so writing can continue as
802  * normal.
803  *
804  * @tw: Writer context
805  * @msg: Indicates the type of caller, for debugging
806  * Returns 0 if OK, -1 on error
807  */
808 static int pop_len(struct twriter *tw, const char *msg)
809 {
810         struct tw_len *lp;
811         int len, ret;
812
813         if (!tw->len_count) {
814                 fprintf(stderr, "Length-stack underflow: %s\n", msg);
815                 return -1;
816         }
817
818         lp = &tw->len_stack[--tw->len_count];
819         if (fseek(tw->fout, lp->ptr, SEEK_SET))
820                 return -1;
821         len = tw->ptr - lp->base;
822         ret = lp->size == 8 ? tputq(tw->fout, len) : tputl(tw->fout, len);
823         if (ret < 0)
824                 return -1;
825         if (fseek(tw->fout, tw->ptr, SEEK_SET))
826                 return -1;
827
828         return 0;
829 }
830
831 /**
832  * start_header() - Start a v7 section
833  *
834  * Writes a header in v7 format
835  *
836  * @tw: Writer context
837  * @id: ID of header to write (SECTION_...)
838  * @flags: Flags value to write
839  * @name: Name of section
840  * Returns: number of bytes written
841  */
842 static int start_header(struct twriter *tw, int id, uint flags,
843                         const char *name)
844 {
845         int str_id;
846         int lptr;
847         int base;
848         int ret;
849
850         base = tw->ptr + 16;
851         lptr = 0;
852         lptr += tputh(tw->fout, id);
853         lptr += tputh(tw->fout, flags);
854         str_id = add_str(tw, name);
855         if (str_id < 0)
856                 return -1;
857         lptr += tputl(tw->fout, str_id);
858
859         /* placeholder for size */
860         ret = push_len(tw, base, "v7 header", 8);
861         if (ret < 0)
862                 return -1;
863         lptr += ret;
864
865         return lptr;
866 }
867
868 /**
869  * start_page() - Start a new page of output data
870  *
871  * The output is arranged in 4KB pages with a base timestamp at the start of
872  * each. This starts a new page, making sure it is aligned to 4KB in the output
873  * file.
874  *
875  * @tw: Writer context
876  * @timestamp: Base timestamp for the page
877  */
878 static int start_page(struct twriter *tw, ulong timestamp)
879 {
880         int start;
881         int ret;
882
883         /* move to start of next page */
884         start = ALIGN(tw->ptr, TRACE_PAGE_SIZE);
885         ret = fseek(tw->fout, start, SEEK_SET);
886         if (ret < 0) {
887                 fprintf(stderr, "Cannot seek to page start\n");
888                 return -1;
889         }
890         tw->ptr = start;
891
892         /* page header */
893         tw->ptr += tputq(tw->fout, timestamp);
894         ret = push_len(tw, start + 16, "page", 8);
895         if (ret < 0)
896                 return ret;
897         tw->ptr += ret;
898
899         return 0;
900 }
901
902 /**
903  * finish_page() - finish a page
904  *
905  * Sets the lengths correctly and moves to the start of the next page
906  *
907  * @tw: Writer context
908  * Returns: 0 on success, -1 on error
909  */
910 static int finish_page(struct twriter *tw)
911 {
912         int ret, end;
913
914         ret = pop_len(tw, "page");
915         if (ret < 0)
916                 return ret;
917         end = ALIGN(tw->ptr, TRACE_PAGE_SIZE);
918
919         /*
920          * Write a byte so that the data actually makes to the file, in the case
921          * that we never write any more pages
922          */
923         if (tw->ptr != end) {
924                 if (fseek(tw->fout, end - 1, SEEK_SET)) {
925                         fprintf(stderr, "cannot seek to start of next page\n");
926                         return -1;
927                 }
928                 fputc(0, tw->fout);
929                 tw->ptr = end;
930         }
931
932         return 0;
933 }
934
935 /**
936  * output_headers() - Output v6 headers to the file
937  *
938  * Writes out the various formats so that trace-cmd and kernelshark can make
939  * sense of the data
940  *
941  * This updates tw->ptr as it goes
942  *
943  * @tw: Writer context
944  * Returns: 0 on success, -ve on error
945  */
946 static int output_headers(struct twriter *tw)
947 {
948         FILE *fout = tw->fout;
949         char str[800];
950         int len, ret;
951
952         tw->ptr += fprintf(fout, "%c%c%ctracing6%c%c%c", 0x17, 0x08, 0x44,
953                            0 /* terminator */, 0 /* little endian */,
954                            4 /* 32-bit long values */);
955
956         /* host-machine page size 4KB */
957         tw->ptr += tputl(fout, 4 << 10);
958
959         tw->ptr += fprintf(fout, "header_page%c", 0);
960
961         snprintf(str, sizeof(str),
962                  "\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0;\n"
963                  "\tfield: local_t commit;\toffset:8;\tsize:8;\tsigned:1;\n"
964                  "\tfield: int overwrite;\toffset:8;\tsize:1;\tsigned:1;\n"
965                  "\tfield: char data;\toffset:16;\tsize:4080;\tsigned:1;\n");
966         len = strlen(str);
967         tw->ptr += tputq(fout, len);
968         tw->ptr += tputs(fout, str);
969
970         if (VERSION7) {
971                 /* no compression */
972                 tw->ptr += fprintf(fout, "none%cversion%c\n", 0, 0);
973
974                 ret = start_header(tw, SECTION_OPTIONS, 0, "options");
975                 if (ret < 0) {
976                         fprintf(stderr, "Cannot start option header\n");
977                         return -1;
978                 }
979                 tw->ptr += ret;
980                 tw->ptr += tputh(fout, OPTION_DONE);
981                 tw->ptr += tputl(fout, 8);
982                 tw->ptr += tputl(fout, 0);
983                 ret = pop_len(tw, "t7 header");
984                 if (ret < 0) {
985                         fprintf(stderr, "Cannot finish option header\n");
986                         return -1;
987                 }
988         }
989
990         tw->ptr += fprintf(fout, "header_event%c", 0);
991         snprintf(str, sizeof(str),
992                  "# compressed entry header\n"
993                  "\ttype_len    :    5 bits\n"
994                  "\ttime_delta  :   27 bits\n"
995                  "\tarray       :   32 bits\n"
996                  "\n"
997                  "\tpadding     : type == 29\n"
998                  "\ttime_extend : type == 30\n"
999                  "\ttime_stamp : type == 31\n"
1000                  "\tdata max type_len  == 28\n");
1001         len = strlen(str);
1002         tw->ptr += tputq(fout, len);
1003         tw->ptr += tputs(fout, str);
1004
1005         /* number of ftrace-event-format files */
1006         tw->ptr += tputl(fout, 3);
1007
1008         snprintf(str, sizeof(str),
1009                  "name: function\n"
1010                  "ID: 1\n"
1011                  "format:\n"
1012                  "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
1013                  "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
1014                  "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
1015                  "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
1016                  "\n"
1017                  "\tfield:unsigned long ip;\toffset:8;\tsize:8;\tsigned:0;\n"
1018                  "\tfield:unsigned long parent_ip;\toffset:16;\tsize:8;\tsigned:0;\n"
1019                  "\n"
1020                  "print fmt: \" %%ps <-- %%ps\", (void *)REC->ip, (void *)REC->parent_ip\n");
1021         len = strlen(str);
1022         tw->ptr += tputq(fout, len);
1023         tw->ptr += tputs(fout, str);
1024
1025         snprintf(str, sizeof(str),
1026                  "name: funcgraph_entry\n"
1027                  "ID: 11\n"
1028                  "format:\n"
1029                  "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
1030                  "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
1031                  "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
1032                  "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
1033                  "\n"
1034                  "\tfield:unsigned long func;\toffset:8;\tsize:8;\tsigned:0;\n"
1035                  "\tfield:int depth;\toffset:16;\tsize:4;\tsigned:1;\n"
1036                 "\n"
1037                  "print fmt: \"--> %%ps (%%d)\", (void *)REC->func, REC->depth\n");
1038         len = strlen(str);
1039         tw->ptr += tputq(fout, len);
1040         tw->ptr += tputs(fout, str);
1041
1042         snprintf(str, sizeof(str),
1043                  "name: funcgraph_exit\n"
1044                  "ID: 10\n"
1045                  "format:\n"
1046                  "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
1047                  "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
1048                  "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
1049                  "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
1050                  "\n"
1051                  "\tfield:unsigned long func;\toffset:8;\tsize:8;\tsigned:0;\n"
1052                  "\tfield:int depth;\toffset:16;\tsize:4;\tsigned:1;\n"
1053                  "\tfield:unsigned int overrun;\toffset:20;\tsize:4;\tsigned:0;\n"
1054                  "\tfield:unsigned long long calltime;\toffset:24;\tsize:8;\tsigned:0;\n"
1055                  "\tfield:unsigned long long rettime;\toffset:32;\tsize:8;\tsigned:0;\n"
1056                  "\n"
1057                  "print fmt: \"<-- %%ps (%%d) (start: %%llx  end: %%llx) over: %%d\", (void *)REC->func, REC->depth, REC->calltime, REC->rettime, REC->depth\n");
1058         len = strlen(str);
1059         tw->ptr += tputq(fout, len);
1060         tw->ptr += tputs(fout, str);
1061
1062         return 0;
1063 }
1064
1065 /**
1066  * write_symbols() - Write the symbols out
1067  *
1068  * Writes the symbol information in the following format to mimic the Linux
1069  * /proc/kallsyms file:
1070  *
1071  * <address> T <name>
1072  *
1073  * This updates tw->ptr as it goes
1074  *
1075  * @tw: Writer context
1076  * Returns: 0 on success, -ve on error
1077  */
1078 static int write_symbols(struct twriter *tw)
1079 {
1080         char str[200];
1081         int ret, i;
1082
1083         /* write symbols */
1084         ret = push_len(tw, tw->ptr + 4, "syms", 4);
1085         if (ret < 0)
1086                 return -1;
1087         tw->ptr += ret;
1088         for (i = 0; i < func_count; i++) {
1089                 struct func_info *func = &func_list[i];
1090
1091                 snprintf(str, sizeof(str), "%016lx T %s\n",
1092                          text_offset + func->offset, func->name);
1093                 tw->ptr += tputs(tw->fout, str);
1094         }
1095         ret = pop_len(tw, "syms");
1096         if (ret < 0)
1097                 return -1;
1098         tw->ptr += ret;
1099
1100         return 0;
1101 }
1102
1103 /**
1104  * write_options() - Write the options out
1105  *
1106  * Writes various options which are needed or useful. We use OPTION_TSC2NSEC
1107  * to indicates that values in the output need to be multiplied by 1000 since
1108  * U-Boot's trace values are in microseconds.
1109  *
1110  * This updates tw->ptr as it goes
1111  *
1112  * @tw: Writer context
1113  * Returns: 0 on success, -ve on error
1114  */
1115 static int write_options(struct twriter *tw)
1116 {
1117         FILE *fout = tw->fout;
1118         char str[200];
1119         int len;
1120
1121         /* trace_printk, 0 for now */
1122         tw->ptr += tputl(fout, 0);
1123
1124         /* processes */
1125         snprintf(str, sizeof(str), "%d u-boot\n", TRACE_PID);
1126         len = strlen(str);
1127         tw->ptr += tputq(fout, len);
1128         tw->ptr += tputs(fout, str);
1129
1130         /* number of CPUs */
1131         tw->ptr += tputl(fout, 1);
1132
1133         tw->ptr += fprintf(fout, "options  %c", 0);
1134
1135         /* traceclock */
1136         tw->ptr += tputh(fout, OPTION_TRACECLOCK);
1137         tw->ptr += tputl(fout, 0);
1138
1139         /* uname */
1140         tw->ptr += tputh(fout, OPTION_UNAME);
1141         snprintf(str, sizeof(str), "U-Boot");
1142         len = strlen(str);
1143         tw->ptr += tputl(fout, len);
1144         tw->ptr += tputs(fout, str);
1145
1146         /* version */
1147         tw->ptr += tputh(fout, OPTION_VERSION);
1148         snprintf(str, sizeof(str), "unknown");
1149         len = strlen(str);
1150         tw->ptr += tputl(fout, len);
1151         tw->ptr += tputs(fout, str);
1152
1153         /* trace ID */
1154         tw->ptr += tputh(fout, OPTION_TRACEID);
1155         tw->ptr += tputl(fout, 8);
1156         tw->ptr += tputq(fout, 0x123456780abcdef0);
1157
1158         /* time conversion */
1159         tw->ptr += tputh(fout, OPTION_TSC2NSEC);
1160         tw->ptr += tputl(fout, 16);
1161         tw->ptr += tputl(fout, 1000);   /* multiplier */
1162         tw->ptr += tputl(fout, 0);      /* shift */
1163         tw->ptr += tputq(fout, 0);      /* offset */
1164
1165         /* cpustat - bogus data for now, but at least it mentions the CPU */
1166         tw->ptr += tputh(fout, OPTION_CPUSTAT);
1167         snprintf(str, sizeof(str),
1168                  "CPU: 0\n"
1169                  "entries: 100\n"
1170                  "overrun: 43565\n"
1171                  "commit overrun: 0\n"
1172                  "bytes: 3360\n"
1173                  "oldest event ts: 963732.447752\n"
1174                  "now ts: 963832.146824\n"
1175                  "dropped events: 0\n"
1176                  "read events: 42379\n");
1177         len = strlen(str);
1178         tw->ptr += tputl(fout, len);
1179         tw->ptr += tputs(fout, str);
1180
1181         tw->ptr += tputh(fout, OPTION_DONE);
1182
1183         return 0;
1184 }
1185
1186 /**
1187  * calc_min_depth() - Calculate the minimum call depth from the call list
1188  *
1189  * Starting with a depth of 0, this works through the call list, adding 1 for
1190  * each function call and subtracting 1 for each function return. Most likely
1191  * the value ends up being negative, since the trace does not start at the
1192  * very top of the call stack, e.g. main(), but some function called by that.
1193  *
1194  * This value can be used to calculate the depth value for the first call,
1195  * such that it never goes negative for subsequent returns.
1196  *
1197  * Returns: minimum call depth (e.g. -2)
1198  */
1199 static int calc_min_depth(void)
1200 {
1201         struct trace_call *call;
1202         int depth, min_depth, i;
1203
1204         /* Calculate minimum depth */
1205         depth = 0;
1206         min_depth = 0;
1207         for (i = 0, call = call_list; i < call_count; i++, call++) {
1208                 switch (TRACE_CALL_TYPE(call)) {
1209                 case FUNCF_ENTRY:
1210                         depth++;
1211                         break;
1212                 case FUNCF_EXIT:
1213                         depth--;
1214                         if (depth < min_depth)
1215                                 min_depth = depth;
1216                         break;
1217                 }
1218         }
1219
1220         return min_depth;
1221 }
1222
1223 /**
1224  * write_pages() - Write the pages of trace data
1225  *
1226  * This works through all the calls, writing out as many pages of data as are
1227  * needed.
1228  *
1229  * @tw: Writer context
1230  * @out_format: Output format to use
1231  * @missing_countp: Returns number of missing functions (not found in function
1232  * list)
1233  * @skip_countp: Returns number of skipped functions (excluded from trace)
1234  *
1235  * Returns: 0 on success, -ve on error
1236  */
1237 static int write_pages(struct twriter *tw, enum out_format_t out_format,
1238                        int *missing_countp, int *skip_countp)
1239 {
1240         ulong func_stack[MAX_STACK_DEPTH];
1241         int stack_ptr;  /* next free position in stack */
1242         int upto, depth, page_upto, i;
1243         int missing_count = 0, skip_count = 0;
1244         struct trace_call *call;
1245         ulong last_timestamp;
1246         FILE *fout = tw->fout;
1247         int last_delta = 0;
1248         int err_count;
1249         bool in_page;
1250
1251         in_page = false;
1252         last_timestamp = 0;
1253         upto = 0;
1254         page_upto = 0;
1255         err_count = 0;
1256
1257         /* maintain a stack of start times for calling functions */
1258         stack_ptr = 0;
1259
1260         /*
1261          * The first thing in the trace may not be the top-level function, so
1262          * set the initial depth so that no function goes below depth 0
1263          */
1264         depth = -calc_min_depth();
1265         for (i = 0, call = call_list; i < call_count; i++, call++) {
1266                 bool entry = TRACE_CALL_TYPE(call) == FUNCF_ENTRY;
1267                 struct func_info *func;
1268                 ulong timestamp;
1269                 uint rec_words;
1270                 int delta;
1271
1272                 func = find_func_by_offset(call->func);
1273                 if (!func) {
1274                         warn("Cannot find function at %lx\n",
1275                              text_offset + call->func);
1276                         missing_count++;
1277                         if (missing_count > 20) {
1278                                 /* perhaps trace does not match System.map */
1279                                 fprintf(stderr, "Too many missing functions\n");
1280                                 return -1;
1281                         }
1282                         continue;
1283                 }
1284
1285                 if (!(func->flags & FUNCF_TRACE)) {
1286                         debug("Funcion '%s' is excluded from trace\n",
1287                               func->name);
1288                         skip_count++;
1289                         continue;
1290                 }
1291
1292                 if (out_format == OUT_FMT_FUNCTION)
1293                         rec_words = 6;
1294                 else /* 2 header words and then 3 or 8 others */
1295                         rec_words = 2 + (entry ? 3 : 8);
1296
1297                 /* convert timestamp from us to ns */
1298                 timestamp = call->flags & FUNCF_TIMESTAMP_MASK;
1299                 if (in_page) {
1300                         if (page_upto + rec_words * 4 > TRACE_PAGE_SIZE) {
1301                                 if (finish_page(tw))
1302                                         return -1;
1303                                 in_page = false;
1304                         }
1305                 }
1306                 if (!in_page) {
1307                         if (start_page(tw, timestamp))
1308                                 return -1;
1309                         in_page = true;
1310                         last_timestamp = timestamp;
1311                         last_delta = 0;
1312                         page_upto = tw->ptr & TRACE_PAGE_MASK;
1313                         if (_DEBUG) {
1314                                 fprintf(stderr,
1315                                         "new page, last_timestamp=%ld, upto=%d\n",
1316                                         last_timestamp, upto);
1317                         }
1318                 }
1319
1320                 delta = timestamp - last_timestamp;
1321                 if (delta < 0) {
1322                         fprintf(stderr, "Time went backwards\n");
1323                         err_count++;
1324                 }
1325
1326                 if (err_count > 20) {
1327                         fprintf(stderr, "Too many errors, giving up\n");
1328                         return -1;
1329                 }
1330
1331                 if (delta > 0x07fffff) {
1332                         /*
1333                          * hard to imagine how this could happen since it means
1334                          * that no function calls were made for a long time
1335                          */
1336                         fprintf(stderr, "cannot represent time delta %x\n",
1337                                 delta);
1338                         return -1;
1339                 }
1340
1341                 if (out_format == OUT_FMT_FUNCTION) {
1342                         struct func_info *caller_func;
1343
1344                         if (_DEBUG) {
1345                                 fprintf(stderr, "%d: delta=%d, stamp=%ld\n",
1346                                         upto, delta, timestamp);
1347                                 fprintf(stderr,
1348                                         "   last_delta %x to %x: last_timestamp=%lx, "
1349                                         "timestamp=%lx, call->flags=%x, upto=%d\n",
1350                                         last_delta, delta, last_timestamp,
1351                                         timestamp, call->flags, upto);
1352                         }
1353
1354                         /* type_len is 6, meaning 4 * 6 = 24 bytes */
1355                         tw->ptr += tputl(fout, rec_words | (uint)delta << 5);
1356                         tw->ptr += tputh(fout, TRACE_FN);
1357                         tw->ptr += tputh(fout, 0);      /* flags */
1358                         tw->ptr += tputl(fout, TRACE_PID);      /* PID */
1359                         /* function */
1360                         tw->ptr += tputq(fout, text_offset + func->offset);
1361                         caller_func = find_caller_by_offset(call->caller);
1362                         /* caller */
1363                         tw->ptr += tputq(fout,
1364                                          text_offset + caller_func->offset);
1365                 } else {
1366                         tw->ptr += tputl(fout, rec_words | delta << 5);
1367                         tw->ptr += tputh(fout, entry ? TRACE_GRAPH_ENT
1368                                                 : TRACE_GRAPH_RET);
1369                         tw->ptr += tputh(fout, 0);      /* flags */
1370                         tw->ptr += tputl(fout, TRACE_PID); /* PID */
1371                         /* function */
1372                         tw->ptr += tputq(fout, text_offset + func->offset);
1373                         tw->ptr += tputl(fout, depth); /* depth */
1374                         if (entry) {
1375                                 depth++;
1376                                 if (stack_ptr < MAX_STACK_DEPTH)
1377                                         func_stack[stack_ptr] = timestamp;
1378                                 stack_ptr++;
1379                         } else {
1380                                 ulong func_duration = 0;
1381
1382                                 depth--;
1383                                 if (stack_ptr && stack_ptr <= MAX_STACK_DEPTH) {
1384                                         ulong start = func_stack[--stack_ptr];
1385
1386                                         func_duration = timestamp - start;
1387                                 }
1388                                 tw->ptr += tputl(fout, 0);      /* overrun */
1389                                 tw->ptr += tputq(fout, 0);      /* calltime */
1390                                 /* rettime */
1391                                 tw->ptr += tputq(fout, func_duration);
1392                         }
1393                 }
1394
1395                 last_delta = delta;
1396                 last_timestamp = timestamp;
1397                 page_upto += 4 + rec_words * 4;
1398                 upto++;
1399                 if (stack_ptr == MAX_STACK_DEPTH)
1400                         break;
1401         }
1402         if (in_page && finish_page(tw))
1403                 return -1;
1404         *missing_countp = missing_count;
1405         *skip_countp = skip_count;
1406
1407         return 0;
1408 }
1409
1410 /**
1411  * write_flyrecord() - Write the flyrecord information
1412  *
1413  * Writes the header and pages of data for the "flyrecord" section. It also
1414  * writes out the counter-type info, selecting "[local]"
1415  *
1416  * @tw: Writer context
1417  * @out_format: Output format to use
1418  * @missing_countp: Returns number of missing functions (not found in function
1419  * list)
1420  * @skip_countp: Returns number of skipped functions (excluded from trace)
1421  *
1422  * Returns: 0 on success, -ve on error
1423  */
1424 static int write_flyrecord(struct twriter *tw, enum out_format_t out_format,
1425                            int *missing_countp, int *skip_countp)
1426 {
1427         int start, ret, len;
1428         FILE *fout = tw->fout;
1429         char str[200];
1430
1431         tw->ptr += fprintf(fout, "flyrecord%c", 0);
1432
1433         /* trace data */
1434         start = ALIGN(tw->ptr + 16, TRACE_PAGE_SIZE);
1435         tw->ptr += tputq(fout, start);
1436
1437         /* use a placeholder for the size */
1438         ret = push_len(tw, start, "flyrecord", 8);
1439         if (ret < 0)
1440                 return -1;
1441         tw->ptr += ret;
1442
1443         snprintf(str, sizeof(str),
1444                  "[local] global counter uptime perf mono mono_raw boot x86-tsc\n");
1445         len = strlen(str);
1446         tw->ptr += tputq(fout, len);
1447         tw->ptr += tputs(fout, str);
1448
1449         debug("trace text base %lx, map file %lx\n", text_base, text_offset);
1450
1451         ret = write_pages(tw, out_format, missing_countp, skip_countp);
1452         if (ret < 0) {
1453                 fprintf(stderr, "Cannot output pages\n");
1454                 return -1;
1455         }
1456
1457         ret = pop_len(tw, "flyrecord");
1458         if (ret < 0) {
1459                 fprintf(stderr, "Cannot finish flyrecord header\n");
1460                 return -1;
1461         }
1462
1463         return 0;
1464 }
1465
1466 /**
1467  * make_ftrace() - Write out an ftrace file
1468  *
1469  * See here for format:
1470  *
1471  * https://github.com/rostedt/trace-cmd/blob/master/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt
1472  *
1473  * @fout: Output file
1474  * @out_format: Output format to use
1475  * Returns: 0 on success, -ve on error
1476  */
1477 static int make_ftrace(FILE *fout, enum out_format_t out_format)
1478 {
1479         int missing_count, skip_count;
1480         struct twriter tws, *tw = &tws;
1481         int ret;
1482
1483         memset(tw, '\0', sizeof(*tw));
1484         abuf_init(&tw->str_buf);
1485         tw->fout = fout;
1486
1487         tw->ptr = 0;
1488         ret = output_headers(tw);
1489         if (ret < 0) {
1490                 fprintf(stderr, "Cannot output headers\n");
1491                 return -1;
1492         }
1493         /* number of event systems files */
1494         tw->ptr += tputl(fout, 0);
1495
1496         ret = write_symbols(tw);
1497         if (ret < 0) {
1498                 fprintf(stderr, "Cannot write symbols\n");
1499                 return -1;
1500         }
1501
1502         ret = write_options(tw);
1503         if (ret < 0) {
1504                 fprintf(stderr, "Cannot write options\n");
1505                 return -1;
1506         }
1507
1508         ret = write_flyrecord(tw, out_format, &missing_count, &skip_count);
1509         if (ret < 0) {
1510                 fprintf(stderr, "Cannot write flyrecord\n");
1511                 return -1;
1512         }
1513
1514         info("ftrace: %d functions not found, %d excluded\n", missing_count,
1515              skip_count);
1516
1517         return 0;
1518 }
1519
1520 /**
1521  * prof_tool() - Performs requested action
1522  *
1523  * @argc: Number of arguments (used to obtain the command
1524  * @argv: List of arguments
1525  * @trace_fname: Filename of input file (trace data from U-Boot)
1526  * @map_fname: Filename of map file (System.map from U-Boot)
1527  * @trace_config_fname: Trace-configuration file, or NULL if none
1528  * @out_fname: Output filename
1529  */
1530 static int prof_tool(int argc, char *const argv[],
1531                      const char *trace_fname, const char *map_fname,
1532                      const char *trace_config_fname, const char *out_fname,
1533                      enum out_format_t out_format)
1534 {
1535         int err = 0;
1536
1537         if (read_map_file(map_fname))
1538                 return -1;
1539         if (trace_fname && read_trace_file(trace_fname))
1540                 return -1;
1541         if (trace_config_fname && read_trace_config_file(trace_config_fname))
1542                 return -1;
1543
1544         check_trace_config();
1545
1546         for (; argc; argc--, argv++) {
1547                 const char *cmd = *argv;
1548
1549                 if (!strcmp(cmd, "dump-ftrace")) {
1550                         FILE *fout;
1551
1552                         if (out_format != OUT_FMT_FUNCTION &&
1553                             out_format != OUT_FMT_FUNCGRAPH)
1554                                 out_format = OUT_FMT_FUNCTION;
1555                         fout = fopen(out_fname, "w");
1556                         if (!fout) {
1557                                 fprintf(stderr, "Cannot write file '%s'\n",
1558                                         out_fname);
1559                                 return -1;
1560                         }
1561                         err = make_ftrace(fout, out_format);
1562                         fclose(fout);
1563                 } else {
1564                         warn("Unknown command '%s'\n", cmd);
1565                 }
1566         }
1567
1568         return err;
1569 }
1570
1571 int main(int argc, char *argv[])
1572 {
1573         enum out_format_t out_format = OUT_FMT_DEFAULT;
1574         const char *map_fname = "System.map";
1575         const char *trace_fname = NULL;
1576         const char *config_fname = NULL;
1577         const char *out_fname = NULL;
1578         int opt;
1579
1580         verbose = 2;
1581         while ((opt = getopt(argc, argv, "c:f:m:o:t:v:")) != -1) {
1582                 switch (opt) {
1583                 case 'c':
1584                         config_fname = optarg;
1585                         break;
1586                 case 'f':
1587                         if (!strcmp("function", optarg)) {
1588                                 out_format = OUT_FMT_FUNCTION;
1589                         } else if (!strcmp("funcgraph", optarg)) {
1590                                 out_format = OUT_FMT_FUNCGRAPH;
1591                         } else {
1592                                 fprintf(stderr,
1593                                         "Invalid format: use function, funcgraph, calls, timing\n");
1594                                 exit(1);
1595                         }
1596                         break;
1597                 case 'm':
1598                         map_fname = optarg;
1599                         break;
1600                 case 'o':
1601                         out_fname = optarg;
1602                         break;
1603                 case 't':
1604                         trace_fname = optarg;
1605                         break;
1606                 case 'v':
1607                         verbose = atoi(optarg);
1608                         break;
1609                 default:
1610                         usage();
1611                 }
1612         }
1613         argc -= optind; argv += optind;
1614         if (argc < 1)
1615                 usage();
1616
1617         if (!out_fname || !map_fname || !trace_fname) {
1618                 fprintf(stderr,
1619                         "Must provide trace data, System.map file and output file\n");
1620                 usage();
1621         }
1622
1623         debug("Debug enabled\n");
1624         return prof_tool(argc, argv, trace_fname, map_fname, config_fname,
1625                          out_fname, out_format);
1626 }