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