tracing: wprobe: Add wprobe for watchpoint
Subsystems (5)
Masami Hiramatsu (Google)
maintainerv3 cover tracing: wprobe: Add wprobe for watchpoint
Hi,
Here is the 3rd 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/175708427997.64001.11661389635019507808.stgit@devnote2/
In this version, I fixed to enclose the register-API dependent code in trace_probe.c with CONFIG_HAVE_FUNCTION_ARG_ACCESS_API, since CONFIG_WPROBE_EVENTS does not depend on that config but enables common CONFIG_PROBE_EVENTS[1/7]. And fixed event_trigger_data memory leak bug and add FIELD option to clear_wprobe trigger[4/7]. Also, fixed a syntax test error[6/7] and add a new test case for event trigger [7/7].
I'm using Jinchao's arch_reinstall_hw_breakpoint patch[1], but since Peter pointed that is almost a copy of arch_install_hw_breakpoint, it will be updated later.
[1] https://lore.kernel.org/all/[email protected]/
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: 16/16 #P:8
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
sh-113 [004] ..... 6.467444: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880044f0fd8
sh-113 [004] ..Zff 6.468534: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080
sh-113 [004] ..Zff 6.468542: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080
sh-113 [004] ..Zff 6.468547: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080
sh-113 [004] ..Zff 6.468553: watch: (path_openat+0xb3a/0xe70) address=0xffff8880044f0fd8 value=0x200080
sh-113 [004] ..Zff 6.468557: watch: (path_openat+0xb9a/0xe70) address=0xffff8880044f0fd8 value=0x200080
sh-113 [004] ..... 6.468563: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880044f0fd8
sh-113 [004] ...1. 6.469826: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0ea0
sh-113 [004] ...1. 6.469859: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0d68
rm-118 [001] ..Zff 6.472360: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472366: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472370: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472386: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472390: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472394: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472415: watch: (lookup_one_qstr_excl+0x2c/0x150) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472419: watch: (lookup_one_qstr_excl+0xd5/0x150) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472424: watch: (may_delete+0x18/0x200) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472428: watch: (may_delete+0x194/0x200) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] ..Zff 6.472446: watch: (vfs_unlink+0x63/0x1c0) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] d.Z.. 6.472528: watch: (dont_mount+0x19/0x30) address=0xffff8880044f0fd8 value=0x200180
rm-118 [001] ..Zff 6.472533: watch: (vfs_unlink+0x11a/0x1c0) address=0xffff8880044f0fd8 value=0x200180
rm-118 [001] ..Zff 6.472538: watch: (vfs_unlink+0x12e/0x1c0) address=0xffff8880044f0fd8 value=0x200180
rm-118 [001] d.Z1. 6.472543: watch: (d_delete+0x61/0xa0) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] d.Z1. 6.472547: watch: (dentry_unlink_inode+0x14/0x110) address=0xffff8880044f0fd8 value=0x200080
rm-118 [001] d.Z1. 6.472551: watch: (dentry_unlink_inode+0x1e/0x110) address=0xffff8880044f0fd8 value=0x80
rm-118 [001] d.Z.. 6.472563: watch: (fast_dput+0x8d/0x120) address=0xffff8880044f0fd8 value=0x80
rm-118 [001] ...1. 6.472567: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0fd8
sh-113 [004] ...2. 6.473049: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff888006e383a8Thank you,
---
Jinchao Wang (1):
x86/HWBP: introduce arch_reinstall_hw_breakpoint() for atomic context
Masami Hiramatsu (Google) (6):
tracing: wprobe: Add watchpoint probe event based on hardware breakpoint
HWBP: Add modify_wide_hw_breakpoint_local() API
tracing: wprobe: Add wprobe event trigger
selftests: tracing: Add a basic testcase for wprobe
selftests: tracing: Add syntax testcase for wprobe
selftests: ftrace: Add wprobe trigger testcase
Documentation/trace/index.rst | 1 Documentation/trace/wprobetrace.rst | 157 +++ arch/Kconfig | 10 arch/x86/Kconfig | 1 arch/x86/include/asm/hw_breakpoint.h | 1 arch/x86/kernel/hw_breakpoint.c | 50 + include/linux/hw_breakpoint.h | 6 include/linux/trace_events.h | 3 kernel/events/hw_breakpoint.c | 36 + kernel/trace/Kconfig | 24 kernel/trace/Makefile | 1 kernel/trace/trace.c | 9 kernel/trace/trace.h | 5 kernel/trace/trace_probe.c | 22 kernel/trace/trace_probe.h | 8 kernel/trace/trace_wprobe.c | 1115 ++++++++++++++++++++ .../ftrace/test.d/dynevent/add_remove_wprobe.tc | 68 + .../test.d/dynevent/wprobes_syntax_errors.tc | 20 .../ftrace/test.d/trigger/trigger-wprobe.tc | 48 + 19 files changed, 1582 insertions(+), 3 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.tc
--
Masami Hiramatsu (Google) <[email protected]>
Masami Hiramatsu (Google)
maintainerPATCH v3 1/7 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. But no other variables are supported. To record updated value, use '+0($addr)'.
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 v3:
- Enclose the register-API dependent code in trace_probe.c with CONFIG_HAVE_FUNCTION_ARG_ACCESS_API.
--- Documentation/trace/index.rst | 1 Documentation/trace/wprobetrace.rst | 69 ++++ include/linux/trace_events.h | 2 kernel/trace/Kconfig | 14 + kernel/trace/Makefile | 1 kernel/trace/trace.c | 9 kernel/trace/trace.h | 5 kernel/trace/trace_probe.c | 22 + kernel/trace/trace_probe.h | 8 kernel/trace/trace_wprobe.c | 685 +++++++++++++++++++++++++++++++++++ 10 files changed, 813 insertions(+), 3 deletions(-) create mode 100644 Documentation/trace/wprobetrace.rst create mode 100644 kernel/trace/trace_wprobe.c
Documentation/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 +69 -0
69 lines not on this page · raw message
include/linux/trace_events.h +2 -0
@@ -324,6 +324,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, };
@@ -354,6 +355,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 +14 -0
@@ -807,6 +807,20 @@ 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 + default y + 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 various register and memory values. + config BPF_EVENTS depends on BPF_SYSCALL depends on (KPROBE_EVENTS || UPROBE_EVENTS) && PERF_EVENTS
kernel/trace/Makefile +1 -0
@@ -106,6 +106,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
@@ -5506,8 +5506,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"
@@ -5517,6 +5521,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
@@ -173,6 +173,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 +21 -1
@@ -968,6 +968,24 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, goto inval; } +/* 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 ; + } + err =TP_ERR_BAD_VAR ; + goto inval; + } +if (str_has_prefix (arg,"retval" )) {if (!(ctx->flags &TPARG_FL_RETURN )) { err =TP_ERR_RETVAL_ON_PROBE ;
@@ -1097,8 +1115,9 @@ parse_probe_arg(char *arg, const struct fetch_type *type, ret =parse_probe_vars (arg, type, pcode, end, ctx);break ; +#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API case '%' :/* named register */ -if (ctx->flags & (TPARG_FL_TEVENT |TPARG_FL_FPROBE )) { +if (ctx->flags & (TPARG_FL_TEVENT |TPARG_FL_FPROBE |TPARG_FL_WPROBE )) {/* eprobe and fprobe do not handle registers */ trace_probe_log_err (ctx->offset,BAD_VAR );break ;
@@ -1111,6 +1130,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, } elsetrace_probe_log_err (ctx->offset,BAD_REG_NAME );break ; +#endif case '@' :/* memory, file-offset or symbol */ if (isdigit (arg[1 ])) {
kernel/trace/trace_probe.h +7 -1
@@ -89,6 +89,7 @@ enum fetch_op {FETCH_OP_STACK ,/* Stack : .param = index */ FETCH_OP_STACKP ,/* Stack pointer */ FETCH_OP_RETVAL ,/* Return value */ +FETCH_OP_BADDR ,/* Break address */ FETCH_OP_IMM ,/* Immediate : .immediate */ FETCH_OP_COMM ,/* Current comm */ FETCH_OP_ARG, /* Function argument : .param */
@@ -396,6 +397,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)
@@ -556,7 +558,11 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,C (BAD_TYPE4STR ,"This type does not fit for string." ),\C (NEED_STRING_TYPE ,"$comm and immediate-string only accepts string type" ),\C (TOO_MANY_ARGS ,"Too many arguments are specified" ), \ -C (TOO_MANY_EARGS ,"Too many entry arguments specified" ), +C (TOO_MANY_EARGS ,"Too many entry arguments specified" ), \ +C (BAD_ACCESS_FMT ,"Access memory address requires @" ), \ +C (BAD_ACCESS_TYPE ,"Bad memory access type" ), \ +C (BAD_ACCESS_LEN ,"This memory access length is not supported" ), \ +C (BAD_ACCESS_ADDR ,"Invalid access memory address" ), #undef C #define C(a, b) TP_ERR_##a
kernel/trace/trace_wprobe.c new file +685 -0
685 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v3 2/7 x86/HWBP: introduce arch_reinstall_hw_breakpoint() for atomic context
In reply to Masami Hiramatsu (Google)From: Jinchao Wang <[email protected]>
Introduce arch_reinstall_hw_breakpoint() to update hardware breakpoint parameters (address, length, type) without freeing and reallocating the debug register slot.
This allows atomic updates in contexts where memory allocation is not permitted, such as kprobe handlers.
--- arch/x86/include/asm/hw_breakpoint.h | 1 + arch/x86/kernel/hw_breakpoint.c | 50 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+)
arch/x86/include/asm/hw_breakpoint.h +1 -0
@@ -59,6 +59,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 +50 -0
56 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v3 3/7 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 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 | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+)
arch/Kconfig +10 -0
@@ -418,6 +418,16 @@ config HAVE_MIXED_BREAKPOINTS_REGS Select this option if your arch implements breakpoints under the latter fashion. +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_FUNCTION_TRACER select HAVE_GCC_PLUGINS select HAVE_HW_BREAKPOINT + 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 -ENOSYS ; } +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 +36 -0
@@ -887,6 +887,42 @@ void unregister_wide_hw_breakpoint(struct perf_event * __percpu *cpu_events) }EXPORT_SYMBOL_GPL (unregister_wide_hw_breakpoint); +/** +* modify_wide_hw_breakpoint_local - update breakpoint config for local cpu +* @bp: the hwbp perf event for this cpu +* @attr: the new attribute for @bp +* +* This does not release and reserve the slot of HWBP, just reuse the current +* slot on local CPU. So the users must update the other CPUs by themselves. +* Also, since this does not release/reserve the slot, this can not change the +* type to incompatible type of the HWBP. +* Return err if attr is invalid or the cpu fails to update debug register +* for new @attr. +*/ +#ifdef CONFIG_HAVE_REINSTALL_HW_BREAKPOINT +int modify_wide_hw_breakpoint_local (struct perf_event *bp, +struct perf_event_attr *attr) +{ +int ret; + +if (find_slot_idx (bp->attr.bp_type) !=find_slot_idx (attr->bp_type)) +return -EINVAL ; + + ret =hw_breakpoint_arch_parse (bp, attr,counter_arch_bp (bp)); +if (ret) +return ret; + +return arch_reinstall_hw_breakpoint (bp); +} +#else +int modify_wide_hw_breakpoint_local (struct perf_event *bp, +struct perf_event_attr *attr) +{ +return -EOPNOTSUPP ; +} +#endif +EXPORT_SYMBOL_GPL (modify_wide_hw_breakpoint_local); +/** * hw_breakpoint_is_used - check if breakpoints are currently used *
Masami Hiramatsu (Google)
maintainerPATCH v3 4/7 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 do not change type and length. That should be set when a new wprobe is created.
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 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 | 88 +++++++ include/linux/trace_events.h | 1 kernel/trace/Kconfig | 10 + kernel/trace/trace_wprobe.c | 430 +++++++++++++++++++++++++++++++++++ 4 files changed, 529 insertions(+)
Documentation/trace/wprobetrace.rst +88 -0
@@ -67,3 +67,91 @@ 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 `do_timer()`. + +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 do not change type and length. That should be +set when a new wprobe is created. + +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. + + +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: 16/16 #P:8 +# +# _-----=> irqs-off/BH-disabled +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / _-=> migrate-disable +# |||| / delay +# TASK-PID CPU# ||||| TIMESTAMP FUNCTION +# | | | ||||| | | + sh-113 [004] ..... 6.467444: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880044f0fd8 + sh-113 [004] ..Zff 6.468534: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080 + sh-113 [004] ..Zff 6.468542: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080 + sh-113 [004] ..Zff 6.468547: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080 + sh-113 [004] ..Zff 6.468553: watch: (path_openat+0xb3a/0xe70) address=0xffff8880044f0fd8 value=0x200080 + sh-113 [004] ..Zff 6.468557: watch: (path_openat+0xb9a/0xe70) address=0xffff8880044f0fd8 value=0x200080 + sh-113 [004] ..... 6.468563: truncate: (do_truncate+0x4/0x120) dentry=0xffff8880044f0fd8 + sh-113 [004] ...1. 6.469826: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0ea0 + sh-113 [004] ...1. 6.469859: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0d68 + rm-118 [001] ..Zff 6.472360: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472366: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472370: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472386: watch: (lookup_fast+0xaa/0x150) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472390: watch: (step_into+0x82/0x360) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472394: watch: (step_into+0x9f/0x360) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472415: watch: (lookup_one_qstr_excl+0x2c/0x150) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472419: watch: (lookup_one_qstr_excl+0xd5/0x150) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472424: watch: (may_delete+0x18/0x200) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472428: watch: (may_delete+0x194/0x200) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] ..Zff 6.472446: watch: (vfs_unlink+0x63/0x1c0) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] d.Z.. 6.472528: watch: (dont_mount+0x19/0x30) address=0xffff8880044f0fd8 value=0x200180 + rm-118 [001] ..Zff 6.472533: watch: (vfs_unlink+0x11a/0x1c0) address=0xffff8880044f0fd8 value=0x200180 + rm-118 [001] ..Zff 6.472538: watch: (vfs_unlink+0x12e/0x1c0) address=0xffff8880044f0fd8 value=0x200180 + rm-118 [001] d.Z1. 6.472543: watch: (d_delete+0x61/0xa0) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] d.Z1. 6.472547: watch: (dentry_unlink_inode+0x14/0x110) address=0xffff8880044f0fd8 value=0x200080 + rm-118 [001] d.Z1. 6.472551: watch: (dentry_unlink_inode+0x1e/0x110) address=0xffff8880044f0fd8 value=0x80 + rm-118 [001] d.Z.. 6.472563: watch: (fast_dput+0x8d/0x120) address=0xffff8880044f0fd8 value=0x80 + rm-118 [001] ...1. 6.472567: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff8880044f0fd8 + sh-113 [004] ...2. 6.473049: dentry_kill: (__dentry_kill+0x0/0x220) dentry=0xffff888006e383a8 + +You can see the watch event is correctly configured on the dentry.
include/linux/trace_events.h +1 -0
@@ -720,6 +720,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
@@ -821,6 +821,16 @@ config WPROBE_EVENTS Those events can be inserted wherever hardware breakpoints can be set, and record various register and memory 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_wprobe.c +430 -0
450 lines not on this page · raw message
Masami Hiramatsu (Google)
maintainerPATCH v3 5/7 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.
--- .../ftrace/test.d/dynevent/add_remove_wprobe.tc | 68 ++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc
tools/testing/selftests/ftrace/test.d/dynevent/add_remove_wprobe.tc new file +68 -0
@@ -0,0 +1,68 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Generic dynamic event - add/remove wprobe events +# requires: dynamic_events "w[:[<group>/][<event>]] [r|w|rw]@<addr>[:<len>]":README + +echo 0 > events/enable +echo > dynamic_events + +# Use jiffies as a variable that is frequently written to. +TARGET=jiffies + +echo "w:my_wprobe w@$TARGET" >> dynamic_events + +grep -q my_wprobe dynamic_events +if [ $? -ne 0 ]; then + echo "Failed to create wprobe event" + exit_fail +fi + +test -d events/wprobes/my_wprobe +if [ $? -ne 0 ]; then + echo "Failed to create wprobe event directory" + exit_fail +fi + +echo 1 > events/wprobes/my_wprobe/enable + +# Check if the event is enabled +cat events/wprobes/my_wprobe/enable | grep -q 1 +if [ $? -ne 0 ]; then + echo "Failed to enable wprobe event" + exit_fail +fi + +# Let some time pass to trigger the breakpoint +sleep 1 + +# Check if we got any trace output +if !grep -q my_wprobe trace; then + echo "wprobe event was not triggered" +fi + +echo 0 > events/wprobes/my_wprobe/enable + +# Check if the event is disabled +cat events/wprobes/my_wprobe/enable | grep -q 0 +if [ $? -ne 0 ]; then + echo "Failed to disable wprobe event" + exit_fail +fi + +echo "-:my_wprobe" >> dynamic_events + +! grep -q my_wprobe dynamic_events +if [ $? -ne 0 ]; then + echo "Failed to remove wprobe event" + exit_fail +fi + +! test -d events/wprobes/my_wprobe +if [ $? -ne 0 ]; then + echo "Failed to remove wprobe event directory" + exit_fail +fi + +clear_trace + +exit 0
Masami Hiramatsu (Google)
maintainerPATCH v3 6/7 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.
---
Changes in v3:
- Fix to use "wprobe" prefix for detecting error log.
- Fix an error place with wrong offset.
--- .../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 v3 7/7 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 v3:
- Newly added.
--- .../ftrace/test.d/trigger/trigger-wprobe.tc | 48 ++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc
tools/testing/selftests/ftrace/test.d/trigger/trigger-wprobe.tc new file +48 -0
@@ -0,0 +1,48 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: event trigger - test wprobe trigger +# requires: dynamic_events "w[:[<group>/][<event>]] [r|w|rw]@<addr>[:<len>]":README events/sched/sched_process_fork/trigger + +echo 0 > tracing_on + +:;: "Add a wprobe event used by trigger" ;: +echo 'w:watch rw@0:8 address=$addr value=+0($addr)' > dynamic_events + +:;: "Add events for triggering wprobe" ;: +echo 'f:truncate do_truncate dentry=$arg2' >> dynamic_events +echo 'f:dentry_kill __dentry_kill dentry=$arg1' >> dynamic_events + +:;: "Add wprobe triggers" ;: +echo 'set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger +echo 'clear_wprobe:watch:dentry' >> events/fprobes/dentry_kill/trigger +cat events/fprobes/truncate/trigger | grep ^set_wprobe +cat events/fprobes/dentry_kill/trigger | grep ^clear_wprobe + +:;: "Ensure wprobe is still disabled" ;: +cat events/wprobes/watch/enable | grep 0 + +:;: "Enable events for triggers" ;: +echo 1 >> events/fprobes/truncate/enable +echo 1 >> events/fprobes/dentry_kill/enable + +:;: "Start test workload" ;: +echo 1 >> tracing_on + +echo aaa > /tmp/hoge +echo bbb > /tmp/hoge +echo ccc > /tmp/hoge +rm /tmp/hoge + +:;: "Check trace results" ;: +cat trace | grep watch + +:;: "Ensure wprobe becomes disabled again" ;: +cat events/wprobes/watch/enable | grep 0 + +:;: "Remove wprobe triggers" ;: +echo '!set_wprobe:watch:dentry' >> events/fprobes/truncate/trigger +echo '!clear_wprobe:watch' >> events/fprobes/dentry_kill/trigger +! grep ^set_wprobe events/fprobes/truncate/trigger +! grep ^clear_wprobe events/fprobes/dentry_kill/trigger + +exit 0