All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/11] Uprobes patches.
@ 2010-03-31 15:51 Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 1/11] Move Macro W to insn.h Srikar Dronamraju
                   ` (10 more replies)
  0 siblings, 11 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:51 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

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(-)



^ permalink raw reply	[flat|nested] 46+ messages in thread

* [PATCH v2 1/11] Move Macro W to insn.h
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
@ 2010-03-31 15:51 ` Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 2/11] Move replace_page() to mm/memory.c Srikar Dronamraju
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:51 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

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.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 arch/x86/include/asm/insn.h |    7 +++++++
 arch/x86/kernel/kprobes.c   |    7 -------
 2 files changed, 7 insertions(+), 7 deletions(-)


diff --git a/arch/x86/include/asm/insn.h b/arch/x86/include/asm/insn.h
index 96c2e0a..8586820 100644
--- a/arch/x86/include/asm/insn.h
+++ b/arch/x86/include/asm/insn.h
@@ -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;
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index b43bbae..4379b40 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -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 W
 
 struct kretprobe_blackpoint kretprobe_blacklist[] = {
 	{"__switch_to", }, /* This function switches only current task, but

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 2/11] Move replace_page() to mm/memory.c
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 1/11] Move Macro W to insn.h Srikar Dronamraju
@ 2010-03-31 15:51 ` Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 3/11] Enhance replace_page() to support pagecache Srikar Dronamraju
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:51 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Move 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.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
---

 include/linux/mm.h |    2 ++
 mm/ksm.c           |   59 ----------------------------------------------------
 mm/memory.c        |   59 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 59 deletions(-)


diff --git a/include/linux/mm.h b/include/linux/mm.h
index e70f21b..0f43355 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -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,
diff --git a/mm/ksm.c b/mm/ksm.c
index 8cdfc2a..83fb4fb 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -766,65 +766,6 @@ out:
 	return err;
 }
 
-/**
- * replace_page - replace page in vma by new ksm page
- * @vma:      vma that holds the pte pointing to page
- * @page:     the page we are replacing by kpage
- * @kpage:    the ksm page we replace page by
- * @orig_pte: the original value of the pte
- *
- * Returns 0 on success, -EFAULT on failure.
- */
-static int replace_page(struct vm_area_struct *vma, struct page *page,
-			struct page *kpage, pte_t orig_pte)
-{
-	struct mm_struct *mm = vma->vm_mm;
-	pgd_t *pgd;
-	pud_t *pud;
-	pmd_t *pmd;
-	pte_t *ptep;
-	spinlock_t *ptl;
-	unsigned long addr;
-	int err = -EFAULT;
-
-	addr = page_address_in_vma(page, vma);
-	if (addr == -EFAULT)
-		goto out;
-
-	pgd = pgd_offset(mm, addr);
-	if (!pgd_present(*pgd))
-		goto out;
-
-	pud = pud_offset(pgd, addr);
-	if (!pud_present(*pud))
-		goto out;
-
-	pmd = pmd_offset(pud, addr);
-	if (!pmd_present(*pmd))
-		goto out;
-
-	ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
-	if (!pte_same(*ptep, orig_pte)) {
-		pte_unmap_unlock(ptep, ptl);
-		goto out;
-	}
-
-	get_page(kpage);
-	page_add_anon_rmap(kpage, vma, addr);
-
-	flush_cache_page(vma, addr, pte_pfn(*ptep));
-	ptep_clear_flush(vma, addr, ptep);
-	set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot));
-
-	page_remove_rmap(page);
-	put_page(page);
-
-	pte_unmap_unlock(ptep, ptl);
-	err = 0;
-out:
-	return err;
-}
-
 /*
  * try_to_merge_one_page - take two pages and merge them into one
  * @vma: the vma that holds the pte pointing to page
diff --git a/mm/memory.c b/mm/memory.c
index bc9ba5a..66a3632 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2573,6 +2573,65 @@ void unmap_mapping_range(struct address_space *mapping,
 }
 EXPORT_SYMBOL(unmap_mapping_range);
 
+/**
+ * replace_page - replace page in vma by new ksm page
+ * @vma:      vma that holds the pte pointing to page
+ * @page:     the page we are replacing by kpage
+ * @kpage:    the ksm page we replace page by
+ * @orig_pte: the original value of the pte
+ *
+ * Returns 0 on success, -EFAULT on failure.
+ */
+int replace_page(struct vm_area_struct *vma, struct page *page,
+			struct page *kpage, pte_t orig_pte)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *ptep;
+	spinlock_t *ptl;
+	unsigned long addr;
+	int err = -EFAULT;
+
+	addr = page_address_in_vma(page, vma);
+	if (addr == -EFAULT)
+		goto out;
+
+	pgd = pgd_offset(mm, addr);
+	if (!pgd_present(*pgd))
+		goto out;
+
+	pud = pud_offset(pgd, addr);
+	if (!pud_present(*pud))
+		goto out;
+
+	pmd = pmd_offset(pud, addr);
+	if (!pmd_present(*pmd))
+		goto out;
+
+	ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
+	if (!pte_same(*ptep, orig_pte)) {
+		pte_unmap_unlock(ptep, ptl);
+		goto out;
+	}
+
+	get_page(kpage);
+	page_add_anon_rmap(kpage, vma, addr);
+
+	flush_cache_page(vma, addr, pte_pfn(*ptep));
+	ptep_clear_flush(vma, addr, ptep);
+	set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot));
+
+	page_remove_rmap(page);
+	put_page(page);
+
+	pte_unmap_unlock(ptep, ptl);
+	err = 0;
+out:
+	return err;
+}
+
 int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end)
 {
 	struct address_space *mapping = inode->i_mapping;

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 3/11] Enhance replace_page() to support pagecache
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 1/11] Move Macro W to insn.h Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 2/11] Move replace_page() to mm/memory.c Srikar Dronamraju
@ 2010-03-31 15:51 ` Srikar Dronamraju
  2010-03-31 15:51 ` [PATCH v2 4/11] User Space Breakpoint Assistance Layer Srikar Dronamraju
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:51 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Enhance 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.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
---

 mm/memory.c |    5 ++++-
 1 files changed, 4 insertions(+), 1 deletions(-)


