diff options
Diffstat (limited to 'Documentation/trace/ftrace-design.txt')
-rw-r--r-- | Documentation/trace/ftrace-design.txt | 411 |
1 files changed, 0 insertions, 411 deletions
diff --git a/Documentation/trace/ftrace-design.txt b/Documentation/trace/ftrace-design.txt deleted file mode 100644 index 79fcafc7fd6..00000000000 --- a/Documentation/trace/ftrace-design.txt +++ /dev/null @@ -1,411 +0,0 @@ - function tracer guts - ==================== - By Mike Frysinger - -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. - -Ideally, everyone who wishes to retain performance while supporting tracing in -their kernel should make it all the way to dynamic ftrace support. - - -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 minimum with checking things before returning. That also -means the code flow should usually be 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 is 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 is strictly required, but you should use them anyway 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; - /* passing frame pointer up is optional -- see below */ - prepare_ftrace_return(frompc, selfpc, frame_pointer); - - /* restore all state needed by the ABI */ -} -#endif - -For information on how to implement prepare_ftrace_return(), simply look at the -x86 version (the frame pointer passing is optional; see the next section for -more information). 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_FUNCTION_GRAPH_FP_TEST ---------------------------- - -An arch may pass in a unique value (frame pointer) to both the entering and -exiting of a function. On exit, the value is compared and if it does not -match, then it will panic the kernel. This is largely a sanity check for bad -code generation with gcc. If gcc for your port sanely updates the frame -pointer under different optimization levels, then ignore this option. - -However, adding support for it isn't terribly difficult. In your assembly code -that calls prepare_ftrace_return(), pass the frame pointer as the 3rd argument. -Then in the C version of that function, do what the x86 port does and pass it -along to ftrace_push_return_trace() instead of a stub value of 0. - -Similarly, when you call ftrace_return_to_handler(), pass it the frame pointer. - - -HAVE_FTRACE_NMI_ENTER ---------------------- - -If you can't trace NMI functions, then skip this option. - -<details to be filled> - - -HAVE_SYSCALL_TRACEPOINTS ------------------------- - -You need very few things to get the syscalls tracing in an arch. - -- Support HAVE_ARCH_TRACEHOOK (see arch/Kconfig). -- Have a NR_syscalls variable in <asm/unistd.h> that provides the number - of syscalls supported by the arch. -- Support the TIF_SYSCALL_TRACEPOINT thread flags. -- Put the trace_sys_enter() and trace_sys_exit() tracepoints calls from ptrace - in the ptrace syscalls tracing path. -- If the system call table on this arch is more complicated than a simple array - of addresses of the system calls, implement an arch_syscall_addr to return - the address of a given system call. -- If the symbol names of the system calls do not match the function names on - this arch, define ARCH_HAS_SYSCALL_MATCH_SYM_NAME in asm/ftrace.h and - implement arch_syscall_match_sym_name with the appropriate logic to return - true if the function name corresponds with the symbol name. -- Tag this arch as HAVE_SYSCALL_TRACEPOINTS. - - -HAVE_FTRACE_MCOUNT_RECORD -------------------------- - -See scripts/recordmcount.pl for more info. Just fill in the arch-specific -details for how to locate the addresses of mcount call sites via objdump. -This option doesn't make much sense without also implementing dynamic ftrace. - - -HAVE_DYNAMIC_FTRACE -------------------- - -You will first need HAVE_FTRACE_MCOUNT_RECORD and HAVE_FUNCTION_TRACER, so -scroll your reader back up if you got over eager. - -Once those are out of the way, you will need to implement: - - asm/ftrace.h: - - MCOUNT_ADDR - - ftrace_call_adjust() - - struct dyn_arch_ftrace{} - - asm code: - - mcount() (new stub) - - ftrace_caller() - - ftrace_call() - - ftrace_stub() - - C code: - - ftrace_dyn_arch_init() - - ftrace_make_nop() - - ftrace_make_call() - - ftrace_update_ftrace_func() - -First you will need to fill out some arch details in your asm/ftrace.h. - -Define MCOUNT_ADDR as the address of your mcount symbol similar to: - #define MCOUNT_ADDR ((unsigned long)mcount) -Since no one else will have a decl for that function, you will need to: - extern void mcount(void); - -You will also need the helper function ftrace_call_adjust(). Most people -will be able to stub it out like so: - static inline unsigned long ftrace_call_adjust(unsigned long addr) - { - return addr; - } -<details to be filled> - -Lastly you will need the custom dyn_arch_ftrace structure. If you need -some extra state when runtime patching arbitrary call sites, this is the -place. For now though, create an empty struct: - struct dyn_arch_ftrace { - /* No extra data needed */ - }; - -With the header out of the way, we can fill out the assembly code. While we -did already create a mcount() function earlier, dynamic ftrace only wants a -stub function. This is because the mcount() will only be used during boot -and then all references to it will be patched out never to return. Instead, -the guts of the old mcount() will be used to create a new ftrace_caller() -function. Because the two are hard to merge, it will most likely be a lot -easier to have two separate definitions split up by #ifdefs. Same goes for -the ftrace_stub() as that will now be inlined in ftrace_caller(). - -Before we get confused anymore, let's check out some pseudo code so you can -implement your own stuff in assembly: - -void mcount(void) -{ - return; -} - -void ftrace_caller(void) -{ - /* implement HAVE_FUNCTION_TRACE_MCOUNT_TEST if you desire */ - - /* save all state needed by the ABI (see paragraph above) */ - - unsigned long frompc = ...; - unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE; - -ftrace_call: - ftrace_stub(frompc, selfpc); - - /* restore all state needed by the ABI */ - -ftrace_stub: - return; -} - -This might look a little odd at first, but keep in mind that we will be runtime -patching multiple things. First, only functions that we actually want to trace -will be patched to call ftrace_caller(). Second, since we only have one tracer -active at a time, we will patch the ftrace_caller() function itself to call the -specific tracer in question. That is the point of the ftrace_call label. - -With that in mind, let's move on to the C code that will actually be doing the -runtime patching. You'll need a little knowledge of your arch's opcodes in -order to make it through the next section. - -Every arch has an init callback function. If you need to do something early on -to initialize some state, this is the time to do that. Otherwise, this simple -function below should be sufficient for most people: - -int __init ftrace_dyn_arch_init(void *data) -{ - /* return value is done indirectly via data */ - *(unsigned long *)data = 0; - - return 0; -} - -There are two functions that are used to do runtime patching of arbitrary -functions. The first is used to turn the mcount call site into a nop (which -is what helps us retain runtime performance when not tracing). The second is -used to turn the mcount call site into a call to an arbitrary location (but -typically that is ftracer_caller()). See the general function definition in -linux/ftrace.h for the functions: - ftrace_make_nop() - ftrace_make_call() -The rec->ip value is the address of the mcount call site that was collected -by the scripts/recordmcount.pl during build time. - -The last function is used to do runtime patching of the active tracer. This -will be modifying the assembly code at the location of the ftrace_call symbol -inside of the ftrace_caller() function. So you should have sufficient padding -at that location to support the new function calls you'll be inserting. Some -people will be using a "call" type instruction while others will be using a -"branch" type instruction. Specifically, the function is: - ftrace_update_ftrace_func() - - -HAVE_DYNAMIC_FTRACE + HAVE_FUNCTION_GRAPH_TRACER ------------------------------------------------- - -The function grapher needs a few tweaks in order to work with dynamic ftrace. -Basically, you will need to: - - update: - - ftrace_caller() - - ftrace_graph_call() - - ftrace_graph_caller() - - implement: - - ftrace_enable_ftrace_graph_caller() - - ftrace_disable_ftrace_graph_caller() - -<details to be filled> -Quick notes: - - add a nop stub after the ftrace_call location named ftrace_graph_call; - stub needs to be large enough to support a call to ftrace_graph_caller() - - update ftrace_graph_caller() to work with being called by the new - ftrace_caller() since some semantics may have changed - - ftrace_enable_ftrace_graph_caller() will runtime patch the - ftrace_graph_call location with a call to ftrace_graph_caller() - - ftrace_disable_ftrace_graph_caller() will runtime patch the - ftrace_graph_call location with nops |