blob: 6aa157c8b59e5e96d5482bcd0dadb7f7512bc928 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass6c887b22013-06-11 11:14:43 -07002/*
3 * Copyright (c) 2013 Google, Inc
Simon Glass6c887b22013-06-11 11:14:43 -07004 */
5
6/* Decode and dump U-Boot profiling information */
7
8#include <assert.h>
9#include <ctype.h>
10#include <limits.h>
11#include <regex.h>
12#include <stdarg.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <sys/param.h>
Jörg Krause26e355d2015-04-22 21:36:22 +020018#include <sys/types.h>
Simon Glass6c887b22013-06-11 11:14:43 -070019
20#include <compiler.h>
21#include <trace.h>
22
23#define MAX_LINE_LEN 500
24
25enum {
26 FUNCF_TRACE = 1 << 0, /* Include this function in trace */
27};
28
Simon Glass9efc0b22023-01-15 14:15:52 -070029/**
30 * struct func_info - information recorded for each function
31 *
32 * @offset: Function offset in the image, measured from the text_base
33 * @name: Function name
34 * @code_size: Total code size of the function
35 * @flags: Either 0 or FUNCF_TRACE
36 * @objsection: the section this function is in
37 */
Simon Glass6c887b22013-06-11 11:14:43 -070038struct func_info {
39 unsigned long offset;
40 const char *name;
41 unsigned long code_size;
Simon Glass6c887b22013-06-11 11:14:43 -070042 unsigned flags;
Simon Glass6c887b22013-06-11 11:14:43 -070043 struct objsection_info *objsection;
44};
45
Simon Glass9efc0b22023-01-15 14:15:52 -070046/**
47 * enum trace_line_type - whether to include or exclude a function
48 *
49 * @TRACE_LINE_INCLUDE: Include the function
50 * @TRACE_LINE_EXCLUDE: Exclude the function
51 */
Simon Glass6c887b22013-06-11 11:14:43 -070052enum trace_line_type {
53 TRACE_LINE_INCLUDE,
54 TRACE_LINE_EXCLUDE,
55};
56
Simon Glass9efc0b22023-01-15 14:15:52 -070057/**
58 * struct trace_configline_info - information about a config-file line
59 *
60 * @next: Next line
61 * @type: Line type
62 * @name: identifier name / wildcard
63 * @regex: Regex to use if name starts with '/'
64 */
Simon Glass6c887b22013-06-11 11:14:43 -070065struct trace_configline_info {
66 struct trace_configline_info *next;
67 enum trace_line_type type;
Simon Glass9efc0b22023-01-15 14:15:52 -070068 const char *name;
69 regex_t regex;
Simon Glass6c887b22013-06-11 11:14:43 -070070};
71
72/* The contents of the trace config file */
73struct trace_configline_info *trace_config_head;
74
Simon Glass9efc0b22023-01-15 14:15:52 -070075/* list of all functions in System.map file, sorted by offset in the image */
Simon Glass6c887b22013-06-11 11:14:43 -070076struct func_info *func_list;
Simon Glass6c887b22013-06-11 11:14:43 -070077
Simon Glass9efc0b22023-01-15 14:15:52 -070078int func_count; /* number of functions */
79struct trace_call *call_list; /* list of all calls in the input trace file */
80int call_count; /* number of calls */
81int verbose; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */
82ulong text_offset; /* text address of first function */
83
84/* debugging helpers */
Simon Glass6c887b22013-06-11 11:14:43 -070085static void outf(int level, const char *fmt, ...)
86 __attribute__ ((format (__printf__, 2, 3)));
87#define error(fmt, b...) outf(0, fmt, ##b)
88#define warn(fmt, b...) outf(1, fmt, ##b)
89#define notice(fmt, b...) outf(2, fmt, ##b)
90#define info(fmt, b...) outf(3, fmt, ##b)
91#define debug(fmt, b...) outf(4, fmt, ##b)
92
Simon Glass6c887b22013-06-11 11:14:43 -070093static void outf(int level, const char *fmt, ...)
94{
95 if (verbose >= level) {
96 va_list args;
97
98 va_start(args, fmt);
99 vfprintf(stderr, fmt, args);
100 va_end(args);
101 }
102}
103
104static void usage(void)
105{
106 fprintf(stderr,
Simon Glassb87f0812022-12-21 16:08:24 -0700107 "Usage: proftool [-cmtv] <cmd> <profdata>\n"
Simon Glass6c887b22013-06-11 11:14:43 -0700108 "\n"
109 "Commands\n"
Simon Glass9efc0b22023-01-15 14:15:52 -0700110 " dump-ftrace\t\tDump out records in ftrace format for use by trace-cmd\n"
Simon Glass6c887b22013-06-11 11:14:43 -0700111 "\n"
112 "Options:\n"
Simon Glass9efc0b22023-01-15 14:15:52 -0700113 " -c <cfg>\tSpecify config file\n"
Simon Glass6c887b22013-06-11 11:14:43 -0700114 " -m <map>\tSpecify Systen.map file\n"
Simon Glass9efc0b22023-01-15 14:15:52 -0700115 " -o <fname>\tSpecify output file\n"
Simon Glassb87f0812022-12-21 16:08:24 -0700116 " -t <fname>\tSpecify trace data file (from U-Boot 'trace calls')\n"
Simon Glass6c887b22013-06-11 11:14:43 -0700117 " -v <0-4>\tSpecify verbosity\n");
118 exit(EXIT_FAILURE);
119}
120
Simon Glass9efc0b22023-01-15 14:15:52 -0700121/**
122 * h_cmp_offset - bsearch() function to compare two functions bny their offset
123 *
124 * @v1: Pointer to first function (struct func_info)
125 * @v2: Pointer to second function (struct func_info)
126 * Returns: < 0 if v1 offset < v2 offset, 0 if equal, > 0 otherwise
127 */
Simon Glass6c887b22013-06-11 11:14:43 -0700128static int h_cmp_offset(const void *v1, const void *v2)
129{
130 const struct func_info *f1 = v1, *f2 = v2;
131
132 return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE);
133}
134
Simon Glass9efc0b22023-01-15 14:15:52 -0700135/**
136 * read_system_map() - read the System.map file to create a list of functions
137 *
138 * This also reads the text_offset value, since we assume that the first text
139 * symbol is at that address
140 *
141 * @fin: File to read
142 * Returns: 0 if OK, non-zero on error
143 */
Simon Glass6c887b22013-06-11 11:14:43 -0700144static int read_system_map(FILE *fin)
145{
146 unsigned long offset, start = 0;
147 struct func_info *func;
148 char buff[MAX_LINE_LEN];
149 char symtype;
150 char symname[MAX_LINE_LEN + 1];
151 int linenum;
152 int alloced;
153
154 for (linenum = 1, alloced = func_count = 0;; linenum++) {
155 int fields = 0;
156
157 if (fgets(buff, sizeof(buff), fin))
158 fields = sscanf(buff, "%lx %c %100s\n", &offset,
159 &symtype, symname);
160 if (fields == 2) {
161 continue;
162 } else if (feof(fin)) {
163 break;
164 } else if (fields < 2) {
165 error("Map file line %d: invalid format\n", linenum);
166 return 1;
167 }
168
169 /* Must be a text symbol */
170 symtype = tolower(symtype);
171 if (symtype != 't' && symtype != 'w')
172 continue;
173
174 if (func_count == alloced) {
175 alloced += 256;
176 func_list = realloc(func_list,
177 sizeof(struct func_info) * alloced);
178 assert(func_list);
179 }
180 if (!func_count)
181 start = offset;
182
183 func = &func_list[func_count++];
184 memset(func, '\0', sizeof(*func));
185 func->offset = offset - start;
186 func->name = strdup(symname);
187 func->flags = FUNCF_TRACE; /* trace by default */
188
189 /* Update previous function's code size */
190 if (func_count > 1)
191 func[-1].code_size = func->offset - func[-1].offset;
192 }
193 notice("%d functions found in map file\n", func_count);
194 text_offset = start;
Simon Glass9efc0b22023-01-15 14:15:52 -0700195
Simon Glass6c887b22013-06-11 11:14:43 -0700196 return 0;
197}
198
199static int read_data(FILE *fin, void *buff, int size)
200{
201 int err;
202
203 err = fread(buff, 1, size, fin);
204 if (!err)
205 return 1;
206 if (err != size) {
Simon Glass9efc0b22023-01-15 14:15:52 -0700207 error("Cannot read trace file at pos %lx\n", ftell(fin));
Simon Glass6c887b22013-06-11 11:14:43 -0700208 return -1;
209 }
210 return 0;
211}
212
Simon Glass9efc0b22023-01-15 14:15:52 -0700213/**
214 * find_func_by_offset() - Look up a function by its offset
215 *
216 * @offset: Offset to search for, from text_base
217 * Returns: function, if found, else NULL
218 *
219 * This does a fast search for a function given its offset from text_base
220 *
221 */
222static struct func_info *find_func_by_offset(uint offset)
Simon Glass6c887b22013-06-11 11:14:43 -0700223{
224 struct func_info key, *found;
225
226 key.offset = offset;
227 found = bsearch(&key, func_list, func_count, sizeof(struct func_info),
228 h_cmp_offset);
229
230 return found;
231}
232
Simon Glass9efc0b22023-01-15 14:15:52 -0700233/**
234 * find_caller_by_offset() - finds the function which contains the given offset
235 *
236 * @offset: Offset to search for, from text_base
237 * Returns: function, if found, else NULL
238 *
239 * If the offset falls between two functions, then it is assumed to belong to
240 * the first function (with the lowest offset). This is a way of figuring out
241 * which function owns code at a particular offset
242 */
243static struct func_info *find_caller_by_offset(uint offset)
Simon Glass6c887b22013-06-11 11:14:43 -0700244{
245 int low; /* least function that could be a match */
246 int high; /* greated function that could be a match */
247 struct func_info key;
248
249 low = 0;
250 high = func_count - 1;
251 key.offset = offset;
252 while (high > low + 1) {
253 int mid = (low + high) / 2;
254 int result;
255
256 result = h_cmp_offset(&key, &func_list[mid]);
257 if (result > 0)
258 low = mid;
259 else if (result < 0)
260 high = mid;
261 else
262 return &func_list[mid];
263 }
264
265 return low >= 0 ? &func_list[low] : NULL;
266}
267
Simon Glass9efc0b22023-01-15 14:15:52 -0700268/**
269 * read_calls() - Read the list of calls from the trace data
270 *
271 * The calls are stored consecutively in the trace output produced by U-Boot
272 *
273 * @fin: File to read from
274 * @count: Number of calls to read
275 * Returns: 0 if OK, -1 on error
276 */
Heinrich Schuchardt2b7a3882019-06-14 21:50:55 +0200277static int read_calls(FILE *fin, size_t count)
Simon Glass6c887b22013-06-11 11:14:43 -0700278{
279 struct trace_call *call_data;
280 int i;
281
Heinrich Schuchardt2b7a3882019-06-14 21:50:55 +0200282 notice("call count: %zu\n", count);
Simon Glass6c887b22013-06-11 11:14:43 -0700283 call_list = (struct trace_call *)calloc(count, sizeof(*call_data));
284 if (!call_list) {
285 error("Cannot allocate call_list\n");
286 return -1;
287 }
288 call_count = count;
289
290 call_data = call_list;
291 for (i = 0; i < count; i++, call_data++) {
292 if (read_data(fin, call_data, sizeof(*call_data)))
Simon Glass9efc0b22023-01-15 14:15:52 -0700293 return -1;
Simon Glass6c887b22013-06-11 11:14:43 -0700294 }
295 return 0;
296}
297
Simon Glass9efc0b22023-01-15 14:15:52 -0700298/**
299 * read_trace() - Read the U-Boot trace file
300 *
301 * Read in the calls from the trace file. The function list is ignored at
302 * present
303 *
304 * @fin: File to read
305 * Returns 0 if OK, non-zero on error
306 */
307static int read_trace(FILE *fin)
Simon Glass6c887b22013-06-11 11:14:43 -0700308{
309 struct trace_output_hdr hdr;
310
Simon Glass6c887b22013-06-11 11:14:43 -0700311 while (!feof(fin)) {
312 int err;
313
314 err = read_data(fin, &hdr, sizeof(hdr));
315 if (err == 1)
316 break; /* EOF */
317 else if (err)
318 return 1;
319
320 switch (hdr.type) {
321 case TRACE_CHUNK_FUNCS:
322 /* Ignored at present */
323 break;
324
325 case TRACE_CHUNK_CALLS:
326 if (read_calls(fin, hdr.rec_count))
327 return 1;
328 break;
329 }
330 }
331 return 0;
332}
333
Simon Glass9efc0b22023-01-15 14:15:52 -0700334/**
335 * read_map_file() - Read the System.map file
336 *
337 * This reads the file into the func_list array
338 *
339 * @fname: Filename to read
340 * Returns 0 if OK, non-zero on error
341 */
Simon Glass6c887b22013-06-11 11:14:43 -0700342static int read_map_file(const char *fname)
343{
344 FILE *fmap;
345 int err = 0;
346
347 fmap = fopen(fname, "r");
348 if (!fmap) {
349 error("Cannot open map file '%s'\n", fname);
350 return 1;
351 }
352 if (fmap) {
353 err = read_system_map(fmap);
354 fclose(fmap);
355 }
356 return err;
357}
358
Simon Glass9efc0b22023-01-15 14:15:52 -0700359/**
360 * read_trace_file() - Open and read the U-Boot trace file
361 *
362 * Read in the calls from the trace file. The function list is ignored at
363 * present
364 *
365 * @fin: File to read
366 * Returns 0 if OK, non-zero on error
367 */
368static int read_trace_file(const char *fname)
Simon Glass6c887b22013-06-11 11:14:43 -0700369{
Simon Glass6c887b22013-06-11 11:14:43 -0700370 FILE *fprof;
371 int err;
372
373 fprof = fopen(fname, "rb");
374 if (!fprof) {
Simon Glass9efc0b22023-01-15 14:15:52 -0700375 error("Cannot open trace data file '%s'\n",
Simon Glass6c887b22013-06-11 11:14:43 -0700376 fname);
377 return 1;
378 } else {
Simon Glass9efc0b22023-01-15 14:15:52 -0700379 err = read_trace(fprof);
Simon Glass6c887b22013-06-11 11:14:43 -0700380 fclose(fprof);
381 if (err)
382 return err;
Simon Glass6c887b22013-06-11 11:14:43 -0700383 }
384 return 0;
385}
386
387static int regex_report_error(regex_t *regex, int err, const char *op,
388 const char *name)
389{
390 char buf[200];
391
392 regerror(err, regex, buf, sizeof(buf));
393 error("Regex error '%s' in %s '%s'\n", buf, op, name);
394 return -1;
395}
396
397static void check_trace_config_line(struct trace_configline_info *item)
398{
399 struct func_info *func, *end;
400 int err;
401
402 debug("Checking trace config line '%s'\n", item->name);
403 for (func = func_list, end = func + func_count; func < end; func++) {
404 err = regexec(&item->regex, func->name, 0, NULL, 0);
405 debug(" - regex '%s', string '%s': %d\n", item->name,
406 func->name, err);
407 if (err == REG_NOMATCH)
408 continue;
409
Andreas Bießmannd4125eb2013-07-02 08:37:36 +0200410 if (err) {
Simon Glass6c887b22013-06-11 11:14:43 -0700411 regex_report_error(&item->regex, err, "match",
412 item->name);
413 break;
414 }
415
416 /* It matches, so perform the action */
417 switch (item->type) {
418 case TRACE_LINE_INCLUDE:
419 info(" include %s at %lx\n", func->name,
420 text_offset + func->offset);
421 func->flags |= FUNCF_TRACE;
422 break;
423
424 case TRACE_LINE_EXCLUDE:
425 info(" exclude %s at %lx\n", func->name,
426 text_offset + func->offset);
427 func->flags &= ~FUNCF_TRACE;
428 break;
429 }
430 }
431}
432
Simon Glass9efc0b22023-01-15 14:15:52 -0700433/** check_trace_config() - Check trace-config file, reporting any problems */
Simon Glass6c887b22013-06-11 11:14:43 -0700434static void check_trace_config(void)
435{
436 struct trace_configline_info *line;
437
438 for (line = trace_config_head; line; line = line->next)
439 check_trace_config_line(line);
440}
441
442/**
443 * Check the functions to see if they each have an objsection. If not, then
444 * the linker must have eliminated them.
445 */
446static void check_functions(void)
447{
448 struct func_info *func, *end;
449 unsigned long removed_code_size = 0;
450 int not_found = 0;
451
452 /* Look for missing functions */
453 for (func = func_list, end = func + func_count; func < end; func++) {
454 if (!func->objsection) {
455 removed_code_size += func->code_size;
456 not_found++;
457 }
458 }
459
460 /* Figure out what functions we want to trace */
461 check_trace_config();
462
463 warn("%d functions removed by linker, %ld code size\n",
464 not_found, removed_code_size);
465}
466
Simon Glass9efc0b22023-01-15 14:15:52 -0700467/**
468 * read_trace_config() - read the trace-config file
469 *
470 * This file consists of lines like:
471 *
472 * include-func <regex>
473 * exclude-func <regex>
474 *
475 * where <regex> is a regular expression matched against function names. It
476 * allows some functions to be dropped from the trace when producing ftrace
477 * records
478 *
479 * @fin: File to process
480 * Returns: 0 if OK, -1 on error
481 */
Simon Glass6c887b22013-06-11 11:14:43 -0700482static int read_trace_config(FILE *fin)
483{
484 char buff[200];
485 int linenum = 0;
486 struct trace_configline_info **tailp = &trace_config_head;
487
488 while (fgets(buff, sizeof(buff), fin)) {
489 int len = strlen(buff);
490 struct trace_configline_info *line;
491 char *saveptr;
492 char *s, *tok;
493 int err;
494
495 linenum++;
496 if (len && buff[len - 1] == '\n')
497 buff[len - 1] = '\0';
498
499 /* skip blank lines and comments */
500 for (s = buff; *s == ' ' || *s == '\t'; s++)
501 ;
502 if (!*s || *s == '#')
503 continue;
504
Simon Glass9efc0b22023-01-15 14:15:52 -0700505 line = (struct trace_configline_info *)calloc(1, sizeof(*line));
Simon Glass6c887b22013-06-11 11:14:43 -0700506 if (!line) {
507 error("Cannot allocate config line\n");
508 return -1;
509 }
510
511 tok = strtok_r(s, " \t", &saveptr);
512 if (!tok) {
513 error("Invalid trace config data on line %d\n",
514 linenum);
515 return -1;
516 }
517 if (0 == strcmp(tok, "include-func")) {
518 line->type = TRACE_LINE_INCLUDE;
519 } else if (0 == strcmp(tok, "exclude-func")) {
520 line->type = TRACE_LINE_EXCLUDE;
521 } else {
522 error("Unknown command in trace config data line %d\n",
523 linenum);
524 return -1;
525 }
526
527 tok = strtok_r(NULL, " \t", &saveptr);
528 if (!tok) {
529 error("Missing pattern in trace config data line %d\n",
530 linenum);
531 return -1;
532 }
533
534 err = regcomp(&line->regex, tok, REG_NOSUB);
535 if (err) {
Vincent Stehlé1ca8f882015-10-07 15:48:48 +0200536 int r = regex_report_error(&line->regex, err,
537 "compile", tok);
Simon Glass6c887b22013-06-11 11:14:43 -0700538 free(line);
Vincent Stehlé1ca8f882015-10-07 15:48:48 +0200539 return r;
Simon Glass6c887b22013-06-11 11:14:43 -0700540 }
541
542 /* link this new one to the end of the list */
543 line->name = strdup(tok);
544 line->next = NULL;
545 *tailp = line;
546 tailp = &line->next;
547 }
548
549 if (!feof(fin)) {
550 error("Cannot read from trace config file at position %ld\n",
551 ftell(fin));
552 return -1;
553 }
554 return 0;
555}
556
557static int read_trace_config_file(const char *fname)
558{
559 FILE *fin;
560 int err;
561
562 fin = fopen(fname, "r");
563 if (!fin) {
564 error("Cannot open trace_config file '%s'\n", fname);
565 return -1;
566 }
567 err = read_trace_config(fin);
568 fclose(fin);
569 return err;
570}
571
572static void out_func(ulong func_offset, int is_caller, const char *suffix)
573{
574 struct func_info *func;
575
576 func = (is_caller ? find_caller_by_offset : find_func_by_offset)
577 (func_offset);
578
579 if (func)
580 printf("%s%s", func->name, suffix);
581 else
582 printf("%lx%s", func_offset, suffix);
583}
584
585/*
586 * # tracer: function
587 * #
588 * # TASK-PID CPU# TIMESTAMP FUNCTION
589 * # | | | | |
590 * # bash-4251 [01] 10152.583854: path_put <-path_walk
591 * # bash-4251 [01] 10152.583855: dput <-path_put
592 * # bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput
593 */
594static int make_ftrace(void)
595{
596 struct trace_call *call;
597 int missing_count = 0, skip_count = 0;
598 int i;
599
Simon Glassef42e272022-12-21 16:08:25 -0700600 printf("# tracer: function\n"
601 "#\n"
602 "# entries-in-buffer/entries-written: 140080/250280 #P:4\n"
603 "#\n"
604 "# _-----=> irqs-off\n"
605 "# / _----=> need-resched\n"
606 "# | / _---=> hardirq/softirq\n"
607 "# || / _--=> preempt-depth\n"
608 "# ||| / delay\n"
609 "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n"
610 "# | | | |||| | |\n");
Simon Glass6c887b22013-06-11 11:14:43 -0700611 for (i = 0, call = call_list; i < call_count; i++, call++) {
612 struct func_info *func = find_func_by_offset(call->func);
613 ulong time = call->flags & FUNCF_TIMESTAMP_MASK;
614
615 if (TRACE_CALL_TYPE(call) != FUNCF_ENTRY &&
616 TRACE_CALL_TYPE(call) != FUNCF_EXIT)
617 continue;
618 if (!func) {
619 warn("Cannot find function at %lx\n",
620 text_offset + call->func);
621 missing_count++;
622 continue;
623 }
624
625 if (!(func->flags & FUNCF_TRACE)) {
626 debug("Funcion '%s' is excluded from trace\n",
627 func->name);
628 skip_count++;
629 continue;
630 }
631
Simon Glassef42e272022-12-21 16:08:25 -0700632 printf("%16s-%-5d [000] .... %lu.%06lu: ", "uboot", 1,
Simon Glass6c887b22013-06-11 11:14:43 -0700633 time / 1000000, time % 1000000);
634
635 out_func(call->func, 0, " <- ");
636 out_func(call->caller, 1, "\n");
637 }
638 info("ftrace: %d functions not found, %d excluded\n", missing_count,
639 skip_count);
640
641 return 0;
642}
643
Simon Glass9efc0b22023-01-15 14:15:52 -0700644/**
645 * prof_tool() - Performs requested action
646 *
647 * @argc: Number of arguments (used to obtain the command
648 * @argv: List of arguments
649 * @trace_fname: Filename of input file (trace data from U-Boot)
650 * @map_fname: Filename of map file (System.map from U-Boot)
651 * @trace_config_fname: Trace-configuration file, or NULL if none
652 * @out_fname: Output filename
653 */
Simon Glass09140112020-05-10 11:40:03 -0600654static int prof_tool(int argc, char *const argv[],
Simon Glass9efc0b22023-01-15 14:15:52 -0700655 const char *trace_fname, const char *map_fname,
656 const char *trace_config_fname, const char *out_fname)
Simon Glass6c887b22013-06-11 11:14:43 -0700657{
658 int err = 0;
659
660 if (read_map_file(map_fname))
661 return -1;
Simon Glass9efc0b22023-01-15 14:15:52 -0700662 if (trace_fname && read_trace_file(trace_fname))
Simon Glass6c887b22013-06-11 11:14:43 -0700663 return -1;
664 if (trace_config_fname && read_trace_config_file(trace_config_fname))
665 return -1;
666
667 check_functions();
668
669 for (; argc; argc--, argv++) {
670 const char *cmd = *argv;
671
672 if (0 == strcmp(cmd, "dump-ftrace"))
673 err = make_ftrace();
674 else
675 warn("Unknown command '%s'\n", cmd);
676 }
677
678 return err;
679}
680
681int main(int argc, char *argv[])
682{
683 const char *map_fname = "System.map";
Simon Glassb87f0812022-12-21 16:08:24 -0700684 const char *trace_fname = NULL;
685 const char *config_fname = NULL;
Simon Glass9efc0b22023-01-15 14:15:52 -0700686 const char *out_fname = NULL;
Simon Glass6c887b22013-06-11 11:14:43 -0700687 int opt;
688
689 verbose = 2;
Simon Glass9efc0b22023-01-15 14:15:52 -0700690 while ((opt = getopt(argc, argv, "c:m:o:t:v:")) != -1) {
Simon Glass6c887b22013-06-11 11:14:43 -0700691 switch (opt) {
Simon Glassb87f0812022-12-21 16:08:24 -0700692 case 'c':
693 config_fname = optarg;
694 break;
Simon Glass6c887b22013-06-11 11:14:43 -0700695 case 'm':
696 map_fname = optarg;
697 break;
Simon Glass9efc0b22023-01-15 14:15:52 -0700698 case 'o':
699 out_fname = optarg;
700 break;
Simon Glass6c887b22013-06-11 11:14:43 -0700701 case 't':
Simon Glassb87f0812022-12-21 16:08:24 -0700702 trace_fname = optarg;
Simon Glass6c887b22013-06-11 11:14:43 -0700703 break;
Simon Glass6c887b22013-06-11 11:14:43 -0700704 case 'v':
705 verbose = atoi(optarg);
706 break;
Simon Glass6c887b22013-06-11 11:14:43 -0700707 default:
708 usage();
709 }
710 }
711 argc -= optind; argv += optind;
712 if (argc < 1)
713 usage();
714
Simon Glass9efc0b22023-01-15 14:15:52 -0700715 if (!out_fname || !map_fname || !trace_fname) {
716 fprintf(stderr,
717 "Must provide trace data, System.map file and output file\n");
718 usage();
719 }
720
Simon Glass6c887b22013-06-11 11:14:43 -0700721 debug("Debug enabled\n");
Simon Glass9efc0b22023-01-15 14:15:52 -0700722 return prof_tool(argc, argv, trace_fname, map_fname, config_fname,
723 out_fname);
Simon Glass6c887b22013-06-11 11:14:43 -0700724}