diff --git a/mm/memory.c b/mm/memory.c
index 66a3632..5acf686 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -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);

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 4/11] User Space Breakpoint Assistance Layer
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (2 preceding siblings ...)
  2010-03-31 15:51 ` [PATCH v2 3/11] Enhance replace_page() to support pagecache Srikar Dronamraju
@ 2010-03-31 15:51 ` Srikar Dronamraju
  2010-03-31 15:52 ` [PATCH v2 5/11] X86 details for user space breakpoint assistance Srikar Dronamraju
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:51 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

User 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.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
---

 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


diff --git a/arch/Kconfig b/arch/Kconfig
index e5eb133..5667434 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -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
 
diff --git a/include/linux/user_bkpt.h b/include/linux/user_bkpt.h
new file mode 100644
index 0000000..a9223ee
--- /dev/null
+++ b/include/linux/user_bkpt.h
@@ -0,0 +1,305 @@
+#ifndef _LINUX_USER_BKPT_H
+#define _LINUX_USER_BKPT_H
+/*
+ * User-space BreakPoint support (user_bkpt)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+#include <asm/user_bkpt.h>
+struct task_struct;
+struct pt_regs;
+
+/**
+ * Strategy hints:
+ *
+ * %USER_BKPT_HNT_INLINE: Specifies that the instruction must
+ * be single-stepped inline.  Can be set by the caller of
+ * @arch->analyze_insn() -- e.g., if caller is out of XOL slots --
+ * or by @arch->analyze_insn() if there's no viable XOL strategy
+ * for that instruction.  Set in arch->strategies if the architecture
+ * doesn't implement XOL.
+ *
+ * %USER_BKPT_HNT_PERMSL: Specifies that the instruction slot whose
+ * address is @user_bkpt->xol_vaddr is assigned to @user_bkpt for the life of
+ * the process.  Can be used by @arch->analyze_insn() to simplify
+ * XOL in some cases.  Ignored in @arch->strategies.
+ *
+ * %USER_BKPT_HNT_TSKINFO: Set in @arch->strategies if the architecture's
+ * XOL handling requires the preservation of special
+ * task-specific info between the calls to @arch->pre_xol()
+ * and @arch->post_xol().  (E.g., XOL of x86_64 rip-relative
+ * instructions uses a scratch register, whose value is saved
+ * by pre_xol() and restored by post_xol().)  The caller
+ * of @arch->analyze_insn() should set %USER_BKPT_HNT_TSKINFO in
+ * @user_bkpt->strategy if it's set in @arch->strategies and the caller
+ * can maintain a @user_bkpt_task_arch_info object for each probed task.
+ * @arch->analyze_insn() should leave this flag set in @user_bkpt->strategy
+ * if it needs to use the per-task @user_bkpt_task_arch_info object.
+ */
+#define USER_BKPT_HNT_INLINE	0x1  /* Single-step this insn inline. */
+#define USER_BKPT_HNT_TSKINFO 0x2  /* XOL requires user_bkpt_task_arch_info */
+#define USER_BKPT_HNT_PERMSL	0x4  /* XOL slot assignment is permanent */
+
+#define USER_BKPT_HNT_MASK	0x7
+
+/**
+ * struct user_bkpt - user-space breakpoint/probepoint
+ *
+ * @vaddr:	virtual address of probepoint
+ * @xol_vaddr:	virtual address of XOL slot assigned to this probepoint
+ * @opcode:	copy of opcode at @vaddr
+ * @insn:	typically a copy of the instruction at @vaddr.  More
+ *	precisely, this is the instruction (stream) that will be
+ *	executed in place of the original instruction.
+ * @strategy:	hints about how this instruction will be executed
+ * @fixups:	set of fixups to be executed by @arch->post_xol()
+ * @arch_info:	architecture-specific info about this probepoint
+ */
+struct user_bkpt {
+	unsigned long vaddr;
+	unsigned long xol_vaddr;
+	user_bkpt_opcode_t opcode;
+	u8 insn[USER_BKPT_XOL_SLOT_BYTES];
+	u16 strategy;
+	u16 fixups;
+	struct bkpt_arch_info arch_info;
+};
+
+/* Post-execution fixups.  Some architectures may define others. */
+
+/* No fixup needed */
+#define USER_BKPT_FIX_NONE	0x0
+/* Adjust IP back to vicinity of actual insn */
+#define USER_BKPT_FIX_IP	0x1
+/* Adjust the return address of a call insn */
+#define USER_BKPT_FIX_CALL	0x2
+/* Might sleep while doing Fixup */
+#define USER_BKPT_FIX_SLEEPY	0x4
+
+#ifndef USER_BKPT_FIX_DEFAULT
+#define USER_BKPT_FIX_DEFAULT USER_BKPT_FIX_IP
+#endif
+
+#ifdef CONFIG_USER_BKPT
+extern int user_bkpt_init(u16 *strategies);
+extern int user_bkpt_insert_bkpt(struct task_struct *tsk,
+					struct user_bkpt *user_bkpt);
+extern unsigned long user_bkpt_get_bkpt_addr(struct pt_regs *regs);
+extern int user_bkpt_pre_sstep(struct task_struct *tsk,
+				struct user_bkpt *user_bkpt,
+				struct user_bkpt_task_arch_info *tskinfo,
+				struct pt_regs *regs);
+extern int user_bkpt_post_sstep(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt,
+			struct user_bkpt_task_arch_info *tskinfo,
+			struct pt_regs *regs);
+extern int user_bkpt_cancel_xol(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt);
+extern int user_bkpt_remove_bkpt(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt);
+extern int user_bkpt_validate_insn_addr(struct task_struct *tsk,
+					unsigned long vaddr);
+extern void user_bkpt_set_ip(struct pt_regs *regs, unsigned long vaddr);
+extern bool user_bkpt_resume_can_sleep(struct user_bkpt *user_bkpt);
+#else	/* CONFIG_USER_BKPT */
+static inline int user_bkpt_init(u16 *strategies)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_insert_bkpt(struct task_struct *tsk,
+						struct user_bkpt *user_bkpt)
+{
+	return -ENOSYS;
+}
+static inline unsigned long user_bkpt_get_bkpt_addr(struct pt_regs *regs)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_pre_sstep(struct task_struct *tsk,
+	struct user_bkpt *user_bkpt, struct user_bkpt_task_arch_info *tskinfo,
+	struct pt_regs *regs)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_post_sstep(struct task_struct *tsk,
+	struct user_bkpt *user_bkpt, struct user_bkpt_task_arch_info *tskinfo,
+	struct pt_regs *regs)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_cancel_xol(struct task_struct *tsk,
+	struct user_bkpt *user_bkpt)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_remove_bkpt(struct task_struct *tsk,
+	struct user_bkpt *user_bkpt)
+{
+	return -ENOSYS;
+}
+static inline int user_bkpt_validate_insn_addr(struct task_struct *tsk,
+	unsigned long vaddr)
+{
+	return -ENOSYS;
+}
+static inline void user_bkpt_set_ip(struct pt_regs *regs, unsigned long vaddr)
+{
+}
+static bool user_bkpt_resume_can_sleep(struct user_bkpt *user_bkpt)
+{
+	return false;
+}
+#endif	/* CONFIG_USER_BKPT */
+
+/**
+ * struct user_bkpt_arch_info - architecture-specific parameters and
+ * functions
+ *
+ * Most architectures can use the default versions of @read_opcode(),
+ * @set_bkpt(), @set_orig_insn(), and @is_bkpt_insn(); ia64 is an
+ * exception.  All functions (including @validate_address()) can assume
+ * that the caller has verified that the probepoint's virtual address
+ * resides in an executable VM area.
+ *
+ * @bkpt_insn:
+ *	The architecture's breakpoint instruction.  This is used by
+ *	the default versions of @set_bkpt(), @set_orig_insn(), and
+ *	@is_bkpt_insn().
+ * @ip_advancement_by_bkpt_insn:
+ *	The number of bytes the instruction pointer is advanced by
+ *	this architecture's breakpoint instruction.  For example, after
+ *	the powerpc trap instruction executes, the ip still points to the
+ *	breakpoint instruction (ip_advancement_by_bkpt_insn = 0); but the
+ *	x86 int3 instruction (1 byte) advances the ip past the int3
+ *	(ip_advancement_by_bkpt_insn = 1).
+ * @max_insn_bytes:
+ *	The maximum length, in bytes, of an instruction in this
+ *	architecture.  This must be <= USER_BKPT_XOL_SLOT_BYTES;
+ * @strategies:
+ *	Bit-map of %USER_BKPT_HNT_* values recognized by this architecture.
+ *	Include %USER_BKPT_HNT_INLINE iff this architecture doesn't support
+ *	execution out of line.  Include %USER_BKPT_HNT_TSKINFO if
+ *	XOL of at least some instructions requires communication of
+ *	per-task state between @pre_xol() and @post_xol().
+ * @set_ip:
+ *	Set the instruction pointer in @regs to @vaddr.
+ * @validate_address:
+ *	Return 0 if @vaddr is a valid instruction address, or a negative
+ *	errno (typically -%EINVAL) otherwise.  If you don't provide
+ *	@validate_address(), any address will be accepted.  Caller
+ *	guarantees that @vaddr is in an executable VM area.  This
+ *	function typically just enforces arch-specific instruction
+ *	alignment.
+ * @read_opcode:
+ *	For task @tsk, read the opcode at @vaddr and store it in
+ *	@opcode.  Return 0 (success) or a negative errno.  Defaults to
+ *	@user_bkpt_read_opcode().
+ * @set_bkpt:
+ *	For task @tsk, store @bkpt_insn at @user_bkpt->vaddr.  Return 0
+ *	(success) or a negative errno. Defaults to @user_bkpt_set_bkpt().
+ * @set_orig_insn:
+ *	For task @tsk, restore the original opcode (@user_bkpt->opcode) at
+ *	@user_bkpt->vaddr.  If @check is true, first verify that there's
+ *	actually a breakpoint instruction there.  Return 0 (success) or
+ *	a negative errno.  Defaults to @user_bkpt_set_orig_insn().
+ * @is_bkpt_insn:
+ *	Return %true if @user_bkpt->opcode is @bkpt_insn.  Defaults to
+ *	@user_bkpt_is_bkpt_insn(), which just tests (user_bkpt->opcode ==
+ *	arch->bkpt_insn).
+ * @analyze_insn:
+ *	Analyze @user_bkpt->insn.  Return 0 if @user_bkpt->insn is an
+ *	instruction you can probe, or a negative errno (typically -%EPERM)
+ *	otherwise.  The caller sets @user_bkpt->strategy to
+ *	%USER_BKPT_HNT_INLINE to suppress XOL for this instruction (e.g.,
+ *	because we're out of XOL slots).  If the instruction can be probed
+ *	but can't be executed out of line, set @user_bkpt->strategy to
+ *	%USER_BKPT_HNT_INLINE.  Otherwise, determine what sort of
+ *	XOL-related fixups @post_xol() (and possibly @pre_xol()) will need
+ *	to do for this instruction, and annotate @user_bkpt accordingly.
+ *	You may modify @user_bkpt->insn (e.g., the x86_64 port does this
+ *	for rip-relative instructions), but if you do so, you should
+ *	retain a copy in @user_bkpt->arch_info in case you have to revert
+ *	to single-stepping inline (see @cancel_xol()).
+ * @pre_xol:
+ *	Called just before executing the instruction associated
+ *	with @user_bkpt out of line.  @user_bkpt->xol_vaddr is the address
+ *	in @tsk's virtual address space where @user_bkpt->insn has been
+ *	copied.  @pre_xol() should at least set the instruction pointer in
+ *	@regs to @user_bkpt->xol_vaddr -- which is what the default,
+ *	@user_bkpt_pre_xol(), does.  If @user_bkpt->strategy includes the
+ *	%USER_BKPT_HNT_TSKINFO flag, then @tskinfo points to a per-task
+ *	copy of struct user_bkpt_task_arch_info.
+ * @post_xol:
+ *	Called after executing the instruction associated with
+ *	@user_bkpt out of line.  @post_xol() should perform the fixups
+ *	specified in @user_bkpt->fixups, which includes ensuring that the
+ *	instruction pointer in @regs points at the next instruction in
+ *	the probed instruction stream.  @tskinfo is as for @pre_xol().
+ *	You must provide this function.
+ * @cancel_xol:
+ *	The instruction associated with @user_bkpt cannot be executed
+ *	out of line after all.  (This can happen when XOL slots
+ *	are lazily assigned, and we run out of slots before we
+ *	hit this breakpoint.  This function should never be called
+ *	if @analyze_insn() was previously called for @user_bkpt with a
+ *	non-zero value of @user_bkpt->xol_vaddr and with
+ *	%USER_BKPT_HNT_PERMSL set in @user_bkpt->strategy.)  Adjust
+ *	@user_bkpt as needed so it can be single-stepped inline.  Omit this
+ *	function if you don't need it.
+ */
+
+struct user_bkpt_arch_info {
+	user_bkpt_opcode_t bkpt_insn;
+	u8 ip_advancement_by_bkpt_insn;
+	u8 max_insn_bytes;
+	u16 strategies;
+	void (*set_ip)(struct pt_regs *regs, unsigned long vaddr);
+	int (*validate_address)(struct task_struct *tsk, unsigned long vaddr);
+	int (*read_opcode)(struct task_struct *tsk, unsigned long vaddr,
+						user_bkpt_opcode_t *opcode);
+	int (*set_bkpt)(struct task_struct *tsk, struct user_bkpt *user_bkpt);
+	int (*set_orig_insn)(struct task_struct *tsk,
+				struct user_bkpt *user_bkpt, bool check);
+	bool (*is_bkpt_insn)(struct user_bkpt *user_bkpt);
+	int (*analyze_insn)(struct task_struct *tsk,
+						struct user_bkpt *user_bkpt);
+	int (*pre_xol)(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+				struct user_bkpt_task_arch_info *tskinfo,
+				struct pt_regs *regs);
+	int (*post_xol)(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+				struct user_bkpt_task_arch_info *tskinfo,
+				struct pt_regs *regs);
+	void (*cancel_xol)(struct task_struct *tsk,
+						struct user_bkpt *user_bkpt);
+};
+
+/* Unexported functions & macros for use by arch-specific code */
+#define user_bkpt_opcode_sz (sizeof(user_bkpt_opcode_t))
+extern unsigned long user_bkpt_read_vm(struct task_struct *tsk,
+			void __user *vaddr, void *kbuf,
+			unsigned long nbytes);
+extern unsigned long user_bkpt_write_data(struct task_struct *tsk,
+			void __user *vaddr, const void *kbuf,
+			unsigned long nbytes);
+
+extern struct user_bkpt_arch_info user_bkpt_arch_info;
+
+#endif	/* _LINUX_USER_BKPT_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index a987aa1..36a35b0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -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 <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/user_bkpt.c b/kernel/user_bkpt.c
new file mode 100644
index 0000000..c25a4ca
--- /dev/null
+++ b/kernel/user_bkpt.c
@@ -0,0 +1,598 @@
+/*
+ * User-space BreakPoint support (user_bkpt)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ *	Ananth N Mavinakayanahalli
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/user_bkpt.h>
+#include <linux/uaccess.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+
+static struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;
+
+static bool uses_xol_strategy(u16 strategy)
+{
+	return !(strategy & USER_BKPT_HNT_INLINE);
+}
+
+static bool validate_strategy(u16 strategy, u16 valid_bits)
+{
+	return ((strategy & (~valid_bits)) == 0);
+}
+
+/**
+ * user_bkpt_init - initialize the user_bkpt data structures
+ * @strategies indicates which breakpoint-related strategies are
+ * supported by the client:
+ *   %USER_BKPT_HNT_INLINE: Client supports only single-stepping inline.
+ *	Otherwise client must provide an instruction slot
+ *	(USER_BKPT_XOL_SLOT_BYTES bytes) in the probed process's address
+ *	space for each instruction to be executed out of line.
+ *   %USER_BKPT_HNT_TSKINFO: Client can provide and maintain one
+ *	@user_bkpt_task_arch_info object for each probed task.  (Failure to
+ *	support this will prevent XOL of rip-relative instructions on
+ *	x86_64, at least.)
+ * Upon return, @strategies is updated to reflect those strategies
+ * required by this particular architecture's implementation of user_bkpt:
+ *   %USER_BKPT_HNT_INLINE: Architecture or client supports only
+ *	single-stepping inline.
+ *   %USER_BKPT_HNT_TSKINFO: Architecture uses @user_bkpt_task_arch_info,
+ *   and will expect it to be passed to @user_bkpt_pre_sstep() and
+ *   @user_bkpt_post_sstep() as needed (see @user_bkpt_insert_bkpt()).
+ * Possible errors:
+ * -%ENOSYS: user_bkpt not supported for this architecture.
+ * -%EINVAL: unrecognized flags in @strategies
+ */
+int user_bkpt_init(u16 *strategies)
+{
+	u16 inline_bit, tskinfo_bit;
+	u16 client_strategies = *strategies;
+
+	if (!validate_strategy(client_strategies,
+			USER_BKPT_HNT_INLINE | USER_BKPT_HNT_TSKINFO))
+		return -EINVAL;
+
+	inline_bit = (client_strategies | arch->strategies) &
+						USER_BKPT_HNT_INLINE;
+	tskinfo_bit = (client_strategies & arch->strategies) &
+						USER_BKPT_HNT_TSKINFO;
+	*strategies = (inline_bit | tskinfo_bit);
+	return 0;
+}
+
+/**
+ * user_bkpt_read_vm - Read @nbytes at @vaddr from @tsk into @kbuf.
+ * @tsk: The probed task
+ * @vaddr: Source address, in user space to be read.
+ * @kbuf: Destination address, in kernel space.
+ *
+ * Context: This function may sleep.
+ *
+ * Returns number of bytes that could be copied.
+ */
+unsigned long user_bkpt_read_vm(struct task_struct *tsk, void __user *vaddr,
+					void *kbuf, unsigned long nbytes)
+{
+	if (tsk == current) {
+		unsigned long nleft = copy_from_user(kbuf, vaddr, nbytes);
+		return nbytes - nleft;
+	} else
+		return access_process_vm(tsk, (unsigned long) vaddr, kbuf,
+							nbytes, 0);
+}
+
+/**
+ * user_bkpt_write_data - Write @nbytes from @kbuf at @vaddr in @tsk.
+ * Can be used to write to stack or data VM areas, but not instructions.
+ * Not exported, but available for use by arch-specific user_bkpt code.
+ * @tsk: The probed task
+ * @vaddr: Destination address, in user space.
+ * @kbuf: Source address, in kernel space to be read.
+ *
+ * Context: This function may sleep.
+ *
+ * Return number of bytes written.
+ */
+unsigned long user_bkpt_write_data(struct task_struct *tsk,
+				void __user *vaddr, const void *kbuf,
+				unsigned long nbytes)
+{
+	unsigned long nleft;
+
+	if (tsk == current) {
+		nleft = copy_to_user(vaddr, kbuf, nbytes);
+		return nbytes - nleft;
+	} else
+		return access_process_vm(tsk, (unsigned long) vaddr,
+						(void *) kbuf, nbytes, 1);
+}
+
+/*
+ * Given an address, get its pte. Very similar to get_locked_pte except
+ * there is no spinlock involved.
+ */
+static pte_t *get_pte(struct mm_struct *mm, unsigned long vaddr)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgd_offset(mm, vaddr);
+	pud = pud_alloc(mm, pgd, vaddr);
+	if (!pud)
+		return NULL;
+	pmd = pmd_alloc(mm, pud, vaddr);
+	if (!pmd)
+		return NULL;
+	pte = pte_alloc_map(mm, pmd, vaddr);
+	return pte;
+}
+
+static int write_opcode(struct task_struct *tsk, unsigned long vaddr,
+						user_bkpt_opcode_t opcode)
+{
+	struct mm_struct *mm;
+	struct vm_area_struct *vma;
+	struct page *old_page, *new_page;
+	void *maddr;
+	pte_t *old_pte;
+	int offset;
+	int ret = -EINVAL;
+
+	if (!tsk)
+		return ret;
+
+	mm = get_task_mm(tsk);
+	if (!mm)
+		return ret;
+
+	down_read(&mm->mmap_sem);
+
+	/* Read the page with vaddr into memory */
+	ret = get_user_pages(tsk, mm, vaddr, 1, 0, 1, &old_page, &vma);
+	if (ret <= 0)
+		goto mmput_out;
+
+	/* Allocate a page and copy contents over from the old page */
+	new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
+	if (!new_page) {
+		ret = -ENOMEM;
+		put_page(old_page);
+		goto mmput_out;
+	}
+
+	/*
+	 * check if the page we are interested is read-only mapped
+	 * Since we are interested in text pages, Our pages of interest
+	 * should be mapped read-only.
+	 */
+	if ((vma->vm_flags && (VM_READ|VM_WRITE)) != VM_READ) {
+		ret = -EINVAL;
+		goto unlock_out;
+	}
+
+	copy_user_highpage(new_page, old_page, vaddr, vma);
+	maddr = kmap_atomic(new_page, KM_USER0);
+	offset = vaddr & (PAGE_SIZE - 1);
+	memcpy(maddr + offset, &opcode, user_bkpt_opcode_sz);
+	kunmap_atomic(new_page, KM_USER0);
+
+	old_pte = get_pte(mm, vaddr);
+	if (!old_pte) {
+		ret = -EINVAL;
+		goto unlock_out;
+	}
+
+	/*
+	 * Now replace the page in vma with the new page.
+	 * This is a text page; so, setup mapping and index.
+	 */
+	new_page->mapping = old_page->mapping;
+	new_page->index = old_page->index;
+	ret = replace_page(vma, old_page, new_page, *old_pte);
+
+unlock_out:
+	if (ret != 0)
+		page_cache_release(new_page);
+	put_page(old_page); /* we did a get_page in the beginning */
+
+mmput_out:
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+	return ret;
+}
+
+/* Default implementation of arch->read_opcode */
+static int read_opcode(struct task_struct *tsk, unsigned long vaddr,
+						user_bkpt_opcode_t *opcode)
+{
+	unsigned long bytes_read;
+
+	bytes_read = user_bkpt_read_vm(tsk, (void __user *) vaddr, opcode,
+							user_bkpt_opcode_sz);
+	return (bytes_read == user_bkpt_opcode_sz ? 0 : -EFAULT);
+}
+
+/* Default implementation of arch->set_bkpt */
+static int set_bkpt(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	return write_opcode(tsk, user_bkpt->vaddr, arch->bkpt_insn);
+}
+
+/* Default implementation of arch->set_orig_insn */
+static int set_orig_insn(struct task_struct *tsk,
+				struct user_bkpt *user_bkpt, bool check)
+{
+	if (check) {
+		user_bkpt_opcode_t opcode;
+		int result = arch->read_opcode(tsk, user_bkpt->vaddr,
+								&opcode);
+		if (result)
+			return result;
+		if (opcode != arch->bkpt_insn)
+			return -EINVAL;
+	}
+	return write_opcode(tsk, user_bkpt->vaddr, user_bkpt->opcode);
+}
+
+/* Return 0 if vaddr is in an executable VM area, or -EINVAL otherwise. */
+static inline int check_vma(struct task_struct *tsk, unsigned long vaddr)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+	int ret = -EINVAL;
+
+	mm = get_task_mm(tsk);
+	if (!mm)
+		return -EINVAL;
+	down_read(&mm->mmap_sem);
+	vma = find_vma(mm, vaddr);
+	if (vma && vaddr >= vma->vm_start && (vma->vm_flags & VM_EXEC))
+		ret = 0;
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+	return ret;
+}
+
+/**
+ * user_bkpt_validate_insn_addr - Validate if the instruction is an
+ * executable vma.
+ * Returns 0 if the vaddr is a valid instruction address.
+ * @tsk: the probed task
+ * @vaddr: virtual address of the instruction to be verified.
+ *
+ * Possible errors:
+ * -%EINVAL: Instruction passed is not a valid instruction address.
+ */
+int user_bkpt_validate_insn_addr(struct task_struct *tsk, unsigned long vaddr)
+{
+	int result;
+
+	result = check_vma(tsk, vaddr);
+	if (result != 0)
+		return result;
+	if (arch->validate_address)
+		result = arch->validate_address(tsk, vaddr);
+	return result;
+}
+
+static void print_insert_fail(struct task_struct *tsk,
+				struct user_bkpt *user_bkpt, const char *why)
+{
+	printk(KERN_ERR "Can't place breakpoint at pid %d vaddr %#lx: %s\n",
+					tsk->pid, user_bkpt->vaddr, why);
+}
+
+/**
+ * user_bkpt_insert_bkpt - insert breakpoint
+ * Insert a breakpoint into the process that includes @tsk, at the
+ * virtual address @user_bkpt->vaddr.
+ *
+ * @user_bkpt->strategy affects how this breakpoint will be handled:
+ *   %USER_BKPT_HNT_INLINE: Probed instruction will be single-stepped inline.
+ *   %USER_BKPT_HNT_TSKINFO: As above.
+ *   %USER_BKPT_HNT_PERMSL: An XOL instruction slot in the probed process's
+ *	address space has been allocated to this probepoint, and will
+ *	remain so allocated as long as it's needed.  @user_bkpt->xol_vaddr is
+ *	its address.  (This slot can be reallocated if
+ *	@user_bkpt_insert_bkpt() fails.)  The client is NOT required to
+ *	allocate an instruction slot before calling @user_bkpt_insert_bkpt().
+ * @user_bkpt_insert_bkpt() updates @user_bkpt->strategy as needed:
+ *   %USER_BKPT_HNT_INLINE: Architecture or client cannot do XOL for this
+ *	probepoint.
+ *   %USER_BKPT_HNT_TSKINFO: @user_bkpt_task_arch_info will be used for this
+ *	probepoint.
+ *
+ * All threads of the probed process must be stopped while
+ * @user_bkpt_insert_bkpt() runs.
+ *
+ * Possible errors:
+ * -%ENOSYS: user_bkpt not supported for this architecture
+ * -%EINVAL: unrecognized/invalid strategy flags
+ * -%EINVAL: invalid instruction address
+ * -%EEXIST: breakpoint instruction already exists at that address
+ * -%EPERM: cannot probe this instruction
+ * -%EFAULT: failed to insert breakpoint instruction
+ * [TBD: Validate xol_vaddr?]
+ */
+int user_bkpt_insert_bkpt(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	int result;
+	unsigned long len;
+
+	if (!validate_strategy(user_bkpt->strategy, USER_BKPT_HNT_MASK))
+		return -EINVAL;
+
+	result = user_bkpt_validate_insn_addr(tsk, user_bkpt->vaddr);
+	if (result != 0)
+		return result;
+
+	/*
+	 * If user_bkpt_read_vm() transfers fewer bytes than the maximum
+	 * instruction size, assume that the probed instruction is smaller
+	 * than the max and near the end of the last page of instructions.
+	 * But there must be room at least for a breakpoint-size instruction.
+	 */
+	len = user_bkpt_read_vm(tsk, (void __user *) user_bkpt->vaddr,
+				user_bkpt->insn, arch->max_insn_bytes);
+	if (len < user_bkpt_opcode_sz) {
+		print_insert_fail(tsk, user_bkpt,
+				"error reading original instruction");
+		return -EFAULT;
+	}
+	memcpy(&user_bkpt->opcode, user_bkpt->insn, user_bkpt_opcode_sz);
+	if (arch->is_bkpt_insn(user_bkpt)) {
+		print_insert_fail(tsk, user_bkpt,
+					"bkpt already exists at that addr");
+		return -EEXIST;
+	}
+
+	result = arch->analyze_insn(tsk, user_bkpt);
+	if (result < 0) {
+		print_insert_fail(tsk, user_bkpt,
+					"instruction type cannot be probed");
+		return result;
+	}
+
+	result = arch->set_bkpt(tsk, user_bkpt);
+	if (result < 0) {
+		print_insert_fail(tsk, user_bkpt,
+					"failed to insert bkpt instruction");
+		return result;
+	}
+	return 0;
+}
+
+/**
+ * user_bkpt_pre_sstep - prepare to single-step the probed instruction
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information, as returned by
+ *	@user_bkpt_insert_bkpt().  Unless the %USER_BKPT_HNT_INLINE flag is
+ *	set in @user_bkpt->strategy, @user_bkpt->xol_vaddr must be the
+ *	address of an XOL instruction slot that is allocated to this
+ *	probepoint at least until after the completion of
+ *	@user_bkpt_post_sstep(), and populated with the contents of
+ *	@user_bkpt->insn.  [Need to be more precise here to account for
+ *	untimely exit or USER_BKPT_HNT_BOOSTED.]
+ * @tskinfo: points to a @user_bkpt_task_arch_info object for @tsk, if
+ *	the %USER_BKPT_HNT_TSKINFO flag is set in @user_bkpt->strategy.
+ * @regs: reflects the saved user state of @tsk.  @user_bkpt_pre_sstep()
+ *	adjusts this.  In particular, the instruction pointer is set
+ *	to the instruction to be single-stepped.
+ * Possible errors:
+ * -%EFAULT: Failed to read or write @tsk's address space as needed.
+ *
+ * The client must ensure that the contents of @user_bkpt are not
+ * changed during the single-step operation -- i.e., between when
+ * @user_bkpt_pre_sstep() is called and when @user_bkpt_post_sstep() returns.
+ * Additionally, if single-stepping inline is used for this probepoint,
+ * the client must serialize the single-step operation (so multiple
+ * threads don't step on each other while the opcode replacement is
+ * taking place).
+ */
+int user_bkpt_pre_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	int result;
+
+	if (uses_xol_strategy(user_bkpt->strategy))
+		return arch->pre_xol(tsk, user_bkpt, tskinfo, regs);
+
+	/*
+	 * Single-step this instruction inline.  Replace the breakpoint
+	 * with the original opcode.
+	 */
+	result = arch->set_orig_insn(tsk, user_bkpt, false);
+	if (result == 0)
+		arch->set_ip(regs, user_bkpt->vaddr);
+	return result;
+}
+
+/**
+ * user_bkpt_post_sstep - prepare to resume execution after single-step
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information, as with @user_bkpt_pre_sstep()
+ * @tskinfo: the @user_bkpt_task_arch_info object, if any, passed to
+ *	@user_bkpt_pre_sstep()
+ * @regs: reflects the saved state of @tsk after the single-step
+ *	operation.  @user_bkpt_post_sstep() adjusts @tsk's state as needed,
+ *	including pointing the instruction pointer at the instruction
+ *	following the probed instruction.
+ * Possible errors:
+ * -%EFAULT: Failed to read or write @tsk's address space as needed.
+ */
+int user_bkpt_post_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	if (uses_xol_strategy(user_bkpt->strategy))
+		return arch->post_xol(tsk, user_bkpt, tskinfo, regs);
+
+	/*
+	 * Single-stepped this instruction inline.  Put the breakpoint
+	 * instruction back.
+	 */
+	return arch->set_bkpt(tsk, user_bkpt);
+}
+
+/**
+ * user_bkpt_cancel_xol - cancel XOL for this probepoint
+ * @tsk: a task in the probed process
+ * @user_bkpt: the probepoint information
+ * Switch @user_bkpt's single-stepping strategy from out-of-line to inline.
+ * If the client employs lazy XOL-slot allocation, it can call this function
+ * if it determines that it can't provide an XOL slot for @user_bkpt.
+ * @user_bkpt_cancel_xol() adjusts @user_bkpt appropriately.
+ *
+ * @user_bkpt_cancel_xol()'s behavior is undefined if @user_bkpt_pre_sstep()
+ * has already been called for @user_bkpt.
+ *
+ * Possible errors:
+ * Can't think of any yet.
+ */
+int user_bkpt_cancel_xol(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	if (arch->cancel_xol)
+		arch->cancel_xol(tsk, user_bkpt);
+	user_bkpt->strategy |= USER_BKPT_HNT_INLINE;
+	return 0;
+}
+
+/**
+ * user_bkpt_get_bkpt_addr - compute address of bkpt given post-bkpt regs
+ * @regs: Reflects the saved state of the task after it has hit a breakpoint
+ * instruction.
+ * Return the address of the breakpoint instruction.
+ */
+unsigned long user_bkpt_get_bkpt_addr(struct pt_regs *regs)
+{
+	return instruction_pointer(regs) - arch->ip_advancement_by_bkpt_insn;
+}
+
+/**
+ * user_bkpt_remove_bkpt - remove breakpoint
+ * For the process that includes @tsk, remove the breakpoint specified
+ * by @user_bkpt, restoring the original opcode.
+ *
+ * Possible errors:
+ * -%EINVAL: @user_bkpt->vaddr is not a valid instruction address.
+ * -%ENOENT: There is no breakpoint instruction at @user_bkpt->vaddr.
+ * -%EFAULT: Failed to read/write @tsk's address space as needed.
+ */
+int user_bkpt_remove_bkpt(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	if (user_bkpt_validate_insn_addr(tsk, user_bkpt->vaddr) != 0)
+		return -EINVAL;
+	return arch->set_orig_insn(tsk, user_bkpt, true);
+}
+
+/**
+ * user_bkpt_set_ip - Set instruction pointer
+ * @regs: Reflects the saved state of the task
+ */
+void user_bkpt_set_ip(struct pt_regs *regs, unsigned long vaddr)
+{
+	arch->set_ip(regs, vaddr);
+}
+
+/**
+ * user_bkpt_resume_can_sleep - Check if fixup might result in sleep.
+ * @user_bkpt: the probepoint information.
+ *
+ * Returns true if fixup might result in sleep.
+ */
+bool user_bkpt_resume_can_sleep(struct user_bkpt *user_bkpt)
+{
+	return user_bkpt->fixups & USER_BKPT_FIX_SLEEPY;
+}
+
+/* Default implementation of arch->is_bkpt_insn */
+static bool is_bkpt_insn(struct user_bkpt *user_bkpt)
+{
+	return (user_bkpt->opcode == arch->bkpt_insn);
+}
+
+/* Default implementation of arch->pre_xol */
+static int pre_xol(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt,
+			struct user_bkpt_task_arch_info *tskinfo,
+			struct pt_regs *regs)
+{
+	arch->set_ip(regs, user_bkpt->xol_vaddr);
+	return 0;
+}
+
+/* Validate arch-specific info during user_bkpt initialization. */
+static int bad_arch_param(const char *param_name, int value)
+{
+	printk(KERN_ERR "user_bkpt: bad value %d/%#x for parameter %s"
+		" in user_bkpt_arch_info\n", value, value, param_name);
+	return -ENOSYS;
+}
+
+static int missing_arch_func(const char *func_name)
+{
+	printk(KERN_ERR "user_bkpt: user_bkpt_arch_info lacks required "
+					"function: %s\n", func_name);
+	return -ENOSYS;
+}
+
+static int __init init_user_bkpt(void)
+{
+	int result = 0;
+
+	/* Accept any value of bkpt_insn. */
+	if (arch->max_insn_bytes < 1)
+		result = bad_arch_param("max_insn_bytes",
+						arch->max_insn_bytes);
+	if (arch->ip_advancement_by_bkpt_insn > arch->max_insn_bytes)
+		result = bad_arch_param("ip_advancement_by_bkpt_insn",
+				arch->ip_advancement_by_bkpt_insn);
+	/* Accept any value of strategies. */
+	if (!arch->set_ip)
+		result = missing_arch_func("set_ip");
+	/* Null validate_address() is OK. */
+	if (!arch->read_opcode)
+		arch->read_opcode = read_opcode;
+	if (!arch->set_bkpt)
+		arch->set_bkpt = set_bkpt;
+	if (!arch->set_orig_insn)
+		arch->set_orig_insn = set_orig_insn;
+	if (!arch->is_bkpt_insn)
+		arch->is_bkpt_insn = is_bkpt_insn;
+	if (!arch->analyze_insn)
+		result = missing_arch_func("analyze_insn");
+	if (!arch->pre_xol)
+		arch->pre_xol = pre_xol;
+	if (uses_xol_strategy(arch->strategies) && !arch->post_xol)
+		result = missing_arch_func("post_xol");
+	/* Null cancel_xol() is OK. */
+	return result;
+}
+
+module_init(init_user_bkpt);

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 5/11] X86 details for user space breakpoint assistance.
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (3 preceding siblings ...)
  2010-03-31 15:51 ` [PATCH v2 4/11] User Space Breakpoint Assistance Layer Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-03-31 15:52 ` [PATCH v2 6/11] Slot allocation for Execution out of line Srikar Dronamraju
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

x86 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

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 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


diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 0eacb1f..851cedc 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -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
diff --git a/arch/x86/include/asm/user_bkpt.h b/arch/x86/include/asm/user_bkpt.h
new file mode 100644
index 0000000..df8a4a0
--- /dev/null
+++ b/arch/x86/include/asm/user_bkpt.h
@@ -0,0 +1,43 @@
+#ifndef _ASM_USER_BKPT_H
+#define _ASM_USER_BKPT_H
+/*
+ * User-space BreakPoint support (user_bkpt) for x86
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+typedef u8 user_bkpt_opcode_t;
+#define MAX_UINSN_BYTES 16
+#define USER_BKPT_XOL_SLOT_BYTES (MAX_UINSN_BYTES)
+
+#ifdef CONFIG_X86_64
+struct bkpt_arch_info {
+	unsigned long rip_target_address;
+	u8 orig_insn[MAX_UINSN_BYTES];
+};
+struct user_bkpt_task_arch_info {
+	unsigned long saved_scratch_register;
+};
+#else
+struct bkpt_arch_info {};
+struct user_bkpt_task_arch_info {};
+#endif
+
+#endif	/* _ASM_USER_BKPT_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 4c58352..98c74b4 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -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)
diff --git a/arch/x86/kernel/user_bkpt.c b/arch/x86/kernel/user_bkpt.c
new file mode 100644
index 0000000..eb74a8d
--- /dev/null
+++ b/arch/x86/kernel/user_bkpt.c
@@ -0,0 +1,572 @@
+/*
+ * User-space BreakPoint support (user_bkpt) for x86
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/user_bkpt.h>
+#include <asm/insn.h>
+
+#ifdef CONFIG_X86_32
+#define is_32bit_app(tsk) 1
+#else
+#define is_32bit_app(tsk) (test_tsk_thread_flag(tsk, TIF_IA32))
+#endif
+
+#define USER_BKPT_FIX_RIP_AX	0x8000
+#define USER_BKPT_FIX_RIP_CX	0x4000
+
+/* Adaptations for mhiramat x86 decoder v14. */
+#define OPCODE1(insn) ((insn)->opcode.bytes[0])
+#define OPCODE2(insn) ((insn)->opcode.bytes[1])
+#define OPCODE3(insn) ((insn)->opcode.bytes[2])
+#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value)
+
+static void set_ip(struct pt_regs *regs, unsigned long vaddr)
+{
+	regs->ip = vaddr;
+}
+
+#ifdef CONFIG_X86_64
+static bool is_riprel_insn(struct user_bkpt *user_bkpt)
+{
+	return ((user_bkpt->fixups &
+			(USER_BKPT_FIX_RIP_AX | USER_BKPT_FIX_RIP_CX)) != 0);
+}
+
+static void cancel_xol(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	if (is_riprel_insn(user_bkpt)) {
+		/*
+		 * We rewrote user_bkpt->insn to use indirect addressing rather
+		 * than rip-relative addressing for XOL.  For
+		 * single-stepping inline, put back the original instruction.
+		 */
+		memcpy(user_bkpt->insn, user_bkpt->arch_info.orig_insn,
+							MAX_UINSN_BYTES);
+		user_bkpt->strategy &= ~USER_BKPT_HNT_TSKINFO;
+	}
+}
+#endif	/* CONFIG_X86_64 */
+
+static const u32 good_insns_64[256 / 32] = {
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+	/*      ----------------------------------------------         */
+	W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */
+	W(0x10, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 10 */
+	W(0x20, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 20 */
+	W(0x30, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 30 */
+	W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */
+	W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
+	W(0x60, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */
+	W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */
+	W(0x80, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
+	W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
+	W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */
+	W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
+	W(0xc0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */
+	W(0xd0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
+	W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */
+	W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1)   /* f0 */
+	/*      ----------------------------------------------         */
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+};
+
+/* Good-instruction tables for 32-bit apps -- copied from i386 uprobes */
+
+static const u32 good_insns_32[256 / 32] = {
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+	/*      ----------------------------------------------         */
+	W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */
+	W(0x10, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) , /* 10 */
+	W(0x20, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) | /* 20 */
+	W(0x30, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) , /* 30 */
+	W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
+	W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
+	W(0x60, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */
+	W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */
+	W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
+	W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
+	W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */
+	W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
+	W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */
+	W(0xd0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
+	W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */
+	W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1)   /* f0 */
+	/*      ----------------------------------------------         */
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+};
+
+/* Using this for both 64-bit and 32-bit apps */
+static const u32 good_2byte_insns[256 / 32] = {
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+	/*      ----------------------------------------------         */
+	W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */
+	W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */
+	W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */
+	W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */
+	W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */
+	W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */
+	W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */
+	W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */
+	W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */
+	W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */
+	W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */
+	W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */
+	W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */
+	W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */
+	W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */
+	W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)   /* f0 */
+	/*      ----------------------------------------------         */
+	/*      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f         */
+};
+
+/*
+ * opcodes we'll probably never support:
+ * 6c-6d, e4-e5, ec-ed - in
+ * 6e-6f, e6-e7, ee-ef - out
+ * cc, cd - int3, int
+ * cf - iret
+ * d6 - illegal instruction
+ * f1 - int1/icebp
+ * f4 - hlt
+ * fa, fb - cli, sti
+ * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2
+ *
+ * invalid opcodes in 64-bit mode:
+ * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5
+ *
+ * 63 - we support this opcode in x86_64 but not in i386.
+ *
+ * opcodes we may need to refine support for:
+ * 0f - 2-byte instructions: For many of these instructions, the validity
+ * depends on the prefix and/or the reg field.  On such instructions, we
+ * just consider the opcode combination valid if it corresponds to any
+ * valid instruction.
+ * 8f - Group 1 - only reg = 0 is OK
+ * c6-c7 - Group 11 - only reg = 0 is OK
+ * d9-df - fpu insns with some illegal encodings
+ * f2, f3 - repnz, repz prefixes.  These are also the first byte for
+ * certain floating-point instructions, such as addsd.
+ * fe - Group 4 - only reg = 0 or 1 is OK
+ * ff - Group 5 - only reg = 0-6 is OK
+ *
+ * others -- Do we need to support these?
+ * 0f - (floating-point?) prefetch instructions
+ * 07, 17, 1f - pop es, pop ss, pop ds
+ * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes --
+ *	but 64 and 65 (fs: and gs:) seem to be used, so we support them
+ * 67 - addr16 prefix
+ * ce - into
+ * f0 - lock prefix
+ */
+
+/*
+ * TODO:
+ * - Where necessary, examine the modrm byte and allow only valid instructions
+ * in the different Groups and fpu instructions.
+ */
+
+static bool is_prefix_bad(struct insn *insn)
+{
+	int i;
+
+	for (i = 0; i < insn->prefixes.nbytes; i++) {
+		switch (insn->prefixes.bytes[i]) {
+		case 0x26:	 /*INAT_PFX_ES   */
+		case 0x2E:	 /*INAT_PFX_CS   */
+		case 0x36:	 /*INAT_PFX_DS   */
+		case 0x3E:	 /*INAT_PFX_SS   */
+		case 0xF0:	 /*INAT_PFX_LOCK */
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static void report_bad_prefix(void)
+{
+	printk(KERN_ERR "user_bkpt does not currently support probing "
+		"instructions with any of the following prefixes: "
+		"cs:, ds:, es:, ss:, lock:\n");
+}
+
+static void report_bad_1byte_opcode(int mode, user_bkpt_opcode_t op)
+{
+	printk(KERN_ERR "In %d-bit apps, "
+		"user_bkpt does not currently support probing "
+		"instructions whose first byte is 0x%2.2x\n", mode, op);
+}
+
+static void report_bad_2byte_opcode(user_bkpt_opcode_t op)
+{
+	printk(KERN_ERR "user_bkpt does not currently support probing "
+		"instructions with the 2-byte opcode 0x0f 0x%2.2x\n", op);
+}
+
+static int validate_insn_32bits(struct user_bkpt *user_bkpt, struct insn *insn)
+{
+	insn_init(insn, user_bkpt->insn, false);
+
+	/* Skip good instruction prefixes; reject "bad" ones. */
+	insn_get_opcode(insn);
+	if (is_prefix_bad(insn)) {
+		report_bad_prefix();
+		return -EPERM;
+	}
+	if (test_bit(OPCODE1(insn), (unsigned long *) good_insns_32))
+		return 0;
+	if (insn->opcode.nbytes == 2) {
+		if (test_bit(OPCODE2(insn),
+					(unsigned long *) good_2byte_insns))
+			return 0;
+		report_bad_2byte_opcode(OPCODE2(insn));
+	} else
+		report_bad_1byte_opcode(32, OPCODE1(insn));
+	return -EPERM;
+}
+
+static int validate_insn_64bits(struct user_bkpt *user_bkpt, struct insn *insn)
+{
+	insn_init(insn, user_bkpt->insn, true);
+
+	/* Skip good instruction prefixes; reject "bad" ones. */
+	insn_get_opcode(insn);
+	if (is_prefix_bad(insn)) {
+		report_bad_prefix();
+		return -EPERM;
+	}
+	if (test_bit(OPCODE1(insn), (unsigned long *) good_insns_64))
+		return 0;
+	if (insn->opcode.nbytes == 2) {
+		if (test_bit(OPCODE2(insn),
+					(unsigned long *) good_2byte_insns))
+			return 0;
+		report_bad_2byte_opcode(OPCODE2(insn));
+	} else
+		report_bad_1byte_opcode(64, OPCODE1(insn));
+	return -EPERM;
+}
+
+/*
+ * Figure out which fixups post_xol() will need to perform, and annotate
+ * user_bkpt->fixups accordingly.  To start with, user_bkpt->fixups is
+ * either zero or it reflects rip-related fixups.
+ */
+static void prepare_fixups(struct user_bkpt *user_bkpt, struct insn *insn)
+{
+	bool fix_ip = true, fix_call = false;	/* defaults */
+	insn_get_opcode(insn);	/* should be a nop */
+
+	switch (OPCODE1(insn)) {
+	case 0xc3:		/* ret/lret */
+	case 0xcb:
+	case 0xc2:
+	case 0xca:
+		/* ip is correct */
+		fix_ip = false;
+		break;
+	case 0xe8:		/* call relative - Fix return addr */
+		fix_call = true;
+		break;
+	case 0x9a:		/* call absolute - Fix return addr, not ip */
+		fix_call = true;
+		fix_ip = false;
+		break;
+	case 0xff:
+	    {
+		int reg;
+		insn_get_modrm(insn);
+		reg = MODRM_REG(insn);
+		if (reg == 2 || reg == 3) {
+			/* call or lcall, indirect */
+			/* Fix return addr; ip is correct. */
+			fix_call = true;
+			fix_ip = false;
+		} else if (reg == 4 || reg == 5) {
+			/* jmp or ljmp, indirect */
+			/* ip is correct. */
+			fix_ip = false;
+		}
+		break;
+	    }
+	case 0xea:		/* jmp absolute -- ip is correct */
+		fix_ip = false;
+		break;
+	default:
+		break;
+	}
+	if (fix_ip)
+		user_bkpt->fixups |= USER_BKPT_FIX_IP;
+	if (fix_call)
+		user_bkpt->fixups |=
+			(USER_BKPT_FIX_CALL | USER_BKPT_FIX_SLEEPY);
+}
+
+#ifdef CONFIG_X86_64
+static int handle_riprel_insn(struct user_bkpt *user_bkpt, struct insn *insn);
+#endif
+
+static int analyze_insn(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	int ret;
+	struct insn insn;
+
+	user_bkpt->fixups = 0;
+#ifdef CONFIG_X86_64
+	user_bkpt->arch_info.rip_target_address = 0x0;
+#endif
+
+	if (is_32bit_app(tsk))
+		ret = validate_insn_32bits(user_bkpt, &insn);
+	else
+		ret = validate_insn_64bits(user_bkpt, &insn);
+	if (ret != 0)
+		return ret;
+	if (user_bkpt->strategy & USER_BKPT_HNT_INLINE)
+		return 0;
+#ifdef CONFIG_X86_64
+	ret = handle_riprel_insn(user_bkpt, &insn);
+	if (ret == -1)
+		/* rip-relative; can't XOL */
+		return 0;
+	else if (ret == 0)
+		/* not rip-relative */
+		user_bkpt->strategy &= ~USER_BKPT_HNT_TSKINFO;
+#endif
+	prepare_fixups(user_bkpt, &insn);
+	return 0;
+}
+
+#ifdef CONFIG_X86_64
+/*
+ * If user_bkpt->insn doesn't use rip-relative addressing, return 0.  Otherwise,
+ * rewrite the instruction so that it accesses its memory operand
+ * indirectly through a scratch register.  Set user_bkpt->fixups and
+ * user_bkpt->arch_info.rip_target_address accordingly.  (The contents of the
+ * scratch register will be saved before we single-step the modified
+ * instruction, and restored afterward.)  Return 1.
+ *
+ * (... except if the client doesn't support our USER_BKPT_HNT_TSKINFO strategy,
+ * we must suppress XOL for rip-relative instructions: return -1.)
+ *
+ * We do this because a rip-relative instruction can access only a
+ * relatively small area (+/- 2 GB from the instruction), and the XOL
+ * area typically lies beyond that area.  At least for instructions
+ * that store to memory, we can't execute the original instruction
+ * and "fix things up" later, because the misdirected store could be
+ * disastrous.
+ *
+ * Some useful facts about rip-relative instructions:
+ * - There's always a modrm byte.
+ * - There's never a SIB byte.
+ * - The displacement is always 4 bytes.
+ */
+static int handle_riprel_insn(struct user_bkpt *user_bkpt, struct insn *insn)
+{
+	u8 *cursor;
+	u8 reg;
+
+	if (!insn_rip_relative(insn))
+		return 0;
+
+	/*
+	 * We have a rip-relative instruction.  To allow this instruction
+	 * to be single-stepped out of line, the client must provide us
+	 * with a per-task user_bkpt_task_arch_info object.
+	 */
+	if (!(user_bkpt->strategy & USER_BKPT_HNT_TSKINFO)) {
+		user_bkpt->strategy |= USER_BKPT_HNT_INLINE;
+		return -1;
+	}
+	memcpy(user_bkpt->arch_info.orig_insn, user_bkpt->insn,
+							MAX_UINSN_BYTES);
+
+	/*
+	 * Point cursor at the modrm byte.  The next 4 bytes are the
+	 * displacement.  Beyond the displacement, for some instructions,
+	 * is the immediate operand.
+	 */
+	cursor = user_bkpt->insn + insn->prefixes.nbytes
+			+ insn->rex_prefix.nbytes + insn->opcode.nbytes;
+	insn_get_length(insn);
+
+	/*
+	 * Convert from rip-relative addressing to indirect addressing
+	 * via a scratch register.  Change the r/m field from 0x5 (%rip)
+	 * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field.
+	 */
+	reg = MODRM_REG(insn);
+	if (reg == 0) {
+		/*
+		 * The register operand (if any) is either the A register
+		 * (%rax, %eax, etc.) or (if the 0x4 bit is set in the
+		 * REX prefix) %r8.  In any case, we know the C register
+		 * is NOT the register operand, so we use %rcx (register
+		 * #1) for the scratch register.
+		 */
+		user_bkpt->fixups = USER_BKPT_FIX_RIP_CX;
+		/* Change modrm from 00 000 101 to 00 000 001. */
+		*cursor = 0x1;
+	} else {
+		/* Use %rax (register #0) for the scratch register. */
+		user_bkpt->fixups = USER_BKPT_FIX_RIP_AX;
+		/* Change modrm from 00 xxx 101 to 00 xxx 000 */
+		*cursor = (reg << 3);
+	}
+
+	/* Target address = address of next instruction + (signed) offset */
+	user_bkpt->arch_info.rip_target_address = (long) user_bkpt->vaddr +
+				insn->length + insn->displacement.value;
+	/* Displacement field is gone; slide immediate field (if any) over. */
+	if (insn->immediate.nbytes) {
+		cursor++;
+		memmove(cursor, cursor + insn->displacement.nbytes,
+						insn->immediate.nbytes);
+	}
+	return 1;
+}
+
+/*
+ * If we're emulating a rip-relative instruction, save the contents
+ * of the scratch register and store the target address in that register.
+ */
+static int pre_xol(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	BUG_ON(!user_bkpt->xol_vaddr);
+	regs->ip = user_bkpt->xol_vaddr;
+	if (user_bkpt->fixups & USER_BKPT_FIX_RIP_AX) {
+		tskinfo->saved_scratch_register = regs->ax;
+		regs->ax = user_bkpt->arch_info.rip_target_address;
+	} else if (user_bkpt->fixups & USER_BKPT_FIX_RIP_CX) {
+		tskinfo->saved_scratch_register = regs->cx;
+		regs->cx = user_bkpt->arch_info.rip_target_address;
+	}
+	return 0;
+}
+#endif
+
+/*
+ * Called by post_xol() to adjust the return address pushed by a call
+ * instruction executed out of line.
+ */
+static int adjust_ret_addr(struct task_struct *tsk, unsigned long sp,
+							long correction)
+{
+	int rasize, ncopied;
+	long ra = 0;
+
+	if (is_32bit_app(tsk))
+		rasize = 4;
+	else
+		rasize = 8;
+	ncopied = user_bkpt_read_vm(tsk, (void __user *) sp, &ra, rasize);
+	if (unlikely(ncopied != rasize))
+		goto fail;
+	ra += correction;
+	ncopied = user_bkpt_write_data(tsk, (void __user *) sp, &ra, rasize);
+	if (unlikely(ncopied != rasize))
+		goto fail;
+	return 0;
+
+fail:
+	printk(KERN_ERR
+		"user_bkpt: Failed to adjust return address after"
+		" single-stepping call instruction;"
+		" pid=%d, sp=%#lx\n", tsk->pid, sp);
+	return -EFAULT;
+}
+
+/*
+ * Called after single-stepping.  user_bkpt->vaddr is the address of the
+ * instruction whose first byte has been replaced by the "int3"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is user_bkpt->xol_vaddr.
+ *
+ * This function prepares to resume execution after the single-step.
+ * We have to fix things up as follows:
+ *
+ * Typically, the new ip is relative to the copied instruction.  We need
+ * to make it relative to the original instruction (FIX_IP).  Exceptions
+ * are return instructions and absolute or indirect jump or call instructions.
+ *
+ * If the single-stepped instruction was a call, the return address that
+ * is atop the stack is the address following the copied instruction.  We
+ * need to make it the address following the original instruction (FIX_CALL).
+ *
+ * If the original instruction was a rip-relative instruction such as
+ * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent
+ * instruction using a scratch register -- e.g., "movl %edx,(%rax)".
+ * We need to restore the contents of the scratch register and adjust
+ * the ip, keeping in mind that the instruction we executed is 4 bytes
+ * shorter than the original instruction (since we squeezed out the offset
+ * field).  (FIX_RIP_AX or FIX_RIP_CX)
+ */
+static int post_xol(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	/* Typically, the XOL vma is at a high addr, so correction < 0. */
+	long correction = (long) (user_bkpt->vaddr - user_bkpt->xol_vaddr);
+	int result = 0;
+
+#ifdef CONFIG_X86_64
+	if (is_riprel_insn(user_bkpt)) {
+		if (user_bkpt->fixups & USER_BKPT_FIX_RIP_AX)
+			regs->ax = tskinfo->saved_scratch_register;
+		else
+			regs->cx = tskinfo->saved_scratch_register;
+		/*
+		 * The original instruction includes a displacement, and so
+		 * is 4 bytes longer than what we've just single-stepped.
+		 * Fall through to handle stuff like "jmpq *...(%rip)" and
+		 * "callq *...(%rip)".
+		 */
+		correction += 4;
+	}
+#endif
+	if (user_bkpt->fixups & USER_BKPT_FIX_IP)
+		regs->ip += correction;
+	if (user_bkpt->fixups & USER_BKPT_FIX_CALL)
+		result = adjust_ret_addr(tsk, regs->sp, correction);
+	return result;
+}
+
+struct user_bkpt_arch_info user_bkpt_arch_info = {
+	.bkpt_insn = 0xcc,
+	.ip_advancement_by_bkpt_insn = 1,
+	.max_insn_bytes = MAX_UINSN_BYTES,
+#ifdef CONFIG_X86_32
+	.strategies = 0x0,
+#else
+	/* rip-relative instructions require special handling. */
+	.strategies = USER_BKPT_HNT_TSKINFO,
+	.pre_xol = pre_xol,
+	.cancel_xol = cancel_xol,
+#endif
+	.set_ip = set_ip,
+	.analyze_insn = analyze_insn,
+	.post_xol = post_xol,
+};

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 6/11] Slot allocation for Execution out of line
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (4 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 5/11] X86 details for user space breakpoint assistance Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-03-31 15:52 ` [PATCH v2 7/11] Uprobes Implementation Srikar Dronamraju
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Slot 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.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 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


