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