Commit 9849ed4d72251d273524efb8b70be0be9aecb1df
Committed by
Steven Rostedt
1 parent
ef710e100c
Exists in
master
and in
7 other branches
tracing/documentation: Document dynamic ftracer internals
Add more details to the dynamic function tracing design implementation. Signed-off-by: Mike Frysinger <vapier@gentoo.org> LKML-Reference: <1279610015-10250-1-git-send-email-vapier@gentoo.org> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Showing 2 changed files with 153 additions and 5 deletions Side-by-side Diff
Documentation/trace/ftrace-design.txt
... | ... | @@ -13,7 +13,10 @@ |
13 | 13 | want more explanation of a feature in terms of common code, review the common |
14 | 14 | ftrace.txt file. |
15 | 15 | |
16 | +Ideally, everyone who wishes to retain performance while supporting tracing in | |
17 | +their kernel should make it all the way to dynamic ftrace support. | |
16 | 18 | |
19 | + | |
17 | 20 | Prerequisites |
18 | 21 | ------------- |
19 | 22 | |
... | ... | @@ -215,7 +218,7 @@ |
215 | 218 | exiting of a function. On exit, the value is compared and if it does not |
216 | 219 | match, then it will panic the kernel. This is largely a sanity check for bad |
217 | 220 | code generation with gcc. If gcc for your port sanely updates the frame |
218 | -pointer under different opitmization levels, then ignore this option. | |
221 | +pointer under different optimization levels, then ignore this option. | |
219 | 222 | |
220 | 223 | However, adding support for it isn't terribly difficult. In your assembly code |
221 | 224 | that calls prepare_ftrace_return(), pass the frame pointer as the 3rd argument. |
... | ... | @@ -234,7 +237,7 @@ |
234 | 237 | |
235 | 238 | |
236 | 239 | HAVE_SYSCALL_TRACEPOINTS |
237 | ---------------------- | |
240 | +------------------------ | |
238 | 241 | |
239 | 242 | You need very few things to get the syscalls tracing in an arch. |
240 | 243 | |
241 | 244 | |
242 | 245 | |
243 | 246 | |
244 | 247 | |
245 | 248 | |
... | ... | @@ -250,13 +253,153 @@ |
250 | 253 | HAVE_FTRACE_MCOUNT_RECORD |
251 | 254 | ------------------------- |
252 | 255 | |
253 | -See scripts/recordmcount.pl for more info. | |
256 | +See scripts/recordmcount.pl for more info. Just fill in the arch-specific | |
257 | +details for how to locate the addresses of mcount call sites via objdump. | |
258 | +This option doesn't make much sense without also implementing dynamic ftrace. | |
254 | 259 | |
260 | + | |
261 | +HAVE_DYNAMIC_FTRACE | |
262 | +------------------- | |
263 | + | |
264 | +You will first need HAVE_FTRACE_MCOUNT_RECORD and HAVE_FUNCTION_TRACER, so | |
265 | +scroll your reader back up if you got over eager. | |
266 | + | |
267 | +Once those are out of the way, you will need to implement: | |
268 | + - asm/ftrace.h: | |
269 | + - MCOUNT_ADDR | |
270 | + - ftrace_call_adjust() | |
271 | + - struct dyn_arch_ftrace{} | |
272 | + - asm code: | |
273 | + - mcount() (new stub) | |
274 | + - ftrace_caller() | |
275 | + - ftrace_call() | |
276 | + - ftrace_stub() | |
277 | + - C code: | |
278 | + - ftrace_dyn_arch_init() | |
279 | + - ftrace_make_nop() | |
280 | + - ftrace_make_call() | |
281 | + - ftrace_update_ftrace_func() | |
282 | + | |
283 | +First you will need to fill out some arch details in your asm/ftrace.h. | |
284 | + | |
285 | +Define MCOUNT_ADDR as the address of your mcount symbol similar to: | |
286 | + #define MCOUNT_ADDR ((unsigned long)mcount) | |
287 | +Since no one else will have a decl for that function, you will need to: | |
288 | + extern void mcount(void); | |
289 | + | |
290 | +You will also need the helper function ftrace_call_adjust(). Most people | |
291 | +will be able to stub it out like so: | |
292 | + static inline unsigned long ftrace_call_adjust(unsigned long addr) | |
293 | + { | |
294 | + return addr; | |
295 | + } | |
255 | 296 | <details to be filled> |
256 | 297 | |
298 | +Lastly you will need the custom dyn_arch_ftrace structure. If you need | |
299 | +some extra state when runtime patching arbitrary call sites, this is the | |
300 | +place. For now though, create an empty struct: | |
301 | + struct dyn_arch_ftrace { | |
302 | + /* No extra data needed */ | |
303 | + }; | |
257 | 304 | |
258 | -HAVE_DYNAMIC_FTRACE | |
259 | ---------------------- | |
305 | +With the header out of the way, we can fill out the assembly code. While we | |
306 | +did already create a mcount() function earlier, dynamic ftrace only wants a | |
307 | +stub function. This is because the mcount() will only be used during boot | |
308 | +and then all references to it will be patched out never to return. Instead, | |
309 | +the guts of the old mcount() will be used to create a new ftrace_caller() | |
310 | +function. Because the two are hard to merge, it will most likely be a lot | |
311 | +easier to have two separate definitions split up by #ifdefs. Same goes for | |
312 | +the ftrace_stub() as that will now be inlined in ftrace_caller(). | |
260 | 313 | |
314 | +Before we get confused anymore, let's check out some pseudo code so you can | |
315 | +implement your own stuff in assembly: | |
316 | + | |
317 | +void mcount(void) | |
318 | +{ | |
319 | + return; | |
320 | +} | |
321 | + | |
322 | +void ftrace_caller(void) | |
323 | +{ | |
324 | + /* implement HAVE_FUNCTION_TRACE_MCOUNT_TEST if you desire */ | |
325 | + | |
326 | + /* save all state needed by the ABI (see paragraph above) */ | |
327 | + | |
328 | + unsigned long frompc = ...; | |
329 | + unsigned long selfpc = <return address> - MCOUNT_INSN_SIZE; | |
330 | + | |
331 | +ftrace_call: | |
332 | + ftrace_stub(frompc, selfpc); | |
333 | + | |
334 | + /* restore all state needed by the ABI */ | |
335 | + | |
336 | +ftrace_stub: | |
337 | + return; | |
338 | +} | |
339 | + | |
340 | +This might look a little odd at first, but keep in mind that we will be runtime | |
341 | +patching multiple things. First, only functions that we actually want to trace | |
342 | +will be patched to call ftrace_caller(). Second, since we only have one tracer | |
343 | +active at a time, we will patch the ftrace_caller() function itself to call the | |
344 | +specific tracer in question. That is the point of the ftrace_call label. | |
345 | + | |
346 | +With that in mind, let's move on to the C code that will actually be doing the | |
347 | +runtime patching. You'll need a little knowledge of your arch's opcodes in | |
348 | +order to make it through the next section. | |
349 | + | |
350 | +Every arch has an init callback function. If you need to do something early on | |
351 | +to initialize some state, this is the time to do that. Otherwise, this simple | |
352 | +function below should be sufficient for most people: | |
353 | + | |
354 | +int __init ftrace_dyn_arch_init(void *data) | |
355 | +{ | |
356 | + /* return value is done indirectly via data */ | |
357 | + *(unsigned long *)data = 0; | |
358 | + | |
359 | + return 0; | |
360 | +} | |
361 | + | |
362 | +There are two functions that are used to do runtime patching of arbitrary | |
363 | +functions. The first is used to turn the mcount call site into a nop (which | |
364 | +is what helps us retain runtime performance when not tracing). The second is | |
365 | +used to turn the mcount call site into a call to an arbitrary location (but | |
366 | +typically that is ftracer_caller()). See the general function definition in | |
367 | +linux/ftrace.h for the functions: | |
368 | + ftrace_make_nop() | |
369 | + ftrace_make_call() | |
370 | +The rec->ip value is the address of the mcount call site that was collected | |
371 | +by the scripts/recordmcount.pl during build time. | |
372 | + | |
373 | +The last function is used to do runtime patching of the active tracer. This | |
374 | +will be modifying the assembly code at the location of the ftrace_call symbol | |
375 | +inside of the ftrace_caller() function. So you should have sufficient padding | |
376 | +at that location to support the new function calls you'll be inserting. Some | |
377 | +people will be using a "call" type instruction while others will be using a | |
378 | +"branch" type instruction. Specifically, the function is: | |
379 | + ftrace_update_ftrace_func() | |
380 | + | |
381 | + | |
382 | +HAVE_DYNAMIC_FTRACE + HAVE_FUNCTION_GRAPH_TRACER | |
383 | +------------------------------------------------ | |
384 | + | |
385 | +The function grapher needs a few tweaks in order to work with dynamic ftrace. | |
386 | +Basically, you will need to: | |
387 | + - update: | |
388 | + - ftrace_caller() | |
389 | + - ftrace_graph_call() | |
390 | + - ftrace_graph_caller() | |
391 | + - implement: | |
392 | + - ftrace_enable_ftrace_graph_caller() | |
393 | + - ftrace_disable_ftrace_graph_caller() | |
394 | + | |
261 | 395 | <details to be filled> |
396 | +Quick notes: | |
397 | + - add a nop stub after the ftrace_call location named ftrace_graph_call; | |
398 | + stub needs to be large enough to support a call to ftrace_graph_caller() | |
399 | + - update ftrace_graph_caller() to work with being called by the new | |
400 | + ftrace_caller() since some semantics may have changed | |
401 | + - ftrace_enable_ftrace_graph_caller() will runtime patch the | |
402 | + ftrace_graph_call location with a call to ftrace_graph_caller() | |
403 | + - ftrace_disable_ftrace_graph_caller() will runtime patch the | |
404 | + ftrace_graph_call location with nops |