diff --git a/arch/Kconfig b/arch/Kconfig
index 5667434..1a53e30 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -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
 
diff --git a/include/linux/user_bkpt_xol.h b/include/linux/user_bkpt_xol.h
new file mode 100644
index 0000000..b1d4d5a
--- /dev/null
+++ b/include/linux/user_bkpt_xol.h
@@ -0,0 +1,61 @@
+#ifndef _LINUX_XOL_H
+#define _LINUX_XOL_H
+/*
+ * User-space BreakPoint support (user_bkpt) -- Allocation of instruction
+ * slots for execution out of line (XOL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2009, 2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+#define UINSNS_PER_PAGE	(PAGE_SIZE/USER_BKPT_XOL_SLOT_BYTES)
+#define MAX_USER_BKPT_XOL_SLOTS UINSNS_PER_PAGE
+
+#ifdef CONFIG_USER_BKPT_XOL
+extern unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
+							void *xol_area);
+extern void xol_free_insn_slot(unsigned long, void *xol_area);
+extern int xol_validate_vaddr(struct pid *pid, unsigned long vaddr,
+							void *xol_area);
+extern void *xol_alloc_area(void);
+extern void xol_free_area(void *xol_area);
+#else /* CONFIG_USER_BKPT_XOL */
+static inline unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
+							void *xol_area)
+{
+	return 0;
+}
+static inline void xol_free_insn_slot(unsigned long slot_addr, void *xol_area)
+{
+}
+static inline int xol_validate_vaddr(struct pid *pid, unsigned long vaddr,
+							void *xol_area)
+{
+	return -ENOSYS;
+}
+static inline void *xol_alloc_area(void)
+{
+	return NULL;
+}
+static inline void xol_free_area(void *xol_area)
+{
+}
+#endif /* CONFIG_USER_BKPT_XOL */
+
+#endif  /* _LINUX_XOL_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 36a35b0..e404aa0 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -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 <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/user_bkpt_xol.c b/kernel/user_bkpt_xol.c
new file mode 100644
index 0000000..409b481
--- /dev/null
+++ b/kernel/user_bkpt_xol.c
@@ -0,0 +1,289 @@
+/*
+ * User-space BreakPoint support (user_bkpt) -- Allocation of instruction
+ * slots for execution out of line (XOL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2009, 2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+/*
+ * Every probepoint gets its own slot.  Once it's assigned a slot, it
+ * keeps that slot until the probepoint goes away. Only definite number
+ * of slots are allocated.
+ */
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/pid.h>
+#include <linux/sched.h>
+#include <linux/user_bkpt.h>
+#include <linux/user_bkpt_xol.h>
+
+struct user_bkpt_xol_area {
+	spinlock_t lock;	/* protects bitmap and slot (de)allocation*/
+	unsigned long *bitmap;	/* 0 = free slot */
+
+	/*
+	 * We keep the vma's vm_start rather than a pointer to the vma
+	 * itself.  The probed process or a naughty kernel module could make
+	 * the vma go away, and we must handle that reasonably gracefully.
+	 */
+	unsigned long vaddr;		/* Page(s) of instruction slots */
+};
+
+static int xol_add_vma(struct user_bkpt_xol_area *area)
+{
+	struct vm_area_struct *vma;
+	struct mm_struct *mm;
+	struct file *file;
+	unsigned long addr;
+
+	mm = get_task_mm(current);
+	if (!mm)
+		return -ESRCH;
+
+	down_write(&mm->mmap_sem);
+	/*
+	 * Find the end of the top mapping and skip a page.
+	 * If there is no space for PAGE_SIZE above
+	 * that, mmap will ignore our address hint.
+	 *
+	 * We allocate a "fake" unlinked shmem file because
+	 * anonymous memory might not be granted execute
+	 * permission when the selinux security hooks have
+	 * their way.
+	 */
+	vma = rb_entry(rb_last(&mm->mm_rb), struct vm_area_struct, vm_rb);
+	addr = vma->vm_end + PAGE_SIZE;
+	file = shmem_file_setup("uprobes/xol", PAGE_SIZE, VM_NORESERVE);
+	if (!file) {
+		printk(KERN_ERR "user_bkpt_xol failed to setup shmem_file "
+			"while allocating vma for pid/tgid %d/%d for "
+			"single-stepping out of line.\n",
+			current->pid, current->tgid);
+		goto fail;
+	}
+	addr = do_mmap_pgoff(file, addr, PAGE_SIZE, PROT_EXEC, MAP_PRIVATE, 0);
+	fput(file);
+
+	if (addr & ~PAGE_MASK) {
+		printk(KERN_ERR "user_bkpt_xol failed to allocate a vma for "
+				"pid/tgid %d/%d for single-stepping out of "
+				"line.\n", current->pid, current->tgid);
+		goto fail;
+	}
+	vma = find_vma(mm, addr);
+
+	/* Don't expand vma on mremap(). */
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
+	area->vaddr = vma->vm_start;
+	up_write(&mm->mmap_sem);
+	mmput(mm);
+	return 0;
+
+fail:
+	up_write(&mm->mmap_sem);
+	mmput(mm);
+	return -ENOMEM;
+}
+
+/**
+ * xol_alloc_area - Allocate process's user_bkpt_xol_area.
+ * This area will be used for storing instructions for execution out of
+ * line.
+ */
+void *xol_alloc_area(void)
+{
+	struct user_bkpt_xol_area *area = NULL;
+
+	area = kzalloc(sizeof(*area), GFP_USER);
+	if (unlikely(!area))
+		return NULL;
+
+	area->bitmap = kzalloc(BITS_TO_LONGS(UINSNS_PER_PAGE) * sizeof(long),
+								GFP_USER);
+
+	if (!area->bitmap)
+		goto fail;
+	if (xol_add_vma(area)) {
+		kfree(area->bitmap);
+		goto fail;
+	}
+	spin_lock_init(&area->lock);
+	return (void *)area;
+
+fail:
+	kfree(area);
+	return NULL;
+}
+
+void xol_free_area(void *xol_area)
+{
+	struct user_bkpt_xol_area *area;
+
+	area = (struct user_bkpt_xol_area *)xol_area;
+	kfree(area->bitmap);
+	kfree(area);
+}
+
+/*
+ * Find a slot
+ *  - searching in existing vmas for a free slot.
+ *  - If no free slot in existing vmas, return 0;
+ *  called with lock acquired.
+ */
+static unsigned long xol_take_insn_slot(struct user_bkpt_xol_area *area)
+{
+	unsigned long slot_addr;
+	int slot_nr;
+
+	slot_nr = find_first_zero_bit(area->bitmap, UINSNS_PER_PAGE);
+	if (slot_nr < UINSNS_PER_PAGE) {
+		set_bit(slot_nr, area->bitmap);
+		slot_addr = area->vaddr +
+				(slot_nr * USER_BKPT_XOL_SLOT_BYTES);
+		return slot_addr;
+	}
+
+	return 0;
+}
+
+/**
+ * xol_get_insn_slot - If user_bkpt was not allocated a slot, then
+ * allocate a slot. If user_bkpt_insert_bkpt is already called, (i.e
+ * user_bkpt.vaddr != 0) then copy the instruction into the slot.
+ * Returns the allocated slot address or 0.
+ * @user_bkpt: probepoint information
+ * @xol_area refers the unique per process user_bkpt_xol_area for
+ * this process.
+ */
+unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt, void *xol_area)
+{
+	struct user_bkpt_xol_area *area;
+	unsigned long flags;
+	int len;
+
+	area = (struct user_bkpt_xol_area *)xol_area;
+	if (unlikely(!area))
+		return 0;
+
+	if (!user_bkpt->xol_vaddr) {
+		spin_lock_irqsave(&area->lock, flags);
+		if (likely(!user_bkpt->xol_vaddr))
+			user_bkpt->xol_vaddr = xol_take_insn_slot(area);
+		spin_unlock_irqrestore(&area->lock, flags);
+
+		/*
+		 * Initialize the slot if user_bkpt->vaddr points to valid
+		 * instruction slot.
+		 */
+		if (likely(user_bkpt->xol_vaddr) && user_bkpt->vaddr) {
+			len = access_process_vm(current, user_bkpt->xol_vaddr,
+				user_bkpt->insn, USER_BKPT_XOL_SLOT_BYTES, 1);
+			if (unlikely(len < USER_BKPT_XOL_SLOT_BYTES))
+				printk(KERN_ERR "Failed to copy instruction"
+						" at %#lx len = %d\n",
+						user_bkpt->vaddr, len);
+		}
+	}
+	return user_bkpt->xol_vaddr;
+}
+
+/**
+ * xol_free_insn_slot - If slot was earlier allocated by
+ * @xol_get_insn_slot(), make the slot available for
+ * subsequent requests.
+ * @slot_addr: slot address as returned by
+ * @xol_get_insn_area().
+ * @xol_area refers the unique per process user_bkpt_xol_area for
+ * this process.
+ */
+void xol_free_insn_slot(unsigned long slot_addr, void *xol_area)
+{
+	struct user_bkpt_xol_area *area;
+	unsigned long vma_end;
+	int found = 0;
+
+	area = (struct user_bkpt_xol_area *)xol_area;
+	if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
+		return;
+
+	if (unlikely(!area))
+		return;
+
+	vma_end = area->vaddr + PAGE_SIZE;
+	if (area->vaddr <= slot_addr && slot_addr < vma_end) {
+		int slot_nr;
+		unsigned long offset = slot_addr - area->vaddr;
+		unsigned long flags;
+
+		BUG_ON(offset % USER_BKPT_XOL_SLOT_BYTES);
+
+		slot_nr = offset / USER_BKPT_XOL_SLOT_BYTES;
+		BUG_ON(slot_nr >= UINSNS_PER_PAGE);
+
+		spin_lock_irqsave(&area->lock, flags);
+		clear_bit(slot_nr, area->bitmap);
+		spin_unlock_irqrestore(&area->lock, flags);
+		found = 1;
+	}
+
+	if (!found)
+		printk(KERN_ERR "%s: no XOL vma for slot address %#lx\n",
+						__func__, slot_addr);
+}
+
+/**
+ * xol_validate_vaddr - Verify if the specified address is in an
+ * executable vma, but not in an XOL vma.
+ *	- Return 0 if the specified virtual address is in an
+ *	  executable vma, but not in an XOL vma.
+ *	- Return 1 if the specified virtual address is in an
+ *	  XOL vma.
+ *	- Return -EINTR otherwise.(i.e non executable vma, or
+ *	  not a valid address
+ * @pid: the probed process
+ * @vaddr: virtual address of the instruction to be validated.
+ * @xol_area refers the unique per process user_bkpt_xol_area for
+ * this process.
+ */
+int xol_validate_vaddr(struct pid *pid, unsigned long vaddr, void *xol_area)
+{
+	struct user_bkpt_xol_area *area;
+	struct task_struct *tsk;
+	unsigned long vma_end;
+	int result;
+
+	area = (struct user_bkpt_xol_area *) xol_area;
+	tsk = pid_task(pid, PIDTYPE_PID);
+	result = user_bkpt_validate_insn_addr(tsk, vaddr);
+	if (result != 0)
+		return result;
+
+	if (unlikely(!area))
+		return 0;
+
+	vma_end = area->vaddr + PAGE_SIZE;
+	if (area->vaddr <= vaddr && vaddr < vma_end)
+		result = 1;
+
+	return result;
+}

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 7/11] Uprobes Implementation
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (5 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 6/11] Slot allocation for Execution out of line Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-04-13 18:35   ` Oleg Nesterov
  2010-03-31 15:52 ` [PATCH v2 8/11] X86 details for uprobes Srikar Dronamraju
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Uprobes 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.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
---

 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


