On Wed, 2009-06-10 at 16:52 -0400, Mike Frysinger wrote:> While implementing ftracer and ftracer graph support, I found the exact> arch implementation details to be a bit lacking (and my x86 foo ain't> great). So after pounding out support for the Blackfin arch, start> documenting the requirements/details.> > Signed-off-by: Mike Frysinger <vapier@gentoo.org>> ---> v2> - move to Documentation/ and add a lot more details> > note: yes, i think every kconfig option that is documented should point to> the relevant text file as this helps in the case of people who use kconfig's> search function to look for things> > Documentation/trace/ftrace-implementation.txt | 223 +++++++++++++++++++++++++> Documentation/trace/ftrace.txt | 6 +> kernel/trace/Kconfig | 16 ++-> 3 files changed, 242 insertions(+), 3 deletions(-)> create mode 100644 Documentation/trace/ftrace-implementation.txt> > diff --git a/Documentation/trace/ftrace-implementation.txt b/Documentation/trace/ftrace-implementation.txt> new file mode 100644> index 0000000..cba63b9> --- /dev/null> +++ b/Documentation/trace/ftrace-implementation.txt> @@ -0,0 +1,223 @@> + ftrace guts> + ===========> +> +Introduction> +------------> +> +Here we will cover the architecture pieces that the common ftrace code relies> +on for proper functioning. Things are broken down into increasing complexity> +so that you can start at the top and get at least 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_GRAPH_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 'm(){}' | gcc -x c -S -o - - -pg | grep mcount> + call mcount> +> +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/clobbering is a major> +problem here. 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 */> +> + unsigned long frompc = ...;> + unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE;> + ftrace_trace_function(frompc, selfpc);> +> + /* restore all state needed by the ABI */> +}> +> +> +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.> +

In reality, the prepare_ftrace_return() can have the same arguments asftrace_trace_function(), and the fault recovery table is not needed.

in my mips-specific implementation(in the stage of -v2, i will send -v3later), i removed the fault recovery table directly and implemented itin the following method:

as i know, prepare_ftrace_return() is used to check whether the callingfunction expect to trace, if yes, return the 'hook' function&return_to_handler, otherwise, return back to the parent_ip directly.so, here, i think there is no need to transfer the data via address, butjust using the same arguments like ftrace_trace_function does.