efi: Add 64-bit payload support
[platform/kernel/u-boot.git] / lib / trace.c
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