diff --git a/arch/Kconfig b/arch/Kconfig
index 1a53e30..5144fc3 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -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:
 #
diff --git a/include/linux/sched.h b/include/linux/sched.h
index dad7f66..2d2433a 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -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. */
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
index 10db010..9a91d1e 100644
--- a/include/linux/tracehook.h
+++ b/include/linux/tracehook.h
@@ -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 */
 
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
new file mode 100644
index 0000000..58bb07b
--- /dev/null
+++ b/include/linux/uprobes.h
@@ -0,0 +1,187 @@
+#ifndef _LINUX_UPROBES_H
+#define _LINUX_UPROBES_H
+/*
+ * Userspace Probes (UProbes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/spinlock_types.h>
+#include <asm/atomic.h>
+#include <linux/user_bkpt.h>
+#include <linux/user_bkpt_xol.h>
+
+struct task_struct;
+struct pid;
+struct pt_regs;
+
+/* This is what the user supplies us. */
+struct uprobe {
+	/*
+	 * The pid of the probed process.  Currently, this can be the
+	 * thread ID (task->pid) of any active thread in the process.
+	 */
+	pid_t pid;
+
+	/* Location of the probepoint */
+	unsigned long vaddr;
+
+	/* Handler to run when the probepoint is hit */
+	void (*handler)(struct uprobe*, struct pt_regs*);
+
+	/* true if handler runs in interrupt context*/
+	bool handler_in_interrupt;
+};
+
+/*
+ * uprobe_process -- not a user-visible struct.
+ * A uprobe_process represents a probed process.  A process can have
+ * multiple probepoints (each represented by a uprobe_probept) and
+ * one or more threads (each represented by a uprobe_task).
+ */
+struct uprobe_process {
+	/*
+	 * mutex locked for any change to the uprobe_process's
+	 * graph (including uprobe_probept, taking a slot in xol_area) --
+	 * e.g., due to probe [un]registration or special events like exit.
+	 */
+	struct mutex mutex;
+
+	/* Table of uprobe_probepts registered for this process */
+	struct list_head uprobe_list;
+
+	atomic_t refcount;
+
+	/* lock held while traversing/modifying uprobe_list and n_ppts */
+	spinlock_t pptlist_lock;	/* protects uprobe_list */
+
+	/* number of probept allocated for this process */
+	int n_ppts;
+
+	/*
+	 * All threads (tasks) in a process share the same uprobe_process.
+	 */
+	struct pid *tg_leader;
+
+	/*
+	 * Manages slots for instruction-copies to be single-stepped
+	 * out of line.
+	 */
+	void *xol_area;
+};
+
+/*
+ * uprobe_probept -- not a user-visible struct.
+ * A uprobe_probept represents a probepoint.
+ * Guarded by uproc->lock.
+ */
+struct uprobe_probept {
+	/* breakpoint/XOL details */
+	struct user_bkpt user_bkpt;
+
+	/*
+	 * ppt goes in the uprobe_process->uprobe_table when registered --
+	 * even before the breakpoint has been inserted.
+	 */
+	struct list_head ut_node;
+
+	atomic_t refcount;
+
+	/* The parent uprobe_process */
+	struct uprobe_process *uproc;
+
+	struct uprobe *uprobe;
+};
+
+enum uprobe_task_state {
+	UTASK_RUNNING,
+	UTASK_BP_HIT,
+	UTASK_SSTEP
+};
+
+/*
+ * uprobe_utask -- not a user-visible struct.
+ * Corresponds to a thread in a probed process.
+ * Guarded by uproc->mutex.
+ */
+struct uprobe_task {
+	struct user_bkpt_task_arch_info arch_info;
+
+	/* Back pointer to the associated uprobe_process */
+	struct uprobe_process *uproc;
+
+	enum uprobe_task_state state;
+
+
+	struct uprobe_probept *active_ppt;
+};
+
+#ifdef CONFIG_UPROBES
+extern int uprobes_exception_notify(struct notifier_block *self,
+				unsigned long val, void *data);
+extern int uprobe_bkpt_notifier(struct pt_regs *regs);
+extern int uprobe_post_notifier(struct pt_regs *regs);
+extern void uprobe_notify_resume(struct pt_regs *regs);
+extern void arch_uprobe_enable_sstep(struct pt_regs *regs);
+extern void arch_uprobe_disable_sstep(struct pt_regs *regs);
+extern int register_uprobe(struct uprobe *u);
+extern void unregister_uprobe(struct uprobe *u);
+extern void uprobe_free_utask(void);
+extern void uprobe_handle_clone(unsigned long clone_flags,
+				struct task_struct *child);
+extern void uprobe_enable_interrupts(void);
+extern void uprobe_disable_interrupts(void);
+#else
+
+/*
+ * Only register_uprobe() and unregister_uprobe() are part of
+ * the client API.
+ */
+static inline int register_uprobe(struct uprobe *u)
+{
+	return -ENOSYS;
+}
+static inline void unregister_uprobe(struct uprobe *u)
+{
+}
+static inline void uprobe_free_utask(void)
+{
+}
+static inline void uprobe_handle_clone(unsigned long clone_flags,
+				struct task_struct *child)
+{
+}
+static inline void uprobe_notify_resume(struct pt_regs *regs)
+{
+}
+static inline int uprobe_bkpt_notifier(struct pt_regs *regs)
+{
+	return 0;
+}
+static inline int uprobe_post_notifier(struct pt_regs *regs)
+{
+	return 0;
+}
+#endif	/* CONFIG_UPROBES */
+#endif	/* _LINUX_UPROBES_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index e404aa0..f0cfb02 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -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 <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/fork.c b/kernel/fork.c
index 4799c5f..63c5efc 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -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
 	 */
