From 64d445a3b7018dac5131cba9f5d308c64a45e95f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:43 -0700 Subject: time: Tidy up some unnecessary #ifdefs Avoid using the preprocessor with TIMER_EARLY. Signed-off-by: Simon Glass --- lib/time.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/time.c b/lib/time.c index 5252190af53..00f4a1ac8fb 100644 --- a/lib/time.c +++ b/lib/time.c @@ -70,15 +70,14 @@ extern unsigned long timer_read_counter(void); ulong notrace get_tbclk(void) { if (!gd->timer) { -#ifdef CONFIG_TIMER_EARLY - return timer_early_get_rate(); -#else int ret; + if (IS_ENABLED(CONFIG_TIMER_EARLY)) + return timer_early_get_rate(); + ret = dm_timer_init(); if (ret) return ret; -#endif } return timer_get_rate(gd->timer); @@ -90,15 +89,14 @@ uint64_t notrace get_ticks(void) int ret; if (!gd->timer) { -#ifdef CONFIG_TIMER_EARLY - return timer_early_get_count(); -#else int ret; + if (IS_ENABLED(CONFIG_TIMER_EARLY)) + return timer_early_get_count(); + ret = dm_timer_init(); if (ret) panic("Could not initialize timer (err %d)\n", ret); -#endif } ret = timer_get_count(gd->timer, &count); -- cgit v1.3.1 From c33425c6f960944ba88104f281217de5a89a5562 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:44 -0700 Subject: trace: Reduce the default for TRACE_EARLY_CALL_DEPTH_LIMIT This is a silly value at present, since U-Boot's call depth never reaches 200. Fix it. Signed-off-by: Simon Glass --- lib/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/Kconfig b/lib/Kconfig index 06c873c2bd2..83e5edd73b0 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -368,7 +368,7 @@ config TRACE_EARLY_SIZE config TRACE_EARLY_CALL_DEPTH_LIMIT int "Early trace call depth limit" depends on TRACE_EARLY - default 200 + default 15 help Sets the maximum call depth up to which function calls are recorded during early tracing. -- cgit v1.3.1 From 90ad4e28e8d299395922364409c37bd7afc293e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:45 -0700 Subject: abuf: Support use from tools Update the code slightly so that abuf can be used in U-Boot tools. It will soon be needed for proftool. Signed-off-by: Simon Glass --- lib/abuf.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/abuf.c b/lib/abuf.c index 1635d58682c..bd270467dd4 100644 --- a/lib/abuf.c +++ b/lib/abuf.c @@ -6,11 +6,14 @@ * Written by Simon Glass */ +#ifndef USE_HOSTCC #include -#include #include #include #include +#endif + +#include void abuf_set(struct abuf *abuf, void *data, size_t size) { @@ -19,10 +22,26 @@ void abuf_set(struct abuf *abuf, void *data, size_t size) abuf->size = size; } +#ifndef USE_HOSTCC void abuf_map_sysmem(struct abuf *abuf, ulong addr, size_t size) { abuf_set(abuf, map_sysmem(addr, size), size); } +#else +/* copied from lib/string.c for convenience */ +static char *memdup(const void *src, size_t len) +{ + char *p; + + p = malloc(len); + if (!p) + return NULL; + + memcpy(p, src, len); + + return p; +} +#endif bool abuf_realloc(struct abuf *abuf, size_t new_size) { -- cgit v1.3.1 From d9044e5363bb0b32a7aff00adf47c227f78f0f32 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:46 -0700 Subject: trace: Update the file header It seems better to put the TEXT_BASE value in the file header rather than in an entry record. While it is true that there is a separate base for pre-relocation, this can be handled by using offsets in the file. It is useful to have a version number in case we need to change the trace format again. Update the header to make these changes. Signed-off-by: Simon Glass --- include/trace.h | 12 ++++++++++-- lib/trace.c | 19 ++++--------------- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/include/trace.h b/include/trace.h index e7aee024f03..2be8d81b515 100644 --- a/include/trace.h +++ b/include/trace.h @@ -6,6 +6,8 @@ #ifndef __TRACE_H #define __TRACE_H +/* this file is included from a tool so uses uint32_t instead of u32, etc. */ + enum { /* * This affects the granularity of our trace. We can bin function @@ -23,6 +25,8 @@ enum { * this value. */ FUNC_SITE_SIZE = 4, /* distance between function sites */ + + TRACE_VERSION = 1, }; enum trace_chunk_type { @@ -39,7 +43,11 @@ struct trace_output_func { /* A header at the start of the trace output buffer */ struct trace_output_hdr { enum trace_chunk_type type; /* Record type */ - size_t rec_count; /* Number of records */ + uint32_t version; /* Version (TRACE_VERSION) */ + uint32_t rec_count; /* Number of records */ + uint32_t spare; /* 0 */ + uint64_t text_base; /* Value of CONFIG_TEXT_BASE */ + uint64_t spare2; /* 0 */ }; /* Print statistics about traced function calls */ @@ -63,7 +71,7 @@ int trace_list_functions(void *buff, size_t buff_size, size_t *needed); enum ftrace_flags { FUNCF_EXIT = 0UL << 30, FUNCF_ENTRY = 1UL << 30, - FUNCF_TEXTBASE = 2UL << 30, + /* two more values are available */ FUNCF_TIMESTAMP_MASK = 0x3fffffff, }; diff --git a/lib/trace.c b/lib/trace.c index b9dc6d2e4b5..2e2c1bed54f 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -118,18 +118,6 @@ static void notrace add_ftrace(void *func_ptr, void *caller, ulong flags) hdr->ftrace_count++; } -static void notrace add_textbase(void) -{ - if (hdr->ftrace_count < hdr->ftrace_size) { - struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; - - rec->func = CONFIG_TEXT_BASE; - rec->caller = 0; - rec->flags = FUNCF_TEXTBASE; - } - hdr->ftrace_count++; -} - /** * __cyg_profile_func_enter() - record function entry * @@ -278,8 +266,11 @@ int trace_list_calls(void *buff, size_t buff_size, size_t *needed) /* Update the header */ if (output_hdr) { + memset(output_hdr, '\0', sizeof(*output_hdr)); output_hdr->rec_count = upto; output_hdr->type = TRACE_CHUNK_CALLS; + output_hdr->version = TRACE_VERSION; + output_hdr->text_base = CONFIG_TEXT_BASE; } /* Work out how must of the buffer we used */ @@ -385,10 +376,9 @@ int notrace trace_init(void *buff, size_t buff_size) /* Use any remaining space for the timed function trace */ hdr->ftrace = (struct trace_call *)(buff + needed); hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); - add_textbase(); + hdr->depth_limit = CONFIG_TRACE_CALL_DEPTH_LIMIT; puts("trace: enabled\n"); - hdr->depth_limit = CONFIG_TRACE_CALL_DEPTH_LIMIT; trace_enabled = 1; trace_inited = 1; @@ -426,7 +416,6 @@ int notrace trace_early_init(void) /* Use any remaining space for the timed function trace */ hdr->ftrace = (struct trace_call *)((char *)hdr + needed); hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); - add_textbase(); hdr->depth_limit = CONFIG_TRACE_EARLY_CALL_DEPTH_LIMIT; printf("trace: early enable at %08x\n", CONFIG_TRACE_EARLY_ADDR); -- cgit v1.3.1 From c3d91812a2b69fbc73eccdcec70d7ea510d7c8bd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:47 -0700 Subject: trace: Reduce the number of function sites Given that the compiler adds two function calls into each function, the current spacing is overkill. Drop it down to 16 bytes per function, which is still plenty. This saves some space in the trace buffer. Also move the calculation into a function, so it is common code. Add a check for gd->mon_len being unset, which breaks tracing. Signed-off-by: Simon Glass --- include/trace.h | 6 ++++-- lib/trace.c | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/include/trace.h b/include/trace.h index 2be8d81b515..763d6d1255a 100644 --- a/include/trace.h +++ b/include/trace.h @@ -17,14 +17,16 @@ enum { * * The value here assumes a minimum instruction size of 4 bytes, * or that instructions are 2 bytes but there are at least 2 of - * them in every function. + * them in every function. Given that each function needs a call to + * __cyg_profile_func_enter() and __cyg_profile_func_exit() as well, + * we cannot have functions smaller that 16 bytes. * * Increasing this value reduces the number of functions we can * resolve, but reduces the size of the uintptr_t array used for * our function list, which is the length of the code divided by * this value. */ - FUNC_SITE_SIZE = 4, /* distance between function sites */ + FUNC_SITE_SIZE = 16, /* distance between function sites */ TRACE_VERSION = 1, }; diff --git a/lib/trace.c b/lib/trace.c index 2e2c1bed54f..12dae2089a4 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -321,6 +321,17 @@ void notrace trace_set_enabled(int enabled) trace_enabled = enabled != 0; } +static int get_func_count(void) +{ + /* Detect no support for mon_len since this means tracing cannot work */ + if (IS_ENABLED(CONFIG_SANDBOX) && !gd->mon_len) { + puts("Tracing is not supported on this board\n"); + return -ENOTSUPP; + } + + return gd->mon_len / FUNC_SITE_SIZE; +} + /** * trace_init() - initialize the tracing system and enable it * @@ -330,10 +341,12 @@ void notrace trace_set_enabled(int enabled) */ int notrace trace_init(void *buff, size_t buff_size) { - ulong func_count = gd->mon_len / FUNC_SITE_SIZE; + int func_count = get_func_count(); size_t needed; int was_disabled = !trace_enabled; + if (func_count < 0) + return func_count; trace_save_gd(); if (!was_disabled) { @@ -393,10 +406,12 @@ int notrace trace_init(void *buff, size_t buff_size) */ int notrace trace_early_init(void) { - ulong func_count = gd->mon_len / FUNC_SITE_SIZE; + int func_count = get_func_count(); size_t buff_size = CONFIG_TRACE_EARLY_SIZE; size_t needed; + if (func_count < 0) + return func_count; /* We can ignore additional calls to this function */ if (trace_enabled) return 0; -- cgit v1.3.1 From daca66d55f39551c3499de87397f08fbd3795a9b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:48 -0700 Subject: trace: Track the minimum stack depth The trace does not necessarily start at the top level, so we can see it go negative. Track this so that we can show an accurate value for the stack depth. Signed-off-by: Simon Glass --- lib/trace.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/trace.c b/lib/trace.c index 12dae2089a4..3551ef3a23c 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -35,9 +35,10 @@ struct trace_hdr { ulong ftrace_count; /* Num. of ftrace records written */ ulong ftrace_too_deep_count; /* Functions that were too deep */ - int depth; - int depth_limit; - int max_depth; + int depth; /* Depth of function calls */ + int depth_limit; /* Depth limit to trace to */ + int max_depth; /* Maximum depth seen so far */ + int min_depth; /* Minimum depth seen so far */ }; /* Pointer to start of trace buffer */ @@ -142,7 +143,7 @@ void notrace __cyg_profile_func_enter(void *func_ptr, void *caller) hdr->untracked_count++; } hdr->depth++; - if (hdr->depth > hdr->depth_limit) + if (hdr->depth > hdr->max_depth) hdr->max_depth = hdr->depth; trace_swap_gd(); } @@ -158,8 +159,10 @@ void notrace __cyg_profile_func_exit(void *func_ptr, void *caller) { if (trace_enabled) { trace_swap_gd(); - add_ftrace(func_ptr, caller, FUNCF_EXIT); hdr->depth--; + add_ftrace(func_ptr, caller, FUNCF_EXIT); + if (hdr->depth < hdr->min_depth) + hdr->min_depth = hdr->depth; trace_swap_gd(); } } @@ -309,8 +312,10 @@ void trace_print_stats(void) printf(" (%lu dropped due to overflow)", hdr->ftrace_count - hdr->ftrace_size); } - puts("\n"); - printf("%15d maximum observed call depth\n", hdr->max_depth); + + /* Add in minimum depth since the trace did not start at top level */ + printf("\n%15d maximum observed call depth\n", + hdr->max_depth - hdr->min_depth); printf("%15d call depth limit\n", hdr->depth_limit); print_grouped_ull(hdr->ftrace_too_deep_count, 10); puts(" calls not traced due to depth\n"); @@ -381,8 +386,10 @@ int notrace trace_init(void *buff, size_t buff_size) return -ENOSPC; } - if (was_disabled) + if (was_disabled) { memset(hdr, '\0', needed); + hdr->min_depth = INT_MAX; + } hdr->func_count = func_count; hdr->call_accum = (uintptr_t *)(hdr + 1); @@ -427,6 +434,7 @@ int notrace trace_early_init(void) memset(hdr, '\0', needed); hdr->call_accum = (uintptr_t *)(hdr + 1); hdr->func_count = func_count; + hdr->min_depth = INT_MAX; /* Use any remaining space for the timed function trace */ hdr->ftrace = (struct trace_call *)((char *)hdr + needed); -- cgit v1.3.1 From 9dd665ad333039a1b4f6c39cec088d7bf7eb787d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:49 -0700 Subject: trace: Show a few more stats about tracing Add a few more useful items into the output. Update the buffers to use hex consistently. Signed-off-by: Simon Glass --- lib/trace.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/trace.c b/lib/trace.c index 3551ef3a23c..c3354a256fb 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -319,6 +319,10 @@ void trace_print_stats(void) printf("%15d call depth limit\n", hdr->depth_limit); print_grouped_ull(hdr->ftrace_too_deep_count, 10); puts(" calls not traced due to depth\n"); + print_grouped_ull(hdr->ftrace_size, 10); + puts(" max function calls\n"); + printf("\ntrace buffer %lx call records %lx\n", + (ulong)map_to_sysmem(hdr), (ulong)map_to_sysmem(hdr->ftrace)); } void notrace trace_set_enabled(int enabled) @@ -381,7 +385,7 @@ int notrace trace_init(void *buff, size_t buff_size) hdr = (struct trace_hdr *)buff; needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); if (needed > buff_size) { - printf("trace: buffer size %zd bytes: at least %zd needed\n", + printf("trace: buffer size %zx bytes: at least %zx needed\n", buff_size, needed); return -ENOSPC; } @@ -426,7 +430,7 @@ int notrace trace_early_init(void) hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE); needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); if (needed > buff_size) { - printf("trace: buffer size is %zd bytes, at least %zd needed\n", + printf("trace: buffer size is %zx bytes, at least %zx needed\n", buff_size, needed); return -ENOSPC; } -- cgit v1.3.1 From 80f91558a173fe841810ab6a7a0f70b52344ec76 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:50 -0700 Subject: trace: Correct the relocation handover with buffer overflow When the early trace buffer overflows it leaves a gap in the trace buffer between where the actual data finished and where it would have finished if there were enough buffer space. This results in corrupted output. Adjust the logic to resolve this and add a message when the buffer overflows. Signed-off-by: Simon Glass --- lib/trace.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/trace.c b/lib/trace.c index c3354a256fb..bbc316af295 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -360,8 +360,8 @@ int notrace trace_init(void *buff, size_t buff_size) if (!was_disabled) { #ifdef CONFIG_TRACE_EARLY + ulong used, count; char *end; - ulong used; /* * Copy over the early trace data if we have it. Disable @@ -370,12 +370,19 @@ int notrace trace_init(void *buff, size_t buff_size) trace_enabled = 0; hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE); - end = (char *)&hdr->ftrace[min(hdr->ftrace_count, - hdr->ftrace_size)]; + count = min(hdr->ftrace_count, hdr->ftrace_size); + end = (char *)&hdr->ftrace[count]; used = end - (char *)hdr; printf("trace: copying %08lx bytes of early data from %x to %08lx\n", used, CONFIG_TRACE_EARLY_ADDR, (ulong)map_to_sysmem(buff)); + printf("%lu traced function calls", count); + if (hdr->ftrace_count > hdr->ftrace_size) { + printf(" (%lu dropped due to overflow)", + hdr->ftrace_count - hdr->ftrace_size); + hdr->ftrace_count = hdr->ftrace_size; + } + puts("\n"); memcpy(buff, hdr, used); #else puts("trace: already enabled\n"); -- cgit v1.3.1 From 852d4dbd70baa550cc5e8ef789aa719d30e94242 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sun, 15 Jan 2023 14:15:51 -0700 Subject: trace: Detect an infinite loop If something is wrong with a board's timer function such that it calls functions not marked with notrace, U-Boot will hang. Detect this, print a message and disable the trace. Signed-off-by: Simon Glass --- lib/trace.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'lib') diff --git a/lib/trace.c b/lib/trace.c index bbc316af295..1091a5793a1 100644 --- a/lib/trace.c +++ b/lib/trace.c @@ -39,6 +39,7 @@ struct trace_hdr { int depth_limit; /* Depth limit to trace to */ int max_depth; /* Maximum depth seen so far */ int min_depth; /* Minimum depth seen so far */ + bool trace_locked; /* Used to detect recursive tracing */ }; /* Pointer to start of trace buffer */ @@ -133,6 +134,14 @@ void notrace __cyg_profile_func_enter(void *func_ptr, void *caller) if (trace_enabled) { int func; + if (hdr->trace_locked) { + trace_enabled = 0; + puts("trace: recursion detected, disabling\n"); + hdr->trace_locked = false; + return; + } + + hdr->trace_locked = true; trace_swap_gd(); add_ftrace(func_ptr, caller, FUNCF_ENTRY); func = func_ptr_to_num(func_ptr); @@ -146,6 +155,7 @@ void notrace __cyg_profile_func_enter(void *func_ptr, void *caller) if (hdr->depth > hdr->max_depth) hdr->max_depth = hdr->depth; trace_swap_gd(); + hdr->trace_locked = false; } } -- cgit v1.3.1