123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233 |
- function tracer guts
- ====================
- Introduction
- ------------
- Here we will cover the architecture pieces that the common function tracing
- code relies on for proper functioning. Things are broken down into increasing
- complexity so that you can start simple and at least get basic functionality.
- Note that this focuses on architecture implementation details only. If you
- want more explanation of a feature in terms of common code, review the common
- ftrace.txt file.
- Prerequisites
- -------------
- Ftrace relies on these features being implemented:
- STACKTRACE_SUPPORT - implement save_stack_trace()
- TRACE_IRQFLAGS_SUPPORT - implement include/asm/irqflags.h
- HAVE_FUNCTION_TRACER
- --------------------
- You will need to implement the mcount and the ftrace_stub functions.
- The exact mcount symbol name will depend on your toolchain. Some call it
- "mcount", "_mcount", or even "__mcount". You can probably figure it out by
- running something like:
- $ echo 'main(){}' | gcc -x c -S -o - - -pg | grep mcount
- call mcount
- We'll make the assumption below that the symbol is "mcount" just to keep things
- nice and simple in the examples.
- Keep in mind that the ABI that is in effect inside of the mcount function is
- *highly* architecture/toolchain specific. We cannot help you in this regard,
- sorry. Dig up some old documentation and/or find someone more familiar than
- you to bang ideas off of. Typically, register usage (argument/scratch/etc...)
- is a major issue at this point, especially in relation to the location of the
- mcount call (before/after function prologue). You might also want to look at
- how glibc has implemented the mcount function for your architecture. It might
- be (semi-)relevant.
- The mcount function should check the function pointer ftrace_trace_function
- to see if it is set to ftrace_stub. If it is, there is nothing for you to do,
- so return immediately. If it isn't, then call that function in the same way
- the mcount function normally calls __mcount_internal -- the first argument is
- the "frompc" while the second argument is the "selfpc" (adjusted to remove the
- size of the mcount call that is embedded in the function).
- For example, if the function foo() calls bar(), when the bar() function calls
- mcount(), the arguments mcount() will pass to the tracer are:
- "frompc" - the address bar() will use to return to foo()
- "selfpc" - the address bar() (with _mcount() size adjustment)
- Also keep in mind that this mcount function will be called *a lot*, so
- optimizing for the default case of no tracer will help the smooth running of
- your system when tracing is disabled. So the start of the mcount function is
- typically the bare min with checking things before returning. That also means
- the code flow should usually kept linear (i.e. no branching in the nop case).
- This is of course an optimization and not a hard requirement.
- Here is some pseudo code that should help (these functions should actually be
- implemented in assembly):
- void ftrace_stub(void)
- {
- return;
- }
- void mcount(void)
- {
- /* save any bare state needed in order to do initial checking */
- extern void (*ftrace_trace_function)(unsigned long, unsigned long);
- if (ftrace_trace_function != ftrace_stub)
- goto do_trace;
- /* restore any bare state */
- return;
- do_trace:
- /* save all state needed by the ABI (see paragraph above) */
- unsigned long frompc = ...;
- unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
- ftrace_trace_function(frompc, selfpc);
- /* restore all state needed by the ABI */
- }
- Don't forget to export mcount for modules !
- extern void mcount(void);
- EXPORT_SYMBOL(mcount);
- HAVE_FUNCTION_TRACE_MCOUNT_TEST
- -------------------------------
- This is an optional optimization for the normal case when tracing is turned off
- in the system. If you do not enable this Kconfig option, the common ftrace
- code will take care of doing the checking for you.
- To support this feature, you only need to check the function_trace_stop
- variable in the mcount function. If it is non-zero, there is no tracing to be
- done at all, so you can return.
- This additional pseudo code would simply be:
- void mcount(void)
- {
- /* save any bare state needed in order to do initial checking */
- + if (function_trace_stop)
- + return;
- extern void (*ftrace_trace_function)(unsigned long, unsigned long);
- if (ftrace_trace_function != ftrace_stub)
- ...
- HAVE_FUNCTION_GRAPH_TRACER
- --------------------------
- Deep breath ... time to do some real work. Here you will need to update the
- mcount function to check ftrace graph function pointers, as well as implement
- some functions to save (hijack) and restore the return address.
- The mcount function should check the function pointers ftrace_graph_return
- (compare to ftrace_stub) and ftrace_graph_entry (compare to
- ftrace_graph_entry_stub). If either of those are not set to the relevant stub
- function, call the arch-specific function ftrace_graph_caller which in turn
- calls the arch-specific function prepare_ftrace_return. Neither of these
- function names are strictly required, but you should use them anyways to stay
- consistent across the architecture ports -- easier to compare & contrast
- things.
- The arguments to prepare_ftrace_return are slightly different than what are
- passed to ftrace_trace_function. The second argument "selfpc" is the same,
- but the first argument should be a pointer to the "frompc". Typically this is
- located on the stack. This allows the function to hijack the return address
- temporarily to have it point to the arch-specific function return_to_handler.
- That function will simply call the common ftrace_return_to_handler function and
- that will return the original return address with which, you can return to the
- original call site.
- Here is the updated mcount pseudo code:
- void mcount(void)
- {
- ...
- if (ftrace_trace_function != ftrace_stub)
- goto do_trace;
- +#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- + extern void (*ftrace_graph_return)(...);
- + extern void (*ftrace_graph_entry)(...);
- + if (ftrace_graph_return != ftrace_stub ||
- + ftrace_graph_entry != ftrace_graph_entry_stub)
- + ftrace_graph_caller();
- +#endif
- /* restore any bare state */
- ...
- Here is the pseudo code for the new ftrace_graph_caller assembly function:
- #ifdef CONFIG_FUNCTION_GRAPH_TRACER
- void ftrace_graph_caller(void)
- {
- /* save all state needed by the ABI */
- unsigned long *frompc = &...;
- unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;
- prepare_ftrace_return(frompc, selfpc);
- /* restore all state needed by the ABI */
- }
- #endif
- For information on how to implement prepare_ftrace_return(), simply look at
- the x86 version. The only architecture-specific piece in it is the setup of
- the fault recovery table (the asm(...) code). The rest should be the same
- across architectures.
- Here is the pseudo code for the new return_to_handler assembly function. Note
- that the ABI that applies here is different from what applies to the mcount
- code. Since you are returning from a function (after the epilogue), you might
- be able to skimp on things saved/restored (usually just registers used to pass
- return values).
- #ifdef CONFIG_FUNCTION_GRAPH_TRACER
- void return_to_handler(void)
- {
- /* save all state needed by the ABI (see paragraph above) */
- void (*original_return_point)(void) = ftrace_return_to_handler();
- /* restore all state needed by the ABI */
- /* this is usually either a return or a jump */
- original_return_point();
- }
- #endif
- HAVE_FTRACE_NMI_ENTER
- ---------------------
- If you can't trace NMI functions, then skip this option.
- <details to be filled>
- HAVE_FTRACE_SYSCALLS
- ---------------------
- <details to be filled>
- HAVE_FTRACE_MCOUNT_RECORD
- -------------------------
- See scripts/recordmcount.pl for more info.
- <details to be filled>
- HAVE_DYNAMIC_FTRACE
- ---------------------
- <details to be filled>
|