diff --git a/kernel/uprobes.c b/kernel/uprobes.c
new file mode 100644
index 0000000..4a1325d
--- /dev/null
+++ b/kernel/uprobes.c
@@ -0,0 +1,803 @@
+/*
+ *  Userspace Probes (UProbes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+#include <linux/types.h>
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/uprobes.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/errno.h>
+#include <linux/kdebug.h>
+
+static u16 user_bkpt_strategies;
+
+struct notifier_block uprobes_exception_nb = {
+	.notifier_call = uprobes_exception_notify,
+	.priority = 0x7ffffff0,
+};
+
+typedef void (*uprobe_handler_t)(struct uprobe*, struct pt_regs*);
+
+/* Guards lookup, creation, and deletion of uproc. */
+static DEFINE_MUTEX(uprobe_mutex);
+
+static inline void get_probept(struct uprobe_probept *ppt)
+{
+	atomic_inc(&ppt->refcount);
+}
+
+/*
+ * Creates a uprobe_probept and connects it to uprobe and uproc.
+ * Runs with uproc->mutex locked.
+ */
+static struct uprobe_probept *add_probept(struct uprobe *u,
+					struct uprobe_process *uproc)
+{
+	struct uprobe_probept *ppt;
+
+	ppt = kzalloc(sizeof *ppt, GFP_USER);
+	if (unlikely(ppt == NULL))
+		return ERR_PTR(-ENOMEM);
+
+	ppt->user_bkpt.vaddr = u->vaddr;
+	ppt->uprobe = u;
+	ppt->user_bkpt.xol_vaddr = 0;
+
+	ppt->user_bkpt.strategy = user_bkpt_strategies;
+
+	ppt->uproc = uproc;
+	INIT_LIST_HEAD(&ppt->ut_node);
+	spin_lock(&uproc->pptlist_lock);
+	list_add(&ppt->ut_node, &uproc->uprobe_list);
+	uproc->n_ppts++;
+	spin_unlock(&uproc->pptlist_lock);
+	atomic_set(&ppt->refcount, 1);
+	return ppt;
+}
+
+static void put_probept(struct uprobe_probept *ppt)
+{
+	struct uprobe_process *uproc;
+
+	uproc = ppt->uproc;
+	if (atomic_dec_and_lock(&ppt->refcount, &uproc->pptlist_lock)) {
+		list_del(&ppt->ut_node);
+		uproc->n_ppts--;
+		xol_free_insn_slot(ppt->user_bkpt.xol_vaddr, uproc->xol_area);
+		spin_unlock(&uproc->pptlist_lock);
+		kfree(ppt);
+	}
+}
+
+/*
+ * In the given uproc's hash table of probepoints, find the one with the
+ * specified virtual address.
+ * Called with uproc->pptlist_lock acquired.
+ */
+static struct uprobe_probept *find_probept(struct uprobe_process *uproc,
+							unsigned long vaddr)
+{
+	struct uprobe_probept *ppt;
+
+	list_for_each_entry(ppt, &uproc->uprobe_list, ut_node) {
+		if (ppt->user_bkpt.vaddr == vaddr)
+			return ppt;
+	}
+	return NULL;
+}
+
+/*
+ * Save a copy of the original instruction (so it can be single-stepped
+ * out of line), insert the breakpoint instruction.
+ * Runs with uproc->mutex locked.
+ */
+static int insert_bkpt(struct uprobe_probept *ppt, struct task_struct *tsk)
+{
+	int result;
+
+	if (tsk)
+		result = user_bkpt_insert_bkpt(tsk, &ppt->user_bkpt);
+	else
+		/* No surviving tasks associated with ppt->uproc */
+		result = -ESRCH;
+	return result;
+}
+
+ /* Runs with uproc->mutex locked. */
+static void remove_bkpt(struct uprobe_probept *ppt, struct task_struct *tsk)
+{
+	if (!tsk)
+		return;
+
+	if (user_bkpt_remove_bkpt(tsk, &ppt->user_bkpt) != 0) {
+		printk(KERN_ERR "Error removing uprobe at pid %d vaddr %#lx:"
+			" can't restore original instruction\n",
+			tsk->tgid, ppt->user_bkpt.vaddr);
+		/*
+		 * This shouldn't happen, since we were previously able
+		 * to write the breakpoint at that address.  There's not
+		 * much we can do besides let the process die with a
+		 * SIGTRAP the next time the breakpoint is hit.
+		 */
+	}
+}
+
+/* Runs with the uprobe_mutex held. */
+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;
+	}
+
+	uproc = tsk->utask->uproc;
+	BUG_ON(uproc->tg_leader != tg_leader);
+	atomic_inc(&uproc->refcount);
+	put_task_struct(tsk);
+	return uproc;
+}
+
+/*
+ * uproc's process is exiting or exec-ing.
+ * Called in context of the last thread of the process. This thread is also
+ * exiting. Hence we can traverse the uprobe_list without
+ * taking spinlock.
+ * Called with no locks held.
+ */
+static int free_uprocess(struct uprobe_process *uproc)
+{
+	struct uprobe_probept *ppt, *pnode;
+
+	mutex_lock(&uproc->mutex);
+	list_for_each_entry_safe(ppt, pnode, &uproc->uprobe_list, ut_node) {
+		put_probept(ppt);
+	}
+	if (uproc->xol_area)
+		xol_free_area(uproc->xol_area);
+
+	put_pid(uproc->tg_leader);
+	uproc->tg_leader = NULL;
+	mutex_unlock(&uproc->mutex);	/* So kfree doesn't complain */
+	kfree(uproc);
+	return 0;
+}
+
+/*
+ * Dismantle uproc and all its remaining uprobe_tasks.
+ * Runs with uprobe_mutex held;
+ */
+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) {
+			kfree(tsk->utask);
+			tsk->utask = NULL;
+		}
+		tsk = next_thread(tsk);
+	} while (tsk != start);
+	rcu_read_unlock();
+}
+
+/* Called with no locks held */
+static void put_uprocess(struct uprobe_process *uproc)
+{
+	if (atomic_dec_and_test(&uproc->refcount))
+		free_uprocess(uproc);
+}
+
+/*
+ * Called with no locks held.
+ * Called in context of a exiting or a exec-ing thread.
+ */
+void uprobe_free_utask(void)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_task *utask;
+
+	mutex_lock(&uprobe_mutex);
+	utask = current->utask;
+	if (!utask) {
+		mutex_unlock(&uprobe_mutex);
+		return;
+	}
+	uproc = utask->uproc;
+
+	kfree(utask);
+	current->utask = NULL;
+	mutex_unlock(&uprobe_mutex);
+	put_uprocess(uproc);
+}
+
+/*
+ * Allocate a uprobe_task object for the task and add it to uproc's list.
+ * Called with t "got" and uprobe_mutex locked.  Called in one of
+ * the following cases:
+ * - before setting the first uprobe in t's process
+ * - we're in clone() and t is the newly added thread
+ *
+ * Returns:
+ * - pointer to new uprobe_task on success
+ * - negative errno otherwise
+ */
+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;
+	t->utask = utask;
+	atomic_inc(&uproc->refcount);
+
+	return utask;
+}
+
+/*
+ * Find the next thread that doesn't have a corresponding uprobe_task
+ * yet.  start is the a ref-counted thread in the probed process. We
+ * decrement the ref-count for start here.
+ *
+ * Return the next ref-counted thread for that process, if any, else
+ * NULL.
+ */
+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;
+			if (t->utask)
+				/* Already added */
+				goto dont_add;
+
+			/* Found thread/task to add. */
+			get_task_struct(t);
+			next_t = t;
+			break;
+dont_add:
+			t = next_thread(t);
+		} while (t != start);
+	}
+	rcu_read_unlock();
+	return next_t;
+}
+
+/* Runs with uprobe_mutex held; */
+static struct uprobe_process *create_uprocess(struct pid *tg_leader)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_task *utask;
+	struct task_struct *add_me, *cur_t;
+	long err;
+
+	uproc = kzalloc(sizeof *uproc, GFP_USER);
+	if (unlikely(uproc == NULL))
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize fields */
+	mutex_init(&uproc->mutex);
+	spin_lock_init(&uproc->pptlist_lock);
+	atomic_set(&uproc->refcount, 1);
+	uproc->tg_leader = get_pid(tg_leader);
+	INIT_LIST_HEAD(&uproc->uprobe_list);
+
+	/*
+	 * 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);
+		put_task_struct(cur_t);
+		cur_t = add_me;
+	} while (add_me != NULL);
+
+	return uproc;
+
+fail:
+	cleanup_uprocess(uproc, cur_t);
+	put_task_struct(cur_t);
+	kfree(uproc);
+	return ERR_PTR(err);
+}
+
+/*
+ * Given a numeric thread ID, return a ref-counted struct pid for the
+ * task-group-leader thread.
+ */
+static struct pid *get_tg_leader(pid_t p)
+{
+	struct pid *pid = NULL;
+
+	rcu_read_lock();
+	if (current->nsproxy)
+		pid = find_vpid(p);
+	if (pid) {
+		struct task_struct *t = pid_task(pid, PIDTYPE_PID);
+
+		if (!t || unlikely(t->flags & PF_EXITING))
+			pid = NULL;
+		else
+			pid = get_pid(task_tgid(t));
+	}
+	rcu_read_unlock();
+	return pid;
+}
+
+/* See Documentation/uprobes.txt. */
+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);
+			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);
+	if (ret < 0)
+		goto fail_uproc;
+
+	/* See if we already have a probepoint at the vaddr. */
+	spin_lock(&uproc->pptlist_lock);
+	ppt = find_probept(uproc, u->vaddr);
+	spin_unlock(&uproc->pptlist_lock);
+	if (ppt) {
+		/*
+		 * A uprobe already exists at that address.
+		 */
+		ret = -EALREADY;
+		goto fail_uproc;
+	} else {
+		ppt = add_probept(u, uproc);
+		if (IS_ERR(ppt)) {
+			ret = (int) PTR_ERR(ppt);
+			goto fail_uproc;
+		}
+		ret = insert_bkpt(ppt, pid_task(p, PIDTYPE_PID));
+		if (ret != 0)
+			goto fail_uproc;
+	}
+
+fail_uproc:
+	mutex_unlock(&uproc->mutex);
+	put_uprocess(uproc);
+
+fail_tsk:
+	put_pid(p);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(register_uprobe);
+
+/* See Documentation/uprobes.txt. */
+void unregister_uprobe(struct uprobe *u)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	struct pid *p;
+
+	if (!u)
+		return;
+	p = get_tg_leader(u->pid);
+	if (!p)
+		return;
+
+	/* Get the uprobe_process for this pid. */
+	mutex_lock(&uprobe_mutex);
+	uproc = find_uprocess(p);
+	mutex_unlock(&uprobe_mutex);
+	if (!uproc) {
+		put_pid(p);
+		return;
+	}
+
+	/*
+	 * Lock uproc before walking the graph, in case the process
+	 * we're probing is exiting.
+	 */
+	mutex_lock(&uproc->mutex);
+
+	spin_lock(&uproc->pptlist_lock);
+	ppt = find_probept(uproc, u->vaddr);
+	spin_unlock(&uproc->pptlist_lock);
+	if (!ppt)
+		/*
+		 * This probe was never successfully registered, or
+		 * has already been unregistered.
+		 */
+		goto done;
+
+	if (ppt->uprobe != u)
+		/*
+		 * unregister request doesnt correspond to successful
+		 * register request.
+		 */
+		goto done;
+
+	remove_bkpt(ppt, pid_task(p, PIDTYPE_PID));
+
+	/*
+	 * Breakpoint is removed; however a thread could have hit the
+	 * same breakpoint and yet to find its corresponding probepoint.
+	 * Before we remove the probepoint, give the breakpointed thread a
+	 * chance to find the probepoint.
+	 */
+	mutex_unlock(&uproc->mutex);
+	synchronize_sched();
+	mutex_lock(&uproc->mutex);
+	put_probept(ppt);
+
+done:
+	mutex_unlock(&uproc->mutex);
+	put_uprocess(uproc);
+	put_pid(p);
+}
+EXPORT_SYMBOL_GPL(unregister_uprobe);
+
+/* Prepare to single-step ppt's probed instruction out of line. */
+static int pre_ssout(struct uprobe_task *utask, struct uprobe_probept *ppt,
+						struct pt_regs *regs)
+{
+	struct uprobe_process *uproc = utask->uproc;
+
+	if (unlikely(!ppt->user_bkpt.xol_vaddr)) {
+#ifdef CONFIG_X86_32
+		/*
+		 * On x86_32, do_notify_resume() gets called with
+		 * interrupts disabled. Hence enable interrupts if they
+		 * are still disabled.
+		 */
+		uprobe_enable_interrupts();
+#endif
+		mutex_lock(&uproc->mutex);
+		if (unlikely(!uproc->xol_area))
+			uproc->xol_area = xol_alloc_area();
+		if (uproc->xol_area && !ppt->user_bkpt.xol_vaddr)
+			ppt->user_bkpt.xol_vaddr =
+					xol_get_insn_slot(&ppt->user_bkpt,
+							uproc->xol_area);
+		mutex_unlock(&uproc->mutex);
+#ifdef CONFIG_X86_32
+		uprobe_disable_interrupts();
+#endif
+		if (unlikely(!ppt->user_bkpt.xol_vaddr))
+			goto fail;
+	}
+	user_bkpt_pre_sstep(current, &ppt->user_bkpt,
+						&utask->arch_info, regs);
+	user_bkpt_set_ip(regs, ppt->user_bkpt.xol_vaddr);
+	return 0;
+
+/*
+ * We failed to execute out of line.
+ * reset the instruction pointer and remove the breakpoint.
+ */
+fail:
+	remove_bkpt(ppt, current);
+	user_bkpt_set_ip(regs, ppt->user_bkpt.vaddr);
+	put_probept(ppt);
+	return -1;
+}
+
+/* Prepare to continue execution after single-stepping out of line. */
+static int post_ssout(struct uprobe_probept *ppt, struct pt_regs *regs)
+{
+	return user_bkpt_post_sstep(current, &ppt->user_bkpt,
+					&current->utask->arch_info, regs);
+}
+
+/*
+ * Verify from Instruction Pointer if singlestep has indeed occurred.
+ * If Singlestep has occurred, then do post singlestep fix-ups.
+ */
+static bool sstep_complete(struct pt_regs *regs,
+				struct uprobe_probept *ppt)
+{
+	unsigned long vaddr = instruction_pointer(regs);
+
+	/*
+	 * If we have executed out of line, Instruction pointer
+	 * cannot be same as virtual address of XOL slot.
+	 */
+	if (vaddr == ppt->user_bkpt.xol_vaddr)
+		return false;
+	post_ssout(ppt, regs);
+	return true;
+}
+
+/*
+ * Clone callback: The current task has spawned a thread/process.
+ * NOTE: For now, we don't pass on uprobes from the parent to the
+ * child. We now do the necessary clearing of breakpoints in the
+ * child's address space.
+ *
+ * TODO:
+ *	- Provide option for child to inherit uprobes.
+ */
+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);
+		/* 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);
+		mutex_unlock(&uprobe_mutex);
+	} 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;
+		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);
+			if (ret && ret != -EINVAL) {
+				/* Ratelimit this? */
+				printk(KERN_ERR "Pid %d forked %d; failed to"
+					" remove probepoint at %#lx in child\n",
+					current->pid, child->pid,
+					ppt->user_bkpt.vaddr);
+			}
+		}
+		mutex_unlock(&uproc->mutex);
+	}
+}
+
+
+/*
+ * uprobe_notify_resume gets called in task context just before returning
+ * to userspace.  it gets called if.
+ *  - handler has to be run in task context.
+ *  - 1st time the probepoint is hit.
+ *
+ *  If its the first time the probepoint is hit, we allocate the slot here.
+ */
+void uprobe_notify_resume(struct pt_regs *regs)
+{
+	struct uprobe_probept *ppt;
+	struct uprobe_task *utask;
+	struct uprobe *u;
+
+	utask = current->utask;
+	if (!utask)
+		return;
+
+	ppt = utask->active_ppt;
+	if (!ppt)
+		return;
+
+	if (utask->state == UTASK_BP_HIT) {
+		u = ppt->uprobe;
+		if (!u->handler_in_interrupt && u->handler)
+			u->handler(u, regs);
+		if (!pre_ssout(utask, ppt, regs))
+			arch_uprobe_enable_sstep(regs);
+	} else if (utask->state == UTASK_SSTEP) {
+		if (sstep_complete(regs, ppt)) {
+			arch_uprobe_disable_sstep(regs);
+			put_probept(ppt);
+			utask->active_ppt = NULL;
+			utask->state = UTASK_RUNNING;
+		}
+	}
+}
+
+/*
+ * uprobe_bkpt_notifier gets called from interrupt context
+ * it sets TIF_NOTIFY_RESUME flag, if
+ *	- handler has to be run in task context
+ *	- slot for the probepoint is not yet allocated.
+ */
+int uprobe_bkpt_notifier(struct pt_regs *regs)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	struct uprobe_task *utask;
+	struct uprobe *u;
+	unsigned long probept;
+
+	utask = current->utask;
+	if (!utask || !utask->uproc)
+		/* task is currently not uprobed */
+		return 0;
+
+	uproc = utask->uproc;
+	probept = user_bkpt_get_bkpt_addr(regs);
+	spin_lock(&uproc->pptlist_lock);
+	ppt = find_probept(uproc, probept);
+	if (!ppt) {
+		spin_unlock(&uproc->pptlist_lock);
+		return 0;
+	}
+	get_probept(ppt);
+	spin_unlock(&uproc->pptlist_lock);
+	utask->active_ppt = ppt;
+	u = ppt->uprobe;
+
+	if (u->handler_in_interrupt && u->handler)
+		u->handler(u, regs);
+
+	utask->state = UTASK_BP_HIT;
+	if (ppt->user_bkpt.xol_vaddr && u->handler_in_interrupt) {
+		user_bkpt_pre_sstep(current, &ppt->user_bkpt,
+						&utask->arch_info, regs);
+		arch_uprobe_enable_sstep(regs);
+	} else
+		set_thread_flag(TIF_NOTIFY_RESUME);
+	return 1;
+}
+
+/*
+ * uprobe_post_notifier gets called in interrupt context.
+ * It completes the single step operation.
+ */
+int uprobe_post_notifier(struct pt_regs *regs)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	struct uprobe_task *utask;
+
+	utask = current->utask;
+	ppt = utask->active_ppt;
+	uproc = utask->uproc;
+
+	if (!utask || !utask->uproc)
+		/* task is currently not uprobed */
+		return 0;
+
+	if (!ppt)
+		return 0;
+
+	utask->state = UTASK_SSTEP;
+	if (user_bkpt_resume_can_sleep(&ppt->user_bkpt)) {
+		set_thread_flag(TIF_NOTIFY_RESUME);
+		return 1;
+	}
+
+	if (sstep_complete(regs, ppt)) {
+		arch_uprobe_disable_sstep(regs);
+		put_probept(ppt);
+		utask->active_ppt = NULL;
+		utask->state = UTASK_RUNNING;
+		return 1;
+	}
+	return 0;
+}
+
+
+static int __init init_uprobes(void)
+{
+	int ret;
+
+	user_bkpt_strategies = USER_BKPT_HNT_TSKINFO;
+	ret = user_bkpt_init(&user_bkpt_strategies);
+	if (ret != 0) {
+		printk(KERN_ERR "Can't start uprobes: user_bkpt_init() returned %d\n",
+								ret);
+		return ret;
+	}
+
+	register_die_notifier(&uprobes_exception_nb);
+	return 0;
+}
+
+static void __exit exit_uprobes(void)
+{
+}
+
+module_init(init_uprobes);
+module_exit(exit_uprobes);

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 8/11] X86 details for uprobes.
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (6 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 7/11] Uprobes Implementation Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-03-31 15:52 ` [PATCH v2 9/11] Uprobes Documentation patch Srikar Dronamraju
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

X86 support for Uprobes

This patch provides x86 specific details for uprobes.
This includes interrupt notifier for uprobes, enabling/disabling
singlestep.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
---

 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


diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 851cedc..a860a9b 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -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
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 98c74b4..bfa48f0 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -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
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
new file mode 100644
index 0000000..1acce22
--- /dev/null
+++ b/arch/x86/kernel/uprobes.c
@@ -0,0 +1,87 @@
+/*
+ *  Userspace Probes (UProbes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ * Authors:
+ *	Srikar Dronamraju
+ *	Ananth N Mavinakayanahalli
+ */
+
+#include <linux/sched.h>
+#include <linux/kdebug.h>
+#include <linux/uprobes.h>
+
+/*
+ * Wrapper routine for handling exceptions.
+ */
+int uprobes_exception_notify(struct notifier_block *self,
+				       unsigned long val, void *data)
+{
+	struct die_args *args = data;
+	struct pt_regs *regs = args->regs;
+	int ret = NOTIFY_DONE;
+
+	/* We are only interested in userspace traps */
+	if (regs && !user_mode_vm(regs))
+		return NOTIFY_DONE;
+
+	switch (val) {
+	case DIE_INT3:
+		/* Run your handler here */
+		if (uprobe_bkpt_notifier(regs))
+			ret = NOTIFY_STOP;
+		break;
+	case DIE_DEBUG:
+		if (uprobe_post_notifier(regs))
+			ret = NOTIFY_STOP;
+	default:
+		break;
+	}
+	return ret;
+}
+
+void arch_uprobe_enable_sstep(struct pt_regs *regs)
+{
+	/*
+	 * Enable single-stepping by
+	 * - Set TF on stack
+	 * - Set TIF_SINGLESTEP: Guarantees that TF is set when
+	 *	returning to user mode.
+	 *  - Indicate that TF is set by us.
+	 */
+	regs->flags |= X86_EFLAGS_TF;
+	set_thread_flag(TIF_SINGLESTEP);
+	set_thread_flag(TIF_FORCED_TF);
+}
+
+void arch_uprobe_disable_sstep(struct pt_regs *regs)
+{
+		/* Disable single-stepping by clearing what we set */
+		clear_thread_flag(TIF_SINGLESTEP);
+		clear_thread_flag(TIF_FORCED_TF);
+		regs->flags &= ~X86_EFLAGS_TF;
+}
+
+void uprobe_enable_interrupts(void)
+{
+	native_irq_enable();
+}
+
+void uprobe_disable_interrupts(void)
+{
+	native_irq_disable();
+}

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 9/11] Uprobes Documentation patch
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (7 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 8/11] X86 details for uprobes Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-03-31 15:52 ` [PATCH v2 10/11] Uprobes samples Srikar Dronamraju
  2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Uprobes documentation.

