1b2e16a85SSimon Glass /* 2b2e16a85SSimon Glass * Copyright (c) 2012 The Chromium OS Authors. 3b2e16a85SSimon Glass * 4*1a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 5b2e16a85SSimon Glass */ 6b2e16a85SSimon Glass 7b2e16a85SSimon Glass #include <common.h> 8b2e16a85SSimon Glass #include <trace.h> 9b2e16a85SSimon Glass #include <asm/io.h> 10b2e16a85SSimon Glass #include <asm/sections.h> 11b2e16a85SSimon Glass 12b2e16a85SSimon Glass DECLARE_GLOBAL_DATA_PTR; 13b2e16a85SSimon Glass 14b2e16a85SSimon Glass static char trace_enabled __attribute__((section(".data"))); 15b2e16a85SSimon Glass static char trace_inited __attribute__((section(".data"))); 16b2e16a85SSimon Glass 17b2e16a85SSimon Glass /* The header block at the start of the trace memory area */ 18b2e16a85SSimon Glass struct trace_hdr { 19b2e16a85SSimon Glass int func_count; /* Total number of function call sites */ 20b2e16a85SSimon Glass u64 call_count; /* Total number of tracked function calls */ 21b2e16a85SSimon Glass u64 untracked_count; /* Total number of untracked function calls */ 22b2e16a85SSimon Glass int funcs_used; /* Total number of functions used */ 23b2e16a85SSimon Glass 24b2e16a85SSimon Glass /* 25b2e16a85SSimon Glass * Call count for each function. This is indexed by the word offset 26b2e16a85SSimon Glass * of the function from gd->relocaddr 27b2e16a85SSimon Glass */ 28b2e16a85SSimon Glass uintptr_t *call_accum; 29b2e16a85SSimon Glass 30b2e16a85SSimon Glass /* Function trace list */ 31b2e16a85SSimon Glass struct trace_call *ftrace; /* The function call records */ 32b2e16a85SSimon Glass ulong ftrace_size; /* Num. of ftrace records we have space for */ 33b2e16a85SSimon Glass ulong ftrace_count; /* Num. of ftrace records written */ 34b2e16a85SSimon Glass ulong ftrace_too_deep_count; /* Functions that were too deep */ 35b2e16a85SSimon Glass 36b2e16a85SSimon Glass int depth; 37b2e16a85SSimon Glass int depth_limit; 38b2e16a85SSimon Glass int max_depth; 39b2e16a85SSimon Glass }; 40b2e16a85SSimon Glass 41b2e16a85SSimon Glass static struct trace_hdr *hdr; /* Pointer to start of trace buffer */ 42b2e16a85SSimon Glass 43b2e16a85SSimon Glass static inline uintptr_t __attribute__((no_instrument_function)) 44b2e16a85SSimon Glass func_ptr_to_num(void *func_ptr) 45b2e16a85SSimon Glass { 46b2e16a85SSimon Glass uintptr_t offset = (uintptr_t)func_ptr; 47b2e16a85SSimon Glass 48b2e16a85SSimon Glass #ifdef CONFIG_SANDBOX 49b2e16a85SSimon Glass offset -= (uintptr_t)&_init; 50b2e16a85SSimon Glass #else 51b2e16a85SSimon Glass if (gd->flags & GD_FLG_RELOC) 52b2e16a85SSimon Glass offset -= gd->relocaddr; 53b2e16a85SSimon Glass else 54b2e16a85SSimon Glass offset -= CONFIG_SYS_TEXT_BASE; 55b2e16a85SSimon Glass #endif 56b2e16a85SSimon Glass return offset / FUNC_SITE_SIZE; 57b2e16a85SSimon Glass } 58b2e16a85SSimon Glass 59b2e16a85SSimon Glass static void __attribute__((no_instrument_function)) add_ftrace(void *func_ptr, 60b2e16a85SSimon Glass void *caller, ulong flags) 61b2e16a85SSimon Glass { 62b2e16a85SSimon Glass if (hdr->depth > hdr->depth_limit) { 63b2e16a85SSimon Glass hdr->ftrace_too_deep_count++; 64b2e16a85SSimon Glass return; 65b2e16a85SSimon Glass } 66b2e16a85SSimon Glass if (hdr->ftrace_count < hdr->ftrace_size) { 67b2e16a85SSimon Glass struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; 68b2e16a85SSimon Glass 69b2e16a85SSimon Glass rec->func = func_ptr_to_num(func_ptr); 70b2e16a85SSimon Glass rec->caller = func_ptr_to_num(caller); 71b2e16a85SSimon Glass rec->flags = flags | (timer_get_us() & FUNCF_TIMESTAMP_MASK); 72b2e16a85SSimon Glass } 73b2e16a85SSimon Glass hdr->ftrace_count++; 74b2e16a85SSimon Glass } 75b2e16a85SSimon Glass 76b2e16a85SSimon Glass static void __attribute__((no_instrument_function)) add_textbase(void) 77b2e16a85SSimon Glass { 78b2e16a85SSimon Glass if (hdr->ftrace_count < hdr->ftrace_size) { 79b2e16a85SSimon Glass struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; 80b2e16a85SSimon Glass 81b2e16a85SSimon Glass rec->func = CONFIG_SYS_TEXT_BASE; 82b2e16a85SSimon Glass rec->caller = 0; 83b2e16a85SSimon Glass rec->flags = FUNCF_TEXTBASE; 84b2e16a85SSimon Glass } 85b2e16a85SSimon Glass hdr->ftrace_count++; 86b2e16a85SSimon Glass } 87b2e16a85SSimon Glass 88b2e16a85SSimon Glass /** 89b2e16a85SSimon Glass * This is called on every function entry 90b2e16a85SSimon Glass * 91b2e16a85SSimon Glass * We add to our tally for this function and add to the list of called 92b2e16a85SSimon Glass * functions. 93b2e16a85SSimon Glass * 94b2e16a85SSimon Glass * @param func_ptr Pointer to function being entered 95b2e16a85SSimon Glass * @param caller Pointer to function which called this function 96b2e16a85SSimon Glass */ 97b2e16a85SSimon Glass void __attribute__((no_instrument_function)) __cyg_profile_func_enter( 98b2e16a85SSimon Glass void *func_ptr, void *caller) 99b2e16a85SSimon Glass { 100b2e16a85SSimon Glass if (trace_enabled) { 101b2e16a85SSimon Glass int func; 102b2e16a85SSimon Glass 103b2e16a85SSimon Glass add_ftrace(func_ptr, caller, FUNCF_ENTRY); 104b2e16a85SSimon Glass func = func_ptr_to_num(func_ptr); 105b2e16a85SSimon Glass if (func < hdr->func_count) { 106b2e16a85SSimon Glass hdr->call_accum[func]++; 107b2e16a85SSimon Glass hdr->call_count++; 108b2e16a85SSimon Glass } else { 109b2e16a85SSimon Glass hdr->untracked_count++; 110b2e16a85SSimon Glass } 111b2e16a85SSimon Glass hdr->depth++; 112b2e16a85SSimon Glass if (hdr->depth > hdr->depth_limit) 113b2e16a85SSimon Glass hdr->max_depth = hdr->depth; 114b2e16a85SSimon Glass } 115b2e16a85SSimon Glass } 116b2e16a85SSimon Glass 117b2e16a85SSimon Glass /** 118b2e16a85SSimon Glass * This is called on every function exit 119b2e16a85SSimon Glass * 120b2e16a85SSimon Glass * We do nothing here. 121b2e16a85SSimon Glass * 122b2e16a85SSimon Glass * @param func_ptr Pointer to function being entered 123b2e16a85SSimon Glass * @param caller Pointer to function which called this function 124b2e16a85SSimon Glass */ 125b2e16a85SSimon Glass void __attribute__((no_instrument_function)) __cyg_profile_func_exit( 126b2e16a85SSimon Glass void *func_ptr, void *caller) 127b2e16a85SSimon Glass { 128b2e16a85SSimon Glass if (trace_enabled) { 129b2e16a85SSimon Glass add_ftrace(func_ptr, caller, FUNCF_EXIT); 130b2e16a85SSimon Glass hdr->depth--; 131b2e16a85SSimon Glass } 132b2e16a85SSimon Glass } 133b2e16a85SSimon Glass 134b2e16a85SSimon Glass /** 135b2e16a85SSimon Glass * Produce a list of called functions 136b2e16a85SSimon Glass * 137b2e16a85SSimon Glass * The information is written into the supplied buffer - a header followed 138b2e16a85SSimon Glass * by a list of function records. 139b2e16a85SSimon Glass * 140b2e16a85SSimon Glass * @param buff Buffer to place list into 141b2e16a85SSimon Glass * @param buff_size Size of buffer 142b2e16a85SSimon Glass * @param needed Returns size of buffer needed, which may be 143b2e16a85SSimon Glass * greater than buff_size if we ran out of space. 144b2e16a85SSimon Glass * @return 0 if ok, -1 if space was exhausted 145b2e16a85SSimon Glass */ 146b2e16a85SSimon Glass int trace_list_functions(void *buff, int buff_size, unsigned int *needed) 147b2e16a85SSimon Glass { 148b2e16a85SSimon Glass struct trace_output_hdr *output_hdr = NULL; 149b2e16a85SSimon Glass void *end, *ptr = buff; 150b2e16a85SSimon Glass int func; 151b2e16a85SSimon Glass int upto; 152b2e16a85SSimon Glass 153b2e16a85SSimon Glass end = buff ? buff + buff_size : NULL; 154b2e16a85SSimon Glass 155b2e16a85SSimon Glass /* Place some header information */ 156b2e16a85SSimon Glass if (ptr + sizeof(struct trace_output_hdr) < end) 157b2e16a85SSimon Glass output_hdr = ptr; 158b2e16a85SSimon Glass ptr += sizeof(struct trace_output_hdr); 159b2e16a85SSimon Glass 160b2e16a85SSimon Glass /* Add information about each function */ 161b2e16a85SSimon Glass for (func = upto = 0; func < hdr->func_count; func++) { 162b2e16a85SSimon Glass int calls = hdr->call_accum[func]; 163b2e16a85SSimon Glass 164b2e16a85SSimon Glass if (!calls) 165b2e16a85SSimon Glass continue; 166b2e16a85SSimon Glass 167b2e16a85SSimon Glass if (ptr + sizeof(struct trace_output_func) < end) { 168b2e16a85SSimon Glass struct trace_output_func *stats = ptr; 169b2e16a85SSimon Glass 170b2e16a85SSimon Glass stats->offset = func * FUNC_SITE_SIZE; 171b2e16a85SSimon Glass stats->call_count = calls; 172b2e16a85SSimon Glass upto++; 173b2e16a85SSimon Glass } 174b2e16a85SSimon Glass ptr += sizeof(struct trace_output_func); 175b2e16a85SSimon Glass } 176b2e16a85SSimon Glass 177b2e16a85SSimon Glass /* Update the header */ 178b2e16a85SSimon Glass if (output_hdr) { 179b2e16a85SSimon Glass output_hdr->rec_count = upto; 180b2e16a85SSimon Glass output_hdr->type = TRACE_CHUNK_FUNCS; 181b2e16a85SSimon Glass } 182b2e16a85SSimon Glass 183b2e16a85SSimon Glass /* Work out how must of the buffer we used */ 184b2e16a85SSimon Glass *needed = ptr - buff; 185b2e16a85SSimon Glass if (ptr > end) 186b2e16a85SSimon Glass return -1; 187b2e16a85SSimon Glass return 0; 188b2e16a85SSimon Glass } 189b2e16a85SSimon Glass 190b2e16a85SSimon Glass int trace_list_calls(void *buff, int buff_size, unsigned *needed) 191b2e16a85SSimon Glass { 192b2e16a85SSimon Glass struct trace_output_hdr *output_hdr = NULL; 193b2e16a85SSimon Glass void *end, *ptr = buff; 194b2e16a85SSimon Glass int rec, upto; 195b2e16a85SSimon Glass int count; 196b2e16a85SSimon Glass 197b2e16a85SSimon Glass end = buff ? buff + buff_size : NULL; 198b2e16a85SSimon Glass 199b2e16a85SSimon Glass /* Place some header information */ 200b2e16a85SSimon Glass if (ptr + sizeof(struct trace_output_hdr) < end) 201b2e16a85SSimon Glass output_hdr = ptr; 202b2e16a85SSimon Glass ptr += sizeof(struct trace_output_hdr); 203b2e16a85SSimon Glass 204b2e16a85SSimon Glass /* Add information about each call */ 205b2e16a85SSimon Glass count = hdr->ftrace_count; 206b2e16a85SSimon Glass if (count > hdr->ftrace_size) 207b2e16a85SSimon Glass count = hdr->ftrace_size; 208b2e16a85SSimon Glass for (rec = upto = 0; rec < count; rec++) { 209b2e16a85SSimon Glass if (ptr + sizeof(struct trace_call) < end) { 210b2e16a85SSimon Glass struct trace_call *call = &hdr->ftrace[rec]; 211b2e16a85SSimon Glass struct trace_call *out = ptr; 212b2e16a85SSimon Glass 213b2e16a85SSimon Glass out->func = call->func * FUNC_SITE_SIZE; 214b2e16a85SSimon Glass out->caller = call->caller * FUNC_SITE_SIZE; 215b2e16a85SSimon Glass out->flags = call->flags; 216b2e16a85SSimon Glass upto++; 217b2e16a85SSimon Glass } 218b2e16a85SSimon Glass ptr += sizeof(struct trace_call); 219b2e16a85SSimon Glass } 220b2e16a85SSimon Glass 221b2e16a85SSimon Glass /* Update the header */ 222b2e16a85SSimon Glass if (output_hdr) { 223b2e16a85SSimon Glass output_hdr->rec_count = upto; 224b2e16a85SSimon Glass output_hdr->type = TRACE_CHUNK_CALLS; 225b2e16a85SSimon Glass } 226b2e16a85SSimon Glass 227b2e16a85SSimon Glass /* Work out how must of the buffer we used */ 228b2e16a85SSimon Glass *needed = ptr - buff; 229b2e16a85SSimon Glass if (ptr > end) 230b2e16a85SSimon Glass return -1; 231b2e16a85SSimon Glass return 0; 232b2e16a85SSimon Glass } 233b2e16a85SSimon Glass 234b2e16a85SSimon Glass /* Print basic information about tracing */ 235b2e16a85SSimon Glass void trace_print_stats(void) 236b2e16a85SSimon Glass { 237b2e16a85SSimon Glass ulong count; 238b2e16a85SSimon Glass 239b2e16a85SSimon Glass #ifndef FTRACE 240b2e16a85SSimon Glass puts("Warning: make U-Boot with FTRACE to enable function instrumenting.\n"); 241b2e16a85SSimon Glass puts("You will likely get zeroed data here\n"); 242b2e16a85SSimon Glass #endif 243b2e16a85SSimon Glass if (!trace_inited) { 244b2e16a85SSimon Glass printf("Trace is disabled\n"); 245b2e16a85SSimon Glass return; 246b2e16a85SSimon Glass } 247b2e16a85SSimon Glass print_grouped_ull(hdr->func_count, 10); 248b2e16a85SSimon Glass puts(" function sites\n"); 249b2e16a85SSimon Glass print_grouped_ull(hdr->call_count, 10); 250b2e16a85SSimon Glass puts(" function calls\n"); 251b2e16a85SSimon Glass print_grouped_ull(hdr->untracked_count, 10); 252b2e16a85SSimon Glass puts(" untracked function calls\n"); 253b2e16a85SSimon Glass count = min(hdr->ftrace_count, hdr->ftrace_size); 254b2e16a85SSimon Glass print_grouped_ull(count, 10); 255b2e16a85SSimon Glass puts(" traced function calls"); 256b2e16a85SSimon Glass if (hdr->ftrace_count > hdr->ftrace_size) { 257b2e16a85SSimon Glass printf(" (%lu dropped due to overflow)", 258b2e16a85SSimon Glass hdr->ftrace_count - hdr->ftrace_size); 259b2e16a85SSimon Glass } 260b2e16a85SSimon Glass puts("\n"); 261b2e16a85SSimon Glass printf("%15d maximum observed call depth\n", hdr->max_depth); 262b2e16a85SSimon Glass printf("%15d call depth limit\n", hdr->depth_limit); 263b2e16a85SSimon Glass print_grouped_ull(hdr->ftrace_too_deep_count, 10); 264b2e16a85SSimon Glass puts(" calls not traced due to depth\n"); 265b2e16a85SSimon Glass } 266b2e16a85SSimon Glass 267b2e16a85SSimon Glass void __attribute__((no_instrument_function)) trace_set_enabled(int enabled) 268b2e16a85SSimon Glass { 269b2e16a85SSimon Glass trace_enabled = enabled != 0; 270b2e16a85SSimon Glass } 271b2e16a85SSimon Glass 272b2e16a85SSimon Glass /** 273b2e16a85SSimon Glass * Init the tracing system ready for used, and enable it 274b2e16a85SSimon Glass * 275b2e16a85SSimon Glass * @param buff Pointer to trace buffer 276b2e16a85SSimon Glass * @param buff_size Size of trace buffer 277b2e16a85SSimon Glass */ 278b2e16a85SSimon Glass int __attribute__((no_instrument_function)) trace_init(void *buff, 279b2e16a85SSimon Glass size_t buff_size) 280b2e16a85SSimon Glass { 281b2e16a85SSimon Glass ulong func_count = gd->mon_len / FUNC_SITE_SIZE; 282b2e16a85SSimon Glass size_t needed; 283b2e16a85SSimon Glass int was_disabled = !trace_enabled; 284b2e16a85SSimon Glass 285b2e16a85SSimon Glass if (!was_disabled) { 286b2e16a85SSimon Glass #ifdef CONFIG_TRACE_EARLY 287b2e16a85SSimon Glass char *end; 288b2e16a85SSimon Glass ulong used; 289b2e16a85SSimon Glass 290b2e16a85SSimon Glass /* 291b2e16a85SSimon Glass * Copy over the early trace data if we have it. Disable 292b2e16a85SSimon Glass * tracing while we are doing this. 293b2e16a85SSimon Glass */ 294b2e16a85SSimon Glass trace_enabled = 0; 295b2e16a85SSimon Glass hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, 296b2e16a85SSimon Glass CONFIG_TRACE_EARLY_SIZE); 297b2e16a85SSimon Glass end = (char *)&hdr->ftrace[hdr->ftrace_count]; 298b2e16a85SSimon Glass used = end - (char *)hdr; 299b2e16a85SSimon Glass printf("trace: copying %08lx bytes of early data from %x to %08lx\n", 300b2e16a85SSimon Glass used, CONFIG_TRACE_EARLY_ADDR, 301b2e16a85SSimon Glass (ulong)map_to_sysmem(buff)); 302b2e16a85SSimon Glass memcpy(buff, hdr, used); 303b2e16a85SSimon Glass #else 304b2e16a85SSimon Glass puts("trace: already enabled\n"); 305b2e16a85SSimon Glass return -1; 306b2e16a85SSimon Glass #endif 307b2e16a85SSimon Glass } 308b2e16a85SSimon Glass hdr = (struct trace_hdr *)buff; 309b2e16a85SSimon Glass needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); 310b2e16a85SSimon Glass if (needed > buff_size) { 311b2e16a85SSimon Glass printf("trace: buffer size %zd bytes: at least %zd needed\n", 312b2e16a85SSimon Glass buff_size, needed); 313b2e16a85SSimon Glass return -1; 314b2e16a85SSimon Glass } 315b2e16a85SSimon Glass 316b2e16a85SSimon Glass if (was_disabled) 317b2e16a85SSimon Glass memset(hdr, '\0', needed); 318b2e16a85SSimon Glass hdr->func_count = func_count; 319b2e16a85SSimon Glass hdr->call_accum = (uintptr_t *)(hdr + 1); 320b2e16a85SSimon Glass 321b2e16a85SSimon Glass /* Use any remaining space for the timed function trace */ 322b2e16a85SSimon Glass hdr->ftrace = (struct trace_call *)(buff + needed); 323b2e16a85SSimon Glass hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); 324b2e16a85SSimon Glass add_textbase(); 325b2e16a85SSimon Glass 326b2e16a85SSimon Glass puts("trace: enabled\n"); 327b2e16a85SSimon Glass hdr->depth_limit = 15; 328b2e16a85SSimon Glass trace_enabled = 1; 329b2e16a85SSimon Glass trace_inited = 1; 330b2e16a85SSimon Glass return 0; 331b2e16a85SSimon Glass } 332b2e16a85SSimon Glass 333b2e16a85SSimon Glass #ifdef CONFIG_TRACE_EARLY 334b2e16a85SSimon Glass int __attribute__((no_instrument_function)) trace_early_init(void) 335b2e16a85SSimon Glass { 336b2e16a85SSimon Glass ulong func_count = gd->mon_len / FUNC_SITE_SIZE; 337b2e16a85SSimon Glass size_t buff_size = CONFIG_TRACE_EARLY_SIZE; 338b2e16a85SSimon Glass size_t needed; 339b2e16a85SSimon Glass 340b2e16a85SSimon Glass /* We can ignore additional calls to this function */ 341b2e16a85SSimon Glass if (trace_enabled) 342b2e16a85SSimon Glass return 0; 343b2e16a85SSimon Glass 344b2e16a85SSimon Glass hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE); 345b2e16a85SSimon Glass needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); 346b2e16a85SSimon Glass if (needed > buff_size) { 347b2e16a85SSimon Glass printf("trace: buffer size is %zd bytes, at least %zd needed\n", 348b2e16a85SSimon Glass buff_size, needed); 349b2e16a85SSimon Glass return -1; 350b2e16a85SSimon Glass } 351b2e16a85SSimon Glass 352b2e16a85SSimon Glass memset(hdr, '\0', needed); 353b2e16a85SSimon Glass hdr->call_accum = (uintptr_t *)(hdr + 1); 354b2e16a85SSimon Glass hdr->func_count = func_count; 355b2e16a85SSimon Glass 356b2e16a85SSimon Glass /* Use any remaining space for the timed function trace */ 357b2e16a85SSimon Glass hdr->ftrace = (struct trace_call *)((char *)hdr + needed); 358b2e16a85SSimon Glass hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); 359b2e16a85SSimon Glass add_textbase(); 360b2e16a85SSimon Glass hdr->depth_limit = 200; 361b2e16a85SSimon Glass printf("trace: early enable at %08x\n", CONFIG_TRACE_EARLY_ADDR); 362b2e16a85SSimon Glass 363b2e16a85SSimon Glass trace_enabled = 1; 364b2e16a85SSimon Glass return 0; 365b2e16a85SSimon Glass } 366b2e16a85SSimon Glass #endif 367