If a function had ever had IPMODIFY or DIRECT attached to it, where this is how live kernel patching and BPF overrides work, mark them and display an "M" in the enabled_functions and touched_functions files. This can be used for debugging. If a function had been modified and later there's a bug in the code related to that function, this can be used to know if the cause is possibly from a live kernel patch or a BPF program that changed the behavior of the code. Also update the documentation on the enabled_functions and touched_functions output, as it was missing direct callers and CALL_OPS. And include this new modify attribute. Link: https://lore.kernel.org/linux-trace-kernel/20230502213233.004e3ae4@gandalf.local.home
Unified · Split
| @@ -350,6 +350,19 @@ of ftrace. Here is a list of some of the key files: · 349 unmodified lines | |||
| 350 | an 'I' will be displayed on the same line as the function that | 350 | an 'I' will be displayed on the same line as the function that |
| 351 | can be overridden. | 351 | can be overridden. |
| 352 | 352 | ||
| 353 | If a non ftrace trampoline is attached (BPF) a 'D' will be displayed. | ||
| 354 | Note, normal ftrace trampolines can also be attached, but only one | ||
| 355 | "direct" trampoline can be attached to a given function at a time. | ||
| 356 | |||
| 357 | Some architectures can not call direct trampolines, but instead have | ||
| 358 | the ftrace ops function located above the function entry point. In | ||
| 359 | such cases an 'O' will be displayed. | ||
| 360 | |||
| 361 | If a function had either the "ip modify" or a "direct" call attached to | ||
| 362 | it in the past, a 'M' will be shown. This flag is never cleared. It is | ||
| 363 | used to know if a function was every modified by the ftrace infrastructure, | ||
| 364 | and can be used for debugging. | ||
| 365 | |||
| 353 | If the architecture supports it, it will also show what callback | 366 | If the architecture supports it, it will also show what callback |
| 354 | is being directly called by the function. If the count is greater | 367 | is being directly called by the function. If the count is greater |
| 355 | than 1 it most likely will be ftrace_ops_list_func(). | 368 | than 1 it most likely will be ftrace_ops_list_func(). |
| @@ -359,6 +372,18 @@ of ftrace. Here is a list of some of the key files: · 3 unmodified lines | |||
| 359 | its address will be printed as well as the function that the | 372 | its address will be printed as well as the function that the |
| 360 | trampoline calls. | 373 | trampoline calls. |
| 361 | 374 | ||
| 375 | touched_functions: | ||
| 376 | |||
| 377 | This file contains all the functions that ever had a function callback | ||
| 378 | to it via the ftrace infrastructure. It has the same format as | ||
| 379 | enabled_functions but shows all functions that have every been | ||
| 380 | traced. | ||
| 381 | |||
| 382 | To see any function that has every been modified by "ip modify" or a | ||
| 383 | direct trampoline, one can perform the following command: | ||
| 384 | |||
| 385 | grep ' M ' /sys/kernel/tracing/touched_functions | ||
| 386 | |||
| 362 | function_profile_enabled: | 387 | function_profile_enabled: |
| 363 | 388 | ||
| 364 | When set it will enable all functions with either the function | 389 | When set it will enable all functions with either the function |
| @@ -549,6 +549,7 @@ bool is_ftrace_trampoline(unsigned long addr); · 548 unmodified lines | |||
| 549 | * CALL_OPS - the record can use callsite-specific ops | 549 | * CALL_OPS - the record can use callsite-specific ops |
| 550 | * CALL_OPS_EN - the function is set up to use callsite-specific ops | 550 | * CALL_OPS_EN - the function is set up to use callsite-specific ops |
| 551 | * TOUCHED - A callback was added since boot up | 551 | * TOUCHED - A callback was added since boot up |
| 552 | * MODIFIED - The function had IPMODIFY or DIRECT attached to it | ||
| 552 | * | 553 | * |
| 553 | * When a new ftrace_ops is registered and wants a function to save | 554 | * When a new ftrace_ops is registered and wants a function to save |
| 554 | * pt_regs, the rec->flags REGS is set. When the function has been | 555 | * pt_regs, the rec->flags REGS is set. When the function has been |
| @@ -569,9 +570,10 @@ enum { · 14 unmodified lines | |||
| 569 | FTRACE_FL_CALL_OPS = (1UL << 22), | 570 | FTRACE_FL_CALL_OPS = (1UL << 22), |
| 570 | FTRACE_FL_CALL_OPS_EN = (1UL << 21), | 571 | FTRACE_FL_CALL_OPS_EN = (1UL << 21), |
| 571 | FTRACE_FL_TOUCHED = (1UL << 20), | 572 | FTRACE_FL_TOUCHED = (1UL << 20), |
| 573 | FTRACE_FL_MODIFIED = (1UL << 19), | ||
| 572 | }; | 574 | }; |
| 573 | 575 | ||
| 574 | #define FTRACE_REF_MAX_SHIFT 20 | 576 | #define FTRACE_REF_MAX_SHIFT 19 |
| 575 | #define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) | 577 | #define FTRACE_REF_MAX ((1UL << FTRACE_REF_MAX_SHIFT) - 1) |
| 576 | 578 | ||
| 577 | #define ftrace_rec_count(rec) ((rec)->flags & FTRACE_REF_MAX) | 579 | #define ftrace_rec_count(rec) ((rec)->flags & FTRACE_REF_MAX) |
| @@ -46,7 +46,8 @@ · 45 unmodified lines | |||
| 46 | #include "trace_stat.h" | 46 | #include "trace_stat.h" |
| 47 | 47 | ||
| 48 | /* Flags that do not get reset */ | 48 | /* Flags that do not get reset */ |
| 49 | #define FTRACE_NOCLEAR_FLAGS (FTRACE_FL_DISABLED | FTRACE_FL_TOUCHED) | 49 | #define FTRACE_NOCLEAR_FLAGS (FTRACE_FL_DISABLED | FTRACE_FL_TOUCHED | \ |
| 50 | FTRACE_FL_MODIFIED) | ||
| 50 | 51 | ||
| 51 | #define FTRACE_INVALID_FUNCTION "__ftrace_invalid_address__" | 52 | #define FTRACE_INVALID_FUNCTION "__ftrace_invalid_address__" |
| 52 | 53 | ||
| @@ -2273,6 +2274,10 @@ static int ftrace_check_record(struct dyn_ftrace *rec, bool enable, bool update) · 2220 unmodified lines | |||
| 2273 | rec->flags &= ~FTRACE_FL_TRAMP_EN; | 2274 | rec->flags &= ~FTRACE_FL_TRAMP_EN; |
| 2274 | } | 2275 | } |
| 2275 | 2276 | ||
| 2277 | /* Keep track of anything that modifies the function */ | ||
| 2278 | if (rec->flags & (FTRACE_FL_DIRECT | FTRACE_FL_IPMODIFY)) | ||
| 2279 | rec->flags |= FTRACE_FL_MODIFIED; | ||
| 2280 | |||
| 2276 | if (flag & FTRACE_FL_DIRECT) { | 2281 | if (flag & FTRACE_FL_DIRECT) { |
| 2277 | /* | 2282 | /* |
| 2278 | * If there's only one user (direct_ops helper) | 2283 | * If there's only one user (direct_ops helper) |
| @@ -3866,12 +3871,13 @@ static int t_show(struct seq_file *m, void *v) · 1587 unmodified lines | |||
| 3866 | if (iter->flags & (FTRACE_ITER_ENABLED | FTRACE_ITER_TOUCHED)) { | 3871 | if (iter->flags & (FTRACE_ITER_ENABLED | FTRACE_ITER_TOUCHED)) { |
| 3867 | struct ftrace_ops *ops; | 3872 | struct ftrace_ops *ops; |
| 3868 | 3873 | ||
| 3869 | seq_printf(m, " (%ld)%s%s%s%s", | 3874 | seq_printf(m, " (%ld)%s%s%s%s%s", |
| 3870 | ftrace_rec_count(rec), | 3875 | ftrace_rec_count(rec), |
| 3871 | rec->flags & FTRACE_FL_REGS ? " R" : " ", | 3876 | rec->flags & FTRACE_FL_REGS ? " R" : " ", |
| 3872 | rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ", | 3877 | rec->flags & FTRACE_FL_IPMODIFY ? " I" : " ", |
| 3873 | rec->flags & FTRACE_FL_DIRECT ? " D" : " ", | 3878 | rec->flags & FTRACE_FL_DIRECT ? " D" : " ", |
| 3874 | rec->flags & FTRACE_FL_CALL_OPS ? " O" : " "); | 3879 | rec->flags & FTRACE_FL_CALL_OPS ? " O" : " ", |
| 3880 | rec->flags & FTRACE_FL_MODIFIED ? " M " : " "); | ||
| 3875 | if (rec->flags & FTRACE_FL_TRAMP_EN) { | 3881 | if (rec->flags & FTRACE_FL_TRAMP_EN) { |
| 3876 | ops = ftrace_find_tramp_ops_any(rec); | 3882 | ops = ftrace_find_tramp_ops_any(rec); |
| 3877 | if (ops) { | 3883 | if (ops) { |