Changelog:
	Addressed comments from Randy Dunlap.

Signed-off-by: Jim Keniston <jkenisto@us.ibm.com>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 Documentation/uprobes.txt |  244 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 244 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/uprobes.txt


diff --git a/Documentation/uprobes.txt b/Documentation/uprobes.txt
new file mode 100644
index 0000000..d68dcdb
--- /dev/null
+++ b/Documentation/uprobes.txt
@@ -0,0 +1,244 @@
+Title	: User-Space Probes (Uprobes)
+Authors	: Jim Keniston <jkenisto@us.ibm.com>
+	: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
+
+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 - jkenisto@us.ibm.com
+Srikar Dronamraju - srikar@linux.vnet.ibm.com
+Ananth Mavinakayanahalli - ananth@in.ibm.com
+Prasanna Panchamukhi - prasanna@in.ibm.com
+Dave Wilder - dwilder@us.ibm.com
+
+9. Uprobes Example
+
+samples/uprobes/uprobe_example.c

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 10/11] Uprobes samples.
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (8 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 9/11] Uprobes Documentation patch Srikar Dronamraju
@ 2010-03-31 15:52 ` Srikar Dronamraju
  2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
  10 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:52 UTC (permalink / raw)
  To: Peter Zijlstra, Andrew Morton, Ingo Molnar, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Uprobes 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. ]

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 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


diff --git a/samples/Kconfig b/samples/Kconfig
index 8924f72..50b8b1c 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -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
diff --git a/samples/uprobes/Makefile b/samples/uprobes/Makefile
new file mode 100644
index 0000000..f535f6f
--- /dev/null
+++ b/samples/uprobes/Makefile
@@ -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
diff --git a/samples/uprobes/uprobe_example.c b/samples/uprobes/uprobe_example.c
new file mode 100644
index 0000000..f625bae
--- /dev/null
+++ b/samples/uprobes/uprobe_example.c
@@ -0,0 +1,83 @@
+/*
+ * Uprobes Example
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, 2008-2010
+ * Authors:
+ *	Srikar Dronamraju
+ *	Jim Keniston
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/uprobes.h>
+
+/*
+ * Usage: insmod uprobe_example.ko pid=<pid> vaddr=<address> [verbose=0]
+ * where <pid> identifies the probed process and <address> is the virtual
+ * address of the probed instruction.
+ */
+
+static int pid;
+module_param(pid, int, 0);
+MODULE_PARM_DESC(pid, "pid");
+
+static int verbose;
+module_param(verbose, int, 0);
+MODULE_PARM_DESC(verbose, "verbose");
+
+static long vaddr;
+module_param(vaddr, long, 0);
+MODULE_PARM_DESC(vaddr, "vaddr");
+
+static int nhits;
+static struct uprobe usp;
+
+static void uprobe_handler(struct uprobe *u, struct pt_regs *regs)
+{
+	nhits++;
+	if (verbose)
+		printk(KERN_INFO "Hit #%d on probepoint at %#lx\n",
+			nhits, u->vaddr);
+}
+
+int __init init_module(void)
+{
+	int ret;
+	usp.pid = pid;
+	usp.vaddr = vaddr;
+	usp.handler = uprobe_handler;
+	printk(KERN_INFO "Registering uprobe on pid %d, vaddr %#lx\n",
+		usp.pid, usp.vaddr);
+	ret = register_uprobe(&usp);
+	if (ret != 0) {
+		printk(KERN_ERR "register_uprobe() failed, returned %d\n", ret);
+		printk(KERN_ERR "Usage: insmod uprobe_example.ko pid=<pid> "
+						"vaddr=<address>\n");
+		return ret;
+	}
+	return 0;
+}
+
+void __exit cleanup_module(void)
+{
+	printk(KERN_INFO "Unregistering uprobe on pid %d, vaddr %#lx\n",
+		usp.pid, usp.vaddr);
+	printk(KERN_INFO "Probepoint was hit %d times\n", nhits);
+	unregister_uprobe(&usp);
+}
+MODULE_LICENSE("GPL");

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* [PATCH v2 11/11] Uprobes traceevents patch.
  2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
                   ` (9 preceding siblings ...)
  2010-03-31 15:52 ` [PATCH v2 10/11] Uprobes samples Srikar Dronamraju
@ 2010-03-31 15:53 ` Srikar Dronamraju
  2010-03-31 21:24   ` Steven Rostedt
                     ` (2 more replies)
  10 siblings, 3 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-03-31 15:53 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds
  Cc: Masami Hiramatsu, Srikar Dronamraju, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML

