Uprobes patches.
Subsystems (9)
v2 cover Uprobes patches.
Uprobes Patches
Changelog:
- Added trace_event interface for uprobes.
- Addressed comments from Andrew Morton and Randy Dunlap.
For previous posting: please refer: http://lkml.org/lkml/2010/3/20/107
This patchset implements Uprobes which enables you to dynamically break into any routine in a user space application and collect information non-disruptively.
This patchset is a rework based on suggestions from discussions on lkml in January this year (http://lkml.org/lkml/2010/1/11/92 and
http://lkml.org/lkml/2010/1/27/19). This implementation of uprobes doesnt depend on utrace.
When a uprobe is registered, Uprobes makes a copy of the probed instruction, replaces the first byte(s) of the probed instruction with a breakpoint instruction. (Uprobes uses background page replacement mechanism and ensures that the breakpoint affects only that process.)
When a CPU hits the breakpoint instruction, Uprobes gets notified of trap and finds the associated uprobe. It then executes the associated handler. Uprobes single-steps its copy of the probed instruction and resumes execution of the probed process at the instruction following the probepoint. Instruction copies to be single-stepped are stored in a per-process "execution out of line (XOL) area". Currently XOL area is allocated as one page vma.
Advantages of uprobes over conventional debugging include:
1. Non-disruptive.
Unlike current ptrace based mechanisms, uprobes tracing wouldnt involve signals, stopping threads and context switching between the tracer and tracee.
2. Much better handling of multithreaded programs because of XOL. Current ptrace based mechanisms use single stepping inline, i.e they copy back the original instruction on hitting a breakpoint. In such mechanisms tracers have to stop all the threads on a breakpoint hit or tracers will not be able to handle all hits to the location of interest. Uprobes uses execution out of line, where the instruction to be traced is analysed at the time of breakpoint insertion and a copy of instruction is stored at a different location. On breakpoint hit, uprobes jumps to that copied location and singlesteps the same instruction and does the necessary fixups post singlestepping.
3. Multiple tracers for an application.
Multiple uprobes based tracer could work in unison to trace an application. There could one tracer that could be interested in generic events for a particular set of process. While there could be another tracer that is just interested in one specific event of a particular process thats part of the previous set of process.
4. Corelating events from kernels and userspace.
Uprobes could be used with other tools like kprobes, tracepoints or as part of higher level tools like perf to give a consolidated set of events from kernel and userspace. In future we could look at a single backtrace showing application, library and kernel calls.
Here is the list of TODO Items.
- Provide a perf interface to uprobes.
- Allowing probes across fork/exec.
- Allowing probes on per-executable/per dso.
- Allow multiple probes to share a probepoint.
- Merge user_bkpt into uprobes.
- Merge uprobes and kprobes trace_event.
- replace macro with bits in inat table.
- Support for other architectures.
- Return probes.
- Uprobes booster.
This patchset is based on 2.6.34-rc3.
Please do provide your valuable comments.
Thanks in advance.
Srikar
Srikar Dronamraju (10):
1. X86 instruction analysis: Move Macro W to insn.h 2. mm: Move replace_page() to mm/memory.c 3. mm: Enhance replace_page() to support pagecache 4. user_bkpt: User Space Breakpoint Assistance Layer 5. user_bkpt: X86 details for User space breakpoint assistance 6. user_bkpt: Slot allocation for Execution out of line 7. uprobes: Uprobes Implementation 8. uprobes: X86 details for Uprobes 9. uprobes: Uprobes Documentation patch
10. samples: Uprobes samples
11. trace: uprobes trace_event interface
Documentation/uprobes.txt | 244 ++++++++++ arch/Kconfig | 31 ++ arch/x86/Kconfig | 2 + arch/x86/include/asm/insn.h | 7 + arch/x86/include/asm/user_bkpt.h | 43 ++ arch/x86/kernel/Makefile | 3 + arch/x86/kernel/kprobes.c | 7 - arch/x86/kernel/uprobes.c | 87 ++++ arch/x86/kernel/user_bkpt.c | 572 +++++++++++++++++++++++ include/linux/mm.h | 2 + include/linux/sched.h | 3 + include/linux/tracehook.h | 18 + include/linux/uprobes.h | 187 ++++++++ include/linux/user_bkpt.h | 305 +++++++++++++ include/linux/user_bkpt_xol.h | 61 +++ kernel/Makefile | 3 + kernel/fork.c | 3 + kernel/trace/Kconfig | 8 + kernel/trace/Makefile | 1 + kernel/trace/trace.h | 12 + kernel/trace/trace_uprobe.c | 926 ++++++++++++++++++++++++++++++++++++++ kernel/uprobes.c | 803 +++++++++++++++++++++++++++++++++ kernel/user_bkpt.c | 598 ++++++++++++++++++++++++ kernel/user_bkpt_xol.c | 289 ++++++++++++ mm/ksm.c | 59 --- mm/memory.c | 62 +++ samples/Kconfig | 7 + samples/uprobes/Makefile | 17 + samples/uprobes/uprobe_example.c | 83 ++++
---
0 files changed, 0 insertions(+), 0 deletions(-)
PATCH v2 1/11 Move Macro W to insn.h
Move Macro W to asm/insn.h
Macro W used to know if the instructions are valid for user-space/kernel space. This macro is used by kprobes and user_bkpt. (i.e user space breakpoint assistance layer.) So moving it to a common header file asm/insn.h.
TODO: replace macro W with bits in inat table.
---
arch/x86/include/asm/insn.h | 7 +++++++ arch/x86/kernel/kprobes.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-)
arch/x86/include/asm/insn.h +7 -0
@@ -23,6 +23,13 @@/* insn_attr_t is defined in inat.h */ #include <asm/inat.h> +#define W (row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ + (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ + (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \ + (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \ + (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ + << (row % 32)) + struct insn_field { union { insn_value_t value;
arch/x86/kernel/kprobes.c +0 -7
@@ -66,12 +66,6 @@ DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);#define stack_addr (regs) ((unsigned long *)kernel_stack_pointer(regs)) -#define W (row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ - (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ - (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \ - (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \ - (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ - << (row % 32))/* * Undefined/reserved opcodes, conditional jump, Opcode Extension * Groups, and some special opcodes can not boost.
@@ -98,7 +92,6 @@ static const u32 twobyte_is_boostable[256 / 32] = {/* ----------------------------------------------- */ /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ }; -#undef Wstruct kretprobe_blackpoint kretprobe_blacklist[] = { {"__switch_to" , },/* This function switches only current task, but
PATCH v2 2/11 Move replace_page() to mm/memory.c
In reply to Srikar DronamrajuMove replace_page() to mm/memory.c
Move replace_page from mm/ksm.c to mm/memory.c.
User bkpt will use background page replacement approach to insert/delete breakpoints. Background page replacement approach will be based on replace_page. Now replace_page() loses its static attribute.
---
include/linux/mm.h | 2 ++ mm/ksm.c | 59 ---------------------------------------------------- mm/memory.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 59 deletions(-)
include/linux/mm.h +2 -0
@@ -854,6 +854,8 @@ void account_page_dirtied(struct page *page, struct address_space *mapping);int set_page_dirty (struct page *page);int set_page_dirty_lock (struct page *page);int clear_page_dirty_for_io (struct page *page); +int replace_page (struct vm_area_struct *vma,struct page *page, +struct page *kpage,pte_t orig_pte); extern unsigned long move_page_tables(struct vm_area_struct *vma, unsigned long old_addr, struct vm_area_struct *new_vma,
mm/ksm.c +0 -59
65 lines not on this page · raw message
mm/memory.c +59 -0
65 lines not on this page · raw message
PATCH v2 3/11 Enhance replace_page() to support pagecache
In reply to Srikar DronamrajuEnhance replace_page() to support pagecache
Currently replace_page would work only for anonymous pages. This patch enhances replace_page() to work for pagecache pages
This enhancement is useful for user_bkpt's replace_page based background page replacement for insertion and removal of breakpoints.
---
mm/memory.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-)
mm/memory.c +4 -1
@@ -2617,7 +2617,10 @@ int replace_page(struct vm_area_struct *vma, struct page *page, }get_page (kpage); -page_add_anon_rmap (kpage, vma, addr); +if (PageAnon (kpage)) +page_add_anon_rmap (kpage, vma, addr); +else +page_add_file_rmap (kpage);flush_cache_page (vma, addr,pte_pfn (*ptep));ptep_clear_flush (vma, addr, ptep);
PATCH v2 4/11 User Space Breakpoint Assistance Layer
In reply to Srikar DronamrajuUser Space Breakpoint Assistance Layer (USER_BKPT)
Changelog:
Use k(un)map_atomic instead of k(un)map. Remove BUG_ON.
Few parameter changes to be more consistent with sparse. Added kernel-doc comments whereever necessary. Introduce a check to detect if post_xol can sleep.
Currently there is no mechanism in kernel to insert/remove breakpoints.
This patch implements user space breakpoint assistance layer provides kernel subsystems with architecture independent interface to establish breakpoints in user applications. This patch provides core implementation of user_bkpt and also wrappers for architecture dependent methods.
USER_BKPT currently supports both single stepping inline and execution out of line strategies. Two different probepoints in the same process can have two different strategies. It handles pre-processing and post-processing of singlestep after a breakpoint hit.
Single stepping inline strategy is the traditional method where original instructions replace the breakpointed instructions on a breakpoint hit. This method works well with single threaded applications. However its racy with multithreaded applications.
Execution out of line strategy single steps on a copy of the instruction. This method works well for both single-threaded and multithreaded applications.
There could be other strategies like emulating an instruction. However they are currently not implemented.
Insertion and removal of breakpoints is by "Background page replacement". i.e make a copy of the page, modify its the contents, set the pagetable and flush the tlbs. This page uses enhanced replace_page to cow the page. Modified page is only reflected for the interested process. Others sharing the page will still see the old copy.
You need to follow this up with the USER_BKPT patch for your architecture.
Uprobes uses this facility to insert/remove breakpoint.
TODO: Merge user_bkpt layer with uprobes.
---
arch/Kconfig | 14 + include/linux/user_bkpt.h | 305 +++++++++++++++++++++++ kernel/Makefile | 1 kernel/user_bkpt.c | 598 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 918 insertions(+), 0 deletions(-) create mode 100644 include/linux/user_bkpt.h create mode 100644 kernel/user_bkpt.c
arch/Kconfig +14 -0
@@ -52,6 +52,17 @@ config OPTPROBES This option will allow kprobes to optimize breakpoint to a jump for reducing its overhead. +config USER_BKPT + bool "User-space breakpoint assistance (EXPERIMENTAL)" + depends on MODULES + depends on HAVE_USER_BKPT + depends on !HIGHPTE + help + User_bkpt (User-space breakpoint assistance layer) enables + kernel subsystems to establish breakpoints in user applications. + This service is used by components such as uprobes. + If in doubt, say "N". + config HAVE_EFFICIENT_UNALIGNED_ACCESS bool help
@@ -85,6 +96,9 @@ config USER_RETURN_NOTIFIER Provide a kernel-internal notification when a cpu is about to switch to user mode. +config HAVE_USER_BKPT + def_bool n + config HAVE_IOREMAP_PROT bool
include/linux/user_bkpt.h new file +305 -0
305 lines not on this page · raw message
kernel/Makefile +1 -0
@@ -105,6 +105,7 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o +obj-$(CONFIG_USER_BKPT) += user_bkpt.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/user_bkpt.c new file +598 -0
598 lines not on this page · raw message
PATCH v2 5/11 X86 details for user space breakpoint assistance.
In reply to Srikar Dronamrajux86 support for user breakpoint Infrastructure
Changelog:
set USER_BKPT_FIX_SLEEPY if post_xol might sleep.
This patch provides x86 specific userspace breakpoint assistance implementation details. It uses the "x86: instruction decoder API" patch to do validate and analyze the instructions. This analysis is used at the time of post-processing of breakpoint hit to do the necessary fix-ups.
Almost all instructions are handled for traditional strategy and execution out of line strategy. Instruction handled include the RIP relative instructions.
This patch requires "x86: instruction decoder API" patch.
http://lkml.org/lkml/2009/6/1/459
---
arch/x86/Kconfig | 1 arch/x86/include/asm/user_bkpt.h | 43 +++ arch/x86/kernel/Makefile | 2 arch/x86/kernel/user_bkpt.c | 572 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 618 insertions(+), 0 deletions(-) create mode 100644 arch/x86/include/asm/user_bkpt.h create mode 100644 arch/x86/kernel/user_bkpt.c
arch/x86/Kconfig +1 -0
@@ -53,6 +53,7 @@ config X86 select HAVE_KERNEL_LZMA select HAVE_KERNEL_LZO select HAVE_HW_BREAKPOINT + select HAVE_USER_BKPT select PERF_EVENTS select ANON_INODES select HAVE_ARCH_KMEMCHECK
arch/x86/include/asm/user_bkpt.h new file +43 -0
43 lines not on this page · raw message
arch/x86/kernel/Makefile +2 -0
@@ -117,6 +117,8 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o +obj-$(CONFIG_USER_BKPT) += user_bkpt.o + ### # 64 bit specific files ifeq ($(CONFIG_X86_64),y)
arch/x86/kernel/user_bkpt.c new file +572 -0
572 lines not on this page · raw message
PATCH v2 6/11 Slot allocation for Execution out of line
In reply to Srikar DronamrajuSlot allocation for Execution out of line strategy(XOL)
This patch provides slot allocation mechanism for execution out of line strategy for use with user space breakpoint infrastructure.
Traditional method of replacing the original instructions on breakpoint hit are racy when used on multithreaded applications.
Alternatives for the traditional method include:
- Emulating the breakpointed instruction.
- Execution out of line.
Emulating the instruction:
This approach would use a in-kernel instruction emulator to
emulate the breakpointed instruction. This approach could be looked in at a later point of time.
Execution out of line:
In execution out of line strategy, a new vma is injected into
the target process, a copy of the instructions which are breakpointed is stored in one of the slots. On breakpoint hit, the copy of the instruction is single-stepped leaving the breakpoint instruction as is. This method is architecture independent.
This method is useful while handling multithreaded processes.
This patch allocates one page per process for slots to be used to copy the breakpointed instructions.
Current slot allocation mechanism:
1. Allocate one dedicated slot per user breakpoint. Each slot is big enuf to accomodate the biggest instruction for that architecture. (16 bytes for x86).
2. We currently allocate only one page for slots. Hence the number of slots is limited to active breakpoint hits on that process.
3. Bitmap to track used slots.
---
arch/Kconfig | 4 + include/linux/user_bkpt_xol.h | 61 +++++++++ kernel/Makefile | 1 kernel/user_bkpt_xol.c | 289 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 355 insertions(+), 0 deletions(-) create mode 100644 include/linux/user_bkpt_xol.h create mode 100644 kernel/user_bkpt_xol.c
arch/Kconfig +4 -0
@@ -99,6 +99,10 @@ config USER_RETURN_NOTIFIER config HAVE_USER_BKPT def_bool n +config USER_BKPT_XOL + def_bool y + depends on USER_BKPT + config HAVE_IOREMAP_PROT bool
include/linux/user_bkpt_xol.h new file +61 -0
61 lines not on this page · raw message
kernel/Makefile +1 -0
@@ -106,6 +106,7 @@ obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_USER_BKPT) += user_bkpt.o +obj-$(CONFIG_USER_BKPT_XOL) += user_bkpt_xol.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/user_bkpt_xol.c new file +289 -0
289 lines not on this page · raw message
PATCH v2 7/11 Uprobes Implementation
In reply to Srikar DronamrajuUprobes Implementation
Changelog:
- If fixup might sleep; then do the post singlestep processing in task context.
The uprobes infrastructure enables a user to dynamically establish probepoints in user applications and collect information by executing a handler function when a probepoint is hit.
The user specifies the virtual address and the pid of the process of interest along with the action to be performed (handler). The handle Uprobes is implemented on the user-space breakpoint assistance layer and uses the execution out of line strategy. Uprobes follows lazy slot allocation. I.e, on the first probe hit for that process, a new vma (to hold the probed instructions for execution out of line) is allocated. Once allocated, this vma remains for the life of the process, and is reused as needed for subsequent probes. A slot in the vma is allocated for a probepoint when it is first hit.
A slot is marked for reuse when the probe gets unregistered and no threads are using that slot.
In a multithreaded process, a probepoint once registered is active for all threads of a process. If a thread specific action for a probepoint is required then the handler should be implemented to do the same.
If a breakpoint already exists at a particular address (irrespective of who inserted the breakpoint including uprobes), uprobes will refuse to register any more probes at that address.
You need to follow this up with the uprobes patch for your architecture.
For more information: please refer to Documentation/uprobes.txt
TODO:
1. Perf/trace events interface for uprobes.
2. Allow multiple probes at a probepoint.
3. Booster probes.
4. Allow probes to be inherited across fork.
5. probing function returns.
---
arch/Kconfig | 13 + include/linux/sched.h | 3 include/linux/tracehook.h | 18 + include/linux/uprobes.h | 187 ++++++++++ kernel/Makefile | 1 kernel/fork.c | 3 kernel/uprobes.c | 803 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1028 insertions(+), 0 deletions(-) create mode 100644 include/linux/uprobes.h create mode 100644 kernel/uprobes.c
arch/Kconfig +13 -0
@@ -63,6 +63,16 @@ config USER_BKPT This service is used by components such as uprobes. If in doubt, say "N". +config UPROBES + bool "User-space probes (EXPERIMENTAL)" + depends on MODULES && USER_BKPT_XOL + depends on HAVE_UPROBES + help + Uprobes enables kernel modules to establish probepoints + in user applications and execute handler functions when + the probepoints are hit. For more information, refer to + Documentation/uprobes.txt. If in doubt, say "N". + config HAVE_EFFICIENT_UNALIGNED_ACCESS bool help
@@ -114,6 +124,9 @@ config HAVE_KRETPROBES config HAVE_OPTPROBES bool + +config HAVE_UPROBES + def_bool n # # An arch should select this if it provides all these things: #
include/linux/sched.h +3 -0
@@ -1506,6 +1506,9 @@ struct task_struct { unsigned long memsw_bytes; /* uncharged mem+swap usage */ } memcg_batch;#endif +#ifdef CONFIG_UPROBES +struct uprobe_task *utask; +#endif };/* Future-safe accessor for struct task_struct's cpus_allowed. */
include/linux/tracehook.h +18 -0
@@ -49,6 +49,7 @@#include <linux/sched.h> #include <linux/ptrace.h> #include <linux/security.h> +#include <linux/uprobes.h> struct linux_binprm ;/**
@@ -204,6 +205,11 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt,if (!ptrace_event (PT_TRACE_EXEC ,PTRACE_EVENT_EXEC ,0 ) &&unlikely (task_ptrace (current) &PT_PTRACED ))send_sig (SIGTRAP , current,0 ); + +#ifdef CONFIG_UPROBES +if (unlikely (current->utask)) +uprobe_free_utask (); +#endif }/**
@@ -219,6 +225,10 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt,static inline void tracehook_report_exit (long *exit_code) {ptrace_event (PT_TRACE_EXIT ,PTRACE_EVENT_EXIT , *exit_code); +#ifdef CONFIG_UPROBES +if (unlikely (current->utask)) +uprobe_free_utask (); +#endif }/**
@@ -293,6 +303,10 @@ static inline void tracehook_report_clone(struct pt_regs *regs,sigaddset (&child->pending.signal,SIGSTOP );set_tsk_thread_flag (child,TIF_SIGPENDING ); } +#ifdef CONFIG_UPROBES +if (unlikely (current->utask)) +uprobe_handle_clone (clone_flags, child); +#endif }/**
@@ -593,6 +607,10 @@ static inline void set_notify_resume(struct task_struct *task)*/ static inline void tracehook_notify_resume (struct pt_regs *regs) { +#ifdef CONFIG_UPROBES +if (current->utask && current->utask->active_ppt) +uprobe_notify_resume (regs); +#endif }#endif /* TIF_NOTIFY_RESUME */
include/linux/uprobes.h new file +187 -0
187 lines not on this page · raw message
kernel/Makefile +1 -0
@@ -107,6 +107,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_USER_BKPT) += user_bkpt.o obj-$(CONFIG_USER_BKPT_XOL) += user_bkpt_xol.o +obj-$(CONFIG_UPROBES) += uprobes.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/fork.c +3 -0
@@ -1180,6 +1180,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,INIT_LIST_HEAD (&p->pi_state_list); p->pi_state_cache =NULL ;#endif +#ifdef CONFIG_UPROBES + p->utask =NULL ; +#endif /* * sigaltstack should be cleared when sharing the same VM */
kernel/uprobes.c new file +803 -0
803 lines not on this page · raw message
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 12 lines · Srikar Dronamraju
kernel/uprobes.c new file +8
-0+static struct uprobe_process *find_uprocess (struct pid *tg_leader) +{ +struct uprobe_process *uproc; +struct task_struct *tsk =get_pid_task (tg_leader,PIDTYPE_PID ); + +if (!tsk) +return NULL ; +
kernel/uprobes.c line 166
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (!tsk->utask || !tsk->utask->uproc) { +put_task_struct (tsk); +return NULL ; + } + + uproc = tsk->utask->uproc; +BUG_ON (uproc->tg_leader != tg_leader); +atomic_inc (&uproc->refcount); +put_task_struct (tsk); +return uproc;
Looks like, this doesn't need get/put task_struct, you could just use pid_task() under rcu_read_lock().
Quoted 5 lines
kernel/uprobes.c new file +5
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *tsk = start; +
kernel/uprobes.c line 212
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (!start) +return ; + +rcu_read_lock (); + do { + if (tsk->utask) { + kfree(tsk->utask); + tsk->utask = NULL; + } + tsk = next_thread(tsk);
This doesn't look right. We can't trust ->thread_group list even under rcu_read_lock(). The task can exit and __exit_signal() can remove it from ->thread_group list before we take rcu_read_lock().
Quoted 8 lines
kernel/uprobes.c new file +8
-0+static struct uprobe_task *add_utask (struct task_struct *t, +struct uprobe_process *uproc) +{ +struct uprobe_task *utask; + +if (!t) +return NULL ; + utask =kzalloc (sizeof *utask,GFP_USER );
kernel/uprobes.c line 275
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (unlikely (utask ==NULL )) +return ERR_PTR (-ENOMEM ); + + utask->uproc = uproc; + utask->active_ppt =NULL ; + t->utask = utask; +atomic_inc (&uproc->refcount); + +return utask; +}
This is called by create_uprocess(). Who will free t->utask if t has already passed tracehook_report_exit() ?
kernel/uprobes.c line 296
Quoted 12 lines
kernel/uprobes.c new file +12
-0+static struct task_struct *find_next_thread (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *next_t =NULL ; + +rcu_read_lock (); +if (start) { +struct task_struct *t = start; + + do { + if (unlikely(t->flags & PF_EXITING)) + goto dont_add;
not sure I understand this check. Somehow we should prevent the races with tracehook_report_exit/tracehook_report_exec, but PF_EXITING can't help ?
kernel/uprobes.c line 307
Quoted 3 lines
kernel/uprobes.c new file +3
-0+dont_add: + t =next_thread (t); + }while (t != start);
again, this doesn't look right. Btw, I'd suggest to use while_each_thread().
Quoted 14 lines
kernel/uprobes.c new file +13
-0+static struct uprobe_process *create_uprocess (struct pid *tg_leader) +{ +/* +* Create and populate one utask per thread in this process. We +* can't call add_utask() while holding RCU lock, so we: +* 1. rcu_read_lock() +* 2. Find the next thread, add_me, in this process that's not +* having a utask struct allocated. +* 3. rcu_read_unlock() +* 4. add_utask(add_me, uproc) +* Repeat 1-4 'til we have utasks for all threads. +*/ + cur_t =get_pid_task (tg_leader,PIDTYPE_PID );
kernel/uprobes.c line 352
Quoted 10 lines
kernel/uprobes.c new file +10
-0+do { + utask =add_utask (cur_t, uproc); +if (IS_ERR (utask)) { + err =PTR_ERR (utask); + goto fail; + } + add_me =find_next_thread (uproc, cur_t); +put_task_struct (cur_t); + cur_t = add_me; + }while (add_me !=NULL );
can't we race with clone(CLONE_THREAD) and miss the new thread? Probably I missed something, but afaics we need some barriers to ensure that either tracehook_report_clone() sees current->utask != NULL or find_next_thread() sees the new thread in ->thread_group.
kernel/uprobes.c line 373
Quoted 7 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);
Is it really possible to call register/unregister with nsproxy == NULL?
kernel/uprobes.c line 377
Quoted 4 lines
kernel/uprobes.c new file +4
-0+if (pid) { +struct task_struct *t =pid_task (pid,PIDTYPE_PID ); + +if (!t ||unlikely (t->flags &PF_EXITING ))
Why do we check PF_EXITING?
Quoted 24 lines
kernel/uprobes.c new file +24
-0+int register_uprobe (struct uprobe *u) +{ +struct uprobe_process *uproc; +struct uprobe_probept *ppt; +struct pid *p; +int ret =0 ; + +if (!u || !u->handler) +return -EINVAL ; + + p =get_tg_leader (u->pid); +if (!p) +return -ESRCH ; + + +/* Get the uprobe_process for this pid, or make a new one. */ +mutex_lock (&uprobe_mutex); + uproc =find_uprocess (p); + +if (!uproc) { + uproc =create_uprocess (p); +if (IS_ERR (uproc)) { + ret = (int )PTR_ERR (uproc); +mutex_unlock (&uprobe_mutex);
kernel/uprobes.c line 420
Quoted 10 lines
kernel/uprobes.c new file +10
-0+ goto fail_tsk; + } + } +mutex_unlock (&uprobe_mutex); +mutex_lock (&uproc->mutex); + +if (uproc->n_ppts >=MAX_USER_BKPT_XOL_SLOTS ) + goto fail_uproc; + + ret =xol_validate_vaddr (p, u->vaddr, uproc->xol_area);
OK, uproc and p can't go away. But why it is safe to use pid_task(p) ?
I am looking at 6th patch http://marc.info/?l=linux-kernel&m=127005086102256
and xol_validate_vaddr() calls pid_task() without rcu and doesn't check the result is not NULL.
We already dropped uprobe_mutex, can't this task exit?
Quoted 14 lines
kernel/uprobes.c new file +14
-0+void uprobe_handle_clone (unsigned long clone_flags, +struct task_struct *child) +{ +struct uprobe_process *uproc; +struct uprobe_task *ptask, *ctask; + + ptask = current->utask; +if (!ptask) +return ; + + uproc = ptask->uproc; + +if (clone_flags &CLONE_THREAD ) { +mutex_lock (&uprobe_mutex);
kernel/uprobes.c line 623
Quoted 10 lines
kernel/uprobes.c new file +10
-0+/* New thread in the same process. */ + ctask = child->utask; +if (unlikely (ctask)) { +/* +* create_uprocess() ran just as this clone +* happened, and has already accounted for the +* new child. +*/ + }else + ctask =add_utask (child, uproc);
This looks a bit strange. Why do we need "ctask" at all? It is not used, you could just do
if (likely(!child->utask)) add_utask(child, uproc);
The same for "else" branch.
Quoted 13 lines
kernel/uprobes.c new file +13
-0+ }else { +struct uprobe_probept *ppt; +int ret; + +/* +* New process spawned by parent. Remove the probepoints +* in the child's text. +* +* We also hold the uproc->mutex for the parent - so no +* new uprobes will be registered 'til we return. +*/ +mutex_lock (&uproc->mutex); + ctask = child->utask;
kernel/uprobes.c line 647
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (unlikely (ctask)) { +/* +* create_uprocess() ran just as this fork +* happened, and has already created a new utask. +*/ +mutex_unlock (&uproc->mutex); +return ; + } +list_for_each_entry (ppt, &uproc->uprobe_list, ut_node) { + ret =user_bkpt_remove_bkpt (child, &ppt->user_bkpt);
OK, iiuc this should restore the original instruction, right?
But what about clone(CLONE_VM)? In this case this child shares ->mm with parent.
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Oleg, Thanks for the review.
Quoted 14 lines
Quoted 14 lines
...
kernel/uprobes.c new file +13
-0+static struct uprobe_process *find_uprocess (struct pid *tg_leader) +{ +struct uprobe_process *uproc; +struct task_struct *tsk =get_pid_task (tg_leader,PIDTYPE_PID ); + +if (!tsk) +return NULL ; + +if (!tsk->utask || !tsk->utask->uproc) { +put_task_struct (tsk); +return NULL ; + } +
Quoted 8 lines
kernel/uprobes.c line 166
Quoted 5 lines
kernel/uprobes.c new file +5
-0+ uproc = tsk->utask->uproc; +BUG_ON (uproc->tg_leader != tg_leader); +atomic_inc (&uproc->refcount); +put_task_struct (tsk); +return uproc;Looks like, this doesn't need get/put
task_struct, you could just usepid_task()underrcu_read_lock().
Okay.
Quoted 12 lines
Quoted 12 lines
kernel/uprobes.c new file +11
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *tsk = start; + +if (!start) +return ; + +rcu_read_lock (); + do { + if (tsk->utask) {
Quoted 8 lines
kernel/uprobes.c line 212
Quoted 4 lines
kernel/uprobes.c new file +4
-0+kfree (tsk->utask); + tsk->utask =NULL ; + } + tsk =next_thread (tsk);This doesn't look right. We can't trust ->thread_group list even under
rcu_read_lock(). The task can exit and__exit_signal()can remove it from ->thread_group list before we takercu_read_lock().
Oh Okay, I get that the thread could be exiting from the time we allocated the utask to the time we are cleaning up here and hence we could be leaking utask.
Would it be okay if we explicitly (instead of the using tracehook_report_exit) call uprobe_free_utask() just after we set PF_EXITING. We could take the task_lock to synchronize with the add_utask() and do_exit().
Quoted 14 lines
Quoted 14 lines
kernel/uprobes.c new file +13
-0+static struct uprobe_task *add_utask (struct task_struct *t, +struct uprobe_process *uproc) +{ +struct uprobe_task *utask; + +if (!t) +return NULL ; + utask =kzalloc (sizeof *utask,GFP_USER ); +if (unlikely (utask ==NULL )) +return ERR_PTR (-ENOMEM ); + + utask->uproc = uproc; + utask->active_ppt =NULL ;
Quoted 8 lines
kernel/uprobes.c line 275
Quoted 5 lines
kernel/uprobes.c new file +5
-0+ t->utask = utask; +atomic_inc (&uproc->refcount); + +return utask; +}This is called by
create_uprocess(). Who will freet->utaskif t has already passedtracehook_report_exit()?
Okay.
Would it work if we
static struct uprobe_task *add_utask(struct task_struct *t,
struct uprobe_process *uproc)
{
struct uprobe_task *utask;
int exiting = 0;
if (!t)
return NULL;
utask = kzalloc(sizeof *utask, GFP_USER);
if (unlikely(utask == NULL))
return ERR_PTR(-ENOMEM);
task_lock(t);
if (unlikely(t->flags & PF_EXITING)) {
task_unlock(t);
kfree(utask);
return;
}
t->utask = utask;
task_unlock(t);
utask->uproc = uproc;
utask->active_ppt = NULL;
atomic_inc(&uproc->refcount);
}NORET_TYPE void do_exit(long code)
{
...
exit_irq_thread();
exit_signals(tsk); /* sets PF_EXITING */
task_lock(tsk)
if (tsk->utask);
uprobe_free_utask();
task_unlock(tsk);
.....
}
Something similar would be needed in exec path too.
Quoted 17 lines
kernel/uprobes.c line 296
Quoted 13 lines
kernel/uprobes.c new file +12
-0+static struct task_struct *find_next_thread (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *next_t =NULL ; + +rcu_read_lock (); +if (start) { +struct task_struct *t = start; + + do { + if (unlikely(t->flags & PF_EXITING)) + goto dont_add;not sure I understand this check. Somehow we should prevent the races with tracehook_report_exit/tracehook_report_exec, but
PF_EXITINGcan't help ?
yeah, PF_EXITING doesnt seem to help us here. But will this be an issue once we move uprobe_free_utask() after exit_signals.
Quoted 28 lines
kernel/uprobes.c line 307
Quoted 4 lines
kernel/uprobes.c new file +3
-0+dont_add: + t =next_thread (t); + }while (t != start);again, this doesn't look right. Btw, I'd suggest to use
while_each_thread().Quoted 21 lines
kernel/uprobes.c new file +20
-0+static struct uprobe_process *create_uprocess (struct pid *tg_leader) +{ +/* +* Create and populate one utask per thread in this process. We +* can't call add_utask() while holding RCU lock, so we: +* 1. rcu_read_lock() +* 2. Find the next thread, add_me, in this process that's not +* having a utask struct allocated. +* 3. rcu_read_unlock() +* 4. add_utask(add_me, uproc) +* Repeat 1-4 'til we have utasks for all threads. +*/ + cur_t =get_pid_task (tg_leader,PIDTYPE_PID ); + do { + utask = add_utask(cur_t, uproc); + if (IS_ERR(utask)) { + err = PTR_ERR(utask); + goto fail; + } + add_me = find_next_thread(uproc, cur_t);
Quoted 8 lines
kernel/uprobes.c line 352
Quoted 3 lines
kernel/uprobes.c new file +3
-0+put_task_struct (cur_t); + cur_t = add_me; + }while (add_me !=NULL );can't we race with
clone(CLONE_THREAD)and miss the new thread? Probably I missed something, but afaics we need some barriers to ensure that eithertracehook_report_clone()seescurrent->utask!= NULL orfind_next_thread()sees the new thread in ->thread_group.
The tracehook_report_clone is called after the element gets added to the thread_group list in copy_process().
Looking at three cases where current thread could be cloning a new thread.
a) current thread has a utask and tracehook_report_clone is not yet called.
utask for the new thread will be created by eithertracehook_report_clone or the find_next_thread whichever comes first.
b) current thread has no utask and tracehook_report_clone is already called. new thread is in the thread_group list; so a utask will be created
by find_next_thread.
c) current thread has no utask yet and tracehook_report_clone is not yet called. If the creation of utask for the current thread completes after
the current thread called tracehook_report_clone; then its same as case b.
If the creation of utask for the current thread completes before
the current thread calls tracehook_report_clone; then its same as case a;
Am I missing any other cases?
Also if we are using explicit uprobe calls in exec/exit events; I propose we use a explicit uprobe_handle_clone call from copy_process.
Quoted 11 lines
kernel/uprobes.c line 373
Quoted 8 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);Is it really possible to call register/unregister with nsproxy == NULL?
Quoted 6 lines
kernel/uprobes.c line 377
Quoted 4 lines
kernel/uprobes.c new file +4
-0+if (pid) { +struct task_struct *t =pid_task (pid,PIDTYPE_PID ); + +if (!t ||unlikely (t->flags &PF_EXITING ))Why do we check
PF_EXITING?
We didnt want to trace process which is exiting but I think what we might be doing is not allowing multi-threaded process whose thread group leader exited. So I remove this check.
Do you see a need for checking if the process is exiting before we place the probes?
Quoted 38 lines
Quoted 25 lines
kernel/uprobes.c new file +24
-0+int register_uprobe (struct uprobe *u) +{ +struct uprobe_process *uproc; +struct uprobe_probept *ppt; +struct pid *p; +int ret =0 ; + +if (!u || !u->handler) +return -EINVAL ; + + p =get_tg_leader (u->pid); +if (!p) +return -ESRCH ; + + +/* Get the uprobe_process for this pid, or make a new one. */ +mutex_lock (&uprobe_mutex); + uproc =find_uprocess (p); + +if (!uproc) { + uproc =create_uprocess (p); +if (IS_ERR (uproc)) { + ret = (int )PTR_ERR (uproc); +mutex_unlock (&uprobe_mutex);kernel/uprobes.c line 420
Quoted 10 lines
kernel/uprobes.c new file +10
-0+ goto fail_tsk; + } + } +mutex_unlock (&uprobe_mutex); +mutex_lock (&uproc->mutex); + +if (uproc->n_ppts >=MAX_USER_BKPT_XOL_SLOTS ) + goto fail_uproc; + + ret =xol_validate_vaddr (p, u->vaddr, uproc->xol_area);OK, uproc and p can't go away. But why it is safe to use
pid_task(p)?
Quoted 5 lines
I am looking at 6th patch http://marc.info/?l=linux-kernel&m=127005086102256
andxol_validate_vaddr()callspid_task()without rcu and doesn't check the result is not NULL.We already dropped
uprobe_mutex, can't this task exit?
Okay.. Agree, will do changes accordingly.
Quoted 29 lines
Quoted 15 lines
kernel/uprobes.c new file +14
-0+void uprobe_handle_clone (unsigned long clone_flags, +struct task_struct *child) +{ +struct uprobe_process *uproc; +struct uprobe_task *ptask, *ctask; + + ptask = current->utask; +if (!ptask) +return ; + + uproc = ptask->uproc; + +if (clone_flags &CLONE_THREAD ) { +mutex_lock (&uprobe_mutex);kernel/uprobes.c line 623
Quoted 10 lines
kernel/uprobes.c new file +10
-0+/* New thread in the same process. */ + ctask = child->utask; +if (unlikely (ctask)) { +/* +* create_uprocess() ran just as this clone +* happened, and has already accounted for the +* new child. +*/ + }else + ctask =add_utask (child, uproc);This looks a bit strange. Why do we need "ctask" at all? It is not used, you could just do
Quoted 2 lines
if (likely(!child->utask)) add_utask(child, uproc);
Okay, I agree that we can remove ctask here and in the else branch.
Quoted 29 lines
The same for "else" branch.
Quoted 13 lines
kernel/uprobes.c new file +13
-0+ }else { +struct uprobe_probept *ppt; +int ret; + +/* +* New process spawned by parent. Remove the probepoints +* in the child's text. +* +* We also hold the uproc->mutex for the parent - so no +* new uprobes will be registered 'til we return. +*/ +mutex_lock (&uproc->mutex); + ctask = child->utask;kernel/uprobes.c line 647
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (unlikely (ctask)) { +/* +* create_uprocess() ran just as this fork +* happened, and has already created a new utask. +*/ +mutex_unlock (&uproc->mutex); +return ; + } +list_for_each_entry (ppt, &uproc->uprobe_list, ut_node) { + ret =user_bkpt_remove_bkpt (child, &ppt->user_bkpt);OK, iiuc this should restore the original instruction, right?
Quoted 2 lines
But what about
clone(CLONE_VM)? In this case this child shares ->mm with parent.
Okay, So I will remove the breakpoints only for ! CLONE(CLONE_VM) and !CLONE(CLONE_THREAD)
For CLONE(CLONE_VM) I will create a new uproc and utask structures. Since mm is shared; I guess the XOL vma gets shared between the processes.
Again, Thanks Oleg for your comments.
--
Thanks and Regards
Srikar
Quoted 1 line
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Srikar, sorry for delay...
Quoted 21 lines · Srikar Dronamraju
Quoted 12 lines
Quoted 12 lines
kernel/uprobes.c new file +11
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *tsk = start; + +if (!start) +return ; + +rcu_read_lock (); + do { + if (tsk->utask) {Quoted 8 lines
kernel/uprobes.c line 212
Quoted 4 lines
kernel/uprobes.c new file +4
-0+kfree (tsk->utask); + tsk->utask =NULL ; + } + tsk =next_thread (tsk);This doesn't look right. We can't trust ->thread_group list even under
rcu_read_lock(). The task can exit and__exit_signal()can remove it from ->thread_group list before we takercu_read_lock().
Quoted 8 lines
Oh Okay, I get that the thread could be exiting from the time we allocated the utask to the time we are cleaning up here and hence we could be leaking utask.
Would it be okay if we explicitly (instead of the using
tracehook_report_exit) calluprobe_free_utask()just after we setPF_EXITING. We could take thetask_lockto synchronize with theadd_utask()anddo_exit().
Not sure I understand....
I meant, it is not safe to use next_thread(tsk) if tsk was already removed from list by __unhash_process before we take rcu_read_lock().
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
kernel/uprobes.c new file +13
-0+static struct uprobe_task *add_utask (struct task_struct *t, +struct uprobe_process *uproc) +{ +struct uprobe_task *utask; + +if (!t) +return NULL ; + utask =kzalloc (sizeof *utask,GFP_USER ); +if (unlikely (utask ==NULL )) +return ERR_PTR (-ENOMEM ); + + utask->uproc = uproc; + utask->active_ppt =NULL ;Quoted 8 lines
kernel/uprobes.c line 275
Quoted 5 lines
kernel/uprobes.c new file +5
-0+ t->utask = utask; +atomic_inc (&uproc->refcount); + +return utask; +}This is called by
create_uprocess(). Who will freet->utaskif t has already passedtracehook_report_exit()?
Quoted 4 lines
Okay.
Would it work if we[... snip ...]
I think yes. But see below.
Quoted 26 lines
Quoted 17 lines
Quoted 17 lines
kernel/uprobes.c new file +17
-0+* Create and populate one utask per thread in this process. We +* can't call add_utask() while holding RCU lock, so we: +* 1. rcu_read_lock() +* 2. Find the next thread, add_me, in this process that's not +* having a utask struct allocated. +* 3. rcu_read_unlock() +* 4. add_utask(add_me, uproc) +* Repeat 1-4 'til we have utasks for all threads. +*/ + cur_t =get_pid_task (tg_leader,PIDTYPE_PID ); + do { + utask = add_utask(cur_t, uproc); + if (IS_ERR(utask)) { + err = PTR_ERR(utask); + goto fail; + } + add_me = find_next_thread(uproc, cur_t);Quoted 8 lines
kernel/uprobes.c line 352
Quoted 3 lines
kernel/uprobes.c new file +3
-0+put_task_struct (cur_t); + cur_t = add_me; + }while (add_me !=NULL );can't we race with
clone(CLONE_THREAD)and miss the new thread? Probably I missed something, but afaics we need some barriers to ensure that eithertracehook_report_clone()seescurrent->utask!= NULL orfind_next_thread()sees the new thread in ->thread_group.
Quoted 8 lines
The
tracehook_report_cloneis called after the element gets added to thethread_grouplist incopy_process().
Looking at three cases where current thread could be cloning a new thread.a) current thread has a utask and
tracehook_report_cloneis not yet called.
utask for the new thread will be created by eithertracehook_report_cloneor thefind_next_threadwhichever comes first.
Yes, but my point was, we probably need mb's on both sides. Of course, this is only theoretical problem, but tracehook_report_clone() can read current->utask == NULL before the result of copy_process()->list_add_tail() is visible to another CPU which does create_uprocess().
Quoted 9 lines
Quoted 9 lines
kernel/uprobes.c line 373
Quoted 7 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);Is it really possible to call register/unregister with nsproxy == NULL?
You didn't answer ;)
Quoted 2 lines
Do you see a need for checking if the process is exiting before we place the probes?
Oh, I don't know. You are going to change this code anyway, I can't see in advance.
I tried to read the next 8/11 patch, and I have a couple more random questions.
- uprobe_process->tg_leader is not really used ?
- looks like, 7/11 can't be compiled without the next 8/11 ? say, the next patch defines arch_uprobe_disable_sstep() but it is used by 7/11
- I don't understand why do we need uprobe_{en,dis}able_interrupts helpers. pre_ssout() could just do local_irq_enable(). This path leads to get_signal_to_deliver() which enables irqs anyway, it is always safe to do this earlier and I don't think we need to disable irqs again later. In any case, I don't understand why these helpers use native_irq_xxx().
- pre_ssout() does .xol_vaddr = xol_get_insn_slot(). This looks a bit confusing, xol_get_insn_slot() should set .xol_vaddr correctly under lock.
- pre_ssout() does user_bkpt_set_ip() after user_bkpt_pre_sstep(). Why? Shouldn't user_bkpt_pre_sstep() always set regs->ip ? Otherwise uprobe_bkpt_notifier()->user_bkpt_pre_sstep() is not right.
- I don't really understand why ->handler_in_interrupt is really useful, but never mind.
- However, handler_in_interrupt && !uses_xol_strategy() doesn't look right. uprobe_bkpt_notifier() is called with irqs disabled, right? but set_orig_insn() is might_sleep().
Quoted 26 lines
Quoted 26 lines
Quoted 13 lines
kernel/uprobes.c new file +13
-0+ }else { +struct uprobe_probept *ppt; +int ret; + +/* +* New process spawned by parent. Remove the probepoints +* in the child's text. +* +* We also hold the uproc->mutex for the parent - so no +* new uprobes will be registered 'til we return. +*/ +mutex_lock (&uproc->mutex); + ctask = child->utask;kernel/uprobes.c line 647
Quoted 10 lines
kernel/uprobes.c new file +10
-0+if (unlikely (ctask)) { +/* +* create_uprocess() ran just as this fork +* happened, and has already created a new utask. +*/ +mutex_unlock (&uproc->mutex); +return ; + } +list_for_each_entry (ppt, &uproc->uprobe_list, ut_node) { + ret =user_bkpt_remove_bkpt (child, &ppt->user_bkpt);OK, iiuc this should restore the original instruction, right?
Quoted 7 lines
Quoted 2 lines
But what about
clone(CLONE_VM)? In this case this child shares ->mm with parent.Okay, So I will remove the breakpoints only for !
CLONE(CLONE_VM)and !CLONE(CLONE_THREAD)
ForCLONE(CLONE_VM)I will create a new uproc and utask structures. Since mm is shared; I guess the XOL vma gets shared between the processes.
Yes, I think CLONE_VM without CLONE_THREAD needs utask too, but do we need the new uproc? OK, please forget about this for the moment.
Suppose that register_uprobe() succeeds and does set_bkpt(). What if another process (not sub-thread) with the same ->mm hits this bp? uprobe_bkpt_notifier() will see ->utask == NULL and return 0. Then do_int3() sends SIGTRAP and kills this task. OK, probably CLONE_VM alone is exotic, but CLONE_VFORK | VM is not.
I think uprobe_process should be per ->mm, not per-process.
I wonder if there any possibility to avoid task_struct->utask, or at least, if we can allocate it in uprobe_bkpt_notifier() on demand. Not sure.
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 29 lines
Quoted 20 lines
Quoted 11 lines
Quoted 11 lines
kernel/uprobes.c new file +11
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) +{ +struct task_struct *tsk = start; + +if (!start) +return ; + +rcu_read_lock (); + do { + if (tsk->utask) {Quoted 8 lines
kernel/uprobes.c line 212
Quoted 4 lines
kernel/uprobes.c new file +4
-0+kfree (tsk->utask); + tsk->utask =NULL ; + } + tsk =next_thread (tsk);This doesn't look right. We can't trust ->thread_group list even under
rcu_read_lock(). The task can exit and__exit_signal()can remove it from ->thread_group list before we takercu_read_lock().Quoted 8 lines
Oh Okay, I get that the thread could be exiting from the time we allocated the utask to the time we are cleaning up here and hence we could be leaking utask.
Would it be okay if we explicitly (instead of the using
tracehook_report_exit) calluprobe_free_utask()just after we setPF_EXITING. We could take thetask_lockto synchronize with theadd_utask()anddo_exit().
Quoted 4 lines
Not sure I understand....
I meant, it is not safe to use
next_thread(tsk)if tsk was already removed from list by__unhash_processbefore we takercu_read_lock().
Okay, cleanup_process() gets called only and only if add_utask() fails to allocated utask struct. Based on your inputs I will synchronize exit_signals() and uprobe_free_utask(). However it still can happen that uprobe calls cleanup_uprocess() with reference to task struct which has just called __unhash_process(). Is there a way out of this?
[ snip ..]
Quoted 15 lines
Quoted 6 lines
Quoted 5 lines
can't we race with
clone(CLONE_THREAD)and miss the new thread? Probably I missed something, but afaics we need some barriers to ensure that eithertracehook_report_clone()seescurrent->utask!= NULL orfind_next_thread()sees the new thread in ->thread_group.Quoted 8 lines
The
tracehook_report_cloneis called after the element gets added to thethread_grouplist incopy_process().
Looking at three cases where current thread could be cloning a new thread.a) current thread has a utask and
tracehook_report_cloneis not yet called.
utask for the new thread will be created by eithertracehook_report_cloneor thefind_next_threadwhichever comes first.
Quoted 4 lines
Yes, but my point was, we probably need mb's on both sides. Of course, this is only theoretical problem, but
tracehook_report_clone()can readcurrent->utask== NULL before the result of copy_process()->list_add_tail() is visible to another CPU which doescreate_uprocess().
Okay.
Quoted 11 lines
Quoted 10 lines
Quoted 10 lines
kernel/uprobes.c line 373
Quoted 8 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);Is it really possible to call register/unregister with nsproxy == NULL?
Quoted 1 line
You didn't answer ;)
Can you please let me know when nsproxy is set to NULL? If we are sure that register/unregister will be called with nsproxy set, then I am happy to remove this check.
Quoted 6 lines
Quoted 3 lines
Do you see a need for checking if the process is exiting before we place the probes?
Oh, I don't know. You are going to change this code anyway, I can't see in advance.
Okay .
Quoted 5 lines
I tried to read the next 8/11 patch, and I have a couple more random questions.
-
uprobe_process->tg_leaderis not really used ?
Currently we have a reference to pid struct from the time we created a uprobe_process to the time we free the uprobe process. So are you suggesting that we dont have a reference to the pid structure or is that we dont need to cache the pid struct and access it thro task_pid(current) in free_uprobes()?
Quoted 4 lines
- looks like, 7/11 can't be compiled without the next 8/11 ? say, the next patch defines
arch_uprobe_disable_sstep()but it is used by 7/11
Okay, I will take care of this in the next iteration.
Quoted 7 lines
- I don't understand why do we need uprobe_{en,dis}able_interrupts helpers.
pre_ssout()could just dolocal_irq_enable(). This path leads toget_signal_to_deliver()which enables irqs anyway, it is always safe to do this earlier and I don't think we need to disable irqs again later. In any case, I don't understand why these helpers usenative_irq_xxx().
On i686, (unlike x86_64), do_notify_resume() gets called with irqs disabled. I had tried local_irq_enable couple of times but that didnt help probably because CONFIG_PARAVIRT is set in my .config and hence raw_local_irq_enable resolves to
static inline void raw_local_irq_enable(void)
{
PVOP_VCALLEE0(pv_irq_ops.irq_enable);
}What we need is the "sti" instruction. It looks like local_irq_enable actually doesnt do "sti". So I had to go back to using native_irq_enable().
Do you have any ideas how to force local_irq_enable to do a "sti." Or Am I missing something?
Since I wasnt sure why do_notify_resume() was called under irqs_disabled only for x86. I disabled irqs again just to be sure that I am not messing some assumption.
Quoted 4 lines
-
pre_ssout()does .xol_vaddr =xol_get_insn_slot(). This looks a bit confusing,xol_get_insn_slot()should set .xol_vaddr correctly under lock.
Can you please elaborate.
Quoted 5 lines
- pre_ssout() does user_bkpt_set_ip() after user_bkpt_pre_sstep(). Why? Shouldn't user_bkpt_pre_sstep() always set regs->ip ? Otherwise uprobe_bkpt_notifier()->user_bkpt_pre_sstep() is not right.
Right, user_bkpt_set_ip is redudant as user_bkpt_pre_sstep sets regs->ip. I will remove user_bkpt_set_ip after user_bkpt_pre_sstep
Quoted 3 lines
- I don't really understand why ->handler_in_interrupt is really useful, but never mind.
Uprobes can run handlers either in interrupt context or in task context. If the user is sure that his handler is not going to sleep, then he can set handler_in_interrupt flag while registering the probe.
There is a small overhead when running the handlers in task context. Here is a brief benchmark on a x86_64 machine.
========================================================================
Results when running a kernel without lockdep and other debug kernel / kernel hacking features.
Running benchmark not probed
pid is 5236
10000 interations in 0.000049 sec
0.004900 usec per iteration
sum = 50005000
Running benchmark .. interrupt context handler pid is 5252
10000 interations in 0.009123 sec
0.912300 usec per iteration
sum = 50005000
overhead per probe = (0.912300-0.004900) = .907400 usec
Running benchmark .. task context handler
pid is 5268
10000 interations in 0.010169 sec
1.016900 usec per iteration
sum = 50005000
overhead per probe = (1.016900-0.000049) = 1.016851 usec
overhead of task over interrupt = (1.016851 - .907400) = .109451 usec % additional overhead = (.109451/.907400) * 100 = 12.062%
=====================================================================
Results when running a kernel with lockdep and other debug kernel / kernel hacking features.
Running benchmark not probed
pid is 3778
10000 interations in 0.000043 sec
0.004300 usec per iteration
sum = 50005000
Running benchmark .. interrupt context handler pid is 3794
10000 interations in 0.020318 sec
2.031800 usec per iteration
sum = 50005000
overhead per probe = (2.031800-0.004300) = 2.027500 usecs
Running benchmark .. task context handler
pid is 3810
10000 interations in 0.021790 sec
2.179000 usec per iteration
sum = 50005000
overhead per probe = (2.179000-0.004300) = 2.174700 usecs
overhead of task over interrupt = (2.174700 - 2.027500) = .147200 usec %additional overhead = (0.147200/2.027500) * 100 = 7.26
=======================================================================
Quoted 5 lines
- However,
handler_in_interrupt&& !uses_xol_strategy() doesn't look right.uprobe_bkpt_notifier()is called with irqs disabled, right? butset_orig_insn()ismight_sleep().
Yes, Uprobes currently supports only xol strategy. I.e I have
dropped single stepping inline strategy for uprobes. Hence when user_bkpt_pre_sstep gets called from uprobe_bkpt_notifier; we are sure that it doesnt call set_orig_insn().
[ snip ... ]
Quoted 10 lines
Quoted 9 lines
Quoted 4 lines
OK, iiuc this should restore the original instruction, right?
But what about
clone(CLONE_VM)? In this case this child shares ->mm with parent.Okay, So I will remove the breakpoints only for !
CLONE(CLONE_VM)and !CLONE(CLONE_THREAD)
ForCLONE(CLONE_VM)I will create a new uproc and utask structures. Since mm is shared; I guess the XOL vma gets shared between the processes.
Quoted 8 lines
Yes, I think
CLONE_VMwithoutCLONE_THREADneeds utask too, but do we need the new uproc? OK, please forget about this for the moment.Suppose that
register_uprobe()succeeds and doesset_bkpt(). What if another process (not sub-thread) with the same ->mm hits this bp?uprobe_bkpt_notifier()will see ->utask == NULL and return 0. Thendo_int3()sends SIGTRAP and kills this task. OK, probablyCLONE_VMalone is exotic, butCLONE_VFORK| VM is not.
I was looking at duplicating the uprobe_process. But yes that may not be a nice idea, esp if new probes get added then we would have to take care of updating both the uprobe_process lists.
Yes, we can share the uprobe_process shared between the process that share mm. Thats a definitely a better idea than duplicating.
Quoted 1 line
I think
uprobe_processshould be per ->mm, not per-process.
True, One possibility could be to move the uprobe_process structure to mm_struct. But now sure if VM folks would be okay with that idea.
Quoted 3 lines
I wonder if there any possibility to avoid
task_struct->utask, or at least, if we can allocate it inuprobe_bkpt_notifier()on demand. Not sure.
Except for the pointer to uprobe_process, all other fields in utask are per task. This per task information is mostly used at probe hit. Hence having it in task_struct makes it easily accessible. Do you have other ideas from where we could refer utask.
I did think about allocating a utask on the first hit of a breakpoint. However there are couple of issues.
1. Uprobes needs access to uprobe_process to search the breakpoints installed for that process. Currently we hang it out of utask. However if uprobe_process is made a part of mm_struct, this would no
more be an issue.
2. Currently when a breakpoint is hit, uprobes increments the refcount for the corresponding probepoint, and sets active_ppt in the utask for the current thread. This happens in interrupt context. Allocating utask on first breakpoint hit for that thread; has to be handled in task context. If the utask has to be allocated, then uprobes has to search for the probepoint again in task context.
I dont think it would be an issue to search for the probepoint a
second time in the task context.
--
Thanks and Regards
Srikar
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 10 lines · Srikar Dronamraju
Quoted 7 lines
Quoted 5 lines
Quoted 5 lines
kernel/uprobes.c line 212
Quoted 4 lines
kernel/uprobes.c new file +3
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) + tsk =next_thread (tsk);I meant, it is not safe to use
next_thread(tsk)if tsk was already removed from list by__unhash_processbefore we takercu_read_lock().Okay,
cleanup_process()gets called only and only ifadd_utask()fails to allocated utask struct.
Yes, but afaics we have the same issues in find_next_thread() called by create_uprocess().
Quoted 4 lines
Based on your inputs I will synchronize
exit_signals()anduprobe_free_utask(). However it still can happen that uprobe callscleanup_uprocess()with reference to task struct which has just called__unhash_process(). Is there a way out of this?
In this particular case, probably we can rely on uprobe_mutex. Currently cleanup_uprocess() is called with start == cur_t. Instead, we should use the last task on which add_utask() succeeded, it can't exit (assuming we fix other discussed races with exit) because uprobe_free_utask() takes this mutex too.
However, perhaps it is better to rework this all. Say, can't we move uprobe_free_utask() into __put_task_struct() ? Afaics, this can greatly simplify things. If we add mm_struct->uproc, then utask doesn't need the pointer to uprobe_process.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 9 lines
kernel/uprobes.c line 373
Quoted 7 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);Is it really possible to call register/unregister with nsproxy == NULL?
Quoted 3 lines
Quoted 1 line
You didn't answer ;)
Can you please let me know when nsproxy is set to NULL?
exit_notify()->exit_task_namespaces()
Quoted 3 lines
If we are sure
that register/unregister will be called with nsproxy set, then I am happy to remove this check.
I think the exiting task shouldn't call register/unregister.
Quoted 4 lines
Quoted 1 line
-
uprobe_process->tg_leaderis not really used ?Currently we have a reference to pid struct from the time we created a
uprobe_processto the time we free the uprobe process.
Yes, but
Quoted 4 lines
So are you
suggesting that we dont have a reference to the pid structure or is that we dont need to cache the pid struct and access it throtask_pid(current)infree_uprobes()?
I must have missed something. But I do not see where do we use uprobe_process->tg_leader. We never read it, apart from BUG_ON(uproc->tg_leader != tg_leader). No?
Quoted 12 lines
Quoted 6 lines
- I don't understand why do we need uprobe_{en,dis}able_interrupts helpers.
pre_ssout()could just dolocal_irq_enable(). This path leads toget_signal_to_deliver()which enables irqs anyway, it is always safe to do this earlier and I don't think we need to disable irqs again later. In any case, I don't understand why these helpers usenative_irq_xxx().On i686, (unlike
x86_64),do_notify_resume()gets called with irqs disabled. I had triedlocal_irq_enablecouple of times but that didnt help probably becauseCONFIG_PARAVIRTis set in my .config and henceraw_local_irq_enableresolves to
Quoted 8 lines
static inline void
raw_local_irq_enable(void){ PVOP_VCALLEE0(pv_irq_ops.irq_enable); }What we need is the "sti" instruction. It looks like
local_irq_enableactually doesnt do "sti". So I had to go back to usingnative_irq_enable().
Hmm. No, I can't explain this, I know nothing about paravirt. But this doesn't look right to me. Probably this should be discussed with paravirt developers...
Quoted 5 lines
Quoted 3 lines
-
pre_ssout()does .xol_vaddr =xol_get_insn_slot(). This looks a bit confusing,xol_get_insn_slot()should set .xol_vaddr correctly under lock.Can you please elaborate.
pre_ssout() does
if (!user_bkpt.xol_vaddr) user_bkpt.xol_vaddr = xol_get_insn_slot();
but it could just do
if (!user_bkpt.xol_vaddr) xol_get_insn_slot();
because xol_get_insn_slot() populates user_bkpt.xol_vaddr.
Btw. Why do we have the !CONFIG_USER_BKPT_XOL code in include/linux/user_bkpt_xol.h? CONFIG_UPROBES depends on CONFIG_USER_BKPT_XOL.
Also the declarations don't look nice... Probably I missed something, but why the code uses "void *" instead of "user_bkpt_xol_area *" for xol_area everywhere?
OK, even if "void *" makes sense for uproc->uprobe_process, why xol_alloc_area/xol_get_insn_slot/etc do not use "user_bkpt_xol_area *" ?
Quoted 4 lines
Quoted 2 lines
- I don't really understand why ->handler_in_interrupt is really useful, but never mind.
There is a small overhead when running the handlers in task context.
Sure, but
Quoted 2 lines
overhead of task over interrupt = (1.016851 - .907400) = .109451 usec % additional overhead = (.109451/.907400) * 100 = 12.062%
this overhead looks very minor. To me, it is better to simplify the code, at least in the first version.
That said, this is up to you, I am not asking you to remove this optimization. Just imho.
Quoted 9 lines
Quoted 4 lines
- However,
handler_in_interrupt&& !uses_xol_strategy() doesn't look right.uprobe_bkpt_notifier()is called with irqs disabled, right? butset_orig_insn()ismight_sleep().Yes, Uprobes currently supports only xol strategy. I.e I have
dropped single stepping inline strategy for uprobes. Hence whenuser_bkpt_pre_sstepgets called fromuprobe_bkpt_notifier; we are sure that it doesnt callset_orig_insn().
OK, thanks. Perhaps a small comment to explain this makes sense...
Quoted 9 lines
Quoted 6 lines
Suppose that
register_uprobe()succeeds and doesset_bkpt(). What if another process (not sub-thread) with the same ->mm hits this bp?uprobe_bkpt_notifier()will see ->utask == NULL and return 0. Thendo_int3()sends SIGTRAP and kills this task. OK, probablyCLONE_VMalone is exotic, butCLONE_VFORK| VM is not.
...
I thinkuprobe_processshould be per ->mm, not per-process.True, One possibility could be to move the
uprobe_processstructure tomm_struct. But now sure if VM folks would be okay with that idea.
Yes, I was thinking about mm->struct->uproc too.
And, assuming we have this pointer in mm_struct:
Quoted 7 lines
Quoted 2 lines
I wonder if there any possibility to avoid
task_struct->utask, or at least, if we can allocate it inuprobe_bkpt_notifier()on demand. Not sure.Except for the pointer to
uprobe_process, all other fields in utask are per task. This per task information is mostly used at probe hit. Hence having it intask_structmakes it easily accessible. Do you have other ideas from where we could refer utask.
Well, we could add the list of uprobe_task's into uprobe_process, it represents the tasks "inside" the probe hit. But yes, this is not easy, lets forget this, at least for now.
Quoted 7 lines
I did think about allocating a utask on the first hit of a breakpoint. However there are couple of issues.
1. Uprobes needs access to
uprobe_processto search the breakpoints installed for that process. Currently we hang it out of utask. However ifuprobe_processis made a part ofmm_struct, this would no
more be an issue.
Yes,
Quoted 5 lines
2. Currently when a breakpoint is hit, uprobes increments the refcount for the corresponding probepoint, and sets
active_pptin the utask for the current thread. This happens in interrupt context. Allocating utask on first breakpoint hit for that thread; has to be handled in task context.
we could use GFP_ATOMIC, but I agree, this is not nice.
Quoted 4 lines
If the utask has to be allocated, then uprobes has to search for the probepoint again in task context.
I dont think it would be an issue to search for the probepoint a
second time in the task context.
Agreed. Although we need the new TIF_ bit for tracehook_notify_resume(), it can't trust "if (current->utask...)" checks.
Alternatively, without the "on demand" allocations, register_uprobe() has to find all threads which use the same ->mm and initialize ->utask. This is not easy.
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 6 lines · Oleg Nesterov
Quoted 5 lines · Srikar Dronamraju
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
kernel/uprobes.c new file +3
-0+static void cleanup_uprocess (struct uprobe_process *uproc, +struct task_struct *start) + tsk =next_thread (tsk);
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
I meant, it is not safe to use
next_thread(tsk)if tsk was already removed from list by__unhash_processbefore we takercu_read_lock().Okay,
cleanup_process()gets called only and only ifadd_utask()fails to allocated utask struct.Yes, but afaics we have the same issues in
find_next_thread()called bycreate_uprocess().
Okay.
Quoted 12 lines
Quoted 5 lines
Based on your inputs I will synchronize
exit_signals()anduprobe_free_utask(). However it still can happen that uprobe callscleanup_uprocess()with reference to task struct which has just called__unhash_process(). Is there a way out of this?In this particular case, probably we can rely on
uprobe_mutex. Currentlycleanup_uprocess()is called with start ==cur_t. Instead, we should use the last task on whichadd_utask()succeeded, it can't exit (assuming we fix other discussed races with exit) becauseuprobe_free_utask()takes this mutex too.
Quoted 4 lines
However, perhaps it is better to rework this all. Say, can't we move
uprobe_free_utask()into__put_task_struct()? Afaics, this can greatly simplify things. If we addmm_struct->uproc, then utask doesn't need the pointer touprobe_process.
Okay. I will use mm_struct->uproc, dynamic allocation of utask on probe hit and freeing of utask on __put_task_struct.
Quoted 15 lines
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
Quoted 10 lines
kernel/uprobes.c line 373
Quoted 8 lines
kernel/uprobes.c new file +7
-0+static struct pid *get_tg_leader (pid_t p) +{ +struct pid *pid =NULL ; + +rcu_read_lock (); +if (current->nsproxy) + pid =find_vpid (p);Is it really possible to call register/unregister with nsproxy == NULL?
Quoted 3 lines
Quoted 1 line
You didn't answer ;)
Can you please let me know when nsproxy is set to NULL?
Quoted 7 lines
exit_notify()->exit_task_namespaces()
Quoted 3 lines
If we are sure
that register/unregister will be called with nsproxy set, then I am happy to remove this check.I think the exiting task shouldn't call register/unregister.
Okay I will remove the check for current->nsproxy being non-NULL.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
-
uprobe_process->tg_leaderis not really used ?Currently we have a reference to pid struct from the time we created a
uprobe_processto the time we free the uprobe process.Yes, but
Quoted 8 lines
Quoted 4 lines
So are you
suggesting that we dont have a reference to the pid structure or is that we dont need to cache the pid struct and access it throtask_pid(current)infree_uprobes()?I must have missed something. But I do not see where do we use
uprobe_process->tg_leader. We never read it, apart fromBUG_ON(uproc->tg_leader != tg_leader). No?
static int free_uprocess(struct uprobe_process *uproc)
{
....
put_pid(uproc->tg_leader);
uproc->tg_leader = NULL;}
Quoted 18 lines
Quoted 18 lines
Quoted 7 lines
- I don't understand why do we need uprobe_{en,dis}able_interrupts helpers.
pre_ssout()could just dolocal_irq_enable(). This path leads toget_signal_to_deliver()which enables irqs anyway, it is always safe to do this earlier and I don't think we need to disable irqs again later. In any case, I don't understand why these helpers usenative_irq_xxx().On i686, (unlike
x86_64),do_notify_resume()gets called with irqs disabled. I had triedlocal_irq_enablecouple of times but that didnt help probably becauseCONFIG_PARAVIRTis set in my .config and henceraw_local_irq_enableresolves tostatic inline void
raw_local_irq_enable(void){ PVOP_VCALLEE0(pv_irq_ops.irq_enable); }
Quoted 7 lines
Quoted 3 lines
What we need is the "sti" instruction. It looks like
local_irq_enableactually doesnt do "sti". So I had to go back to usingnative_irq_enable().Hmm. No, I can't explain this, I know nothing about paravirt. But this doesn't look right to me. Probably this should be discussed with paravirt developers...
Okay.
Quoted 12 lines
Quoted 6 lines
Quoted 4 lines
-
pre_ssout()does .xol_vaddr =xol_get_insn_slot(). This looks a bit confusing,xol_get_insn_slot()should set .xol_vaddr correctly under lock.Can you please elaborate.
pre_ssout()doesif (!user_bkpt.xol_vaddr) user_bkpt.xol_vaddr = xol_get_insn_slot();
Quoted 6 lines
but it could just do
if (!user_bkpt.xol_vaddr) xol_get_insn_slot();because
xol_get_insn_slot()populates user_bkpt.xol_vaddr.
Agreed
Quoted 3 lines
Btw. Why do we have the !CONFIG_USER_BKPT_XOL code in
include/linux/user_bkpt_xol.h?CONFIG_UPROBESdepends onCONFIG_USER_BKPT_XOL.
Okay we can remove the !CONFIG_USER_BKPT_XOL code in user_bkpt_xol.h
Quoted 8 lines
Also the declarations don't look nice... Probably I missed something, but why the code uses "void *" instead of "
user_bkpt_xol_area*" forxol_areaeverywhere?OK, even if "void *" makes sense for
uproc->uprobe_process, why xol_alloc_area/xol_get_insn_slot/etc do not use "user_bkpt_xol_area*" ?
user_bkpt_xol_area isn't exposed. This provides flexibility in changing the algorithm for more efficient slot allocation. Currently we allocate slots from just one page. Later on we could end-up having to allocate from more than contiguous pages. There was some discussion about allocating slots from TLS. So there is more than one reason that user_bkpt_xol can change. We could expose the struct and not access the fields directly but that would be hard to enforce.
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
- I don't really understand why ->handler_in_interrupt is really useful, but never mind.
There is a small overhead when running the handlers in task context.
Sure, but
Quoted 8 lines
Quoted 2 lines
overhead of task over interrupt = (1.016851 - .907400) = .109451 usec % additional overhead = (.109451/.907400) * 100 = 12.062%
this overhead looks very minor. To me, it is better to simplify the code, at least in the first version.
That said, this is up to you, I am not asking you to remove this optimization. Just imho.
Okay.
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
- However,
handler_in_interrupt&& !uses_xol_strategy() doesn't look right.uprobe_bkpt_notifier()is called with irqs disabled, right? butset_orig_insn()ismight_sleep().
Quoted 6 lines
Quoted 4 lines
Yes, Uprobes currently supports only xol strategy. I.e I have
dropped single stepping inline strategy for uprobes. Hence whenuser_bkpt_pre_sstepgets called fromuprobe_bkpt_notifier; we are sure that it doesnt callset_orig_insn().OK, thanks. Perhaps a small comment to explain this makes sense...
Okay.
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Suppose that
register_uprobe()succeeds and doesset_bkpt(). What if another process (not sub-thread) with the same ->mm hits this bp?uprobe_bkpt_notifier()will see ->utask == NULL and return 0. Thendo_int3()sends SIGTRAP and kills this task. OK, probablyCLONE_VMalone is exotic, butCLONE_VFORK| VM is not.
...
I thinkuprobe_processshould be per ->mm, not per-process.
Quoted 5 lines
Quoted 2 lines
True, One possibility could be to move the
uprobe_processstructure tomm_struct. But now sure if VM folks would be okay with that idea.Yes, I was thinking about
mm->struct->uproctoo.
Okay, I will try with mm_struct->uproc.
Quoted 32 lines
And, assuming we have this pointer in
mm_struct:Quoted 7 lines
Quoted 2 lines
I wonder if there any possibility to avoid
task_struct->utask, or at least, if we can allocate it inuprobe_bkpt_notifier()on demand. Not sure.Except for the pointer to
uprobe_process, all other fields in utask are per task. This per task information is mostly used at probe hit. Hence having it intask_structmakes it easily accessible. Do you have other ideas from where we could refer utask.Well, we could add the list of uprobe_task's into
uprobe_process, it represents the tasks "inside" the probe hit. But yes, this is not easy, lets forget this, at least for now.Quoted 7 lines
I did think about allocating a utask on the first hit of a breakpoint. However there are couple of issues.
1. Uprobes needs access to
uprobe_processto search the breakpoints installed for that process. Currently we hang it out of utask. However ifuprobe_processis made a part ofmm_struct, this would no
more be an issue.Yes,
Quoted 5 lines
2. Currently when a breakpoint is hit, uprobes increments the refcount for the corresponding probepoint, and sets
active_pptin the utask for the current thread. This happens in interrupt context. Allocating utask on first breakpoint hit for that thread; has to be handled in task context.we could use
GFP_ATOMIC, but I agree, this is not nice.
Quoted 7 lines
Quoted 4 lines
If the utask has to be allocated, then uprobes has to search for the probepoint again in task context.
I dont think it would be an issue to search for the probepoint a
second time in the task context.Agreed. Although we need the new
TIF_bit fortracehook_notify_resume(), it can't trust "if (current->utask...)" checks.
But do we need a new TIF bit? Can we just reuse the TIF_NOTIFY_RESUME flag that we use now?
Quoted 5 lines
Alternatively, without the "on demand" allocations,
register_uprobe()has to find all threads which use the same ->mm and initialize ->utask. This is not easy.
Okay I will try the on demand allocations in the next iteration.
Thanks again for your detailed explainations and suggestions.
--
Thanks and Regards
Srikar
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 7 lines · Srikar Dronamraju
Quoted 3 lines · Oleg Nesterov
I must have missed something. But I do not see where do we use
uprobe_process->tg_leader. We never read it, apart fromBUG_ON(uproc->tg_leader != tg_leader). No?
Quoted 7 lines
static int
free_uprocess(struct uprobe_process *uproc){ .... put_pid(uproc->tg_leader); uproc->tg_leader = NULL;}
Yes, yes, I see it does get/put pid. But where do we actually use uproc->tg_leader? Why it is needed at all?
Quoted 5 lines
Quoted 5 lines
Also the declarations don't look nice... Probably I missed something, but why the code uses "void *" instead of "
user_bkpt_xol_area*" forxol_areaeverywhere?OK, even if "void *" makes sense for
uproc->uprobe_process,
^^^^^^^^^^^^^^^^^^^^^
I meant uprobe_process->xol_area
Quoted 11 lines
why
Quoted 2 lines
xol_alloc_area/xol_get_insn_slot/etc do not use "
user_bkpt_xol_area*" ?
user_bkpt_xol_areaisn't exposed. This provides flexibility in changing the algorithm for more efficient slot allocation. Currently we allocate slots from just one page. Later on we could end-up having to allocate from more than contiguous pages. There was some discussion about allocating slots from TLS. So there is more than one reason thatuser_bkpt_xolcan change. We could expose the struct and not access the fields directly but that would be hard to enforce.
Still can't understand... Yes, we shouldn't expose the details, but we can just add "struct user_bkpt_xol_area;" into include file.
OK, this is minor.
Quoted 10 lines
Quoted 7 lines
Quoted 4 lines
If the utask has to be allocated, then uprobes has to search for the probepoint again in task context.
I dont think it would be an issue to search for the probepoint a
second time in the task context.Agreed. Although we need the new
TIF_bit fortracehook_notify_resume(), it can't trust "if (current->utask...)" checks.But do we need a new TIF bit? Can we just reuse the
TIF_NOTIFY_RESUMEflag that we use now?
Probably not... But somehow tracehook_notify_resume/uprobe_notify_resume should know we hit the bp and we need to allocate utask. Yes, tracehook_notify_resume() can always call uprobe_notify_resume() unconditionally, and uprobe_notify_resume() can notice the "find_probept() && !current->utask" case, but probably it is better to make this more explicit. And of course, the new bit should be set along with TIF_NOTIFY_RESUME.
Or. Instead of TIF_ bit, we can use something like
#define UTASK_PLEASE_ALLOCATE_ME ((struct uprobe_task *)1)
uprobe_bkpt_notifier() sets current->utask = UTASK_PLEASE_ALLOCATE_ME, then tracehook_notify_resume/uprobe_notify_resume check this case.
I dunno, please do what you think right.
OK, the last questions:
1. Can't multiple write_opcode()'s race with each other?
Say, pre_ssout() calls remove_bkpt() lockless. can't it race with register_uprobe() which may write to the same page?
And, without uses_xol_strategy() there are more racy callers of write_opcode()... Probably something else.
2. Can't write_opcode() conflict with ksm doing replace_page() ?
3. mprotect(). write_opcode() checks !VM_WRITE. This is correct, otherwise we can race with the user-space writing to the same page.
But suppose that the application does mprotect(PROT_WRITE) after register_uprobe() installs the bp, now unregister_uprobe/etc can't restore the original insn?
4. mremap(). What if the application does mremap() and moves the memory? After that vaddr of user_bkpt/uprobe no longer matches the virtual address of bp. This breaks uprobe_bkpt_notifier(), unregister_uprobe(), etc.
Even worse. Say, unregister_uprobe() calls remove_bkpt(). mremap()+mmap() can be called after ->read_opcode() verifies vaddr points to bkpt_insn, but before write_opcode() changes the page.
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 14 lines · Oleg Nesterov
Quoted 13 lines · Srikar Dronamraju
Quoted 3 lines · Oleg Nesterov
I must have missed something. But I do not see where do we use
uprobe_process->tg_leader. We never read it, apart fromBUG_ON(uproc->tg_leader != tg_leader). No?static int
free_uprocess(struct uprobe_process *uproc){ .... put_pid(uproc->tg_leader); uproc->tg_leader = NULL;
Quoted 4 lines
Quoted 1 line
}
Yes, yes, I see it does get/put pid. But where do we actually use
uproc->tg_leader? Why it is needed at all?
uproc->tg_leader was used to validate looked up uproc belongs to the process. It was used to check if the uproc belonged to the process for which we are currently trying to register/unregister uprobes.
Since we want to share the uproc with process that share the same mm, I agree that its better off we remove the tg_leader.
Quoted 21 lines
Quoted 6 lines
Quoted 6 lines
Also the declarations don't look nice... Probably I missed something, but why the code uses "void *" instead of "
user_bkpt_xol_area*" forxol_areaeverywhere?OK, even if "void *" makes sense for
uproc->uprobe_process,^^^^^^^^^^^^^^^^^^^^^I meant
uprobe_process->xol_areaQuoted 11 lines
why
Quoted 2 lines
xol_alloc_area/xol_get_insn_slot/etc do not use "
user_bkpt_xol_area*" ?
user_bkpt_xol_areaisn't exposed. This provides flexibility in changing the algorithm for more efficient slot allocation. Currently we allocate slots from just one page. Later on we could end-up having to allocate from more than contiguous pages. There was some discussion about allocating slots from TLS. So there is more than one reason thatuser_bkpt_xolcan change. We could expose the struct and not access the fields directly but that would be hard to enforce.
Quoted 4 lines
Still can't understand... Yes, we shouldn't expose the details, but we can just add "struct
user_bkpt_xol_area;" into include file.OK, this is minor.
Okay, I will add the forward declaration in the include file and update the function prototypes accordingly.
Quoted 20 lines
Quoted 11 lines
Quoted 8 lines
Quoted 5 lines
If the utask has to be allocated, then uprobes has to search for the probepoint again in task context.
I dont think it would be an issue to search for the probepoint a
second time in the task context.Agreed. Although we need the new
TIF_bit fortracehook_notify_resume(), it can't trust "if (current->utask...)" checks.But do we need a new TIF bit? Can we just reuse the
TIF_NOTIFY_RESUMEflag that we use now?Probably not... But somehow tracehook_notify_resume/uprobe_notify_resume should know we hit the bp and we need to allocate utask. Yes,
tracehook_notify_resume()can always calluprobe_notify_resume()unconditionally, anduprobe_notify_resume()can notice the "find_probept()&& !current->utask" case, but probably it is better to make this more explicit. And of course, the new bit should be set along withTIF_NOTIFY_RESUME.
Quoted 9 lines
Or. Instead of
TIF_bit, we can use something like#define UTASK_PLEASE_ALLOCATE_ME ((struct uprobe_task *)1)
uprobe_bkpt_notifier()setscurrent->utask=UTASK_PLEASE_ALLOCATE_ME, then tracehook_notify_resume/uprobe_notify_resume check this case.I dunno, please do what you think right.
Yeah, since tracehook_notify_resume() is in fast path, its worth adding a new TIF flag.
Quoted 4 lines
OK, the last questions:
1. Can't multiple write_opcode()'s race with each other?
All callers of write_opcodes should have taken uproc->mutex. If there are other users of write_opcode, we will have to add a way to synchronize this.
Quoted 3 lines
Say,
pre_ssout()callsremove_bkpt()lockless. can't it race withregister_uprobe()which may write to the same page?
That's a bug, I will fix it. remove_bkpt() clearly says it needs to be called with uproc->mutex held.
Quoted 3 lines
And, without
uses_xol_strategy()there are more racy callers ofwrite_opcode()... Probably something else.
Okay.
Quoted 2 lines
2. Can't
write_opcode()conflict with ksm doingreplace_page()?
I dont think so.
If uprobes runs on hosts, it would be calling replace_page() on text pages. KSM for now works on anonymous pages. Even the replaced page we add still belongs to the text VMA.
If uprobes runs on guest, KSM should be taking care of cases where similar pages are inserted/deleted
Quoted 9 lines
3.
mprotect().write_opcode()checks !VM_WRITE. This is correct, otherwise we can race with the user-space writing to the same page.But suppose that the application does
mprotect(PROT_WRITE)afterregister_uprobe()installs the bp, now unregister_uprobe/etc can't restore the original insn?
I still need to verify this. I shall get back to you on this. However are there applications that mprotect(PROT_WRITE) text pages?
Quoted 9 lines
4.
mremap(). What if the application doesmremap()and moves the memory? After that vaddr of user_bkpt/uprobe no longer matches the virtual address of bp. This breaksuprobe_bkpt_notifier(),unregister_uprobe(), etc.Even worse. Say,
unregister_uprobe()callsremove_bkpt(). mremap()+mmap() can be called after ->read_opcode() verifies vaddr points tobkpt_insn, but beforewrite_opcode()changes the page.
I dont think we handle this case now. I think even munmap of the region where there are probes inserted also can have the same problem.
Are there ways to handle this.
I think taking a write lock on mmap_sem instead of the read lock could handle this problem.
I am copying Mel Gorman and Andrea Arcangeli so that they can provide their inputs on VM and KSM related issues.
--
Thanks and Regards
Srikar
Quoted 1 line
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 7 lines · Srikar Dronamraju
Quoted 4 lines · Oleg Nesterov
3.
mprotect().write_opcode()checks !VM_WRITE. This is correct, otherwise we can race with the user-space writing to the same page.
Quoted 7 lines
Quoted 4 lines
But suppose that the application does
mprotect(PROT_WRITE)afterregister_uprobe()installs the bp, now unregister_uprobe/etc can't restore the original insn?I still need to verify this. I shall get back to you on this. However are there applications that
mprotect(PROT_WRITE)text pages?
Well, I think the kernel should assume that the user-space can do anything.
Hmm. And if this vma is VM_SHARED, then this bp could be actually written to vm_file after mprotect().
But I think this doesn't really matter. When I actually look at patches 3 and 4, I am starting to think this all is very wrong.
Quoted 2 lines
I am copying Mel Gorman and Andrea Arcangeli so that they can provide their inputs on VM and KSM related issues.
Yes. We need vm experts here, I am not. Still, I'd like to share my concerns. I also added Rik and Hugh.
So, 3/11 does
@@ -2617,7 +2617,10 @@ int replace_page(struct vm_area_struct *vma, struct page *page, } get_page(kpage); - page_add_anon_rmap(kpage, vma, addr); + if (PageAnon(kpage)) + page_add_anon_rmap(kpage, vma, addr); + else + page_add_file_rmap(kpage); flush_cache_page(vma, addr, pte_pfn(*ptep)); ptep_clear_flush(vma, addr, ptep);
I see no point in this patch, please see below.
The next 4/11 patch introduces write_opcode() which roughly does:
int write_opcode(unsigned long vaddr, user_bkpt_opcode_t opcode)
{
get_user_pages(write => false, &old_page);
new_page = alloc_page_vma(...);
... insert the bp into the new_page ...
new_page->mapping = old_page->mapping;
new_page->index = old_page->index;
replace_page(old_page, new_page);
}This doesn't look right at all to me.
IF PageAnon(old_page):
in this case replace_page() calls page_add_anon_rmap() which needs the locked page.
ELSE:
I don't think the new page should evere preserve the mapping, this looks just wrong. It should be always anonymous.
And in fact, I do not understand why write_opcode() needs replace_page(). It could just use get_user_pages(FOLL_WRITE | FOLL_FORCE), no? It should create the anonymous page correctly.
Either way, I think register_uprobe() should disallow the probes in VM_SHARED/VM_MAYWRITE vmas.
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 13 lines · Oleg Nesterov
Quoted 12 lines · Srikar Dronamraju
Quoted 8 lines · Oleg Nesterov
3.
mprotect().write_opcode()checks !VM_WRITE. This is correct, otherwise we can race with the user-space writing to the same page.But suppose that the application does
mprotect(PROT_WRITE)afterregister_uprobe()installs the bp, now unregister_uprobe/etc can't restore the original insn?
Quoted 8 lines
Quoted 2 lines
I still need to verify this. I shall get back to you on this. However are there applications that
mprotect(PROT_WRITE)text pages?Well, I think the kernel should assume that the user-space can do anything.
Hmm. And if this vma is
VM_SHARED, then this bp could be actually written tovm_fileaftermprotect().
When I look through the load_.*_binary and load_.*_library functions, they seem to map the text regions MAP_PRIVATE|MAP_DENY_WRITE. (Few exceptions like load_som_binary that seem to map text regions with MAP_PRIVATE only).
Also if vma are marked VM_SHARED and bp are inserted through ptrace, i.e(access_process_vm/get_user_pages), then we would still be writing to vm_file after mprotect?
Again, I am not sure if executable pages should be marked VM_SHARED.
Quoted 44 lines
But I think this doesn't really matter. When I actually look at patches 3 and 4, I am starting to think this all is very wrong.
Quoted 2 lines
I am copying Mel Gorman and Andrea Arcangeli so that they can provide their inputs on VM and KSM related issues.
Yes. We need vm experts here, I am not. Still, I'd like to share my concerns. I also added Rik and Hugh.
So, 3/11 does
@@ -2617,7 +2617,10 @@ int replace_page(struct vm_area_struct *vma, struct page *page, } get_page(kpage); - page_add_anon_rmap(kpage, vma, addr); + if (PageAnon(kpage)) + page_add_anon_rmap(kpage, vma, addr); + else + page_add_file_rmap(kpage); flush_cache_page(vma, addr, pte_pfn(*ptep)); ptep_clear_flush(vma, addr, ptep);I see no point in this patch, please see below.
The next 4/11 patch introduces
write_opcode()which roughly does:int write_opcode(unsigned long vaddr, user_bkpt_opcode_t opcode) { get_user_pages(write => false, &old_page); new_page = alloc_page_vma(...); ... insert the bp into the new_page ... new_page->mapping = old_page->mapping; new_page->index = old_page->index; replace_page(old_page, new_page); }
Quoted 3 lines
This doesn't look right at all to me.
IF
PageAnon(old_page):
^^^ newpage
Quoted 8 lines
in this case
replace_page()callspage_add_anon_rmap()which needs the locked page.ELSE:
I don't think the new page should evere preserve the mapping, this looks just wrong. It should be always anonymous.
I did verify that page_add_file_rmap gets called from replace_page when we insert or remove a probe.
This should be because uprobes doesnt do a anon_vma_prepare() before the alloc_page_vma().
I would leave it for vm experts to decide what the right thing to do.
Quoted 5 lines
And in fact, I do not understand why
write_opcode()needsreplace_page(). It could just useget_user_pages(FOLL_WRITE | FOLL_FORCE), no? It should create the anonymous page correctly.
We were earlier doing access_process_vm that would inturn call get_user_pages to COW the page. However that needed that the threads of the target process be stopped.
In the access_process_vm method,
1. we get a copy of page,
2. flush the tlbs.
3. modify the page.
The concern was if the threads were executing in the vicinity. Hence we were stopping all threads while inserting/deleting breakpoints.
Background page replacement was suggested by Linus and Peter. In this method.
1. we get a copy of the page.
2. modify the page
3. flush the tlbs.
This method is suppose to be atomic enuf that we dont need to stop the threads.
Quoted 3 lines
Either way, I think
register_uprobe()should disallow the probes in VM_SHARED/VM_MAYWRITE vmas.
Yes, we certainly could add that check.
--
Thanks and Regards
Srikar
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 8 lines · Srikar Dronamraju
Quoted 5 lines · Oleg Nesterov
Quoted 3 lines · Srikar Dronamraju
I still need to verify this. I shall get back to you on this. However are there applications that
mprotect(PROT_WRITE)text pages?
Quoted 8 lines
Quoted 5 lines
Well, I think the kernel should assume that the user-space can do anything.
Hmm. And if this vma is
VM_SHARED, then this bp could be actually written tovm_fileaftermprotect().When I look through the load_.*_binary and load_.*_library functions, they seem to map the text regions MAP_PRIVATE|MAP_DENY_WRITE.
Sure, I didn't mean exec can use MAP_SHARED or mprotect().
Quoted 3 lines
Also if vma are marked
VM_SHAREDand bp are inserted through ptrace,i.e(access_process_vm/get_user_pages), then we would still be writing tovm_fileafter mprotect?
Yes, that is why I mentioned register_uprobe() should check SHARED/MAYWRITE.
Quoted 1 line
Again, I am not sure if executable pages should be marked
VM_SHARED.
Again, I didn't mean they should. But they can.
Not only VM_SHARED, the application can create the anonymous PROT_EXEC region, in this case write_opcode() looks wrong, please see below.
Quoted 28 lines
Quoted 28 lines
@@ -2617,7 +2617,10 @@ int replace_page(struct vm_area_struct *vma, struct page *page, } get_page(kpage); - page_add_anon_rmap(kpage, vma, addr); + if (PageAnon(kpage)) + page_add_anon_rmap(kpage, vma, addr); + else + page_add_file_rmap(kpage); flush_cache_page(vma, addr, pte_pfn(*ptep)); ptep_clear_flush(vma, addr, ptep);I see no point in this patch, please see below.
The next 4/11 patch introduces
write_opcode()which roughly does:int write_opcode(unsigned long vaddr, user_bkpt_opcode_t opcode) { get_user_pages(write => false, &old_page); new_page = alloc_page_vma(...); ... insert the bp into the new_page ... new_page->mapping = old_page->mapping; new_page->index = old_page->index;
Quoted 7 lines
Quoted 6 lines
replace_page(old_page, new_page); }This doesn't look right at all to me.
IF
PageAnon(old_page):^^^ newpage
Yes,
Quoted 10 lines
Quoted 7 lines
in this case
replace_page()callspage_add_anon_rmap()which needs the locked page.ELSE:
I don't think the new page should evere preserve the mapping, this looks just wrong. It should be always anonymous.
I did verify that
page_add_file_rmapgets called fromreplace_pagewhen we insert or remove a probe.
Of course! but see above, PageAnon() case is possible too. I think the code should handle this case correctly anyway, but it seems it doesn't. Not only page_add_anon_rmap() needs the locked page, I am not not sure page_add_anon_rmap() is fine for write_opcode() which allocates the new page. LRU? SetPageSwapBacked?
And you seem to miss my point. I think page_add_file_rmap() is always wrong. I mean, no matter what is the page_mapping(old_page), the new page should be mapped anonymously.
Quoted 1 line
I would leave it for vm experts to decide what the right thing to do.
Sure.
Quoted 7 lines
Quoted 3 lines
And in fact, I do not understand why
write_opcode()needsreplace_page(). It could just useget_user_pages(FOLL_WRITE | FOLL_FORCE), no? It should create the anonymous page correctly.We were earlier doing
access_process_vmthat would inturn callget_user_pagesto COW the page. However that needed that the threads of the target process be stopped.
OK, I missed this, thanks.
Quoted 5 lines
Background page replacement was suggested by Linus and Peter. In this method.
1. we get a copy of the page.
2. modify the page
3. flush the tlbs.
OK.
I must admit, I don't understand the usage of the lockless get_pte() in write_opcode(). replace_page() checks orig_pte, yes. But how this check can help write_opcode and why it is needed? I do not think it can prevent any race, pte can be changed even before write_opcode() calls get_pte(). I guess this is only done because replace_page() requires this argument?
Oleg.
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Srikar DronamrajuQuoted 2 lines · Srikar Dronamraju
Again, I am not sure if executable pages should be marked
VM_SHARED.
Well, they typically are not, I could imagine some JITs maybe using it, but those would most probably be shared anonymous.
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Oleg NesterovQuoted 3 lines · Oleg Nesterov
Hmm. And if this vma is
VM_SHARED, then this bp could be actually written tovm_fileaftermprotect().
I don't think we should allow breakpoints on VM_SHARED maps.
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Oleg NesterovQuoted 3 lines · Oleg Nesterov
Either way, I think
register_uprobe()should disallow the probes in VM_SHARED/VM_MAYWRITE vmas.
VM_SHARED, fully agreed, MAYWRITE not so sure, MAP_PRIVATE has MAYWRITE iirc and its perfectly fine to poke at those.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 8 lines · Peter Zijlstra
Quoted 3 lines · Oleg Nesterov
Either way, I think
register_uprobe()should disallow the probes in VM_SHARED/VM_MAYWRITE vmas.
VM_SHARED, fully agreed, MAYWRITE not so sure,MAP_PRIVATEhas MAYWRITE iirc and its perfectly fine to poke at those.
Okay, I will put a check to disallow probes in VM_SHARED vmas.
--
Thanks and Regards
Srikar
Oleg Nesterov
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Peter ZijlstraQuoted 8 lines · Peter Zijlstra
Quoted 3 lines · Oleg Nesterov
Either way, I think
register_uprobe()should disallow the probes in VM_SHARED/VM_MAYWRITE vmas.
VM_SHARED, fully agreed, MAYWRITE not so sure,MAP_PRIVATEhas MAYWRITE iirc and its perfectly fine to poke at those.
Yes, sorry for confusion. Not sure where this VM_MAYWRITE came from.
But I still think this doesn't actually matter, replace_page() shouldn't preserve the mapping, it should always install the anonymous page. I can be wrong, of course.
(I didn't read the next version yet)
Oleg.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 4 lines · Oleg Nesterov
But I still think this doesn't actually matter,
replace_page()shouldn't preserve the mapping, it should always install the anonymous page. I can be wrong, of course.
Well, if I reasd the patches right, uprobes will use "copy_to_user()" for the self-probing case. So that would definitely just modify a shared mapping.
Of course, arguably, who really cares? As long as it's not a security issue (and it isn't - since the person could just have written to the thing directly instead), I guess it doesn't much matter. But it's a bit sad when a probing feature either
- changes a global mapping that may be executed by other non-related processes that the prober isn't even _aware_ of.
- changes semantics by creating a non-coherent private page
so arguably it would be good to just make the rule be that you cannot probe a shared mapping. Because whatever you do, it's always the wrong thing.
Linus
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 3 lines · Linus Torvalds
so arguably it would be good to just make the rule be that you cannot probe a shared mapping. Because whatever you do, it's always the wrong thing.
But isn't text usually shared? I don't see how you could set any break points or jump probes on text pages with that restriction.
BTW there were old patches for NUMA text duplication, maybe they could be resurrected for that too.
-Andi
Signature · 1 line
[email protected] -- Speaking for myself only.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 3 lines · Andi Kleen
But isn't text usually shared? I don't see how you could set any break points or jump probes on text pages with that restriction.
Text is usually private, and read-only. Not generally MAP_SHARED. The pages end up getting shared because nobody writes to them, but that's almost accidental.
If you write to them, you get a nice clean COW fault, and you are _supposed_ to get a nice clean COW fault. It's not changing any semantics:
the write is not visible to outside users, and those "get a private page" semantics were what the mmap() was all about.
In contrast, if it's a MAP_SHARED mapping and writable, the write would actually be _visible_ outside the process. And that's clearly totally wrong on all levels. Tracing a process should _never_ cause visible damage outside that process (you'd hope it wouldn't be all that visibel to the tracee either, but that's still secondary).
The alternative, ie a MAP_SHARED but read-only mapping (which looks very much like a private mapping) if you use get_user_pages(.force=1), the kernel will actually end up forcing a COW break, because making the write visible outside would be a security issue (you don't even have the right to write to the thing).
Notice how the MAP_SHARED case - writable or not - ends up doing the wrong thing. Arguably it does the _even_worse_ thing in the writable case, but in either case it's not good.
Linus
Re: [PATCH v2 7/11] Uprobes Implementation
In reply to Linus TorvaldsQuoted 3 lines
so arguably it would be good to just make the rule be that you cannot probe a shared mapping. Because whatever you do, it's always the wrong thing.
Agreed. Or, if you do, it's doing something entirely different and should be in an interface where you're explicitly attaching it generically to the file (what's being shared) without regard to any individual process. But, as you mentioned, shared, executable mappings are well outside the normal case and there is no reason to think that a first (or fourth) version of anything needs to support them at all.
Thanks,
Roland
Re: [PATCH v2 7/11] Uprobes Implementation
In reply to Linus TorvaldsQuoted 10 lines · Oleg Nesterov
Quoted 4 lines
But I still think this doesn't actually matter,
replace_page()shouldn't preserve the mapping, it should always install the anonymous page. I can be wrong, of course.Well, if I reasd the patches right, uprobes will use "
copy_to_user()" for the self-probing case. So that would definitely just modify a shared mapping.
Uprobes uses copy_to_user() to write data/stack and never to write to instruction addresses.
To write an instruction uprobes either used access_process_vm or the replace_page() based background page replacement method. This is true even if the process was probing itself.
Soon to be posted v4 will revert to background page replacement method on the lines illustrated by Peter in one of his mails.
Quoted 10 lines
Of course, arguably, who really cares? As long as it's not a security issue (and it isn't - since the person could just have written to the thing directly instead), I guess it doesn't much matter. But it's a bit sad when a probing feature either
- changes a global mapping that may be executed by other non-related processes that the prober isn't even _aware_ of.
- changes semantics by creating a non-coherent private page
Do you see these concerns even when uprobes uses background page replacement?
Quoted 5 lines
so arguably it would be good to just make the rule be that you cannot probe a shared mapping. Because whatever you do, it's always the wrong thing.
Yes, I will be adding a check to discard probing if the vma has VM_SHARED flag set. I have already committed to Oleg on this issue. I didnt include this check in v3 patchset, because uprobes was using access_process_vm in v3 patchset and I thought access_process_vm would do the right thing even if VM_SHARED is set.
--
Thanks and Regards
Srikar
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Srikar DronamrajuQuoted 15 lines · Srikar Dronamraju
Quoted 9 lines · Oleg Nesterov
4.
mremap(). What if the application doesmremap()and moves the memory? After that vaddr of user_bkpt/uprobe no longer matches the virtual address of bp. This breaksuprobe_bkpt_notifier(),unregister_uprobe(), etc.Even worse. Say,
unregister_uprobe()callsremove_bkpt(). mremap()+mmap() can be called after ->read_opcode() verifies vaddr points tobkpt_insn, but beforewrite_opcode()changes the page.I dont think we handle this case now. I think even munmap of the region where there are probes inserted also can have the same problem.
Quoted 6 lines
Are there ways to handle this.
I think taking a write lock onmmap_seminstead of the read lock could handle this problem.I am copying Mel Gorman and Andrea Arcangeli so that they can provide their inputs on VM and KSM related issues.
KSM only does anonymous pages, and I thought uprobes was limited to MAP_PRIVATE|PROT_EXEC file maps.
We can't hold mmap_sem (for either read or write -- read would be sufficient to serialize against mmap/mremap/munmap) from atomic uprobe context, what we can do is validate that there is a INT3 on that particular address, a mremap/munmap/munmap+mmap will either end not having a pte entry for the address, or not have the INT3.
That said, you shouldn't be executing code on maps you're changing, much fun can happen if you try, so I don't think we should expend too much effort as long as the race will only result in the app crashing and not the kernel.
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 23 lines · Peter Zijlstra
Quoted 15 lines · Srikar Dronamraju
Quoted 9 lines · Oleg Nesterov
4.
mremap(). What if the application doesmremap()and moves the memory? After that vaddr of user_bkpt/uprobe no longer matches the virtual address of bp. This breaksuprobe_bkpt_notifier(),unregister_uprobe(), etc.Even worse. Say,
unregister_uprobe()callsremove_bkpt(). mremap()+mmap() can be called after ->read_opcode() verifies vaddr points tobkpt_insn, but beforewrite_opcode()changes the page.I dont think we handle this case now. I think even munmap of the region where there are probes inserted also can have the same problem.
Quoted 6 lines
Are there ways to handle this.
I think taking a write lock onmmap_seminstead of the read lock could handle this problem.I am copying Mel Gorman and Andrea Arcangeli so that they can provide their inputs on VM and KSM related issues.
Quoted 8 lines
KSM only does anonymous pages, and I thought uprobes was limited to MAP_PRIVATE|PROT_EXEC file maps.
We can't hold
mmap_sem(for either read or write -- read would be sufficient to serialize against mmap/mremap/munmap) from atomic uprobe context, what we can do is validate that there is a INT3 on that particular address, a mremap/munmap/munmap+mmap will either end not having a pte entry for the address, or not have the INT3.
Did you mean "We can hold mmap_sem?" Else I am not sure if we can traverse the vma. Infact alloc_page_vma() needs mmap_sem to be acquired. Please clarify?
Quoted 6 lines
That said, you shouldn't be executing code on maps you're changing, much fun can happen if you try, so I don't think we should expend too much effort as long as the race will only result in the app crashing and not the kernel.
Okay.
--
Thanks and Regards
Srikar
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 9 lines · Srikar Dronamraju
Quoted 5 lines
We can't hold
mmap_sem(for either read or write -- read would be sufficient to serialize against mmap/mremap/munmap) from atomic uprobe context, what we can do is validate that there is a INT3 on that particular address, a mremap/munmap/munmap+mmap will either end not having a pte entry for the address, or not have the INT3.Did you mean "We can hold
mmap_sem?" Else I am not sure if we can traverse the vma. Infactalloc_page_vma()needsmmap_semto be acquired. Please clarify?
OK, so maybe I misunderstood, this is from the INT3 trap handler, right?
We can _not_ take a sleeping lock from trap context. Why would you want the vma anyway?
Re: [PATCH v2 7/11] Uprobes Implementation
Quoted 7 lines · Peter Zijlstra
Quoted 6 lines · Srikar Dronamraju
Quoted 5 lines
We can't hold
mmap_sem(for either read or write -- read would be sufficient to serialize against mmap/mremap/munmap) from atomic uprobe context, what we can do is validate that there is a INT3 on that particular address, a mremap/munmap/munmap+mmap will either end not having a pte entry for the address, or not have the INT3.
Quoted 5 lines
Quoted 3 lines
Did you mean "We can hold
mmap_sem?" Else I am not sure if we can traverse the vma. Infactalloc_page_vma()needsmmap_semto be acquired. Please clarify?OK, so maybe I misunderstood, this is from the INT3 trap handler, right?
If I am right, the initial comment was both from the unregister_uprobe() -> write_opcode() context and uprobe_bkpt_notifier context.
[ snipping relevant part of Oleg's mail from where the conversation started ]
---------------------------------------------------------------------
Quoted 9 lines
Quoted 9 lines
4.
mremap(). What if the application doesmremap()and moves the memory? After that vaddr of user_bkpt/uprobe no longer matches the virtual address of bp. This breaksuprobe_bkpt_notifier(),unregister_uprobe(), etc.Even worse. Say,
unregister_uprobe()callsremove_bkpt(). mremap()+mmap() can be called after ->read_opcode() verifies vaddr
points tobkpt_insn, but beforewrite_opcode()changes the page.
---------------------------------------------------------------------
But yes, if the mmap/mremap/munmap can happen between validating the int3 and removal of the breakpoint in the unregister_uprobe path, then it can as well happen between the breakpoint hit and the time uprobes does the fixups to continue execution after running the handler and single-stepping.
I agree with you that we shouldnt bother about mmap/mremap/munmap of the executable code. Probably we could document the same.
Quoted 3 lines
We can _not_ take a sleeping lock from trap context. Why would you want the vma anyway?
Yes, we dont look at the vma in trap context at all. If we need to allocate a slot in the xol_vma then we set the TIF_UPROBE do the stuff in task context.
--
Thanks and Regards
Srikar
Quoted 1 line
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
In reply to Oleg NesterovQuoted 7 lines · Oleg Nesterov
Quoted 4 lines
Quoted 2 lines
- I don't really understand why ->handler_in_interrupt is really useful, but never mind.
There is a small overhead when running the handlers in task context.
Sure, but
Quoted 8 lines
Quoted 2 lines
overhead of task over interrupt = (1.016851 - .907400) = .109451 usec % additional overhead = (.109451/.907400) * 100 = 12.062%
this overhead looks very minor. To me, it is better to simplify the code, at least in the first version.
That said, this is up to you, I am not asking you to remove this optimization. Just imho.
Right so what I've suggested several times it to simply call the same handler in both contexts. If it returns -EFAULT, set TIF_UPROBE or whatever and try again from task context.
Re: [PATCH v2 7/11] Uprobes Implementation
Hi -
Quoted 4 lines · Peter Zijlstra
[...]
Right so what I've suggested several times it to simply call the same handler in both contexts. If it returns-EFAULT, setTIF_UPROBEor whatever and try again from task context.
That could work, but random partial execution & restart of the handler will make it tricky to write a single handler that reliably produces results. It would likely need a flag to indicate that it failed previously so as to throw away partial results.
- FChE
Peter Zijlstra
maintainerRe: [PATCH v2 7/11] Uprobes Implementation
Quoted 8 lines · Frank Ch. Eigler
Hi -
Quoted 4 lines · Peter Zijlstra
[...]
Right so what I've suggested several times it to simply call the same handler in both contexts. If it returns-EFAULT, setTIF_UPROBEor whatever and try again from task context.
Quoted 4 lines
That could work, but random partial execution & restart of the handler will make it tricky to write a single handler that reliably produces results. It would likely need a flag to indicate that it failed previously so as to throw away partial results.
Or it shouldn't leave half-assed state around to begin with.
PATCH v2 8/11 X86 details for uprobes.
In reply to Srikar DronamrajuX86 support for Uprobes
This patch provides x86 specific details for uprobes. This includes interrupt notifier for uprobes, enabling/disabling singlestep.
---
arch/x86/Kconfig | 1 + arch/x86/kernel/Makefile | 1 + arch/x86/kernel/uprobes.c | 87 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 0 deletions(-) create mode 100644 arch/x86/kernel/uprobes.c
arch/x86/Kconfig +1 -0
@@ -54,6 +54,7 @@ config X86 select HAVE_KERNEL_LZO select HAVE_HW_BREAKPOINT select HAVE_USER_BKPT + select HAVE_UPROBES select PERF_EVENTS select ANON_INODES select HAVE_ARCH_KMEMCHECK
arch/x86/kernel/Makefile +1 -0
@@ -118,6 +118,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o obj-$(CONFIG_USER_BKPT) += user_bkpt.o +obj-$(CONFIG_UPROBES) += uprobes.o ### # 64 bit specific files
arch/x86/kernel/uprobes.c new file +87 -0
87 lines not on this page · raw message
PATCH v2 9/11 Uprobes Documentation patch
In reply to Srikar DronamrajuUprobes documentation.
Changelog:
Addressed comments from Randy Dunlap.
---
Documentation/uprobes.txt | 244 +++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 244 insertions(+), 0 deletions(-) create mode 100644 Documentation/uprobes.txt
Documentation/uprobes.txt new file +244 -0
@@ -0,0 +1,244 @@ +Title : User-Space Probes (Uprobes) +Authors : Jim Keniston <[email protected]> + : Srikar Dronamraju <[email protected]> + +CONTENTS + +1. Concepts: Uprobes +2. Architectures Supported +3. Configuring Uprobes +4. API Reference +5. Uprobes Features and Limitations +6. Probe Overhead +7. TODO +8. Uprobes Team +9. Uprobes Example + +1. Concepts: Uprobes + +Uprobes enables you to dynamically break into any routine in a +user application and collect debugging and performance information +non-disruptively. You can trap at any code address, specifying a +kernel handler routine to be invoked when the breakpoint is hit. + +A uprobe can be inserted on any instruction in the application's +virtual address space. The registration function register_uprobe() +specifies which process is to be probed, where the probe is to be +inserted, and what handler is to be called when the probe is hit. + +Uprobes-based instrumentation can be packaged as a kernel +module. In the simplest case, the module's init function installs +("registers") one or more probes, and the exit function unregisters +them. + +1.1 How Does a Uprobe Work? + +When a uprobe is registered, Uprobes makes a copy of the probed +instruction, stops the probed application, replaces the first byte(s) +of the probed instruction with a breakpoint instruction (e.g., int3 +on i386 and x86_64), and allows the probed application to continue. +(When inserting the breakpoint, Uprobes uses background page +replacement mechanism, so that the breakpoint affects only that +process, and not any other process running that program. This is +true even if the probed instruction is in a shared library.) + +When a CPU hits the breakpoint instruction, a trap occurs, the CPU's +user-mode registers are saved, and uprobes notifier code finds the +associated uprobe. It then executes the handler associated with the +uprobe, passing the handler the addresses of the uprobe struct and the +saved registers. The handler can run either in interrupt context or in +task context; this is specified by the user at the time of registration. +When run in task context, the handler may block, but keep in mind that +the probed thread remains stopped while your handler runs. + +Next, Uprobes single-steps its copy of the probed instruction and +resumes execution of the probed process at the instruction following +the probepoint. (It would be simpler to single-step the actual +instruction in place, but then Uprobes would have to temporarily +remove the breakpoint instruction. This would create problems in a +multithreaded application. For example, it would open a time window +when another thread could sail right past the probepoint.) + +Instruction copies to be single-stepped are stored in a per-process +"execution out of line (XOL) area," which is a little VM area +created by Uprobes in each probed process's address space. + +Uprobes handles interesting events in the lifetime of the probed +process, such as fork, clone, exec, and exit. + +1.2 Multithreaded Applications + +Uprobes supports the probing of multithreaded applications. Uprobes +imposes no limit on the number of threads in a probed application. +All threads in a process use the same text pages, so every probe +in a process affects all threads; of course, each thread hits the +probepoint (and runs the handler) independently. Multiple threads +may run the same handler simultaneously. If you want a particular +thread or set of threads to run a particular handler, your handler +should check current or current->pid to determine which thread has +hit the probepoint. + +When a process clones a new thread, that thread automatically shares +all current and future probes established for that process. + +2. Architectures Supported + +This user_bkpt based version of Uprobes is implemented on the following +architectures: + +- x86 + +3. Configuring Uprobes + +When configuring the kernel using make menuconfig/xconfig/oldconfig, +ensure that CONFIG_UPROBES is set to "y". Under "General setup" select +"User-space breakpoint assistance" then select "User-space probes". + +So that you can load and unload Uprobes-based instrumentation modules, +make sure "Loadable module support" (CONFIG_MODULES) and "Module +unloading" (CONFIG_MODULE_UNLOAD) are set to "y". + +4. API Reference + +The Uprobes API includes a "register" function and an "unregister" +function for uprobes. Here are terse, mini-man-page specifications for +these functions and the associated probe handlers that you'll write. +See the latter half of this document for examples. + +4.1 register_uprobe + +#include <linux/uprobes.h> +int register_uprobe(struct uprobe *u); + +Sets a breakpoint at virtual address u->vaddr in the process whose +pid is u->pid. When the breakpoint is hit, Uprobes calls u->handler. +If u->handler_in_interrupt is set, the handler runs in interrupt +context. Otherwise it runs in task context. + +register_uprobe() returns 0 on success, or a negative errno +otherwise. + +User's handler (u->handler): +#include <linux/uprobes.h> +#include <linux/ptrace.h> +void handler(struct uprobe *u, struct pt_regs *regs); + +Called with u pointing to the uprobe associated with the breakpoint, +and regs pointing to the struct containing the registers saved when +the breakpoint was hit. + +4.2 unregister_uprobe + +#include <linux/uprobes.h> +void unregister_uprobe(struct uprobe *u); + +Removes the specified probe. The unregister function can be called +at any time after the probe has been registered, and can be called +from a uprobe handler. + +5. Uprobes Features and Limitations + +The user is expected to assign values to the following members +of struct uprobe: pid, vaddr, handler, and handler_in_interrupt. +Uprobes may produce unexpected results if you: +- change the contents of a uprobe object while it is registered; or +- attempt to register a uprobe that is already registered. + +In this implementation, Uprobes allows only one uprobe at a particular +address. + +Any number of kernel modules may probe a particular process +simultaneously, and a particular module may probe any number of +processes simultaneously. + +Probes are shared by all threads in a process (including newly +created threads). + +If a probed process exits or execs, Uprobes automatically +unregisters all uprobes associated with that process. Subsequent +attempts to unregister these probes will be treated as no-ops. + +On the other hand, if a probed memory area is removed from the +process's virtual memory map (e.g., via dlclose(3) or munmap(2)), +it's currently up to you to unregister the probes first. + +There is no way to specify that probes should be inherited across fork; +Uprobes removes all probepoints in the newly created child process. + +To avoid interfering with interactive debuggers, Uprobes will refuse +to insert a probepoint where a breakpoint instruction already exists. +Some architectures may refuse to insert probes on other types of +instructions. + +If you install a probe in an inline-able function, Uprobes makes +no attempt to chase down all inline instances of the function and +install probes there. gcc may inline a function without being asked, +so keep this in mind if you're not seeing the probe hits you expect. + +A probe handler can modify the environment of the probed function +-- e.g., by modifying data structures, or by modifying the +contents of the pt_regs struct (which are restored to the registers +upon return from the breakpoint). So Uprobes can be used, for example, +to install a bug fix or to inject faults for testing. Uprobes, of +course, has no way to distinguish the deliberately injected faults +from the accidental ones. Don't drink and probe. + +When Uprobes establishes a probepoint on a previous unprobed page +of text, Linux creates a new copy of the page via its copy-on-write +mechanism. When probepoints are removed, Uprobes makes no attempt +to consolidate identical copies of the same page. This could affect +memory availability if you probe many, many pages in many, many +long-running processes. + +6. Probe Overhead + +Probe overhead is measured on a benchmark that hits the same probepoint +repeatedly, firing a simple handler each time. Probe overhead +changes with different cpus/archs/probe handlers and the number of +iterations. + +Here are sample overhead figures (in usec) for x86 architecture. + +i686: Intel(R) Xeon(TM) CPU 2.40GHz +Without probe module. +100000 interations in 0.000650 sec i.e 0.006500 usec per iteration + +With probes and handler run in interrupt context. +100000 interations in 0.340774 sec i.e 3.407740 usec per iteration +probe overhead is 3.401240 usec per probe hit. + +With probes and handler run in task context. +100000 interations in 0.365589 sec i.e 3.655890 usec per iteration +probe overhead is 3.649390 usec per probe hit. + +x86_64: Intel(R) Xeon(R) CPU X7350 @ 2.93GHz +Without probe module. +100000 interations in 0.000468 sec i.e 0.004680 usec per iteration + +With probes and handler run in interrupt context. +100000 interations in 0.120369 sec i.e 1.203690 usec per iteration +Probe overhead is 1.199010 usec per probe hit. + +With probes and handler run in task context. +100000 interations in 0.130685 sec i.e 1.306850 usec per iteration +Probe overhead is 1.302170 usec per probe hit. + +7. TODO + +a. Support for other architectures. +b. Support for multiple probes at the same address. +c. Support for boosted probes. +d. Support return probes. + +8. Uprobes Team + +The following people have made major contributions to Uprobes: +Jim Keniston - [email protected] +Srikar Dronamraju - [email protected] +Ananth Mavinakayanahalli - [email protected] +Prasanna Panchamukhi - [email protected] +Dave Wilder - [email protected] + +9. Uprobes Example + +samples/uprobes/uprobe_example.c
PATCH v2 10/11 Uprobes samples.
In reply to Srikar DronamrajuUprobes Samples
This provides an example uprobes module in the samples directory.
To run this module run (as root)
insmod uprobe_example.ko vaddr=<vaddr> pid=<pid>
Where <vaddr> is the address where we want to place the probe.
<pid> is the pid of the process we are interested to probe.
example: -
# cd samples/uprobes
[get the virtual address to place the probe.]
# vaddr=0x$(objdump -T /bin/bash |awk '/echo_builtin/ {print $1}')
[Run a bash shell in the background; have it echo 4 lines.] # (sleep 10; echo 1; echo 2; echo 3; echo 4) & [Probe calls echo_builtin() in the background bash process.]
# insmod uprobe_example.ko vaddr=$vaddr pid=$! # sleep 10 # rmmod uprobe_example # dmesg | tail -n 3 Registering uprobe on pid 10875, vaddr 0x45aa30 Unregistering uprobe on pid 10875, vaddr 0x45aa30 Probepoint was hit 4 times # [ Output shows that echo_builtin function was hit 4 times. ]
---
samples/Kconfig | 7 +++ samples/uprobes/Makefile | 17 ++++++++ samples/uprobes/uprobe_example.c | 83 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 0 deletions(-) create mode 100644 samples/uprobes/Makefile create mode 100644 samples/uprobes/uprobe_example.c
samples/Kconfig +7 -0
@@ -44,4 +44,11 @@ config SAMPLE_HW_BREAKPOINT help This builds kernel hardware breakpoint example modules. +config SAMPLE_UPROBES + tristate "Build uprobes example -- loadable module only" + depends on UPROBES && m + help + This builds uprobes example module. + + endif # SAMPLES
samples/uprobes/Makefile new file +17 -0
@@ -0,0 +1,17 @@ +# builds the uprobes example kernel modules; +# then to use one (as root): +# insmod <module_name.ko> vaddr=<vaddr> pid=<pid> +# +# +# example: - +# vaddr=0x$(objdump -T /bin/bash |awk '/echo_builtin/ print $1}') +# (sleep 10; echo 1; echo 2; echo 3; echo 4) & +# insmod uprobe_example.ko vaddr=$vaddr pid=$! +# sleep 10 +# rmmod uprobe_example +# dmesg | tail -n 3 +# Registering uprobe on pid 3920, vaddr 0x45aa30 +# Unregistering uprobe on pid 3920, vaddr 0x45aa30 +# Probepoint was hit 4 times + +obj-$(CONFIG_SAMPLE_UPROBES) += uprobe_example.o
samples/uprobes/uprobe_example.c new file +83 -0
83 lines not on this page · raw message
PATCH v2 11/11 Uprobes traceevents patch.
In reply to Srikar DronamrajuUprobes Trace_events interface
The following patch implements trace_event support for uprobes. In its current form it can be used to put probes at a specified text address in a process and dump the required registers when the code flow reaches the probed address.
This is based on trace_events for kprobes to the extent that it may resemble that file on 2.6.34-rc3.
The following example shows how to dump the instruction pointer and %ax a register at the probed text address.
Start a process to trace. Get the address to trace.
[Here pid is asssumed as 3548]
[Address to trace is 0x0000000000446420] [Registers to be dumped are %ip and %ax]
# cd /sys/kernel/debug/tracing/
# echo 'p 3548:0x0000000000446420 %ip %ax' > uprobe_events
# cat uprobe_events
p:uprobes/p_3548_0x0000000000446420 3548:0x0000000000446420 %ip=%ip %ax=%ax
# cat events/uprobes/p_3548_0x0000000000446420/enable
0
[enable the event]
# echo 1 > events/uprobes/p_3548_0x0000000000446420/enable
# cat events/uprobes/p_3548_0x0000000000446420/enable
1
# #### do some activity on the program so that it hits the breakpoint
# cat uprobe_profile
3548 p_3548_0x0000000000446420 234
# head trace
# tracer: nop
#
# TASK-PID CPU# TIMESTAMP FUNCTION
# | | | | |
zsh-3548 [001] 294.285812: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1
zsh-3548 [001] 294.285884: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1
zsh-3548 [001] 294.285894: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1
zsh-3548 [001] 294.285903: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1
zsh-3548 [001] 294.285912: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1
zsh-3548 [001] 294.285922: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1TODO: Documentation/trace/uprobetrace.txt
---
kernel/trace/Kconfig | 8 kernel/trace/Makefile | 1 kernel/trace/trace.h | 12 + kernel/trace/trace_uprobe.c | 926 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 947 insertions(+), 0 deletions(-) create mode 100644 kernel/trace/trace_uprobe.c
kernel/trace/Kconfig +8 -0
@@ -557,6 +557,14 @@ config RING_BUFFER_BENCHMARK If unsure, say N. +config UPROBE_EVENT + depends on UPROBES + bool "Enable uprobes-based dynamic events" + select TRACING + default y + help + This allows the user to add tracing events (similar to tracepoints) + on the fly via the traceevents interface. endif # FTRACE endif # TRACING_SUPPORT
kernel/trace/Makefile +1 -0
@@ -58,5 +58,6 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o obj-$(CONFIG_EVENT_TRACING) += power-traces.o +obj-$(CONFIG_UPROBE_EVENT) += trace_uprobe.o libftrace-y := ftrace.o
kernel/trace/trace.h +12 -0
@@ -126,6 +126,18 @@ struct kretprobe_trace_entry { (offsetof(struct kretprobe_trace_entry , args) + \ (sizeof(unsigned long) * (n))) +struct uprobe_trace_entry { +struct trace_entry ent; +pid_t pid; +unsigned long ip; +int nargs; +unsigned long args[]; +}; + +#define SIZEOF_UPROBE_TRACE_ENTRY (n) \ + (offsetof(struct uprobe_trace_entry, args) + \ + (sizeof(unsigned long) * (n))) +/* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are:
kernel/trace/trace_uprobe.c new file +926 -0
926 lines not on this page · raw message
Steven Rostedt
maintainerRe: [PATCH v2 11/11] Uprobes traceevents patch.
kernel/trace/trace.h struct kretprobe_trace_entry { · line 131
Quoted 12 lines · Srikar Dronamraju
libftrace-y := ftrace.o
kernel/trace/trace.h +3
-0@@ -126,6 +126,18 @@ struct kretprobe_trace_entry { (offsetof(struct kretprobe_trace_entry , args) + \ (sizeof(unsigned long) * (n))) +struct uprobe_trace_entry { +struct trace_entry ent; +pid_t pid;
Unless pid is not the current pid, ent already records it.
kernel/trace/trace.h struct kretprobe_trace_entry { · line 135
Quoted 4 lines
kernel/trace/trace.h +4
-0+unsigned long ip; +int nargs; +unsigned long args[]; +};
Note, you want to really add this to trace_entries.h instead:
FTRACE_ENTRY(uprobe, uprobe_trace_entry,
TRACE_GRAPH_ENT,
F_STRUCT(
__field( unsigned long, ip )
__field( int, nargs )
__dynamic_array(unsigned long, args )
),
F_printk("%lx nrargs:%u", __entry->ip, __entry->nargs));
This will put this event into the events/ftrace directory. Don't worry about the printk format, we can write a plugin for it to override it if need be.
By adding the above, other tools can know what it encountered instead of having a "Unknown Event" show up.
Quoted 734 lines
kernel/trace/trace.h +5
-0+ +#define SIZEOF_UPROBE_TRACE_ENTRY (n) \ + (offsetof(struct uprobe_trace_entry, args) + \ + (sizeof(unsigned long) * (n))) +/* * trace_flag_type is an enumeration that holds different * states when a trace occurs. These are: kernel/trace/trace_uprobe.c new file +720
-0@@ -0,0 +1,926 @@ +/* +* Uprobes-based tracing events +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Copyright (C) IBM Corporation, 2010 +* Author: Srikar Dronamraju +*/ + +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/uprobes.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/ptrace.h> +#include <linux/perf_event.h> + +#include "trace.h" +#include "trace_output.h" + +#define MAX_TRACE_ARGS 128 +#define MAX_ARGSTR_LEN 63 +#define MAX_EVENT_NAME_LEN 64 +#define UPROBE_EVENT_SYSTEM "uprobes" + +/* Reserved field names */ +#define FIELD_STRING_IP "__probe_ip" +#define FIELD_STRING_NARGS "__probe_nargs" +#define FIELD_STRING_PID "__probe_pid" + +const char *ureserved_field_names[] = { +"common_type" , +"common_flags" , +"common_preempt_count" , +"common_pid" , +"common_tgid" , +"common_lock_depth" , +FIELD_STRING_IP , +FIELD_STRING_NARGS , +FIELD_STRING_PID , +}; + +struct fetch_func { +unsigned long (*func)(struct pt_regs *,void *); +void *data; +}; + +static unsigned long call_fetch (struct fetch_func *f, +struct pt_regs *regs) +{ +return f->func (regs, f->data); +} + +/* fetch handlers */ +static unsigned long fetch_register (struct pt_regs *regs, +void *offset) +{ +return regs_get_register (regs, (unsigned int )((unsigned long )offset)); +} + +/** +* Uprobe event core functions +*/ + +/* Flags for trace_probe */ +#define TP_FLAG_TRACE 1 +#define TP_FLAG_PROFILE 2 +#define UPROBE_ENABLED 4 + +struct probe_arg { +struct fetch_func fetch; +const char *name; +}; + +struct trace_uprobe { +struct list_head list; +struct uprobe up; +unsigned long nhit; +unsigned int flags;/* For TP_FLAG_* */ +struct ftrace_event_call call; +struct trace_event event; +unsigned int nr_args; +struct probe_arg args[]; +}; + +#define SIZEOF_TRACE_UPROBE (n) \ + (offsetof(struct trace_uprobe, args) + \ + (sizeof(struct probe_arg) * (n))) + +static int probe_arg_string (char *buf,size_t n,struct fetch_func *ff) +{ +int ret = -EINVAL ; + +if (ff->func == fetch_register) { +const char *name; + name =regs_query_register_name ((unsigned int )((long )ff->data)); + ret =snprintf (buf, n,"%%%s" , name); + } +if (ret >= n) +return -ENOSPC ; +return ret; +} + +static int register_uprobe_event (struct trace_uprobe *tp); +static void unregister_uprobe_event (struct trace_uprobe *tp); + +static DEFINE_MUTEX (uprobe_lock); +static LIST_HEAD (uprobe_list); + +static void uprobe_dispatcher (struct uprobe *up,struct pt_regs *regs); + +/* Check the name is good for event/group */ +static int check_event_name (const char *name) +{ +if (!isalpha (*name) && *name !='_' ) +return 0 ; +while (*++name !='\0' ) { +if (!isalpha (*name) && !isdigit (*name) && *name !='_' ) +return 0 ; + } +return 1 ; +} + +/* +* Allocate new trace_uprobe and initialize it (including uprobes). +*/ +static struct trace_uprobe *alloc_trace_uprobe (const char * group, +const char *event, +void *addr, +pid_t pid,int nargs) +{ +struct trace_uprobe *tp; +int ret = -ENOMEM ; + +if (!event || !check_event_name (event)) +return ERR_PTR (-EINVAL ); + +if (!group || !check_event_name (group)) +return ERR_PTR (-EINVAL ); + + tp =kzalloc (SIZEOF_TRACE_UPROBE (nargs),GFP_KERNEL ); +if (!tp) +return ERR_PTR (ret); + + tp->up.vaddr = (unsigned long )addr; + tp->up.pid = pid; + tp->up.handler = uprobe_dispatcher; + + tp->call.name =kstrdup (event,GFP_KERNEL ); +if (!tp->call.name) + goto error; + + tp->call.system =kstrdup (group,GFP_KERNEL ); +if (!tp->call.system) + goto error; + +INIT_LIST_HEAD (&tp->list); +return tp; +error: +kfree (tp->call.name); +kfree (tp); +return ERR_PTR (ret); +} + +static void free_probe_arg (struct probe_arg *arg) +{ +kfree (arg->name); +} + +static void free_trace_uprobe (struct trace_uprobe *tp) +{ +int i; + +for (i =0 ; i < tp->nr_args; i++) +free_probe_arg (&tp->args[i]); + +kfree (tp->call.system); +kfree (tp->call.name); +kfree (tp); +} + +static struct trace_uprobe *find_probe_event (const char *event, +const char *group) +{ +struct trace_uprobe *tp; + +list_for_each_entry (tp, &uprobe_list, list) +if (strcmp (tp->call.name, event) ==0 && +strcmp (tp->call.system, group) ==0 ) +return tp; +return NULL ; +} + +/* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */ +static void unregister_trace_uprobe (struct trace_uprobe *tp) +{ +if (tp->flags &UPROBE_ENABLED ) +unregister_uprobe (&tp->up); +list_del (&tp->list); +unregister_uprobe_event (tp); +} + +/* Register a trace_uprobe and probe_event */ +static int register_trace_uprobe (struct trace_uprobe *tp) +{ +struct trace_uprobe *old_tp; +int ret; + +mutex_lock (&uprobe_lock); + +/* register as an event */ + old_tp =find_probe_event (tp->call.name, tp->call.system); +if (old_tp) { +/* delete old event */ +unregister_trace_uprobe (old_tp); +free_trace_uprobe (old_tp); + } + ret =register_uprobe_event (tp); +if (ret) { +pr_warning ("Faild to register probe event(%d)\n" , ret); + goto end; + } + +list_add_tail (&tp->list, &uprobe_list); +end: +mutex_unlock (&uprobe_lock); +return ret; +} + +#define PARAM_MAX_ARGS 16 +#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) + +/* Recursive argument parser */ +static int __parse_probe_arg (char *arg,struct fetch_func *ff) +{ +int ret =0 ; + +switch (arg[0 ]) { +case '%' :/* named register */ + ret =regs_query_register_offset (arg +1 ); +if (ret >=0 ) { + ff->func = fetch_register; + ff->data = (void *)(unsigned long )ret; + ret =0 ; + } +break ; +default : +/* TODO: support custom handler */ + ret = -EINVAL ; + } +return ret; +} + +/* String length checking wrapper */ +static int parse_probe_arg (char *arg,struct fetch_func *ff) +{ +if (strlen (arg) >MAX_ARGSTR_LEN ) { +pr_info ("Argument is too long.: %s\n" , arg); +return -ENOSPC ; + } +return __parse_probe_arg (arg, ff); +} + +/* Return 1 if name is reserved or already used by another argument */ +static int conflict_field_name (const char *name, +struct probe_arg *args,int narg) +{ +int i; +for (i =0 ; i <ARRAY_SIZE (ureserved_field_names); i++) +if (strcmp (ureserved_field_names[i], name) ==0 ) +return 1 ; +for (i =0 ; i < narg; i++) +if (strcmp (args[i].name, name) ==0 ) +return 1 ; +return 0 ; +} + +static int create_trace_uprobe (int argc,char **argv) +{ +/* +* Argument syntax: +* - Add uprobe: p[:[GRP/]EVENT] VADDR@PID [%REG] +* +* - Remove uprobe: -:[GRP/]EVENT +*/ +struct trace_uprobe *tp; +int i, ret =0 ; +int is_delete =0 ; +char *arg =NULL , *event =NULL , *group =NULL ; +void *addr =NULL ; +pid_t pid =0 ; +char buf[MAX_EVENT_NAME_LEN ]; + +/* argc must be >= 1 */ +if (argv[0 ][0 ] =='-' ) + is_delete =1 ; +else if (argv[0 ][0 ] !='p' ) { +pr_info ("Probe definition must be started with 'p', 'r' or" +" '-'.\n" ); +return -EINVAL ; + } + +if (argv[0 ][1 ] ==':' ) { + event = &argv[0 ][2 ]; +if (strchr (event,'/' )) { + group = event; + event =strchr (group,'/' ) +1 ; + event[-1 ] ='\0' ; +if (strlen (group) ==0 ) { +pr_info ("Group name is not specified\n" ); +return -EINVAL ; + } + } +if (strlen (event) ==0 ) { +pr_info ("Event name is not specified\n" ); +return -EINVAL ; + } + } +if (!group) + group =UPROBE_EVENT_SYSTEM ; + +if (is_delete) { +if (!event) { +pr_info ("Delete command needs an event name.\n" ); +return -EINVAL ; + } + tp =find_probe_event (event, group); +if (!tp) { +pr_info ("Event %s/%s doesn't exist.\n" , group, event); +return -ENOENT ; + } +/* delete an event */ +unregister_trace_uprobe (tp); +free_trace_uprobe (tp); +return 0 ; + } + +if (argc <2 ) { +pr_info ("Probe point is not specified.\n" ); +return -EINVAL ; + } +if (isdigit (argv[1 ][0 ])) { +/* an address specified */ + arg =strchr (argv[1 ],':' ); +if (!arg) + goto fail_address_parse; + + *arg++ ='\0' ; + ret =strict_strtoul (&argv[1 ][0 ],0 , (unsigned long *)&pid); +if (ret) + goto fail_address_parse; + + ret =strict_strtoul (arg,0 , (unsigned long *)&addr); +if (ret) + goto fail_address_parse; + } + argc -=2 ; argv +=2 ; + +/* setup a probe */ +if (!event) { +snprintf (buf,MAX_EVENT_NAME_LEN ,"%c_%d_0x%p" ,'p' , + pid, addr); + event = buf; + } + tp =alloc_trace_uprobe (group, event, addr, pid, argc); +if (IS_ERR (tp)) { +pr_info ("Failed to allocate trace_uprobe.(%d)\n" , + (int )PTR_ERR (tp)); +return PTR_ERR (tp); + } + +/* parse arguments */ + ret =0 ; +for (i =0 ; i < argc && i <MAX_TRACE_ARGS ; i++) { +/* Parse argument name */ + arg =strchr (argv[i],'=' ); +if (arg) + *arg++ ='\0' ; +else + arg = argv[i]; + +if (conflict_field_name (argv[i], tp->args, i)) { +pr_info ("Argument%d name '%s' conflicts with " +"another field.\n" , i, argv[i]); + ret = -EINVAL ; + goto error; + } + + tp->args[i].name =kstrdup (argv[i],GFP_KERNEL ); +if (!tp->args[i].name) { +pr_info ("Failed to allocate argument%d name '%s'.\n" , + i, argv[i]); + ret = -ENOMEM ; + goto error; + } + +/* Parse fetch argument */ + ret =parse_probe_arg (arg, &tp->args[i].fetch); +if (ret) { +pr_info ("Parse error at argument%d. (%d)\n" , i, ret); +kfree (tp->args[i].name); + goto error; + } + + tp->nr_args++; + } + + ret =register_trace_uprobe (tp); +if (ret) + goto error; +return 0 ; + +error: +free_trace_uprobe (tp); +return ret; + +fail_address_parse: +pr_info ("Failed to parse address.\n" ); +return ret; +} + +static void cleanup_all_probes (void ) +{ +struct trace_uprobe *tp; + +mutex_lock (&uprobe_lock); +/* TODO: Use batch unregistration */ +while (!list_empty (&uprobe_list)) { + tp = list_entry(uprobe_list.next, struct trace_uprobe, list); +unregister_trace_uprobe (tp); +free_trace_uprobe (tp); + } +mutex_unlock (&uprobe_lock); +} + + +/* Probes listing interfaces */ +static void *probes_seq_start (struct seq_file *m,loff_t *pos) +{ +mutex_lock (&uprobe_lock); +return seq_list_start (&uprobe_list, *pos); +} + +static void *probes_seq_next (struct seq_file *m,void *v,loff_t *pos) +{ +return seq_list_next (v, &uprobe_list, pos); +} + +static void probes_seq_stop (struct seq_file *m,void *v) +{ +mutex_unlock (&uprobe_lock); +} + +static int probes_seq_show (struct seq_file *m,void *v) +{ +struct trace_uprobe *tp = v; +int i, ret; +char buf[MAX_ARGSTR_LEN +1 ]; + +seq_printf (m,"%c" ,'p' ); +seq_printf (m,":%s/%s" , tp->call.system, tp->call.name); + +seq_printf (m," %d:0x%p" , tp->up.pid, (void *)tp->up.vaddr); + +for (i =0 ; i < tp->nr_args; i++) { + ret =probe_arg_string (buf,MAX_ARGSTR_LEN , &tp->args[i].fetch); +if (ret <0 ) { +pr_warning ("Argument%d decoding error(%d).\n" , i, ret); +return ret; + } +seq_printf (m," %s=%s" , tp->args[i].name, buf); + } +seq_printf (m,"\n" ); +return 0 ; +} + +static const struct seq_operations probes_seq_op = { + .start = probes_seq_start, + .next = probes_seq_next, + .stop = probes_seq_stop, + .show = probes_seq_show +}; + +static int probes_open (struct inode *inode,struct file *file) +{ +if ((file->f_mode &FMODE_WRITE ) && + (file->f_flags &O_TRUNC )) +cleanup_all_probes (); + +return seq_open (file, &probes_seq_op); +} + +static int command_trace_uprobe (const char *buf) +{ +char **argv; +int argc =0 , ret =0 ; + + argv =argv_split (GFP_KERNEL , buf, &argc); +if (!argv) +return -ENOMEM ; + +if (argc) + ret =create_trace_uprobe (argc, argv); + +argv_free (argv); +return ret; +} + +#define WRITE_BUFSIZE 128 + +static ssize_t probes_write (struct file *file,const char __user *buffer, +size_t count,loff_t *ppos) +{ +char *kbuf, *tmp; +int ret; +size_t done; +size_t size; + + kbuf =kmalloc (WRITE_BUFSIZE ,GFP_KERNEL ); +if (!kbuf) +return -ENOMEM ; + + ret = done =0 ; +while (done < count) { + size = count - done; +if (size >=WRITE_BUFSIZE ) + size =WRITE_BUFSIZE -1 ; +if (copy_from_user (kbuf, buffer + done, size)) { + ret = -EFAULT ; + goto out; + } + kbuf[size] ='\0' ; + tmp =strchr (kbuf,'\n' ); +if (tmp) { + *tmp ='\0' ; + size = tmp - kbuf +1 ; + }else if (done + size < count) { +pr_warning ("Line length is too long: " +"Should be less than %d." ,WRITE_BUFSIZE ); + ret = -EINVAL ; + goto out; + } + done += size; +/* Remove comments */ + tmp =strchr (kbuf,'#' ); +if (tmp) + *tmp ='\0' ; + + ret =command_trace_uprobe (kbuf); +if (ret) + goto out; + } + ret = done; +out: +kfree (kbuf); +return ret; +} + +static const struct file_operations uprobe_events_ops = { + .owner =THIS_MODULE , + .open = probes_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, + .write = probes_write, +}; + +/* Probes profiling interfaces */ +static int probes_profile_seq_show (struct seq_file *m,void *v) +{ +struct trace_uprobe *tp = v; + +seq_printf (m," %d %-44s %15lu\n" ,tp->up.pid, tp->call.name, tp->nhit); +return 0 ; +} + +static const struct seq_operations profile_seq_op = { + .start = probes_seq_start, + .next = probes_seq_next, + .stop = probes_seq_stop, + .show = probes_profile_seq_show +}; + +static int profile_open (struct inode *inode,struct file *file) +{ +return seq_open (file, &profile_seq_op); +} + +static const struct file_operations uprobe_profile_ops = { + .owner =THIS_MODULE , + .open = profile_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* Uprobe handler */ +static void uprobe_trace_func (struct uprobe *up,struct pt_regs *regs) +{ + struct trace_uprobe *tp = container_of(up, struct trace_uprobe, up); +struct uprobe_trace_entry *entry; +struct ring_buffer_event *event; +struct ring_buffer *buffer; +int size, i, pc; +unsigned long irq_flags; +struct ftrace_event_call *call = &tp->call; + + tp->nhit++; + +local_save_flags (irq_flags); + pc =preempt_count (); + + size =SIZEOF_UPROBE_TRACE_ENTRY (tp->nr_args); + + event =trace_current_buffer_lock_reserve (&buffer, call->id, size, + irq_flags, pc); +if (!event) +return ; + + entry =ring_buffer_event_data (event); + entry->nargs = tp->nr_args; + entry->ip = (unsigned long )up->vaddr; +for (i =0 ; i < tp->nr_args; i++) + entry->args[i] =call_fetch (&tp->args[i].fetch, regs); + +if (!filter_current_check_discard (buffer, call, entry, event)) +trace_nowake_buffer_unlock_commit (buffer, event, irq_flags, pc); +} + +/* Event entry printers */ +enum print_line_t +print_uprobe_event (struct trace_iterator *iter,int flags) +{ +struct uprobe_trace_entry *field; +struct trace_seq *s = &iter->seq; +struct trace_event *event; +struct trace_uprobe *tp; +int i; + + field = (struct uprobe_trace_entry *)iter->ent; + event =ftrace_find_event (field->ent.type); + tp = container_of(event, struct trace_uprobe, event); + +if (!trace_seq_printf (s,"%s: (" , tp->call.name)) + goto partial; + +if (!seq_print_ip_sym (s, field->ip, flags |TRACE_ITER_SYM_OFFSET )) + goto partial; + +if (!trace_seq_puts (s,")" )) + goto partial; + +for (i =0 ; i < field->nargs; i++) +if (!trace_seq_printf (s," %s=%lx" , + tp->args[i].name, field->args[i])) + goto partial; + +if (!trace_seq_puts (s,"\n" )) + goto partial; + +return TRACE_TYPE_HANDLED ; +partial: +return TRACE_TYPE_PARTIAL_LINE ; +} + + +static int probe_event_enable (struct ftrace_event_call *call) +{ +int ret =0 ; +struct trace_uprobe *tp = (struct trace_uprobe *)call->data; + +if (!(tp->flags &UPROBE_ENABLED )) { + ret =register_uprobe (&tp->up); +if (!ret) + tp->flags |= (UPROBE_ENABLED |TP_FLAG_TRACE ); + } +return ret; +} + +static void probe_event_disable (struct ftrace_event_call *call) +{ +struct trace_uprobe *tp = (struct trace_uprobe *)call->data; + +if (tp->flags &UPROBE_ENABLED ) { +unregister_uprobe (&tp->up); + tp->flags &= ~(UPROBE_ENABLED |TP_FLAG_TRACE ); + } +} + +static int probe_event_raw_init (struct ftrace_event_call *event_call) +{ +INIT_LIST_HEAD (&event_call->fields); + +return 0 ; +} + +#undef DEFINE_FIELD +#define DEFINE_FIELD (type, item, name, is_signed) \ + do { \ + ret = trace_define_field(event_call, #type, name, \ + offsetof(typeof(field), item), \ + sizeof(field.item), is_signed, \ + FILTER_OTHER); \ + if (ret) \ + return ret; \ + } while (0)
kernel/trace/trace_uprobe.c line 730
Quoted 10 lines
kernel/trace/trace_uprobe.c new file +10
-0+ +static int uprobe_event_define_fields (struct ftrace_event_call *event_call) +{ +int ret, i; +struct uprobe_trace_entry field; +struct trace_uprobe *tp = (struct trace_uprobe *)event_call->data; + + DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0); +DEFINE_FIELD (int, nargs,FIELD_STRING_NARGS ,1 ); +DEFINE_FIELD (pid_t, pid,FIELD_STRING_PID ,2 );
If you added the event to trace_entries.h then this should be done automatically in trace_export.c
kernel/trace/trace_uprobe.c
Quoted 3 lines
kernel/trace/trace_uprobe.c new file +3
-0+/* Set argument names as fields */ +for (i =0 ; i < tp->nr_args; i++) + DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 0)
Hmm, we don't do it for dynamic arrays, well not yet anyway.
Quoted 40 lines
;
kernel/trace/trace_uprobe.c new file +39
-0+return 0 ; +} + +static int __set_print_fmt (struct trace_uprobe *tp,char *buf,int len) +{ +int i; +int pos =0 ; + +const char *fmt, *arg; + + fmt ="(%lx)" ; + arg ="REC->" FIELD_STRING_IP ; + +/* When len=0, we just calculate the needed length */ +#define LEN_OR_ZERO (len ? len - pos : 0) + + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\"%s" , fmt); +for (i =0 ; i < tp->nr_args; i++) + pos +=snprintf (buf + pos,LEN_OR_ZERO ," %s=%%lx" , + tp->args[i].name); + + pos +=snprintf (buf + pos,LEN_OR_ZERO ,"\", %s" , arg); +for (i =0 ; i < tp->nr_args; i++) + pos +=snprintf (buf + pos,LEN_OR_ZERO ,", REC->%s" , + tp->args[i].name); + +#undef LEN_OR_ZERO + +/* return the length of print_fmt */ +return pos; +} + +static int set_print_fmt (struct trace_uprobe *tp) +{ +int len; +char *print_fmt; + +/* First: called with 0 length to calculate the needed length */ + len =__set_print_fmt (tp,NULL ,0 );
kernel/trace/trace_uprobe.c line 782
Quoted 10 lines
kernel/trace/trace_uprobe.c new file +10
-0+ print_fmt =kmalloc (len +1 ,GFP_KERNEL ); +if (!print_fmt) +return -ENOMEM ; + +/* Second: actually write the @print_fmt */ +__set_print_fmt (tp, print_fmt, len +1 ); + tp->call.print_fmt = print_fmt; + +return 0 ; +}
Or is it because of this special logic that you could not use the trace_entries.h?
-- Steve
Quoted 149 lines
kernel/trace/trace_uprobe.c new file +144
-1+ +#ifdef CONFIG_PERF_EVENTS + +/* Uprobe profile handler */ +static void uprobe_perf_func (struct uprobe *up, +struct pt_regs *regs) +{ + struct trace_uprobe *tp = container_of(up, struct trace_uprobe, up); +struct ftrace_event_call *call = &tp->call; +struct uprobe_trace_entry *entry; +int size, __size, i; +unsigned long irq_flags; +int rctx; + + __size =SIZEOF_UPROBE_TRACE_ENTRY (tp->nr_args); + size =ALIGN (__size +sizeof (u32),sizeof (u64)); + size -=sizeof (u32); +if (WARN_ONCE (size >PERF_MAX_TRACE_SIZE , +"profile buffer not large enough" )) +return ; + + entry =perf_trace_buf_prepare (size, call->id, &rctx, &irq_flags); +if (!entry) +return ; + + entry->nargs = tp->nr_args; + entry->ip = (unsigned long )up->vaddr; +for (i =0 ; i < tp->nr_args; i++) + entry->args[i] =call_fetch (&tp->args[i].fetch, regs); + +perf_trace_buf_submit (entry, size, rctx, entry->ip,1 , irq_flags, regs); +} + +static int probe_perf_enable (struct ftrace_event_call *call) +{ +int ret =0 ; +struct trace_uprobe *tp = (struct trace_uprobe *)call->data; + +if (!(tp->flags &UPROBE_ENABLED )) { + ret =register_uprobe (&tp->up); +if (!ret) + tp->flags |= (UPROBE_ENABLED |TP_FLAG_PROFILE ); + } +return ret; +} + +static void probe_perf_disable (struct ftrace_event_call *call) +{ +struct trace_uprobe *tp = (struct trace_uprobe *)call->data; + +if (tp->flags &UPROBE_ENABLED ) { +unregister_uprobe (&tp->up); + tp->flags &= ~(UPROBE_ENABLED |TP_FLAG_PROFILE ); + } +} +#endif /* CONFIG_PERF_EVENTS */ + + +static +void uprobe_dispatcher (struct uprobe *up,struct pt_regs *regs) +{ + struct trace_uprobe *tp = container_of(up, struct trace_uprobe, up); + +if (tp->flags &TP_FLAG_TRACE ) +uprobe_trace_func (up, regs); +#ifdef CONFIG_PERF_EVENTS +if (tp->flags &TP_FLAG_PROFILE ) +uprobe_perf_func (up, regs); +#endif +} + + +static int register_uprobe_event (struct trace_uprobe *tp) +{ +struct ftrace_event_call *call = &tp->call; +int ret; + +/* Initialize ftrace_event_call */ + tp->event.trace = print_uprobe_event; + call->raw_init = probe_event_raw_init; + call->define_fields = uprobe_event_define_fields; +if (set_print_fmt (tp) <0 ) +return -ENOMEM ; + + call->event = &tp->event; + call->id =register_ftrace_event (&tp->event); +if (!call->id) { +kfree (call->print_fmt); +return -ENODEV ; + } + + call->enabled =0 ; + call->regfunc = probe_event_enable; + call->unregfunc = probe_event_disable; + +#ifdef CONFIG_PERF_EVENTS + call->perf_event_enable = probe_perf_enable; + call->perf_event_disable = probe_perf_disable; +#endif + call->data = tp; + ret =trace_add_event_call (call); +if (ret) { +pr_info ("Failed to register uprobe event: %s\n" , call->name); +kfree (call->print_fmt); +unregister_ftrace_event (&tp->event); + } +return ret; +} + +static void unregister_uprobe_event (struct trace_uprobe *tp) +{ +/* tp->event is unregistered in trace_remove_event_call() */ +trace_remove_event_call (&tp->call); +kfree (tp->call.print_fmt); +} + +/* Make a debugfs interface for controling probe points */ +static __init int init_uprobe_trace (void ) +{ +struct dentry *d_tracer; +struct dentry *entry; + + d_tracer =tracing_init_dentry (); +if (!d_tracer) +return 0 ; + + entry =debugfs_create_file ("uprobe_events" ,0644 , d_tracer, +NULL , &uprobe_events_ops); + +/* Event list interface */ +if (!entry) +pr_warning ("Could not create debugfs " +"'uprobe_events' entry\n" ); + +/* Profile interface */ + entry =debugfs_create_file ("uprobe_profile" ,0444 , d_tracer, +NULL , &uprobe_profile_ops); + +if (!entry) +pr_warning ("Could not create debugfs " +"'uprobe_profile' entry\n" ); +return 0 ; +} +fs_initcall (init_uprobe_trace); --To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v2 11/11] Uprobes traceevents patch.
Hi Steven,
Quoted 11 lines · Steven Rostedt
Quoted 9 lines · Srikar Dronamraju
libftrace-y := ftrace.o
kernel/trace/trace.h +0
-0@@ -126,6 +126,18 @@ struct kretprobe_trace_entry { (offsetof(struct kretprobe_trace_entry , args) + \ (sizeof (unsigned long ) * (n)))
Quoted 5 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 131
Quoted 3 lines
kernel/trace/trace.h +3
-0+struct uprobe_trace_entry { +struct trace_entry ent; +pid_t pid;Unless pid is not the current pid, ent already records it.
Indeed.
Quoted 18 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 135
Quoted 4 lines
kernel/trace/trace.h +4
-0+unsigned long ip; +int nargs; +unsigned long args[]; +};Note, you want to really add this to
trace_entries.hinstead:FTRACE_ENTRY(uprobe,
uprobe_trace_entry,TRACE_GRAPH_ENT, F_STRUCT( __field( unsigned long, ip ) __field( int, nargs ) __dynamic_array(unsigned long, args ) ), F_printk("%lx nrargs:%u", __entry->ip, __entry->nargs)
Quoted 6 lines
);
This will put this event into the events/ftrace directory. Don't worry about the printk format, we can write a plugin for it to override it if need be.
Hmm, interesting idea. But this dynamic event definition allows us to filter events based on each argument value.
As you can see this code,
Quoted 4 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 139
Quoted 4 lines
kernel/trace/trace.h +4
-0+struct probe_arg { +struct fetch_func fetch; +const char *name; +};
each argument can have unique name. Therefore user can write a filter by using these names.
Moreover, dynamic events (at least kprobe-tracer) are going to support 'types' for each argument. this means that the arg[] in *probe_trace_entry will be no longer an unsigned long array.
Thank you,
Signature · 2 lines
Masami Hiramatsu e-mail: [email protected]
Re: [PATCH v2 11/11] Uprobes traceevents patch.
Quoted 60 lines · Masami Hiramatsu
Hi Steven,
Quoted 11 lines · Steven Rostedt
Quoted 9 lines · Srikar Dronamraju
libftrace-y := ftrace.o
kernel/trace/trace.h +0
-0@@ -126,6 +126,18 @@ struct kretprobe_trace_entry { (offsetof(struct kretprobe_trace_entry , args) + \ (sizeof (unsigned long ) * (n)))Quoted 5 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 131
Quoted 3 lines
kernel/trace/trace.h +3
-0+struct uprobe_trace_entry { +struct trace_entry ent; +pid_t pid;Unless pid is not the current pid, ent already records it.
Indeed.
Quoted 18 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 135
Quoted 4 lines
kernel/trace/trace.h +4
-0+unsigned long ip; +int nargs; +unsigned long args[]; +};Note, you want to really add this to
trace_entries.hinstead:FTRACE_ENTRY(uprobe,
uprobe_trace_entry,TRACE_GRAPH_ENT, F_STRUCT( __field( unsigned long, ip ) __field( int, nargs ) __dynamic_array(unsigned long, args ) ), F_printk("%lx nrargs:%u", __entry->ip, __entry->nargs)Quoted 6 lines
);
This will put this event into the events/ftrace directory. Don't worry about the printk format, we can write a plugin for it to override it if need be.
Hmm, interesting idea. But this dynamic event definition allows us to filter events based on each argument value.
As you can see this code,
Quoted 4 lines
kernel/trace/trace.h struct kretprobe_trace_entry { · line 139
Quoted 4 lines
kernel/trace/trace.h +4
-0+struct probe_arg { +struct fetch_func fetch; +const char *name; +};each argument can have unique name. Therefore user can write a filter by using these names.
Quoted 5 lines
Moreover, dynamic events (at least kprobe-tracer) are going to support 'types' for each argument. this means that the arg[] in
*probe_trace_entrywill be no longer an unsigned long array.Thank you,
Yeah, I don't think we should FTRACE_ENTRY for that. The format files for [k|u]probes events are created dynamically on top of what the user requested, which is a very nice feature.
Re: [PATCH v2 11/11] Uprobes traceevents patch.
In reply to Srikar DronamrajuQuoted 62 lines · Srikar Dronamraju
Uprobes
Trace_eventsinterfaceThe following patch implements
trace_eventsupport for uprobes. In its current form it can be used to put probes at a specified text address in a process and dump the required registers when the code flow reaches the probed address.This is based on
trace_eventsfor kprobes to the extent that it may resemble that file on 2.6.34-rc3.The following example shows how to dump the instruction pointer and %ax a register at the probed text address.
Start a process to trace. Get the address to trace.
[Here pid is asssumed as 3548]
[Address to trace is0x0000000000446420] [Registers to be dumped are %ip and %ax]# cd /sys/kernel/debug/tracing/ # echo 'p 3548:0x0000000000446420 %ip %ax' > uprobe_events # cat uprobe_events p:uprobes/p_3548_0x0000000000446420 3548:0x0000000000446420 %ip=%ip %ax=%ax # cat events/uprobes/p_3548_0x0000000000446420/enable 0 [enable the event] # echo 1 > events/uprobes/p_3548_0x0000000000446420/enable # cat events/uprobes/p_3548_0x0000000000446420/enable 1 # #### do some activity on the program so that it hits the breakpoint # cat uprobe_profile 3548 p_3548_0x0000000000446420 234 # head trace # tracer: nop # # TASK-PID CPU# TIMESTAMP FUNCTION # | | | | | zsh-3548 [001] 294.285812: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1 zsh-3548 [001] 294.285884: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1 zsh-3548 [001] 294.285894: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1 zsh-3548 [001] 294.285903: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1 zsh-3548 [001] 294.285912: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1 zsh-3548 [001] 294.285922: p_3548_0x0000000000446420: (0x446420) %ip=446421 %ax=1TODO:
Documentation/trace/uprobetrace.txtSigned-off-by Srikar Dronamraju <[email protected]>---
kernel/trace/Kconfig | 8 kernel/trace/Makefile | 1 kernel/trace/trace.h | 12 + kernel/trace/trace_uprobe.c | 926 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 947 insertions(+), 0 deletions(-) create mode 100644 kernel/trace/trace_uprobe.ckernel/trace/Kconfig +0
-0@@ -557,6 +557,14 @@ config RING_BUFFER_BENCHMARK
kernel/trace/Kconfig config RING_BUFFER_BENCHMARK · line 567
Quoted 10 lines
kernel/trace/Kconfig +8
-0If unsure, say N. +config UPROBE_EVENT + depends on UPROBES + bool "Enable uprobes-based dynamic events" + select TRACING + default y + help + This allows the user to add tracing events (similar to tracepoints) + on the fly via the traceevents interface.
That doesn't explain much what it does. Please explain its goal of creating a trace event on top of a userspace dynamic probe.
kernel/trace/Kconfig config RING_BUFFER_BENCHMARK · line 570
Quoted 3 lines
kernel/trace/Kconfig +3
-0+#define MAX_TRACE_ARGS 128 +#define MAX_ARGSTR_LEN 63 +#define MAX_EVENT_NAME_LEN 64
This can be shared with kprobes in a new kernel/trace/dyn_probes.h or something.
kernel/trace/Kconfig config RING_BUFFER_BENCHMARK
Quoted 7 lines
kernel/trace/Kconfig +7
-0+const char *ureserved_field_names[] = { + "common_type", + "common_flags", + "common_preempt_count", + "common_pid", + "common_tgid", + "common_lock_depth",
Same here.
Quoted 8 lines
kernel/trace/Kconfig +8
-0+/* Register a trace_uprobe and probe_event */ +static int register_trace_uprobe(struct trace_uprobe *tp) +{ + struct trace_uprobe *old_tp; + int ret; + + mutex_lock(&uprobe_lock); +
kernel/trace/Kconfig
Quoted 10 lines
kernel/trace/Kconfig +10
-0+ /* register as an event */ + old_tp = find_probe_event(tp->call.name, tp->call.system); + if (old_tp) { + /* delete old event */ + unregister_trace_uprobe(old_tp); + free_trace_uprobe(old_tp); + } + ret = register_uprobe_event(tp); + if (ret) { + pr_warning("Faild to register probe event(%d)\n", ret);
"Failed"
Quoted 46 lines
kernel/trace/Kconfig +46
-0+static int create_trace_uprobe(int argc, char **argv) +{ + /* + * Argument syntax: + * - Add uprobe: p[:[GRP/]EVENT] VADDR@PID [%REG] + * + * - Remove uprobe: -:[GRP/]EVENT + */ + struct trace_uprobe *tp; + int i, ret = 0; + int is_delete = 0; + char *arg = NULL, *event = NULL, *group = NULL; + void *addr = NULL; + pid_t pid = 0; + char buf[MAX_EVENT_NAME_LEN]; + + /* argc must be >= 1 */ + if (argv[0][0] == '-') + is_delete = 1; + else if (argv[0][0] != 'p') { + pr_info("Probe definition must be started with 'p', 'r' or" + " '-'.\n"); + return -EINVAL; + } + + if (argv[0][1] == ':') { + event = &argv[0][2]; + if (strchr(event, '/')) { + group = event; + event = strchr(group, '/') + 1; + event[-1] = '\0'; + if (strlen(group) == 0) { + pr_info("Group name is not specified\n"); + return -EINVAL; + } + } + if (strlen(event) == 0) { + pr_info("Event name is not specified\n"); + return -EINVAL; + } + } + if (!group) + group = UPROBE_EVENT_SYSTEM; + + if (is_delete) { + if (!event) {
kernel/trace/Kconfig
Quoted 10 lines
kernel/trace/Kconfig +10
-0+ pr_info("Delete command needs an event name.\n"); + return -EINVAL; + } + tp = find_probe_event(event, group); + if (!tp) { + pr_info("Event %s/%s doesn't exist.\n", group, event); + return -ENOENT; + } + /* delete an event */ + unregister_trace_uprobe(tp);
Doesn't seem to be under uprobe_lock.
Quoted 7 lines
kernel/trace/Kconfig +7
-0+/* Make a debugfs interface for controling probe points */ +static __init int init_uprobe_trace(void) +{ + struct dentry *d_tracer; + struct dentry *entry; + + d_tracer = tracing_init_dentry();
kernel/trace/Kconfig
Quoted 10 lines
kernel/trace/Kconfig +10
-0+ if (!d_tracer) + return 0; + + entry = debugfs_create_file("uprobe_events", 0644, d_tracer, + NULL, &uprobe_events_ops); + + /* Event list interface */ + if (!entry) + pr_warning("Could not create debugfs " + "'uprobe_events' entry\n");
You can use trace_create_file I think.
kernel/trace/Kconfig
Quoted 11 lines
kernel/trace/Kconfig +11
-0+ + /* Profile interface */ + entry = debugfs_create_file("uprobe_profile", 0444, d_tracer, + NULL, &uprobe_profile_ops); + + if (!entry) + pr_warning("Could not create debugfs " + "'uprobe_profile' entry\n"); + return 0; +} +fs_initcall(init_uprobe_trace);
Thanks.
Re: [PATCH v2 11/11] Uprobes traceevents patch.
Quoted 15 lines
kernel/trace/Kconfig config RING_BUFFER_BENCHMARK · line 567
Quoted 9 lines
kernel/trace/Kconfig +8
-0+config UPROBE_EVENT + depends on UPROBES + bool "Enable uprobes-based dynamic events" + select TRACING + default y + help + This allows the user to add tracing events (similar to tracepoints) + on the fly via the traceevents interface.That doesn't explain much what it does. Please explain its goal of creating a trace event on top of a userspace dynamic probe.
Okay.
Quoted 10 lines
kernel/trace/trace_uprobe.c line 37
Quoted 5 lines
kernel/trace/trace_uprobe.c new file +3
-0+#define MAX_TRACE_ARGS 128 +#define MAX_ARGSTR_LEN 63 +#define MAX_EVENT_NAME_LEN 64This can be shared with kprobes in a new
kernel/trace/dyn_probes.hor something.
Okay.
Quoted 6 lines
kernel/trace/trace_uprobe.c line 234
Quoted 3 lines
kernel/trace/trace_uprobe.c new file +3
-0+ ret =register_uprobe_event (tp); +if (ret) { +pr_warning ("Faild to register probe event(%d)\n" , ret);"Failed"
Okay.
Quoted 12 lines
Quoted 12 lines
kernel/trace/trace_uprobe.c new file +10
-0+ group =UPROBE_EVENT_SYSTEM ; + +if (is_delete) { +if (!event) { +pr_info ("Delete command needs an event name.\n" ); +return -EINVAL ; + } + tp =find_probe_event (event, group); +if (!tp) { +pr_info ("Event %s/%s doesn't exist.\n" , group, event);
Quoted 8 lines
kernel/trace/trace_uprobe.c line 347
Quoted 4 lines
kernel/trace/trace_uprobe.c new file +4
-0+return -ENOENT ; + } +/* delete an event */ +unregister_trace_uprobe (tp);Doesn't seem to be under
uprobe_lock.
Agree, the unregister_trace_uprobe() has to be called after locking uprobe_lock.
Quoted 16 lines
Quoted 16 lines
kernel/trace/trace_uprobe.c new file +13
-0+/* Make a debugfs interface for controling probe points */ +static __init int init_uprobe_trace (void ) +{ +struct dentry *d_tracer; +struct dentry *entry; + + d_tracer =tracing_init_dentry (); +if (!d_tracer) +return 0 ; + + entry =debugfs_create_file ("uprobe_events" ,0644 , d_tracer, +NULL , &uprobe_events_ops); +
Quoted 8 lines
kernel/trace/trace_uprobe.c line 915
Quoted 4 lines
kernel/trace/trace_uprobe.c new file +4
-0+/* Event list interface */ +if (!entry) +pr_warning ("Could not create debugfs " +"'uprobe_events' entry\n" );You can use
trace_create_fileI think.
Okay, will check that.
Quoted 2 lines
Signature · 2 lines
Thanks and Regards Srikar
Re: [PATCH v2 11/11] Uprobes traceevents patch.
In reply to Srikar DronamrajuQuoted 20 lines · Srikar Dronamraju
kernel/trace/trace_uprobe.c new file +20
-0+static void uprobe_trace_func (struct uprobe *up,struct pt_regs *regs) +{ + struct trace_uprobe *tp = container_of(up, struct trace_uprobe, up); +struct uprobe_trace_entry *entry; +struct ring_buffer_event *event; +struct ring_buffer *buffer; +int size, i, pc; +unsigned long irq_flags; +struct ftrace_event_call *call = &tp->call; + + tp->nhit++; + +local_save_flags (irq_flags); + pc =preempt_count (); + + size =SIZEOF_UPROBE_TRACE_ENTRY (tp->nr_args); + + event =trace_current_buffer_lock_reserve (&buffer, call->id, size, + irq_flags, pc); +if (!event)
kernel/trace/trace_uprobe.c line 641
Quoted 10 lines
kernel/trace/trace_uprobe.c new file +10
-0+return ; + + entry =ring_buffer_event_data (event); + entry->nargs = tp->nr_args; + entry->ip = (unsigned long )up->vaddr; +for (i =0 ; i < tp->nr_args; i++) + entry->args[i] =call_fetch (&tp->args[i].fetch, regs); + +if (!filter_current_check_discard (buffer, call, entry, event)) +trace_nowake_buffer_unlock_commit (buffer, event, irq_flags, pc);
You can use the non-nowake version I think. nowake is for events that might occur when we hold the rq lock, hence when it's too dangerous to wake up. It doesn't seem to be the case since we came here after a trap in userspace.