Commit b2e16a85a1fa3f871ca73a554a7fd63067d9ad14

Authored by Simon Glass
Committed by Tom Rini
1 parent b8bcaa3ad3

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

  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 +=>print
  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
... ... @@ -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  
  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
... ... @@ -71,6 +71,7 @@
71 71 COBJS-$(CONFIG_REGEX) += slre.o
72 72 COBJS-y += string.o
73 73 COBJS-y += time.o
  74 +COBJS-$(CONFIG_TRACE) += trace.o
74 75 COBJS-$(CONFIG_BOOTP_PXE) += uuid.o
75 76 COBJS-y += vsprintf.o
76 77 COBJS-$(CONFIG_RANDOM_MACADDR) += rand.o
  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