Uprobes 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=1

TODO: Documentation/trace/uprobetrace.txt

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
---

 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


diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 13e13d4..1435e09 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -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
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 78edc64..8911959 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -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
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 2825ef2..9fe02ab 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -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:
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
new file mode 100644
index 0000000..3e146ef
--- /dev/null
+++ b/kernel/trace/trace_uprobe.c
@@ -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)
+
+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);
+	/* Set argument names as fields */
+	for (i = 0; i < tp->nr_args; i++)
+		DEFINE_FIELD(unsigned long, args[i], tp->args[i].name, 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);
+	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;
+}
+
+#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);

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
@ 2010-03-31 21:24   ` Steven Rostedt
  2010-04-01  4:16     ` Masami Hiramatsu
  2010-05-12 11:02   ` Frederic Weisbecker
  2010-05-12 15:15   ` Frederic Weisbecker
  2 siblings, 1 reply; 46+ messages in thread
From: Steven Rostedt @ 2010-03-31 21:24 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML

On Wed, 2010-03-31 at 21:23 +0530, Srikar Dronamraju wrote:

>  libftrace-y := ftrace.o
> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> index 2825ef2..9fe02ab 100644
> --- a/kernel/trace/trace.h
> +++ b/kernel/trace/trace.h
> @@ -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.

> +	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.

> +
> +#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:
> diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
> new file mode 100644
> index 0000000..3e146ef
> --- /dev/null
> +++ b/kernel/trace/trace_uprobe.c
> @@ -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)
> +
> +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


