Commit b2e16a85a1fa3f871ca73a554a7fd63067d9ad14
Committed by
Tom Rini
1 parent
b8bcaa3ad3
Exists in
master
and in
53 other branches
Add trace library
Add a library which supports tracing of execution using built-in gcc features and a microsecond timer. This can be used to record a list of function which are executed, along with a timestamp for each. Later this information can be sent to the host for processing. Signed-off-by: Simon Glass <sjg@chromium.org>
Showing 5 changed files with 870 additions and 0 deletions Side-by-side Diff
doc/README.trace
1 | +# | |
2 | +# Copyright (c) 2013 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 Foundatio; 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 | +Tracing in U-Boot | |
21 | +================= | |
22 | + | |
23 | +U-Boot supports a simple tracing feature which allows a record of excecution | |
24 | +to be collected and sent to a host machine for analysis. At present the | |
25 | +main use for this is to profile boot time. | |
26 | + | |
27 | + | |
28 | +Overview | |
29 | +-------- | |
30 | + | |
31 | +The trace feature uses GCC's instrument-functions feature to trace all | |
32 | +function entry/exit points. These are then recorded in a memory buffer. | |
33 | +The memory buffer can be saved to the host over a network link using | |
34 | +tftpput or by writing to an attached memory device such as MMC. | |
35 | + | |
36 | +On the host, the file is first converted with a tool called 'proftool', | |
37 | +which extracts useful information from it. The resulting trace output | |
38 | +resembles that emitted by Linux's ftrace feature, so can be visually | |
39 | +displayed by pytimechart. | |
40 | + | |
41 | + | |
42 | +Quick-start using Sandbox | |
43 | +------------------------- | |
44 | + | |
45 | +Sandbox is a build of U-Boot that can run under Linux so it is a convenient | |
46 | +way of trying out tracing before you use it on your actual board. To do | |
47 | +this, follow these steps: | |
48 | + | |
49 | +Add the following to include/configs/sandbox.h (if not already there) | |
50 | + | |
51 | +#define CONFIG_TRACE | |
52 | +#define CONFIG_CMD_TRACE | |
53 | +#define CONFIG_TRACE_BUFFER_SIZE (16 << 20) | |
54 | +#define CONFIG_TRACE_EARLY_SIZE (8 << 20) | |
55 | +#define CONFIG_TRACE_EARLY | |
56 | +#define CONFIG_TRACE_EARLY_ADDR 0x00100000 | |
57 | + | |
58 | +Build sandbox U-Boot with tracing enabled: | |
59 | + | |
60 | +$ make FTRACE=1 O=sandbox sandbox_config | |
61 | +$ make FTRACE=1 O=sandbox | |
62 | + | |
63 | +Run sandbox, wait for a bit of trace information to appear, and then capture | |
64 | +a trace: | |
65 | + | |
66 | +$ ./sandbox/u-boot | |
67 | + | |
68 | + | |
69 | +U-Boot 2013.04-rc2-00100-ga72fcef (Apr 17 2013 - 19:25:24) | |
70 | + | |
71 | +DRAM: 128 MiB | |
72 | +trace: enabled | |
73 | +Using default environment | |
74 | + | |
75 | +In: serial | |
76 | +Out: serial | |
77 | +Err: serial | |
78 | +=>trace stats | |
79 | + 671,406 function sites | |
80 | + 69,712 function calls | |
81 | + 0 untracked function calls | |
82 | + 73,373 traced function calls | |
83 | + 16 maximum observed call depth | |
84 | + 15 call depth limit | |
85 | + 66,491 calls not traced due to depth | |
86 | +=>trace stats | |
87 | + 671,406 function sites | |
88 | + 1,279,450 function calls | |
89 | + 0 untracked function calls | |
90 | + 950,490 traced function calls (333217 dropped due to overflow) | |
91 | + 16 maximum observed call depth | |
92 | + 15 call depth limit | |
93 | + 1,275,767 calls not traced due to depth | |
94 | +=>trace calls 0 e00000 | |
95 | +Call list dumped to 00000000, size 0xae0a40 | |
96 | ||
97 | +baudrate=115200 | |
98 | +profbase=0 | |
99 | +profoffset=ae0a40 | |
100 | +profsize=e00000 | |
101 | +stderr=serial | |
102 | +stdin=serial | |
103 | +stdout=serial | |
104 | + | |
105 | +Environment size: 117/8188 bytes | |
106 | +=>sb save host 0 trace 0 ${profoffset} | |
107 | +11405888 bytes written in 10 ms (1.1 GiB/s) | |
108 | +=>reset | |
109 | + | |
110 | + | |
111 | +Then run proftool to convert the trace information to ftrace format. | |
112 | + | |
113 | +$ ./sandbox/tools/proftool -m sandbox/System.map -p trace dump-ftrace >trace.txt | |
114 | + | |
115 | +Finally run pytimechart to display it: | |
116 | + | |
117 | +$ pytimechart trace.txt | |
118 | + | |
119 | +Using this tool you can zoom and pan across the trace, with the function | |
120 | +calls on the left and little marks representing the start and end of each | |
121 | +function. | |
122 | + | |
123 | + | |
124 | +CONFIG Options | |
125 | +-------------- | |
126 | + | |
127 | +- CONFIG_TRACE | |
128 | + Enables the trace feature in U-Boot. | |
129 | + | |
130 | +- CONFIG_CMD_TRACE | |
131 | + Enables the trace command. | |
132 | + | |
133 | +- CONFIG_TRACE_BUFFER_SIZE | |
134 | + Size of trace buffer to allocate for U-Boot. This buffer is | |
135 | + used after relocation, as a place to put function tracing | |
136 | + information. The address of the buffer is determined by | |
137 | + the relocation code. | |
138 | + | |
139 | +- CONFIG_TRACE_EARLY | |
140 | + Define this to start tracing early, before relocation. | |
141 | + | |
142 | +- CONFIG_TRACE_EARLY_SIZE | |
143 | + Size of 'early' trace buffer. Before U-Boot has relocated | |
144 | + it doesn't have a proper trace buffer. On many boards | |
145 | + you can define an area of memory to use for the trace | |
146 | + buffer until the 'real' trace buffer is available after | |
147 | + relocation. The contents of this buffer are then copied to | |
148 | + the real buffer. | |
149 | + | |
150 | +- CONFIG_TRACE_EARLY_ADDR | |
151 | + Address of early trace buffer | |
152 | + | |
153 | + | |
154 | +Building U-Boot with Tracing Enabled | |
155 | +------------------------------------ | |
156 | + | |
157 | +Pass 'FTRACE=1' to the U-Boot Makefile to actually instrument the code. | |
158 | +This is kept as a separate option so that it is easy to enable/disable | |
159 | +instrumenting from the command line instead of having to change board | |
160 | +config files. | |
161 | + | |
162 | + | |
163 | +Collecting Trace Data | |
164 | +--------------------- | |
165 | + | |
166 | +When you run U-Boot on your board it will collect trace data up to the | |
167 | +limit of the trace buffer size you have specified. Once that is exhausted | |
168 | +no more data will be collected. | |
169 | + | |
170 | +Collecting trace data has an affect on execution time/performance. You | |
171 | +will notice this particularly with trvial functions - the overhead of | |
172 | +recording their execution may even exceed their normal execution time. | |
173 | +In practice this doesn't matter much so long as you are aware of the | |
174 | +effect. Once you have done your optimisations, turn off tracing before | |
175 | +doing end-to-end timing. | |
176 | + | |
177 | +The best time to start tracing is right at the beginning of U-Boot. The | |
178 | +best time to stop tracing is right at the end. In practice it is hard | |
179 | +to achieve these ideals. | |
180 | + | |
181 | +This implementation enables tracing early in board_init_f(). This means | |
182 | +that it captures most of the board init process, missing only the | |
183 | +early architecture-specific init. However, it also misses the entire | |
184 | +SPL stage if there is one. | |
185 | + | |
186 | +U-Boot typically ends with a 'bootm' command which loads and runs an | |
187 | +OS. There is useful trace data in the execution of that bootm | |
188 | +command. Therefore this implementation provides a way to collect trace | |
189 | +data after bootm has finished processing, but just before it jumps to | |
190 | +the OS. In practical terms, U-Boot runs the 'fakegocmd' environment | |
191 | +variable at this point. This variable should have a short script which | |
192 | +collects the trace data and writes it somewhere. | |
193 | + | |
194 | +Trace data collection relies on a microsecond timer, accesed through | |
195 | +timer_get_us(). So the first think you should do is make sure that | |
196 | +this produces sensible results for your board. Suitable sources for | |
197 | +this timer include high resolution timers, PWMs or profile timers if | |
198 | +available. Most modern SOCs have a suitable timer for this. Make sure | |
199 | +that you mark this timer (and anything it calls) with | |
200 | +__attribute__((no_instrument_function)) so that the trace library can | |
201 | +use it without causing an infinite loop. | |
202 | + | |
203 | + | |
204 | +Commands | |
205 | +-------- | |
206 | + | |
207 | +The trace command has variable sub-commands: | |
208 | + | |
209 | +- stats | |
210 | + Display tracing statistics | |
211 | + | |
212 | +- pause | |
213 | + Pause tracing | |
214 | + | |
215 | +- resume | |
216 | + Resume tracing | |
217 | + | |
218 | +- funclist [<addr> <size>] | |
219 | + Dump a list of functions into the buffer | |
220 | + | |
221 | +- calls [<addr> <size>] | |
222 | + Dump function call trace into buffer | |
223 | + | |
224 | +If the address and size are not given, these are obtained from environment | |
225 | +variables (see below). In any case the environment variables are updated | |
226 | +after the command runs. | |
227 | + | |
228 | + | |
229 | +Environment Variables | |
230 | +--------------------- | |
231 | + | |
232 | +The following are used: | |
233 | + | |
234 | +- profbase | |
235 | + Base address of trace output buffer | |
236 | + | |
237 | +- profoffset | |
238 | + Offset of first unwritten byte in trace output buffer | |
239 | + | |
240 | +- profsize | |
241 | + Size of trace output buffer | |
242 | + | |
243 | +All of these are set by the 'trace calls' command. | |
244 | + | |
245 | +These variables keep track of the amount of data written to the trace | |
246 | +output buffer by the 'trace' command. The trace commands which write data | |
247 | +to the output buffer can use these to specify the buffer to write to, and | |
248 | +update profoffset each time. This allows successive commands to append data | |
249 | +to the same buffer, for example: | |
250 | + | |
251 | + trace funclist 10000 e00000 | |
252 | + trace calls | |
253 | + | |
254 | +(the latter command appends more data to the buffer). | |
255 | + | |
256 | + | |
257 | +- fakegocmd | |
258 | + Specifies commands to run just before booting the OS. This | |
259 | + is a useful time to write the trace data to the host for | |
260 | + processing. | |
261 | + | |
262 | + | |
263 | +Writing Out Trace Data | |
264 | +---------------------- | |
265 | + | |
266 | +Once the trace data is in an output buffer in memory there are various ways | |
267 | +to transmit it to the host. Notably you can use tftput to send the data | |
268 | +over a network link: | |
269 | + | |
270 | +fakegocmd=trace pause; usb start; set autoload n; bootp; | |
271 | + trace calls 10000000 1000000; | |
272 | + tftpput ${profbase} ${profoffset} 192.168.1.4:/tftpboot/calls | |
273 | + | |
274 | +This starts up USB (to talk to an attached USB Ethernet dongle), writes | |
275 | +a trace log to address 10000000 and sends it to a host machine using | |
276 | +TFTP. After this, U-Boot will boot the OS normally, albeit a little | |
277 | +later. | |
278 | + | |
279 | + | |
280 | +Converting Trace Output Data | |
281 | +---------------------------- | |
282 | + | |
283 | +The trace output data is kept in a binary format which is not documented | |
284 | +here. To convert it into something useful, you can use proftool. | |
285 | + | |
286 | +This tool must be given the U-Boot map file and the trace data received | |
287 | +from running that U-Boot. It produces a text output file. | |
288 | + | |
289 | +Options | |
290 | + -m <map_file> | |
291 | + Specify U-Boot map file | |
292 | + | |
293 | + -p <trace_file> | |
294 | + Specifiy profile/trace file | |
295 | + | |
296 | +Commands: | |
297 | + | |
298 | +- dump-ftrace | |
299 | + Write a text dump of the file in Linux ftrace format to stdout | |
300 | + | |
301 | + | |
302 | +Viewing the Trace Data | |
303 | +---------------------- | |
304 | + | |
305 | +You can use pytimechart for this (sudo apt-get pytimechart might work on | |
306 | +your Debian-style machine, and use your favourite search engine to obtain | |
307 | +documentation). It expects the file to have a .txt extension. The program | |
308 | +has terse user interface but is very convenient for viewing U-Boot | |
309 | +profile information. | |
310 | + | |
311 | + | |
312 | +Workflow Suggestions | |
313 | +-------------------- | |
314 | + | |
315 | +The following suggestions may be helpful if you are trying to reduce boot | |
316 | +time: | |
317 | + | |
318 | +1. Enable CONFIG_BOOTSTAGE and CONFIG_BOOTSTAGE_REPORT. This should get | |
319 | +you are helpful overall snapshot of the boot time. | |
320 | + | |
321 | +2. Build U-Boot with tracing and run it. Note the difference in boot time | |
322 | +(it is common for tracing to add 10% to the time) | |
323 | + | |
324 | +3. Collect the trace information as descibed above. Use this to find where | |
325 | +all the time is being spent. | |
326 | + | |
327 | +4. Take a look at that code and see if you can optimise it. Perhaps it is | |
328 | +possible to speed up the initialisation of a device, or remove an unused | |
329 | +feature. | |
330 | + | |
331 | +5. Rebuild, run and collect again. Compare your results. | |
332 | + | |
333 | +6. Keep going until you run out of steam, or your boot is fast enough. | |
334 | + | |
335 | + | |
336 | +Configuring Trace | |
337 | +----------------- | |
338 | + | |
339 | +There are a few parameters in the code that you may want to consider. | |
340 | +There is a function call depth limit (set to 15 by default). When the | |
341 | +stack depth goes above this then no tracing information is recorded. | |
342 | +The maximum depth reached is recorded and displayed by the 'trace stats' | |
343 | +command. | |
344 | + | |
345 | + | |
346 | +Future Work | |
347 | +----------- | |
348 | + | |
349 | +Tracing could be a little tidier in some areas, for example providing | |
350 | +run-time configuration options for trace. | |
351 | + | |
352 | +Some other features that might be useful: | |
353 | + | |
354 | +- Trace filter to select which functions are recorded | |
355 | +- Sample-based profiling using a timer interrupt | |
356 | +- Better control over trace depth | |
357 | +- Compression of trace information | |
358 | + | |
359 | + | |
360 | +Simon Glass <sjg@chromium.org> | |
361 | +April 2013 |
include/common.h
... | ... | @@ -750,6 +750,10 @@ |
750 | 750 | void irq_free_handler (int); |
751 | 751 | void reset_timer (void); |
752 | 752 | ulong get_timer (ulong base); |
753 | + | |
754 | +/* Return value of monotonic microsecond timer */ | |
755 | +unsigned long timer_get_us(void); | |
756 | + | |
753 | 757 | void enable_interrupts (void); |
754 | 758 | int disable_interrupts (void); |
755 | 759 |
include/trace.h
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 | +#ifndef __TRACE_H | |
21 | +#define __TRACE_H | |
22 | + | |
23 | +enum { | |
24 | + /* | |
25 | + * This affects the granularity of our trace. We can bin function | |
26 | + * entry points into groups on the basis that functions typically | |
27 | + * have a minimum size, so entry points can't appear any closer | |
28 | + * than this to each other. | |
29 | + * | |
30 | + * The value here assumes a minimum instruction size of 4 bytes, | |
31 | + * or that instructions are 2 bytes but there are at least 2 of | |
32 | + * them in every function. | |
33 | + * | |
34 | + * Increasing this value reduces the number of functions we can | |
35 | + * resolve, but reduces the size of the uintptr_t array used for | |
36 | + * our function list, which is the length of the code divided by | |
37 | + * this value. | |
38 | + */ | |
39 | + FUNC_SITE_SIZE = 4, /* distance between function sites */ | |
40 | +}; | |
41 | + | |
42 | +enum trace_chunk_type { | |
43 | + TRACE_CHUNK_FUNCS, | |
44 | + TRACE_CHUNK_CALLS, | |
45 | +}; | |
46 | + | |
47 | +/* A trace record for a function, as written to the profile output file */ | |
48 | +struct trace_output_func { | |
49 | + uint32_t offset; /* Function offset into code */ | |
50 | + uint32_t call_count; /* Number of times called */ | |
51 | +}; | |
52 | + | |
53 | +/* A header at the start of the trace output buffer */ | |
54 | +struct trace_output_hdr { | |
55 | + enum trace_chunk_type type; /* Record type */ | |
56 | + uint32_t rec_count; /* Number of records */ | |
57 | +}; | |
58 | + | |
59 | +/* Print statistics about traced function calls */ | |
60 | +void trace_print_stats(void); | |
61 | + | |
62 | +/** | |
63 | + * Dump a list of functions and call counts into a buffer | |
64 | + * | |
65 | + * Each record in the buffer is a struct trace_func_stats. The 'needed' | |
66 | + * parameter returns the number of bytes needed to complete the operation, | |
67 | + * which may be more than buff_size if your buffer is too small. | |
68 | + * | |
69 | + * @param buff Buffer in which to place data, or NULL to count size | |
70 | + * @param buff_size Size of buffer | |
71 | + * @param needed Returns number of bytes used / needed | |
72 | + * @return 0 if ok, -1 on error (buffer exhausted) | |
73 | + */ | |
74 | +int trace_list_functions(void *buff, int buff_size, unsigned *needed); | |
75 | + | |
76 | +/* Flags for ftrace_record */ | |
77 | +enum ftrace_flags { | |
78 | + FUNCF_EXIT = 0UL << 30, | |
79 | + FUNCF_ENTRY = 1UL << 30, | |
80 | + FUNCF_TEXTBASE = 2UL << 30, | |
81 | + | |
82 | + FUNCF_TIMESTAMP_MASK = 0x3fffffff, | |
83 | +}; | |
84 | + | |
85 | +#define TRACE_CALL_TYPE(call) ((call)->flags & 0xc0000000UL) | |
86 | + | |
87 | +/* Information about a single function entry/exit */ | |
88 | +struct trace_call { | |
89 | + uint32_t func; /* Function offset */ | |
90 | + uint32_t caller; /* Caller function offset */ | |
91 | + uint32_t flags; /* Flags and timestamp */ | |
92 | +}; | |
93 | + | |
94 | +int trace_list_calls(void *buff, int buff_size, unsigned int *needed); | |
95 | + | |
96 | +/** | |
97 | + * Turn function tracing on and off | |
98 | + * | |
99 | + * Don't enable trace if it has not been initialised. | |
100 | + * | |
101 | + * @param enabled 1 to enable trace, 0 to disable | |
102 | + */ | |
103 | +void trace_set_enabled(int enabled); | |
104 | + | |
105 | +#ifdef CONFIG_TRACE_EARLY | |
106 | +int trace_early_init(void); | |
107 | +#else | |
108 | +static inline int trace_early_init(void) | |
109 | +{ | |
110 | + return 0; | |
111 | +} | |
112 | +#endif | |
113 | + | |
114 | +/** | |
115 | + * Init the trace system | |
116 | + * | |
117 | + * This should be called after relocation with a suitably large buffer | |
118 | + * (typically as large as the U-Boot text area) | |
119 | + * | |
120 | + * @param buff Pointer to trace buffer | |
121 | + * @param buff_size Size of trace buffer | |
122 | + */ | |
123 | +int trace_init(void *buff, size_t buff_size); | |
124 | + | |
125 | +#endif |
lib/Makefile
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 |