]>
git.ipfire.org Git - people/ms/u-boot.git/blob - tools/proftool.c
2 * Copyright (c) 2013 Google, Inc
4 * SPDX-License-Identifier: GPL-2.0+
7 /* Decode and dump U-Boot profiling information */
18 #include <sys/param.h>
19 #include <sys/types.h>
24 #define MAX_LINE_LEN 500
27 FUNCF_TRACE
= 1 << 0, /* Include this function in trace */
33 unsigned long code_size
;
34 unsigned long call_count
;
36 /* the section this function is in */
37 struct objsection_info
*objsection
;
40 enum trace_line_type
{
45 struct trace_configline_info
{
46 struct trace_configline_info
*next
;
47 enum trace_line_type type
;
48 const char *name
; /* identifier name / wildcard */
49 regex_t regex
; /* Regex to use if name starts with / */
52 /* The contents of the trace config file */
53 struct trace_configline_info
*trace_config_head
;
55 struct func_info
*func_list
;
57 struct trace_call
*call_list
;
59 int verbose
; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */
60 unsigned long text_offset
; /* text address of first function */
62 static void outf(int level
, const char *fmt
, ...)
63 __attribute__ ((format (__printf__
, 2, 3)));
64 #define error(fmt, b...) outf(0, fmt, ##b)
65 #define warn(fmt, b...) outf(1, fmt, ##b)
66 #define notice(fmt, b...) outf(2, fmt, ##b)
67 #define info(fmt, b...) outf(3, fmt, ##b)
68 #define debug(fmt, b...) outf(4, fmt, ##b)
71 static void outf(int level
, const char *fmt
, ...)
73 if (verbose
>= level
) {
77 vfprintf(stderr
, fmt
, args
);
82 static void usage(void)
85 "Usage: proftool -cds -v3 <cmd> <profdata>\n"
88 " dump-ftrace\t\tDump out textual data in ftrace format\n"
91 " -m <map>\tSpecify Systen.map file\n"
92 " -t <trace>\tSpecific trace data file (from U-Boot)\n"
93 " -v <0-4>\tSpecify verbosity\n");
97 static int h_cmp_offset(const void *v1
, const void *v2
)
99 const struct func_info
*f1
= v1
, *f2
= v2
;
101 return (f1
->offset
/ FUNC_SITE_SIZE
) - (f2
->offset
/ FUNC_SITE_SIZE
);
104 static int read_system_map(FILE *fin
)
106 unsigned long offset
, start
= 0;
107 struct func_info
*func
;
108 char buff
[MAX_LINE_LEN
];
110 char symname
[MAX_LINE_LEN
+ 1];
114 for (linenum
= 1, alloced
= func_count
= 0;; linenum
++) {
117 if (fgets(buff
, sizeof(buff
), fin
))
118 fields
= sscanf(buff
, "%lx %c %100s\n", &offset
,
122 } else if (feof(fin
)) {
124 } else if (fields
< 2) {
125 error("Map file line %d: invalid format\n", linenum
);
129 /* Must be a text symbol */
130 symtype
= tolower(symtype
);
131 if (symtype
!= 't' && symtype
!= 'w')
134 if (func_count
== alloced
) {
136 func_list
= realloc(func_list
,
137 sizeof(struct func_info
) * alloced
);
143 func
= &func_list
[func_count
++];
144 memset(func
, '\0', sizeof(*func
));
145 func
->offset
= offset
- start
;
146 func
->name
= strdup(symname
);
147 func
->flags
= FUNCF_TRACE
; /* trace by default */
149 /* Update previous function's code size */
151 func
[-1].code_size
= func
->offset
- func
[-1].offset
;
153 notice("%d functions found in map file\n", func_count
);
158 static int read_data(FILE *fin
, void *buff
, int size
)
162 err
= fread(buff
, 1, size
, fin
);
166 error("Cannot read profile file at pos %ld\n", ftell(fin
));
172 static struct func_info
*find_func_by_offset(uint32_t offset
)
174 struct func_info key
, *found
;
177 found
= bsearch(&key
, func_list
, func_count
, sizeof(struct func_info
),
183 /* This finds the function which contains the given offset */
184 static struct func_info
*find_caller_by_offset(uint32_t offset
)
186 int low
; /* least function that could be a match */
187 int high
; /* greated function that could be a match */
188 struct func_info key
;
191 high
= func_count
- 1;
193 while (high
> low
+ 1) {
194 int mid
= (low
+ high
) / 2;
197 result
= h_cmp_offset(&key
, &func_list
[mid
]);
203 return &func_list
[mid
];
206 return low
>= 0 ? &func_list
[low
] : NULL
;
209 static int read_calls(FILE *fin
, int count
)
211 struct trace_call
*call_data
;
214 notice("call count: %d\n", count
);
215 call_list
= (struct trace_call
*)calloc(count
, sizeof(*call_data
));
217 error("Cannot allocate call_list\n");
222 call_data
= call_list
;
223 for (i
= 0; i
< count
; i
++, call_data
++) {
224 if (read_data(fin
, call_data
, sizeof(*call_data
)))
230 static int read_profile(FILE *fin
, int *not_found
)
232 struct trace_output_hdr hdr
;
238 err
= read_data(fin
, &hdr
, sizeof(hdr
));
245 case TRACE_CHUNK_FUNCS
:
246 /* Ignored at present */
249 case TRACE_CHUNK_CALLS
:
250 if (read_calls(fin
, hdr
.rec_count
))
258 static int read_map_file(const char *fname
)
263 fmap
= fopen(fname
, "r");
265 error("Cannot open map file '%s'\n", fname
);
269 err
= read_system_map(fmap
);
275 static int read_profile_file(const char *fname
)
277 int not_found
= INT_MAX
;
281 fprof
= fopen(fname
, "rb");
283 error("Cannot open profile data file '%s'\n",
287 err
= read_profile(fprof
, ¬_found
);
293 warn("%d profile functions could not be found in the map file - are you sure that your profile data and map file correspond?\n",
301 static int regex_report_error(regex_t
*regex
, int err
, const char *op
,
306 regerror(err
, regex
, buf
, sizeof(buf
));
307 error("Regex error '%s' in %s '%s'\n", buf
, op
, name
);
311 static void check_trace_config_line(struct trace_configline_info
*item
)
313 struct func_info
*func
, *end
;
316 debug("Checking trace config line '%s'\n", item
->name
);
317 for (func
= func_list
, end
= func
+ func_count
; func
< end
; func
++) {
318 err
= regexec(&item
->regex
, func
->name
, 0, NULL
, 0);
319 debug(" - regex '%s', string '%s': %d\n", item
->name
,
321 if (err
== REG_NOMATCH
)
325 regex_report_error(&item
->regex
, err
, "match",
330 /* It matches, so perform the action */
331 switch (item
->type
) {
332 case TRACE_LINE_INCLUDE
:
333 info(" include %s at %lx\n", func
->name
,
334 text_offset
+ func
->offset
);
335 func
->flags
|= FUNCF_TRACE
;
338 case TRACE_LINE_EXCLUDE
:
339 info(" exclude %s at %lx\n", func
->name
,
340 text_offset
+ func
->offset
);
341 func
->flags
&= ~FUNCF_TRACE
;
347 static void check_trace_config(void)
349 struct trace_configline_info
*line
;
351 for (line
= trace_config_head
; line
; line
= line
->next
)
352 check_trace_config_line(line
);
356 * Check the functions to see if they each have an objsection. If not, then
357 * the linker must have eliminated them.
359 static void check_functions(void)
361 struct func_info
*func
, *end
;
362 unsigned long removed_code_size
= 0;
365 /* Look for missing functions */
366 for (func
= func_list
, end
= func
+ func_count
; func
< end
; func
++) {
367 if (!func
->objsection
) {
368 removed_code_size
+= func
->code_size
;
373 /* Figure out what functions we want to trace */
374 check_trace_config();
376 warn("%d functions removed by linker, %ld code size\n",
377 not_found
, removed_code_size
);
380 static int read_trace_config(FILE *fin
)
384 struct trace_configline_info
**tailp
= &trace_config_head
;
386 while (fgets(buff
, sizeof(buff
), fin
)) {
387 int len
= strlen(buff
);
388 struct trace_configline_info
*line
;
394 if (len
&& buff
[len
- 1] == '\n')
395 buff
[len
- 1] = '\0';
397 /* skip blank lines and comments */
398 for (s
= buff
; *s
== ' ' || *s
== '\t'; s
++)
400 if (!*s
|| *s
== '#')
403 line
= (struct trace_configline_info
*)calloc(1,
406 error("Cannot allocate config line\n");
410 tok
= strtok_r(s
, " \t", &saveptr
);
412 error("Invalid trace config data on line %d\n",
416 if (0 == strcmp(tok
, "include-func")) {
417 line
->type
= TRACE_LINE_INCLUDE
;
418 } else if (0 == strcmp(tok
, "exclude-func")) {
419 line
->type
= TRACE_LINE_EXCLUDE
;
421 error("Unknown command in trace config data line %d\n",
426 tok
= strtok_r(NULL
, " \t", &saveptr
);
428 error("Missing pattern in trace config data line %d\n",
433 err
= regcomp(&line
->regex
, tok
, REG_NOSUB
);
435 int r
= regex_report_error(&line
->regex
, err
,
441 /* link this new one to the end of the list */
442 line
->name
= strdup(tok
);
449 error("Cannot read from trace config file at position %ld\n",
456 static int read_trace_config_file(const char *fname
)
461 fin
= fopen(fname
, "r");
463 error("Cannot open trace_config file '%s'\n", fname
);
466 err
= read_trace_config(fin
);
471 static void out_func(ulong func_offset
, int is_caller
, const char *suffix
)
473 struct func_info
*func
;
475 func
= (is_caller
? find_caller_by_offset
: find_func_by_offset
)
479 printf("%s%s", func
->name
, suffix
);
481 printf("%lx%s", func_offset
, suffix
);
487 * # TASK-PID CPU# TIMESTAMP FUNCTION
489 * # bash-4251 [01] 10152.583854: path_put <-path_walk
490 * # bash-4251 [01] 10152.583855: dput <-path_put
491 * # bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput
493 static int make_ftrace(void)
495 struct trace_call
*call
;
496 int missing_count
= 0, skip_count
= 0;
499 printf("# tracer: ftrace\n"
501 "# TASK-PID CPU# TIMESTAMP FUNCTION\n"
503 for (i
= 0, call
= call_list
; i
< call_count
; i
++, call
++) {
504 struct func_info
*func
= find_func_by_offset(call
->func
);
505 ulong time
= call
->flags
& FUNCF_TIMESTAMP_MASK
;
507 if (TRACE_CALL_TYPE(call
) != FUNCF_ENTRY
&&
508 TRACE_CALL_TYPE(call
) != FUNCF_EXIT
)
511 warn("Cannot find function at %lx\n",
512 text_offset
+ call
->func
);
517 if (!(func
->flags
& FUNCF_TRACE
)) {
518 debug("Funcion '%s' is excluded from trace\n",
524 printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1,
525 time
/ 1000000, time
% 1000000);
527 out_func(call
->func
, 0, " <- ");
528 out_func(call
->caller
, 1, "\n");
530 info("ftrace: %d functions not found, %d excluded\n", missing_count
,
536 static int prof_tool(int argc
, char * const argv
[],
537 const char *prof_fname
, const char *map_fname
,
538 const char *trace_config_fname
)
542 if (read_map_file(map_fname
))
544 if (prof_fname
&& read_profile_file(prof_fname
))
546 if (trace_config_fname
&& read_trace_config_file(trace_config_fname
))
551 for (; argc
; argc
--, argv
++) {
552 const char *cmd
= *argv
;
554 if (0 == strcmp(cmd
, "dump-ftrace"))
557 warn("Unknown command '%s'\n", cmd
);
563 int main(int argc
, char *argv
[])
565 const char *map_fname
= "System.map";
566 const char *prof_fname
= NULL
;
567 const char *trace_config_fname
= NULL
;
571 while ((opt
= getopt(argc
, argv
, "m:p:t:v:")) != -1) {
582 trace_config_fname
= optarg
;
586 verbose
= atoi(optarg
);
593 argc
-= optind
; argv
+= optind
;
597 debug("Debug enabled\n");
598 return prof_tool(argc
, argv
, prof_fname
, map_fname
,