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() |
Randy Dunlap | 7e25f44 | 2009-12-18 15:17:12 -0800 | [diff] [blame^] | 56 | "selfpc" - the address bar() (with mcount() size adjustment) |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 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 |
Randy Dunlap | 7e25f44 | 2009-12-18 15:17:12 -0800 | [diff] [blame^] | 61 | typically the bare minimum with checking things before returning. That also |
| 62 | means the code flow should usually be kept linear (i.e. no branching in the nop |
| 63 | case). This is of course an optimization and not a hard requirement. |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 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 |
Randy Dunlap | 7e25f44 | 2009-12-18 15:17:12 -0800 | [diff] [blame^] | 134 | ftrace_graph_entry_stub). If either of those is not set to the relevant stub |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 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 |
Randy Dunlap | 7e25f44 | 2009-12-18 15:17:12 -0800 | [diff] [blame^] | 137 | function names is strictly required, but you should use them anyway to stay |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 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 |
Randy Dunlap | 7e25f44 | 2009-12-18 15:17:12 -0800 | [diff] [blame^] | 147 | that will return the original return address with which you can return to the |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 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 | |
Frederic Weisbecker | 459c6d1 | 2009-09-19 07:14:15 +0200 | [diff] [blame] | 216 | HAVE_SYSCALL_TRACEPOINTS |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 217 | --------------------- |
| 218 | |
Frederic Weisbecker | 459c6d1 | 2009-09-19 07:14:15 +0200 | [diff] [blame] | 219 | You need very few things to get the syscalls tracing in an arch. |
| 220 | |
| 221 | - Have a NR_syscalls variable in <asm/unistd.h> that provides the number |
| 222 | of syscalls supported by the arch. |
| 223 | - Implement arch_syscall_addr() that resolves a syscall address from a |
| 224 | syscall number. |
| 225 | - Support the TIF_SYSCALL_TRACEPOINT thread flags |
| 226 | - Put the trace_sys_enter() and trace_sys_exit() tracepoints calls from ptrace |
| 227 | in the ptrace syscalls tracing path. |
| 228 | - Tag this arch as HAVE_SYSCALL_TRACEPOINTS. |
Mike Frysinger | 555f386 | 2009-09-14 20:10:15 -0400 | [diff] [blame] | 229 | |
| 230 | |
| 231 | HAVE_FTRACE_MCOUNT_RECORD |
| 232 | ------------------------- |
| 233 | |
| 234 | See scripts/recordmcount.pl for more info. |
| 235 | |
| 236 | <details to be filled> |
| 237 | |
| 238 | |
| 239 | HAVE_DYNAMIC_FTRACE |
| 240 | --------------------- |
| 241 | |
| 242 | <details to be filled> |