tracing: wprobe: x86: Add wprobe for watchpoint
Subsystems (5)
Masami Hiramatsu (Google)
maintainerv9 cover tracing: wprobe: x86: Add wprobe for watchpoint
Hi,
Here is the 9th version of the series for adding new wprobe (watch probe) which provides memory access tracing event. Moreover, this can be used via event trigger. Thus it can trace memory access on a dynamically allocated objects too.
The previous version is here:
https://lore.kernel.org/all/178417033089.209165.16717079876036408877.stgit@devnote2/
This version fixes issues commented by Sashiko[1] and add BTF typecast support [10/10]. Let me list it up.
[1] https://sashiko.dev/#/patchset/178417033089.209165.16717079876036408877.stgit%40devnote2
[PATCH 1/10]
- Rebased on probes/for-next branch.
- Add WPROBE_NO_SIBLING error log to explicitly reject sibling probes since event triggers identify the target wprobe by event name.
- Use traceprobe_parse_event_name() to properly validate group/event names instead of using the raw command string directly.
- Generate unique event name (w_0x<addr>) for anonymous address-based watchpoints to avoid naming collisions.
- Call traceprobe_update_arg() in __register_trace_wprobe() to resolve @symbol fetch arguments, consistent with kprobe and fprobe.
[PATCH 3/10]
- Fix command check logic to prevent early exit under 'set -e' (errexit) when grep or test fails.
- Simplify enable/disable status checks by removing cat pipes.
[PATCH 5/10]
- Define DR_LEN_MASK and DR_TYPE_MASK locally to eliminate magic numbers in decode_dr7() and setup_hwbp().
- Temporarily disable the active slot in setup_hwbp() before updating the address register to avoid spurious debug exceptions.
[PATCH 6/10]
- Update commit message.
[PATCH 7/10]
- Add lockdep_assert_irqs_disabled() to enforce and document the interrupt-disabled calling context requirement.
- Implement state rollback logic in modify_wide_hw_breakpoint_local() to prevent inconsistent state on architecture update failure.
[PATCH 8/10]
- Make event_trigger_free() non-static to solve build dependency.
- Sync irq_work and work inside __unregister_trace_wprobe() before clearing tw->bp_event to prevent concurrent NULL pointer dereference.
- Introduce private_free destructor in struct event_trigger_data to safely release wprobe_trigger_data after tracepoint readers exit.
- Fix filter memory leak in wprobe_trigger_cmd_parse() on the error handling path of trace_event_try_get_ref() failure.
- Avoid overwriting tw->addr on clear_wprobe trigger registration to prevent active watchpoint corruption and hardware breakpoint leak.
[PATCH 9/10]
- Drop dentry cache after removing tempfile for on-disk filesystem.
To support kprobe events, we need to check the previous NMI context or need to use an atomic refcount etc. But that should be another patch for review.
To support arm64, we need to avoid major pagefault on single-stepping issue. I think we can solve it with checking whether the address is the kernel (which must not cause a major page fault), or not.
Usage
-----
The basic usage of this wprobe is similar to other probes;
w:[GRP/][EVENT] [r|w|rw]@<ADDRESS|SYMBOL[+OFFS]> [FETCHARGS]
This defines a new wprobe event. For example, to trace jiffies update, you can do;
echo 'w:my_jiffies w@jiffies:8 value=+0($addr)' >> dynamic_events echo 1 > events/wprobes/my_jiffies/enable
Moreover, this can be combined with event trigger to trace the memory accecss on slab objects. The trigger syntax is;
set_wprobe:WPROBE_EVENT:FIELD[+OFFSET] [if FILTER] clear_wprobe:WPROBE_EVENT[:FIELD[+OFFSET]] [if FILTER]
set_wprobe sets WPROBE_EVENT's watch address on FIELD[+OFFSET]. clear_wprobe clears WPROBE_EVENT's watch address if it is set to FIELD[+OFFSET]. If FIELD is omitted, forcibly clear the watch address when trigger event is hit.
For example, trace the first 8 byte of the dentry data structure passed to do_truncate() until it is deleted by dentry_kill().
(Note: all tracefs setup uses '>>' so that it does not kick do_truncate())
# echo 'w:watch rw@0:8 address=$addr value=+0($addr)' > dynamic_events
# echo 'f:truncate do_truncate dentry=$arg2' >> dynamic_events # echo 'set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger
# echo 'f:dentry_kill dentry_kill dentry=$arg1' >> dynamic_events # echo 'clear_wprobe:watch:dentry' >> events/fprobes/dentry_kill/trigger
# echo 1 >> events/fprobes/truncate/enable # echo 1 >> events/fprobes/dentry_kill/enable
# echo aaa > /tmp/hoge # echo bbb > /tmp/hoge # echo ccc > /tmp/hoge # rm /tmp/hoge
Then, the trace data will show;
# tracer: nop
#
# entries-in-buffer/entries-written: 32/32 #P:8
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sh-107 [004] ...1. 9.990418: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618
sh-107 [004] ...1. 9.990914: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b3de78
sh-107 [004] ...1. 9.993175: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049ddd40
sh-107 [004] ..... 9.995198: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880048083a8
sh-107 [004] ...1. 9.995389: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db998
sh-107 [004] ..Zff 9.997503: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080
sh-107 [004] ..Zff 9.997509: watch: (path_openat+0x211/0xda0) address=0xffff8880048083a8 value=0x8200080
sh-107 [004] ..Zff 9.997514: watch: (path_openat+0xa56/0xda0) address=0xffff8880048083a8 value=0x8200080
sh-107 [004] ..Zff 9.997518: watch: (path_openat+0xae2/0xda0) address=0xffff8880048083a8 value=0x8200080
sh-107 [004] ..... 9.997521: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880048083a8
sh-107 [004] ...1. 9.997582: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004808270
sh-107 [004] ...1. 9.999365: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db728
sh-107 [004] ...1. 9.999388: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b1c000
rm-113 [005] ..Zff 10.000965: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.000971: watch: (path_lookupat+0x97/0x1e0) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.000984: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.000988: watch: (path_lookupat+0x97/0x1e0) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.001010: watch: (lookup_one_qstr_excl+0x28/0x140) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.001014: watch: (lookup_one_qstr_excl+0xd1/0x140) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.001018: watch: (may_delete_dentry+0x1c/0x200) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.001021: watch: (may_delete_dentry+0x195/0x200) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] ..Zff 10.001031: watch: (vfs_unlink+0x5e/0x260) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] d.Z.. 10.001067: watch: (d_make_discardable+0x1b/0x40) address=0xffff8880048083a8 value=0x8200080
rm-113 [005] d.Z.. 10.001071: watch: (d_make_discardable+0x29/0x40) address=0xffff8880048083a8 value=0x200080
rm-113 [005] ...1. 10.001072: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880048083a8
rm-113 [005] ...1. 10.001218: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880048083a8
sh-107 [004] ...1. 10.001416: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db110
sh-107 [004] ...1. 10.001444: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db248
sh-107 [004] ...1. 10.001500: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618
sh-107 [004] ...1. 10.002067: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b41e78
sh-107 [004] ...1. 10.904920: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b41e78
sh-107 [004] ...1. 10.905129: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618Thank you,
---
Jinchao Wang (2):
x86/hw_breakpoint: Unify breakpoint install/uninstall x86/hw_breakpoint: Add arch_reinstall_hw_breakpoint
Masami Hiramatsu (Google) (8):
tracing: wprobe: Add watchpoint probe event based on hardware breakpoint
x86: hw_breakpoint: Add a kconfig to clarify when a breakpoint fires
selftests: tracing: Add a basic testcase for wprobe
selftests: tracing: Add syntax testcase for wprobe
HWBP: Add modify_wide_hw_breakpoint_local() API
tracing: wprobe: Add wprobe event trigger
selftests: ftrace: Add wprobe trigger testcase tracing/wprobe: Support BTF typecast in fetchargs
Documentation/trace/index.rst | 1 Documentation/trace/wprobetrace.rst | 163 +++ arch/Kconfig | 20 arch/x86/Kconfig | 2 arch/x86/include/asm/hw_breakpoint.h | 8 arch/x86/kernel/hw_breakpoint.c | 164 ++- include/linux/hw_breakpoint.h | 6 include/linux/trace_events.h | 3 kernel/events/hw_breakpoint.c | 61 + kernel/trace/Kconfig | 24 kernel/trace/Makefile | 1 kernel/trace/trace.c | 9 kernel/trace/trace.h | 7 kernel/trace/trace_events_trigger.c | 12 kernel/trace/trace_probe.c | 24 kernel/trace/trace_probe.h | 14 kernel/trace/trace_wprobe.c | 1185 ++++++++++++++++++++ tools/testing/selftests/ftrace/config | 2 .../ftrace/test.d/dynevent/add_remove_wprobe.tc | 63 + .../test.d/dynevent/wprobes_syntax_errors.tc | 20 .../test.d/trigger/trigger-wprobe-btf-typecast.tc | 80 + .../ftrace/test.d/trigger/trigger-wprobe.tc | 70 + 22 files changed, 1868 insertions(+), 71 deletions(-) create mode 100644 Documentation/trace/wprobetrace.rst create mode 100644 kernel/trace/trace_wprobe.c create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_errors.tc create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe-btf-typecast.tc create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc
--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerPATCH v9 1/10 tracing: wprobe: Add watchpoint probe event based on hardware breakpoint
From: Masami Hiramatsu (Google) <[email protected]>
Add a new probe event for the hardware breakpoint called wprobe-event. This wprobe allows user to trace (watch) the memory access at the specified memory address.
The new syntax is;
w[:[GROUP/]EVENT] [r|w|rw]@[ADDR|SYM][:SIZE] [FETCH_ARGs]
User also can use $addr to fetch the accessed address and $value to fetch the accessed memory value (shorthand for '+0($addr)'). No other variables are supported.
For example, tracing updates of the jiffies;
/sys/kernel/tracing # echo 'w:my_jiffies w@jiffies' >> dynamic_events /sys/kernel/tracing # cat dynamic_events w:wprobes/my_jiffies w@jiffies:4
/sys/kernel/tracing # echo 1 > events/wprobes/my_jiffies/enable /sys/kernel/tracing # head -n 20 trace | tail -n 5
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
<idle>-0 [000] d.Z1. 206.547317: my_jiffies: (tick_do_update_jiffies64+0xbe/0x130)
<idle>-0 [000] d.Z1. 206.548341: my_jiffies: (tick_do_update_jiffies64+0xbe/0x130)
<idle>-0 [000] d.Z1. 206.549346: my_jiffies: (tick_do_update_jiffies64+0xbe/0x130)---
Changes in v9:
- Rebased on probes/for-next branch.
- Add WPROBE_NO_SIBLING error log to explicitly reject sibling probes since event triggers identify the target wprobe by event name.
- Use traceprobe_parse_event_name() to properly validate group/event names instead of using the raw command string directly.
- Generate unique event name (w_0x<addr>) for anonymous address-based watchpoints to avoid naming collisions.
- Call traceprobe_update_arg() in __register_trace_wprobe() to resolve @symbol fetch arguments, consistent with kprobe and fprobe.
Changes in v8:
- Include required header files.
- Use READ_ONCE(tw->addr) in trace handler to safely check dynamically updated addresses.
- Prohibit unsafe perf support by returning -EOPNOTSUPP in wprobe_register().
- Add rollback logic to unregister already-enabled sibling probes if registration fails mid-loop.
- Resolve symbol offsets dynamically in trace_wprobe_show() using kallsyms_lookup_name().
- Fix memory leak of parse_address_spec()'s symbol output in
__trace_wprobe_create().
- Print "rw" instead of "x" for read-write type breakpoints in
trace_wprobe_show().
- Document the $value fetcharg in wprobetrace.rst.
Changes in v7:
- Include IS_ERR_PCPU fix.
- use seq_print_ip_sym_offset().
- fix checkpatch warning on DEFINE_FREE()
- Use bp->attr.bp_addr instead of tw->addr because it can be updated from another CPU.
---
Documentation/trace/index.rst | 1
Documentation/trace/wprobetrace.rst | 70 +++
include/linux/trace_events.h | 2
kernel/trace/Kconfig | 13 +
kernel/trace/Makefile | 1
kernel/trace/trace.c | 9
kernel/trace/trace.h | 5
kernel/trace/trace_probe.c | 21 +
kernel/trace/trace_probe.h | 9
kernel/trace/trace_wprobe.c | 747 +++++++++++++++++++++++++++++++++++
10 files changed, 874 insertions(+), 4 deletions(-)
create mode 100644 Documentation/trace/wprobetrace.rst
create mode 100644 kernel/trace/trace_wprobe.cDocumentation/trace/index.rst +1 -0
@@ -36,6 +36,7 @@ the Linux kernel. kprobes kprobetrace fprobetrace + wprobetrace eprobetrace fprobe ring-buffer-design
Documentation/trace/wprobetrace.rst new file +70 -0
70 lines not on this page · raw message
include/linux/trace_events.h +2 -0
@@ -328,6 +328,7 @@ enum {TRACE_EVENT_FL_UPROBE_BIT ,TRACE_EVENT_FL_EPROBE_BIT ,TRACE_EVENT_FL_FPROBE_BIT , +TRACE_EVENT_FL_WPROBE_BIT ,TRACE_EVENT_FL_CUSTOM_BIT , TRACE_EVENT_FL_TEST_STR_BIT, };
@@ -358,6 +359,7 @@ enum {TRACE_EVENT_FL_UPROBE = (1 <<TRACE_EVENT_FL_UPROBE_BIT ),TRACE_EVENT_FL_EPROBE = (1 <<TRACE_EVENT_FL_EPROBE_BIT ),TRACE_EVENT_FL_FPROBE = (1 <<TRACE_EVENT_FL_FPROBE_BIT ), +TRACE_EVENT_FL_WPROBE = (1 <<TRACE_EVENT_FL_WPROBE_BIT ),TRACE_EVENT_FL_CUSTOM = (1 <<TRACE_EVENT_FL_CUSTOM_BIT ), TRACE_EVENT_FL_TEST_STR = (1 << TRACE_EVENT_FL_TEST_STR_BIT), };
kernel/trace/Kconfig +13 -0
@@ -862,6 +862,19 @@ config EPROBE_EVENTS convert the type of an event field. For example, turn an address into a string. +config WPROBE_EVENTS + bool "Enable wprobe-based dynamic events" + depends on TRACING + depends on HAVE_HW_BREAKPOINT + select PROBE_EVENTS + select DYNAMIC_EVENTS + help + This allows the user to add watchpoint tracing events based on + hardware breakpoints on the fly via the ftrace interface. + + Those events can be inserted wherever hardware breakpoints can be + set, and record accessed memory address and values. + config BPF_EVENTS depends on BPF_SYSCALL depends on (KPROBE_EVENTS || UPROBE_EVENTS) && PERF_EVENTS
kernel/trace/Makefile +1 -0
@@ -126,6 +126,7 @@ obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o obj-$(CONFIG_FPROBE) += fprobe.o obj-$(CONFIG_RETHOOK) += rethook.o obj-$(CONFIG_FPROBE_EVENTS) += trace_fprobe.o +obj-$(CONFIG_WPROBE_EVENTS) += trace_wprobe.o obj-$(CONFIG_TRACEPOINT_BENCHMARK) += trace_benchmark.o obj-$(CONFIG_RV) += rv/
kernel/trace/trace.c +8 -1
@@ -4294,8 +4294,12 @@ static const char readme_msg[] =" uprobe_events\t\t- Create/append/remove/show the userspace dynamic events\n" "\t\t\t Write into this file to define/undefine new trace events.\n"#endif +#ifdef CONFIG_WPROBE_EVENTS +" wprobe_events\t\t- Create/append/remove/show the hardware breakpoint dynamic events\n" + "\t\t\t Write into this file to define/undefine new trace events.\n" +#endif #if defined(CONFIG_KPROBE_EVENTS ) || defined(CONFIG_UPROBE_EVENTS ) || \ - defined(CONFIG_FPROBE_EVENTS ) + defined(CONFIG_FPROBE_EVENTS ) || defined(CONFIG_WPROBE_EVENTS ) "\t accepts: event-definitions (one definition per line)\n"#if defined(CONFIG_KPROBE_EVENTS ) || defined(CONFIG_UPROBE_EVENTS )"\t Format: p[:[<group>/][<event>]] <place> [<args>]\n"
@@ -4305,6 +4309,9 @@ static const char readme_msg[] ="\t f[:[<group>/][<event>]] <func-name>[%return] [<args>]\n" "\t t[:[<group>/][<event>]] <tracepoint> [<args>]\n"#endif +#ifdef CONFIG_WPROBE_EVENTS + "\t w[:[<group>/][<event>]] [r|w|rw]@<addr>[:<len>]\n" +#endif #ifdef CONFIG_HIST_TRIGGERS "\t s:[synthetic/]<event> <field> [<field>]\n"#endif
kernel/trace/trace.h +5 -0
@@ -179,6 +179,11 @@ struct fexit_trace_entry_head {unsigned long ret_ip; }; +struct wprobe_trace_entry_head { +struct trace_entry ent; +unsigned long ip; +}; +#define TRACE_BUF_SIZE 1024struct trace_array ;
kernel/trace/trace_probe.c +19 -2
@@ -1405,6 +1405,23 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t,return 0 ; } +/* wprobe only support "$addr" and "$value" variable */ +if (ctx->flags &TPARG_FL_WPROBE ) { +if (!strcmp (arg,"addr" )) { + code->op =FETCH_OP_BADDR ; +return 0 ; + } +if (!strcmp (arg,"value" )) { + code->op =FETCH_OP_BADDR ; + code++; + code->op =FETCH_OP_DEREF ; + code->offset =0 ; + *pcode = code; +return 0 ; + } + goto inval; + } +if (strcmp (arg,"comm" ) ==0 ||strcmp (arg,"COMM" ) ==0 ) { code->op =FETCH_OP_COMM ;return 0 ;
@@ -1464,8 +1481,8 @@ static int parse_probe_arg_register(char *arg, struct fetch_insn *code, {int ret; -if (ctx->flags & (TPARG_FL_TEVENT |TPARG_FL_FPROBE )) { -/* eprobe and fprobe do not handle registers */ +if (ctx->flags & (TPARG_FL_TEVENT |TPARG_FL_FPROBE |TPARG_FL_WPROBE )) { +/* eprobe, fprobe and wprobe do not handle registers */ trace_probe_log_err (ctx->offset,BAD_VAR );return -EINVAL ; }
kernel/trace/trace_probe.h +8 -1
@@ -91,6 +91,7 @@ typedef int (*print_type_func_t)(struct trace_seq *, void *, void *);FETCH_OP (STACK , param),/* Stack: .param = index */ \FETCH_OP (STACKP , none),/* Stack pointer */ \FETCH_OP (RETVAL , none),/* Return value */ \ +FETCH_OP (BADDR , none),/* Break address */ \FETCH_OP (IMM , imm),/* Immediate: .immediate */ \FETCH_OP (COMM , none),/* Current comm */ \ FETCH_OP(CURRENT, none), /* Current task_struct address */\
@@ -420,6 +421,7 @@ static inline int traceprobe_get_entry_data_size(struct trace_probe *tp)#define TPARG_FL_USER BIT(4)#define TPARG_FL_FPROBE BIT(5)#define TPARG_FL_TPOINT BIT(6) +#define TPARG_FL_WPROBE BIT(7)#define TPARG_FL_LOC_MASK GENMASK(4, 0)static inline bool tparg_is_function_entry (unsigned int flags)
@@ -546,6 +548,10 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,C (ARG_TOO_LONG ,"Argument expression is too long" ), \C (ARRAY_NO_CLOSE ,"Array is not closed" ), \C (ARRAY_TOO_BIG ,"Array number is too big" ), \ +C (BAD_ACCESS_ADDR ,"Invalid access memory address" ), \ +C (BAD_ACCESS_FMT ,"Access memory address requires @" ), \ +C (BAD_ACCESS_LEN ,"This memory access length is not supported" ), \ +C (BAD_ACCESS_TYPE ,"Bad memory access type" ), \C (BAD_ADDR_SUFFIX ,"Invalid probed address suffix" ), \C (BAD_ARG_NAME ,"Argument name must follow the same rules as C identifiers" ), \ C(BAD_ARG_NUM, "Invalid argument number"), \
@@ -628,7 +634,8 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,C (TYPECAST_NOT_EVENT ,"Typecasts are only for eprobe fields" ), \C (TYPECAST_REQ_FIELD ,"Typecast requires a field access" ), \C (TYPECAST_SYM_OFFSET ,"@SYM+/-OFFSET with typecast needs parentheses" ), \ -C (USED_ARG_NAME ,"This argument name is already used" ), +C (USED_ARG_NAME ,"This argument name is already used" ), \ +C (WPROBE_NO_SIBLING ,"Watchpoint probe does not support sibling probes" ), #undef C #define C(a, b) TP_ERR_##a
kernel/trace/trace_wprobe.c new file +747 -0
747 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 2/10 x86: hw_breakpoint: Add a kconfig to clarify when a breakpoint fires
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add CONFIG_HAVE_POST_BREAKPOINT_HOOK which indicates the hw_breakpoint on that architecture fires after the target memory has been modified. This is currently x86 only behavior.
--- arch/Kconfig | 10 ++++++++++ arch/x86/Kconfig | 1 + kernel/trace/Kconfig | 1 + 3 files changed, 12 insertions(+)
arch/Kconfig +10 -0
@@ -457,6 +457,16 @@ config HAVE_MIXED_BREAKPOINTS_REGS Select this option if your arch implements breakpoints under the latter fashion. +config HAVE_POST_BREAKPOINT_HOOK + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them provide breakpoint hook after the target memory + is modified. + Select this option if your arch implements breakpoints overflow + handler hooks after the target memory is modified. + config HAVE_USER_RETURN_NOTIFIER bool
arch/x86/Kconfig +1 -0
@@ -246,6 +246,7 @@ config X86 select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS select HAVE_HW_BREAKPOINT + select HAVE_POST_BREAKPOINT_HOOK select HAVE_IOREMAP_PROT select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_TIME_ACCOUNTING
kernel/trace/Kconfig +1 -0
@@ -866,6 +866,7 @@ config WPROBE_EVENTS bool "Enable wprobe-based dynamic events" depends on TRACING depends on HAVE_HW_BREAKPOINT + depends on HAVE_POST_BREAKPOINT_HOOK select PROBE_EVENTS select DYNAMIC_EVENTS help
Masami Hiramatsu (Google)
maintainerPATCH v9 3/10 selftests: tracing: Add a basic testcase for wprobe
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add 'add_remove_wprobe.tc' testcase for testing wprobe event that tests adding and removing operations of the wprobe event.
---
Changes in v9:
- Fix command check logic to prevent early exit under 'set -e' (errexit) when grep or test fails.
- Simplify enable/disable status checks by removing cat pipes.
Changes in v8:
- Fixed silently test failure path.
--- tools/testing/selftests/ftrace/config | 1 .../ftrace/test.d/dynevent/add_remove_wprobe.tc | 63 ++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc
tools/testing/selftests/ftrace/config +1 -0
@@ -27,3 +27,4 @@ CONFIG_STACK_TRACER=y CONFIG_TRACER_SNAPSHOT=y CONFIG_UPROBES=y CONFIG_UPROBE_EVENTS=y +CONFIG_WPROBE_EVENTS=y
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc new file +63 -0
63 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 4/10 selftests: tracing: Add syntax testcase for wprobe
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add "wprobe_syntax_errors.tc" testcase for testing syntax errors of the watch probe events.
--- .../test.d/dynevent/wprobes_syntax_errors.tc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_errors.tc
tools/testing/selftests/ftrace/test.d/dynevent/wprobes_syntax_errors.tc new file +20 -0
@@ -0,0 +1,20 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Watch probe event parser error log check +# requires: dynamic_events "w[:[<group>/][<event>]] [r|w|rw]@<addr>[:<len>]":README + +check_error() { # command-with-error-pos-by-^ + ftrace_errlog_check 'wprobe' "$1" 'dynamic_events' +} + +check_error 'w ^symbol' # BAD_ACCESS_FMT +check_error 'w ^a@symbol' # BAD_ACCESS_TYPE +check_error 'w w@^symbol' # BAD_ACCESS_ADDR +check_error 'w w@jiffies^+offset' # BAD_ACCESS_ADDR +check_error 'w w@jiffies:^100' # BAD_ACCESS_LEN +check_error 'w w@jiffies ^$arg1' # BAD_VAR +check_error 'w w@jiffies ^$retval' # BAD_VAR +check_error 'w w@jiffies ^$stack' # BAD_VAR +check_error 'w w@jiffies ^%ax' # BAD_VAR + +exit 0
Masami Hiramatsu (Google)
maintainerPATCH v9 5/10 x86/hw_breakpoint: Unify breakpoint install/uninstall
In reply to Masami Hiramatsu (Google)From: Jinchao Wang <[email protected]>
Consolidate breakpoint management to reduce code duplication. The diffstat was misleading, so the stripped code size is compared instead. After refactoring, it is reduced from 11976 bytes to 11448 bytes on my x86_64 system built with clang.
This also makes it easier to introduce arch_reinstall_hw_breakpoint().
In addition, including linux/types.h to fix a missing build dependency.
---
Changes in v8:
- Add missing barrier() on the disable path of setup_hwbp() to prevent the compiler from reordering this_cpu_write(cpu_dr7, ...) before set_debugreg(dr7, 7).
- Clear existing slot control and enable bits in cpu_dr7 inside setup_hwbp() using __encode_dr7() before setting new ones to prevent register state corruption on update/reinstall.
--- arch/x86/include/asm/hw_breakpoint.h | 6 + arch/x86/kernel/hw_breakpoint.c | 144 +++++++++++++++++++--------------- 2 files changed, 86 insertions(+), 64 deletions(-)
arch/x86/include/asm/hw_breakpoint.h +6 -0
@@ -5,6 +5,7 @@#include <uapi/asm/hw_breakpoint.h> #define __ARCH_HW_BREAKPOINT_H +#include <linux/types.h> /* * The name should probably be something dealt in
@@ -18,6 +19,11 @@ struct arch_hw_breakpoint {u8 type; }; +enum bp_slot_action { +BP_SLOT_ACTION_INSTALL , +BP_SLOT_ACTION_UNINSTALL , +}; +#include <linux/kdebug.h> #include <linux/percpu.h> #include <linux/list.h>
arch/x86/kernel/hw_breakpoint.c +80 -64
183 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 6/10 x86/hw_breakpoint: Add arch_reinstall_hw_breakpoint
In reply to Masami Hiramatsu (Google)From: Jinchao Wang <[email protected]>
The new arch_reinstall_hw_breakpoint() function can be used in an atomic context, unlike the more expensive free and re-allocation path. This allows callers to efficiently re-establish an existing breakpoint.
---
Changes in v9:
- Update commit message.
- Temporarily disable the active slot in setup_hwbp() before updating the address register to avoid spurious debug exceptions.
--- arch/x86/include/asm/hw_breakpoint.h | 2 ++ arch/x86/kernel/hw_breakpoint.c | 34 ++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-)
arch/x86/include/asm/hw_breakpoint.h +2 -0
@@ -21,6 +21,7 @@ struct arch_hw_breakpoint {enum bp_slot_action {BP_SLOT_ACTION_INSTALL , +BP_SLOT_ACTION_REINSTALL ,BP_SLOT_ACTION_UNINSTALL , };
@@ -65,6 +66,7 @@ extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,int arch_install_hw_breakpoint (struct perf_event *bp); +int arch_reinstall_hw_breakpoint (struct perf_event *bp);void arch_uninstall_hw_breakpoint (struct perf_event *bp);void hw_breakpoint_pmu_read (struct perf_event *bp);void hw_breakpoint_pmu_unthrottle (struct perf_event *bp);
arch/x86/kernel/hw_breakpoint.c +28 -6
@@ -100,6 +100,10 @@ static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action action) old_bp =NULL ; new_bp = bp;break ; +case BP_SLOT_ACTION_REINSTALL : + old_bp = bp; + new_bp = bp; +break ;case BP_SLOT_ACTION_UNINSTALL : old_bp = bp; new_bp =NULL ;
@@ -129,23 +133,36 @@ static int manage_bp_slot(struct perf_event *bp, enum bp_slot_action action)static void setup_hwbp (struct arch_hw_breakpoint *info,int slot,bool enable) {unsigned long dr7; - -set_debugreg (info->address, slot); -__this_cpu_write (cpu_debugreg[slot], info->address); +bool enabled; dr7 =this_cpu_read (cpu_dr7); + enabled = dr7 & ((DR_LOCAL_ENABLE |DR_GLOBAL_ENABLE ) << (slot *DR_ENABLE_SIZE )); dr7 &= ~(__encode_dr7 (slot,0xc ,0x3 ) | (DR_LOCAL_ENABLE << (slot *DR_ENABLE_SIZE ))); -if (enable) - dr7 |=encode_dr7 (slot, info->len, info->type); + +/* +* If the slot is currently enabled, disable it first before updating +* the address register to prevent spurious debug exceptions. +*/ +if (enable && enabled) { +barrier (); +set_debugreg (dr7,7 ); +barrier (); +this_cpu_write (cpu_dr7, dr7); + } + +set_debugreg (info->address, slot); +__this_cpu_write (cpu_debugreg[slot], info->address);/* * Enabling: * Ensure we first write cpu_dr7 before we set the DR7 register. * This ensures an NMI never see cpu_dr7 0 when DR7 is not. */ -if (enable) +if (enable) { + dr7 |=encode_dr7 (slot, info->len, info->type);this_cpu_write (cpu_dr7, dr7); + }barrier ();
@@ -189,6 +206,11 @@ int arch_install_hw_breakpoint(struct perf_event *bp)return arch_manage_bp (bp,BP_SLOT_ACTION_INSTALL ); } +int arch_reinstall_hw_breakpoint (struct perf_event *bp) +{ +return arch_manage_bp (bp,BP_SLOT_ACTION_REINSTALL ); +} +void arch_uninstall_hw_breakpoint (struct perf_event *bp) {arch_manage_bp (bp,BP_SLOT_ACTION_UNINSTALL );
Masami Hiramatsu (Google)
maintainerPATCH v9 7/10 HWBP: Add modify_wide_hw_breakpoint_local() API
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add modify_wide_hw_breakpoint_local() arch-wide interface which allows hwbp users to update watch address on-line. This is available if the arch supports CONFIG_HAVE_REINSTALL_HW_BREAKPOINT.
Note that this allows to change the type only for compatible types, because it does not release and reserve the hwbp slot based on type. For instance, you can not change HW_BREAKPOINT_W to HW_BREAKPOINT_X.
---
Changes in v9:
- Add lockdep_assert_irqs_disabled() to enforce and document the interrupt-disabled calling context requirement.
- Implement state rollback logic in modify_wide_hw_breakpoint_local() to prevent inconsistent state on architecture update failure.
Changes in v8:
- Parse new attributes into a temporary struct arch_hw_breakpoint copy to prevent in-place mutation and corruption on parse error paths.
- Synchronize logical perf_event attributes by updating bp->attr.bp_type and bp->attr.bp_len in modify_wide_hw_breakpoint_local().
Changes in v7:
- Update bp->attr.bp_attr so that we can correctly check the address on it.
- Use -EOPNOTSUPP instead of -ENOSYS.
Changes in v4:
- Update kerneldoc comment about modify_wide_hw_breakpoint_local according to Randy's comment.
Changes in v2:
- Check type compatibility by checking slot. (Thanks Jinchao!)
--- arch/Kconfig | 10 +++++++ arch/x86/Kconfig | 1 + include/linux/hw_breakpoint.h | 6 ++++ kernel/events/hw_breakpoint.c | 61 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+)
arch/Kconfig +10 -0
@@ -467,6 +467,16 @@ config HAVE_POST_BREAKPOINT_HOOK Select this option if your arch implements breakpoints overflow handler hooks after the target memory is modified. +config HAVE_REINSTALL_HW_BREAKPOINT + bool + depends on HAVE_HW_BREAKPOINT + help + Depending on the arch implementation of hardware breakpoints, + some of them are able to update the breakpoint configuration + without release and reserve the hardware breakpoint register. + What configuration is able to update depends on hardware and + software implementation. + config HAVE_USER_RETURN_NOTIFIER bool
arch/x86/Kconfig +1 -0
@@ -247,6 +247,7 @@ config X86 select HAVE_GCC_PLUGINS select HAVE_HW_BREAKPOINT select HAVE_POST_BREAKPOINT_HOOK + select HAVE_REINSTALL_HW_BREAKPOINT select HAVE_IOREMAP_PROT select HAVE_IRQ_EXIT_ON_IRQ_STACK if X86_64 select HAVE_IRQ_TIME_ACCOUNTING
include/linux/hw_breakpoint.h +6 -0
@@ -81,6 +81,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,perf_overflow_handler_t triggered, void *context); +extern int modify_wide_hw_breakpoint_local (struct perf_event *bp, +struct perf_event_attr *attr); +extern int register_perf_hw_breakpoint (struct perf_event *bp);extern void unregister_hw_breakpoint (struct perf_event *bp);extern void unregister_wide_hw_breakpoint (struct perf_event *__percpu *cpu_events);
@@ -124,6 +127,9 @@ register_wide_hw_breakpoint(struct perf_event_attr *attr,perf_overflow_handler_t triggered, void *context) { return NULL; }static inline int +modify_wide_hw_breakpoint_local (struct perf_event *bp, +struct perf_event_attr *attr) {return -EOPNOTSUPP ; } +static inline int register_perf_hw_breakpoint (struct perf_event *bp) {return -ENOSYS ; }static inline void unregister_hw_breakpoint (struct perf_event *bp) { } static inline void
kernel/events/hw_breakpoint.c +61 -0
67 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 8/10 tracing: wprobe: Add wprobe event trigger
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add wprobe event trigger to set and clear the watch event dynamically. This allows us to set an watchpoint on a given local variables and a slab object instead of static objects.
The trigger syntax is below:
- set_wprobe:WPROBE:FIELD[+OFFSET] [if FILTER]
- clear_wprobe:WPROBE[:FIELD[+OFFSET]] [if FILTER]
set_wprobe sets the address pointed by FIELD[+offset] to the WPROBE event. The FIELD is the field name of trigger event. clear_wprobe clears the watch address of WPROBE event. If the FIELD option is specified, it clears only if the current watch address is same as the given FIELD[+OFFSET] value.
The set_wprobe trigger does not change the type and length, these must be set when creating a new wprobe.
Also, the WPROBE event must be disabled when setting the new trigger and it will be busy afterwards. Recommended usage is to add a new wprobe at NULL address and keep disabled.
---
Changes in v9:
- Make event_trigger_free() non-static to solve build dependency.
- Sync irq_work and work inside __unregister_trace_wprobe() before
clearing tw->bp_event to prevent concurrent NULL pointer dereference.
- Introduce private_free destructor in struct event_trigger_data to
safely release wprobe_trigger_data after tracepoint readers exit.
- Fix filter memory leak in wprobe_trigger_cmd_parse() on the error
handling path of trace_event_try_get_ref() failure.
- Avoid overwriting tw->addr on clear_wprobe trigger registration to
prevent active watchpoint corruption and hardware breakpoint leak.
Changes in v8:
- Redesign wprobe_trigger() to be safe in NMI/hardirq contexts by
deferring register updates to a workqueue via irq_work.
- Skip trigger execution and increment an atomic missed count
(tw->missed) if the tracepoint runs in NMI context to prevent
recursive spinlock deadlocks. (this is currently hidden counter)
- Prohibit attaching wprobe triggers to kprobe_events by checking
TRACE_EVENT_FL_KPROBE in wprobe_trigger_cmd_parse().
- Use call_rcu() in trigger deactivation path and add
synchronize_rcu() in parse failure path to ensure safe RCU
lifetime cleanup.
- Acquire the target tracepoint's module reference via
trace_event_try_get_ref() to prevent module refcount underflows.
- Fix event_trigger_data memory leak by properly freeing the
initial refcount in wprobe_trigger_cmd_parse() on success.
- Call on_each_cpu() with wait=true in wprobe_work_func() to
prevent use-after-free during trigger unregistration.
- Synchronize concurrent wprobe triggers on the same event by
using a shared raw spinlock (tw->lock).
- Drop the support of kprobe events (that should be done later).
Changes in v7:
- Use kzalloc_obj().
- Update sample code in document.
Changes in v6:
- Update according to the latest change of trigger ops.
Changes in v5:
- Following the suggestions, the documentation was revised to suit rst.
Changes in v3:
- Add FIELD option support for clear_wprobe and update document.
- Fix to unregister/free event_trigger_data on file correctly.
- Fix syntax comments.
Changes in v2:
- Getting local cpu perf_event from trace_wprobe directly.
- Remove trace_wprobe_local_perf() because it is conditionally unused.
- Make CONFIG_WPROBE_TRIGGERS a hidden config.
---
Documentation/trace/wprobetrace.rst | 93 +++++++
include/linux/trace_events.h | 1
kernel/trace/Kconfig | 10 +
kernel/trace/trace.h | 2
kernel/trace/trace_events_trigger.c | 12 +
kernel/trace/trace_wprobe.c | 438 +++++++++++++++++++++++++++++++++++
6 files changed, 553 insertions(+), 3 deletions(-)Documentation/trace/wprobetrace.rst +93 -0
@@ -68,3 +68,96 @@ Here is an example to add a wprobe event on a variable `jiffies`. <idle>-0 [000] d.Z1. 717.026373: my_jiffies: (tick_do_update_jiffies64+0xbe/0x130) You can see the code which writes to `jiffies` is `tick_do_update_jiffies64()`. + +Combination with trigger action +------------------------------- +The event trigger action can extend the utilization of this wprobe. + +- set_wprobe:WPEVENT:FIELD[+|-ADJUST] +- clear_wprobe:WPEVENT[:FIELD[+|-]ADJUST] + +Set these triggers to the target event, then the WPROBE event will be +setup to trace the memory access at FIELD[+|-ADJUST] address. +When clear_wprobe is hit, if FIELD is NOT specified, the WPEVENT is +forcibly cleared. If FIELD[[+|-]ADJUST] is set, it clears WPEVENT only +if its watching address is the same as the FIELD[[+|-]ADJUST] value. + +Notes: +The set_wprobe trigger does not change the type and length, these +must be set when creating a new wprobe. + +The WPROBE event must be disabled when setting the new trigger +and it will be busy afterwards. Recommended usage is to add a new +wprobe at NULL address and keep disabled. + +Wprobe triggers are not supported on kprobe_events, because kprobes +themselves can use software breakpoints which conflicts with wprobe +operation. + + +For example, trace the first 8 bytes of the dentry data structure passed +to do_truncate() until it is deleted by dentry_kill(). +(Note: all tracefs setup uses '>>' so that it does not kick do_truncate()) +:: + + # echo 'w:watch rw@0:8 address=$addr value=+0($addr)' >> dynamic_events + # echo 'f:truncate do_truncate dentry=$arg2' >> dynamic_events + # echo 'set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger + # echo 'f:dentry_kill dentry_kill dentry=$arg1' >> dynamic_events + # echo 'clear_wprobe:watch:dentry' >> events/fprobes/dentry_kill/trigger + # echo 1 >> events/fprobes/truncate/enable + # echo 1 >> events/fprobes/dentry_kill/enable + + # echo aaa > /tmp/hoge + # echo bbb > /tmp/hoge + # echo ccc > /tmp/hoge + # rm /tmp/hoge + +Then, the trace data will show:: + + # tracer: nop + # + # entries-in-buffer/entries-written: 32/32 #P:8 + # + # _-----=> irqs-off/BH-disabled + # / _----=> need-resched + # | / _---=> hardirq/softirq + # || / _--=> preempt-depth + # ||| / _-=> migrate-disable + # |||| / delay + # TASK-PID CPU# ||||| TIMESTAMP FUNCTION + # | | | ||||| | | + sh-107 [004] ...1. 9.990418: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618 + sh-107 [004] ...1. 9.990914: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b3de78 + sh-107 [004] ...1. 9.993175: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049ddd40 + sh-107 [004] ..... 9.995198: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880048083a8 + sh-107 [004] ...1. 9.995389: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db998 + sh-107 [004] ..Zff 9.997503: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080 + sh-107 [004] ..Zff 9.997509: watch: (path_openat+0x211/0xda0) address=0xffff8880048083a8 value=0x8200080 + sh-107 [004] ..Zff 9.997514: watch: (path_openat+0xa56/0xda0) address=0xffff8880048083a8 value=0x8200080 + sh-107 [004] ..Zff 9.997518: watch: (path_openat+0xae2/0xda0) address=0xffff8880048083a8 value=0x8200080 + sh-107 [004] ..... 9.997521: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880048083a8 + sh-107 [004] ...1. 9.997582: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004808270 + sh-107 [004] ...1. 9.999365: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db728 + sh-107 [004] ...1. 9.999388: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b1c000 + rm-113 [005] ..Zff 10.000965: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.000971: watch: (path_lookupat+0x97/0x1e0) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.000984: watch: (lookup_fast+0xaa/0x150) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.000988: watch: (path_lookupat+0x97/0x1e0) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.001010: watch: (lookup_one_qstr_excl+0x28/0x140) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.001014: watch: (lookup_one_qstr_excl+0xd1/0x140) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.001018: watch: (may_delete_dentry+0x1c/0x200) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.001021: watch: (may_delete_dentry+0x195/0x200) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] ..Zff 10.001031: watch: (vfs_unlink+0x5e/0x260) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] d.Z.. 10.001067: watch: (d_make_discardable+0x1b/0x40) address=0xffff8880048083a8 value=0x8200080 + rm-113 [005] d.Z.. 10.001071: watch: (d_make_discardable+0x29/0x40) address=0xffff8880048083a8 value=0x200080 + rm-113 [005] ...1. 10.001072: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880048083a8 + rm-113 [005] ...1. 10.001218: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880048083a8 + sh-107 [004] ...1. 10.001416: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db110 + sh-107 [004] ...1. 10.001444: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff8880049db248 + sh-107 [004] ...1. 10.001500: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618 + sh-107 [004] ...1. 10.002067: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b41e78 + sh-107 [004] ...1. 10.904920: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004b41e78 + sh-107 [004] ...1. 10.905129: dentry_kill: (dentry_kill+0x0/0x2c0) dentry=0xffff888004ad6618 + +You can see the watch event is correctly configured on the dentry.
include/linux/trace_events.h +1 -0
@@ -729,6 +729,7 @@ enum event_trigger_type {ETT_EVENT_HIST = (1 <<4 ),ETT_HIST_ENABLE = (1 <<5 ),ETT_EVENT_EPROBE = (1 <<6 ), + ETT_EVENT_WPROBE = (1 << 7), };extern int filter_match_preds (struct event_filter *filter,void *rec);
kernel/trace/Kconfig +10 -0
@@ -876,6 +876,16 @@ config WPROBE_EVENTS Those events can be inserted wherever hardware breakpoints can be set, and record accessed memory address and values. +config WPROBE_TRIGGERS + depends on WPROBE_EVENTS + depends on HAVE_REINSTALL_HW_BREAKPOINT + bool + default y + help + This adds an event trigger which will set the wprobe on a specific + field of an event. This allows user to trace the memory access of + an address pointed by the event field. + config BPF_EVENTS depends on BPF_SYSCALL depends on (KPROBE_EVENTS || UPROBE_EVENTS) && PERF_EVENTS
kernel/trace/trace.h +2 -0
@@ -1939,6 +1939,7 @@ struct event_trigger_data {struct event_filter __rcu *filter;char *filter_str;void *private_data; +void (*private_free)(void *private_data);bool paused;bool paused_tmp;struct list_head list;
@@ -1982,6 +1983,7 @@ trigger_data_alloc(struct event_command *cmd_ops, char *cmd, char *param, void *private_data);extern void trigger_data_free (struct event_trigger_data *data);extern int event_trigger_init (struct event_trigger_data *data); +extern void event_trigger_free (struct event_trigger_data *data);extern int trace_event_trigger_enable_disable (struct trace_event_file *file,int trigger_enable);extern void update_cond_flag (struct trace_event_file *file);
kernel/trace/trace_events_trigger.c +9 -3
@@ -51,8 +51,11 @@ static void trigger_data_free_queued_locked(void)tracepoint_synchronize_unregister (); -llist_for_each_entry_safe (data, tmp, llnodes, llist) +llist_for_each_entry_safe (data, tmp, llnodes, llist) { +if (data->private_free) + data->private_free (data->private_data);kfree (data); + } }/* Bulk garbage collection of event_trigger_data elements */
@@ -74,8 +77,11 @@ static int trigger_kthread_fn(void *ignore)/* make sure current triggers exit before free */ tracepoint_synchronize_unregister (); -llist_for_each_entry_safe (data, tmp, llnodes, llist) +llist_for_each_entry_safe (data, tmp, llnodes, llist) { +if (data->private_free) + data->private_free (data->private_data);kfree (data); + } }return 0 ;
@@ -582,7 +588,7 @@ int event_trigger_init(struct event_trigger_data *data)* Usually used directly as the @free method in event trigger * implementations. */ -static void +void event_trigger_free (struct event_trigger_data *data) {if (WARN_ON_ONCE (data->ref <=0 ))
kernel/trace/trace_wprobe.c +438 -0
485 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 9/10 selftests: ftrace: Add wprobe trigger testcase
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Add a testcase for checking wprobe trigger. This sets set_wprobe and clear_wprobe triggers on fprobe events to watch dentry access. So this depends on both wprobe and fprobe.
---
Changes in v9:
- Drop dentry cache after removing tempfile for on-disk filesystem.
Changes in v8:
- Use TMPDIR for making a test file.
- Ensure the fprobe and target functions exists, if not, exit
with UNRESOLVED (== skip). Changes in v7: - Add a newline at the end of file.(style fix) - Use dentry_kill instead of __dentry_kill. Changes in v5: - Enable CONFIG_WPROBE_TRIGGERS in the config for ftrace test. Changes in v3: - Newly added. --- tools/testing/selftests/ftrace/config | 1 .../ftrace/test.d/trigger/trigger-wprobe.tc | 70 ++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc
tools/testing/selftests/ftrace/config +1 -0
@@ -28,3 +28,4 @@ CONFIG_TRACER_SNAPSHOT=y CONFIG_UPROBES=y CONFIG_UPROBE_EVENTS=y CONFIG_WPROBE_EVENTS=y +CONFIG_WPROBE_TRIGGERS=y
tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc new file +70 -0
70 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v9 10/10 tracing/wprobe: Support BTF typecast in fetchargs
In reply to Masami Hiramatsu (Google)From: Masami Hiramatsu (Google) <[email protected]>
Allow BTF typecast syntax (STRUCT)FETCHARG->MEMBER in wprobe event fetchargs. Previously, handle_typecast() rejected any probe context that was not a function entry/return or tracepoint event probe.
Wprobe events use (the accessed address) and (the value at that address). By enabling BTF typecast, users can now cast these to a concrete struct type and access its fields directly. For example:
echo 'w:watch rw@0:8 dflag=(struct dentry)$addr->d_flags' >> dynamic_events
With a set_wprobe trigger pointing the watchpoint at a dentry address, the resulting trace shows d_flags being accessed at that location.
Note that and are restricted to kernel-space memory, which is consistent with the existing TPARG_FL_KERNEL flag used when parsing wprobe fetchargs.
---
Changes in v9:
- Newly added.
--- kernel/trace/trace_probe.c | 3 + kernel/trace/trace_probe.h | 5 + .../test.d/trigger/trigger-wprobe-btf-typecast.tc | 80 ++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe-btf-typecast.tc
kernel/trace/trace_probe.c +2 -1
@@ -967,7 +967,8 @@ static int handle_typecast(char *arg, struct traceprobe_parse_context *ctx)if (!(tparg_is_event_probe (ctx->flags) ||tparg_is_function_entry (ctx->flags) || -tparg_is_function_return (ctx->flags))) { +tparg_is_function_return (ctx->flags) || +tparg_is_wprobe (ctx->flags))) {trace_probe_log_err (ctx->offset,NOSUP_BTFARG );return -EOPNOTSUPP ; }
kernel/trace/trace_probe.h +5 -0
@@ -439,6 +439,11 @@ static inline bool tparg_is_event_probe(unsigned int flags)return !!(flags &TPARG_FL_TEVENT ); } +static inline bool tparg_is_wprobe (unsigned int flags) +{ +return !!(flags &TPARG_FL_WPROBE ); +} +/* Each typecast consumes nested level. So the max number of typecast is 8. */ #define TRACEPROBE_MAX_NESTED_LEVEL 8
tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe-btf-typecast.tc new file +80 -0
80 lines not on this page · raw message
Borislav Petkov
maintainerHi,
Quoted 1 line · Masami Hiramatsu (Google)
Here is the 9th version of the series for adding new wprobe (watch probe)
any chance you won't blast out your patchset every day?
Thx.
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteMasami Hiramatsu (Google)
maintainerQuoted 6 lines · On Fri, 17 Jul 2026 09:28:57 -0700 Borislav Petkov <[email protected]> wrote:
Hi,
Quoted 1 line · Masami Hiramatsu (Google)
Here is the 9th version of the series for adding new wprobe (watch probe)
any chance you won't blast out your patchset every day?
Ah, sorry about that. I'll rate limit myself this weekend on this series. (I hope I have fixed most of major issues...)
Thanks,
Quoted 8 lines
Thx.
Signature · 4 lines
Regards/Gruss, Boris. https://people.kernel.org/tglx/notes-about-netiquette
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Borislav Petkov
maintainerQuoted 11 lines · Masami Hiramatsu
Quoted 6 lines · On Fri, 17 Jul 2026 09:28:57 -0700 Borislav Petkov <[email protected]> wrote:
Hi,
Quoted 1 line · Masami Hiramatsu (Google)
Here is the 9th version of the series for adding new wprobe (watch probe)
any chance you won't blast out your patchset every day?
Ah, sorry about that. I'll rate limit myself this weekend on this series.
Once a week is perfectly fine.
Signature · 1 line
Small device. Typos and formatting crap