| Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 1 | function tracer guts | 
|  | 2 | ==================== | 
|  | 3 |  | 
|  | 4 | Introduction | 
|  | 5 | ------------ | 
|  | 6 |  | 
|  | 7 | Here we will cover the architecture pieces that the common function tracing | 
|  | 8 | code relies on for proper functioning.  Things are broken down into increasing | 
|  | 9 | complexity so that you can start simple and at least get basic functionality. | 
|  | 10 |  | 
|  | 11 | Note that this focuses on architecture implementation details only.  If you | 
|  | 12 | want more explanation of a feature in terms of common code, review the common | 
|  | 13 | ftrace.txt file. | 
|  | 14 |  | 
|  | 15 |  | 
|  | 16 | Prerequisites | 
|  | 17 | ------------- | 
|  | 18 |  | 
|  | 19 | Ftrace relies on these features being implemented: | 
|  | 20 | STACKTRACE_SUPPORT - implement save_stack_trace() | 
|  | 21 | TRACE_IRQFLAGS_SUPPORT - implement include/asm/irqflags.h | 
|  | 22 |  | 
|  | 23 |  | 
|  | 24 | HAVE_FUNCTION_TRACER | 
|  | 25 | -------------------- | 
|  | 26 |  | 
|  | 27 | You will need to implement the mcount and the ftrace_stub functions. | 
|  | 28 |  | 
|  | 29 | The exact mcount symbol name will depend on your toolchain.  Some call it | 
|  | 30 | "mcount", "_mcount", or even "__mcount".  You can probably figure it out by | 
|  | 31 | running something like: | 
|  | 32 | $ echo 'main(){}' | gcc -x c -S -o - - -pg | grep mcount | 
|  | 33 | call    mcount | 
|  | 34 | We'll make the assumption below that the symbol is "mcount" just to keep things | 
|  | 35 | nice and simple in the examples. | 
|  | 36 |  | 
|  | 37 | Keep in mind that the ABI that is in effect inside of the mcount function is | 
|  | 38 | *highly* architecture/toolchain specific.  We cannot help you in this regard, | 
|  | 39 | sorry.  Dig up some old documentation and/or find someone more familiar than | 
|  | 40 | you to bang ideas off of.  Typically, register usage (argument/scratch/etc...) | 
|  | 41 | is a major issue at this point, especially in relation to the location of the | 
|  | 42 | mcount call (before/after function prologue).  You might also want to look at | 
|  | 43 | how glibc has implemented the mcount function for your architecture.  It might | 
|  | 44 | be (semi-)relevant. | 
|  | 45 |  | 
|  | 46 | The mcount function should check the function pointer ftrace_trace_function | 
|  | 47 | to see if it is set to ftrace_stub.  If it is, there is nothing for you to do, | 
|  | 48 | so return immediately.  If it isn't, then call that function in the same way | 
|  | 49 | the mcount function normally calls __mcount_internal -- the first argument is | 
|  | 50 | the "frompc" while the second argument is the "selfpc" (adjusted to remove the | 
|  | 51 | size of the mcount call that is embedded in the function). | 
|  | 52 |  | 
|  | 53 | For example, if the function foo() calls bar(), when the bar() function calls | 
|  | 54 | mcount(), the arguments mcount() will pass to the tracer are: | 
|  | 55 | "frompc" - the address bar() will use to return to foo() | 
|  | 56 | "selfpc" - the address bar() (with _mcount() size adjustment) | 
|  | 57 |  | 
|  | 58 | Also keep in mind that this mcount function will be called *a lot*, so | 
|  | 59 | optimizing for the default case of no tracer will help the smooth running of | 
|  | 60 | your system when tracing is disabled.  So the start of the mcount function is | 
|  | 61 | typically the bare min with checking things before returning.  That also means | 
|  | 62 | the code flow should usually kept linear (i.e. no branching in the nop case). | 
|  | 63 | This is of course an optimization and not a hard requirement. | 
|  | 64 |  | 
|  | 65 | Here is some pseudo code that should help (these functions should actually be | 
|  | 66 | implemented in assembly): | 
|  | 67 |  | 
|  | 68 | void ftrace_stub(void) | 
|  | 69 | { | 
|  | 70 | return; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | void mcount(void) | 
|  | 74 | { | 
|  | 75 | /* save any bare state needed in order to do initial checking */ | 
|  | 76 |  | 
|  | 77 | extern void (*ftrace_trace_function)(unsigned long, unsigned long); | 
|  | 78 | if (ftrace_trace_function != ftrace_stub) | 
|  | 79 | goto do_trace; | 
|  | 80 |  | 
|  | 81 | /* restore any bare state */ | 
|  | 82 |  | 
|  | 83 | return; | 
|  | 84 |  | 
|  | 85 | do_trace: | 
|  | 86 |  | 
|  | 87 | /* save all state needed by the ABI (see paragraph above) */ | 
|  | 88 |  | 
|  | 89 | unsigned long frompc = ...; | 
|  | 90 | unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE; | 
|  | 91 | ftrace_trace_function(frompc, selfpc); | 
|  | 92 |  | 
|  | 93 | /* restore all state needed by the ABI */ | 
|  | 94 | } | 
|  | 95 |  | 
|  | 96 | Don't forget to export mcount for modules ! | 
|  | 97 | extern void mcount(void); | 
|  | 98 | EXPORT_SYMBOL(mcount); | 
|  | 99 |  | 
|  | 100 |  | 
|  | 101 | HAVE_FUNCTION_TRACE_MCOUNT_TEST | 
|  | 102 | ------------------------------- | 
|  | 103 |  | 
|  | 104 | This is an optional optimization for the normal case when tracing is turned off | 
|  | 105 | in the system.  If you do not enable this Kconfig option, the common ftrace | 
|  | 106 | code will take care of doing the checking for you. | 
|  | 107 |  | 
|  | 108 | To support this feature, you only need to check the function_trace_stop | 
|  | 109 | variable in the mcount function.  If it is non-zero, there is no tracing to be | 
|  | 110 | done at all, so you can return. | 
|  | 111 |  | 
|  | 112 | This additional pseudo code would simply be: | 
|  | 113 | void mcount(void) | 
|  | 114 | { | 
|  | 115 | /* save any bare state needed in order to do initial checking */ | 
|  | 116 |  | 
|  | 117 | +	if (function_trace_stop) | 
|  | 118 | +		return; | 
|  | 119 |  | 
|  | 120 | extern void (*ftrace_trace_function)(unsigned long, unsigned long); | 
|  | 121 | if (ftrace_trace_function != ftrace_stub) | 
|  | 122 | ... | 
|  | 123 |  | 
|  | 124 |  | 
|  | 125 | HAVE_FUNCTION_GRAPH_TRACER | 
|  | 126 | -------------------------- | 
|  | 127 |  | 
|  | 128 | Deep breath ... time to do some real work.  Here you will need to update the | 
|  | 129 | mcount function to check ftrace graph function pointers, as well as implement | 
|  | 130 | some functions to save (hijack) and restore the return address. | 
|  | 131 |  | 
|  | 132 | The mcount function should check the function pointers ftrace_graph_return | 
|  | 133 | (compare to ftrace_stub) and ftrace_graph_entry (compare to | 
|  | 134 | ftrace_graph_entry_stub).  If either of those are not set to the relevant stub | 
|  | 135 | function, call the arch-specific function ftrace_graph_caller which in turn | 
|  | 136 | calls the arch-specific function prepare_ftrace_return.  Neither of these | 
|  | 137 | function names are strictly required, but you should use them anyways to stay | 
|  | 138 | consistent across the architecture ports -- easier to compare & contrast | 
|  | 139 | things. | 
|  | 140 |  | 
|  | 141 | The arguments to prepare_ftrace_return are slightly different than what are | 
|  | 142 | passed to ftrace_trace_function.  The second argument "selfpc" is the same, | 
|  | 143 | but the first argument should be a pointer to the "frompc".  Typically this is | 
|  | 144 | located on the stack.  This allows the function to hijack the return address | 
|  | 145 | temporarily to have it point to the arch-specific function return_to_handler. | 
|  | 146 | That function will simply call the common ftrace_return_to_handler function and | 
|  | 147 | that will return the original return address with which, you can return to the | 
|  | 148 | original call site. | 
|  | 149 |  | 
|  | 150 | Here is the updated mcount pseudo code: | 
|  | 151 | void mcount(void) | 
|  | 152 | { | 
|  | 153 | ... | 
|  | 154 | if (ftrace_trace_function != ftrace_stub) | 
|  | 155 | goto do_trace; | 
|  | 156 |  | 
|  | 157 | +#ifdef CONFIG_FUNCTION_GRAPH_TRACER | 
|  | 158 | +	extern void (*ftrace_graph_return)(...); | 
|  | 159 | +	extern void (*ftrace_graph_entry)(...); | 
|  | 160 | +	if (ftrace_graph_return != ftrace_stub || | 
|  | 161 | +	    ftrace_graph_entry != ftrace_graph_entry_stub) | 
|  | 162 | +		ftrace_graph_caller(); | 
|  | 163 | +#endif | 
|  | 164 |  | 
|  | 165 | /* restore any bare state */ | 
|  | 166 | ... | 
|  | 167 |  | 
|  | 168 | Here is the pseudo code for the new ftrace_graph_caller assembly function: | 
|  | 169 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 
|  | 170 | void ftrace_graph_caller(void) | 
|  | 171 | { | 
|  | 172 | /* save all state needed by the ABI */ | 
|  | 173 |  | 
|  | 174 | unsigned long *frompc = &...; | 
|  | 175 | unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE; | 
|  | 176 | prepare_ftrace_return(frompc, selfpc); | 
|  | 177 |  | 
|  | 178 | /* restore all state needed by the ABI */ | 
|  | 179 | } | 
|  | 180 | #endif | 
|  | 181 |  | 
|  | 182 | For information on how to implement prepare_ftrace_return(), simply look at | 
|  | 183 | the x86 version.  The only architecture-specific piece in it is the setup of | 
|  | 184 | the fault recovery table (the asm(...) code).  The rest should be the same | 
|  | 185 | across architectures. | 
|  | 186 |  | 
|  | 187 | Here is the pseudo code for the new return_to_handler assembly function.  Note | 
|  | 188 | that the ABI that applies here is different from what applies to the mcount | 
|  | 189 | code.  Since you are returning from a function (after the epilogue), you might | 
|  | 190 | be able to skimp on things saved/restored (usually just registers used to pass | 
|  | 191 | return values). | 
|  | 192 |  | 
|  | 193 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 
|  | 194 | void return_to_handler(void) | 
|  | 195 | { | 
|  | 196 | /* save all state needed by the ABI (see paragraph above) */ | 
|  | 197 |  | 
|  | 198 | void (*original_return_point)(void) = ftrace_return_to_handler(); | 
|  | 199 |  | 
|  | 200 | /* restore all state needed by the ABI */ | 
|  | 201 |  | 
|  | 202 | /* this is usually either a return or a jump */ | 
|  | 203 | original_return_point(); | 
|  | 204 | } | 
|  | 205 | #endif | 
|  | 206 |  | 
|  | 207 |  | 
|  | 208 | HAVE_FTRACE_NMI_ENTER | 
|  | 209 | --------------------- | 
|  | 210 |  | 
|  | 211 | If you can't trace NMI functions, then skip this option. | 
|  | 212 |  | 
|  | 213 | <details to be filled> | 
|  | 214 |  | 
|  | 215 |  | 
|  | 216 | HAVE_FTRACE_SYSCALLS | 
|  | 217 | --------------------- | 
|  | 218 |  | 
|  | 219 | <details to be filled> | 
|  | 220 |  | 
|  | 221 |  | 
|  | 222 | HAVE_FTRACE_MCOUNT_RECORD | 
|  | 223 | ------------------------- | 
|  | 224 |  | 
|  | 225 | See scripts/recordmcount.pl for more info. | 
|  | 226 |  | 
|  | 227 | <details to be filled> | 
|  | 228 |  | 
|  | 229 |  | 
|  | 230 | HAVE_DYNAMIC_FTRACE | 
|  | 231 | --------------------- | 
|  | 232 |  | 
|  | 233 | <details to be filled> |