blob: b5b8a9f7938f80f9fbba3cfd02456e26a538cca5 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass9c628922013-06-11 11:14:43 -07002/*
Simon Glassdd8f19a2023-01-15 14:15:53 -07003 * Copyright 2023 Google LLC
4 * Written by Simon Glass <sjg@chromium.org>
Simon Glass9c628922013-06-11 11:14:43 -07005 */
6
Simon Glassdd8f19a2023-01-15 14:15:53 -07007/*
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 */
Simon Glass9c628922013-06-11 11:14:43 -070013
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>
Jörg Krause983487f2015-04-22 21:36:22 +020024#include <sys/types.h>
Simon Glass9c628922013-06-11 11:14:43 -070025
26#include <compiler.h>
27#include <trace.h>
Simon Glassdd8f19a2023-01-15 14:15:53 -070028#include <abuf.h>
Simon Glass9c628922013-06-11 11:14:43 -070029
Simon Glassdd8f19a2023-01-15 14:15:53 -070030/* 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)
Simon Glass9c628922013-06-11 11:14:43 -070039
40enum {
41 FUNCF_TRACE = 1 << 0, /* Include this function in trace */
Simon Glassdd8f19a2023-01-15 14:15:53 -070042 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/* Section types for v7 format (trace-cmd format) */
51enum {
52 SECTION_OPTIONS,
53};
54
55/* Option types (trace-cmd format) */
56enum {
57 OPTION_DONE,
58 OPTION_DATE,
59 OPTION_CPUSTAT,
60 OPTION_BUFFER,
61 OPTION_TRACECLOCK,
62 OPTION_UNAME,
63 OPTION_HOOK,
64 OPTION_OFFSET,
65 OPTION_CPUCOUNT,
66 OPTION_VERSION,
67 OPTION_PROCMAPS,
68 OPTION_TRACEID,
69 OPTION_TIME_SHIFT,
70 OPTION_GUEST,
71 OPTION_TSC2NSEC,
72};
73
74/* types of trace records (trace-cmd format) */
75enum trace_type {
76 __TRACE_FIRST_TYPE = 0,
77
78 TRACE_FN,
79 TRACE_CTX,
80 TRACE_WAKE,
81 TRACE_STACK,
82 TRACE_PRINT,
83 TRACE_BPRINT,
84 TRACE_MMIO_RW,
85 TRACE_MMIO_MAP,
86 TRACE_BRANCH,
87 TRACE_GRAPH_RET,
88 TRACE_GRAPH_ENT,
Simon Glass9c628922013-06-11 11:14:43 -070089};
90
Simon Glass9e2afae2023-01-15 14:15:52 -070091/**
92 * struct func_info - information recorded for each function
93 *
94 * @offset: Function offset in the image, measured from the text_base
95 * @name: Function name
96 * @code_size: Total code size of the function
97 * @flags: Either 0 or FUNCF_TRACE
Simon Glass9e2afae2023-01-15 14:15:52 -070098 */
Simon Glass9c628922013-06-11 11:14:43 -070099struct func_info {
100 unsigned long offset;
101 const char *name;
102 unsigned long code_size;
Simon Glass9c628922013-06-11 11:14:43 -0700103 unsigned flags;
Simon Glass9c628922013-06-11 11:14:43 -0700104};
105
Simon Glass9e2afae2023-01-15 14:15:52 -0700106/**
107 * enum trace_line_type - whether to include or exclude a function
108 *
109 * @TRACE_LINE_INCLUDE: Include the function
110 * @TRACE_LINE_EXCLUDE: Exclude the function
111 */
Simon Glass9c628922013-06-11 11:14:43 -0700112enum trace_line_type {
113 TRACE_LINE_INCLUDE,
114 TRACE_LINE_EXCLUDE,
115};
116
Simon Glass9e2afae2023-01-15 14:15:52 -0700117/**
118 * struct trace_configline_info - information about a config-file line
119 *
120 * @next: Next line
121 * @type: Line type
122 * @name: identifier name / wildcard
123 * @regex: Regex to use if name starts with '/'
124 */
Simon Glass9c628922013-06-11 11:14:43 -0700125struct trace_configline_info {
126 struct trace_configline_info *next;
127 enum trace_line_type type;
Simon Glass9e2afae2023-01-15 14:15:52 -0700128 const char *name;
129 regex_t regex;
Simon Glass9c628922013-06-11 11:14:43 -0700130};
131
Simon Glassdd8f19a2023-01-15 14:15:53 -0700132/**
133 * struct tw_len - holds information about a length value that need fix-ups
134 *
135 * This is used to record a placeholder for a u32 or u64 length which is written
136 * to the output file but needs to be updated once the length is actually known
137 *
138 * This allows us to write tw->ptr - @len_base to position @ptr in the file
139 *
140 * @ptr: Position of the length value in the file
141 * @base: Base position for the calculation
142 * @size: Size of the length value, in bytes (4 or 8)
143 */
144struct tw_len {
145 int ptr;
146 int base;
147 int size;
148};
149
150/**
151 * struct twriter - Writer for trace records
152 *
153 * Maintains state used when writing the output file in trace-cmd format
154 *
155 * @ptr: Current file position
156 * @len_stack: Stack of length values that need fixing up
157 * @len: Number of items on @len_stack
158 * @str_buf: Buffer of strings (for v7 format)
159 * @str_ptr: Current write-position in the buffer for strings
160 * @fout: Output file
161 */
162struct twriter {
163 int ptr;
164 struct tw_len len_stack[LEN_STACK_SIZE];
165 int len_count;
166 struct abuf str_buf;
167 int str_ptr;
168 FILE *fout;
169};
170
Simon Glass9c628922013-06-11 11:14:43 -0700171/* The contents of the trace config file */
172struct trace_configline_info *trace_config_head;
173
Simon Glass9e2afae2023-01-15 14:15:52 -0700174/* list of all functions in System.map file, sorted by offset in the image */
Simon Glass9c628922013-06-11 11:14:43 -0700175struct func_info *func_list;
Simon Glass9e2afae2023-01-15 14:15:52 -0700176
177int func_count; /* number of functions */
178struct trace_call *call_list; /* list of all calls in the input trace file */
179int call_count; /* number of calls */
Simon Glass9c628922013-06-11 11:14:43 -0700180int verbose; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */
Simon Glass9e2afae2023-01-15 14:15:52 -0700181ulong text_offset; /* text address of first function */
Simon Glass7ac0a502023-01-15 14:15:55 -0700182ulong text_base; /* CONFIG_TEXT_BASE from trace file */
Simon Glass9c628922013-06-11 11:14:43 -0700183
Simon Glass9e2afae2023-01-15 14:15:52 -0700184/* debugging helpers */
Simon Glass9c628922013-06-11 11:14:43 -0700185static void outf(int level, const char *fmt, ...)
186 __attribute__ ((format (__printf__, 2, 3)));
187#define error(fmt, b...) outf(0, fmt, ##b)
188#define warn(fmt, b...) outf(1, fmt, ##b)
189#define notice(fmt, b...) outf(2, fmt, ##b)
190#define info(fmt, b...) outf(3, fmt, ##b)
191#define debug(fmt, b...) outf(4, fmt, ##b)
192
Simon Glass9c628922013-06-11 11:14:43 -0700193static void outf(int level, const char *fmt, ...)
194{
195 if (verbose >= level) {
196 va_list args;
197
198 va_start(args, fmt);
199 vfprintf(stderr, fmt, args);
200 va_end(args);
201 }
202}
203
204static void usage(void)
205{
206 fprintf(stderr,
Simon Glassec86a632022-12-21 16:08:24 -0700207 "Usage: proftool [-cmtv] <cmd> <profdata>\n"
Simon Glass9c628922013-06-11 11:14:43 -0700208 "\n"
209 "Commands\n"
Simon Glass9e2afae2023-01-15 14:15:52 -0700210 " dump-ftrace\t\tDump out records in ftrace format for use by trace-cmd\n"
Simon Glass9c628922013-06-11 11:14:43 -0700211 "\n"
212 "Options:\n"
Simon Glass9e2afae2023-01-15 14:15:52 -0700213 " -c <cfg>\tSpecify config file\n"
Simon Glass9c628922013-06-11 11:14:43 -0700214 " -m <map>\tSpecify Systen.map file\n"
Simon Glass9e2afae2023-01-15 14:15:52 -0700215 " -o <fname>\tSpecify output file\n"
Simon Glassec86a632022-12-21 16:08:24 -0700216 " -t <fname>\tSpecify trace data file (from U-Boot 'trace calls')\n"
Simon Glass9c628922013-06-11 11:14:43 -0700217 " -v <0-4>\tSpecify verbosity\n");
218 exit(EXIT_FAILURE);
219}
220
Simon Glass9e2afae2023-01-15 14:15:52 -0700221/**
222 * h_cmp_offset - bsearch() function to compare two functions bny their offset
223 *
224 * @v1: Pointer to first function (struct func_info)
225 * @v2: Pointer to second function (struct func_info)
226 * Returns: < 0 if v1 offset < v2 offset, 0 if equal, > 0 otherwise
227 */
Simon Glass9c628922013-06-11 11:14:43 -0700228static int h_cmp_offset(const void *v1, const void *v2)
229{
230 const struct func_info *f1 = v1, *f2 = v2;
231
232 return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE);
233}
234
Simon Glass9e2afae2023-01-15 14:15:52 -0700235/**
236 * read_system_map() - read the System.map file to create a list of functions
237 *
238 * This also reads the text_offset value, since we assume that the first text
239 * symbol is at that address
240 *
241 * @fin: File to read
242 * Returns: 0 if OK, non-zero on error
243 */
Simon Glass9c628922013-06-11 11:14:43 -0700244static int read_system_map(FILE *fin)
245{
246 unsigned long offset, start = 0;
247 struct func_info *func;
248 char buff[MAX_LINE_LEN];
249 char symtype;
250 char symname[MAX_LINE_LEN + 1];
251 int linenum;
252 int alloced;
253
254 for (linenum = 1, alloced = func_count = 0;; linenum++) {
255 int fields = 0;
256
257 if (fgets(buff, sizeof(buff), fin))
258 fields = sscanf(buff, "%lx %c %100s\n", &offset,
259 &symtype, symname);
260 if (fields == 2) {
261 continue;
262 } else if (feof(fin)) {
263 break;
264 } else if (fields < 2) {
265 error("Map file line %d: invalid format\n", linenum);
266 return 1;
267 }
268
269 /* Must be a text symbol */
270 symtype = tolower(symtype);
271 if (symtype != 't' && symtype != 'w')
272 continue;
273
274 if (func_count == alloced) {
275 alloced += 256;
276 func_list = realloc(func_list,
277 sizeof(struct func_info) * alloced);
278 assert(func_list);
279 }
280 if (!func_count)
281 start = offset;
282
283 func = &func_list[func_count++];
284 memset(func, '\0', sizeof(*func));
285 func->offset = offset - start;
286 func->name = strdup(symname);
287 func->flags = FUNCF_TRACE; /* trace by default */
288
289 /* Update previous function's code size */
290 if (func_count > 1)
291 func[-1].code_size = func->offset - func[-1].offset;
292 }
Simon Glass7ac0a502023-01-15 14:15:55 -0700293 notice("%d functions found in map file, start addr %lx\n", func_count,
294 start);
Simon Glass9c628922013-06-11 11:14:43 -0700295 text_offset = start;
Simon Glass9e2afae2023-01-15 14:15:52 -0700296
Simon Glass9c628922013-06-11 11:14:43 -0700297 return 0;
298}
299
300static int read_data(FILE *fin, void *buff, int size)
301{
302 int err;
303
304 err = fread(buff, 1, size, fin);
305 if (!err)
306 return 1;
307 if (err != size) {
Simon Glass9e2afae2023-01-15 14:15:52 -0700308 error("Cannot read trace file at pos %lx\n", ftell(fin));
Simon Glass9c628922013-06-11 11:14:43 -0700309 return -1;
310 }
311 return 0;
312}
313
Simon Glass9e2afae2023-01-15 14:15:52 -0700314/**
315 * find_func_by_offset() - Look up a function by its offset
316 *
317 * @offset: Offset to search for, from text_base
318 * Returns: function, if found, else NULL
319 *
320 * This does a fast search for a function given its offset from text_base
321 *
322 */
323static struct func_info *find_func_by_offset(uint offset)
Simon Glass9c628922013-06-11 11:14:43 -0700324{
325 struct func_info key, *found;
326
327 key.offset = offset;
328 found = bsearch(&key, func_list, func_count, sizeof(struct func_info),
329 h_cmp_offset);
330
331 return found;
332}
333
Simon Glass9e2afae2023-01-15 14:15:52 -0700334/**
335 * find_caller_by_offset() - finds the function which contains the given offset
336 *
337 * @offset: Offset to search for, from text_base
338 * Returns: function, if found, else NULL
339 *
340 * If the offset falls between two functions, then it is assumed to belong to
341 * the first function (with the lowest offset). This is a way of figuring out
342 * which function owns code at a particular offset
343 */
344static struct func_info *find_caller_by_offset(uint offset)
Simon Glass9c628922013-06-11 11:14:43 -0700345{
346 int low; /* least function that could be a match */
347 int high; /* greated function that could be a match */
348 struct func_info key;
349
350 low = 0;
351 high = func_count - 1;
352 key.offset = offset;
353 while (high > low + 1) {
354 int mid = (low + high) / 2;
355 int result;
356
357 result = h_cmp_offset(&key, &func_list[mid]);
358 if (result > 0)
359 low = mid;
360 else if (result < 0)
361 high = mid;
362 else
363 return &func_list[mid];
364 }
365
366 return low >= 0 ? &func_list[low] : NULL;
367}
368
Simon Glass9e2afae2023-01-15 14:15:52 -0700369/**
370 * read_calls() - Read the list of calls from the trace data
371 *
372 * The calls are stored consecutively in the trace output produced by U-Boot
373 *
374 * @fin: File to read from
375 * @count: Number of calls to read
376 * Returns: 0 if OK, -1 on error
377 */
Heinrich Schuchardt14690902019-06-14 21:50:55 +0200378static int read_calls(FILE *fin, size_t count)
Simon Glass9c628922013-06-11 11:14:43 -0700379{
380 struct trace_call *call_data;
381 int i;
382
Heinrich Schuchardt14690902019-06-14 21:50:55 +0200383 notice("call count: %zu\n", count);
Simon Glass9c628922013-06-11 11:14:43 -0700384 call_list = (struct trace_call *)calloc(count, sizeof(*call_data));
385 if (!call_list) {
386 error("Cannot allocate call_list\n");
387 return -1;
388 }
389 call_count = count;
390
391 call_data = call_list;
392 for (i = 0; i < count; i++, call_data++) {
393 if (read_data(fin, call_data, sizeof(*call_data)))
Simon Glass9e2afae2023-01-15 14:15:52 -0700394 return -1;
Simon Glass9c628922013-06-11 11:14:43 -0700395 }
396 return 0;
397}
398
Simon Glass9e2afae2023-01-15 14:15:52 -0700399/**
400 * read_trace() - Read the U-Boot trace file
401 *
402 * Read in the calls from the trace file. The function list is ignored at
403 * present
404 *
405 * @fin: File to read
406 * Returns 0 if OK, non-zero on error
407 */
408static int read_trace(FILE *fin)
Simon Glass9c628922013-06-11 11:14:43 -0700409{
410 struct trace_output_hdr hdr;
411
Simon Glass9c628922013-06-11 11:14:43 -0700412 while (!feof(fin)) {
413 int err;
414
415 err = read_data(fin, &hdr, sizeof(hdr));
416 if (err == 1)
417 break; /* EOF */
418 else if (err)
419 return 1;
Simon Glass7ac0a502023-01-15 14:15:55 -0700420 text_base = hdr.text_base;
Simon Glass9c628922013-06-11 11:14:43 -0700421
422 switch (hdr.type) {
423 case TRACE_CHUNK_FUNCS:
424 /* Ignored at present */
425 break;
426
427 case TRACE_CHUNK_CALLS:
428 if (read_calls(fin, hdr.rec_count))
429 return 1;
430 break;
431 }
432 }
433 return 0;
434}
435
Simon Glass9e2afae2023-01-15 14:15:52 -0700436/**
437 * read_map_file() - Read the System.map file
438 *
439 * This reads the file into the func_list array
440 *
441 * @fname: Filename to read
442 * Returns 0 if OK, non-zero on error
443 */
Simon Glass9c628922013-06-11 11:14:43 -0700444static int read_map_file(const char *fname)
445{
446 FILE *fmap;
447 int err = 0;
448
449 fmap = fopen(fname, "r");
450 if (!fmap) {
451 error("Cannot open map file '%s'\n", fname);
452 return 1;
453 }
454 if (fmap) {
455 err = read_system_map(fmap);
456 fclose(fmap);
457 }
458 return err;
459}
460
Simon Glass9e2afae2023-01-15 14:15:52 -0700461/**
462 * read_trace_file() - Open and read the U-Boot trace file
463 *
464 * Read in the calls from the trace file. The function list is ignored at
465 * present
466 *
467 * @fin: File to read
468 * Returns 0 if OK, non-zero on error
469 */
470static int read_trace_file(const char *fname)
Simon Glass9c628922013-06-11 11:14:43 -0700471{
Simon Glass9c628922013-06-11 11:14:43 -0700472 FILE *fprof;
473 int err;
474
475 fprof = fopen(fname, "rb");
476 if (!fprof) {
Simon Glass9e2afae2023-01-15 14:15:52 -0700477 error("Cannot open trace data file '%s'\n",
Simon Glass9c628922013-06-11 11:14:43 -0700478 fname);
479 return 1;
480 } else {
Simon Glass9e2afae2023-01-15 14:15:52 -0700481 err = read_trace(fprof);
Simon Glass9c628922013-06-11 11:14:43 -0700482 fclose(fprof);
483 if (err)
484 return err;
Simon Glass9c628922013-06-11 11:14:43 -0700485 }
486 return 0;
487}
488
489static int regex_report_error(regex_t *regex, int err, const char *op,
490 const char *name)
491{
492 char buf[200];
493
494 regerror(err, regex, buf, sizeof(buf));
495 error("Regex error '%s' in %s '%s'\n", buf, op, name);
496 return -1;
497}
498
499static void check_trace_config_line(struct trace_configline_info *item)
500{
501 struct func_info *func, *end;
502 int err;
503
504 debug("Checking trace config line '%s'\n", item->name);
505 for (func = func_list, end = func + func_count; func < end; func++) {
506 err = regexec(&item->regex, func->name, 0, NULL, 0);
507 debug(" - regex '%s', string '%s': %d\n", item->name,
508 func->name, err);
509 if (err == REG_NOMATCH)
510 continue;
511
Andreas Bießmann8a0103f2013-07-02 08:37:36 +0200512 if (err) {
Simon Glass9c628922013-06-11 11:14:43 -0700513 regex_report_error(&item->regex, err, "match",
514 item->name);
515 break;
516 }
517
518 /* It matches, so perform the action */
519 switch (item->type) {
520 case TRACE_LINE_INCLUDE:
521 info(" include %s at %lx\n", func->name,
522 text_offset + func->offset);
523 func->flags |= FUNCF_TRACE;
524 break;
525
526 case TRACE_LINE_EXCLUDE:
527 info(" exclude %s at %lx\n", func->name,
528 text_offset + func->offset);
529 func->flags &= ~FUNCF_TRACE;
530 break;
531 }
532 }
533}
534
Simon Glass9e2afae2023-01-15 14:15:52 -0700535/** check_trace_config() - Check trace-config file, reporting any problems */
Simon Glass9c628922013-06-11 11:14:43 -0700536static void check_trace_config(void)
537{
538 struct trace_configline_info *line;
539
540 for (line = trace_config_head; line; line = line->next)
541 check_trace_config_line(line);
542}
543
544/**
Simon Glass9e2afae2023-01-15 14:15:52 -0700545 * read_trace_config() - read the trace-config file
546 *
547 * This file consists of lines like:
548 *
549 * include-func <regex>
550 * exclude-func <regex>
551 *
552 * where <regex> is a regular expression matched against function names. It
553 * allows some functions to be dropped from the trace when producing ftrace
554 * records
555 *
556 * @fin: File to process
557 * Returns: 0 if OK, -1 on error
558 */
Simon Glass9c628922013-06-11 11:14:43 -0700559static int read_trace_config(FILE *fin)
560{
561 char buff[200];
562 int linenum = 0;
563 struct trace_configline_info **tailp = &trace_config_head;
564
565 while (fgets(buff, sizeof(buff), fin)) {
566 int len = strlen(buff);
567 struct trace_configline_info *line;
568 char *saveptr;
569 char *s, *tok;
570 int err;
571
572 linenum++;
573 if (len && buff[len - 1] == '\n')
574 buff[len - 1] = '\0';
575
576 /* skip blank lines and comments */
577 for (s = buff; *s == ' ' || *s == '\t'; s++)
578 ;
579 if (!*s || *s == '#')
580 continue;
581
Simon Glass9e2afae2023-01-15 14:15:52 -0700582 line = (struct trace_configline_info *)calloc(1, sizeof(*line));
Simon Glass9c628922013-06-11 11:14:43 -0700583 if (!line) {
584 error("Cannot allocate config line\n");
585 return -1;
586 }
587
588 tok = strtok_r(s, " \t", &saveptr);
589 if (!tok) {
590 error("Invalid trace config data on line %d\n",
591 linenum);
592 return -1;
593 }
594 if (0 == strcmp(tok, "include-func")) {
595 line->type = TRACE_LINE_INCLUDE;
596 } else if (0 == strcmp(tok, "exclude-func")) {
597 line->type = TRACE_LINE_EXCLUDE;
598 } else {
599 error("Unknown command in trace config data line %d\n",
600 linenum);
601 return -1;
602 }
603
604 tok = strtok_r(NULL, " \t", &saveptr);
605 if (!tok) {
606 error("Missing pattern in trace config data line %d\n",
607 linenum);
608 return -1;
609 }
610
611 err = regcomp(&line->regex, tok, REG_NOSUB);
612 if (err) {
Vincent Stehlé451b1fc2015-10-07 15:48:48 +0200613 int r = regex_report_error(&line->regex, err,
614 "compile", tok);
Simon Glass9c628922013-06-11 11:14:43 -0700615 free(line);
Vincent Stehlé451b1fc2015-10-07 15:48:48 +0200616 return r;
Simon Glass9c628922013-06-11 11:14:43 -0700617 }
618
619 /* link this new one to the end of the list */
620 line->name = strdup(tok);
621 line->next = NULL;
622 *tailp = line;
623 tailp = &line->next;
624 }
625
626 if (!feof(fin)) {
627 error("Cannot read from trace config file at position %ld\n",
628 ftell(fin));
629 return -1;
630 }
631 return 0;
632}
633
634static int read_trace_config_file(const char *fname)
635{
636 FILE *fin;
637 int err;
638
639 fin = fopen(fname, "r");
640 if (!fin) {
641 error("Cannot open trace_config file '%s'\n", fname);
642 return -1;
643 }
644 err = read_trace_config(fin);
645 fclose(fin);
646 return err;
647}
648
Simon Glassdd8f19a2023-01-15 14:15:53 -0700649/**
650 * tputh() - Write a 16-bit little-endian value to a file
651 *
652 * @fout: File to write to
653 * @val: Value to write
654 * Returns: number of bytes written (2)
655 */
656static int tputh(FILE *fout, unsigned int val)
Simon Glass9c628922013-06-11 11:14:43 -0700657{
Simon Glassdd8f19a2023-01-15 14:15:53 -0700658 fputc(val, fout);
659 fputc(val >> 8, fout);
Simon Glass9c628922013-06-11 11:14:43 -0700660
Simon Glassdd8f19a2023-01-15 14:15:53 -0700661 return 2;
662}
Simon Glass9c628922013-06-11 11:14:43 -0700663
Simon Glassdd8f19a2023-01-15 14:15:53 -0700664/**
665 * tputl() - Write a 32-bit little-endian value to a file
666 *
667 * @fout: File to write to
668 * @val: Value to write
669 * Returns: number of bytes written (4)
670 */
671static int tputl(FILE *fout, ulong val)
672{
673 fputc(val, fout);
674 fputc(val >> 8, fout);
675 fputc(val >> 16, fout);
676 fputc(val >> 24, fout);
677
678 return 4;
Simon Glass9c628922013-06-11 11:14:43 -0700679}
680
Simon Glassdd8f19a2023-01-15 14:15:53 -0700681/**
682 * tputh() - Write a 64-bit little-endian value to a file
683 *
684 * @fout: File to write to
685 * @val: Value to write
686 * Returns: number of bytes written (8)
Simon Glass9c628922013-06-11 11:14:43 -0700687 */
Simon Glassdd8f19a2023-01-15 14:15:53 -0700688static int tputq(FILE *fout, unsigned long long val)
Simon Glass9c628922013-06-11 11:14:43 -0700689{
Simon Glassdd8f19a2023-01-15 14:15:53 -0700690 tputl(fout, val);
691 tputl(fout, val >> 32U);
692
693 return 8;
694}
695
696/**
697 * tputh() - Write a string to a file
698 *
699 * The string is written without its terminator
700 *
701 * @fout: File to write to
702 * @val: Value to write
703 * Returns: number of bytes written
704 */
705static int tputs(FILE *fout, const char *str)
706{
707 fputs(str, fout);
708
709 return strlen(str);
710}
711
712/**
713 * add_str() - add a name string to the string table
714 *
715 * This is used by the v7 format
716 *
717 * @tw: Writer context
718 * @name: String to write
719 * Returns: Updated value of string pointer, or -1 if out of memory
720 */
721static int add_str(struct twriter *tw, const char *name)
722{
723 int str_ptr;
724 int len;
725
726 len = strlen(name) + 1;
727 str_ptr = tw->str_ptr;
728 tw->str_ptr += len;
729
730 if (tw->str_ptr > abuf_size(&tw->str_buf)) {
731 int new_size;
732
733 new_size = ALIGN(tw->str_ptr, 4096);
734 if (!abuf_realloc(&tw->str_buf, new_size))
735 return -1;
736 }
737
738 return str_ptr;
739}
740
741/**
742 * push_len() - Push a new length request onto the stack
743 *
744 * @tw: Writer context
745 * @base: Base position of the length calculation
746 * @msg: Indicates the type of caller, for debugging
747 * @size: Size of the length value, either 4 bytes or 8
748 * Returns number of bytes written to the file (=@size on success), -ve on error
749 *
750 * This marks a place where a length must be written, covering data that is
751 * about to be written. It writes a placeholder value.
752 *
753 * Once the data is written, calling pop_len() will update the placeholder with
754 * the correct length based on how many bytes have been written
755 */
756static int push_len(struct twriter *tw, int base, const char *msg, int size)
757{
758 struct tw_len *lp;
759
760 if (tw->len_count >= LEN_STACK_SIZE) {
761 fprintf(stderr, "Length-stack overflow: %s\n", msg);
762 return -1;
763 }
764 if (size != 4 && size != 8) {
765 fprintf(stderr, "Length-stack invalid size %d: %s\n", size,
766 msg);
767 return -1;
768 }
769
770 lp = &tw->len_stack[tw->len_count++];
771 lp->base = base;
772 lp->ptr = tw->ptr;
773 lp->size = size;
774
775 return size == 8 ? tputq(tw->fout, 0) : tputl(tw->fout, 0);
776}
777
778/**
779 * pop_len() - Update a length value once the length is known
780 *
781 * Pops a value of the length stack and updates the file at that position with
782 * the number of bytes written between now and then. Once done, the file is
783 * seeked to the current (tw->ptr) position again, so writing can continue as
784 * normal.
785 *
786 * @tw: Writer context
787 * @msg: Indicates the type of caller, for debugging
788 * Returns 0 if OK, -1 on error
789 */
790static int pop_len(struct twriter *tw, const char *msg)
791{
792 struct tw_len *lp;
793 int len, ret;
794
795 if (!tw->len_count) {
796 fprintf(stderr, "Length-stack underflow: %s\n", msg);
797 return -1;
798 }
799
800 lp = &tw->len_stack[--tw->len_count];
801 if (fseek(tw->fout, lp->ptr, SEEK_SET))
802 return -1;
803 len = tw->ptr - lp->base;
804 ret = lp->size == 8 ? tputq(tw->fout, len) : tputl(tw->fout, len);
805 if (ret < 0)
806 return -1;
807 if (fseek(tw->fout, tw->ptr, SEEK_SET))
808 return -1;
809
810 return 0;
811}
812
813/**
814 * start_header() - Start a v7 section
815 *
816 * Writes a header in v7 format
817 *
818 * @tw: Writer context
819 * @id: ID of header to write (SECTION_...)
820 * @flags: Flags value to write
821 * @name: Name of section
822 * Returns: number of bytes written
823 */
824static int start_header(struct twriter *tw, int id, uint flags,
825 const char *name)
826{
827 int str_id;
828 int lptr;
829 int base;
830 int ret;
831
832 base = tw->ptr + 16;
833 lptr = 0;
834 lptr += tputh(tw->fout, id);
835 lptr += tputh(tw->fout, flags);
836 str_id = add_str(tw, name);
837 if (str_id < 0)
838 return -1;
839 lptr += tputl(tw->fout, str_id);
840
841 /* placeholder for size */
842 ret = push_len(tw, base, "v7 header", 8);
843 if (ret < 0)
844 return -1;
845 lptr += ret;
846
847 return lptr;
848}
849
850/**
851 * start_page() - Start a new page of output data
852 *
853 * The output is arranged in 4KB pages with a base timestamp at the start of
854 * each. This starts a new page, making sure it is aligned to 4KB in the output
855 * file.
856 *
857 * @tw: Writer context
858 * @timestamp: Base timestamp for the page
859 */
860static int start_page(struct twriter *tw, ulong timestamp)
861{
862 int start;
863 int ret;
864
865 /* move to start of next page */
866 start = ALIGN(tw->ptr, TRACE_PAGE_SIZE);
867 ret = fseek(tw->fout, start, SEEK_SET);
868 if (ret < 0) {
869 fprintf(stderr, "Cannot seek to page start\n");
870 return -1;
871 }
872 tw->ptr = start;
873
874 /* page header */
875 tw->ptr += tputq(tw->fout, timestamp);
876 ret = push_len(tw, start + 16, "page", 8);
877 if (ret < 0)
878 return ret;
879 tw->ptr += ret;
880
881 return 0;
882}
883
884/**
885 * finish_page() - finish a page
886 *
887 * Sets the lengths correctly and moves to the start of the next page
888 *
889 * @tw: Writer context
890 * Returns: 0 on success, -1 on error
891 */
892static int finish_page(struct twriter *tw)
893{
894 int ret, end;
895
896 ret = pop_len(tw, "page");
897 if (ret < 0)
898 return ret;
899 end = ALIGN(tw->ptr, TRACE_PAGE_SIZE);
900
901 /*
902 * Write a byte so that the data actually makes to the file, in the case
903 * that we never write any more pages
904 */
905 if (tw->ptr != end) {
906 if (fseek(tw->fout, end - 1, SEEK_SET)) {
907 fprintf(stderr, "cannot seek to start of next page\n");
908 return -1;
909 }
910 fputc(0, tw->fout);
911 tw->ptr = end;
912 }
913
914 return 0;
915}
916
917/**
918 * output_headers() - Output v6 headers to the file
919 *
920 * Writes out the various formats so that trace-cmd and kernelshark can make
921 * sense of the data
922 *
923 * This updates tw->ptr as it goes
924 *
925 * @tw: Writer context
926 * Returns: 0 on success, -ve on error
927 */
928static int output_headers(struct twriter *tw)
929{
930 FILE *fout = tw->fout;
931 char str[800];
932 int len, ret;
933
934 tw->ptr += fprintf(fout, "%c%c%ctracing6%c%c%c", 0x17, 0x08, 0x44,
935 0 /* terminator */, 0 /* little endian */,
936 4 /* 32-bit long values */);
937
938 /* host-machine page size 4KB */
939 tw->ptr += tputl(fout, 4 << 10);
940
941 tw->ptr += fprintf(fout, "header_page%c", 0);
942
943 snprintf(str, sizeof(str),
944 "\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0;\n"
945 "\tfield: local_t commit;\toffset:8;\tsize:8;\tsigned:1;\n"
946 "\tfield: int overwrite;\toffset:8;\tsize:1;\tsigned:1;\n"
947 "\tfield: char data;\toffset:16;\tsize:4080;\tsigned:1;\n");
948 len = strlen(str);
949 tw->ptr += tputq(fout, len);
950 tw->ptr += tputs(fout, str);
951
952 if (VERSION7) {
953 /* no compression */
954 tw->ptr += fprintf(fout, "none%cversion%c\n", 0, 0);
955
956 ret = start_header(tw, SECTION_OPTIONS, 0, "options");
957 if (ret < 0) {
958 fprintf(stderr, "Cannot start option header\n");
959 return -1;
960 }
961 tw->ptr += ret;
962 tw->ptr += tputh(fout, OPTION_DONE);
963 tw->ptr += tputl(fout, 8);
964 tw->ptr += tputl(fout, 0);
965 ret = pop_len(tw, "t7 header");
966 if (ret < 0) {
967 fprintf(stderr, "Cannot finish option header\n");
968 return -1;
969 }
970 }
971
972 tw->ptr += fprintf(fout, "header_event%c", 0);
973 snprintf(str, sizeof(str),
974 "# compressed entry header\n"
975 "\ttype_len : 5 bits\n"
976 "\ttime_delta : 27 bits\n"
977 "\tarray : 32 bits\n"
978 "\n"
979 "\tpadding : type == 29\n"
980 "\ttime_extend : type == 30\n"
981 "\ttime_stamp : type == 31\n"
982 "\tdata max type_len == 28\n");
983 len = strlen(str);
984 tw->ptr += tputq(fout, len);
985 tw->ptr += tputs(fout, str);
986
987 /* number of ftrace-event-format files */
988 tw->ptr += tputl(fout, 3);
989
990 snprintf(str, sizeof(str),
991 "name: function\n"
992 "ID: 1\n"
993 "format:\n"
994 "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
995 "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
996 "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
997 "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
998 "\n"
999 "\tfield:unsigned long ip;\toffset:8;\tsize:8;\tsigned:0;\n"
1000 "\tfield:unsigned long parent_ip;\toffset:16;\tsize:8;\tsigned:0;\n"
1001 "\n"
1002 "print fmt: \" %%ps <-- %%ps\", (void *)REC->ip, (void *)REC->parent_ip\n");
1003 len = strlen(str);
1004 tw->ptr += tputq(fout, len);
1005 tw->ptr += tputs(fout, str);
1006
1007 snprintf(str, sizeof(str),
1008 "name: funcgraph_entry\n"
1009 "ID: 11\n"
1010 "format:\n"
1011 "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
1012 "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
1013 "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
1014 "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
1015 "\n"
1016 "\tfield:unsigned long func;\toffset:8;\tsize:8;\tsigned:0;\n"
1017 "\tfield:int depth;\toffset:16;\tsize:4;\tsigned:1;\n"
1018 "\n"
1019 "print fmt: \"--> %%ps (%%d)\", (void *)REC->func, REC->depth\n");
1020 len = strlen(str);
1021 tw->ptr += tputq(fout, len);
1022 tw->ptr += tputs(fout, str);
1023
1024 snprintf(str, sizeof(str),
1025 "name: funcgraph_exit\n"
1026 "ID: 10\n"
1027 "format:\n"
1028 "\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n"
1029 "\tfield:unsigned char common_flags;\toffset:2;\tsize:1;\tsigned:0;\n"
1030 "\tfield:unsigned char common_preempt_count;\toffset:3;\tsize:1;signed:0;\n"
1031 "\tfield:int common_pid;\toffset:4;\tsize:4;\tsigned:1;\n"
1032 "\n"
1033 "\tfield:unsigned long func;\toffset:8;\tsize:8;\tsigned:0;\n"
1034 "\tfield:int depth;\toffset:16;\tsize:4;\tsigned:1;\n"
1035 "\tfield:unsigned int overrun;\toffset:20;\tsize:4;\tsigned:0;\n"
1036 "\tfield:unsigned long long calltime;\toffset:24;\tsize:8;\tsigned:0;\n"
1037 "\tfield:unsigned long long rettime;\toffset:32;\tsize:8;\tsigned:0;\n"
1038 "\n"
1039 "print fmt: \"<-- %%ps (%%d) (start: %%llx end: %%llx) over: %%d\", (void *)REC->func, REC->depth, REC->calltime, REC->rettime, REC->depth\n");
1040 len = strlen(str);
1041 tw->ptr += tputq(fout, len);
1042 tw->ptr += tputs(fout, str);
1043
1044 return 0;
1045}
1046
1047/**
1048 * write_symbols() - Write the symbols out
1049 *
1050 * Writes the symbol information in the following format to mimic the Linux
1051 * /proc/kallsyms file:
1052 *
1053 * <address> T <name>
1054 *
1055 * This updates tw->ptr as it goes
1056 *
1057 * @tw: Writer context
1058 * Returns: 0 on success, -ve on error
1059 */
1060static int write_symbols(struct twriter *tw)
1061{
1062 char str[200];
1063 int ret, i;
1064
1065 /* write symbols */
1066 ret = push_len(tw, tw->ptr + 4, "syms", 4);
1067 if (ret < 0)
1068 return -1;
1069 tw->ptr += ret;
1070 for (i = 0; i < func_count; i++) {
1071 struct func_info *func = &func_list[i];
1072
1073 snprintf(str, sizeof(str), "%016lx T %s\n",
1074 text_offset + func->offset, func->name);
1075 tw->ptr += tputs(tw->fout, str);
1076 }
1077 ret = pop_len(tw, "syms");
1078 if (ret < 0)
1079 return -1;
1080 tw->ptr += ret;
1081
1082 return 0;
1083}
1084
1085/**
1086 * write_options() - Write the options out
1087 *
1088 * Writes various options which are needed or useful. We use OPTION_TSC2NSEC
1089 * to indicates that values in the output need to be multiplied by 1000 since
1090 * U-Boot's trace values are in microseconds.
1091 *
1092 * This updates tw->ptr as it goes
1093 *
1094 * @tw: Writer context
1095 * Returns: 0 on success, -ve on error
1096 */
1097static int write_options(struct twriter *tw)
1098{
1099 FILE *fout = tw->fout;
1100 char str[200];
1101 int len;
1102
1103 /* trace_printk, 0 for now */
1104 tw->ptr += tputl(fout, 0);
1105
1106 /* processes */
1107 snprintf(str, sizeof(str), "%d u-boot\n", TRACE_PID);
1108 len = strlen(str);
1109 tw->ptr += tputq(fout, len);
1110 tw->ptr += tputs(fout, str);
1111
1112 /* number of CPUs */
1113 tw->ptr += tputl(fout, 1);
1114
1115 tw->ptr += fprintf(fout, "options %c", 0);
1116
1117 /* traceclock */
1118 tw->ptr += tputh(fout, OPTION_TRACECLOCK);
1119 tw->ptr += tputl(fout, 0);
1120
1121 /* uname */
1122 tw->ptr += tputh(fout, OPTION_UNAME);
1123 snprintf(str, sizeof(str), "U-Boot");
1124 len = strlen(str);
1125 tw->ptr += tputl(fout, len);
1126 tw->ptr += tputs(fout, str);
1127
1128 /* version */
1129 tw->ptr += tputh(fout, OPTION_VERSION);
1130 snprintf(str, sizeof(str), "unknown");
1131 len = strlen(str);
1132 tw->ptr += tputl(fout, len);
1133 tw->ptr += tputs(fout, str);
1134
1135 /* trace ID */
1136 tw->ptr += tputh(fout, OPTION_TRACEID);
1137 tw->ptr += tputl(fout, 8);
1138 tw->ptr += tputq(fout, 0x123456780abcdef0);
1139
1140 /* time conversion */
1141 tw->ptr += tputh(fout, OPTION_TSC2NSEC);
1142 tw->ptr += tputl(fout, 16);
1143 tw->ptr += tputl(fout, 1000); /* multiplier */
1144 tw->ptr += tputl(fout, 0); /* shift */
1145 tw->ptr += tputq(fout, 0); /* offset */
1146
1147 /* cpustat - bogus data for now, but at least it mentions the CPU */
1148 tw->ptr += tputh(fout, OPTION_CPUSTAT);
1149 snprintf(str, sizeof(str),
1150 "CPU: 0\n"
1151 "entries: 100\n"
1152 "overrun: 43565\n"
1153 "commit overrun: 0\n"
1154 "bytes: 3360\n"
1155 "oldest event ts: 963732.447752\n"
1156 "now ts: 963832.146824\n"
1157 "dropped events: 0\n"
1158 "read events: 42379\n");
1159 len = strlen(str);
1160 tw->ptr += tputl(fout, len);
1161 tw->ptr += tputs(fout, str);
1162
1163 tw->ptr += tputh(fout, OPTION_DONE);
1164
1165 return 0;
1166}
1167
1168/**
1169 * write_pages() - Write the pages of trace data
1170 *
1171 * This works through all the calls, writing out as many pages of data as are
1172 * needed.
1173 *
1174 * @tw: Writer context
1175 * @missing_countp: Returns number of missing functions (not found in function
1176 * list)
1177 * @skip_countp: Returns number of skipped functions (excluded from trace)
1178 *
1179 * Returns: 0 on success, -ve on error
1180 */
1181static int write_pages(struct twriter *tw, int *missing_countp,
1182 int *skip_countp)
1183{
1184 int stack_ptr; /* next free position in stack */
1185 int upto, page_upto, i;
Simon Glass9c628922013-06-11 11:14:43 -07001186 int missing_count = 0, skip_count = 0;
Simon Glassdd8f19a2023-01-15 14:15:53 -07001187 struct trace_call *call;
1188 ulong last_timestamp;
1189 FILE *fout = tw->fout;
1190 int last_delta = 0;
1191 int err_count;
1192 bool in_page;
Simon Glass9c628922013-06-11 11:14:43 -07001193
Simon Glassdd8f19a2023-01-15 14:15:53 -07001194 in_page = false;
1195 last_timestamp = 0;
1196 upto = 0;
1197 page_upto = 0;
1198 err_count = 0;
1199
1200 /* maintain a stack of start times for calling functions */
1201 stack_ptr = 0;
1202
Simon Glass9c628922013-06-11 11:14:43 -07001203 for (i = 0, call = call_list; i < call_count; i++, call++) {
Simon Glassdd8f19a2023-01-15 14:15:53 -07001204 struct func_info *caller_func;
1205 struct func_info *func;
1206 ulong timestamp;
1207 uint rec_words;
1208 int delta;
Simon Glass9c628922013-06-11 11:14:43 -07001209
Simon Glassdd8f19a2023-01-15 14:15:53 -07001210 func = find_func_by_offset(call->func);
Simon Glass9c628922013-06-11 11:14:43 -07001211 if (!func) {
1212 warn("Cannot find function at %lx\n",
1213 text_offset + call->func);
1214 missing_count++;
Simon Glassdd8f19a2023-01-15 14:15:53 -07001215 if (missing_count > 20) {
1216 /* perhaps trace does not match System.map */
1217 fprintf(stderr, "Too many missing functions\n");
1218 return -1;
1219 }
Simon Glass9c628922013-06-11 11:14:43 -07001220 continue;
1221 }
1222
1223 if (!(func->flags & FUNCF_TRACE)) {
1224 debug("Funcion '%s' is excluded from trace\n",
1225 func->name);
1226 skip_count++;
1227 continue;
1228 }
1229
Simon Glassdd8f19a2023-01-15 14:15:53 -07001230 rec_words = 6;
1231
1232 /* convert timestamp from us to ns */
1233 timestamp = call->flags & FUNCF_TIMESTAMP_MASK;
1234 if (in_page) {
1235 if (page_upto + rec_words * 4 > TRACE_PAGE_SIZE) {
1236 if (finish_page(tw))
1237 return -1;
1238 in_page = false;
1239 }
1240 }
1241 if (!in_page) {
1242 if (start_page(tw, timestamp))
1243 return -1;
1244 in_page = true;
1245 last_timestamp = timestamp;
1246 last_delta = 0;
1247 page_upto = tw->ptr & TRACE_PAGE_MASK;
1248 if (_DEBUG) {
1249 fprintf(stderr,
1250 "new page, last_timestamp=%ld, upto=%d\n",
1251 last_timestamp, upto);
1252 }
1253 }
1254
1255 delta = timestamp - last_timestamp;
1256 if (delta < 0) {
1257 fprintf(stderr, "Time went backwards\n");
1258 err_count++;
1259 }
1260
1261 if (err_count > 20) {
1262 fprintf(stderr, "Too many errors, giving up\n");
1263 return -1;
1264 }
1265
1266 if (delta > 0x07fffff) {
1267 /*
1268 * hard to imagine how this could happen since it means
1269 * that no function calls were made for a long time
1270 */
1271 fprintf(stderr, "cannot represent time delta %x\n",
1272 delta);
1273 return -1;
1274 }
1275
1276 if (_DEBUG) {
1277 fprintf(stderr, "%d: delta=%d, stamp=%ld\n",
1278 upto, delta, timestamp);
1279 fprintf(stderr,
1280 " last_delta %x to %x: last_timestamp=%lx, timestamp=%lx, call->flags=%x, upto=%d\n",
1281 last_delta, delta, last_timestamp, timestamp, call->flags, upto);
1282 }
1283
1284 /* type_len is 6, meaning 4 * 6 = 24 bytes */
1285 tw->ptr += tputl(fout, rec_words | (uint)delta << 5);
1286 tw->ptr += tputh(fout, TRACE_FN);
1287 tw->ptr += tputh(fout, 0); /* flags */
1288 tw->ptr += tputl(fout, TRACE_PID); /* PID */
1289 /* function */
1290 tw->ptr += tputq(fout, text_offset + func->offset);
1291 caller_func = find_caller_by_offset(call->caller);
1292 /* caller */
1293 tw->ptr += tputq(fout, text_offset + caller_func->offset);
1294
1295 last_delta = delta;
1296 last_timestamp = timestamp;
1297 page_upto += 4 + rec_words * 4;
1298 upto++;
1299 if (stack_ptr == MAX_STACK_DEPTH)
1300 break;
1301 }
1302 if (in_page && finish_page(tw))
1303 return -1;
1304 *missing_countp = missing_count;
1305 *skip_countp = skip_count;
1306
1307 return 0;
1308}
1309
1310/**
1311 * write_flyrecord() - Write the flyrecord information
1312 *
1313 * Writes the header and pages of data for the "flyrecord" section. It also
1314 * writes out the counter-type info, selecting "[local]"
1315 *
1316 * @tw: Writer context
1317 * @missing_countp: Returns number of missing functions (not found in function
1318 * list)
1319 * @skip_countp: Returns number of skipped functions (excluded from trace)
1320 *
1321 * Returns: 0 on success, -ve on error
1322 */
1323static int write_flyrecord(struct twriter *tw, int *missing_countp,
1324 int *skip_countp)
1325{
1326 int start, ret, len;
1327 FILE *fout = tw->fout;
1328 char str[200];
1329
1330 tw->ptr += fprintf(fout, "flyrecord%c", 0);
1331
1332 /* trace data */
1333 start = ALIGN(tw->ptr + 16, TRACE_PAGE_SIZE);
1334 tw->ptr += tputq(fout, start);
1335
1336 /* use a placeholder for the size */
1337 ret = push_len(tw, start, "flyrecord", 8);
1338 if (ret < 0)
1339 return -1;
1340 tw->ptr += ret;
1341
1342 snprintf(str, sizeof(str),
1343 "[local] global counter uptime perf mono mono_raw boot x86-tsc\n");
1344 len = strlen(str);
1345 tw->ptr += tputq(fout, len);
1346 tw->ptr += tputs(fout, str);
1347
Simon Glass7ac0a502023-01-15 14:15:55 -07001348 debug("trace text base %lx, map file %lx\n", text_base, text_offset);
1349
Simon Glassdd8f19a2023-01-15 14:15:53 -07001350 ret = write_pages(tw, missing_countp, skip_countp);
1351 if (ret < 0) {
1352 fprintf(stderr, "Cannot output pages\n");
1353 return -1;
1354 }
1355
1356 ret = pop_len(tw, "flyrecord");
1357 if (ret < 0) {
1358 fprintf(stderr, "Cannot finish flyrecord header\n");
1359 return -1;
1360 }
1361
1362 return 0;
1363}
1364
1365/**
1366 * make_ftrace() - Write out an ftrace file
1367 *
1368 * See here for format:
1369 *
1370 * https://github.com/rostedt/trace-cmd/blob/master/Documentation/trace-cmd/trace-cmd.dat.v7.5.txt
1371 *
1372 * @fout: Output file
1373 * Returns: 0 on success, -ve on error
1374 */
1375static int make_ftrace(FILE *fout)
1376{
1377 int missing_count, skip_count;
1378 struct twriter tws, *tw = &tws;
1379 int ret;
1380
1381 memset(tw, '\0', sizeof(*tw));
1382 abuf_init(&tw->str_buf);
1383 tw->fout = fout;
1384
1385 tw->ptr = 0;
1386 ret = output_headers(tw);
1387 if (ret < 0) {
1388 fprintf(stderr, "Cannot output headers\n");
1389 return -1;
1390 }
1391 /* number of event systems files */
1392 tw->ptr += tputl(fout, 0);
Simon Glass9c628922013-06-11 11:14:43 -07001393
Simon Glassdd8f19a2023-01-15 14:15:53 -07001394 ret = write_symbols(tw);
1395 if (ret < 0) {
1396 fprintf(stderr, "Cannot write symbols\n");
1397 return -1;
1398 }
1399
1400 ret = write_options(tw);
1401 if (ret < 0) {
1402 fprintf(stderr, "Cannot write options\n");
1403 return -1;
Simon Glass9c628922013-06-11 11:14:43 -07001404 }
Simon Glassdd8f19a2023-01-15 14:15:53 -07001405
1406 ret = write_flyrecord(tw, &missing_count, &skip_count);
1407 if (ret < 0) {
1408 fprintf(stderr, "Cannot write flyrecord\n");
1409 return -1;
1410 }
1411
Simon Glass9c628922013-06-11 11:14:43 -07001412 info("ftrace: %d functions not found, %d excluded\n", missing_count,
1413 skip_count);
1414
1415 return 0;
1416}
1417
Simon Glass9e2afae2023-01-15 14:15:52 -07001418/**
1419 * prof_tool() - Performs requested action
1420 *
1421 * @argc: Number of arguments (used to obtain the command
1422 * @argv: List of arguments
1423 * @trace_fname: Filename of input file (trace data from U-Boot)
1424 * @map_fname: Filename of map file (System.map from U-Boot)
1425 * @trace_config_fname: Trace-configuration file, or NULL if none
1426 * @out_fname: Output filename
1427 */
Simon Glassed38aef2020-05-10 11:40:03 -06001428static int prof_tool(int argc, char *const argv[],
Simon Glass9e2afae2023-01-15 14:15:52 -07001429 const char *trace_fname, const char *map_fname,
1430 const char *trace_config_fname, const char *out_fname)
Simon Glass9c628922013-06-11 11:14:43 -07001431{
1432 int err = 0;
1433
1434 if (read_map_file(map_fname))
1435 return -1;
Simon Glass9e2afae2023-01-15 14:15:52 -07001436 if (trace_fname && read_trace_file(trace_fname))
Simon Glass9c628922013-06-11 11:14:43 -07001437 return -1;
1438 if (trace_config_fname && read_trace_config_file(trace_config_fname))
1439 return -1;
1440
Simon Glass0974d002023-01-15 14:15:54 -07001441 check_trace_config();
Simon Glass9c628922013-06-11 11:14:43 -07001442
1443 for (; argc; argc--, argv++) {
1444 const char *cmd = *argv;
1445
Simon Glassdd8f19a2023-01-15 14:15:53 -07001446 if (!strcmp(cmd, "dump-ftrace")) {
1447 FILE *fout;
1448
1449 fout = fopen(out_fname, "w");
1450 if (!fout) {
1451 fprintf(stderr, "Cannot write file '%s'\n",
1452 out_fname);
1453 return -1;
1454 }
1455 err = make_ftrace(fout);
1456 fclose(fout);
1457 } else {
Simon Glass9c628922013-06-11 11:14:43 -07001458 warn("Unknown command '%s'\n", cmd);
Simon Glassdd8f19a2023-01-15 14:15:53 -07001459 }
Simon Glass9c628922013-06-11 11:14:43 -07001460 }
1461
1462 return err;
1463}
1464
1465int main(int argc, char *argv[])
1466{
1467 const char *map_fname = "System.map";
Simon Glassec86a632022-12-21 16:08:24 -07001468 const char *trace_fname = NULL;
1469 const char *config_fname = NULL;
Simon Glass9e2afae2023-01-15 14:15:52 -07001470 const char *out_fname = NULL;
Simon Glass9c628922013-06-11 11:14:43 -07001471 int opt;
1472
1473 verbose = 2;
Simon Glass9e2afae2023-01-15 14:15:52 -07001474 while ((opt = getopt(argc, argv, "c:m:o:t:v:")) != -1) {
Simon Glass9c628922013-06-11 11:14:43 -07001475 switch (opt) {
Simon Glassec86a632022-12-21 16:08:24 -07001476 case 'c':
1477 config_fname = optarg;
Simon Glass9c628922013-06-11 11:14:43 -07001478 break;
Simon Glassec86a632022-12-21 16:08:24 -07001479 case 'm':
1480 map_fname = optarg;
Simon Glass9c628922013-06-11 11:14:43 -07001481 break;
Simon Glass9e2afae2023-01-15 14:15:52 -07001482 case 'o':
1483 out_fname = optarg;
1484 break;
Simon Glass9c628922013-06-11 11:14:43 -07001485 case 't':
Simon Glassec86a632022-12-21 16:08:24 -07001486 trace_fname = optarg;
Simon Glass9c628922013-06-11 11:14:43 -07001487 break;
Simon Glass9c628922013-06-11 11:14:43 -07001488 case 'v':
1489 verbose = atoi(optarg);
1490 break;
Simon Glass9c628922013-06-11 11:14:43 -07001491 default:
1492 usage();
1493 }
1494 }
1495 argc -= optind; argv += optind;
1496 if (argc < 1)
1497 usage();
1498
Simon Glass9e2afae2023-01-15 14:15:52 -07001499 if (!out_fname || !map_fname || !trace_fname) {
1500 fprintf(stderr,
1501 "Must provide trace data, System.map file and output file\n");
1502 usage();
1503 }
1504
Simon Glass9c628922013-06-11 11:14:43 -07001505 debug("Debug enabled\n");
Simon Glass9e2afae2023-01-15 14:15:52 -07001506 return prof_tool(argc, argv, trace_fname, map_fname, config_fname,
1507 out_fname);
Simon Glass9c628922013-06-11 11:14:43 -07001508}