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