> +	/* 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.

> ;
> +	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);
> +	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

> +
> +#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 majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-03-31 21:24   ` Steven Rostedt
@ 2010-04-01  4:16     ` Masami Hiramatsu
  2010-05-12 14:57       ` Frederic Weisbecker
  0 siblings, 1 reply; 46+ messages in thread
From: Masami Hiramatsu @ 2010-04-01  4:16 UTC (permalink / raw)
  To: rostedt
  Cc: Srikar Dronamraju, Peter Zijlstra, Ingo Molnar, Andrew Morton,
	Linus Torvalds, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML

Hi Steven,

Steven Rostedt wrote:
> On Wed, 2010-03-31 at 21:23 +0530, Srikar Dronamraju wrote:
> 
>>  libftrace-y := ftrace.o
>> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
>> index 2825ef2..9fe02ab 100644
>> --- a/kernel/trace/trace.h
>> +++ b/kernel/trace/trace.h
>> @@ -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.

Indeed.

>> +	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.

Hmm, interesting idea. But this dynamic event definition allows us
to filter events based on each argument value.

As you can see this code, 

>> +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,

-- 
Masami Hiramatsu
e-mail: mhiramat@redhat.com

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-03-31 15:52 ` [PATCH v2 7/11] Uprobes Implementation Srikar Dronamraju
@ 2010-04-13 18:35   ` Oleg Nesterov
  2010-04-15  9:35     ` Srikar Dronamraju
  0 siblings, 1 reply; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-13 18:35 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

On 03/31, Srikar Dronamraju wrote:
>
> --- /dev/null
> +++ b/kernel/uprobes.c
> ...
> +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;
> +	}
> +
> +	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().

> +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) {
> +			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().

> +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;
> +	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() ?

> +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 ?

> +dont_add:
> +			t = next_thread(t);
> +		} while (t != start);

again, this doesn't look right. Btw, I'd suggest to use while_each_thread().

> +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);
> +		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.

> +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?

> +	if (pid) {
> +		struct task_struct *t = pid_task(pid, PIDTYPE_PID);
> +
> +		if (!t || unlikely(t->flags & PF_EXITING))

Why do we check PF_EXITING?

> +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);
> +			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?

> +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);
> +		/* 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.

> +	} 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;
> +		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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-13 18:35   ` Oleg Nesterov
@ 2010-04-15  9:35     ` Srikar Dronamraju
  2010-04-19 19:31       ` Oleg Nesterov
  0 siblings, 1 reply; 46+ messages in thread
From: Srikar Dronamraju @ 2010-04-15  9:35 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath


Oleg, Thanks for the review.

> > ...
> > +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;
> > +	}
> > +
> > +	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().
Okay.

> 
> > +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) {
> > +			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().

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().

> 
> > +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;
> > +	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() ?

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.
> 
> > +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 ?

yeah, PF_EXITING doesnt seem to help us here. But will this be an issue once we
move uprobe_free_utask() after exit_signals.

> 
> > +dont_add:
> > +			t = next_thread(t);
> > +		} while (t != start);
> 
> again, this doesn't look right. Btw, I'd suggest to use while_each_thread().
> 
> > +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);
> > +		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.

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 either
tracehook_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.


> 
> > +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?
> 
> > +	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?

> 
> > +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);
> > +			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?

Okay.. Agree, will do changes accordingly.

> 
> > +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);
> > +		/* 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);

Okay, I agree that we can remove ctask here and in the else branch.

> 
> The same for "else" branch.
> 
> > +	} 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;
> > +		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.

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
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-15  9:35     ` Srikar Dronamraju
@ 2010-04-19 19:31       ` Oleg Nesterov
  2010-04-20 12:43         ` Srikar Dronamraju
  0 siblings, 1 reply; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-19 19:31 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

Srikar, sorry for delay...

On 04/15, Srikar Dronamraju wrote:
>
> > > +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) {
> > > +			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().
>
> 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().

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().

> > > +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;
> > > +	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() ?
>
> Okay.
> Would it work if we
>
> [... snip ...]

I think yes. But see below.

> > > +	 * 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);
> > > +		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.
>
> 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 either
> tracehook_report_clone or the find_next_thread whichever 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().

> > > +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 ;)

> 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().


> > > +	} 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;
> > > +		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.
>
> 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.

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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-19 19:31       ` Oleg Nesterov
@ 2010-04-20 12:43         ` Srikar Dronamraju
  2010-04-20 15:30           ` Oleg Nesterov
  0 siblings, 1 reply; 46+ messages in thread
From: Srikar Dronamraju @ 2010-04-20 12:43 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

> > > > +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) {
> > > > +			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().
> >
> > 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().
> 
> 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().

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 ..]
> > >
> > > 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.
> >
> > 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 either
> > tracehook_report_clone or the find_next_thread whichever 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().

Okay. 

> 
> > > > +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 ;)

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.

> 
> > 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 .

> 
> 
> 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 ?

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()?


> 
> 	- 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.

> 
> 	- 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().

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.

> 
> 	- 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 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
	
> 
> 	- 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

=======================================================================

> 
> 	- 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().
> 

	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 ... ]

> > > 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)
> > 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.
> 
> 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 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.

> I think uprobe_process should 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.

> 
> 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.

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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-20 12:43         ` Srikar Dronamraju
@ 2010-04-20 15:30           ` Oleg Nesterov
  2010-04-21  6:59             ` Srikar Dronamraju
  2010-05-11 20:32             ` Peter Zijlstra
  0 siblings, 2 replies; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-20 15:30 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

On 04/20, Srikar Dronamraju wrote:
>
> > > > > +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_process before we take rcu_read_lock().
>
> Okay, cleanup_process() gets called only and only if add_utask() fails
> to allocated utask struct.

Yes, but afaics we have the same issues in find_next_thread() called
by create_uprocess().

> 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?

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.

> > > > > +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 ;)
>
> Can you please let me know when nsproxy is set to NULL?

exit_notify()->exit_task_namespaces()

> 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.

> > 	- uprobe_process->tg_leader is 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.

Yes, but

> 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()?

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?

> > 	- 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().
>
> 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().

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...

> > 	- 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 *" ?

> > 	- 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

> 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.

> > 	- 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().
> >
>
> 	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().

OK, thanks. Perhaps a small comment to explain this makes sense...

> > 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.
>
> 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.

Yes, I was thinking about mm->struct->uproc too.

And, assuming we have this pointer in mm_struct:

> > 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.
>
> 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.

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.

> 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.

Yes,

> 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.

we could use GFP_ATOMIC, but I agree, this is not nice.

> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-20 15:30           ` Oleg Nesterov
@ 2010-04-21  6:59             ` Srikar Dronamraju
  2010-04-21 16:05               ` Oleg Nesterov
  2010-05-11 20:32             ` Peter Zijlstra
  1 sibling, 1 reply; 46+ messages in thread
From: Srikar Dronamraju @ 2010-04-21  6:59 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

* Oleg Nesterov <oleg@redhat.com> [2010-04-20 17:30:23]:

> On 04/20, Srikar Dronamraju wrote:
> >
> > > > > > +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_process before we take rcu_read_lock().
> >
> > Okay, cleanup_process() gets called only and only if add_utask() fails
> > to allocated utask struct.
> 
> Yes, but afaics we have the same issues in find_next_thread() called
> by create_uprocess().

Okay.

> 
> > 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?
> 
> 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.

Okay. I will use mm_struct->uproc, dynamic allocation of utask on probe
hit and freeing of utask on __put_task_struct.

> 
> > > > > > +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 ;)
> >
> > Can you please let me know when nsproxy is set to NULL?
> 
> exit_notify()->exit_task_namespaces()
> 
> > 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.

> 
> > > 	- uprobe_process->tg_leader is 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.
> 
> Yes, but
> 
> > 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()?
> 
> 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?

static int free_uprocess(struct uprobe_process *uproc)
{
	....
	put_pid(uproc->tg_leader);
        uproc->tg_leader = NULL;
	
}

> 
> > > 	- 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().
> >
> > 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().
> 
> 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.

> 
> > > 	- 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.

Agreed 

> 
> 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.

Okay we can remove the !CONFIG_USER_BKPT_XOL code in user_bkpt_xol.h

> 
> 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 *" ?
> 

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.

> > > 	- 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
> 
> > 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. 

> 
> > > 	- 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().
> > >
> >
> > 	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().
> 
> OK, thanks. Perhaps a small comment to explain this makes sense...

Okay. 

> 
> > > 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.
> >
> > 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.
> 
> Yes, I was thinking about mm->struct->uproc too.
> 

Okay, I will try with mm_struct->uproc. 

> And, assuming we have this pointer in mm_struct:
> 
> > > 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.
> >
> > 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.
> 
> 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.
> 
> > 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.
> 
> Yes,
> 
> > 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.
> 
> we could use GFP_ATOMIC, but I agree, this is not nice.
> 
> > 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.

But do we need a new TIF bit? Can we just reuse the TIF_NOTIFY_RESUME
flag that we use now?

> 
> 
> 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


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-21  6:59             ` Srikar Dronamraju
@ 2010-04-21 16:05               ` Oleg Nesterov
  2010-04-22 13:31                 ` Srikar Dronamraju
  0 siblings, 1 reply; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-21 16:05 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

On 04/21, Srikar Dronamraju wrote:
>
> * Oleg Nesterov <oleg@redhat.com> [2010-04-20 17:30:23]:
>
> > 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?
>
> 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?

> > 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,
                                         ^^^^^^^^^^^^^^^^^^^^^
I meant uprobe_process->xol_area

> 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.

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.

> > > 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.
>
> But do we need a new TIF bit? Can we just reuse the TIF_NOTIFY_RESUME
> flag 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-21 16:05               ` Oleg Nesterov
@ 2010-04-22 13:31                 ` Srikar Dronamraju
  2010-04-22 15:40                   ` Oleg Nesterov
  2010-05-11 20:43                   ` Peter Zijlstra
  0 siblings, 2 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-04-22 13:31 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli

* Oleg Nesterov <oleg@redhat.com> [2010-04-21 18:05:15]:

> On 04/21, Srikar Dronamraju wrote:
> >
> > * Oleg Nesterov <oleg@redhat.com> [2010-04-20 17:30:23]:
> >
> > > 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?
> >
> > 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?

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.

> 
> > > 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,
>                                          ^^^^^^^^^^^^^^^^^^^^^
> I meant uprobe_process->xol_area
> 
> > 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.
> 
> 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.

> 
> > > > 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.
> >
> > But do we need a new TIF bit? Can we just reuse the TIF_NOTIFY_RESUME
> > flag 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.
> 

Yeah, since tracehook_notify_resume() is in fast path, its worth adding
a new TIF flag.

> 
> 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. 

> 
>    Say, pre_ssout() calls remove_bkpt() lockless. can't it race
>    with register_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.

> 
>    And, without uses_xol_strategy() there are more racy callers
>    of write_opcode()... Probably something else.

Okay. 

> 
> 2. Can't write_opcode() conflict with ksm doing replace_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 

> 
> 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?
> 

I still need to verify this. I shall get back to you on this.
However are there applications that mprotect(PROT_WRITE) text pages?


> 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.
> 

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
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-22 13:31                 ` Srikar Dronamraju
@ 2010-04-22 15:40                   ` Oleg Nesterov
  2010-04-23 14:58                     ` Srikar Dronamraju
                                       ` (2 more replies)
  2010-05-11 20:43                   ` Peter Zijlstra
  1 sibling, 3 replies; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-22 15:40 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On 04/22, Srikar Dronamraju wrote:
>
> * Oleg Nesterov <oleg@redhat.com> [2010-04-21 18:05:15]:
>
> > 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?
> >
>
> 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.

> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-22 15:40                   ` Oleg Nesterov
@ 2010-04-23 14:58                     ` Srikar Dronamraju
  2010-04-23 18:53                       ` Oleg Nesterov
  2010-05-11 20:47                       ` Peter Zijlstra
  2010-05-11 20:44                     ` Peter Zijlstra
  2010-05-11 20:45                     ` Peter Zijlstra
  2 siblings, 2 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-04-23 14:58 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

* Oleg Nesterov <oleg@redhat.com> [2010-04-22 17:40:59]:

> On 04/22, Srikar Dronamraju wrote:
> >
> > * Oleg Nesterov <oleg@redhat.com> [2010-04-21 18:05:15]:
> >
> > > 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?
> > >
> >
> > 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().

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.

> 
> 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.
> 
> > 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):
		   ^^^ newpage

> 
> 		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.

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.

> 
> 
> 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.

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.

> 
> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-23 14:58                     ` Srikar Dronamraju
@ 2010-04-23 18:53                       ` Oleg Nesterov
  2010-05-11 20:47                       ` Peter Zijlstra
  1 sibling, 0 replies; 46+ messages in thread
From: Oleg Nesterov @ 2010-04-23 18:53 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On 04/23, Srikar Dronamraju wrote:
>
> * Oleg Nesterov <oleg@redhat.com> [2010-04-22 17:40:59]:
>
> > On 04/22, Srikar Dronamraju wrote:
> > >
> > > 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().
>
> 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().

> 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?

Yes, that is why I mentioned register_uprobe() should check SHARED/MAYWRITE.

> 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.

> > 	@@ -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):
> 		   ^^^ newpage

Yes,

> > 		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.
>
> I did verify that page_add_file_rmap gets called from replace_page when
> 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.

> I would leave it for vm experts to decide what the right thing to do.

Sure.

> > 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.
>
> 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.

OK, I missed this, thanks.

> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-20 15:30           ` Oleg Nesterov
  2010-04-21  6:59             ` Srikar Dronamraju
@ 2010-05-11 20:32             ` Peter Zijlstra
  2010-05-11 20:57               ` Frank Ch. Eigler
  1 sibling, 1 reply; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 20:32 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Srikar Dronamraju, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath

On Tue, 2010-04-20 at 17:30 +0200, Oleg Nesterov wrote:
> > >     - 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
> 
> > 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.




^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-22 13:31                 ` Srikar Dronamraju
  2010-04-22 15:40                   ` Oleg Nesterov
@ 2010-05-11 20:43                   ` Peter Zijlstra
  2010-05-12 10:41                     ` Srikar Dronamraju
  1 sibling, 1 reply; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 20:43 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli

On Thu, 2010-04-22 at 19:01 +0530, Srikar Dronamraju wrote:
> * Oleg Nesterov <oleg@redhat.com> [2010-04-21 18:05:15]:

> > 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.
> > 
> 
> 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.

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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-22 15:40                   ` Oleg Nesterov
  2010-04-23 14:58                     ` Srikar Dronamraju
@ 2010-05-11 20:44                     ` Peter Zijlstra
  2010-05-11 20:45                     ` Peter Zijlstra
  2 siblings, 0 replies; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 20:44 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Srikar Dronamraju, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On Thu, 2010-04-22 at 17:40 +0200, Oleg Nesterov wrote:
> 
> Hmm. And if this vma is VM_SHARED, then this bp could be actually
> written to vm_file after mprotect(). 

I don't think we should allow breakpoints on VM_SHARED maps.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-22 15:40                   ` Oleg Nesterov
  2010-04-23 14:58                     ` Srikar Dronamraju
  2010-05-11 20:44                     ` Peter Zijlstra
@ 2010-05-11 20:45                     ` Peter Zijlstra
  2010-05-12 10:31                       ` Srikar Dronamraju
  2010-05-13 19:40                       ` Oleg Nesterov
  2 siblings, 2 replies; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 20:45 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Srikar Dronamraju, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On Thu, 2010-04-22 at 17:40 +0200, Oleg Nesterov wrote:
> 
> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-04-23 14:58                     ` Srikar Dronamraju
  2010-04-23 18:53                       ` Oleg Nesterov
@ 2010-05-11 20:47                       ` Peter Zijlstra
  1 sibling, 0 replies; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 20:47 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On Fri, 2010-04-23 at 20:28 +0530, Srikar Dronamraju wrote:
> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-11 20:32             ` Peter Zijlstra
@ 2010-05-11 20:57               ` Frank Ch. Eigler
  2010-05-11 21:01                 ` Peter Zijlstra
  0 siblings, 1 reply; 46+ messages in thread
From: Frank Ch. Eigler @ 2010-05-11 20:57 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Oleg Nesterov, Srikar Dronamraju, Ingo Molnar, Andrew Morton,
	Linus Torvalds, Masami Hiramatsu, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	LKML, Roland McGrath

Hi -

On Tue, May 11, 2010 at 10:32:18PM +0200, Peter Zijlstra wrote:
> [...]
> 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.

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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-11 20:57               ` Frank Ch. Eigler
@ 2010-05-11 21:01                 ` Peter Zijlstra
  0 siblings, 0 replies; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-11 21:01 UTC (permalink / raw)
  To: Frank Ch. Eigler
  Cc: Oleg Nesterov, Srikar Dronamraju, Ingo Molnar, Andrew Morton,
	Linus Torvalds, Masami Hiramatsu, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	LKML, Roland McGrath

On Tue, 2010-05-11 at 16:57 -0400, Frank Ch. Eigler wrote:
> Hi -
> 
> On Tue, May 11, 2010 at 10:32:18PM +0200, Peter Zijlstra wrote:
> > [...]
> > 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.
> 
> 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-11 20:45                     ` Peter Zijlstra
@ 2010-05-12 10:31                       ` Srikar Dronamraju
  2010-05-13 19:40                       ` Oleg Nesterov
  1 sibling, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-05-12 10:31 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

* Peter Zijlstra <peterz@infradead.org> [2010-05-11 22:45:23]:

> On Thu, 2010-04-22 at 17:40 +0200, Oleg Nesterov wrote:
> > 
> > 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.
> 

Okay, I will put a check to disallow probes in VM_SHARED vmas.

--
Thanks and Regards
Srikar

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-11 20:43                   ` Peter Zijlstra
@ 2010-05-12 10:41                     ` Srikar Dronamraju
  2010-05-12 11:12                       ` Peter Zijlstra
  0 siblings, 1 reply; 46+ messages in thread
From: Srikar Dronamraju @ 2010-05-12 10:41 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli

* Peter Zijlstra <peterz@infradead.org> [2010-05-11 22:43:23]:

> On Thu, 2010-04-22 at 19:01 +0530, Srikar Dronamraju wrote:
> > * Oleg Nesterov <oleg@redhat.com> [2010-04-21 18:05:15]:
> 
> > > 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.
> > > 
> > 
> > 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.
> 
> 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?

> 
> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
  2010-03-31 21:24   ` Steven Rostedt
@ 2010-05-12 11:02   ` Frederic Weisbecker
  2010-05-12 14:34     ` Srikar Dronamraju
  2010-05-12 15:15   ` Frederic Weisbecker
  2 siblings, 1 reply; 46+ messages in thread
From: Frederic Weisbecker @ 2010-05-12 11:02 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frank Ch. Eigler, LKML

On Wed, Mar 31, 2010 at 09:23:11PM +0530, Srikar Dronamraju wrote:
> Uprobes 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=1
> 
> TODO: Documentation/trace/uprobetrace.txt
> 
> Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
> ---
> 
>  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
> 
> 
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 13e13d4..1435e09 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -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.



That doesn't explain much what it does. Please explain its goal of
creating a trace event on top of a userspace dynamic probe.



> +#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.




> +const char *ureserved_field_names[] = {
> +	"common_type",
> +	"common_flags",
> +	"common_preempt_count",
> +	"common_pid",
> +	"common_tgid",
> +	"common_lock_depth",


Same here.



> +/* 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);


"Failed"


> +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);



Doesn't seem to be under uprobe_lock.



> +/* 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");



You can use trace_create_file I think.



> +
> +	/* 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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-12 10:41                     ` Srikar Dronamraju
@ 2010-05-12 11:12                       ` Peter Zijlstra
  2010-05-12 14:24                         ` Srikar Dronamraju
  0 siblings, 1 reply; 46+ messages in thread
From: Peter Zijlstra @ 2010-05-12 11:12 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli

On Wed, 2010-05-12 at 16:11 +0530, Srikar Dronamraju wrote:
> > 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? 

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?


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-12 11:12                       ` Peter Zijlstra
@ 2010-05-12 14:24                         ` Srikar Dronamraju
  0 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-05-12 14:24 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Oleg Nesterov, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli

* Peter Zijlstra <peterz@infradead.org> [2010-05-12 13:12:31]:

> On Wed, 2010-05-12 at 16:11 +0530, Srikar Dronamraju wrote:
> > > 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? 
> 
> 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 ]
---------------------------------------------------------------------
> > 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.     
---------------------------------------------------------------------

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.

> 
> 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
> 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-05-12 11:02   ` Frederic Weisbecker
@ 2010-05-12 14:34     ` Srikar Dronamraju
  0 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-05-12 14:34 UTC (permalink / raw)
  To: Frederic Weisbecker
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frank Ch. Eigler, LKML

> >  
> > +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. 

> 
> 
> > +#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.
> 

Okay. 

> > +	ret = register_uprobe_event(tp);
> > +	if (ret) {
> > +		pr_warning("Faild to register probe event(%d)\n", ret);
> 
> 
> "Failed"

Okay. 

> 
> 
> > +		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);
> 
> 
> 
> Doesn't seem to be under uprobe_lock.

Agree, the unregister_trace_uprobe() has to be called after locking 
uprobe_lock.

> 
> 
> 
> > +/* 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");
> 
> 
> 
> You can use trace_create_file I think.

Okay, will check that. 

> 
> 

-- 
Thanks and Regards
Srikar

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-04-01  4:16     ` Masami Hiramatsu
@ 2010-05-12 14:57       ` Frederic Weisbecker
  0 siblings, 0 replies; 46+ messages in thread
From: Frederic Weisbecker @ 2010-05-12 14:57 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: rostedt, Srikar Dronamraju, Peter Zijlstra, Ingo Molnar,
	Andrew Morton, Linus Torvalds, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frank Ch. Eigler, LKML

On Thu, Apr 01, 2010 at 12:16:56AM -0400, Masami Hiramatsu wrote:
> Hi Steven,
> 
> Steven Rostedt wrote:
> > On Wed, 2010-03-31 at 21:23 +0530, Srikar Dronamraju wrote:
> > 
> >>  libftrace-y := ftrace.o
> >> diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
> >> index 2825ef2..9fe02ab 100644
> >> --- a/kernel/trace/trace.h
> >> +++ b/kernel/trace/trace.h
> >> @@ -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.
> 
> Indeed.
> 
> >> +	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.
> 
> Hmm, interesting idea. But this dynamic event definition allows us
> to filter events based on each argument value.
> 
> As you can see this code, 
> 
> >> +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,


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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 11/11] Uprobes traceevents patch.
  2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
  2010-03-31 21:24   ` Steven Rostedt
  2010-05-12 11:02   ` Frederic Weisbecker
@ 2010-05-12 15:15   ` Frederic Weisbecker
  2 siblings, 0 replies; 46+ messages in thread
From: Frederic Weisbecker @ 2010-05-12 15:15 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frank Ch. Eigler, LKML

On Wed, Mar 31, 2010 at 09:23:11PM +0530, Srikar Dronamraju wrote:
> +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);



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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-11 20:45                     ` Peter Zijlstra
  2010-05-12 10:31                       ` Srikar Dronamraju
@ 2010-05-13 19:40                       ` Oleg Nesterov
  2010-05-13 19:59                         ` Linus Torvalds
  1 sibling, 1 reply; 46+ messages in thread
From: Oleg Nesterov @ 2010-05-13 19:40 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Srikar Dronamraju, Ingo Molnar, Andrew Morton, Linus Torvalds,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

On 05/11, Peter Zijlstra wrote:
>
> On Thu, 2010-04-22 at 17:40 +0200, Oleg Nesterov wrote:
> >
> > 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.

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.


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-13 19:40                       ` Oleg Nesterov
@ 2010-05-13 19:59                         ` Linus Torvalds
  2010-05-13 22:12                           ` Andi Kleen
                                             ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Linus Torvalds @ 2010-05-13 19:59 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Peter Zijlstra, Srikar Dronamraju, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel



On Thu, 13 May 2010, Oleg Nesterov wrote:
> 
> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-13 19:59                         ` Linus Torvalds
@ 2010-05-13 22:12                           ` Andi Kleen
  2010-05-13 22:25                             ` Linus Torvalds
  2010-05-14  0:56                           ` Roland McGrath
  2010-05-14  5:42                           ` Srikar Dronamraju
  2 siblings, 1 reply; 46+ messages in thread
From: Andi Kleen @ 2010-05-13 22:12 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Oleg Nesterov, Peter Zijlstra, Srikar Dronamraju, Ingo Molnar,
	Andrew Morton, Masami Hiramatsu, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML, Roland McGrath, Mel Gorman,
	Paul E. McKenney, Andrea Arcangeli, Hugh Dickins, Rik van Riel

Linus Torvalds <torvalds@linux-foundation.org> writes:

> 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
-- 
ak@linux.intel.com -- Speaking for myself only.

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-13 22:12                           ` Andi Kleen
@ 2010-05-13 22:25                             ` Linus Torvalds
  0 siblings, 0 replies; 46+ messages in thread
From: Linus Torvalds @ 2010-05-13 22:25 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Oleg Nesterov, Peter Zijlstra, Srikar Dronamraju, Ingo Molnar,
	Andrew Morton, Masami Hiramatsu, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML, Roland McGrath, Mel Gorman,
	Paul E. McKenney, Andrea Arcangeli, Hugh Dickins, Rik van Riel



On Fri, 14 May 2010, Andi Kleen wrote:
> 
> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-13 19:59                         ` Linus Torvalds
  2010-05-13 22:12                           ` Andi Kleen
@ 2010-05-14  0:56                           ` Roland McGrath
  2010-05-14  5:42                           ` Srikar Dronamraju
  2 siblings, 0 replies; 46+ messages in thread
From: Roland McGrath @ 2010-05-14  0:56 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Oleg Nesterov, Peter Zijlstra, Srikar Dronamraju, Ingo Molnar,
	Andrew Morton, Masami Hiramatsu, Randy Dunlap,
	Ananth N Mavinakayanahalli, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, LKML, Mel Gorman, Paul E. McKenney,
	Andrea Arcangeli, Hugh Dickins, Rik van Riel

> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH v2 7/11] Uprobes Implementation
  2010-05-13 19:59                         ` Linus Torvalds
  2010-05-13 22:12                           ` Andi Kleen
  2010-05-14  0:56                           ` Roland McGrath
@ 2010-05-14  5:42                           ` Srikar Dronamraju
  2 siblings, 0 replies; 46+ messages in thread
From: Srikar Dronamraju @ 2010-05-14  5:42 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Oleg Nesterov, Peter Zijlstra, Ingo Molnar, Andrew Morton,
	Masami Hiramatsu, Randy Dunlap, Ananth N Mavinakayanahalli,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler, LKML,
	Roland McGrath, Mel Gorman, Paul E. McKenney, Andrea Arcangeli,
	Hugh Dickins, Rik van Riel

> 
> On Thu, 13 May 2010, Oleg Nesterov wrote:
> > 
> > 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.

> 
> 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?

> 
> 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

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2010-05-14  5:42 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-31 15:51 [PATCH v2 0/11] Uprobes patches Srikar Dronamraju
2010-03-31 15:51 ` [PATCH v2 1/11] Move Macro W to insn.h Srikar Dronamraju
2010-03-31 15:51 ` [PATCH v2 2/11] Move replace_page() to mm/memory.c Srikar Dronamraju
2010-03-31 15:51 ` [PATCH v2 3/11] Enhance replace_page() to support pagecache Srikar Dronamraju
2010-03-31 15:51 ` [PATCH v2 4/11] User Space Breakpoint Assistance Layer Srikar Dronamraju
2010-03-31 15:52 ` [PATCH v2 5/11] X86 details for user space breakpoint assistance Srikar Dronamraju
2010-03-31 15:52 ` [PATCH v2 6/11] Slot allocation for Execution out of line Srikar Dronamraju
2010-03-31 15:52 ` [PATCH v2 7/11] Uprobes Implementation Srikar Dronamraju
2010-04-13 18:35   ` Oleg Nesterov
2010-04-15  9:35     ` Srikar Dronamraju
2010-04-19 19:31       ` Oleg Nesterov
2010-04-20 12:43         ` Srikar Dronamraju
2010-04-20 15:30           ` Oleg Nesterov
2010-04-21  6:59             ` Srikar Dronamraju
2010-04-21 16:05               ` Oleg Nesterov
2010-04-22 13:31                 ` Srikar Dronamraju
2010-04-22 15:40                   ` Oleg Nesterov
2010-04-23 14:58                     ` Srikar Dronamraju
2010-04-23 18:53                       ` Oleg Nesterov
2010-05-11 20:47                       ` Peter Zijlstra
2010-05-11 20:44                     ` Peter Zijlstra
2010-05-11 20:45                     ` Peter Zijlstra
2010-05-12 10:31                       ` Srikar Dronamraju
2010-05-13 19:40                       ` Oleg Nesterov
2010-05-13 19:59                         ` Linus Torvalds
2010-05-13 22:12                           ` Andi Kleen
2010-05-13 22:25                             ` Linus Torvalds
2010-05-14  0:56                           ` Roland McGrath
2010-05-14  5:42                           ` Srikar Dronamraju
2010-05-11 20:43                   ` Peter Zijlstra
2010-05-12 10:41                     ` Srikar Dronamraju
2010-05-12 11:12                       ` Peter Zijlstra
2010-05-12 14:24                         ` Srikar Dronamraju
2010-05-11 20:32             ` Peter Zijlstra
2010-05-11 20:57               ` Frank Ch. Eigler
2010-05-11 21:01                 ` Peter Zijlstra
2010-03-31 15:52 ` [PATCH v2 8/11] X86 details for uprobes Srikar Dronamraju
2010-03-31 15:52 ` [PATCH v2 9/11] Uprobes Documentation patch Srikar Dronamraju
2010-03-31 15:52 ` [PATCH v2 10/11] Uprobes samples Srikar Dronamraju
2010-03-31 15:53 ` [PATCH v2 11/11] Uprobes traceevents patch Srikar Dronamraju
2010-03-31 21:24   ` Steven Rostedt
2010-04-01  4:16     ` Masami Hiramatsu
2010-05-12 14:57       ` Frederic Weisbecker
2010-05-12 11:02   ` Frederic Weisbecker
2010-05-12 14:34     ` Srikar Dronamraju
2010-05-12 15:15   ` Frederic Weisbecker

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.