All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv11 2.6.36-rc2-tip 0/15]  0: Uprobes Patches
@ 2010-08-25 13:41 Srikar Dronamraju
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 1/15] 1: mm: Move replace_page() / write_protect_page() to mm/memory.c Srikar Dronamraju
                   ` (15 more replies)
  0 siblings, 16 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:41 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Naren A Devaiah,
	Christoph Hellwig, Ananth N Mavinakayanahalli, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Andrew Morton, Paul E. McKenney


Changelog from V10:
 * Replaced function pointers in user_bkpt structure with weak
   functions as suggested by Peter Zijlstra.
 * CONFIG_PROBE_EVENTS now selects uprobe-tracer and kprobe-tracer
   as suggested by Frederic.
 * Split perf-probe listing patches into smaller patches.

Changelog from V9:
 * Resolved comments from Arnaldo on perf support for uprobes.
 * perf probe -S will now list only global binding functions as
   requested by Christoph Hellwig.
 * Moved Changelog to below Signed-off-by: line, so that its not part
   of the patch description. (Suggested by Christoph.)

Changelog from V8:
 * Fix build issues reported by Christoph.
 * List available probes in a file without need to specify pid.

Changelog from V7:
 * New feature: perf probe lists available probes.
 * Fix perf probes for uprobes to exit with a error message on dwarf
   based probes.
 * Merge changes to kprobes traceevent infrastructure.
 * Merge changes to perf.

Changelog from V6:
 * Remove perf adjust symbols patch.

Changelog from V5:
 * Merged user_bkpt and user_bkpt_xol into uprobes.
 * Addressed comments till now.

Changelog from V4:
 * Rebased to tip tree. (2.6.35-rc3-tip)

Changelog from v3:
 * Reverted to background page replacement as suggested by Peter Zijlstra.
 * Dso in 'perf probe' can be either be a short name or a absolute path.
 * Addressed comments from Masami, Frederic, Steven on traceevents and perf

Changelog from v2:
 * Addressed comments from Oleg, including removal of interrupt context
    handlers, reverting background page replacement in favour of
    access_process_vm().

 * Provides perf interface for uprobes.

Changelog from v1:
 * Added trace_event interface for uprobes.
 * Addressed comments from Andrew Morton and Randy Dunlap.

For previous posting: please refer:
http://lkml.org/lkml/2010/7/27/121, http://lkml.org/lkml/2010/7/12/67,
http://lkml.org/lkml/2010/7/8/239, http://lkml.org/lkml/2010/6/29/299,
http://lkml.org/lkml/2010/6/14/41, http://lkml.org/lkml/2010/3/20/107
and http://lkml.org/lkml/2010/5/18/307

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 and March 2010 (http://lkml.org/lkml/2010/1/11/92,
http://lkml.org/lkml/2010/1/27/19, http://lkml.org/lkml/2010/3/20/107
and http://lkml.org/lkml/2010/3/31/199 ). 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.

- Perf automatically adds probes for high-overhead functions.
- Allow trace scripts to insert probes
- Syscall integration and TRACE_EVENT() universe.
	(Need clarity/discussion on API)
- Allowing probes across fork.
- Allowing probes per-executable/per dso.
- Allow multiple probes to share a probepoint.
- Return probes.
- Support for other architectures.
- Uprobes booster.
- Merge uprobes and kprobes trace_event.
- replace macro W with bits in inat table.

Please do provide your valuable comments.

Thanks in advance.
Srikar

---

 Srikar Dronamraju(15)
 1: mm: Move replace_page() / write_protect_page() to mm/memory.c
 2: uprobes: Breakpoint insertion/removal in user space applications.
 3: uprobes: Slot allocation for Execution out of line(XOL)
 4: uprobes: x86 specific functions for user space breakpointing.
 5: uprobes: Uprobes (un)registration and exception handling.
 6: uprobes: X86 support for Uprobes
 7: uprobes: Uprobes Documentation
 8: tracing: Extract out common code for kprobes/uprobes traceevents.
 9: tracing: uprobes trace_event interface
10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
11: perf: list symbols in a dso in ascending order.
12: perf: show possible probes in a given file.
13: perf: Loop thro each of the maps in a map_group.
14: perf: perf interface for uprobes
15: perf: Show Potential probe points.


 Documentation/uprobes.txt          |  188 +++++
 arch/Kconfig                       |    6 +
 arch/x86/Kconfig                   |    1 +
 arch/x86/include/asm/thread_info.h |    2 +
 arch/x86/include/asm/uprobes.h     |   43 ++
 arch/x86/kernel/Makefile           |    2 +
 arch/x86/kernel/signal.c           |   13 +
 arch/x86/kernel/uprobes.c          |  613 ++++++++++++++++
 fs/exec.c                          |    4 +
 include/linux/mm.h                 |    4 +
 include/linux/mm_types.h           |    4 +
 include/linux/sched.h              |    3 +
 include/linux/uprobes.h            |  305 ++++++++
 kernel/Makefile                    |    1 +
 kernel/fork.c                      |   21 +
 kernel/trace/Kconfig               |   61 ++-
 kernel/trace/Makefile              |    2 +
 kernel/trace/trace.h               |    5 +
 kernel/trace/trace_kprobe.c        |  752 +-------------------
 kernel/trace/trace_probe.c         |  654 +++++++++++++++++
 kernel/trace/trace_probe.h         |  157 ++++
 kernel/trace/trace_uprobe.c        |  739 +++++++++++++++++++
 kernel/uprobes.c                   | 1405 ++++++++++++++++++++++++++++++++++++
 mm/ksm.c                           |  112 ---
 mm/memory.c                        |  120 +++
 tools/perf/builtin-probe.c         |   55 ++-
 tools/perf/util/map.h              |   27 +
 tools/perf/util/probe-event.c      |  503 +++++++++++--
 tools/perf/util/probe-event.h      |   13 +-
 tools/perf/util/symbol.c           |   22 +
 tools/perf/util/symbol.h           |    2 +
 31 files changed, 4886 insertions(+), 953 deletions(-)

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

* [PATCHv11 2.6.36-rc2-tip 1/15]  1: mm: Move replace_page() / write_protect_page() to mm/memory.c
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
@ 2010-08-25 13:41 ` Srikar Dronamraju
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 2/15] 2: uprobes: Breakpoint insertion/removal in user space applications Srikar Dronamraju
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:41 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Masami Hiramatsu,
	Christoph Hellwig, Ananth N Mavinakayanahalli, Oleg Nesterov,
	Mark Wielaard, Mathieu Desnoyers, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler, Andrew Morton, LKML,
	Paul E. McKenney


User bkpt will use background page replacement approach to insert/delete
breakpoints. Background page replacement approach will be based on
replace_page and write_protect_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 |    4 ++
 mm/ksm.c           |  112 -------------------------------------------------
 mm/memory.c        |  120 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 124 insertions(+), 112 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 831c693..3f014e4 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -863,6 +863,10 @@ 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);
+int write_protect_page(struct vm_area_struct *vma, struct page *page,
+						      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 e2ae004..8a792d0 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -694,118 +694,6 @@ static inline int pages_identical(struct page *page1, struct page *page2)
 	return !memcmp_pages(page1, page2);
 }
 
-static int write_protect_page(struct vm_area_struct *vma, struct page *page,
-			      pte_t *orig_pte)
-{
-	struct mm_struct *mm = vma->vm_mm;
-	unsigned long addr;
-	pte_t *ptep;
-	spinlock_t *ptl;
-	int swapped;
-	int err = -EFAULT;
-
-	addr = page_address_in_vma(page, vma);
-	if (addr == -EFAULT)
-		goto out;
-
-	ptep = page_check_address(page, mm, addr, &ptl, 0);
-	if (!ptep)
-		goto out;
-
-	if (pte_write(*ptep)) {
-		pte_t entry;
-
-		swapped = PageSwapCache(page);
-		flush_cache_page(vma, addr, page_to_pfn(page));
-		/*
-		 * Ok this is tricky, when get_user_pages_fast() run it doesnt
-		 * take any lock, therefore the check that we are going to make
-		 * with the pagecount against the mapcount is racey and
-		 * O_DIRECT can happen right after the check.
-		 * So we clear the pte and flush the tlb before the check
-		 * this assure us that no O_DIRECT can happen after the check
-		 * or in the middle of the check.
-		 */
-		entry = ptep_clear_flush(vma, addr, ptep);
-		/*
-		 * Check that no O_DIRECT or similar I/O is in progress on the
-		 * page
-		 */
-		if (page_mapcount(page) + 1 + swapped != page_count(page)) {
-			set_pte_at(mm, addr, ptep, entry);
-			goto out_unlock;
-		}
-		entry = pte_wrprotect(entry);
-		set_pte_at_notify(mm, addr, ptep, entry);
-	}
-	*orig_pte = *ptep;
-	err = 0;
-
-out_unlock:
-	pte_unmap_unlock(ptep, ptl);
-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 135a596..afa51a2 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -2589,6 +2589,126 @@ 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;
+}
+
+/**
+ * write_protect_page - mark the page readonly
+ * @vma:      vma that holds the page we want to mark
+ * @page:     page that needs to be marked readonly
+ * @orig_pte: pte for the protected page.
+ *
+ * Returns 0 on success, -EFAULT on failure.
+ */
+int write_protect_page(struct vm_area_struct *vma, struct page *page,
+						      pte_t *orig_pte)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	unsigned long addr;
+	pte_t *ptep;
+	spinlock_t *ptl;
+	int swapped;
+	int err = -EFAULT;
+
+	addr = page_address_in_vma(page, vma);
+	if (addr == -EFAULT)
+		goto out;
+
+	ptep = page_check_address(page, mm, addr, &ptl, 0);
+	if (!ptep)
+		goto out;
+
+	if (pte_write(*ptep)) {
+		pte_t entry;
+
+		swapped = PageSwapCache(page);
+		flush_cache_page(vma, addr, page_to_pfn(page));
+		/*
+		 * Ok this is tricky, when get_user_pages_fast() run it doesnt
+		 * take any lock, therefore the check that we are going to make
+		 * with the pagecount against the mapcount is racey and
+		 * O_DIRECT can happen right after the check.
+		 * So we clear the pte and flush the tlb before the check
+		 * this assure us that no O_DIRECT can happen after the check
+		 * or in the middle of the check.
+		 */
+		entry = ptep_clear_flush(vma, addr, ptep);
+		/*
+		 * Check that no O_DIRECT or similar I/O is in progress on the
+		 * page
+		 */
+		if (page_mapcount(page) + 1 + swapped != page_count(page)) {
+			set_pte_at(mm, addr, ptep, entry);
+			goto out_unlock;
+		}
+		entry = pte_wrprotect(entry);
+		set_pte_at_notify(mm, addr, ptep, entry);
+	}
+	*orig_pte = *ptep;
+	err = 0;
+
+out_unlock:
+	pte_unmap_unlock(ptep, ptl);
+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] 71+ messages in thread

* [PATCHv11 2.6.36-rc2-tip 2/15]  2: uprobes: Breakpoint insertion/removal in user space applications.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 1/15] 1: mm: Move replace_page() / write_protect_page() to mm/memory.c Srikar Dronamraju
@ 2010-08-25 13:41 ` Srikar Dronamraju
  2010-09-01 19:38   ` Peter Zijlstra
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 3/15] 3: uprobes: Slot allocation for Execution out of line(XOL) Srikar Dronamraju
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:41 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Ananth N Mavinakayanahalli, Oleg Nesterov,
	Mark Wielaard, Mathieu Desnoyers, LKML, Naren A Devaiah,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Andrew Morton, Paul E. McKenney


Provides a mechanism in kernel to insert/remove breakpoints in
user space applications including
   - architecture independent mechanism to establish breakpoints in
     userspace applications.
   - helper functions for reading/writing/validating data/opcodes from
     target process's address space.
   - wrappers and default implementation(whereever possible) of
     architecture dependent functions(setting breakpoint)
   - preprocessing and postprocessing of singlestep on breakpoint hit

Single stepping inline 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.

In execution out of line, threads single steps on a copy of the
instruction. This method works well for both single-threaded and
multithreaded applications.

Uprobes uses execution out of line method.

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 uprobes patch for your
architecture to define architecture specific functionality for
reading/writing/validating data/opcodes.

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

Changelog from V10: (replace function pointers with weak functions)
  * Removed architecture specific function pointers in user_bkpt
    structure and replaced them with weak functions as suggested by
    Peter Zijlstra.

Changelog from V5: (Merge user_bkpt into uprobes)
  * Merged user_bkpt into uprobes as suggested by Christoph Hellwig
    and Peter Zijlstra.

Changelog from V3: (reimplement background page replacement)
  * Replemented background page replacement based on inputs
    from Peter Zijlstra.

Changelog from v2: (addressing comments from Oleg)
  * Use access_process_vm instead of replace_page based
    background page replacement.

Changelog from v1:
  * 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.

 arch/Kconfig            |   14 +
 include/linux/uprobes.h |  160 ++++++++++++++++
 kernel/Makefile         |    1 
 kernel/uprobes.c        |  475 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 650 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 4877a8c..87bd26b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -47,6 +47,17 @@ config OPTPROBES
 	depends on !PREEMPT
 	select KALLSYMS_ALL
 
+config UPROBES
+	bool "User-space probes (EXPERIMENTAL)"
+	depends on ARCH_SUPPORTS_UPROBES
+	depends on MMU
+	help
+	  Uprobes enables kernel subsystems 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
@@ -80,6 +91,9 @@ config USER_RETURN_NOTIFIER
 	  Provide a kernel-internal notification when a cpu is about to
 	  switch to user mode.
 
+config ARCH_SUPPORTS_UPROBES
+	def_bool n
+
 config HAVE_IOREMAP_PROT
 	bool
 
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
new file mode 100644
index 0000000..faacb2f
--- /dev/null
+++ b/include/linux/uprobes.h
@@ -0,0 +1,160 @@
+#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
+ */
+
+#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
+#include <asm/uprobes.h>
+#else
+/*
+ * ARCH_SUPPORTS_UPROBES has not be defined.
+ */
+typedef u8 user_bkpt_opcode_t;
+#define UPROBES_XOL_SLOT_BYTES 1
+
+struct bkpt_arch_info {};
+struct user_bkpt_task_arch_info {};
+struct user_bkpt_arch_info;
+#endif
+
+
+struct task_struct;
+struct pt_regs;
+
+/**
+ * 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.
+ * @fixups:	set of fixups to be executed by @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[UPROBES_XOL_SLOT_BYTES];
+	u16 fixups;
+	struct bkpt_arch_info arch_info;
+};
+
+/* Post-execution fixups.  Some architectures may define others. */
+
+/* No fixup needed */
+#define UPROBES_FIX_NONE	0x0
+/* Adjust IP back to vicinity of actual insn */
+#define UPROBES_FIX_IP	0x1
+/* Adjust the return address of a call insn */
+#define UPROBES_FIX_CALL	0x2
+/* Might sleep while doing Fixup */
+#define UPROBES_FIX_SLEEPY	0x4
+
+#ifndef UPROBES_FIX_DEFAULT
+#define UPROBES_FIX_DEFAULT UPROBES_FIX_IP
+#endif
+
+/**
+ * struct user_bkpt_arch_info - architecture-specific parameters and
+ * functions
+ *
+ * @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 <= UPROBES_XOL_SLOT_BYTES;
+ */
+
+struct user_bkpt_arch_info {
+	user_bkpt_opcode_t bkpt_insn;
+	u8 ip_advancement_by_bkpt_insn;
+	u8 max_insn_bytes;
+};
+
+/* Unexported functions & macros for use by arch-specific code */
+#define user_bkpt_opcode_sz (sizeof(user_bkpt_opcode_t))
+extern unsigned long uprobes_read_vm(struct task_struct *tsk,
+			void __user *vaddr, void *kbuf,
+			unsigned long nbytes);
+extern unsigned long uprobes_write_data(struct task_struct *tsk,
+			void __user *vaddr, const void *kbuf,
+			unsigned long nbytes);
+
+/*
+ * Most architectures can use the default versions of @read_opcode(),
+ * @set_bkpt(), @set_orig_insn(), and @is_bkpt_insn();
+ * All functions (including @validate_address()) can assume
+ * that the caller has verified that the probepoint's virtual address
+ * resides in an executable VM area.
+ * @set_ip:
+ *	Set the instruction pointer in @regs to @vaddr.
+ * @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. 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).
+ * @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.
+ */
+
+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 verify);
+int validate_address(struct task_struct *tsk, unsigned long vaddr);
+bool is_bkpt_insn(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);
+
+extern void set_ip(struct pt_regs *regs, unsigned long vaddr);
+extern int analyze_insn(struct task_struct *tsk, struct user_bkpt *user_bkpt);
+extern int post_xol(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt,
+			struct user_bkpt_task_arch_info *tskinfo,
+			struct pt_regs *regs);
+
+extern struct user_bkpt_arch_info user_bkpt_arch_info;
+
+#endif	/* _LINUX_UPROBES_H */
diff --git a/kernel/Makefile b/kernel/Makefile
index 17046b6..af4ec3f 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_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/uprobes.c b/kernel/uprobes.c
new file mode 100644
index 0000000..8a659c9
--- /dev/null
+++ b/kernel/uprobes.c
@@ -0,0 +1,475 @@
+/*
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/mm.h>
+#include <linux/uprobes.h>
+#include <linux/uaccess.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+
+struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;
+
+/**
+ * uprobes_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 uprobes_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);
+}
+
+/**
+ * uprobes_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 uprobes 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 uprobes_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);
+}
+
+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 *vaddr_old, *vaddr_new;
+	pte_t orig_pte;
+	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, 1, 1, &old_page, &vma);
+	if (ret <= 0)
+		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 put_out;
+	}
+
+	/* If its VM_SHARED vma, lets not write to such vma's.  */
+	if (vma->vm_flags & VM_SHARED) {
+		ret = -EINVAL;
+		goto put_out;
+	}
+
+	/* Allocate a page */
+	new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
+	if (!new_page) {
+		ret = -ENOMEM;
+		goto put_out;
+	}
+
+	/*
+	 * lock page will serialize against do_wp_page()'s
+	 * PageAnon() handling
+	 */
+	lock_page(old_page);
+	/* mark page RO so any concurrent access will end up in do_wp_page() */
+	if (write_protect_page(vma, old_page, &orig_pte))
+		goto unlock_out;
+
+	/* copy the page now that we've got it stable */
+	vaddr_old = kmap_atomic(old_page, KM_USER0);
+	vaddr_new = kmap_atomic(new_page, KM_USER1);
+
+	memcpy(vaddr_new, vaddr_old, PAGE_SIZE);
+	/* poke the new insn in, ASSUMES we don't cross page boundary */
+	vaddr &= ~PAGE_MASK;
+	memcpy(vaddr_new + vaddr, &opcode, user_bkpt_opcode_sz);
+
+	kunmap_atomic(vaddr_new, KM_USER1);
+	kunmap_atomic(vaddr_old, KM_USER0);
+
+	lock_page(new_page);
+	/* flip pages, do_wp_page() will fail pte_same() and bail */
+	ret = replace_page(vma, old_page, new_page, orig_pte);
+
+unlock_out:
+	unlock_page(new_page);
+	unlock_page(old_page);
+	if (ret != 0)
+		page_cache_release(new_page);
+
+put_out:
+	put_page(old_page); /* we did a get_page in the beginning */
+
+mmput_out:
+	up_read(&mm->mmap_sem);
+	mmput(mm);
+	return ret;
+}
+
+/**
+ * read_opcode - read the opcode at a given virtual address.
+ * @tsk: the probed task.
+ * @vaddr: the virtual address to store the opcode.
+ * @opcode: location to store the read opcode.
+ *
+ * For task @tsk, read the opcode at @vaddr and store it in @opcode.
+ * Return 0 (success) or a negative errno.
+ */
+int __weak read_opcode(struct task_struct *tsk, unsigned long vaddr,
+						user_bkpt_opcode_t *opcode)
+{
+	unsigned long bytes_read;
+
+	bytes_read = uprobes_read_vm(tsk, (void __user *) vaddr, opcode,
+						user_bkpt_opcode_sz);
+	return (bytes_read == user_bkpt_opcode_sz ? 0 : -EFAULT);
+}
+
+/**
+ * set_bkpt - store breakpoint at a given address.
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information
+ *
+ * For task @tsk, store the breakpoint instruction at
+ * @user_bkpt->vaddr.
+ * Return 0 (success) or a negative errno.
+ */
+int __weak set_bkpt(struct task_struct *tsk, struct user_bkpt *user_bkpt)
+{
+	return write_opcode(tsk, user_bkpt->vaddr, arch->bkpt_insn);
+}
+
+/**
+ * set_orig_insn - Restore the original instruction.
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information
+ * @verify: if true, verify existance of breakpoint instruction.
+ *
+ * For task @tsk, restore the original opcode (@user_bkpt->opcode) at
+ * @user_bkpt->vaddr.
+ * Return 0 (success) or a negative errno.
+ */
+int __weak set_orig_insn(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt, bool verify)
+{
+	if (verify) {
+		user_bkpt_opcode_t opcode;
+		int result = 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);
+}
+
+/**
+ * check_vma - verify if the address is in a executable vma.
+ * @tsk: the probed task
+ * @vaddr: virtual address of the instruction to be verified.
+ *
+ * Return 0 if vaddr is in an executable VM area,
+ * or -EINVAL otherwise.
+ */
+static 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;
+}
+
+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);
+}
+
+/**
+ * validate_address - validate instruction address.
+ * @tsk: the probed task
+ * @vaddr: virtual address of the instruction to be verified.
+ *
+ * Returns 0 if the vaddr is a valid instruction address.
+ * Possible errors:
+ * -%EINVAL: Instruction passed is not a valid instruction address.
+ */
+int __weak validate_address(struct task_struct *tsk, unsigned long vaddr)
+{
+	return check_vma(tsk, vaddr);
+}
+
+/*
+ * __insert_bkpt - insert breakpoint
+ * Insert a breakpoint into the process that includes @tsk, at the
+ * virtual address @user_bkpt->vaddr.
+ *
+ * All threads of the probed process must be stopped while
+ * @__insert_bkpt() runs.
+ *
+ * Possible errors:
+ * -%ENOSYS: user_bkpt not supported for this architecture
+ * -%EINVAL: invalid instruction address
+ * -%EEXIST: breakpoint instruction already exists at that address
+ * -%EPERM: cannot probe this instruction
+ * -%EFAULT: failed to insert breakpoint instruction
+ */
+static int __insert_bkpt(struct task_struct *tsk,
+					struct user_bkpt *user_bkpt)
+{
+	int result;
+	unsigned long len;
+
+	result = validate_address(tsk, user_bkpt->vaddr);
+	if (result != 0)
+		return result;
+
+	/*
+	 * If uprobes_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 = uprobes_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 (is_bkpt_insn(user_bkpt)) {
+		print_insert_fail(tsk, user_bkpt,
+					"bkpt already exists at that addr");
+		return -EEXIST;
+	}
+
+	result = analyze_insn(tsk, user_bkpt);
+	if (result < 0) {
+		print_insert_fail(tsk, user_bkpt,
+					"instruction type cannot be probed");
+		return result;
+	}
+
+	result = set_bkpt(tsk, user_bkpt);
+	if (result < 0) {
+		print_insert_fail(tsk, user_bkpt,
+					"failed to insert bkpt instruction");
+		return result;
+	}
+	return 0;
+}
+
+/*
+ * pre_sstep - prepare to single-step the probed instruction
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information, as returned by
+ *	@__insert_bkpt(). @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
+ *	@uprobes_post_sstep(), and populated with the contents of
+ *	@user_bkpt->insn.
+ *	@tskinfo: points to a @user_bkpt_task_arch_info object for @tsk.
+ *	@regs: reflects the saved user state of @tsk.  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
+ * @uprobes_pre_sstep() is called and when @uprobes_post_sstep() returns.
+ */
+static int pre_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	return pre_xol(tsk, user_bkpt, tskinfo, regs);
+}
+
+/*
+ * 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
+ *	@pre_sstep()
+ * @regs: reflects the saved state of @tsk after the single-step
+ *	operation.  @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.
+ */
+static int post_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
+		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
+{
+	return post_xol(tsk, user_bkpt, tskinfo, regs);
+}
+
+/**
+ * uprobes_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 uprobes_get_bkpt_addr(struct pt_regs *regs)
+{
+	return instruction_pointer(regs) - arch->ip_advancement_by_bkpt_insn;
+}
+
+/*
+ * __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.
+ */
+static int __remove_bkpt(struct task_struct *tsk,
+					struct user_bkpt *user_bkpt)
+{
+	if (validate_address(tsk, user_bkpt->vaddr) != 0)
+		return -EINVAL;
+	return set_orig_insn(tsk, user_bkpt, true);
+}
+
+/*
+ * uprobes_resume_can_sleep - Check if fixup might result in sleep.
+ * @user_bkpt: the probepoint information.
+ *
+ * Returns true if fixup might result in sleep.
+ */
+static bool uprobes_resume_can_sleep(struct user_bkpt *user_bkpt)
+{
+	return user_bkpt->fixups & UPROBES_FIX_SLEEPY;
+}
+
+/**
+ * is_bkpt_insn - check if instruction is breakpoint instruction.
+ * @user_bkpt: the probepoint information.
+ * Default implementation of is_bkpt_insn
+ * Returns true if @user_bkpt->opcode is @bkpt_insn.
+ */
+bool __weak is_bkpt_insn(struct user_bkpt *user_bkpt)
+{
+	return (user_bkpt->opcode == arch->bkpt_insn);
+}
+
+/**
+ * pre_xol - prepare to execute out of line.
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information.
+ * @tskinfo: points to a @user_bkpt_task_arch_info object for @tsk.
+ * @regs: reflects the saved user state of @tsk.
+ *
+ * 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.
+ * Always returns true.
+ */
+int __weak pre_xol(struct task_struct *tsk,
+			struct user_bkpt *user_bkpt,
+			struct user_bkpt_task_arch_info *tskinfo,
+			struct pt_regs *regs)
+{
+	set_ip(regs, user_bkpt->xol_vaddr);
+	return 0;
+}
+
+/* Validate arch-specific info during uprobes initialization. */
+static int bad_arch_param(const char *param_name, int value)
+{
+	printk(KERN_ERR "uprobes: bad value %d/%#x for parameter %s"
+		" in user_bkpt_arch_info\n", value, value, param_name);
+	return -ENOSYS;
+}
+
+static int __init init_uprobes(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);
+	return result;
+}
+
+module_init(init_uprobes);

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

* [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 1/15] 1: mm: Move replace_page() / write_protect_page() to mm/memory.c Srikar Dronamraju
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 2/15] 2: uprobes: Breakpoint insertion/removal in user space applications Srikar Dronamraju
@ 2010-08-25 13:41 ` Srikar Dronamraju
  2010-09-01 20:13   ` Peter Zijlstra
  2010-09-02  8:23   ` Peter Zijlstra
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 4/15] 4: uprobes: x86 specific functions for user space breakpointing Srikar Dronamraju
                   ` (12 subsequent siblings)
  15 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:41 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


Provides slot allocation mechanism for execution out of line for use
with user space breakpointing.

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

Changelog from V5: Merged into uprobes.

Changelog form V3:
 * Added a memory barrier after the slot gets initialized.

Changelog from V2: (addressing Oleg's comments)
 * Removed code in !CONFIG_UPROBES_XOL
 * Functions now pass pointer to uprobes_xol_area instead of pointer
   to void.

 include/linux/uprobes.h |    2 
 kernel/uprobes.c        |  283 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 285 insertions(+), 0 deletions(-)

diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index faacb2f..84a078c 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -37,6 +37,8 @@ struct user_bkpt_task_arch_info {};
 struct user_bkpt_arch_info;
 #endif
 
+#define UINSNS_PER_PAGE	(PAGE_SIZE/UPROBES_XOL_SLOT_BYTES)
+#define MAX_UPROBES_XOL_SLOTS UINSNS_PER_PAGE
 
 struct task_struct;
 struct pt_regs;
diff --git a/kernel/uprobes.c b/kernel/uprobes.c
index 8a659c9..230adf3 100644
--- a/kernel/uprobes.c
+++ b/kernel/uprobes.c
@@ -30,6 +30,12 @@
 #include <linux/uaccess.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/pid.h>
+#include <linux/slab.h>
 
 struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;
 
@@ -458,6 +464,283 @@ static int bad_arch_param(const char *param_name, int value)
 	return -ENOSYS;
 }
 
+/* Slot allocation for XOL */
+
+/*
+ * 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.
+ */
+
+struct uprobes_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 uprobes_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 "uprobes_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 "uprobes_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 uprobes_xol_area.
+ * This area will be used for storing instructions for execution out of
+ * line.
+ *
+ * Called with mm->uproc->mutex locked.
+ * Returns the allocated area or NULL.
+ */
+static struct uprobes_xol_area *xol_alloc_area(void)
+{
+	struct uprobes_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 area;
+
+fail:
+	kfree(area);
+	return NULL;
+}
+
+/*
+ * xol_free_area - Free the area allocated for slots.
+ * @xol_area refers the unique per process uprobes_xol_area for
+ * this process.
+ *
+ */
+static void xol_free_area(struct uprobes_xol_area *xol_area)
+{
+	kfree(xol_area->bitmap);
+	kfree(xol_area);
+}
+
+/*
+ * Find a slot
+ *  - searching in existing vmas for a free slot.
+ *  - If no free slot in existing vmas, return 0;
+ *
+ * Called when holding xol_area->lock
+ */
+static unsigned long xol_take_insn_slot(struct uprobes_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 * UPROBES_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 uprobes_insert_bkpt is already called, (i.e
+ * user_bkpt.vaddr != 0) then copy the instruction into the slot.
+ * @user_bkpt: probepoint information
+ * @xol_area refers the unique per process uprobes_xol_area for
+ * this process.
+ *
+ * Called with mm->uproc->mutex locked.
+ * Returns the allocated slot address or 0.
+ */
+static unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
+				struct uprobes_xol_area *xol_area)
+{
+	unsigned long flags, xol_vaddr = 0;
+	int len;
+
+	if (unlikely(!xol_area))
+		return 0;
+
+	if (user_bkpt->xol_vaddr)
+		return user_bkpt->xol_vaddr;
+
+	spin_lock_irqsave(&xol_area->lock, flags);
+	xol_vaddr = xol_take_insn_slot(xol_area);
+	spin_unlock_irqrestore(&xol_area->lock, flags);
+
+	/*
+	 * Initialize the slot if user_bkpt->vaddr points to valid
+	 * instruction slot.
+	 */
+	if (likely(xol_vaddr) && user_bkpt->vaddr) {
+		len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
+						UPROBES_XOL_SLOT_BYTES, 1);
+		if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
+			printk(KERN_ERR "Failed to copy instruction at %#lx "
+					"len = %d\n", user_bkpt->vaddr, len);
+	}
+
+	/*
+	 * Update user_bkpt->xol_vaddr after giving a chance for the slot to
+	 * be initialized.
+	 */
+	mb();
+	user_bkpt->xol_vaddr = xol_vaddr;
+	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 uprobes_xol_area for
+ * this process.
+ */
+static void xol_free_insn_slot(unsigned long slot_addr,
+				struct uprobes_xol_area *xol_area)
+{
+	unsigned long vma_end;
+	int found = 0;
+
+	if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
+		return;
+
+	if (unlikely(!xol_area))
+		return;
+
+	vma_end = xol_area->vaddr + PAGE_SIZE;
+	if (xol_area->vaddr <= slot_addr && slot_addr < vma_end) {
+		int slot_nr;
+		unsigned long offset = slot_addr - xol_area->vaddr;
+		unsigned long flags;
+
+		BUG_ON(offset % UPROBES_XOL_SLOT_BYTES);
+
+		slot_nr = offset / UPROBES_XOL_SLOT_BYTES;
+		BUG_ON(slot_nr >= UINSNS_PER_PAGE);
+
+		spin_lock_irqsave(&xol_area->lock, flags);
+		clear_bit(slot_nr, xol_area->bitmap);
+		spin_unlock_irqrestore(&xol_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 uprobes_xol_area for
+ * this process.
+ */
+static int xol_validate_vaddr(struct pid *pid, unsigned long vaddr,
+				struct uprobes_xol_area *xol_area)
+{
+	struct task_struct *tsk;
+	unsigned long vma_end;
+	int result;
+
+	if (unlikely(!xol_area))
+		return 0;
+
+	tsk = get_pid_task(pid, PIDTYPE_PID);
+	if (unlikely(!tsk))
+		return -EINVAL;
+
+	result = validate_address(tsk, vaddr);
+	if (result != 0)
+		goto validate_end;
+
+	vma_end = xol_area->vaddr + PAGE_SIZE;
+	if (xol_area->vaddr <= vaddr && vaddr < vma_end)
+		result = 1;
+
+validate_end:
+	put_task_struct(tsk);
+	return result;
+}
+/* end of slot allocation for XOL */
+
 static int __init init_uprobes(void)
 {
 	int result = 0;

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

* [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (2 preceding siblings ...)
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 3/15] 3: uprobes: Slot allocation for Execution out of line(XOL) Srikar Dronamraju
@ 2010-08-25 13:42 ` Srikar Dronamraju
  2010-09-03 10:26   ` Andi Kleen
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 5/15] 5: uprobes: Uprobes (un)registration and exception handling Srikar Dronamraju
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


Provides x86 specific functions for instruction analysis and
instruction validation and x86 specific pre-processing and
post-processing of singlestep especially for RIP relative
instructions. Uses "x86: instruction decoder API" for validation and
analysis of user space instructions. This analysis is used at the time
of post-processing of breakpoint hit to do the necessary fix-ups.
There is support for breakpointing RIP relative instructions. However
there are still few instructions that cannot be singlestepped.

Also defines TIF_UPROBE flag for x86.

This patch requires "x86: instruction decoder API"
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>
---

Changelog from V10: (replace function pointers with weak function
      * Removed architecture specific function pointers in user_bkpt
        structure and replaced them with weak functions as suggested
        Peter Zijlstra.


Changelog from V5: Merged into uprobes layer.

Changelog from V1:
   set UPROBES_FIX_SLEEPY if post_xol might sleep.
---
 arch/x86/Kconfig                   |    1 
 arch/x86/include/asm/thread_info.h |    2 
 arch/x86/include/asm/uprobes.h     |   43 +++
 arch/x86/kernel/Makefile           |    2 
 arch/x86/kernel/uprobes.c          |  561 ++++++++++++++++++++++++++++++++++++
 5 files changed, 609 insertions(+), 0 deletions(-)
 create mode 100644 arch/x86/include/asm/uprobes.h
 create mode 100644 arch/x86/kernel/uprobes.c

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index f0ee331..4710268 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_MIXED_BREAKPOINTS_REGS
+	select ARCH_SUPPORTS_UPROBES
 	select PERF_EVENTS
 	select HAVE_PERF_EVENTS_NMI
 	select ANON_INODES
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index f0b6e5d..5b9c9f0 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -84,6 +84,7 @@ struct thread_info {
 #define TIF_SECCOMP		8	/* secure computing */
 #define TIF_MCE_NOTIFY		10	/* notify userspace of an MCE */
 #define TIF_USER_RETURN_NOTIFY	11	/* notify kernel of userspace return */
+#define TIF_UPROBE		12	/* breakpointed or singlestepping */
 #define TIF_NOTSC		16	/* TSC is not accessible in userland */
 #define TIF_IA32		17	/* 32bit process */
 #define TIF_FORK		18	/* ret_from_fork */
@@ -107,6 +108,7 @@ struct thread_info {
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_MCE_NOTIFY		(1 << TIF_MCE_NOTIFY)
 #define _TIF_USER_RETURN_NOTIFY	(1 << TIF_USER_RETURN_NOTIFY)
+#define _TIF_UPROBE		(1 << TIF_UPROBE)
 #define _TIF_NOTSC		(1 << TIF_NOTSC)
 #define _TIF_IA32		(1 << TIF_IA32)
 #define _TIF_FORK		(1 << TIF_FORK)
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
new file mode 100644
index 0000000..035c2c9
--- /dev/null
+++ b/arch/x86/include/asm/uprobes.h
@@ -0,0 +1,43 @@
+#ifndef _ASM_UPROBES_H
+#define _ASM_UPROBES_H
+/*
+ * Userspace Probes (UProbes) 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 UPROBES_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_UPROBES_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 801127c..4ef1efa 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -115,6 +115,8 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o
 
 obj-$(CONFIG_SWIOTLB)			+= pci-swiotlb.o
 
+obj-$(CONFIG_UPROBES)			+= uprobes.o
+
 ###
 # 64 bit specific files
 ifeq ($(CONFIG_X86_64),y)
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
new file mode 100644
index 0000000..ceaedc9
--- /dev/null
+++ b/arch/x86/kernel/uprobes.c
@@ -0,0 +1,561 @@
+/*
+ * Userspace Probes (UProbes) 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/uprobes.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 UPROBES_FIX_RIP_AX	0x8000
+#define UPROBES_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)
+
+/*
+ * @reg: reflects the saved state of the task
+ * @vaddr: the virtual address to jump to.
+ * Return 0 on success or a -ve number on error.
+ */
+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 &
+			(UPROBES_FIX_RIP_AX | UPROBES_FIX_RIP_CX)) != 0);
+}
+
+#endif	/* CONFIG_X86_64 */
+
+#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))
+
+
+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 */
+
+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         */
+};
+#undef W
+
+/*
+ * 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 "uprobes 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, "
+		"uprobes 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 "uprobes 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 |= UPROBES_FIX_IP;
+	if (fix_call)
+		user_bkpt->fixups |=
+			(UPROBES_FIX_CALL | UPROBES_FIX_SLEEPY);
+}
+
+#ifdef CONFIG_X86_64
+static int handle_riprel_insn(struct user_bkpt *user_bkpt, struct insn *insn);
+#endif
+
+/**
+ * analyze_insn - instruction analysis including validity and fixups.
+ * @tsk: the probed task.
+ * @user_bkpt: the probepoint information.
+ * Return 0 on success or a -ve number on error.
+ */
+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;
+#ifdef CONFIG_X86_64
+	ret = handle_riprel_insn(user_bkpt, &insn);
+	if (ret == -1)
+		/* rip-relative; can't XOL */
+		return 0;
+#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.
+ *
+ * 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;
+
+	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 = UPROBES_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 = UPROBES_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;
+}
+
+/*
+ * pre_xol - prepare to execute out of line.
+ * @tsk: the probed task
+ * @user_bkpt: the probepoint information.
+ * @tskinfo: points to a @user_bkpt_task_arch_info object for @tsk.
+ * @regs: reflects the saved user state of @tsk.
+ *
+ * If we're emulating a rip-relative instruction, save the contents
+ * of the scratch register and store the target address in that register.
+ *
+ * Returns true if @user_bkpt->opcode is @bkpt_insn.
+ */
+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 & UPROBES_FIX_RIP_AX) {
+		tskinfo->saved_scratch_register = regs->ax;
+		regs->ax = user_bkpt->arch_info.rip_target_address;
+	} else if (user_bkpt->fixups & UPROBES_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 = uprobes_read_vm(tsk, (void __user *) sp, &ra, rasize);
+	if (unlikely(ncopied != rasize))
+		goto fail;
+	ra += correction;
+	ncopied = uprobes_write_data(tsk, (void __user *) sp, &ra, rasize);
+	if (unlikely(ncopied != rasize))
+		goto fail;
+	return 0;
+
+fail:
+	printk(KERN_ERR
+		"uprobes: 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)
+ */
+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 & UPROBES_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 & UPROBES_FIX_IP)
+		regs->ip += correction;
+	if (user_bkpt->fixups & UPROBES_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,
+};

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

* [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (3 preceding siblings ...)
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 4/15] 4: uprobes: x86 specific functions for user space breakpointing Srikar Dronamraju
@ 2010-08-25 13:42 ` Srikar Dronamraju
  2010-09-01 21:43   ` Peter Zijlstra
  2010-09-01 21:46   ` Peter Zijlstra
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 6/15] 6: uprobes: X86 support for Uprobes Srikar Dronamraju
                   ` (10 subsequent siblings)
  15 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


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.  Uprobes uses the
execution out of line strategy and 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 only when the probe gets unregistered and
there are no threads in the vicinity.

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. Allow multiple probes at a probepoint.
2. Booster probes.
3. Allow probes to be inherited across fork.
4. probing function returns.

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

Changelog from V5:
   - Merged user_bkpt and user_bkpt_xol layers into uprobes.

Changelog from V2:
   - Introduce TIF_UPROBE flag.
   - uprobes hooks now in fork/exec/exit paths instead of tracehooks.
   - uprobe_process is now part of the mm struct and is shared between
     processes that share the mm.
   - per thread information is now allocated on the fly.
     * Hence allocation and freeing of this information is lockless.
   - find_probept() takes the spinlock; unlike previously when it was
     expected that the spinlock was taken before calling it.
   - For now run the handler in task context. The reasons for this
     change being.
     * utask (per task meta data structure is now allocated on the
       fly. Hence first request on the thread and first request for
       the breakpoint have to be anyway allocated in task context.
     * Measurements showed task based handler had negligible
       overhead over interrupt based handlers.
     * Feedback from Oleg and few others.
     * Feedback at LFCS.
     * Simplicity  atleast till uprobes stabilizes.
       (However we introduce interrupt based handlers at a later time.)

Changelog from v1:
	- If fixup might sleep; then do the post singlestep
	   processing in task context.

 arch/Kconfig             |    1 
 fs/exec.c                |    4 
 include/linux/mm_types.h |    4 
 include/linux/sched.h    |    3 
 include/linux/uprobes.h  |  143 ++++++++++
 kernel/fork.c            |   21 +
 kernel/uprobes.c         |  653 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 826 insertions(+), 3 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index 87bd26b..c8c8e3f 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -49,6 +49,7 @@ config OPTPROBES
 
 config UPROBES
 	bool "User-space probes (EXPERIMENTAL)"
+	default n
 	depends on ARCH_SUPPORTS_UPROBES
 	depends on MMU
 	help
diff --git a/fs/exec.c b/fs/exec.c
index 2d94552..d7e64d0 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1053,6 +1053,10 @@ void setup_new_exec(struct linux_binprm * bprm)
 			
 	flush_signal_handlers(current, 0);
 	flush_old_files(current->files);
+#ifdef CONFIG_UPROBES
+	if (unlikely(current->utask))
+		uprobe_free_utask(current);
+#endif
 }
 EXPORT_SYMBOL(setup_new_exec);
 
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index cb57d65..8f79e51 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -12,6 +12,7 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/page-debug-flags.h>
+#include <linux/uprobes.h>
 #include <asm/page.h>
 #include <asm/mmu.h>
 
@@ -310,6 +311,9 @@ struct mm_struct {
 #ifdef CONFIG_MMU_NOTIFIER
 	struct mmu_notifier_mm *mmu_notifier_mm;
 #endif
+#ifdef CONFIG_UPROBES
+	struct uprobe_process *uproc;	/* per mm uprobes info */
+#endif
 };
 
 /* Future-safe accessor for struct mm_struct's cpu_vm_mask. */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index e18473f..e237ba2 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -1496,6 +1496,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/uprobes.h b/include/linux/uprobes.h
index 84a078c..a85cd36 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -23,6 +23,14 @@
  *	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>
+
 #ifdef CONFIG_ARCH_SUPPORTS_UPROBES
 #include <asm/uprobes.h>
 #else
@@ -159,4 +167,139 @@ extern int post_xol(struct task_struct *tsk,
 
 extern struct user_bkpt_arch_info user_bkpt_arch_info;
 
+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*);
+};
+
+/*
+ * 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).
+ *
+ * All processes/threads that share a mm share the same uprobe_process.
+ */
+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;
+
+	/*
+	 * 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;
+
+	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(struct task_struct *tsk);
+extern void uprobe_handle_fork(struct task_struct *child);
+extern void uprobe_put_uprocess(struct mm_struct *mm);
+#else	/* CONFIG_UPROBES */
+
+/*
+ * 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_fork(struct task_struct *child)
+{
+}
+static inline void uprobe_notify_resume(struct pt_regs *regs)
+{
+}
+static inline void uprobe_put_uprocess(struct mm_struct *mm)
+{
+}
+#endif	/* CONFIG_UPROBES */
 #endif	/* _LINUX_UPROBES_H */
+
diff --git a/kernel/fork.c b/kernel/fork.c
index b7e9d60..d78cf5d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -187,6 +187,11 @@ void __put_task_struct(struct task_struct *tsk)
 	delayacct_tsk_free(tsk);
 	put_signal_struct(tsk->signal);
 
+#ifdef CONFIG_UPROBES
+	if (unlikely(tsk->utask))
+		uprobe_free_utask(tsk);
+#endif
+
 	if (!profile_handoff_task(tsk))
 		free_task(tsk);
 }
@@ -525,6 +530,10 @@ void __mmdrop(struct mm_struct *mm)
 	mm_free_pgd(mm);
 	destroy_context(mm);
 	mmu_notifier_mm_destroy(mm);
+#ifdef CONFIG_UPROBES
+	if (unlikely(mm->uproc))
+		uprobe_put_uprocess(mm);
+#endif
 	free_mm(mm);
 }
 EXPORT_SYMBOL_GPL(__mmdrop);
@@ -683,6 +692,9 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
 	if (mm->binfmt && !try_module_get(mm->binfmt->module))
 		goto free_pt;
 
+#ifdef CONFIG_UPROBES
+	mm->uproc = NULL;
+#endif
 	return mm;
 
 free_pt:
@@ -1190,6 +1202,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
 	 */
@@ -1288,6 +1303,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 	proc_fork_connector(p);
 	cgroup_post_fork(p);
 	perf_event_fork(p);
+#ifdef CONFIG_UPROBES
+	if ((current->mm) && !(clone_flags & CLONE_VM)) {
+		if (unlikely(current->mm->uproc))
+			uprobe_handle_fork(p);
+	}
+#endif
 	return p;
 
 bad_fork_free_pid:
diff --git a/kernel/uprobes.c b/kernel/uprobes.c
index 230adf3..38c7abb 100644
--- a/kernel/uprobes.c
+++ b/kernel/uprobes.c
@@ -21,6 +21,7 @@
  *	Jim Keniston
  */
 #include <linux/kernel.h>
+#include <linux/types.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
@@ -36,6 +37,9 @@
 #include <linux/file.h>
 #include <linux/pid.h>
 #include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/kdebug.h>
+
 
 struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;
 
@@ -345,7 +349,7 @@ static int __insert_bkpt(struct task_struct *tsk,
  *	@__insert_bkpt(). @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
- *	@uprobes_post_sstep(), and populated with the contents of
+ *	@post_sstep(), and populated with the contents of
  *	@user_bkpt->insn.
  *	@tskinfo: points to a @user_bkpt_task_arch_info object for @tsk.
  *	@regs: reflects the saved user state of @tsk.  pre_sstep()
@@ -356,7 +360,7 @@ static int __insert_bkpt(struct task_struct *tsk,
  *
  * The client must ensure that the contents of @user_bkpt are not
  * changed during the single-step operation -- i.e., between when
- * @uprobes_pre_sstep() is called and when @uprobes_post_sstep() returns.
+ * @pre_sstep() is called and when @post_sstep() returns.
  */
 static int pre_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
 		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
@@ -610,7 +614,7 @@ static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
 
 /*
  * xol_get_insn_slot - If user_bkpt  was not allocated a slot, then
- * allocate a slot. If uprobes_insert_bkpt is already called, (i.e
+ * allocate a slot. If insert_bkpt is already called, (i.e
  * user_bkpt.vaddr != 0) then copy the instruction into the slot.
  * @user_bkpt: probepoint information
  * @xol_area refers the unique per process uprobes_xol_area for
@@ -741,6 +745,642 @@ validate_end:
 }
 /* end of slot allocation for XOL */
 
+
+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->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 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;
+
+	spin_lock(&uproc->pptlist_lock);
+	list_for_each_entry(ppt, &uproc->uprobe_list, ut_node) {
+		if (ppt->user_bkpt.vaddr == vaddr) {
+			spin_unlock(&uproc->pptlist_lock);
+			return ppt;
+		}
+	}
+	spin_unlock(&uproc->pptlist_lock);
+	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 = __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 (__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 = NULL;
+	struct task_struct *tsk;
+
+	rcu_read_lock();
+	tsk = pid_task(tg_leader, PIDTYPE_PID);
+	if (!tsk || !tsk->mm)
+		goto end;
+
+	uproc = tsk->mm->uproc;
+	if (uproc)
+		atomic_inc(&uproc->refcount);
+
+end:
+	rcu_read_unlock();
+	return uproc;
+}
+
+/*
+ * uproc's process is exiting or exec-ing.
+ * The last thread of uproc's process is about to die, and its
+ * mm_struct is about to be released.
+ * Hence do the cleanup without holding locks.
+ *
+ * Called with no locks held.
+ */
+static int free_uprocess(struct uprobe_process *uproc)
+{
+	struct uprobe_probept *ppt, *pnode;
+
+	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);
+
+	kfree(uproc);
+	return 0;
+}
+
+/* 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(struct task_struct *tsk)
+{
+	if (!tsk->utask)
+		return;
+
+	if (tsk->utask->active_ppt)
+		put_probept(tsk->utask->active_ppt);
+	kfree(tsk->utask);
+	tsk->utask = NULL;
+}
+
+/*
+ * Callback from mmput() when mm->users count reduces to zero.
+ */
+void uprobe_put_uprocess(struct mm_struct *mm)
+{
+	put_uprocess(mm->uproc);
+	mm->uproc = NULL;
+}
+
+/*
+ * Allocate a uprobe_task object for the task.
+ * Called with t "got" and uprobe_mutex locked.
+ * Called when the thread hits a breakpoint for the first time.
+ *
+ * Returns:
+ * - pointer to new uprobe_task on success
+ * - negative errno otherwise
+ */
+static struct uprobe_task *add_utask(struct uprobe_process *uproc)
+{
+	struct uprobe_task *utask;
+
+	utask = kzalloc(sizeof *utask, GFP_KERNEL);
+	if (unlikely(utask == NULL))
+		return ERR_PTR(-ENOMEM);
+
+	utask->active_ppt = NULL;
+	current->utask = utask;
+	atomic_inc(&uproc->refcount);
+
+	return utask;
+}
+
+/* Runs with uprobe_mutex held; */
+static struct uprobe_process *create_uprocess(struct pid *tg_leader)
+{
+	struct uprobe_process *uproc = ERR_PTR(-ENOMEM);
+	struct task_struct *tsk;
+	struct mm_struct *mm = NULL;
+
+	tsk = get_pid_task(tg_leader, PIDTYPE_PID);
+	if (tsk)
+		mm = get_task_mm(tsk);
+	if (!mm) {
+		if (tsk)
+			put_task_struct(tsk);
+		return ERR_PTR(-ESRCH);
+	}
+
+	uproc = kzalloc(sizeof *uproc, GFP_KERNEL);
+	if (unlikely(uproc == NULL)) {
+		uproc =  ERR_PTR(-ENOMEM);
+		goto end;
+	}
+
+	/* Initialize fields */
+	mutex_init(&uproc->mutex);
+	spin_lock_init(&uproc->pptlist_lock);
+	atomic_set(&uproc->refcount, 1);
+	INIT_LIST_HEAD(&uproc->uprobe_list);
+
+	BUG_ON(mm->uproc);
+	mm->uproc = uproc;
+
+	/*
+	 * Incrementing the refcount saves us from calling find_uprocess
+	 * in register_uprobe path.
+	 */
+	atomic_inc(&uproc->refcount);
+
+end:
+	put_task_struct(tsk);
+	mmput(mm);
+	return uproc;
+}
+
+/*
+ * 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();
+	pid = find_vpid(p);
+	if (pid) {
+		struct task_struct *t = pid_task(pid, PIDTYPE_PID);
+
+		if (!t)
+			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_UPROBES_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. */
+	ppt = find_probept(uproc, u->vaddr);
+	if (ppt) {
+		/*
+		 * A uprobe already exists at that address.
+		 */
+		ret = -EALREADY;
+		goto fail_uproc;
+	} else {
+		struct task_struct *t;
+
+		ppt = add_probept(u, uproc);
+		if (IS_ERR(ppt)) {
+			ret = (int) PTR_ERR(ppt);
+			goto fail_uproc;
+		}
+
+		t = get_pid_task(p, PIDTYPE_PID);
+		if (!t)
+			goto fail_uproc;
+
+		ret = insert_bkpt(ppt, t);
+		put_task_struct(t);
+		if (ret != 0)
+			goto fail_uproc;
+	}
+
+fail_uproc:
+	mutex_unlock(&uproc->mutex);
+	put_uprocess(uproc);
+
+fail_tsk:
+	put_pid(p);
+	return ret;
+}
+
+/* See Documentation/uprobes.txt. */
+void unregister_uprobe(struct uprobe *u)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	struct task_struct *t;
+	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);
+
+	ppt = find_probept(uproc, u->vaddr);
+	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;
+
+	t = get_pid_task(p, PIDTYPE_PID);
+	if (!t)
+		goto done;
+
+	remove_bkpt(ppt, t);
+	put_task_struct(t);
+
+	/*
+	 * 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);
+}
+
+/* Prepare to single-step ppt's probed instruction out of line. */
+static int pre_ssout(struct uprobe_probept *ppt, struct pt_regs *regs)
+{
+	struct uprobe_process *uproc = current->mm->uproc;
+
+	if (unlikely(!ppt->user_bkpt.xol_vaddr)) {
+		mutex_lock(&uproc->mutex);
+		if (unlikely(!uproc->xol_area))
+			uproc->xol_area = xol_alloc_area();
+		if (uproc->xol_area && !ppt->user_bkpt.xol_vaddr)
+			xol_get_insn_slot(&ppt->user_bkpt, uproc->xol_area);
+		if (unlikely(!ppt->user_bkpt.xol_vaddr))
+			goto fail;
+		mutex_unlock(&uproc->mutex);
+	}
+	pre_sstep(current, &ppt->user_bkpt,
+					&current->utask->arch_info, regs);
+	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);
+	mutex_unlock(&uproc->mutex);
+	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 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;
+}
+
+/*
+ * Fork callback: The current task has spawned a 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.
+ * This function handles the case where vm is not shared between
+ * the parent and the child.
+ *
+ * TODO:
+ *	- Provide option for child to inherit uprobes.
+ */
+void uprobe_handle_fork(struct task_struct *child)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	int ret;
+
+	uproc = current->mm->uproc;
+
+	/*
+	 * New process spawned by parent but not sharing the same mm.
+	 * 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);
+	list_for_each_entry(ppt, &uproc->uprobe_list, ut_node) {
+		ret = __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.
+ *
+ *  If its the first time the probepoint is hit, slot gets allocated here.
+ *  If its the first time the thread hit a breakpoint, utask gets
+ *  allocated here.
+ */
+void uprobe_notify_resume(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;
+	uproc = current->mm->uproc;
+	if (unlikely(!utask)) {
+		utask = add_utask(uproc);
+
+		/* Failed to allocate utask for the current task. */
+		BUG_ON(!utask);
+		probept = uprobes_get_bkpt_addr(regs);
+		ppt = find_probept(uproc, probept);
+
+		/*
+		 * The probept was refcounted in uprobe_bkpt_notifier;
+		 * Hence it would be mysterious to miss ppt now
+		 */
+		WARN_ON(!ppt);
+		utask->active_ppt = ppt;
+		utask->state = UTASK_BP_HIT;
+	} else
+		ppt = utask->active_ppt;
+
+	if (utask->state == UTASK_BP_HIT) {
+		utask->state = UTASK_SSTEP;
+		u = ppt->uprobe;
+		if (u && u->handler)
+			u->handler(u, regs);
+
+		if (!pre_ssout(ppt, regs))
+			arch_uprobe_enable_sstep(regs);
+	} else if (utask->state == UTASK_SSTEP) {
+		if (sstep_complete(regs, ppt)) {
+			put_probept(ppt);
+			utask->active_ppt = NULL;
+			utask->state = UTASK_RUNNING;
+			arch_uprobe_disable_sstep(regs);
+		}
+	}
+}
+
+/*
+ * uprobe_bkpt_notifier gets called from interrupt context
+ * it gets a reference to the ppt and sets TIF_UPROBE flag,
+ */
+int uprobe_bkpt_notifier(struct pt_regs *regs)
+{
+	struct uprobe_process *uproc;
+	struct uprobe_probept *ppt;
+	struct uprobe_task *utask;
+	unsigned long probept;
+
+	if (!current->mm || !current->mm->uproc)
+		/* task is currently not uprobed */
+		return 0;
+
+	uproc = current->mm->uproc;
+	utask = current->utask;
+	probept = uprobes_get_bkpt_addr(regs);
+	ppt = find_probept(uproc, probept);
+	if (!ppt)
+		return 0;
+	get_probept(ppt);
+	if (utask) {
+		utask->active_ppt = ppt;
+		utask->state = UTASK_BP_HIT;
+	}
+#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
+	set_thread_flag(TIF_UPROBE);
+#endif
+	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_probept *ppt;
+	struct uprobe_task *utask;
+
+	if (!current->mm || !current->mm->uproc || !current->utask)
+		/* task is currently not uprobed */
+		return 0;
+
+	utask = current->utask;
+
+	ppt = utask->active_ppt;
+	if (!ppt)
+		return 0;
+
+	if (uprobes_resume_can_sleep(&ppt->user_bkpt)) {
+#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
+		set_thread_flag(TIF_UPROBE);
+#endif
+		return 1;
+	}
+	if (sstep_complete(regs, ppt)) {
+		put_probept(ppt);
+		arch_uprobe_disable_sstep(regs);
+		utask->active_ppt = NULL;
+		utask->state = UTASK_RUNNING;
+		return 1;
+	}
+	return 0;
+}
+
 static int __init init_uprobes(void)
 {
 	int result = 0;
@@ -752,7 +1392,14 @@ static int __init init_uprobes(void)
 	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);
+
+	register_die_notifier(&uprobes_exception_nb);
 	return result;
 }
 
+static void __exit exit_uprobes(void)
+{
+}
+
 module_init(init_uprobes);
+module_exit(exit_uprobes);

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

* [PATCHv11 2.6.36-rc2-tip 6/15]  6: uprobes: X86 support for Uprobes
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (4 preceding siblings ...)
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 5/15] 5: uprobes: Uprobes (un)registration and exception handling Srikar Dronamraju
@ 2010-08-25 13:42 ` Srikar Dronamraju
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 7/15] 7: uprobes: Uprobes Documentation Srikar Dronamraju
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


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

Changelog from V5: Using local_irq_enable() instead of
    native_irq_enable and no more disabling irqs as suggested by Oleg
    Nesterov.

 arch/x86/kernel/signal.c  |   13 +++++++++++
 arch/x86/kernel/uprobes.c |   52 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 0 deletions(-)

diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index 4fd173c..3657563 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -848,6 +848,19 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
 	if (thread_info_flags & _TIF_SIGPENDING)
 		do_signal(regs);
 
+	if (thread_info_flags & _TIF_UPROBE) {
+		clear_thread_flag(TIF_UPROBE);
+#ifdef CONFIG_X86_32
+		/*
+		 * On x86_32, do_notify_resume() gets called with
+		 * interrupts disabled. Hence enable interrupts if they
+		 * are still disabled.
+		 */
+		local_irq_enable();
+#endif
+		uprobe_notify_resume(regs);
+	}
+
 	if (thread_info_flags & _TIF_NOTIFY_RESUME) {
 		clear_thread_flag(TIF_NOTIFY_RESUME);
 		tracehook_notify_resume(regs);
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index ceaedc9..6985b4c 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -26,6 +26,7 @@
 #include <linux/ptrace.h>
 #include <linux/uprobes.h>
 
+#include <linux/kdebug.h>
 #include <asm/insn.h>
 
 #ifdef CONFIG_X86_32
@@ -559,3 +560,54 @@ struct user_bkpt_arch_info user_bkpt_arch_info = {
 	.ip_advancement_by_bkpt_insn = 1,
 	.max_insn_bytes = MAX_UINSN_BYTES,
 };
+
+/*
+ * 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;
+}

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

* [PATCHv11 2.6.36-rc2-tip 7/15]  7: uprobes: Uprobes Documentation
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (5 preceding siblings ...)
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 6/15] 6: uprobes: X86 support for Uprobes Srikar Dronamraju
@ 2010-08-25 13:42 ` Srikar Dronamraju
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 8/15] 8: tracing: Extract out common code for kprobes/uprobes traceevents Srikar Dronamraju
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


Uprobes Documentation.

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

Changelog from V5: Removed references to Modules, Samples, and
   probe Overhead.

Changelog from v3: Updated measurements.

Changelog from v2: Updated measurements.

Changelog from v1: Addressed comments from Randy Dunlap.
		 : Updated measurements.

 Documentation/uprobes.txt |  188 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 188 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..5b620d8
--- /dev/null
+++ b/Documentation/uprobes.txt
@@ -0,0 +1,188 @@
+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. API Reference
+4. Uprobes Features and Limitations
+5. TODO
+6. Uprobes Team
+
+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.
+
+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. 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.
+
+3.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.
+
+3.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.
+
+4. Uprobes Features and Limitations
+
+The user is expected to assign values to the following members
+of struct uprobe: pid, vaddr, handler.
+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.
+
+Uprobe clients may be used to probe a particular process
+simultaneously, or 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 background page
+replacement  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.
+
+5. TODO
+
+a. Support for other architectures.
+b. Support for multiple probes at the same address.
+c. Support for boosted probes.
+d. Support return probes.
+
+6. 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

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

* [PATCHv11 2.6.36-rc2-tip 8/15]  8: tracing: Extract out common code for kprobes/uprobes traceevents.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (6 preceding siblings ...)
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 7/15] 7: uprobes: Uprobes Documentation Srikar Dronamraju
@ 2010-08-25 13:42 ` Srikar Dronamraju
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 9/15] 9: tracing: uprobes trace_event interface Srikar Dronamraju
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:42 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


Move parts of trace_kprobe.c that can be shared with upcoming
trace_uprobe.c. Common code to kernel/trace/trace_probe.h and
kernel/trace/trace_probe.c.

TODO: Merge both events to a single probe event.

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

Changelog from V10: delete references to is_kprobe and make
         is_return a bool.

Changelog from V7: Merge changes due to string support in kprobes
	traceevent.

Changelog from V5: Addressed comments from Masami Hiramatsu
	and Steven Rostedt. Also shared lot more code from kprobes
        traceevents.

 kernel/trace/Kconfig        |    4 
 kernel/trace/Makefile       |    1 
 kernel/trace/trace_kprobe.c |  752 +------------------------------------------
 kernel/trace/trace_probe.c  |  648 +++++++++++++++++++++++++++++++++++++
 kernel/trace/trace_probe.h  |  155 +++++++++
 5 files changed, 822 insertions(+), 738 deletions(-)
 create mode 100644 kernel/trace/trace_probe.c
 create mode 100644 kernel/trace/trace_probe.h

diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 538501c..d709697 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -353,6 +353,7 @@ config KPROBE_EVENT
 	depends on HAVE_REGS_AND_STACK_ACCESS_API
 	bool "Enable kprobes-based dynamic events"
 	select TRACING
+	select PROBE_EVENTS
 	default y
 	help
 	  This allows the user to add tracing events (similar to tracepoints)
@@ -365,6 +366,9 @@ config KPROBE_EVENT
 	  This option is also required by perf-probe subcommand of perf tools.
 	  If you want to use perf tools, this option is strongly recommended.
 
+config PROBE_EVENTS
+	def_bool n
+
 config DYNAMIC_FTRACE
 	bool "enable/disable ftrace tracepoints dynamically"
 	depends on FUNCTION_TRACER
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 53f3381..95d2043 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -56,5 +56,6 @@ obj-$(CONFIG_EVENT_TRACING) += power-traces.o
 ifeq ($(CONFIG_TRACING),y)
 obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
 endif
+obj-$(CONFIG_PROBE_EVENTS) +=trace_probe.o
 
 libftrace-y := ftrace.o
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 8b27c98..e66450d 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -19,463 +19,15 @@
 
 #include <linux/module.h>
 #include <linux/uaccess.h>
-#include <linux/kprobes.h>
-#include <linux/seq_file.h>
-#include <linux/slab.h>
-#include <linux/smp.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 <linux/stringify.h>
-#include <linux/limits.h>
-#include <linux/uaccess.h>
-#include <asm/bitsperlong.h>
 
-#include "trace.h"
-#include "trace_output.h"
+#include "trace_probe.h"
 
-#define MAX_TRACE_ARGS 128
-#define MAX_ARGSTR_LEN 63
-#define MAX_EVENT_NAME_LEN 64
-#define MAX_STRING_SIZE PATH_MAX
 #define KPROBE_EVENT_SYSTEM "kprobes"
 
-/* Reserved field names */
-#define FIELD_STRING_IP "__probe_ip"
-#define FIELD_STRING_RETIP "__probe_ret_ip"
-#define FIELD_STRING_FUNC "__probe_func"
-
-const char *reserved_field_names[] = {
-	"common_type",
-	"common_flags",
-	"common_preempt_count",
-	"common_pid",
-	"common_tgid",
-	"common_lock_depth",
-	FIELD_STRING_IP,
-	FIELD_STRING_RETIP,
-	FIELD_STRING_FUNC,
-};
-
-/* Printing function type */
-typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *,
-				 void *);
-#define PRINT_TYPE_FUNC_NAME(type)	print_type_##type
-#define PRINT_TYPE_FMT_NAME(type)	print_type_format_##type
-
-/* Printing  in basic type function template */
-#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast)			\
-static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,	\
-						const char *name,	\
-						void *data, void *ent)\
-{									\
-	return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
-}									\
-static const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
-
-DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)
-DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)
-DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)
-DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)
-DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)
-DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
-DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
-DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
-
-/* data_rloc: data relative location, compatible with u32 */
-#define make_data_rloc(len, roffs)	\
-	(((u32)(len) << 16) | ((u32)(roffs) & 0xffff))
-#define get_rloc_len(dl)	((u32)(dl) >> 16)
-#define get_rloc_offs(dl)	((u32)(dl) & 0xffff)
-
-static inline void *get_rloc_data(u32 *dl)
-{
-	return (u8 *)dl + get_rloc_offs(*dl);
-}
-
-/* For data_loc conversion */
-static inline void *get_loc_data(u32 *dl, void *ent)
-{
-	return (u8 *)ent + get_rloc_offs(*dl);
-}
-
-/*
- * Convert data_rloc to data_loc:
- *  data_rloc stores the offset from data_rloc itself, but data_loc
- *  stores the offset from event entry.
- */
-#define convert_rloc_to_loc(dl, offs)	((u32)(dl) + (offs))
-
-/* For defining macros, define string/string_size types */
-typedef u32 string;
-typedef u32 string_size;
-
-/* Print type function for string type */
-static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
-						  const char *name,
-						  void *data, void *ent)
-{
-	int len = *(u32 *)data >> 16;
-
-	if (!len)
-		return trace_seq_printf(s, " %s=(fault)", name);
-	else
-		return trace_seq_printf(s, " %s=\"%s\"", name,
-					(const char *)get_loc_data(data, ent));
-}
-static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
-
-/* Data fetch function type */
-typedef	void (*fetch_func_t)(struct pt_regs *, void *, void *);
-
-struct fetch_param {
-	fetch_func_t	fn;
-	void *data;
-};
-
-static __kprobes void call_fetch(struct fetch_param *fprm,
-				 struct pt_regs *regs, void *dest)
-{
-	return fprm->fn(regs, fprm->data, dest);
-}
-
-#define FETCH_FUNC_NAME(method, type)	fetch_##method##_##type
-/*
- * Define macro for basic types - we don't need to define s* types, because
- * we have to care only about bitwidth at recording time.
- */
-#define DEFINE_BASIC_FETCH_FUNCS(method) \
-DEFINE_FETCH_##method(u8)		\
-DEFINE_FETCH_##method(u16)		\
-DEFINE_FETCH_##method(u32)		\
-DEFINE_FETCH_##method(u64)
-
-#define CHECK_FETCH_FUNCS(method, fn)			\
-	(((FETCH_FUNC_NAME(method, u8) == fn) ||	\
-	  (FETCH_FUNC_NAME(method, u16) == fn) ||	\
-	  (FETCH_FUNC_NAME(method, u32) == fn) ||	\
-	  (FETCH_FUNC_NAME(method, u64) == fn) ||	\
-	  (FETCH_FUNC_NAME(method, string) == fn) ||	\
-	  (FETCH_FUNC_NAME(method, string_size) == fn)) \
-	 && (fn != NULL))
-
-/* Data fetch function templates */
-#define DEFINE_FETCH_reg(type)						\
-static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs,	\
-					void *offset, void *dest)	\
-{									\
-	*(type *)dest = (type)regs_get_register(regs,			\
-				(unsigned int)((unsigned long)offset));	\
-}
-DEFINE_BASIC_FETCH_FUNCS(reg)
-/* No string on the register */
-#define fetch_reg_string NULL
-#define fetch_reg_string_size NULL
-
-#define DEFINE_FETCH_stack(type)					\
-static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
-					  void *offset, void *dest)	\
-{									\
-	*(type *)dest = (type)regs_get_kernel_stack_nth(regs,		\
-				(unsigned int)((unsigned long)offset));	\
-}
-DEFINE_BASIC_FETCH_FUNCS(stack)
-/* No string on the stack entry */
-#define fetch_stack_string NULL
-#define fetch_stack_string_size NULL
-
-#define DEFINE_FETCH_retval(type)					\
-static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
-					  void *dummy, void *dest)	\
-{									\
-	*(type *)dest = (type)regs_return_value(regs);			\
-}
-DEFINE_BASIC_FETCH_FUNCS(retval)
-/* No string on the retval */
-#define fetch_retval_string NULL
-#define fetch_retval_string_size NULL
-
-#define DEFINE_FETCH_memory(type)					\
-static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
-					  void *addr, void *dest)	\
-{									\
-	type retval;							\
-	if (probe_kernel_address(addr, retval))				\
-		*(type *)dest = 0;					\
-	else								\
-		*(type *)dest = retval;					\
-}
-DEFINE_BASIC_FETCH_FUNCS(memory)
-/*
- * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
- * length and relative data location.
- */
-static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
-						      void *addr, void *dest)
-{
-	long ret;
-	int maxlen = get_rloc_len(*(u32 *)dest);
-	u8 *dst = get_rloc_data(dest);
-	u8 *src = addr;
-	mm_segment_t old_fs = get_fs();
-	if (!maxlen)
-		return;
-	/*
-	 * Try to get string again, since the string can be changed while
-	 * probing.
-	 */
-	set_fs(KERNEL_DS);
-	pagefault_disable();
-	do
-		ret = __copy_from_user_inatomic(dst++, src++, 1);
-	while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen);
-	dst[-1] = '\0';
-	pagefault_enable();
-	set_fs(old_fs);
-
-	if (ret < 0) {	/* Failed to fetch string */
-		((u8 *)get_rloc_data(dest))[0] = '\0';
-		*(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
-	} else
-		*(u32 *)dest = make_data_rloc(src - (u8 *)addr,
-					      get_rloc_offs(*(u32 *)dest));
-}
-/* Return the length of string -- including null terminal byte */
-static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
-							void *addr, void *dest)
-{
-	int ret, len = 0;
-	u8 c;
-	mm_segment_t old_fs = get_fs();
-
-	set_fs(KERNEL_DS);
-	pagefault_disable();
-	do {
-		ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1);
-		len++;
-	} while (c && ret == 0 && len < MAX_STRING_SIZE);
-	pagefault_enable();
-	set_fs(old_fs);
-
-	if (ret < 0)	/* Failed to check the length */
-		*(u32 *)dest = 0;
-	else
-		*(u32 *)dest = len;
-}
-
-/* Memory fetching by symbol */
-struct symbol_cache {
-	char *symbol;
-	long offset;
-	unsigned long addr;
-};
-
-static unsigned long update_symbol_cache(struct symbol_cache *sc)
-{
-	sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol);
-	if (sc->addr)
-		sc->addr += sc->offset;
-	return sc->addr;
-}
-
-static void free_symbol_cache(struct symbol_cache *sc)
-{
-	kfree(sc->symbol);
-	kfree(sc);
-}
-
-static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
-{
-	struct symbol_cache *sc;
-
-	if (!sym || strlen(sym) == 0)
-		return NULL;
-	sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL);
-	if (!sc)
-		return NULL;
-
-	sc->symbol = kstrdup(sym, GFP_KERNEL);
-	if (!sc->symbol) {
-		kfree(sc);
-		return NULL;
-	}
-	sc->offset = offset;
-
-	update_symbol_cache(sc);
-	return sc;
-}
-
-#define DEFINE_FETCH_symbol(type)					\
-static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
-					  void *data, void *dest)	\
-{									\
-	struct symbol_cache *sc = data;					\
-	if (sc->addr)							\
-		fetch_memory_##type(regs, (void *)sc->addr, dest);	\
-	else								\
-		*(type *)dest = 0;					\
-}
-DEFINE_BASIC_FETCH_FUNCS(symbol)
-DEFINE_FETCH_symbol(string)
-DEFINE_FETCH_symbol(string_size)
-
-/* Dereference memory access function */
-struct deref_fetch_param {
-	struct fetch_param orig;
-	long offset;
-};
-
-#define DEFINE_FETCH_deref(type)					\
-static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
-					    void *data, void *dest)	\
-{									\
-	struct deref_fetch_param *dprm = data;				\
-	unsigned long addr;						\
-	call_fetch(&dprm->orig, regs, &addr);				\
-	if (addr) {							\
-		addr += dprm->offset;					\
-		fetch_memory_##type(regs, (void *)addr, dest);		\
-	} else								\
-		*(type *)dest = 0;					\
-}
-DEFINE_BASIC_FETCH_FUNCS(deref)
-DEFINE_FETCH_deref(string)
-DEFINE_FETCH_deref(string_size)
-
-static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
-{
-	if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
-		free_deref_fetch_param(data->orig.data);
-	else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
-		free_symbol_cache(data->orig.data);
-	kfree(data);
-}
-
-/* Default (unsigned long) fetch type */
-#define __DEFAULT_FETCH_TYPE(t) u##t
-#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
-#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
-#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
-
-/* Fetch types */
-enum {
-	FETCH_MTD_reg = 0,
-	FETCH_MTD_stack,
-	FETCH_MTD_retval,
-	FETCH_MTD_memory,
-	FETCH_MTD_symbol,
-	FETCH_MTD_deref,
-	FETCH_MTD_END,
-};
-
-#define ASSIGN_FETCH_FUNC(method, type)	\
-	[FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type)
-
-#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype)	\
-	{.name = _name,				\
-	 .size = _size,					\
-	 .is_signed = sign,				\
-	 .print = PRINT_TYPE_FUNC_NAME(ptype),		\
-	 .fmt = PRINT_TYPE_FMT_NAME(ptype),		\
-	 .fmttype = _fmttype,				\
-	 .fetch = {					\
-ASSIGN_FETCH_FUNC(reg, ftype),				\
-ASSIGN_FETCH_FUNC(stack, ftype),			\
-ASSIGN_FETCH_FUNC(retval, ftype),			\
-ASSIGN_FETCH_FUNC(memory, ftype),			\
-ASSIGN_FETCH_FUNC(symbol, ftype),			\
-ASSIGN_FETCH_FUNC(deref, ftype),			\
-	  }						\
-	}
-
-#define ASSIGN_FETCH_TYPE(ptype, ftype, sign)			\
-	__ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype)
-
-#define FETCH_TYPE_STRING 0
-#define FETCH_TYPE_STRSIZE 1
-
-/* Fetch type information table */
-static const struct fetch_type {
-	const char	*name;		/* Name of type */
-	size_t		size;		/* Byte size of type */
-	int		is_signed;	/* Signed flag */
-	print_type_func_t	print;	/* Print functions */
-	const char	*fmt;		/* Fromat string */
-	const char	*fmttype;	/* Name in format file */
-	/* Fetch functions */
-	fetch_func_t	fetch[FETCH_MTD_END];
-} fetch_type_table[] = {
-	/* Special types */
-	[FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string,
-					sizeof(u32), 1, "__data_loc char[]"),
-	[FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32,
-					string_size, sizeof(u32), 0, "u32"),
-	/* Basic types */
-	ASSIGN_FETCH_TYPE(u8,  u8,  0),
-	ASSIGN_FETCH_TYPE(u16, u16, 0),
-	ASSIGN_FETCH_TYPE(u32, u32, 0),
-	ASSIGN_FETCH_TYPE(u64, u64, 0),
-	ASSIGN_FETCH_TYPE(s8,  u8,  1),
-	ASSIGN_FETCH_TYPE(s16, u16, 1),
-	ASSIGN_FETCH_TYPE(s32, u32, 1),
-	ASSIGN_FETCH_TYPE(s64, u64, 1),
-};
-
-static const struct fetch_type *find_fetch_type(const char *type)
-{
-	int i;
-
-	if (!type)
-		type = DEFAULT_FETCH_TYPE_STR;
-
-	for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
-		if (strcmp(type, fetch_type_table[i].name) == 0)
-			return &fetch_type_table[i];
-	return NULL;
-}
-
-/* Special function : only accept unsigned long */
-static __kprobes void fetch_stack_address(struct pt_regs *regs,
-					  void *dummy, void *dest)
-{
-	*(unsigned long *)dest = kernel_stack_pointer(regs);
-}
-
-static fetch_func_t get_fetch_size_function(const struct fetch_type *type,
-					    fetch_func_t orig_fn)
-{
-	int i;
-
-	if (type != &fetch_type_table[FETCH_TYPE_STRING])
-		return NULL;	/* Only string type needs size function */
-	for (i = 0; i < FETCH_MTD_END; i++)
-		if (type->fetch[i] == orig_fn)
-			return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i];
-
-	WARN_ON(1);	/* This should not happen */
-	return NULL;
-}
-
 /**
  * Kprobe event core functions
  */
 
-struct probe_arg {
-	struct fetch_param	fetch;
-	struct fetch_param	fetch_size;
-	unsigned int		offset;	/* Offset from argument entry */
-	const char		*name;	/* Name of this argument */
-	const char		*comm;	/* Command of this argument */
-	const struct fetch_type	*type;	/* Type of this argument */
-};
-
-/* Flags for trace_probe */
-#define TP_FLAG_TRACE	1
-#define TP_FLAG_PROFILE	2
-
 struct trace_probe {
 	struct list_head	list;
 	struct kretprobe	rp;	/* Use rp.kp for kprobe use */
@@ -513,19 +65,6 @@ static LIST_HEAD(probe_list);
 static int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs);
 static int kretprobe_dispatcher(struct kretprobe_instance *ri,
 				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_probe and initialize it (including kprobes).
  */
@@ -534,7 +73,7 @@ static struct trace_probe *alloc_trace_probe(const char *group,
 					     void *addr,
 					     const char *symbol,
 					     unsigned long offs,
-					     int nargs, int is_return)
+					     int nargs, bool is_return)
 {
 	struct trace_probe *tp;
 	int ret = -ENOMEM;
@@ -585,22 +124,12 @@ error:
 	return ERR_PTR(ret);
 }
 
-static void free_probe_arg(struct probe_arg *arg)
-{
-	if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
-		free_deref_fetch_param(arg->fetch.data);
-	else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
-		free_symbol_cache(arg->fetch.data);
-	kfree(arg->name);
-	kfree(arg->comm);
-}
-
 static void free_trace_probe(struct trace_probe *tp)
 {
 	int i;
 
 	for (i = 0; i < tp->nr_args; i++)
-		free_probe_arg(&tp->args[i]);
+		traceprobe_free_probe_arg(&tp->args[i]);
 
 	kfree(tp->call.class->system);
 	kfree(tp->call.name);
@@ -674,191 +203,6 @@ end:
 	return ret;
 }
 
-/* Split symbol and offset. */
-static int split_symbol_offset(char *symbol, unsigned long *offset)
-{
-	char *tmp;
-	int ret;
-
-	if (!offset)
-		return -EINVAL;
-
-	tmp = strchr(symbol, '+');
-	if (tmp) {
-		/* skip sign because strict_strtol doesn't accept '+' */
-		ret = strict_strtoul(tmp + 1, 0, offset);
-		if (ret)
-			return ret;
-		*tmp = '\0';
-	} else
-		*offset = 0;
-	return 0;
-}
-
-#define PARAM_MAX_ARGS 16
-#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
-
-static int parse_probe_vars(char *arg, const struct fetch_type *t,
-			    struct fetch_param *f, int is_return)
-{
-	int ret = 0;
-	unsigned long param;
-
-	if (strcmp(arg, "retval") == 0) {
-		if (is_return)
-			f->fn = t->fetch[FETCH_MTD_retval];
-		else
-			ret = -EINVAL;
-	} else if (strncmp(arg, "stack", 5) == 0) {
-		if (arg[5] == '\0') {
-			if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0)
-				f->fn = fetch_stack_address;
-			else
-				ret = -EINVAL;
-		} else if (isdigit(arg[5])) {
-			ret = strict_strtoul(arg + 5, 10, &param);
-			if (ret || param > PARAM_MAX_STACK)
-				ret = -EINVAL;
-			else {
-				f->fn = t->fetch[FETCH_MTD_stack];
-				f->data = (void *)param;
-			}
-		} else
-			ret = -EINVAL;
-	} else
-		ret = -EINVAL;
-	return ret;
-}
-
-/* Recursive argument parser */
-static int __parse_probe_arg(char *arg, const struct fetch_type *t,
-			     struct fetch_param *f, int is_return)
-{
-	int ret = 0;
-	unsigned long param;
-	long offset;
-	char *tmp;
-
-	switch (arg[0]) {
-	case '$':
-		ret = parse_probe_vars(arg + 1, t, f, is_return);
-		break;
-	case '%':	/* named register */
-		ret = regs_query_register_offset(arg + 1);
-		if (ret >= 0) {
-			f->fn = t->fetch[FETCH_MTD_reg];
-			f->data = (void *)(unsigned long)ret;
-			ret = 0;
-		}
-		break;
-	case '@':	/* memory or symbol */
-		if (isdigit(arg[1])) {
-			ret = strict_strtoul(arg + 1, 0, &param);
-			if (ret)
-				break;
-			f->fn = t->fetch[FETCH_MTD_memory];
-			f->data = (void *)param;
-		} else {
-			ret = split_symbol_offset(arg + 1, &offset);
-			if (ret)
-				break;
-			f->data = alloc_symbol_cache(arg + 1, offset);
-			if (f->data)
-				f->fn = t->fetch[FETCH_MTD_symbol];
-		}
-		break;
-	case '+':	/* deref memory */
-	case '-':
-		tmp = strchr(arg, '(');
-		if (!tmp)
-			break;
-		*tmp = '\0';
-		ret = strict_strtol(arg + 1, 0, &offset);
-		if (ret)
-			break;
-		if (arg[0] == '-')
-			offset = -offset;
-		arg = tmp + 1;
-		tmp = strrchr(arg, ')');
-		if (tmp) {
-			struct deref_fetch_param *dprm;
-			const struct fetch_type *t2 = find_fetch_type(NULL);
-			*tmp = '\0';
-			dprm = kzalloc(sizeof(struct deref_fetch_param),
-				       GFP_KERNEL);
-			if (!dprm)
-				return -ENOMEM;
-			dprm->offset = offset;
-			ret = __parse_probe_arg(arg, t2, &dprm->orig,
-						is_return);
-			if (ret)
-				kfree(dprm);
-			else {
-				f->fn = t->fetch[FETCH_MTD_deref];
-				f->data = (void *)dprm;
-			}
-		}
-		break;
-	}
-	if (!ret && !f->fn) {	/* Parsed, but do not find fetch method */
-		pr_info("%s type has no corresponding fetch method.\n",
-			t->name);
-		ret = -EINVAL;
-	}
-	return ret;
-}
-
-/* String length checking wrapper */
-static int parse_probe_arg(char *arg, struct trace_probe *tp,
-			   struct probe_arg *parg, int is_return)
-{
-	const char *t;
-	int ret;
-
-	if (strlen(arg) > MAX_ARGSTR_LEN) {
-		pr_info("Argument is too long.: %s\n",  arg);
-		return -ENOSPC;
-	}
-	parg->comm = kstrdup(arg, GFP_KERNEL);
-	if (!parg->comm) {
-		pr_info("Failed to allocate memory for command '%s'.\n", arg);
-		return -ENOMEM;
-	}
-	t = strchr(parg->comm, ':');
-	if (t) {
-		arg[t - parg->comm] = '\0';
-		t++;
-	}
-	parg->type = find_fetch_type(t);
-	if (!parg->type) {
-		pr_info("Unsupported type: %s\n", t);
-		return -EINVAL;
-	}
-	parg->offset = tp->size;
-	tp->size += parg->type->size;
-	ret = __parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
-	if (ret >= 0) {
-		parg->fetch_size.fn = get_fetch_size_function(parg->type,
-							      parg->fetch.fn);
-		parg->fetch_size.data = parg->fetch.data;
-	}
-	return ret;
-}
-
-/* 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(reserved_field_names); i++)
-		if (strcmp(reserved_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_probe(int argc, char **argv)
 {
 	/*
@@ -881,7 +225,7 @@ static int create_trace_probe(int argc, char **argv)
 	 */
 	struct trace_probe *tp;
 	int i, ret = 0;
-	int is_return = 0, is_delete = 0;
+	bool is_return = false, is_delete = false;
 	char *symbol = NULL, *event = NULL, *group = NULL;
 	char *arg, *tmp;
 	unsigned long offset = 0;
@@ -890,11 +234,11 @@ static int create_trace_probe(int argc, char **argv)
 
 	/* argc must be >= 1 */
 	if (argv[0][0] == 'p')
-		is_return = 0;
+		is_return = false;
 	else if (argv[0][0] == 'r')
-		is_return = 1;
+		is_return = true;
 	else if (argv[0][0] == '-')
-		is_delete = 1;
+		is_delete = true;
 	else {
 		pr_info("Probe definition must be started with 'p', 'r' or"
 			" '-'.\n");
@@ -958,7 +302,7 @@ static int create_trace_probe(int argc, char **argv)
 		/* a symbol specified */
 		symbol = argv[1];
 		/* TODO: support .init module functions */
-		ret = split_symbol_offset(symbol, &offset);
+		ret = traceprobe_split_symbol_offset(symbol, &offset);
 		if (ret) {
 			pr_info("Failed to parse symbol.\n");
 			return ret;
@@ -1010,7 +354,8 @@ static int create_trace_probe(int argc, char **argv)
 		if (tmp)
 			*tmp = '_';	/* convert : to _ */
 
-		if (conflict_field_name(tp->args[i].name, tp->args, i)) {
+		if (traceprobe_conflict_field_name(tp->args[i].name,
+							tp->args, i)) {
 			pr_info("Argument%d name '%s' conflicts with "
 				"another field.\n", i, argv[i]);
 			ret = -EINVAL;
@@ -1018,7 +363,8 @@ static int create_trace_probe(int argc, char **argv)
 		}
 
 		/* Parse fetch argument */
-		ret = parse_probe_arg(arg, tp, &tp->args[i], is_return);
+		ret = traceprobe_parse_probe_arg(arg, &tp->size, &tp->args[i],
+								is_return);
 		if (ret) {
 			pr_info("Parse error at argument%d. (%d)\n", i, ret);
 			kfree(tp->args[i].name);
@@ -1108,70 +454,11 @@ static int probes_open(struct inode *inode, struct file *file)
 	return seq_open(file, &probes_seq_op);
 }
 
-static int command_trace_probe(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_probe(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_probe(kbuf);
-		if (ret)
-			goto out;
-	}
-	ret = done;
-out:
-	kfree(kbuf);
-	return ret;
+	return traceprobe_probes_write(file, buffer, count, ppos,
+			create_trace_probe);
 }
 
 static const struct file_operations kprobe_events_ops = {
@@ -1429,17 +716,6 @@ static void probe_event_disable(struct ftrace_event_call *call)
 	}
 }
 
-#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 kprobe_event_define_fields(struct ftrace_event_call *event_call)
 {
 	int ret, i;
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
new file mode 100644
index 0000000..4ca48c6
--- /dev/null
+++ b/kernel/trace/trace_probe.c
@@ -0,0 +1,648 @@
+/*
+ * Common code for probe-based Dynamic 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
+ *
+ * Derived from kernel/trace/trace_kprobe.c written by
+ * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+ */
+
+#include "trace_probe.h"
+
+const char *reserved_field_names[] = {
+	"common_type",
+	"common_flags",
+	"common_preempt_count",
+	"common_pid",
+	"common_tgid",
+	"common_lock_depth",
+	FIELD_STRING_IP,
+	FIELD_STRING_RETIP,
+	FIELD_STRING_FUNC,
+};
+
+/* Printing function type */
+#define PRINT_TYPE_FUNC_NAME(type)	print_type_##type
+#define PRINT_TYPE_FMT_NAME(type)	print_type_format_##type
+
+/* Printing  in basic type function template */
+#define DEFINE_BASIC_PRINT_TYPE_FUNC(type, fmt, cast)			\
+static __kprobes int PRINT_TYPE_FUNC_NAME(type)(struct trace_seq *s,	\
+						const char *name,	\
+						void *data, void *ent)\
+{									\
+	return trace_seq_printf(s, " %s=" fmt, name, (cast)*(type *)data);\
+}									\
+static const char PRINT_TYPE_FMT_NAME(type)[] = fmt;
+
+DEFINE_BASIC_PRINT_TYPE_FUNC(u8, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u16, "%x", unsigned int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u32, "%lx", unsigned long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(u64, "%llx", unsigned long long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s8, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s16, "%d", int)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s32, "%ld", long)
+DEFINE_BASIC_PRINT_TYPE_FUNC(s64, "%lld", long long)
+
+static inline void *get_rloc_data(u32 *dl)
+{
+	return (u8 *)dl + get_rloc_offs(*dl);
+}
+
+/* For data_loc conversion */
+static inline void *get_loc_data(u32 *dl, void *ent)
+{
+	return (u8 *)ent + get_rloc_offs(*dl);
+}
+
+/* For defining macros, define string/string_size types */
+typedef u32 string;
+typedef u32 string_size;
+
+/* Print type function for string type */
+static __kprobes int PRINT_TYPE_FUNC_NAME(string)(struct trace_seq *s,
+						  const char *name,
+						  void *data, void *ent)
+{
+	int len = *(u32 *)data >> 16;
+
+	if (!len)
+		return trace_seq_printf(s, " %s=(fault)", name);
+	else
+		return trace_seq_printf(s, " %s=\"%s\"", name,
+					(const char *)get_loc_data(data, ent));
+}
+static const char PRINT_TYPE_FMT_NAME(string)[] = "\\\"%s\\\"";
+
+#define FETCH_FUNC_NAME(method, type)	fetch_##method##_##type
+/*
+ * Define macro for basic types - we don't need to define s* types, because
+ * we have to care only about bitwidth at recording time.
+ */
+#define DEFINE_BASIC_FETCH_FUNCS(method) \
+DEFINE_FETCH_##method(u8)		\
+DEFINE_FETCH_##method(u16)		\
+DEFINE_FETCH_##method(u32)		\
+DEFINE_FETCH_##method(u64)
+
+#define CHECK_FETCH_FUNCS(method, fn)			\
+	(((FETCH_FUNC_NAME(method, u8) == fn) ||	\
+	  (FETCH_FUNC_NAME(method, u16) == fn) ||	\
+	  (FETCH_FUNC_NAME(method, u32) == fn) ||	\
+	  (FETCH_FUNC_NAME(method, u64) == fn) ||	\
+	  (FETCH_FUNC_NAME(method, string) == fn) ||	\
+	  (FETCH_FUNC_NAME(method, string_size) == fn)) \
+	 && (fn != NULL))
+
+/* Data fetch function templates */
+#define DEFINE_FETCH_reg(type)						\
+static __kprobes void FETCH_FUNC_NAME(reg, type)(struct pt_regs *regs,	\
+					void *offset, void *dest)	\
+{									\
+	*(type *)dest = (type)regs_get_register(regs,			\
+				(unsigned int)((unsigned long)offset));	\
+}
+DEFINE_BASIC_FETCH_FUNCS(reg)
+/* No string on the register */
+#define fetch_reg_string NULL
+#define fetch_reg_string_size NULL
+
+#define DEFINE_FETCH_stack(type)					\
+static __kprobes void FETCH_FUNC_NAME(stack, type)(struct pt_regs *regs,\
+					  void *offset, void *dest)	\
+{									\
+	*(type *)dest = (type)regs_get_kernel_stack_nth(regs,		\
+				(unsigned int)((unsigned long)offset));	\
+}
+DEFINE_BASIC_FETCH_FUNCS(stack)
+/* No string on the stack entry */
+#define fetch_stack_string NULL
+#define fetch_stack_string_size NULL
+
+#define DEFINE_FETCH_retval(type)					\
+static __kprobes void FETCH_FUNC_NAME(retval, type)(struct pt_regs *regs,\
+					  void *dummy, void *dest)	\
+{									\
+	*(type *)dest = (type)regs_return_value(regs);			\
+}
+DEFINE_BASIC_FETCH_FUNCS(retval)
+/* No string on the retval */
+#define fetch_retval_string NULL
+#define fetch_retval_string_size NULL
+
+#define DEFINE_FETCH_memory(type)					\
+static __kprobes void FETCH_FUNC_NAME(memory, type)(struct pt_regs *regs,\
+					  void *addr, void *dest)	\
+{									\
+	type retval;							\
+	if (probe_kernel_address(addr, retval))				\
+		*(type *)dest = 0;					\
+	else								\
+		*(type *)dest = retval;					\
+}
+DEFINE_BASIC_FETCH_FUNCS(memory)
+/*
+ * Fetch a null-terminated string. Caller MUST set *(u32 *)dest with max
+ * length and relative data location.
+ */
+static __kprobes void FETCH_FUNC_NAME(memory, string)(struct pt_regs *regs,
+						      void *addr, void *dest)
+{
+	long ret;
+	int maxlen = get_rloc_len(*(u32 *)dest);
+	u8 *dst = get_rloc_data(dest);
+	u8 *src = addr;
+	mm_segment_t old_fs = get_fs();
+	if (!maxlen)
+		return;
+	/*
+	 * Try to get string again, since the string can be changed while
+	 * probing.
+	 */
+	set_fs(KERNEL_DS);
+	pagefault_disable();
+	do
+		ret = __copy_from_user_inatomic(dst++, src++, 1);
+	while (dst[-1] && ret == 0 && src - (u8 *)addr < maxlen);
+	dst[-1] = '\0';
+	pagefault_enable();
+	set_fs(old_fs);
+
+	if (ret < 0) {	/* Failed to fetch string */
+		((u8 *)get_rloc_data(dest))[0] = '\0';
+		*(u32 *)dest = make_data_rloc(0, get_rloc_offs(*(u32 *)dest));
+	} else
+		*(u32 *)dest = make_data_rloc(src - (u8 *)addr,
+					      get_rloc_offs(*(u32 *)dest));
+}
+/* Return the length of string -- including null terminal byte */
+static __kprobes void FETCH_FUNC_NAME(memory, string_size)(struct pt_regs *regs,
+							void *addr, void *dest)
+{
+	int ret, len = 0;
+	u8 c;
+	mm_segment_t old_fs = get_fs();
+
+	set_fs(KERNEL_DS);
+	pagefault_disable();
+	do {
+		ret = __copy_from_user_inatomic(&c, (u8 *)addr + len, 1);
+		len++;
+	} while (c && ret == 0 && len < MAX_STRING_SIZE);
+	pagefault_enable();
+	set_fs(old_fs);
+
+	if (ret < 0)	/* Failed to check the length */
+		*(u32 *)dest = 0;
+	else
+		*(u32 *)dest = len;
+}
+
+/* Memory fetching by symbol */
+struct symbol_cache {
+	char *symbol;
+	long offset;
+	unsigned long addr;
+};
+
+static unsigned long update_symbol_cache(struct symbol_cache *sc)
+{
+	sc->addr = (unsigned long)kallsyms_lookup_name(sc->symbol);
+	if (sc->addr)
+		sc->addr += sc->offset;
+	return sc->addr;
+}
+
+static void free_symbol_cache(struct symbol_cache *sc)
+{
+	kfree(sc->symbol);
+	kfree(sc);
+}
+
+static struct symbol_cache *alloc_symbol_cache(const char *sym, long offset)
+{
+	struct symbol_cache *sc;
+
+	if (!sym || strlen(sym) == 0)
+		return NULL;
+	sc = kzalloc(sizeof(struct symbol_cache), GFP_KERNEL);
+	if (!sc)
+		return NULL;
+
+	sc->symbol = kstrdup(sym, GFP_KERNEL);
+	if (!sc->symbol) {
+		kfree(sc);
+		return NULL;
+	}
+	sc->offset = offset;
+
+	update_symbol_cache(sc);
+	return sc;
+}
+
+#define DEFINE_FETCH_symbol(type)					\
+static __kprobes void FETCH_FUNC_NAME(symbol, type)(struct pt_regs *regs,\
+					  void *data, void *dest)	\
+{									\
+	struct symbol_cache *sc = data;					\
+	if (sc->addr)							\
+		fetch_memory_##type(regs, (void *)sc->addr, dest);	\
+	else								\
+		*(type *)dest = 0;					\
+}
+DEFINE_BASIC_FETCH_FUNCS(symbol)
+DEFINE_FETCH_symbol(string)
+DEFINE_FETCH_symbol(string_size)
+
+/* Dereference memory access function */
+struct deref_fetch_param {
+	struct fetch_param orig;
+	long offset;
+};
+
+#define DEFINE_FETCH_deref(type)					\
+static __kprobes void FETCH_FUNC_NAME(deref, type)(struct pt_regs *regs,\
+					    void *data, void *dest)	\
+{									\
+	struct deref_fetch_param *dprm = data;				\
+	unsigned long addr;						\
+	call_fetch(&dprm->orig, regs, &addr);				\
+	if (addr) {							\
+		addr += dprm->offset;					\
+		fetch_memory_##type(regs, (void *)addr, dest);		\
+	} else								\
+		*(type *)dest = 0;					\
+}
+DEFINE_BASIC_FETCH_FUNCS(deref)
+DEFINE_FETCH_deref(string)
+DEFINE_FETCH_deref(string_size)
+
+static __kprobes void free_deref_fetch_param(struct deref_fetch_param *data)
+{
+	if (CHECK_FETCH_FUNCS(deref, data->orig.fn))
+		free_deref_fetch_param(data->orig.data);
+	else if (CHECK_FETCH_FUNCS(symbol, data->orig.fn))
+		free_symbol_cache(data->orig.data);
+	kfree(data);
+}
+
+/* Default (unsigned long) fetch type */
+#define __DEFAULT_FETCH_TYPE(t) u##t
+#define _DEFAULT_FETCH_TYPE(t) __DEFAULT_FETCH_TYPE(t)
+#define DEFAULT_FETCH_TYPE _DEFAULT_FETCH_TYPE(BITS_PER_LONG)
+#define DEFAULT_FETCH_TYPE_STR __stringify(DEFAULT_FETCH_TYPE)
+
+#define ASSIGN_FETCH_FUNC(method, type)	\
+	[FETCH_MTD_##method] = FETCH_FUNC_NAME(method, type)
+
+#define __ASSIGN_FETCH_TYPE(_name, ptype, ftype, _size, sign, _fmttype)	\
+	{.name = _name,				\
+	 .size = _size,					\
+	 .is_signed = sign,				\
+	 .print = PRINT_TYPE_FUNC_NAME(ptype),		\
+	 .fmt = PRINT_TYPE_FMT_NAME(ptype),		\
+	 .fmttype = _fmttype,				\
+	 .fetch = {					\
+ASSIGN_FETCH_FUNC(reg, ftype),				\
+ASSIGN_FETCH_FUNC(stack, ftype),			\
+ASSIGN_FETCH_FUNC(retval, ftype),			\
+ASSIGN_FETCH_FUNC(memory, ftype),			\
+ASSIGN_FETCH_FUNC(symbol, ftype),			\
+ASSIGN_FETCH_FUNC(deref, ftype),			\
+	  }						\
+	}
+
+#define ASSIGN_FETCH_TYPE(ptype, ftype, sign)			\
+	__ASSIGN_FETCH_TYPE(#ptype, ptype, ftype, sizeof(ftype), sign, #ptype)
+
+#define FETCH_TYPE_STRING 0
+#define FETCH_TYPE_STRSIZE 1
+
+/* Fetch type information table */
+static const struct fetch_type fetch_type_table[] = {
+	/* Special types */
+	[FETCH_TYPE_STRING] = __ASSIGN_FETCH_TYPE("string", string, string,
+					sizeof(u32), 1, "__data_loc char[]"),
+	[FETCH_TYPE_STRSIZE] = __ASSIGN_FETCH_TYPE("string_size", u32,
+					string_size, sizeof(u32), 0, "u32"),
+	/* Basic types */
+	ASSIGN_FETCH_TYPE(u8,  u8,  0),
+	ASSIGN_FETCH_TYPE(u16, u16, 0),
+	ASSIGN_FETCH_TYPE(u32, u32, 0),
+	ASSIGN_FETCH_TYPE(u64, u64, 0),
+	ASSIGN_FETCH_TYPE(s8,  u8,  1),
+	ASSIGN_FETCH_TYPE(s16, u16, 1),
+	ASSIGN_FETCH_TYPE(s32, u32, 1),
+	ASSIGN_FETCH_TYPE(s64, u64, 1),
+};
+
+static const struct fetch_type *find_fetch_type(const char *type)
+{
+	int i;
+
+	if (!type)
+		type = DEFAULT_FETCH_TYPE_STR;
+
+	for (i = 0; i < ARRAY_SIZE(fetch_type_table); i++)
+		if (strcmp(type, fetch_type_table[i].name) == 0)
+			return &fetch_type_table[i];
+	return NULL;
+}
+
+/* Special function : only accept unsigned long */
+static __kprobes void fetch_stack_address(struct pt_regs *regs,
+					  void *dummy, void *dest)
+{
+	*(unsigned long *)dest = kernel_stack_pointer(regs);
+}
+
+static fetch_func_t get_fetch_size_function(const struct fetch_type *type,
+					    fetch_func_t orig_fn)
+{
+	int i;
+
+	if (type != &fetch_type_table[FETCH_TYPE_STRING])
+		return NULL;	/* Only string type needs size function */
+	for (i = 0; i < FETCH_MTD_END; i++)
+		if (type->fetch[i] == orig_fn)
+			return fetch_type_table[FETCH_TYPE_STRSIZE].fetch[i];
+
+	WARN_ON(1);	/* This should not happen */
+	return NULL;
+}
+
+/* Split symbol and offset. */
+int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset)
+{
+	char *tmp;
+	int ret;
+
+	if (!offset)
+		return -EINVAL;
+
+	tmp = strchr(symbol, '+');
+	if (tmp) {
+		/* skip sign because strict_strtol doesn't accept '+' */
+		ret = strict_strtoul(tmp + 1, 0, offset);
+		if (ret)
+			return ret;
+		*tmp = '\0';
+	} else
+		*offset = 0;
+	return 0;
+}
+
+
+#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
+
+static int parse_probe_vars(char *arg, const struct fetch_type *t,
+			    struct fetch_param *f, bool is_return)
+{
+	int ret = 0;
+	unsigned long param;
+
+	if (strcmp(arg, "retval") == 0) {
+		if (is_return)
+			f->fn = t->fetch[FETCH_MTD_retval];
+		else
+			ret = -EINVAL;
+	} else if (strncmp(arg, "stack", 5) == 0) {
+		if (arg[5] == '\0') {
+			if (strcmp(t->name, DEFAULT_FETCH_TYPE_STR) == 0)
+				f->fn = fetch_stack_address;
+			else
+				ret = -EINVAL;
+		} else if (isdigit(arg[5])) {
+			ret = strict_strtoul(arg + 5, 10, &param);
+			if (ret || param > PARAM_MAX_STACK)
+				ret = -EINVAL;
+			else {
+				f->fn = t->fetch[FETCH_MTD_stack];
+				f->data = (void *)param;
+			}
+		} else
+			ret = -EINVAL;
+	} else
+		ret = -EINVAL;
+	return ret;
+}
+
+/* Recursive argument parser */
+static int parse_probe_arg(char *arg, const struct fetch_type *t,
+		     struct fetch_param *f, bool is_return)
+{
+	int ret = 0;
+	unsigned long param;
+	long offset;
+	char *tmp;
+
+	switch (arg[0]) {
+	case '$':
+		ret = parse_probe_vars(arg + 1, t, f, is_return);
+		break;
+	case '%':	/* named register */
+		ret = regs_query_register_offset(arg + 1);
+		if (ret >= 0) {
+			f->fn = t->fetch[FETCH_MTD_reg];
+			f->data = (void *)(unsigned long)ret;
+			ret = 0;
+		}
+		break;
+	case '@':	/* memory or symbol */
+		if (isdigit(arg[1])) {
+			ret = strict_strtoul(arg + 1, 0, &param);
+			if (ret)
+				break;
+			f->fn = t->fetch[FETCH_MTD_memory];
+			f->data = (void *)param;
+		} else {
+			ret = traceprobe_split_symbol_offset(arg + 1, &offset);
+			if (ret)
+				break;
+			f->data = alloc_symbol_cache(arg + 1, offset);
+			if (f->data)
+				f->fn = t->fetch[FETCH_MTD_symbol];
+		}
+		break;
+	case '+':	/* deref memory */
+	case '-':
+		tmp = strchr(arg, '(');
+		if (!tmp)
+			break;
+		*tmp = '\0';
+		ret = strict_strtol(arg + 1, 0, &offset);
+		if (ret)
+			break;
+		if (arg[0] == '-')
+			offset = -offset;
+		arg = tmp + 1;
+		tmp = strrchr(arg, ')');
+		if (tmp) {
+			struct deref_fetch_param *dprm;
+			const struct fetch_type *t2 = find_fetch_type(NULL);
+			*tmp = '\0';
+			dprm = kzalloc(sizeof(struct deref_fetch_param),
+				       GFP_KERNEL);
+			if (!dprm)
+				return -ENOMEM;
+			dprm->offset = offset;
+			ret = parse_probe_arg(arg, t2, &dprm->orig, is_return);
+			if (ret)
+				kfree(dprm);
+			else {
+				f->fn = t->fetch[FETCH_MTD_deref];
+				f->data = (void *)dprm;
+			}
+		}
+		break;
+	}
+	if (!ret && !f->fn) {	/* Parsed, but do not find fetch method */
+		pr_info("%s type has no corresponding fetch method.\n",
+			t->name);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+/* String length checking wrapper */
+int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
+		struct probe_arg *parg, bool is_return)
+{
+	const char *t;
+	int ret;
+
+	if (strlen(arg) > MAX_ARGSTR_LEN) {
+		pr_info("Argument is too long.: %s\n",  arg);
+		return -ENOSPC;
+	}
+	parg->comm = kstrdup(arg, GFP_KERNEL);
+	if (!parg->comm) {
+		pr_info("Failed to allocate memory for command '%s'.\n", arg);
+		return -ENOMEM;
+	}
+	t = strchr(parg->comm, ':');
+	if (t) {
+		arg[t - parg->comm] = '\0';
+		t++;
+	}
+	parg->type = find_fetch_type(t);
+	if (!parg->type) {
+		pr_info("Unsupported type: %s\n", t);
+		return -EINVAL;
+	}
+	parg->offset = *size;
+	*size += parg->type->size;
+	ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
+
+	if (ret >= 0) {
+		parg->fetch_size.fn = get_fetch_size_function(parg->type,
+							      parg->fetch.fn);
+		parg->fetch_size.data = parg->fetch.data;
+	}
+	return ret;
+}
+
+/* Return 1 if name is reserved or already used by another argument */
+int traceprobe_conflict_field_name(const char *name,
+			       struct probe_arg *args, int narg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(reserved_field_names); i++)
+		if (strcmp(reserved_field_names[i], name) == 0)
+			return 1;
+	for (i = 0; i < narg; i++)
+		if (strcmp(args[i].name, name) == 0)
+			return 1;
+	return 0;
+}
+
+void traceprobe_free_probe_arg(struct probe_arg *arg)
+{
+	if (CHECK_FETCH_FUNCS(deref, arg->fetch.fn))
+		free_deref_fetch_param(arg->fetch.data);
+	else if (CHECK_FETCH_FUNCS(symbol, arg->fetch.fn))
+		free_symbol_cache(arg->fetch.data);
+	kfree(arg->name);
+	kfree(arg->comm);
+}
+
+static int command_trace_probe(const char *buf,
+			int (*createfn)(int, char**))
+{
+	char **argv;
+	int argc = 0, ret = 0;
+
+	argv = argv_split(GFP_KERNEL, buf, &argc);
+	if (!argv)
+		return -ENOMEM;
+
+	if (argc)
+		ret = createfn(argc, argv);
+
+	argv_free(argv);
+	return ret;
+}
+
+#define WRITE_BUFSIZE 128
+
+ssize_t traceprobe_probes_write(struct file *file, const char __user *buffer,
+	    size_t count, loff_t *ppos, int (*createfn)(int, char**))
+{
+	char *kbuf, *tmp;
+	int ret = 0;
+	size_t done = 0;
+	size_t size;
+
+	kbuf = kmalloc(WRITE_BUFSIZE, GFP_KERNEL);
+	if (!kbuf)
+		return -ENOMEM;
+
+	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_probe(kbuf, createfn);
+		if (ret)
+			goto out;
+	}
+	ret = done;
+out:
+	kfree(kbuf);
+	return ret;
+}
+
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
new file mode 100644
index 0000000..74311ea
--- /dev/null
+++ b/kernel/trace/trace_probe.h
@@ -0,0 +1,155 @@
+/*
+ * Common header file for probe-based Dynamic 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
+ *
+ * Derived from kernel/trace/trace_kprobe.c written by
+ * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
+ */
+
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/smp.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 <linux/kprobes.h>
+#include <linux/stringify.h>
+#include <linux/limits.h>
+#include <linux/uaccess.h>
+#include <asm/bitsperlong.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 MAX_STRING_SIZE PATH_MAX
+
+/* Reserved field names */
+#define FIELD_STRING_IP "__probe_ip"
+#define FIELD_STRING_RETIP "__probe_ret_ip"
+#define FIELD_STRING_FUNC "__probe_func"
+
+#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)
+
+
+/* Flags for trace_probe */
+#define TP_FLAG_TRACE	1
+#define TP_FLAG_PROFILE	2
+
+
+/* data_rloc: data relative location, compatible with u32 */
+#define make_data_rloc(len, roffs)	\
+	(((u32)(len) << 16) | ((u32)(roffs) & 0xffff))
+#define get_rloc_len(dl)	((u32)(dl) >> 16)
+#define get_rloc_offs(dl)	((u32)(dl) & 0xffff)
+
+/*
+ * Convert data_rloc to data_loc:
+ *  data_rloc stores the offset from data_rloc itself, but data_loc
+ *  stores the offset from event entry.
+ */
+#define convert_rloc_to_loc(dl, offs)	((u32)(dl) + (offs))
+
+/* Data fetch function type */
+typedef	void (*fetch_func_t)(struct pt_regs *, void *, void *);
+/* Printing function type */
+typedef int (*print_type_func_t)(struct trace_seq *, const char *, void *,
+				 void *);
+
+/* Fetch types */
+enum {
+	FETCH_MTD_reg = 0,
+	FETCH_MTD_stack,
+	FETCH_MTD_retval,
+	FETCH_MTD_memory,
+	FETCH_MTD_symbol,
+	FETCH_MTD_deref,
+	FETCH_MTD_END,
+};
+
+/* Fetch type information table */
+struct fetch_type {
+	const char	*name;		/* Name of type */
+	size_t		size;		/* Byte size of type */
+	int		is_signed;	/* Signed flag */
+	print_type_func_t	print;	/* Print functions */
+	const char	*fmt;		/* Fromat string */
+	const char	*fmttype;	/* Name in format file */
+	/* Fetch functions */
+	fetch_func_t	fetch[FETCH_MTD_END];
+};
+
+struct fetch_param {
+	fetch_func_t	fn;
+	void *data;
+};
+
+struct probe_arg {
+	struct fetch_param	fetch;
+	struct fetch_param	fetch_size;
+	unsigned int		offset;	/* Offset from argument entry */
+	const char		*name;	/* Name of this argument */
+	const char		*comm;	/* Command of this argument */
+	const struct fetch_type	*type;	/* Type of this argument */
+};
+
+static inline __kprobes void call_fetch(struct fetch_param *fprm,
+				 struct pt_regs *regs, void *dest)
+{
+	return fprm->fn(regs, fprm->data, dest);
+}
+
+/* Check the name is good for event/group */
+static inline 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;
+}
+
+extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
+		   struct probe_arg *parg, bool is_return);
+
+extern int traceprobe_conflict_field_name(const char *name,
+			       struct probe_arg *args, int narg);
+
+extern void traceprobe_free_probe_arg(struct probe_arg *arg);
+
+extern int traceprobe_split_symbol_offset(char *symbol, unsigned long *offset);
+
+extern ssize_t traceprobe_probes_write(struct file *file,
+		const char __user *buffer, size_t count, loff_t *ppos,
+		int (*createfn)(int, char**));

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

* [PATCHv11 2.6.36-rc2-tip 9/15]  9: tracing: uprobes trace_event interface
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (7 preceding siblings ...)
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 8/15] 8: tracing: Extract out common code for kprobes/uprobes traceevents Srikar Dronamraju
@ 2010-08-25 13:43 ` Srikar Dronamraju
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer Srikar Dronamraju
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:43 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


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.

TODO: Documentation/trace/uprobetrace.txt

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

Changelog from v5: Addressed comments from Masami Hiramatsu and Steven
      Rostedt. Some changes because of changes in common probe events.

Changelog from v4: (Merged to 2.6.35-rc3-tip)

Changelog from v2/v3: (Addressing comments from Steven Rostedt
					and Frederic Weisbecker)
	* removed pit field from uprobe_trace_entry.
	* share common parts with kprobe trace events.
	* use trace_create_file instead of debugfs_create_file.


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 6016]
  [Address to trace is 0x0000000000446420]
  [Registers to be dumped are %ip and %ax]

# cd /sys/kernel/debug/tracing/
# echo 'p 6016:0x0000000000446420 %ip %ax' > uprobe_events
# cat uprobe_events
p:uprobes/p_6016_0x0000000000446420 6016:0x0000000000446420 %ip=%ip %ax=%ax
# cat events/uprobes/p_6016_0x0000000000446420/enable
0
[enable the event]
# echo 1 > events/uprobes/p_6016_0x0000000000446420/enable
# cat events/uprobes/p_6016_0x0000000000446420/enable
1
# #### do some activity on the program so that it hits the breakpoint
# cat uprobe_profile
  6016 p_6016_0x0000000000446420                                234
# tracer: nop
#
#           TASK-PID    CPU#    TIMESTAMP  FUNCTION
#              | |       |          |         |
             zsh-6016  [004] 227931.093579: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [005] 227931.097541: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [000] 227931.124909: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [001] 227933.128565: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [004] 227933.132756: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [000] 227933.158802: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [001] 227935.161602: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79
             zsh-6016  [004] 227935.165229: p_6016_0x0000000000446420: (0x446420) %ip=446421 %ax=79

 arch/Kconfig                |   11 -
 kernel/trace/Kconfig        |   16 +
 kernel/trace/Makefile       |    1 
 kernel/trace/trace.h        |    5 
 kernel/trace/trace_kprobe.c |    4 
 kernel/trace/trace_probe.c  |   14 +
 kernel/trace/trace_probe.h  |    4 
 kernel/trace/trace_uprobe.c |  739 +++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 777 insertions(+), 17 deletions(-)
 create mode 100644 kernel/trace/trace_uprobe.c

diff --git a/arch/Kconfig b/arch/Kconfig
index c8c8e3f..af167f8 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -48,16 +48,7 @@ config OPTPROBES
 	select KALLSYMS_ALL
 
 config UPROBES
-	bool "User-space probes (EXPERIMENTAL)"
-	default n
-	depends on ARCH_SUPPORTS_UPROBES
-	depends on MMU
-	help
-	  Uprobes enables kernel subsystems 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".
+	def_bool n
 
 config HAVE_EFFICIENT_UNALIGNED_ACCESS
 	bool
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index d709697..55ba474 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -366,6 +366,22 @@ config KPROBE_EVENT
 	  This option is also required by perf-probe subcommand of perf tools.
 	  If you want to use perf tools, this option is strongly recommended.
 
+config UPROBE_EVENT
+	bool "Enable uprobes-based dynamic events"
+	depends on ARCH_SUPPORTS_UPROBES
+	depends on MMU
+	select UPROBES
+	select PROBE_EVENTS
+	select TRACING
+	default n
+	help
+	  This allows the user to add tracing events on top of userspace dynamic
+	  events (similar to tracepoints) on the fly via the traceevents interface.
+	  Those events can be inserted wherever uprobes can probe, and record
+	  various registers.
+	  This option is required if you plan to use perf-probe subcommand of perf
+	  tools on user space applications.
+
 config PROBE_EVENTS
 	def_bool n
 
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 95d2043..67a12e3 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -57,5 +57,6 @@ ifeq ($(CONFIG_TRACING),y)
 obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
 endif
 obj-$(CONFIG_PROBE_EVENTS) +=trace_probe.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 d39b3c5..c5be8cb 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -97,6 +97,11 @@ struct kretprobe_trace_entry_head {
 	unsigned long		ret_ip;
 };
 
+struct uprobe_trace_entry_head {
+	struct trace_entry	ent;
+	unsigned long		ip;
+};
+
 /*
  * trace_flag_type is an enumeration that holds different
  * states when a trace occurs. These are:
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index e66450d..8b1e55e 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -363,8 +363,8 @@ static int create_trace_probe(int argc, char **argv)
 		}
 
 		/* Parse fetch argument */
-		ret = traceprobe_parse_probe_arg(arg, &tp->size, &tp->args[i],
-								is_return);
+		ret = traceprobe_parse_probe_arg(arg, &tp->size,
+					&tp->args[i], is_return, true);
 		if (ret) {
 			pr_info("Parse error at argument%d. (%d)\n", i, ret);
 			kfree(tp->args[i].name);
diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 4ca48c6..5123d1c 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -443,13 +443,17 @@ static int parse_probe_vars(char *arg, const struct fetch_type *t,
 
 /* Recursive argument parser */
 static int parse_probe_arg(char *arg, const struct fetch_type *t,
-		     struct fetch_param *f, bool is_return)
+		     struct fetch_param *f, bool is_return, bool is_kprobe)
 {
 	int ret = 0;
 	unsigned long param;
 	long offset;
 	char *tmp;
 
+	/* Until uprobe_events supports only reg arguments */
+	if (!is_kprobe && arg[0] != '%')
+		return -EINVAL;
+
 	switch (arg[0]) {
 	case '$':
 		ret = parse_probe_vars(arg + 1, t, f, is_return);
@@ -500,7 +504,8 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
 			if (!dprm)
 				return -ENOMEM;
 			dprm->offset = offset;
-			ret = parse_probe_arg(arg, t2, &dprm->orig, is_return);
+			ret = parse_probe_arg(arg, t2, &dprm->orig, is_return,
+							is_kprobe);
 			if (ret)
 				kfree(dprm);
 			else {
@@ -520,7 +525,7 @@ static int parse_probe_arg(char *arg, const struct fetch_type *t,
 
 /* String length checking wrapper */
 int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
-		struct probe_arg *parg, bool is_return)
+		struct probe_arg *parg, bool is_return, bool is_kprobe)
 {
 	const char *t;
 	int ret;
@@ -546,7 +551,8 @@ int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
 	}
 	parg->offset = *size;
 	*size += parg->type->size;
-	ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return);
+	ret = parse_probe_arg(arg, parg->type, &parg->fetch, is_return,
+							is_kprobe);
 
 	if (ret >= 0) {
 		parg->fetch_size.fn = get_fetch_size_function(parg->type,
diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h
index 74311ea..d905386 100644
--- a/kernel/trace/trace_probe.h
+++ b/kernel/trace/trace_probe.h
@@ -48,6 +48,7 @@
 #define FIELD_STRING_IP "__probe_ip"
 #define FIELD_STRING_RETIP "__probe_ret_ip"
 #define FIELD_STRING_FUNC "__probe_func"
+#define FIELD_STRING_PID "__probe_pid"
 
 #undef DEFINE_FIELD
 #define DEFINE_FIELD(type, item, name, is_signed)			\
@@ -64,6 +65,7 @@
 /* Flags for trace_probe */
 #define TP_FLAG_TRACE	1
 #define TP_FLAG_PROFILE	2
+#define TP_FLAG_UPROBE	4
 
 
 /* data_rloc: data relative location, compatible with u32 */
@@ -141,7 +143,7 @@ static inline int check_event_name(const char *name)
 }
 
 extern int traceprobe_parse_probe_arg(char *arg, ssize_t *size,
-		   struct probe_arg *parg, bool is_return);
+		   struct probe_arg *parg, bool is_return, bool is_kprobe);
 
 extern int traceprobe_conflict_field_name(const char *name,
 			       struct probe_arg *args, int narg);
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
new file mode 100644
index 0000000..3aadac7
--- /dev/null
+++ b/kernel/trace/trace_uprobe.c
@@ -0,0 +1,739 @@
+/*
+ * 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 "trace_probe.h"
+
+#define UPROBE_EVENT_SYSTEM "uprobes"
+
+/**
+ * uprobe event core functions
+ */
+
+struct trace_uprobe {
+	struct list_head	list;
+	struct uprobe		up;
+	unsigned long		nhit;
+	unsigned int		flags;	/* For TP_FLAG_* */
+	struct ftrace_event_class	class;
+	struct ftrace_event_call	call;
+	ssize_t			size;		/* trace entry size */
+	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 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);
+
+/*
+ * 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;
+
+	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(-ENOMEM);
+
+	tp->up.vaddr = (unsigned long)addr;
+	tp->up.pid = pid;
+	tp->up.handler = uprobe_dispatcher;
+
+	tp->call.class = &tp->class;
+	tp->call.name = kstrdup(event, GFP_KERNEL);
+	if (!tp->call.name)
+		goto error;
+
+	tp->class.system = kstrdup(group, GFP_KERNEL);
+	if (!tp->class.system)
+		goto error;
+
+	INIT_LIST_HEAD(&tp->list);
+	return tp;
+error:
+	kfree(tp->call.name);
+	kfree(tp);
+	return ERR_PTR(-ENOMEM);
+}
+
+static void free_trace_uprobe(struct trace_uprobe *tp)
+{
+	int i;
+
+	for (i = 0; i < tp->nr_args; i++)
+		traceprobe_free_probe_arg(&tp->args[i]);
+
+	kfree(tp->call.class->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.class->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 & TP_FLAG_UPROBE)
+		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.class->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("Failed to register probe event(%d)\n", ret);
+		goto end;
+	}
+
+	list_add_tail(&tp->list, &uprobe_list);
+end:
+	mutex_unlock(&uprobe_lock);
+	return ret;
+}
+
+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];
+	char *tmp;
+
+	/* 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;
+		}
+		mutex_lock(&uprobe_lock);
+		tp = find_probe_event(event, group);
+		if (!tp) {
+			mutex_unlock(&uprobe_lock);
+			pr_info("Event %s/%s doesn't exist.\n", group, event);
+			return -ENOENT;
+		}
+		/* delete an event */
+		unregister_trace_uprobe(tp);
+		free_trace_uprobe(tp);
+		mutex_unlock(&uprobe_lock);
+		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];
+
+		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;
+		}
+		tmp = strchr(tp->args[i].name, ':');
+		if (tmp)
+			*tmp = '_';	/* convert : to _ */
+
+		if (traceprobe_conflict_field_name(tp->args[i].name,
+							tp->args, i)) {
+			pr_info("Argument%d name '%s' conflicts with "
+				"another field.\n", i, argv[i]);
+			ret = -EINVAL;
+			goto error;
+		}
+
+		/* Parse fetch argument */
+		ret = traceprobe_parse_probe_arg(arg, &tp->size, &tp->args[i],
+								false, false);
+		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;
+
+	seq_printf(m, "%c", 'p');
+	seq_printf(m, ":%s/%s", tp->call.class->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++)
+		seq_printf(m, " %s=%s", tp->args[i].name, tp->args[i].comm);
+	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 ssize_t probes_write(struct file *file, const char __user *buffer,
+			    size_t count, loff_t *ppos)
+{
+	return traceprobe_probes_write(file, buffer, count, ppos,
+			create_trace_uprobe);
+}
+
+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_head *entry;
+	struct ring_buffer_event *event;
+	struct ring_buffer *buffer;
+	u8 *data;
+	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(*entry) + tp->size;
+
+	event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
+						  size, irq_flags, pc);
+	if (!event)
+		return;
+
+	entry = ring_buffer_event_data(event);
+	entry->ip = (unsigned long)up->vaddr;
+	data = (u8 *)&entry[1];
+	for (i = 0; i < tp->nr_args; i++)
+		call_fetch(&tp->args[i].fetch, regs,
+						data + tp->args[i].offset);
+
+	if (!filter_current_check_discard(buffer, call, entry, event))
+		trace_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 trace_event *event)
+{
+	struct uprobe_trace_entry_head *field;
+	struct trace_seq *s = &iter->seq;
+	struct trace_uprobe *tp;
+	u8 *data;
+	int i;
+
+	field = (struct uprobe_trace_entry_head *)iter->ent;
+	tp = container_of(event, struct trace_uprobe, call.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;
+
+	data = (u8 *)&field[1];
+	for (i = 0; i < tp->nr_args; i++)
+		if (!tp->args[i].type->print(s, tp->args[i].name,
+					     data + tp->args[i].offset, field))
+			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 & TP_FLAG_UPROBE)) {
+		ret = register_uprobe(&tp->up);
+		if (!ret)
+			tp->flags |= (TP_FLAG_UPROBE | 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 & TP_FLAG_UPROBE) {
+		unregister_uprobe(&tp->up);
+		tp->flags &= ~(TP_FLAG_UPROBE | TP_FLAG_TRACE);
+	}
+}
+
+static int uprobe_event_define_fields(struct ftrace_event_call *event_call)
+{
+	int ret, i;
+	struct uprobe_trace_entry_head field;
+	struct trace_uprobe *tp = (struct trace_uprobe *)event_call->data;
+
+	DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
+	/* Set argument names as fields */
+	for (i = 0; i < tp->nr_args; i++) {
+		ret = trace_define_field(event_call, tp->args[i].type->fmttype,
+					 tp->args[i].name,
+					 sizeof(field) + tp->args[i].offset,
+					 tp->args[i].type->size,
+					 tp->args[i].type->is_signed,
+					 FILTER_OTHER);
+		if (ret)
+			return ret;
+	}
+	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=%s",
+				tp->args[i].name, tp->args[i].type->fmt);
+	}
+
+	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_head *entry;
+	struct hlist_head *head;
+	u8 *data;
+	int size, __size, i;
+	int rctx;
+
+	__size = sizeof(*entry) + tp->size;
+	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->event.type, regs, &rctx);
+	if (!entry)
+		return;
+
+	entry->ip = (unsigned long)up->vaddr;
+	data = (u8 *)&entry[1];
+	for (i = 0; i < tp->nr_args; i++)
+		call_fetch(&tp->args[i].fetch, regs,
+						data + tp->args[i].offset);
+
+	head = this_cpu_ptr(call->perf_events);
+	perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
+}
+
+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 & TP_FLAG_UPROBE)) {
+		ret = register_uprobe(&tp->up);
+		if (!ret)
+			tp->flags |= (TP_FLAG_UPROBE | 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 & TP_FLAG_UPROBE) {
+		unregister_uprobe(&tp->up);
+		tp->flags &= ~(TP_FLAG_UPROBE | TP_FLAG_PROFILE);
+	}
+}
+#endif	/* CONFIG_PERF_EVENTS */
+
+static
+int uprobe_register(struct ftrace_event_call *event, enum trace_reg type)
+{
+	switch (type) {
+	case TRACE_REG_REGISTER:
+		return probe_event_enable(event);
+	case TRACE_REG_UNREGISTER:
+		probe_event_disable(event);
+		return 0;
+
+#ifdef CONFIG_PERF_EVENTS
+	case TRACE_REG_PERF_REGISTER:
+		return probe_perf_enable(event);
+	case TRACE_REG_PERF_UNREGISTER:
+		probe_perf_disable(event);
+		return 0;
+#endif
+	}
+	return 0;
+}
+
+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 struct trace_event_functions uprobe_funcs = {
+	.trace		= print_uprobe_event
+};
+
+static int register_uprobe_event(struct trace_uprobe *tp)
+{
+	struct ftrace_event_call *call = &tp->call;
+	int ret;
+
+	/* Initialize ftrace_event_call */
+	INIT_LIST_HEAD(&call->class->fields);
+	call->event.funcs = &uprobe_funcs;
+	call->class->define_fields = uprobe_event_define_fields;
+	if (set_print_fmt(tp) < 0)
+		return -ENOMEM;
+	ret = register_ftrace_event(&call->event);
+	if (!ret) {
+		kfree(call->print_fmt);
+		return -ENODEV;
+	}
+	call->flags = 0;
+	call->class->reg = uprobe_register;
+	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(&call->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 trace 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 = trace_create_file("uprobe_events", 0644, d_tracer,
+				    NULL, &uprobe_events_ops);
+	/* Profile interface */
+	entry = trace_create_file("uprobe_profile", 0444, d_tracer,
+				    NULL, &uprobe_profile_ops);
+	return 0;
+}
+fs_initcall(init_uprobe_trace);

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

* [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (8 preceding siblings ...)
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 9/15] 9: tracing: uprobes trace_event interface Srikar Dronamraju
@ 2010-08-25 13:43 ` Srikar Dronamraju
  2010-08-26  6:02   ` Masami Hiramatsu
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order Srikar Dronamraju
                   ` (5 subsequent siblings)
  15 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:43 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


Selecting CONFIG_PROBE_EVENTS enables both kprobe-based and
uprobes-based dynamic events. However kprobe-tracer or uprobe-tracer
can still be individually selected or disabled.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Suggested-by: Frederic Weisbecker <fweisbec@gmail.com>
---
 kernel/trace/Kconfig |   51 +++++++++++++++++++++++++++++---------------------
 1 files changed, 30 insertions(+), 21 deletions(-)

diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 55ba474..205c12b 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -76,7 +76,7 @@ config RING_BUFFER_ALLOW_SWAP
 # All tracer options should select GENERIC_TRACER. For those options that are
 # enabled by all tracers (context switch and event tracer) they select TRACING.
 # This allows those options to appear when no other tracer is selected. But the
-# options do not appear when something else selects it. We need the two options
+# options do not appear when something else selects it. We need the two option
 # GENERIC_TRACER and TRACING to avoid circular dependencies to accomplish the
 # hiding of the automatic options.
 
@@ -162,7 +162,7 @@ config IRQSOFF_TRACER
 	  This option measures the time spent in irqs-off critical
 	  sections, with microsecond accuracy.
 
-	  The default measurement method is a maximum search, which is
+	  The default measurement method is a maximum search, which i
 	  disabled by default and can be runtime (re-)started
 	  via:
 
@@ -184,7 +184,7 @@ config PREEMPT_TRACER
 	  This option measures the time spent in preemption-off critical
 	  sections, with microsecond accuracy.
 
-	  The default measurement method is a maximum search, which is
+	  The default measurement method is a maximum search, which i
 	  disabled by default and can be runtime (re-)started
 	  via:
 
@@ -228,7 +228,7 @@ choice
 	prompt "Branch Profiling"
 	default BRANCH_PROFILE_NONE
 	help
-	 The branch profiling is a software profiler. It will add hooks
+	 The branch profiling is a software profiler. It will add hook
 	 into the C conditionals to test which path a branch takes.
 
 	 The likely/unlikely profiler only looks at the conditions that
@@ -252,12 +252,12 @@ config PROFILE_ANNOTATED_BRANCHES
 	bool "Trace likely/unlikely profiler"
 	select TRACE_BRANCH_PROFILING
 	help
-	  This tracer profiles all the the likely and unlikely macros
+	  This tracer profiles all the the likely and unlikely macro
 	  in the kernel. It will display the results in:
 
 	  /sys/kernel/debug/tracing/profile_annotated_branch
 
-	  Note: this will add a significant overhead; only turn this
+	  Note: this will add a significant overhead; only turn thi
 	  on if you need to profile the system's use of these macros.
 
 config PROFILE_ALL_BRANCHES
@@ -333,7 +333,7 @@ config BLK_DEV_IO_TRACE
 	select GENERIC_TRACER
 	select STACKTRACE
 	help
-	  Say Y here if you want to be able to trace the block layer actions
+	  Say Y here if you want to be able to trace the block layer action
 	  on a given queue. Tracing allows you to see any traffic happening
 	  on a block device queue. For more information (and the userspace
 	  support tools needed), fetch the blktrace tools from:
@@ -351,9 +351,8 @@ config BLK_DEV_IO_TRACE
 config KPROBE_EVENT
 	depends on KPROBES
 	depends on HAVE_REGS_AND_STACK_ACCESS_API
+	depends on PROBE_EVENTS
 	bool "Enable kprobes-based dynamic events"
-	select TRACING
-	select PROBE_EVENTS
 	default y
 	help
 	  This allows the user to add tracing events (similar to tracepoints)
@@ -370,10 +369,9 @@ config UPROBE_EVENT
 	bool "Enable uprobes-based dynamic events"
 	depends on ARCH_SUPPORTS_UPROBES
 	depends on MMU
+	depends on PROBE_EVENTS
 	select UPROBES
-	select PROBE_EVENTS
-	select TRACING
-	default n
+	default y
 	help
 	  This allows the user to add tracing events on top of userspace dynamic
 	  events (similar to tracepoints) on the fly via the traceevents interface.
@@ -383,7 +381,18 @@ config UPROBE_EVENT
 	  tools on user space applications.
 
 config PROBE_EVENTS
-	def_bool n
+	bool "Enable kprobes and uprobe based dynamic events"
+	select TRACING
+	default n
+	help
+	  This allows a user to add dynamic tracing events in
+	  kernel using kprobe-tracer and in userspace using
+	  uprobe-tracer. However users can still selectively
+	  disable one of these events.
+
+	  For more information on kprobe-tracer and uprobe-tracer
+	  please refer help under KPROBE_EVENT and UPROBE_EVENT
+	  respectively.
 
 config DYNAMIC_FTRACE
 	bool "enable/disable ftrace tracepoints dynamically"
@@ -393,14 +402,14 @@ config DYNAMIC_FTRACE
 	help
           This option will modify all the calls to ftrace dynamically
 	  (will patch them out of the binary image and replace them
-	  with a No-Op instruction) as they are called. A table is
+	  with a No-Op instruction) as they are called. A table i
 	  created to dynamically enable them again.
 
 	  This way a CONFIG_FUNCTION_TRACER kernel is slightly larger, but
 	  otherwise has native performance as long as no tracing is active.
 
 	  The changes to the code are done by a kernel thread that
-	  wakes up once a second and checks to see if any ftrace calls
+	  wakes up once a second and checks to see if any ftrace call
 	  were made. If so, it runs stop_machine (stops all CPUS)
 	  and modifies the code to jump over the call to ftrace.
 
@@ -432,7 +441,7 @@ config FTRACE_STARTUP_TEST
 	select FTRACE_SELFTEST
 	help
 	  This option performs a series of startup tests on ftrace. On bootup
-	  a series of tests are made to verify that the tracer is
+	  a series of tests are made to verify that the tracer i
 	  functioning properly. It will do tests on all the configured
 	  tracers of ftrace.
 
@@ -441,12 +450,12 @@ config EVENT_TRACE_TEST_SYSCALLS
 	depends on FTRACE_STARTUP_TEST
 	help
 	 This option will also enable testing every syscall event.
-	 It only enables the event and disables it and runs various loads
+	 It only enables the event and disables it and runs various load
 	 with the event enabled. This adds a bit more time for kernel boot
 	 up since it runs this on every system call defined.
 
 	 TBD - enable a way to actually call the syscalls as we test their
-	       events
+	       event
 
 config MMIOTRACE
 	bool "Memory mapped IO tracing"
@@ -465,7 +474,7 @@ config MMIOTRACE_TEST
 	tristate "Test module for mmiotrace"
 	depends on MMIOTRACE && m
 	help
-	  This is a dumb module for testing mmiotrace. It is very dangerous
+	  This is a dumb module for testing mmiotrace. It is very dangerou
 	  as it will write garbage to IO memory starting at a given address.
 	  However, it should be safe to use on e.g. unused portion of VRAM.
 
@@ -477,9 +486,9 @@ config RING_BUFFER_BENCHMARK
 	help
 	  This option creates a test to stress the ring buffer and benchmark it.
 	  It creates its own ring buffer such that it will not interfere with
-	  any other users of the ring buffer (such as ftrace). It then creates
+	  any other users of the ring buffer (such as ftrace). It then create
 	  a producer and consumer that will run for 10 seconds and sleep for
-	  10 seconds. Each interval it will print out the number of events
+	  10 seconds. Each interval it will print out the number of event
 	  it recorded and give a rough estimate of how long each iteration took.
 
 	  It does not disable interrupts or raise its priority, so it may be

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

* [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (9 preceding siblings ...)
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer Srikar Dronamraju
@ 2010-08-25 13:43 ` Srikar Dronamraju
  2010-08-25 23:21   ` Arnaldo Carvalho de Melo
  2010-08-30  8:35   ` [tip:perf/core] perf symbols: List symbols in a dso in ascending name order tip-bot for Srikar Dronamraju
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file Srikar Dronamraju
                   ` (4 subsequent siblings)
  15 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:43 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


Given a dso, list the symbols in ascending order. Needed for listing
available symbols from perf-probe.

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

 tools/perf/util/symbol.c |   14 ++++++++++++++
 tools/perf/util/symbol.h |    1 +
 2 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 1a36773..ca22032 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -388,6 +388,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
 	return fprintf(fp, "%s", sbuild_id);
 }
 
+size_t dso__fprintf_symbols(struct dso *self, enum map_type type, FILE *fp)
+{
+	size_t ret = 0;
+	struct rb_node *nd;
+	struct symbol_name_rb_node *pos;
+
+	for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
+		pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
+		fprintf(fp, "%s\n", pos->sym.name);
+	}
+
+	return ret;
+}
+
 size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
 {
 	struct rb_node *nd;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index b7a8da4..72ef973 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -181,6 +181,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
 size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
 
 size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
+size_t dso__fprintf_symbols(struct dso *self, enum map_type type, FILE *fp);
 size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
 
 enum dso_origin {

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

* [PATCHv11 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (10 preceding siblings ...)
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order Srikar Dronamraju
@ 2010-08-25 13:43 ` Srikar Dronamraju
  2010-08-27 14:21   ` [PATCHv11a " Srikar Dronamraju
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 13/15] 13: perf: Loop thro each of the maps in a map_group Srikar Dronamraju
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:43 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


Introduces -S/--show_functions option for perf-probe.
This lists function names in a File. If no file is specified, then lists
functions in the current running kernel.

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

Changelog from V10: As suggested by Arnaldo, filtering is now
 based on sym.binding.

Changelog from V9: Filter labels, weak, and local binding functions
from listing as suggested by Christoph Hellwig.

Show last 10 functions in /bin/zsh.

# perf probe -S -D /bin/zsh | tail
zstrtol
ztrcmp
ztrdup
ztrduppfx
ztrftime
ztrlen
ztrncpy
ztrsub
zwarn
zwarnnam

Show first 10 functions in /lib/libc.so.6

# perf probe -S -D /lib/libc.so.6 | head
_IO_adjust_column
_IO_adjust_wcolumn
_IO_default_doallocate
_IO_default_finish
_IO_default_pbackfail
_IO_default_uflow
_IO_default_xsgetn
_IO_default_xsputn
_IO_do_write@@GLIBC_2.2.5
_IO_doallocbuf

Show last 10 functions in kernel.

# perf probe -S | tail
zlib_inflateInit2
zlib_inflateReset
zlib_inflate_blob
zlib_inflate_table
zlib_inflate_workspacesize
zone_pcp_update
zone_reclaim
zone_reclaimable_pages
zone_statistics
zone_watermark_ok

 tools/perf/builtin-probe.c    |   43 ++++++++++++++++++++++++
 tools/perf/util/probe-event.c |   72 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-event.h |    1 +
 tools/perf/util/symbol.c      |    8 +++++
 tools/perf/util/symbol.h      |    1 +
 5 files changed, 124 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..fa63245 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -50,9 +50,11 @@ static struct {
 	bool list_events;
 	bool force_add;
 	bool show_lines;
+	bool list_functions;
 	int nevents;
 	struct perf_probe_event events[MAX_PROBES];
 	struct strlist *dellist;
+	struct strlist *limitlist;
 	struct line_range line_range;
 	int max_probe_points;
 } params;
@@ -132,6 +134,19 @@ static int opt_show_lines(const struct option *opt __used,
 }
 #endif
 
+static int opt_limit_dsos(const struct option *opt __used,
+			       const char *str, int unset __used)
+{
+	if (str) {
+		if (!params.limitlist)
+			params.limitlist = strlist__new(true, NULL);
+		if (!params.limitlist)
+			return -1;
+		strlist__add(params.limitlist, str);
+	}
+	return 0;
+}
+
 static const char * const probe_usage[] = {
 	"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
 	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
@@ -188,6 +203,10 @@ static const struct option options[] = {
 	OPT__DRY_RUN(&probe_event_dry_run),
 	OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
 		 "Set how many probe points can be found for a probe."),
+	OPT_BOOLEAN('S', "show_functions", &params.list_functions,
+				"Show potential probes."),
+	OPT_CALLBACK('D', "limit_dsos", NULL, "DSO",
+				"Limit Dsos.", opt_limit_dsos),
 	OPT_END()
 };
 
@@ -213,7 +232,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		params.max_probe_points = MAX_PROBES;
 
 	if ((!params.nevents && !params.dellist && !params.list_events &&
-	     !params.show_lines))
+	     !params.show_lines && !params.list_functions))
 		usage_with_options(probe_usage, options);
 
 	if (params.list_events) {
@@ -225,12 +244,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			pr_err("  Error: Don't use --list with --line.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (params.list_functions) {
+			pr_warning("  Error: Don't use --list with"
+						" --show_functions.\n");
+			usage_with_options(probe_usage, options);
+		}
 		ret = show_perf_probe_events();
 		if (ret < 0)
 			pr_err("  Error: Failed to show event list. (%d)\n",
 			       ret);
 		return ret;
 	}
+	if (params.list_functions) {
+		if (params.nevents != 0 || params.dellist) {
+			pr_err("  Error: Don't use --show_functions with"
+					" --add/--del.\n");
+			usage_with_options(probe_usage, options);
+		}
+		if (params.show_lines) {
+			pr_err("  Error: Don't use --show_functions with"
+					" --line.\n");
+			usage_with_options(probe_usage, options);
+		}
+		ret = show_possible_probes(params.limitlist);
+		if (ret < 0)
+			pr_err("  Error: Failed to show possible"
+					" probes (%d).\n", ret);
+		return ret;
+	}
 
 #ifdef DWARF_SUPPORT
 	if (params.show_lines) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e72f05c..2efbe4b 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <limits.h>
+#include <gelf.h>
 
 #undef _GNU_SOURCE
 #include "util.h"
@@ -46,6 +47,7 @@
 #include "trace-event.h"	/* For __unused */
 #include "probe-event.h"
 #include "probe-finder.h"
+#include "session.h"
 
 #define MAX_CMDLEN 256
 #define MAX_PROBE_ARGS 128
@@ -1752,3 +1754,73 @@ int del_perf_probe_events(struct strlist *dellist)
 	return ret;
 }
 
+/*
+ * If a symbol corresponds to a function with global binding return 0.
+ * For all others return 1.
+ */
+static int filter_non_global_functions(struct map *map __unused,
+						struct symbol *sym)
+{
+	if (sym->binding != STB_GLOBAL)
+		return 1;
+
+	return 0;
+}
+
+static int print_list_available_symbols(struct map *map,
+					const char *name __unused)
+{
+	if (map__load(map, filter_non_global_functions) < 0)
+		return -EINVAL;
+	if (!dso__sorted_by_name(map->dso, map->type))
+		dso__sort_by_name(map->dso, map->type);
+
+	dso__fprintf_symbols(map->dso, map->type, stdout);
+	return 0;
+}
+
+int show_possible_probes(struct strlist *limitlist)
+{
+	struct str_node *ent;
+	struct map *map = NULL;
+	char *str;
+	int ret = -EINVAL;
+
+	if (!limitlist) { /* Show functions in kernel */
+		ret = init_vmlinux();
+		if (ret < 0)
+			return ret;
+		return print_list_available_symbols(
+				machine.vmlinux_maps[MAP__FUNCTION], NULL);
+	}
+
+	symbol_conf.try_vmlinux_path = false;
+	symbol_conf.sort_by_name = true;
+	ret = symbol__init();
+	if (ret < 0) {
+		pr_debug("Failed to init symbol map.\n");
+		return ret;
+	}
+
+	strlist__for_each(ent, limitlist) {
+		str = strdup(ent->s);
+		if (!str) {
+			ret = -ENOMEM;
+			goto out_delete;
+		}
+
+		map = dso__new_map(str);
+		ret = print_list_available_symbols(map, NULL);
+		if (ret)
+			pr_warning("No Symbols in dso %s.\n", str);
+
+		dso__delete(map->dso);
+		map__delete(map);
+		free(str);
+	}
+
+out_delete:
+	if (ret)
+		pr_warning("Failed to list available probes.\n");
+	return ret;
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af3924..fcd0f08 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -115,6 +115,7 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 extern int del_perf_probe_events(struct strlist *dellist);
 extern int show_perf_probe_events(void);
 extern int show_line_range(struct line_range *lr);
+extern int show_possible_probes(struct strlist *availlist);
 
 
 /* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index ca22032..385f0a4 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2464,3 +2464,11 @@ int machine__load_vmlinux_path(struct machine *self, enum map_type type,
 
 	return ret;
 }
+
+struct map *dso__new_map(char *name)
+{
+	struct dso *dso = dso__new(name);
+	struct map *map = map__new2(0, dso, MAP__FUNCTION);
+
+	return map;
+}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 72ef973..52a8126 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -202,6 +202,7 @@ char dso__symtab_origin(const struct dso *self);
 void dso__set_long_name(struct dso *self, char *name);
 void dso__set_build_id(struct dso *self, void *build_id);
 void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
+struct map *dso__new_map(char *name);
 struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
 struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
 					const char *name);

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

* [PATCHv11 2.6.36-rc2-tip 13/15] 13: perf: Loop thro each of the maps in a map_group.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (11 preceding siblings ...)
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file Srikar Dronamraju
@ 2010-08-25 13:43 ` Srikar Dronamraju
  2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 14/15] 14: perf: perf interface for uprobes Srikar Dronamraju
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:43 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


Introduces map_groups_for_each_map that iterates over a map_group.
This is useful while listing functions through perf-probe.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Suggested-by: Arnaldo Carvalho de Melo <acme@infradead.org>
---
 tools/perf/util/map.h |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 7857579..45b5f50 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -54,6 +54,33 @@ struct map_groups {
 	struct machine	 *machine;
 };
 
+/* For map_groups iteration */
+static inline struct map *map__first(struct map_groups *self,
+						enum map_type type)
+{
+	struct rb_node *rn = rb_first(&self->maps[type]);
+	return rn ? rb_entry(rn, struct map, rb_node) : NULL;
+}
+
+static inline struct map *map__next(struct map *map)
+{
+	struct rb_node *rn;
+	if (!map)
+		return NULL;
+	rn = rb_next(&map->rb_node);
+	return rn ? rb_entry(rn, struct map, rb_node) : NULL;
+}
+
+/**
+ * map_groups__for_each_map      - iterate over a map_group
+ * @pos:	the &struct map to use as a loop cursor.
+ * @type:	the map type.
+ * @self:	the &struct map_groups for loop.
+ */
+#define map_groups__for_each_map(pos, type, self)	\
+	for (pos = map__first(self, type); pos;		\
+				pos = map__next(pos))
+
 /* Native host kernel uses -1 as pid index in machine */
 #define	HOST_KERNEL_ID			(-1)
 #define	DEFAULT_GUEST_KERNEL_ID		(0)

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

* [PATCHv11 2.6.36-rc2-tip 14/15] 14: perf: perf interface for uprobes
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (12 preceding siblings ...)
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 13/15] 13: perf: Loop thro each of the maps in a map_group Srikar Dronamraju
@ 2010-08-25 13:44 ` Srikar Dronamraju
  2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 15/15] 15: perf: Show Potential probe points Srikar Dronamraju
  2010-10-29  9:23 ` [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Christoph Hellwig
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:44 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney


Enhances perf probe to accept pid and user vaddr.
Provides very basic support for uprobes.

TODO:
Update perf-probes.txt.
Global tracing.

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

Changelog from V10: split few independent hunks into different
 patches as suggested by Arnaldo.

Changelog from v9: Renaming common fields/functions to refer to
probe instead of kprobe. This was suggested by Arnaldo.

Changelog from v8: Fixed a build break reported by Christoph Hellwig.

Changelog from v6: Changelog from v6: Fixed a bug reported by Masami.
  i.e Throw an error message and exit if perf probe is for a dwarf
  based probes.

Changelog from v4: Merged to 2.6.35-rc3-tip.

Changelog from v3: (addressed comments from Masami Hiramatsu)
	* Every process id has a different group name.
	* event name starts with function name.
	* If vaddr is specified, event name has vaddr appended
	  along with function name, (this is to avoid subsequent probes
	  using same event name.)
	* warning if -p and --list options are used together.

	Also dso can either be a short name or absolute path.

Here is a terminal snapshot of placing, using and removing a probe on a
process with pid 3591 (corresponding to zsh)

[ Probing a function in the executable using function name  ]
-------------------------------------------------------------
[root@ABCD]# perf probe -p 3591 zfree@zsh
Added new event:
  probe_3591:zfree                       (on 0x446420)

You can now use it on all perf tools, such as:

	perf record -e probe_3591:zfree -a sleep 1
[root@ABCD]# perf probe --list
probe_3591:zfree                       (on 3591:0x0000000000446420)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446420
[root@ABCD]# perf record -f -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.039 MB perf.data (~1716 samples) ]
[root@ABCD]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root@ABCD]# perf report
# Samples: 447
#
# Overhead          Command  Shared Object  Symbol
# ........  ...............  .............  ......
#
   100.00%              zsh  zsh            [.] zfree


#
# (For a higher level overview, try: perf report --sort comm,dso)
#

[ Probing a function + offset ]
-------------------------------
[root@ABCD]# perf probe -p 3591 zfree@zsh+5
Added new event:
  probe_3591:zfree                         (on 0x446425)

You can now use it on all perf tools, such as:

	perf record -e probe_3591:zfree -a sleep 1
[root@ABCD]# perf probe --list
probe_3591:zfree                       (on 3591:0x0000000000446425)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/zfree 3591:0x0000000000446425
[root@ABCD]# perf record -f -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.036 MB perf.data (~1590 samples) ]
[root@ABCD]# perf probe -p 3591 --del probe_3591:zfree
Remove event: probe_3591:zfree
[root@ABCD]# perf report
# Samples: 18
#
# Overhead          Command  Shared Object  Symbol
# ........  ...............  .............  ......
#
   100.00%              zsh  zsh            [.] zfree


#
# (For a higher level overview, try: perf report --sort comm,dso)
#


[ Probing a library function using function name ]
--------------------------------------------------
[root@ABCD]# perf probe -p 3591 write@libc-2.5.so
Added new event:
  probe_3591:write                       (on 0x36010c6060)

You can now use it on all perf tools, such as:

	perf record -e probe_3591:write -a sleep 1
[root@ABCD]# perf probe --list
probe_3591:write                       (on 3591:0x00000036010c6060)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root@ABCD]# perf record -f -e probe_3591:write -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1738 samples) ]
[root@ABCD]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root@ABCD]# perf report
# Samples: 11
#
# Overhead          Command       Shared Object  Symbol
# ........  ...............  ..................  ......
#
   100.00%              zsh  libc-2.5.so         [.] __GI___libc_write


#
# (For a higher level overview, try: perf report --sort comm,dso)
#

[ Probing a library function using function name and absolute path ]
---------------------------------------------------------------------
[root@ABCD]# perf probe -p 3591 write@/lib64/libc-2.5.so
Added new event:
  probe_3591:write                       (on 0x36010c6060)

You can now use it on all perf tools, such as:

	perf record -e probe_3591:write -a sleep 1
[root@ABCD]# perf probe --list
probe_3591:write                       (on 3591:0x00000036010c6060)
[root@ABCD]# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_3591/write 3591:0x00000036010c6060
[root@ABCD]# perf record -f -e probe_3591:write -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1738 samples) ]
[root@ABCD]# perf probe -p 3591 --del probe_3591:write
Remove event: probe_3591:write
[root@ABCD]# perf report
# Samples: 11
#
# Overhead          Command       Shared Object  Symbol
# ........  ...............  ..................  ......
#
   100.00%              zsh  libc-2.5.so         [.] __GI___libc_write


#
# (For a higher level overview, try: perf report --sort comm,dso)
#

[ Probing using vaddr 0x0000000000446420 (corresponding to zfree)]
-------------------------------------------------------------------
[root@ABCD]# perf probe -p 3591 0x0000000000446420
Added new event:
  probe_3591:zfree_446420          (on 0x0000000000446420)

You can now use it on all perf tools, such as:

        perf record -e probe_3591:zfree_446420 -a sleep 1

[root@ABCD]# perf record -e probe_3591:zfree_446420 -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.041 MB perf.data (~1797 samples) ]
[root@ABCD]# perf report
#
# Samples: 628
#
# Overhead          Command  Shared Object  Symbol
# ........  ...............  .............  ......
#
   100.00%              zsh  zsh            [.] zfree


#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@ABCD]# perf report --sort comm,dso
# Samples: 628
#
# Overhead          Command  Shared Object
# ........  ...............  .............
#
   100.00%              zsh  zsh


[root@ABCD]# perf probe --list
  probe_3591:zfree_446420          (on 3591:0x0000000000446420)
[root@ABCD]#  perf list | grep probe
  probe_3591:zfree_446420            [Tracepoint event]
[root@ABCD]# perf probe -p 3591 --del probe_3591:zfree_446420
Remove event: probe_3591:zfree_446420
[root@ABCD]#


Another example for a shared library: write stub in libc. (corresponds to
0x00000036010c6060)

on a vaddr
[ Probing a libc vaddr 0x00000036010c6060 (corresponding to write) ]
[root@ABCD]# perf probe -p 3591 0x00000036010c6060
dded new event:
  probe_3591:__GI___libc_write_36010c6060          (on 0x00000036010c6060)

You can now use it on all perf tools, such as:

        perf record -e probe_3591:__GI___libc_write_36010c6060 -a sleep 1

[root@ABCD]# perf record -f -e probe_3591:__GI___libc_write_36010c6060 -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.040 MB perf.data (~1748 samples) ]
[root@ABCD]# perf report
# Samples: 24
#
# Overhead          Command       Shared Object  Symbol
# ........  ...............  ..................  ......
#
   100.00%              zsh  libc-2.5.so         [.] __GI___libc_write


#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@ABCD]#

[ Probing using a function without specifying a dso (corresponding to zfree)]
-------------------------------------------------------------------
[root@ABCD]# perf probe -p 3591 zfree
Added new event:
  probe_3591:zfree		          (on 0x0000000000446420)

You can now use it on all perf tools, such as:

        perf record -e probe_3591:zfree -a sleep 1

[root@ABCD]# perf record -e probe_3591:zfree -a sleep 10
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.041 MB perf.data (~1797 samples) ]
[root@ABCD]# perf report
#
# Samples: 628
#
# Overhead          Command  Shared Object  Symbol
# ........  ...............  .............  ......
#
   100.00%              zsh  zsh            [.] zfree


#
# (For a higher level overview, try: perf report --sort comm,dso)
#
[root@ABCD]#

 tools/perf/builtin-probe.c    |   12 +
 tools/perf/util/probe-event.c |  389 +++++++++++++++++++++++++++++++++--------
 tools/perf/util/probe-event.h |   10 +
 3 files changed, 328 insertions(+), 83 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index fa63245..afca6ae 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -57,6 +57,7 @@ static struct {
 	struct strlist *limitlist;
 	struct line_range line_range;
 	int max_probe_points;
+	pid_t upid;
 } params;
 
 
@@ -75,6 +76,7 @@ static int parse_probe_event(const char *str)
 	/* Parse a perf-probe command into event */
 	ret = parse_perf_probe_command(str, pev);
 	pr_debug("%d arguments\n", pev->nargs);
+	pev->upid = params.upid;
 
 	return ret;
 }
@@ -207,6 +209,8 @@ static const struct option options[] = {
 				"Show potential probes."),
 	OPT_CALLBACK('D', "limit_dsos", NULL, "DSO",
 				"Limit Dsos.", opt_limit_dsos),
+	OPT_INTEGER('p', "pid", &params.upid,
+			"Specify pid of process where probe will be added"),
 	OPT_END()
 };
 
@@ -249,6 +253,10 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 						" --show_functions.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (params.upid) {
+			pr_warning("  Error: Don't use --list with --pid.\n");
+			usage_with_options(probe_usage, options);
+		}
 		ret = show_perf_probe_events();
 		if (ret < 0)
 			pr_err("  Error: Failed to show event list. (%d)\n",
@@ -274,7 +282,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 	}
 
 #ifdef DWARF_SUPPORT
-	if (params.show_lines) {
+	if (params.show_lines && !params.upid) {
 		if (params.nevents != 0 || params.dellist) {
 			pr_warning("  Error: Don't use --line with"
 				   " --add/--del.\n");
@@ -289,7 +297,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 #endif
 
 	if (params.dellist) {
-		ret = del_perf_probe_events(params.dellist);
+		ret = del_perf_probe_events(params.dellist, params.upid);
 		strlist__delete(params.dellist);
 		if (ret < 0) {
 			pr_err("  Error: Failed to delete events. (%d)\n", ret);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 2efbe4b..17a89b1 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -74,6 +74,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
 }
 
 static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
+static int convert_name_to_addr(struct perf_probe_event *pev);
 static struct machine machine;
 
 /* Initialize symbol maps and path of vmlinux */
@@ -111,6 +112,32 @@ out:
 	return ret;
 }
 
+static int init_perf_uprobes(void)
+{
+	int ret = 0;
+
+	symbol_conf.try_vmlinux_path = false;
+	symbol_conf.sort_by_name = true;
+	ret = symbol__init();
+	if (ret < 0)
+		pr_debug("Failed to init symbol map.\n");
+
+	return ret;
+}
+
+
+static int convert_to_perf_probe_point(struct probe_trace_point *tp,
+					struct perf_probe_point *pp)
+{
+	pp->function = strdup(tp->symbol);
+	if (pp->function == NULL)
+		return -ENOMEM;
+	pp->offset = tp->offset;
+	pp->retprobe = tp->retprobe;
+
+	return 0;
+}
+
 #ifdef DWARF_SUPPORT
 static int open_vmlinux(void)
 {
@@ -163,6 +190,15 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 	bool need_dwarf = perf_probe_event_need_dwarf(pev);
 	int fd, ntevs;
 
+	if (pev->upid) {
+		if (need_dwarf) {
+			pr_warning("Debuginfo-analysis is not yet supported"
+					" with -p/--pid option.\n");
+			return -ENOSYS;
+		}
+		return convert_name_to_addr(pev);
+	}
+
 	fd = open_vmlinux();
 	if (fd < 0) {
 		if (need_dwarf) {
@@ -385,13 +421,7 @@ end:
 static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
 				       struct perf_probe_point *pp)
 {
-	pp->function = strdup(tp->symbol);
-	if (pp->function == NULL)
-		return -ENOMEM;
-	pp->offset = tp->offset;
-	pp->retprobe = tp->retprobe;
-
-	return 0;
+	return convert_to_perf_probe_point(tp, pp);
 }
 
 static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
@@ -402,6 +432,9 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 		pr_warning("Debuginfo-analysis is not supported.\n");
 		return -ENOSYS;
 	}
+	if (pev->upid)
+		return convert_name_to_addr(pev);
+
 	return 0;
 }
 
@@ -477,6 +510,109 @@ static bool check_event_name(const char *name)
 	return true;
 }
 
+/*
+ * uprobe_events only accepts address:
+ * Convert function and any offset to address
+ */
+static int convert_name_to_addr(struct perf_probe_event *pev)
+{
+	struct perf_probe_point *pp = &pev->point;
+	struct perf_session *session;
+	struct thread *thread;
+	struct symbol *sym;
+	struct map *map;
+	char *name = NULL, *tmpname = NULL;
+	int ret = -EINVAL;
+	unsigned long long vaddr;
+
+	/* check if user has specifed a virtual address */
+	vaddr = strtoul(pp->function, NULL, 0);
+	session = perf_session__new(NULL, O_WRONLY, false, false);
+	if (!session) {
+		pr_warning("Cannot initialize perf session.\n");
+		return -ENOMEM;
+	}
+
+	event__synthesize_thread(pev->upid, event__process, session);
+	thread = perf_session__findnew(session, pev->upid);
+	if (!thread) {
+		pr_warning("Cannot initialize perf session.\n");
+		goto free_session;
+	}
+
+	if (vaddr) {
+		if (pev->event) {
+			ret = 0;
+			goto free_session;
+		}
+
+		pev->event = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
+		if (!pev->event) {
+			ret = -ENOMEM;
+			pr_warning("Cannot allocate memory for event.\n");
+			goto free_session;
+		}
+
+		sym = map_groups__find_symbol(&thread->mg, MAP__FUNCTION,
+							vaddr, NULL, NULL);
+		if (!sym)
+			snprintf(pev->event, MAX_PROBE_ARGS, "p_%llx", vaddr);
+		else
+			snprintf(pev->event, MAX_PROBE_ARGS, "%s_%llx",
+							sym->name, vaddr);
+		ret = 0;
+		goto free_session;
+	}
+
+	if (!pp->file)
+		/* Lets find the function in the executable. */
+		name = thread->comm;
+	else {
+		tmpname = realpath(pp->file, NULL);
+		if (tmpname)
+			name = basename(tmpname);
+		else
+			name = pp->file;
+	}
+
+	map = map_groups__find_by_name(&thread->mg, MAP__FUNCTION, name);
+	if (pp->file && tmpname)
+		free(tmpname);
+	if (!map) {
+		pr_warning("Cannot find appropriate DSO.\n");
+		goto free_session;
+	}
+
+	sym = map__find_symbol_by_name(map, pp->function, NULL);
+	if (!sym) {
+		pr_warning("Cannot find appropriate DSO.\n");
+		goto free_session;
+	}
+
+	if (map->start > sym->start)
+		vaddr = map->start;
+	vaddr += sym->start + pp->offset + map->pgoff;
+	pp->offset = 0;
+
+	if (!pev->event)
+		pev->event = pp->function;
+	else
+		free(pp->function);
+	pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
+	if (!pp->function) {
+		ret = -ENOMEM;
+		pr_warning("Failed to allocate memory by zalloc.\n");
+		goto free_session;
+	}
+	e_snprintf(pp->function, MAX_PROBE_ARGS, "0x%llx", vaddr);
+	ret = 0;
+
+free_session:
+	if (session)
+		perf_session__delete(session);
+	return ret;
+}
+
 /* Parse probepoint definition. */
 static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 {
@@ -616,6 +752,11 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 		return -EINVAL;
 	}
 
+	if (pev->upid && !pp->function) {
+		semantic_error("No function specified for uprobes");
+		return -EINVAL;
+	}
+
 	if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) {
 		semantic_error("Offset/Line/Lazy pattern can't be used with "
 			       "return probe.");
@@ -625,6 +766,11 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
 	pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n",
 		 pp->function, pp->file, pp->line, pp->offset, pp->retprobe,
 		 pp->lazy_line);
+
+	if (pev->upid && perf_probe_event_need_dwarf(pev)) {
+		semantic_error("no dwarf based probes for uprobes.");
+		return -EINVAL;
+	}
 	return 0;
 }
 
@@ -776,7 +922,8 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
 {
 	int i;
 
-	if (pev->point.file || pev->point.line || pev->point.lazy_line)
+	if ((pev->point.file && !pev->upid) || pev->point.line ||
+					pev->point.lazy_line)
 		return true;
 
 	for (i = 0; i < pev->nargs; i++)
@@ -1067,10 +1214,22 @@ char *synthesize_probe_trace_command(struct probe_trace_event *tev)
 	if (buf == NULL)
 		return NULL;
 
-	len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
-			 tp->retprobe ? 'r' : 'p',
-			 tev->group, tev->event,
-			 tp->symbol, tp->offset);
+	if (tev->upid)
+		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %d:%s",
+				 tp->retprobe ? 'r' : 'p',
+				 tev->group, tev->event,
+				 tev->upid, tp->symbol);
+	else if (tp->offset)
+		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu",
+				 tp->retprobe ? 'r' : 'p',
+				 tev->group, tev->event,
+				 tp->symbol, tp->offset);
+	else
+		len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s",
+				 tp->retprobe ? 'r' : 'p',
+				 tev->group, tev->event,
+				 tp->symbol);
+
 	if (len <= 0)
 		goto error;
 
@@ -1089,7 +1248,7 @@ error:
 }
 
 static int convert_to_perf_probe_event(struct probe_trace_event *tev,
-				struct perf_probe_event *pev)
+				struct perf_probe_event *pev, bool is_kprobe)
 {
 	char buf[64] = "";
 	int i, ret;
@@ -1101,7 +1260,11 @@ static int convert_to_perf_probe_event(struct probe_trace_event *tev,
 		return -ENOMEM;
 
 	/* Convert trace_point to probe_point */
-	ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
+	if (is_kprobe)
+		ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
+	else
+		ret = convert_to_perf_probe_point(&tev->point, &pev->point);
+
 	if (ret < 0)
 		return ret;
 
@@ -1195,7 +1358,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev)
 	memset(tev, 0, sizeof(*tev));
 }
 
-static int open_kprobe_events(bool readwrite)
+static int open_probe_events(bool readwrite, bool is_kprobe)
 {
 	char buf[PATH_MAX];
 	const char *__debugfs;
@@ -1206,8 +1369,13 @@ static int open_kprobe_events(bool readwrite)
 		pr_warning("Debugfs is not mounted.\n");
 		return -ENOENT;
 	}
+	if (is_kprobe)
+		ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events",
+							__debugfs);
+	else
+		ret = e_snprintf(buf, PATH_MAX, "%stracing/uprobe_events",
+							__debugfs);
 
-	ret = e_snprintf(buf, PATH_MAX, "%stracing/kprobe_events", __debugfs);
 	if (ret >= 0) {
 		pr_debug("Opening %s write=%d\n", buf, readwrite);
 		if (readwrite && !probe_event_dry_run)
@@ -1218,16 +1386,29 @@ static int open_kprobe_events(bool readwrite)
 
 	if (ret < 0) {
 		if (errno == ENOENT)
-			pr_warning("kprobe_events file does not exist - please"
-				 " rebuild kernel with CONFIG_KPROBE_EVENT.\n");
+			pr_warning("%s file does not exist - please"
+				" rebuild kernel with CONFIG_%s_EVENT.\n",
+				is_kprobe ? "kprobe_events" : "uprobe_events",
+				is_kprobe ? "KPROBE" : "UPROBE");
 		else
-			pr_warning("Failed to open kprobe_events file: %s\n",
-				   strerror(errno));
+			pr_warning("Failed to open %s file: %s\n",
+				is_kprobe ? "kprobe_events" : "uprobe_events",
+				strerror(errno));
 	}
 	return ret;
 }
 
-/* Get raw string list of current kprobe_events */
+static int open_kprobe_events(bool readwrite)
+{
+	return open_probe_events(readwrite, 1);
+}
+
+static int open_uprobe_events(bool readwrite)
+{
+	return open_probe_events(readwrite, 0);
+}
+
+/* Get raw string list of current kprobe_events  or uprobe_events */
 static struct strlist *get_probe_trace_command_rawlist(int fd)
 {
 	int ret, idx;
@@ -1292,36 +1473,26 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
 	return ret;
 }
 
-/* List up current perf-probe events */
-int show_perf_probe_events(void)
+static int __show_perf_probe_events(int fd, bool is_kprobe)
 {
-	int fd, ret;
+	int ret = 0;
 	struct probe_trace_event tev;
 	struct perf_probe_event pev;
 	struct strlist *rawlist;
 	struct str_node *ent;
 
-	setup_pager();
-	ret = init_vmlinux();
-	if (ret < 0)
-		return ret;
-
 	memset(&tev, 0, sizeof(tev));
 	memset(&pev, 0, sizeof(pev));
 
-	fd = open_kprobe_events(false);
-	if (fd < 0)
-		return fd;
-
 	rawlist = get_probe_trace_command_rawlist(fd);
-	close(fd);
 	if (!rawlist)
 		return -ENOENT;
 
 	strlist__for_each(ent, rawlist) {
 		ret = parse_probe_trace_command(ent->s, &tev);
 		if (ret >= 0) {
-			ret = convert_to_perf_probe_event(&tev, &pev);
+			ret = convert_to_perf_probe_event(&tev, &pev,
+								is_kprobe);
 			if (ret >= 0)
 				ret = show_perf_probe_event(&pev);
 		}
@@ -1331,6 +1502,31 @@ int show_perf_probe_events(void)
 			break;
 	}
 	strlist__delete(rawlist);
+	return ret;
+}
+
+/* List up current perf-probe events */
+int show_perf_probe_events(void)
+{
+	int fd, ret;
+
+	setup_pager();
+	fd = open_kprobe_events(false);
+	if (fd < 0)
+		return fd;
+
+	ret = init_vmlinux();
+	if (ret < 0)
+		return ret;
+
+	ret = __show_perf_probe_events(fd, true);
+	close(fd);
+
+	fd = open_uprobe_events(false);
+	if (fd >= 0) {
+		ret = __show_perf_probe_events(fd, false);
+		close(fd);
+	}
 
 	return ret;
 }
@@ -1440,7 +1636,10 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	const char *event, *group;
 	struct strlist *namelist;
 
-	fd = open_kprobe_events(true);
+	if (pev->upid)
+		fd = open_uprobe_events(true);
+	else
+		fd = open_kprobe_events(true);
 	if (fd < 0)
 		return fd;
 	/* Get current event names */
@@ -1454,17 +1653,26 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":");
 	for (i = 0; i < ntevs; i++) {
 		tev = &tevs[i];
-		if (pev->event)
-			event = pev->event;
-		else
-			if (pev->point.function)
-				event = pev->point.function;
-			else
-				event = tev->point.symbol;
+
 		if (pev->group)
 			group = pev->group;
-		else
+		else if (!pev->upid)
 			group = PERFPROBE_GROUP;
+		else {
+			/*
+			 * For uprobes based probes create a group
+			 * probe_<pid>.
+			 */
+			snprintf(buf, 64, "%s_%d", PERFPROBE_GROUP, pev->upid);
+			group = buf;
+		}
+
+		if (pev->event)
+			event = pev->event;
+		else if (pev->point.function)
+			event = pev->point.function;
+		else
+			event = tev->point.symbol;
 
 		/* Get an unused new event name */
 		ret = get_new_event_name(buf, 64, event,
@@ -1572,6 +1780,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
 		}
 	}
 
+	if (pev->upid) {
+		tev->upid = pev->upid;
+		return 1;
+	}
+
 	/* Currently just checking function name from symbol map */
 	sym = map__find_symbol_by_name(machine.vmlinux_maps[MAP__FUNCTION],
 				       tev->point.symbol, NULL);
@@ -1599,15 +1812,19 @@ struct __event_package {
 int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 			  bool force_add, int max_tevs)
 {
-	int i, j, ret;
+	int i, j, ret = 0;
 	struct __event_package *pkgs;
 
 	pkgs = zalloc(sizeof(struct __event_package) * npevs);
 	if (pkgs == NULL)
 		return -ENOMEM;
 
-	/* Init vmlinux path */
-	ret = init_vmlinux();
+	if (!pevs->upid)
+		/* Init vmlinux path */
+		ret = init_vmlinux();
+	else
+		ret = init_perf_uprobes();
+
 	if (ret < 0) {
 		free(pkgs);
 		return ret;
@@ -1672,23 +1889,15 @@ error:
 	return ret;
 }
 
-static int del_trace_probe_event(int fd, const char *group,
-				  const char *event, struct strlist *namelist)
+static int del_trace_probe_event(int fd, const char *buf,
+						  struct strlist *namelist)
 {
-	char buf[128];
 	struct str_node *ent, *n;
-	int found = 0, ret = 0;
-
-	ret = e_snprintf(buf, 128, "%s:%s", group, event);
-	if (ret < 0) {
-		pr_err("Failed to copy event.");
-		return ret;
-	}
+	int ret = -1;
 
 	if (strpbrk(buf, "*?")) { /* Glob-exp */
 		strlist__for_each_safe(ent, n, namelist)
 			if (strglobmatch(ent->s, buf)) {
-				found++;
 				ret = __del_trace_probe_event(fd, ent);
 				if (ret < 0)
 					break;
@@ -1697,40 +1906,44 @@ static int del_trace_probe_event(int fd, const char *group,
 	} else {
 		ent = strlist__find(namelist, buf);
 		if (ent) {
-			found++;
 			ret = __del_trace_probe_event(fd, ent);
 			if (ret >= 0)
 				strlist__remove(namelist, ent);
 		}
 	}
-	if (found == 0 && ret >= 0)
-		pr_info("Info: Event \"%s\" does not exist.\n", buf);
-
 	return ret;
 }
 
-int del_perf_probe_events(struct strlist *dellist)
+int del_perf_probe_events(struct strlist *dellist, pid_t pid)
 {
-	int fd, ret = 0;
+	int ret = -1, ufd = -1, kfd = -1;
+	char buf[128];
 	const char *group, *event;
 	char *p, *str;
 	struct str_node *ent;
-	struct strlist *namelist;
+	struct strlist *namelist = NULL, *unamelist = NULL;
 
-	fd = open_kprobe_events(true);
-	if (fd < 0)
-		return fd;
 
 	/* Get current event names */
-	namelist = get_probe_trace_event_names(fd, true);
-	if (namelist == NULL)
-		return -EINVAL;
+	if (!pid) {
+		kfd = open_kprobe_events(true);
+		if (kfd < 0)
+			return kfd;
+		namelist = get_probe_trace_event_names(kfd, true);
+	}
+
+	ufd = open_uprobe_events(true);
+	if (ufd >= 0)
+		unamelist = get_probe_trace_event_names(ufd, true);
+
+	if (namelist == NULL && unamelist == NULL)
+		goto error;
 
 	strlist__for_each(ent, dellist) {
 		str = strdup(ent->s);
 		if (str == NULL) {
 			ret = -ENOMEM;
-			break;
+			goto error;
 		}
 		pr_debug("Parsing: %s\n", str);
 		p = strchr(str, ':');
@@ -1742,15 +1955,37 @@ int del_perf_probe_events(struct strlist *dellist)
 			group = "*";
 			event = str;
 		}
+
+		ret = e_snprintf(buf, 128, "%s:%s", group, event);
+		if (ret < 0) {
+			pr_err("Failed to copy event.");
+			free(str);
+			goto error;
+		}
+
 		pr_debug("Group: %s, Event: %s\n", group, event);
-		ret = del_trace_probe_event(fd, group, event, namelist);
+		if (!pid && namelist)
+			ret = del_trace_probe_event(kfd, buf, namelist);
+		if (unamelist && ret != 0)
+			ret = del_trace_probe_event(ufd, buf, unamelist);
+
 		free(str);
-		if (ret < 0)
-			break;
+		if (ret != 0)
+			pr_info("Info: Event \"%s\" does not exist.\n", buf);
 	}
-	strlist__delete(namelist);
-	close(fd);
 
+error:
+	if (kfd >= 0) {
+		if (namelist)
+			strlist__delete(namelist);
+		close(kfd);
+	}
+
+	if (ufd >= 0) {
+		if (unamelist)
+			strlist__delete(unamelist);
+		close(ufd);
+	}
 	return ret;
 }
 
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index fcd0f08..9cf1bcc 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -6,7 +6,7 @@
 
 extern bool probe_event_dry_run;
 
-/* kprobe-tracer tracing point */
+/* kprobe-tracer and uprobe-tracer tracing point */
 struct probe_trace_point {
 	char		*symbol;	/* Base symbol */
 	unsigned long	offset;		/* Offset from symbol */
@@ -19,7 +19,7 @@ struct probe_trace_arg_ref {
 	long				offset;	/* Offset value */
 };
 
-/* kprobe-tracer tracing argument */
+/* kprobe-tracer and uprobe-tracer tracing argument */
 struct probe_trace_arg {
 	char				*name;	/* Argument name */
 	char				*value;	/* Base value */
@@ -27,12 +27,13 @@ struct probe_trace_arg {
 	struct probe_trace_arg_ref	*ref;	/* Referencing offset */
 };
 
-/* kprobe-tracer tracing event (point + arg) */
+/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
 struct probe_trace_event {
 	char				*event;	/* Event name */
 	char				*group;	/* Group name */
 	struct probe_trace_point	point;	/* Trace point */
 	int				nargs;	/* Number of args */
+	pid_t				upid;	/* uprobes only */
 	struct probe_trace_arg		*args;	/* Arguments */
 };
 
@@ -68,6 +69,7 @@ struct perf_probe_event {
 	char			*group;	/* Group name */
 	struct perf_probe_point	point;	/* Probe point */
 	int			nargs;	/* Number of arguments */
+	pid_t			upid;
 	struct perf_probe_arg	*args;	/* Arguments */
 };
 
@@ -112,7 +114,7 @@ extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
 
 extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 				 bool force_add, int max_probe_points);
-extern int del_perf_probe_events(struct strlist *dellist);
+extern int del_perf_probe_events(struct strlist *dellist, pid_t pid);
 extern int show_perf_probe_events(void);
 extern int show_line_range(struct line_range *lr);
 extern int show_possible_probes(struct strlist *availlist);

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

* [PATCHv11 2.6.36-rc2-tip 15/15] 15: perf: Show Potential probe points.
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (13 preceding siblings ...)
  2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 14/15] 14: perf: perf interface for uprobes Srikar Dronamraju
@ 2010-08-25 13:44 ` Srikar Dronamraju
  2010-10-29  9:23 ` [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Christoph Hellwig
  15 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-25 13:44 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Srikar Dronamraju, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney


Introduces an option to list potential probes to probe using perf probe
command. Also introduces an option to limit the dso to list the potential
probes. Listing of potential probes is sorted by dso and
alphabetical order.

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

Changelog from V9:
	Filter labels, weak, and local binding functions from listing
as suggested by Christoph Hellwig.
	Incorporated comments from Arnaldo on Version 9 of patchset.

Show all potential probes in the current running kernel and limit to
the last 10 functions.
# perf probe -S | tail
zlib_inflateInit2
zlib_inflateReset
zlib_inflate_blob
zlib_inflate_table
zlib_inflate_workspacesize
zone_pcp_update
zone_reclaim
zone_reclaimable_pages
zone_statistics
zone_watermark_ok

Show all potential probes in a process by pid 3104 across all dsos
and limit to the last 10 functions.
# perf probe -S -p 3104 | tail
_nss_files_setgrent
_nss_files_sethostent
_nss_files_setnetent
_nss_files_setnetgrent
_nss_files_setprotoent
_nss_files_setpwent
_nss_files_setrpcent
_nss_files_setservent
_nss_files_setspent
_nss_netgroup_parseline

Show all potentail probes in a process by pid 3104 limit to zsh dso
and limit to the last 10 functions.
# perf probe -S -p 3104 -D zsh | tail
zstrtol
ztrcmp
ztrdup
ztrduppfx
ztrftime
ztrlen
ztrncpy
ztrsub
zwarn
zwarnnam

 tools/perf/builtin-probe.c    |    2 +
 tools/perf/util/probe-event.c |   68 +++++++++++++++++++++++++++++++++--------
 tools/perf/util/probe-event.h |    4 +-
 3 files changed, 56 insertions(+), 18 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index afca6ae..f5893d9 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -274,7 +274,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 					" --line.\n");
 			usage_with_options(probe_usage, options);
 		}
-		ret = show_possible_probes(params.limitlist);
+		ret = show_possible_probes(params.limitlist, params.upid);
 		if (ret < 0)
 			pr_err("  Error: Failed to show possible"
 					" probes (%d).\n", ret);
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 17a89b1..58146a7 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1923,7 +1923,6 @@ int del_perf_probe_events(struct strlist *dellist, pid_t pid)
 	struct str_node *ent;
 	struct strlist *namelist = NULL, *unamelist = NULL;
 
-
 	/* Get current event names */
 	if (!pid) {
 		kfd = open_kprobe_events(true);
@@ -2014,27 +2013,46 @@ static int print_list_available_symbols(struct map *map,
 	return 0;
 }
 
-int show_possible_probes(struct strlist *limitlist)
+int show_possible_probes(struct strlist *limitlist, pid_t pid)
 {
+	struct perf_session *session = NULL;
+	struct thread *thread = NULL;
 	struct str_node *ent;
 	struct map *map = NULL;
-	char *str;
+	char *name = NULL, *tmpname = NULL, *str;
 	int ret = -EINVAL;
 
-	if (!limitlist) { /* Show functions in kernel */
+	if (!pid && !limitlist) { /* Show functions in kernel */
 		ret = init_vmlinux();
 		if (ret < 0)
 			return ret;
 		return print_list_available_symbols(
 				machine.vmlinux_maps[MAP__FUNCTION], NULL);
+	} else {
+		ret = init_perf_uprobes();
+		if (ret < 0)
+			return ret;
 	}
-
-	symbol_conf.try_vmlinux_path = false;
-	symbol_conf.sort_by_name = true;
-	ret = symbol__init();
-	if (ret < 0) {
-		pr_debug("Failed to init symbol map.\n");
-		return ret;
+	if (pid)  {
+		session = perf_session__new(NULL, O_WRONLY, false, false);
+		if (!session) {
+			ret = -ENOMEM;
+			goto out_delete;
+		}
+		event__synthesize_thread(pid, event__process, session);
+		thread = perf_session__findnew(session, pid);
+		if (!thread)
+			goto out_delete;
+		if (!limitlist) {
+			map_groups__for_each_map(map, MAP__FUNCTION,
+								&thread->mg) {
+				ret = print_list_available_symbols(map, NULL);
+				if (ret)
+					pr_warning("No Symbols in dso %s.\n",
+							map->dso->short_name);
+			}
+			goto out_delete;
+		}
 	}
 
 	strlist__for_each(ent, limitlist) {
@@ -2044,18 +2062,40 @@ int show_possible_probes(struct strlist *limitlist)
 			goto out_delete;
 		}
 
-		map = dso__new_map(str);
+		if (pid) {
+			tmpname = realpath(str, NULL);
+			if (tmpname)
+				name = basename(tmpname);
+			if (!name)
+				name = str;
+			map = map_groups__find_by_name(&thread->mg,
+							MAP__FUNCTION, name);
+			if (tmpname)
+				free(tmpname);
+			if (!map) {
+				pr_warning("Skip probe listing in %s DSO.\n",
+							str);
+				free(str);
+				continue;
+			}
+		} else
+			map = dso__new_map(str);
+
 		ret = print_list_available_symbols(map, NULL);
 		if (ret)
 			pr_warning("No Symbols in dso %s.\n", str);
 
-		dso__delete(map->dso);
-		map__delete(map);
+		if (!pid) {
+			dso__delete(map->dso);
+			map__delete(map);
+		}
 		free(str);
 	}
 
 out_delete:
 	if (ret)
 		pr_warning("Failed to list available probes.\n");
+	if (session)
+		perf_session__delete(session);
 	return ret;
 }
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 9cf1bcc..4a40d2c 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -111,14 +111,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev);
 /* Command string to line-range */
 extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
 
-
 extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 				 bool force_add, int max_probe_points);
 extern int del_perf_probe_events(struct strlist *dellist, pid_t pid);
 extern int show_perf_probe_events(void);
 extern int show_line_range(struct line_range *lr);
-extern int show_possible_probes(struct strlist *availlist);
-
+extern int show_possible_probes(struct strlist *availlist, pid_t pid);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024

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

* Re: [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order.
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order Srikar Dronamraju
@ 2010-08-25 23:21   ` Arnaldo Carvalho de Melo
  2010-08-26  4:32     ` Srikar Dronamraju
  2010-08-30  8:35   ` [tip:perf/core] perf symbols: List symbols in a dso in ascending name order tip-bot for Srikar Dronamraju
  1 sibling, 1 reply; 71+ messages in thread
From: Arnaldo Carvalho de Melo @ 2010-08-25 23:21 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney

Em Wed, Aug 25, 2010 at 07:13:29PM +0530, Srikar Dronamraju escreveu:
> Given a dso, list the symbols in ascending order. Needed for listing
> available symbols from perf-probe.

Applied after renaming it to 'dso__fprintf_symbols_by_name', as at first
I was scratching my head to figure out if we could reuse it in
dso__fprintf() to then notice that it is in ascending _name_ order, not
the default that is ordered by addr :-)

Please fixup the users, i.e. perf probe in your patchset.

- Arnaldo

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

* Re: [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order.
  2010-08-25 23:21   ` Arnaldo Carvalho de Melo
@ 2010-08-26  4:32     ` Srikar Dronamraju
  0 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-26  4:32 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney

* Arnaldo Carvalho de Melo <acme@infradead.org> [2010-08-25 20:21:16]:

> Em Wed, Aug 25, 2010 at 07:13:29PM +0530, Srikar Dronamraju escreveu:
> > Given a dso, list the symbols in ascending order. Needed for listing
> > available symbols from perf-probe.
> 
> Applied after renaming it to 'dso__fprintf_symbols_by_name', as at first
> I was scratching my head to figure out if we could reuse it in
> dso__fprintf() to then notice that it is in ascending _name_ order, not
> the default that is ordered by addr :-)

Okay, Thanks for pushing it up.

> 
> Please fixup the users, i.e. perf probe in your patchset.

Yes, Shall send the refreshed patch now

-- 
Thanks and Regards
Srikar



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

* Re: [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer Srikar Dronamraju
@ 2010-08-26  6:02   ` Masami Hiramatsu
  2010-08-27  9:31     ` Srikar Dronamraju
  2010-08-27 14:10     ` [PATCHv11a " Srikar Dronamraju
  0 siblings, 2 replies; 71+ messages in thread
From: Masami Hiramatsu @ 2010-08-26  6:02 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

(2010/08/25 22:43), Srikar Dronamraju wrote:
> Selecting CONFIG_PROBE_EVENTS enables both kprobe-based and
> uprobes-based dynamic events. However kprobe-tracer or uprobe-tracer
> can still be individually selected or disabled.
> 
> Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
> Suggested-by: Frederic Weisbecker <fweisbec@gmail.com>
> ---
>  kernel/trace/Kconfig |   51 +++++++++++++++++++++++++++++---------------------
>  1 files changed, 30 insertions(+), 21 deletions(-)
> 
> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> index 55ba474..205c12b 100644
> --- a/kernel/trace/Kconfig
> +++ b/kernel/trace/Kconfig
> @@ -76,7 +76,7 @@ config RING_BUFFER_ALLOW_SWAP
>  # All tracer options should select GENERIC_TRACER. For those options that are
>  # enabled by all tracers (context switch and event tracer) they select TRACING.
>  # This allows those options to appear when no other tracer is selected. But the
> -# options do not appear when something else selects it. We need the two options
> +# options do not appear when something else selects it. We need the two option

Why are lots of end-of-line 's' character missing? :)


>  # GENERIC_TRACER and TRACING to avoid circular dependencies to accomplish the
>  # hiding of the automatic options.
>  
> @@ -162,7 +162,7 @@ config IRQSOFF_TRACER
>  	  This option measures the time spent in irqs-off critical
>  	  sections, with microsecond accuracy.
>  
> -	  The default measurement method is a maximum search, which is
> +	  The default measurement method is a maximum search, which i
>  	  disabled by default and can be runtime (re-)started
>  	  via:
>  
> @@ -184,7 +184,7 @@ config PREEMPT_TRACER
>  	  This option measures the time spent in preemption-off critical
>  	  sections, with microsecond accuracy.
>  
> -	  The default measurement method is a maximum search, which is
> +	  The default measurement method is a maximum search, which i
>  	  disabled by default and can be runtime (re-)started
>  	  via:
>  
> @@ -228,7 +228,7 @@ choice
>  	prompt "Branch Profiling"
>  	default BRANCH_PROFILE_NONE
>  	help
> -	 The branch profiling is a software profiler. It will add hooks
> +	 The branch profiling is a software profiler. It will add hook
>  	 into the C conditionals to test which path a branch takes.
>  
>  	 The likely/unlikely profiler only looks at the conditions that
> @@ -252,12 +252,12 @@ config PROFILE_ANNOTATED_BRANCHES
>  	bool "Trace likely/unlikely profiler"
>  	select TRACE_BRANCH_PROFILING
>  	help
> -	  This tracer profiles all the the likely and unlikely macros
> +	  This tracer profiles all the the likely and unlikely macro
>  	  in the kernel. It will display the results in:
>  
>  	  /sys/kernel/debug/tracing/profile_annotated_branch
>  
> -	  Note: this will add a significant overhead; only turn this
> +	  Note: this will add a significant overhead; only turn thi
>  	  on if you need to profile the system's use of these macros.
>  
>  config PROFILE_ALL_BRANCHES
> @@ -333,7 +333,7 @@ config BLK_DEV_IO_TRACE
>  	select GENERIC_TRACER
>  	select STACKTRACE
>  	help
> -	  Say Y here if you want to be able to trace the block layer actions
> +	  Say Y here if you want to be able to trace the block layer action
>  	  on a given queue. Tracing allows you to see any traffic happening
>  	  on a block device queue. For more information (and the userspace
>  	  support tools needed), fetch the blktrace tools from:
> @@ -351,9 +351,8 @@ config BLK_DEV_IO_TRACE
>  config KPROBE_EVENT
>  	depends on KPROBES
>  	depends on HAVE_REGS_AND_STACK_ACCESS_API
> +	depends on PROBE_EVENTS
>  	bool "Enable kprobes-based dynamic events"
> -	select TRACING
> -	select PROBE_EVENTS
>  	default y
>  	help
>  	  This allows the user to add tracing events (similar to tracepoints)
> @@ -370,10 +369,9 @@ config UPROBE_EVENT
>  	bool "Enable uprobes-based dynamic events"
>  	depends on ARCH_SUPPORTS_UPROBES
>  	depends on MMU
> +	depends on PROBE_EVENTS
>  	select UPROBES
> -	select PROBE_EVENTS
> -	select TRACING
> -	default n
> +	default y
>  	help
>  	  This allows the user to add tracing events on top of userspace dynamic
>  	  events (similar to tracepoints) on the fly via the traceevents interface.
> @@ -383,7 +381,18 @@ config UPROBE_EVENT
>  	  tools on user space applications.
>  
>  config PROBE_EVENTS
> -	def_bool n
> +	bool "Enable kprobes and uprobe based dynamic events"
> +	select TRACING
> +	default n

Hmm, without this series, KPROBE_EVENT is set "y" by default.
(PROBE_EVENTS is introduced by 8/15)
I'd like to set this "y" by default, because it doesn't
affect other parts.

Thank you,

> +	help
> +	  This allows a user to add dynamic tracing events in
> +	  kernel using kprobe-tracer and in userspace using
> +	  uprobe-tracer. However users can still selectively
> +	  disable one of these events.
> +
> +	  For more information on kprobe-tracer and uprobe-tracer
> +	  please refer help under KPROBE_EVENT and UPROBE_EVENT
> +	  respectively.
>  
>  config DYNAMIC_FTRACE
>  	bool "enable/disable ftrace tracepoints dynamically"
> @@ -393,14 +402,14 @@ config DYNAMIC_FTRACE
>  	help
>            This option will modify all the calls to ftrace dynamically
>  	  (will patch them out of the binary image and replace them
> -	  with a No-Op instruction) as they are called. A table is
> +	  with a No-Op instruction) as they are called. A table i
>  	  created to dynamically enable them again.
>  
>  	  This way a CONFIG_FUNCTION_TRACER kernel is slightly larger, but
>  	  otherwise has native performance as long as no tracing is active.
>  
>  	  The changes to the code are done by a kernel thread that
> -	  wakes up once a second and checks to see if any ftrace calls
> +	  wakes up once a second and checks to see if any ftrace call
>  	  were made. If so, it runs stop_machine (stops all CPUS)
>  	  and modifies the code to jump over the call to ftrace.
>  
> @@ -432,7 +441,7 @@ config FTRACE_STARTUP_TEST
>  	select FTRACE_SELFTEST
>  	help
>  	  This option performs a series of startup tests on ftrace. On bootup
> -	  a series of tests are made to verify that the tracer is
> +	  a series of tests are made to verify that the tracer i
>  	  functioning properly. It will do tests on all the configured
>  	  tracers of ftrace.
>  
> @@ -441,12 +450,12 @@ config EVENT_TRACE_TEST_SYSCALLS
>  	depends on FTRACE_STARTUP_TEST
>  	help
>  	 This option will also enable testing every syscall event.
> -	 It only enables the event and disables it and runs various loads
> +	 It only enables the event and disables it and runs various load
>  	 with the event enabled. This adds a bit more time for kernel boot
>  	 up since it runs this on every system call defined.
>  
>  	 TBD - enable a way to actually call the syscalls as we test their
> -	       events
> +	       event
>  
>  config MMIOTRACE
>  	bool "Memory mapped IO tracing"
> @@ -465,7 +474,7 @@ config MMIOTRACE_TEST
>  	tristate "Test module for mmiotrace"
>  	depends on MMIOTRACE && m
>  	help
> -	  This is a dumb module for testing mmiotrace. It is very dangerous
> +	  This is a dumb module for testing mmiotrace. It is very dangerou
>  	  as it will write garbage to IO memory starting at a given address.
>  	  However, it should be safe to use on e.g. unused portion of VRAM.
>  
> @@ -477,9 +486,9 @@ config RING_BUFFER_BENCHMARK
>  	help
>  	  This option creates a test to stress the ring buffer and benchmark it.
>  	  It creates its own ring buffer such that it will not interfere with
> -	  any other users of the ring buffer (such as ftrace). It then creates
> +	  any other users of the ring buffer (such as ftrace). It then create
>  	  a producer and consumer that will run for 10 seconds and sleep for
> -	  10 seconds. Each interval it will print out the number of events
> +	  10 seconds. Each interval it will print out the number of event
>  	  it recorded and give a rough estimate of how long each iteration took.
>  
>  	  It does not disable interrupts or raise its priority, so it may be


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

* Re: [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-26  6:02   ` Masami Hiramatsu
@ 2010-08-27  9:31     ` Srikar Dronamraju
  2010-08-27 11:04       ` Masami Hiramatsu
  2010-08-27 14:10     ` [PATCHv11a " Srikar Dronamraju
  1 sibling, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-27  9:31 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

> 
> Why are lots of end-of-line 's' character missing? :)
> 

Okay will correct them.

<snipped>

> >  
> >  config PROBE_EVENTS
> > -	def_bool n
> > +	bool "Enable kprobes and uprobe based dynamic events"
> > +	select TRACING
> > +	default n
> 
> Hmm, without this series, KPROBE_EVENT is set "y" by default.
> (PROBE_EVENTS is introduced by 8/15)
> I'd like to set this "y" by default, because it doesn't
> affect other parts.
> 
> Thank you,
> 

This is based on what we discussed here
http://lkml.org/lkml/2010/8/2/86.

To recollect, 
Frederic wanted that there should be one option to select both
UPROBE_EVENT and KPROBE_EVENT. 

However if we make PROBE_EVENTS (which is the option to enable both
events) default "Y", then both UPROBE_EVENT and KPROBE_EVENT will be
selected.

Also if we look at http://lkml.org/lkml/2010/6/21/160, Steven
Rostedt didnt want UPROBE_EVENT to selected by default.

I agree that we should keep UPROBE_EVENT to be 'default n' till it gets
tested. Hence we have two choices. Either set the common knob to be
'default n' or dont have the common knob for now (i.e drop this
patch for now).

I think we should go with the first one, i.e have a common knob thats
by default unselected.

-- 
Thanks and Regards
Sriakr

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

* Re: [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-27  9:31     ` Srikar Dronamraju
@ 2010-08-27 11:04       ` Masami Hiramatsu
  2010-08-27 12:17         ` Srikar Dronamraju
  0 siblings, 1 reply; 71+ messages in thread
From: Masami Hiramatsu @ 2010-08-27 11:04 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

(2010/08/27 18:31), Srikar Dronamraju wrote:
>>>  config PROBE_EVENTS
>>> -	def_bool n
>>> +	bool "Enable kprobes and uprobe based dynamic events"
>>> +	select TRACING
>>> +	default n
>>
>> Hmm, without this series, KPROBE_EVENT is set "y" by default.
>> (PROBE_EVENTS is introduced by 8/15)
>> I'd like to set this "y" by default, because it doesn't
>> affect other parts.
>>
>> Thank you,
>>
> 
> This is based on what we discussed here
> http://lkml.org/lkml/2010/8/2/86.
> 
> To recollect, 
> Frederic wanted that there should be one option to select both
> UPROBE_EVENT and KPROBE_EVENT. 
> 
> However if we make PROBE_EVENTS (which is the option to enable both
> events) default "Y", then both UPROBE_EVENT and KPROBE_EVENT will be
> selected.
> 
> Also if we look at http://lkml.org/lkml/2010/6/21/160, Steven
> Rostedt didnt want UPROBE_EVENT to selected by default.
> 
> I agree that we should keep UPROBE_EVENT to be 'default n' till it gets
> tested. Hence we have two choices. Either set the common knob to be
> 'default n' or dont have the common knob for now (i.e drop this
> patch for now).
> 
> I think we should go with the first one, i.e have a common knob thats
> by default unselected.

Yeah, I'm OK to have a common knob, but I just don't like to set
KPROBE_EVENT unselected by default. I think there is no reason
to change default selecting (currently, KPROBE_EVENT=y by default.)

So, I think we should have below selecting list;
--- Tracers
...
[*] Enable dynamic events
   [ ] Enable user-space dynamic events (EXPERIMENTAL)
...

What would you think about this ? :)

Thank you,


-- 
Masami HIRAMATSU
2nd Dept. Linux Technology Center
Hitachi, Ltd., Systems Development Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com

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

* Re: [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-27 11:04       ` Masami Hiramatsu
@ 2010-08-27 12:17         ` Srikar Dronamraju
  2010-08-27 15:37           ` Masami Hiramatsu
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-27 12:17 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

> > 
> > I think we should go with the first one, i.e have a common knob thats
> > by default unselected.
> 
> Yeah, I'm OK to have a common knob, but I just don't like to set
> KPROBE_EVENT unselected by default. I think there is no reason
> to change default selecting (currently, KPROBE_EVENT=y by default.)

I understand your concern.

> 
> So, I think we should have below selecting list;
> --- Tracers
> ...
> [*] Enable dynamic events
>    [ ] Enable user-space dynamic events (EXPERIMENTAL)
> ...
> 
> What would you think about this ? :)
> 
> Thank you,


Wouldnt it negate the purpose of common knob?
Because people would still have go and select UPROBE_EVENTS,

I think when Frederic asked for a common knob, he was looking at
enabling both or disabling both and an option to selectively
select one of the tracers. 

--
Thanks and Regards
Srikar

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

* [PATCHv11a 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-26  6:02   ` Masami Hiramatsu
  2010-08-27  9:31     ` Srikar Dronamraju
@ 2010-08-27 14:10     ` Srikar Dronamraju
  1 sibling, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-27 14:10 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

Selecting CONFIG_PROBE_EVENTS enables both kprobe-based and
uprobes-based dynamic events. However kprobe-tracer or uprobe-tracer
can still be individually selected or disabled.

Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Suggested-by: Frederic Weisbecker <fweisbec@gmail.com>
---

Changelog from V10: Fixed few erroneous changes: missing s at eol.
	reported by Masami Hiramatsu.
 
 kernel/trace/Kconfig |   21 +++++++++++++++------
 1 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 55ba474..77e04b0 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -351,9 +351,8 @@ config BLK_DEV_IO_TRACE
 config KPROBE_EVENT
 	depends on KPROBES
 	depends on HAVE_REGS_AND_STACK_ACCESS_API
+	depends on PROBE_EVENTS
 	bool "Enable kprobes-based dynamic events"
-	select TRACING
-	select PROBE_EVENTS
 	default y
 	help
 	  This allows the user to add tracing events (similar to tracepoints)
@@ -370,10 +369,9 @@ config UPROBE_EVENT
 	bool "Enable uprobes-based dynamic events"
 	depends on ARCH_SUPPORTS_UPROBES
 	depends on MMU
+	depends on PROBE_EVENTS
 	select UPROBES
-	select PROBE_EVENTS
-	select TRACING
-	default n
+	default y
 	help
 	  This allows the user to add tracing events on top of userspace dynamic
 	  events (similar to tracepoints) on the fly via the traceevents interface.
@@ -383,7 +381,18 @@ config UPROBE_EVENT
 	  tools on user space applications.
 
 config PROBE_EVENTS
-	def_bool n
+	bool "Enable kprobes and uprobe based dynamic events"
+	select TRACING
+	default n
+	help
+	  This allows a user to add dynamic tracing events in
+	  kernel using kprobe-tracer and in userspace using
+	  uprobe-tracer. However users can still selectively
+	  disable one of these events.
+
+	  For more information on kprobe-tracer and uprobe-tracer
+	  please refer help under KPROBE_EVENT and UPROBE_EVENT
+	  respectively.
 
 config DYNAMIC_FTRACE
 	bool "enable/disable ftrace tracepoints dynamically"

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

* [PATCHv11a 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file.
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file Srikar Dronamraju
@ 2010-08-27 14:21   ` Srikar Dronamraju
  2010-10-20  9:56     ` Masami Hiramatsu
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-08-27 14:21 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar
  Cc: Steven Rostedt, Randy Dunlap, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney

Introduces -S/--show_functions option for perf-probe.
This lists function names in a File. If no file is specified, then lists
functions in the current running kernel.

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

Changelog from V11: accomodate name change dso_fprintf_symbols_by_name.

Changelog from V10: As suggested by Arnaldo, filtering is now
 based on sym.binding.

Changelog from V9: Filter labels, weak, and local binding functions
from listing as suggested by Christoph Hellwig.

Show last 10 functions in /bin/zsh.

# perf probe -S -D /bin/zsh | tail
zstrtol
ztrcmp
ztrdup
ztrduppfx
ztrftime
ztrlen
ztrncpy
ztrsub
zwarn
zwarnnam

Show first 10 functions in /lib/libc.so.6

# perf probe -S -D /lib/libc.so.6 | head
_IO_adjust_column
_IO_adjust_wcolumn
_IO_default_doallocate
_IO_default_finish
_IO_default_pbackfail
_IO_default_uflow
_IO_default_xsgetn
_IO_default_xsputn
_IO_do_write@@GLIBC_2.2.5
_IO_doallocbuf

Show last 10 functions in kernel.

# perf probe -S | tail
zlib_inflateInit2
zlib_inflateReset
zlib_inflate_blob
zlib_inflate_table
zlib_inflate_workspacesize
zone_pcp_update
zone_reclaim
zone_reclaimable_pages
zone_statistics
zone_watermark_ok
---
 tools/perf/builtin-probe.c    |   43 ++++++++++++++++++++++++
 tools/perf/util/probe-event.c |   72 +++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/probe-event.h |    1 +
 tools/perf/util/symbol.c      |    8 +++++
 tools/perf/util/symbol.h      |    1 +
 5 files changed, 124 insertions(+), 1 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 199d5e1..fa63245 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -50,9 +50,11 @@ static struct {
 	bool list_events;
 	bool force_add;
 	bool show_lines;
+	bool list_functions;
 	int nevents;
 	struct perf_probe_event events[MAX_PROBES];
 	struct strlist *dellist;
+	struct strlist *limitlist;
 	struct line_range line_range;
 	int max_probe_points;
 } params;
@@ -132,6 +134,19 @@ static int opt_show_lines(const struct option *opt __used,
 }
 #endif
 
+static int opt_limit_dsos(const struct option *opt __used,
+			       const char *str, int unset __used)
+{
+	if (str) {
+		if (!params.limitlist)
+			params.limitlist = strlist__new(true, NULL);
+		if (!params.limitlist)
+			return -1;
+		strlist__add(params.limitlist, str);
+	}
+	return 0;
+}
+
 static const char * const probe_usage[] = {
 	"perf probe [<options>] 'PROBEDEF' ['PROBEDEF' ...]",
 	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
@@ -188,6 +203,10 @@ static const struct option options[] = {
 	OPT__DRY_RUN(&probe_event_dry_run),
 	OPT_INTEGER('\0', "max-probes", &params.max_probe_points,
 		 "Set how many probe points can be found for a probe."),
+	OPT_BOOLEAN('S', "show_functions", &params.list_functions,
+				"Show potential probes."),
+	OPT_CALLBACK('D', "limit_dsos", NULL, "DSO",
+				"Limit Dsos.", opt_limit_dsos),
 	OPT_END()
 };
 
@@ -213,7 +232,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		params.max_probe_points = MAX_PROBES;
 
 	if ((!params.nevents && !params.dellist && !params.list_events &&
-	     !params.show_lines))
+	     !params.show_lines && !params.list_functions))
 		usage_with_options(probe_usage, options);
 
 	if (params.list_events) {
@@ -225,12 +244,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			pr_err("  Error: Don't use --list with --line.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (params.list_functions) {
+			pr_warning("  Error: Don't use --list with"
+						" --show_functions.\n");
+			usage_with_options(probe_usage, options);
+		}
 		ret = show_perf_probe_events();
 		if (ret < 0)
 			pr_err("  Error: Failed to show event list. (%d)\n",
 			       ret);
 		return ret;
 	}
+	if (params.list_functions) {
+		if (params.nevents != 0 || params.dellist) {
+			pr_err("  Error: Don't use --show_functions with"
+					" --add/--del.\n");
+			usage_with_options(probe_usage, options);
+		}
+		if (params.show_lines) {
+			pr_err("  Error: Don't use --show_functions with"
+					" --line.\n");
+			usage_with_options(probe_usage, options);
+		}
+		ret = show_possible_probes(params.limitlist);
+		if (ret < 0)
+			pr_err("  Error: Failed to show possible"
+					" probes (%d).\n", ret);
+		return ret;
+	}
 
 #ifdef DWARF_SUPPORT
 	if (params.show_lines) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index e72f05c..be90836 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <limits.h>
+#include <gelf.h>
 
 #undef _GNU_SOURCE
 #include "util.h"
@@ -46,6 +47,7 @@
 #include "trace-event.h"	/* For __unused */
 #include "probe-event.h"
 #include "probe-finder.h"
+#include "session.h"
 
 #define MAX_CMDLEN 256
 #define MAX_PROBE_ARGS 128
@@ -1752,3 +1754,73 @@ int del_perf_probe_events(struct strlist *dellist)
 	return ret;
 }
 
+/*
+ * If a symbol corresponds to a function with global binding return 0.
+ * For all others return 1.
+ */
+static int filter_non_global_functions(struct map *map __unused,
+						struct symbol *sym)
+{
+	if (sym->binding != STB_GLOBAL)
+		return 1;
+
+	return 0;
+}
+
+static int print_list_available_symbols(struct map *map,
+					const char *name __unused)
+{
+	if (map__load(map, filter_non_global_functions) < 0)
+		return -EINVAL;
+	if (!dso__sorted_by_name(map->dso, map->type))
+		dso__sort_by_name(map->dso, map->type);
+
+	dso__fprintf_symbols_by_name(map->dso, map->type, stdout);
+	return 0;
+}
+
+int show_possible_probes(struct strlist *limitlist)
+{
+	struct str_node *ent;
+	struct map *map = NULL;
+	char *str;
+	int ret = -EINVAL;
+
+	if (!limitlist) { /* Show functions in kernel */
+		ret = init_vmlinux();
+		if (ret < 0)
+			return ret;
+		return print_list_available_symbols(
+				machine.vmlinux_maps[MAP__FUNCTION], NULL);
+	}
+
+	symbol_conf.try_vmlinux_path = false;
+	symbol_conf.sort_by_name = true;
+	ret = symbol__init();
+	if (ret < 0) {
+		pr_debug("Failed to init symbol map.\n");
+		return ret;
+	}
+
+	strlist__for_each(ent, limitlist) {
+		str = strdup(ent->s);
+		if (!str) {
+			ret = -ENOMEM;
+			goto out_delete;
+		}
+
+		map = dso__new_map(str);
+		ret = print_list_available_symbols(map, NULL);
+		if (ret)
+			pr_warning("No Symbols in dso %s.\n", str);
+
+		dso__delete(map->dso);
+		map__delete(map);
+		free(str);
+	}
+
+out_delete:
+	if (ret)
+		pr_warning("Failed to list available probes.\n");
+	return ret;
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5af3924..fcd0f08 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -115,6 +115,7 @@ extern int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
 extern int del_perf_probe_events(struct strlist *dellist);
 extern int show_perf_probe_events(void);
 extern int show_line_range(struct line_range *lr);
+extern int show_possible_probes(struct strlist *availlist);
 
 
 /* Maximum index number of event-name postfix */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index a08e1cb..1728d71 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -2464,3 +2464,11 @@ int machine__load_vmlinux_path(struct machine *self, enum map_type type,
 
 	return ret;
 }
+
+struct map *dso__new_map(char *name)
+{
+	struct dso *dso = dso__new(name);
+	struct map *map = map__new2(0, dso, MAP__FUNCTION);
+
+	return map;
+}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 0a2c460..145953d 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -202,6 +202,7 @@ char dso__symtab_origin(const struct dso *self);
 void dso__set_long_name(struct dso *self, char *name);
 void dso__set_build_id(struct dso *self, void *build_id);
 void dso__read_running_kernel_build_id(struct dso *self, struct machine *machine);
+struct map *dso__new_map(char *name);
 struct symbol *dso__find_symbol(struct dso *self, enum map_type type, u64 addr);
 struct symbol *dso__find_symbol_by_name(struct dso *self, enum map_type type,
 					const char *name);

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

* Re: [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer.
  2010-08-27 12:17         ` Srikar Dronamraju
@ 2010-08-27 15:37           ` Masami Hiramatsu
  0 siblings, 0 replies; 71+ messages in thread
From: Masami Hiramatsu @ 2010-08-27 15:37 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

(2010/08/27 21:17), Srikar Dronamraju wrote:
>>>
>>> I think we should go with the first one, i.e have a common knob thats
>>> by default unselected.
>>
>> Yeah, I'm OK to have a common knob, but I just don't like to set
>> KPROBE_EVENT unselected by default. I think there is no reason
>> to change default selecting (currently, KPROBE_EVENT=y by default.)
> 
> I understand your concern.
> 
>>
>> So, I think we should have below selecting list;
>> --- Tracers
>> ...
>> [*] Enable dynamic events
>>    [ ] Enable user-space dynamic events (EXPERIMENTAL)
>> ...
>>
>> What would you think about this ? :)
>>
>> Thank you,
> 
> 
> Wouldnt it negate the purpose of common knob?
> Because people would still have go and select UPROBE_EVENTS,

Hmm, I think this just seems an enhancement of dynamic events,
and also you can enable it by default on some point.
I mean, eventually, there will be only "Enable dynamic events"
on the screen.

> I think when Frederic asked for a common knob, he was looking at
> enabling both or disabling both and an option to selectively
> select one of the tracers. 

Yeah, but I'd like to ask Frederic that he expected disabling
KPROBE_EVENT by default too, even though it changes current
default config.

Thank you,

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

* [tip:perf/core] perf symbols: List symbols in a dso in ascending name order
  2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order Srikar Dronamraju
  2010-08-25 23:21   ` Arnaldo Carvalho de Melo
@ 2010-08-30  8:35   ` tip-bot for Srikar Dronamraju
  1 sibling, 0 replies; 71+ messages in thread
From: tip-bot for Srikar Dronamraju @ 2010-08-30  8:35 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, mingo, mathieu.desnoyers, naren.devaiah, fweisbec, rostedt,
	jkenisto, akpm, mjw, oleg, tglx, linux-kernel, hpa, fche,
	rdunlap, hch, ananth, paulmck, masami.hiramatsu.pt, srikar

Commit-ID:  90f18e63fbd005133624bf18a5e8b75c92e90f4d
Gitweb:     http://git.kernel.org/tip/90f18e63fbd005133624bf18a5e8b75c92e90f4d
Author:     Srikar Dronamraju <srikar@linux.vnet.ibm.com>
AuthorDate: Wed, 25 Aug 2010 19:13:29 +0530
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Wed, 25 Aug 2010 17:28:59 -0300

perf symbols: List symbols in a dso in ascending name order

Given a dso, list the symbols in ascending name order. Needed for
listing available symbols from perf probe.

Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: "Frank Ch. Eigler" <fche@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jim Keniston <jkenisto@linux.vnet.ibm.com>
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Cc: Naren A Devaiah <naren.devaiah@in.ibm.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Cc: Randy Dunlap <rdunlap@xenotime.net>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
LKML-Reference: <20100825134329.5447.92261.sendpatchset@localhost6.localdomain6>
Signed-off-by: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/symbol.c |   14 ++++++++++++++
 tools/perf/util/symbol.h |    1 +
 2 files changed, 15 insertions(+), 0 deletions(-)

diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 1a36773..a08e1cb 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -388,6 +388,20 @@ size_t dso__fprintf_buildid(struct dso *self, FILE *fp)
 	return fprintf(fp, "%s", sbuild_id);
 }
 
+size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp)
+{
+	size_t ret = 0;
+	struct rb_node *nd;
+	struct symbol_name_rb_node *pos;
+
+	for (nd = rb_first(&self->symbol_names[type]); nd; nd = rb_next(nd)) {
+		pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
+		fprintf(fp, "%s\n", pos->sym.name);
+	}
+
+	return ret;
+}
+
 size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp)
 {
 	struct rb_node *nd;
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index b7a8da4..0a2c460 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -181,6 +181,7 @@ size_t machines__fprintf_dsos(struct rb_root *self, FILE *fp);
 size_t machines__fprintf_dsos_buildid(struct rb_root *self, FILE *fp, bool with_hits);
 
 size_t dso__fprintf_buildid(struct dso *self, FILE *fp);
+size_t dso__fprintf_symbols_by_name(struct dso *self, enum map_type type, FILE *fp);
 size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp);
 
 enum dso_origin {

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

* Re: [PATCHv11 2.6.36-rc2-tip 2/15]  2: uprobes: Breakpoint insertion/removal in user space applications.
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 2/15] 2: uprobes: Breakpoint insertion/removal in user space applications Srikar Dronamraju
@ 2010-09-01 19:38   ` Peter Zijlstra
  0 siblings, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-01 19:38 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Ananth N Mavinakayanahalli, Oleg Nesterov,
	Mark Wielaard, Mathieu Desnoyers, LKML, Naren A Devaiah,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Andrew Morton, Paul E. McKenney

On Wed, 2010-08-25 at 19:11 +0530, Srikar Dronamraju wrote:

> +struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;

That really wants to be static, 'arch' is a way too generic a name to
inject in the global namespace.

> +unsigned long uprobes_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);
> +}
> +

> +unsigned long uprobes_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);
> +}

either: s/uprobes_read_vm/uprobes_read_data/ or
s/uproves_write_data/uproves_write_vm/

> +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 *vaddr_old, *vaddr_new;
> +	pte_t orig_pte;
> +	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, 1, 1, &old_page, &vma);
> +	if (ret <= 0)
> +		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) {

 s/&&/&/

> +		ret = -EINVAL;
> +		goto put_out;
> +	}
> +
> +	/* If its VM_SHARED vma, lets not write to such vma's.  */
> +	if (vma->vm_flags & VM_SHARED) {
> +		ret = -EINVAL;
> +		goto put_out;
> +	}

Something like:

  /* private, read-only, executable maps only */
  if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) != (VM_READ|VM_EXEC))

maybe?

> +	/* Allocate a page */
> +	new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr);
> +	if (!new_page) {
> +		ret = -ENOMEM;
> +		goto put_out;
> +	}

> +int __weak set_orig_insn(struct task_struct *tsk,
> +			struct user_bkpt *user_bkpt, bool verify)
> +{
> +	if (verify) {
> +		user_bkpt_opcode_t opcode;
> +		int result = read_opcode(tsk, user_bkpt->vaddr, &opcode);
> +		if (result)
> +			return result;
> +		if (opcode != arch->bkpt_insn)

This assumes user_bkpt_opcode_t is a scalar value, but there's no
assertion of that, if someone were to define it like char[5] or somesuch
the comparison would still compile but not do what you'd expect.

> +			return -EINVAL;
> +	}
> +	return write_opcode(tsk, user_bkpt->vaddr, user_bkpt->opcode);
> +}

> +/**
> + * check_vma - verify if the address is in a executable vma.
> + * @tsk: the probed task
> + * @vaddr: virtual address of the instruction to be verified.
> + *
> + * Return 0 if vaddr is in an executable VM area,
> + * or -EINVAL otherwise.
> + */
> +static 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))

you fail to check vma->vm_end

Also, do we want to do the full private,ro,exec check here again?

> +		ret = 0;
> +	up_read(&mm->mmap_sem);
> +	mmput(mm);
> +	return ret;
> +}

> +int __weak validate_address(struct task_struct *tsk, unsigned long vaddr)
> +{
> +	return check_vma(tsk, vaddr);
> +}

So here check_vma() is the default implementation of validate_address(),
so why not name them accordingly?

> +/*
> + * __insert_bkpt - insert breakpoint
> + * Insert a breakpoint into the process that includes @tsk, at the
> + * virtual address @user_bkpt->vaddr.
> + *
> + * All threads of the probed process must be stopped while
> + * @__insert_bkpt() runs.

I hope not,.. the pte swizzle we do above does not require any such
thing, stale comment?

> + * Possible errors:
> + * -%ENOSYS: user_bkpt not supported for this architecture
> + * -%EINVAL: invalid instruction address
> + * -%EEXIST: breakpoint instruction already exists at that address
> + * -%EPERM: cannot probe this instruction
> + * -%EFAULT: failed to insert breakpoint instruction
> + */



> +static int pre_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
> +		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
> +{
> +	return pre_xol(tsk, user_bkpt, tskinfo, regs);
> +}
> +

> +static int post_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
> +		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
> +{
> +	return post_xol(tsk, user_bkpt, tskinfo, regs);
> +}

What's the point of these functions?


> +static int __remove_bkpt(struct task_struct *tsk,
> +					struct user_bkpt *user_bkpt)
> +{
> +	if (validate_address(tsk, user_bkpt->vaddr) != 0)
> +		return -EINVAL;
> +	return set_orig_insn(tsk, user_bkpt, true);
> +}

Why would we even consider calling this function on something that would
fail the validate_address() test? If that fails we would not have
installed the breakpoint to begin with, hence there would be no reason
to remove it.

> +bool __weak is_bkpt_insn(struct user_bkpt *user_bkpt)
> +{
> +	return (user_bkpt->opcode == arch->bkpt_insn);
> +}

Again, assumes the instruction thing is a scalar.



The big thing I'm missing in this patch is generic code handling the
actual breakpoint.. but maybe that's somewhere in the next patches.. /me
goes look.



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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 3/15] 3: uprobes: Slot allocation for Execution out of line(XOL) Srikar Dronamraju
@ 2010-09-01 20:13   ` Peter Zijlstra
  2010-09-03 16:40     ` Srikar Dronamraju
  2010-09-02  8:23   ` Peter Zijlstra
  1 sibling, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-01 20:13 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Wed, 2010-08-25 at 19:11 +0530, Srikar Dronamraju wrote:
>  
> +/* Slot allocation for XOL */
> +
> +/*
> + * 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.
> + */
> +
> +struct uprobes_xol_area {
> +	spinlock_t lock;	/* protects bitmap and slot (de)allocation*/
> +	unsigned long *bitmap;	/* 0 = free slot */

Since you have a static sized bitmap, why not simply declare it here?

	DECLARE_BITMAP(bitmap, MAX_UPROBES_XOL_SLOTS;

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

Naughty kernel modules we don't care about, but yeah, it appears vma's
installed using install_special_mapping() can be unmapped by the process
itself,.. curious. 

Anyway, you could install your own vm_ops and provide a close method to
track this.

> +	unsigned long vaddr;		/* Page(s) of instruction slots */
> +};
> +
> +static int xol_add_vma(struct uprobes_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 "uprobes_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 "uprobes_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;

Seems interesting,.. why not use install_special_mapping(), that's what
the VDSO uses.

> +	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 uprobes_xol_area.
> + * This area will be used for storing instructions for execution out of
> + * line.

It doesn't actually do that, xol_add_vma() does that, this allocates the
storage management bits.

> + * Called with mm->uproc->mutex locked.

There's a nice way to not have to write that:

  lockdep_assert_held(&mm->uproc->mutex);

> + * Returns the allocated area or NULL.
> + */


> +/*
> + * xol_free_area - Free the area allocated for slots.

Again, it doesn't actually free the slots itself.

> + * @xol_area refers the unique per process uprobes_xol_area for
> + * this process.
> + *
> + */


> +/*
> + * Find a slot
> + *  - searching in existing vmas for a free slot.
> + *  - If no free slot in existing vmas, return 0;

I would call that allocate, find would imply a constant operation, but
you actually change the state.

> + * Called when holding xol_area->lock

  lockdep_assert_held(&area->lock);

> + */
> +static unsigned long xol_take_insn_slot(struct uprobes_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);

Since its all serialized by xol_area->lock, why use an atomic bitop?

> +		slot_addr = area->vaddr +
> +				(slot_nr * UPROBES_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 uprobes_insert_bkpt is already called, (i.e
> + * user_bkpt.vaddr != 0) then copy the instruction into the slot.
> + * @user_bkpt: probepoint information
> + * @xol_area refers the unique per process uprobes_xol_area for
> + * this process.
> + *
> + * Called with mm->uproc->mutex locked.
> + * Returns the allocated slot address or 0.
> + */
> +static unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
> +				struct uprobes_xol_area *xol_area)
> +{
> +	unsigned long flags, xol_vaddr = 0;
> +	int len;
> +
> +	if (unlikely(!xol_area))
> +		return 0;
> +
> +	if (user_bkpt->xol_vaddr)
> +		return user_bkpt->xol_vaddr;
> +
> +	spin_lock_irqsave(&xol_area->lock, flags);
> +	xol_vaddr = xol_take_insn_slot(xol_area);
> +	spin_unlock_irqrestore(&xol_area->lock, flags);
> +
> +	/*
> +	 * Initialize the slot if user_bkpt->vaddr points to valid
> +	 * instruction slot.
> +	 */
> +	if (likely(xol_vaddr) && user_bkpt->vaddr) {

if (!xol_vaddr)
  goto bail;

gives nices code, and saves an indent level.

Also, why would we ever get here with !user_bkpt->vaddr.

(fwiw, my fingers hate bkpt, they either want to type bp, or brkpt)

> +		len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
> +						UPROBES_XOL_SLOT_BYTES, 1);
> +		if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
> +			printk(KERN_ERR "Failed to copy instruction at %#lx "
> +					"len = %d\n", user_bkpt->vaddr, len);
> +	}
> +
> +	/*
> +	 * Update user_bkpt->xol_vaddr after giving a chance for the slot to
> +	 * be initialized.
> +	 */
> +	mb();

Where is the matching barrier?

> +	user_bkpt->xol_vaddr = xol_vaddr;
> +	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 uprobes_xol_area for
> + * this process.
> + */
> +static void xol_free_insn_slot(unsigned long slot_addr,
> +				struct uprobes_xol_area *xol_area)
> +{
> +	unsigned long vma_end;
> +	int found = 0;
> +
> +	if (unlikely(!slot_addr || IS_ERR_VALUE(slot_addr)))
> +		return;
> +
> +	if (unlikely(!xol_area))
> +		return;
> +
> +	vma_end = xol_area->vaddr + PAGE_SIZE;
> +	if (xol_area->vaddr <= slot_addr && slot_addr < vma_end) {
> +		int slot_nr;
> +		unsigned long offset = slot_addr - xol_area->vaddr;
> +		unsigned long flags;
> +
> +		BUG_ON(offset % UPROBES_XOL_SLOT_BYTES);
> +
> +		slot_nr = offset / UPROBES_XOL_SLOT_BYTES;
> +		BUG_ON(slot_nr >= UINSNS_PER_PAGE);
> +
> +		spin_lock_irqsave(&xol_area->lock, flags);
> +		clear_bit(slot_nr, xol_area->bitmap);

Again, using atomic bitops while already holding a lock... pick one.

> +		spin_unlock_irqrestore(&xol_area->lock, flags);
> +		found = 1;
> +	}
> +
> +	if (!found)
> +		printk(KERN_ERR "%s: no XOL vma for slot address %#lx\n",
> +						__func__, slot_addr);

funny code flow,.. s/found = 1/return/ and loose the conditional and
indent?

> +}
> +
> +/*
> + * 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 uprobes_xol_area for
> + * this process.
> + */
> +static int xol_validate_vaddr(struct pid *pid, unsigned long vaddr,
> +				struct uprobes_xol_area *xol_area)
> +{
> +	struct task_struct *tsk;
> +	unsigned long vma_end;
> +	int result;
> +
> +	if (unlikely(!xol_area))
> +		return 0;
> +
> +	tsk = get_pid_task(pid, PIDTYPE_PID);
> +	if (unlikely(!tsk))
> +		return -EINVAL;
> +
> +	result = validate_address(tsk, vaddr);
> +	if (result != 0)
> +		goto validate_end;
> +
> +	vma_end = xol_area->vaddr + PAGE_SIZE;
> +	if (xol_area->vaddr <= vaddr && vaddr < vma_end)
> +		result = 1;
> +
> +validate_end:
> +	put_task_struct(tsk);
> +	return result;
> +}

This doesn't actually appear used in this patch,.. does it want to live
elsewhere?

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 5/15] 5: uprobes: Uprobes (un)registration and exception handling Srikar Dronamraju
@ 2010-09-01 21:43   ` Peter Zijlstra
  2010-09-02  8:12     ` Peter Zijlstra
  2010-09-03 16:42     ` Srikar Dronamraju
  2010-09-01 21:46   ` Peter Zijlstra
  1 sibling, 2 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-01 21:43 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Wed, 2010-08-25 at 19:12 +0530, Srikar Dronamraju wrote:

> TODO:
> 1. Allow multiple probes at a probepoint.
> 2. Booster probes.
> 3. Allow probes to be inherited across fork.

I wouldn't worry about that, focus on inode attached probes and you get
fork for free.

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

>  arch/Kconfig             |    1 
>  fs/exec.c                |    4 
>  include/linux/mm_types.h |    4 
>  include/linux/sched.h    |    3 
>  include/linux/uprobes.h  |  143 ++++++++++
>  kernel/fork.c            |   21 +
>  kernel/uprobes.c         |  653 ++++++++++++++++++++++++++++++++++++++++++++++
>  7 files changed, 826 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/Kconfig b/arch/Kconfig
> index 87bd26b..c8c8e3f 100644
> --- a/arch/Kconfig
> +++ b/arch/Kconfig
> @@ -49,6 +49,7 @@ config OPTPROBES
>  
>  config UPROBES
>  	bool "User-space probes (EXPERIMENTAL)"
> +	default n
>  	depends on ARCH_SUPPORTS_UPROBES
>  	depends on MMU
>  	help

Seems like a weird place for this hunk, does this want to live
elsewhere?

> diff --git a/fs/exec.c b/fs/exec.c
> index 2d94552..d7e64d0 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -1053,6 +1053,10 @@ void setup_new_exec(struct linux_binprm * bprm)
>  			
>  	flush_signal_handlers(current, 0);
>  	flush_old_files(current->files);
> +#ifdef CONFIG_UPROBES
> +	if (unlikely(current->utask))
> +		uprobe_free_utask(current);
> +#endif

A nicer way is to provide flush_uprobes() unconditionally.

>  }
>  EXPORT_SYMBOL(setup_new_exec);
>  
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index cb57d65..8f79e51 100644
> --- a/include/linux/mm_types.h
> +++ b/include/linux/mm_types.h
> @@ -12,6 +12,7 @@
>  #include <linux/completion.h>
>  #include <linux/cpumask.h>
>  #include <linux/page-debug-flags.h>
> +#include <linux/uprobes.h>
>  #include <asm/page.h>
>  #include <asm/mmu.h>
>  
> @@ -310,6 +311,9 @@ struct mm_struct {
>  #ifdef CONFIG_MMU_NOTIFIER
>  	struct mmu_notifier_mm *mmu_notifier_mm;
>  #endif
> +#ifdef CONFIG_UPROBES
> +	struct uprobe_process *uproc;	/* per mm uprobes info */
> +#endif
>  };

I find the _process postfix a bit weird in this context, how about
something like:

  struct mm_uprobes *mm_uprobes;

instead?

>  /* Future-safe accessor for struct mm_struct's cpu_vm_mask. */
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index e18473f..e237ba2 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -1496,6 +1496,9 @@ struct task_struct {
>  		unsigned long memsw_bytes; /* uncharged mem+swap usage */
>  	} memcg_batch;
>  #endif
> +#ifdef CONFIG_UPROBES
> +	struct uprobe_task *utask;
> +#endif
>  };

Make that: 

	struct uprobe_task_state uprobe_state;

And provide an empty struct for !CONFIG_UPROBE, see below.


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

Would fail for pid namespaces I guess...

> +	/* Location of the probepoint */
> +	unsigned long vaddr;
> +
> +	/* Handler to run when the probepoint is hit */
> +	void (*handler)(struct uprobe*, struct pt_regs*);
> +};

Like previously said, I would much rather see an inode/offset based
interface and do the pid/fork/pgroup/cgroup etc.. stuff as filters on
top of that.

> +/*
> + * uprobe_process -- not a user-visible struct.

Seems like a weird comment for kernel code..

> + * 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).
> + *
> + * All processes/threads that share a mm share the same uprobe_process.
> + */
> +struct uprobe_process {

struct mm_uprobes {

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

Its customary to write it like:

	spinlock_t	 list_lock; /* protects uprobe_list, nr_uprobes */
	struct list_head uprobe_list;
	int		 nr_uprobes;

> +
> +	/* number of probept allocated for this process */
> +	int n_ppts;
> +
> +	/*
> +	 * Manages slots for instruction-copies to be single-stepped
> +	 * out of line.
> +	 */
> +	void *xol_area;

Why void * and not simply:

	struct uprobes_xol_area xol_area;

That struct is small enough and you only get one per mm and saves you an
allocation/pointer-chase.

> +};
> +
> +/*
> + * uprobe_probept -- not a user-visible struct.
> + * A uprobe_probept represents a probepoint.
> + * Guarded by uproc->lock.
> + */
> +struct uprobe_probept {

you really got a way with names, uprobe_point would be so much better.

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

So this thing is a link between the process and the probe, I'm not quite
sure what you need the refcount for, it seems to me you can only have on
of these per process/probe combination.

If you had used inode/offset based probes they would have been unique in
the system and you could have had an {inode,offset} indexed global tree
(or possibly a tree per inode, but that would mean adding to the inode
structure, which I think is best avoided).

That would also reduce the mm state to purely the xol area, no need to
manage this process to probe map.

> +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;
> +
> +	enum uprobe_task_state state;
> +
> +	struct uprobe_probept *active_ppt;
> +};

I would be thinking you can obtain the active probe point from the
address the task is stuck at and the state seems fairly redundant. Which
leaves you with the arch state, which afaict is exactly as large as the
pointer currently in the task struct.

> --git a/kernel/fork.c b/kernel/fork.c
> index b7e9d60..d78cf5d 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -187,6 +187,11 @@ void __put_task_struct(struct task_struct *tsk)
>  	delayacct_tsk_free(tsk);
>  	put_signal_struct(tsk->signal);
>  
> +#ifdef CONFIG_UPROBES
> +	if (unlikely(tsk->utask))
> +		uprobe_free_utask(tsk);
> +#endif
> +
>  	if (!profile_handoff_task(tsk))
>  		free_task(tsk);
>  }
> @@ -525,6 +530,10 @@ void __mmdrop(struct mm_struct *mm)
>  	mm_free_pgd(mm);
>  	destroy_context(mm);
>  	mmu_notifier_mm_destroy(mm);
> +#ifdef CONFIG_UPROBES
> +	if (unlikely(mm->uproc))
> +		uprobe_put_uprocess(mm);
> +#endif
>  	free_mm(mm);
>  }
>  EXPORT_SYMBOL_GPL(__mmdrop);
> @@ -683,6 +692,9 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
>  	if (mm->binfmt && !try_module_get(mm->binfmt->module))
>  		goto free_pt;
>  
> +#ifdef CONFIG_UPROBES
> +	mm->uproc = NULL;
> +#endif
>  	return mm;
>  
>  free_pt:
> @@ -1190,6 +1202,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
>  	 */
> @@ -1288,6 +1303,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
>  	proc_fork_connector(p);
>  	cgroup_post_fork(p);
>  	perf_event_fork(p);
> +#ifdef CONFIG_UPROBES
> +	if ((current->mm) && !(clone_flags & CLONE_VM)) {
> +		if (unlikely(current->mm->uproc))
> +			uprobe_handle_fork(p);
> +	}
> +#endif
>  	return p;
>  
>  bad_fork_free_pid:

All that can be replaced by unconditional functions, simply stub them
out for !CONFIG_UPROBE.

> diff --git a/kernel/uprobes.c b/kernel/uprobes.c
> index 230adf3..38c7abb 100644
> --- a/kernel/uprobes.c
> +++ b/kernel/uprobes.c
> @@ -21,6 +21,7 @@
>   *	Jim Keniston
>   */
>  #include <linux/kernel.h>
> +#include <linux/types.h>
>  #include <linux/init.h>
>  #include <linux/module.h>
>  #include <linux/sched.h>
> @@ -36,6 +37,9 @@
>  #include <linux/file.h>
>  #include <linux/pid.h>
>  #include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/kdebug.h>
> +
>  
>  struct user_bkpt_arch_info *arch = &user_bkpt_arch_info;
>  
> @@ -345,7 +349,7 @@ static int __insert_bkpt(struct task_struct *tsk,
>   *	@__insert_bkpt(). @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
> - *	@uprobes_post_sstep(), and populated with the contents of
> + *	@post_sstep(), and populated with the contents of
>   *	@user_bkpt->insn.
>   *	@tskinfo: points to a @user_bkpt_task_arch_info object for @tsk.
>   *	@regs: reflects the saved user state of @tsk.  pre_sstep()
> @@ -356,7 +360,7 @@ static int __insert_bkpt(struct task_struct *tsk,
>   *
>   * The client must ensure that the contents of @user_bkpt are not
>   * changed during the single-step operation -- i.e., between when
> - * @uprobes_pre_sstep() is called and when @uprobes_post_sstep() returns.
> + * @pre_sstep() is called and when @post_sstep() returns.
>   */
>  static int pre_sstep(struct task_struct *tsk, struct user_bkpt *user_bkpt,
>  		struct user_bkpt_task_arch_info *tskinfo, struct pt_regs *regs)
> @@ -610,7 +614,7 @@ static unsigned long xol_take_insn_slot(struct uprobes_xol_area *area)
>  
>  /*
>   * xol_get_insn_slot - If user_bkpt  was not allocated a slot, then
> - * allocate a slot. If uprobes_insert_bkpt is already called, (i.e
> + * allocate a slot. If insert_bkpt is already called, (i.e
>   * user_bkpt.vaddr != 0) then copy the instruction into the slot.
>   * @user_bkpt: probepoint information
>   * @xol_area refers the unique per process uprobes_xol_area for

Wandering hunks, these seem to want to get folded back to wherever the
original text comes from..

> @@ -741,6 +745,642 @@ validate_end:
>  }
>  /* end of slot allocation for XOL */
>  
> +
> +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);

uproc is per mm, why a global mutex?


> +/*
> + * Fork callback: The current task has spawned a 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.
> + * This function handles the case where vm is not shared between
> + * the parent and the child.
> + *
> + * TODO:
> + *	- Provide option for child to inherit uprobes.

Don't bother with that...

> + */
> +void uprobe_handle_fork(struct task_struct *child)
> +{
> +	struct uprobe_process *uproc;
> +	struct uprobe_probept *ppt;
> +	int ret;
> +
> +	uproc = current->mm->uproc;
> +
> +	/*
> +	 * New process spawned by parent but not sharing the same mm.
> +	 * 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.
> +	 */

The grand thing about not having any of this process state is that you
dont need to clean it up either..

> +	mutex_lock(&uproc->mutex);
> +	list_for_each_entry(ppt, &uproc->uprobe_list, ut_node) {
> +		ret = __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.
> + *
> + *  If its the first time the probepoint is hit, slot gets allocated here.
> + *  If its the first time the thread hit a breakpoint, utask gets
> + *  allocated here.
> + */
> +void uprobe_notify_resume(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;
> +	uproc = current->mm->uproc;
> +	if (unlikely(!utask)) {
> +		utask = add_utask(uproc);
> +
> +		/* Failed to allocate utask for the current task. */
> +		BUG_ON(!utask);
> +		probept = uprobes_get_bkpt_addr(regs);
> +		ppt = find_probept(uproc, probept);
> +
> +		/*
> +		 * The probept was refcounted in uprobe_bkpt_notifier;
> +		 * Hence it would be mysterious to miss ppt now
> +		 */
> +		WARN_ON(!ppt);
> +		utask->active_ppt = ppt;
> +		utask->state = UTASK_BP_HIT;
> +	} else
> +		ppt = utask->active_ppt;

You can replace this with:

 addr = instruction_pointer(task_pt_regs(current)) -
ip_advancement_by_brkpt_insn;

and then proceed from there like described below to obtain the struct
uprobe.

You can infer the SS/HIT state by checking if the user-addr is in the
XOL area or not.

> +	if (utask->state == UTASK_BP_HIT) {
> +		utask->state = UTASK_SSTEP;
> +		u = ppt->uprobe;
> +		if (u && u->handler)
> +			u->handler(u, regs);
> +
> +		if (!pre_ssout(ppt, regs))
> +			arch_uprobe_enable_sstep(regs);
> +	} else if (utask->state == UTASK_SSTEP) {
> +		if (sstep_complete(regs, ppt)) {
> +			put_probept(ppt);
> +			utask->active_ppt = NULL;
> +			utask->state = UTASK_RUNNING;
> +			arch_uprobe_disable_sstep(regs);
> +		}
> +	}
> +}
> +
> +/*
> + * uprobe_bkpt_notifier gets called from interrupt context
> + * it gets a reference to the ppt and sets TIF_UPROBE flag,
> + */
> +int uprobe_bkpt_notifier(struct pt_regs *regs)
> +{
> +	struct uprobe_process *uproc;
> +	struct uprobe_probept *ppt;
> +	struct uprobe_task *utask;
> +	unsigned long probept;
> +
> +	if (!current->mm || !current->mm->uproc)
> +		/* task is currently not uprobed */
> +		return 0;
> +
> +	uproc = current->mm->uproc;
> +	utask = current->utask;
> +	probept = uprobes_get_bkpt_addr(regs);

Its an address, not a struct uprobe_probept * so call it addr if
anything.

> +	ppt = find_probept(uproc, probept);
> +	if (!ppt)
> +		return 0;
> +	get_probept(ppt);
> +	if (utask) {
> +		utask->active_ppt = ppt;
> +		utask->state = UTASK_BP_HIT;
> +	}

Right, so all this should die..

Obtain the address, lookup the vma, get the file, compute the offset
inside that file -> {inode,offset}

Do the lookup in the global tree to obtain the uprobe, and possibly
execute the probe handler right here in interrupt context, if it fails
with -EFAULT, continue with the below:

> +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
> +	set_thread_flag(TIF_UPROBE);
> +#endif

How can we ever get here if the architecture doesn't support uprobes?

> +	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_probept *ppt;
> +	struct uprobe_task *utask;
> +
> +	if (!current->mm || !current->mm->uproc || !current->utask)
> +		/* task is currently not uprobed */
> +		return 0;
> +
> +	utask = current->utask;
> +
> +	ppt = utask->active_ppt;
> +	if (!ppt)
> +		return 0;
> +
> +	if (uprobes_resume_can_sleep(&ppt->user_bkpt)) {
> +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
> +		set_thread_flag(TIF_UPROBE);
> +#endif
> +		return 1;
> +	}
> +	if (sstep_complete(regs, ppt)) {
> +		put_probept(ppt);
> +		arch_uprobe_disable_sstep(regs);
> +		utask->active_ppt = NULL;
> +		utask->state = UTASK_RUNNING;
> +		return 1;
> +	}
> +	return 0;
> +}
> +
>  static int __init init_uprobes(void)
>  {
>  	int result = 0;
> @@ -752,7 +1392,14 @@ static int __init init_uprobes(void)
>  	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);
> +
> +	register_die_notifier(&uprobes_exception_nb);
>  	return result;
>  }
>  
> +static void __exit exit_uprobes(void)
> +{
> +}
> +
>  module_init(init_uprobes);
> +module_exit(exit_uprobes);



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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 5/15] 5: uprobes: Uprobes (un)registration and exception handling Srikar Dronamraju
  2010-09-01 21:43   ` Peter Zijlstra
@ 2010-09-01 21:46   ` Peter Zijlstra
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-01 21:46 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Wed, 2010-08-25 at 19:12 +0530, Srikar Dronamraju wrote:
> 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.
> 
Right, so in short, I think that if you rework this to be inode:offset
based you'll end up with a much simpler codebase.



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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-01 21:43   ` Peter Zijlstra
@ 2010-09-02  8:12     ` Peter Zijlstra
  2010-09-03 16:42     ` Srikar Dronamraju
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-02  8:12 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Wed, 2010-09-01 at 23:43 +0200, Peter Zijlstra wrote:
> > +void uprobe_notify_resume(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;
> > +     uproc = current->mm->uproc;
> > +     if (unlikely(!utask)) {
> > +             utask = add_utask(uproc);
> > +
> > +             /* Failed to allocate utask for the current task. */
> > +             BUG_ON(!utask);
> > +             probept = uprobes_get_bkpt_addr(regs);
> > +             ppt = find_probept(uproc, probept);
> > +
> > +             /*
> > +              * The probept was refcounted in uprobe_bkpt_notifier;
> > +              * Hence it would be mysterious to miss ppt now
> > +              */
> > +             WARN_ON(!ppt);
> > +             utask->active_ppt = ppt;
> > +             utask->state = UTASK_BP_HIT;
> > +     } else
> > +             ppt = utask->active_ppt;
> 
> You can replace this with:
> 
>  addr = instruction_pointer(task_pt_regs(current)) -
> ip_advancement_by_brkpt_insn;
> 
> and then proceed from there like described below to obtain the struct
> uprobe.
> 
> You can infer the SS/HIT state by checking if the user-addr is in the
> XOL area or not.

Right, so one problem I overlooked is that you need to have the actual
probe to compute the jump address, but that could be fixed by
implementing boosted probes.

> > +     if (utask->state == UTASK_BP_HIT) {
> > +             utask->state = UTASK_SSTEP;
> > +             u = ppt->uprobe;
> > +             if (u && u->handler)
> > +                     u->handler(u, regs);
> > +
> > +             if (!pre_ssout(ppt, regs))
> > +                     arch_uprobe_enable_sstep(regs);
> > +     } else if (utask->state == UTASK_SSTEP) {
> > +             if (sstep_complete(regs, ppt)) {
> > +                     put_probept(ppt);
> > +                     utask->active_ppt = NULL;
> > +                     utask->state = UTASK_RUNNING;
> > +                     arch_uprobe_disable_sstep(regs);
> > +             }
> > +     }
> > +}
> > +
> > +/*
> > + * uprobe_bkpt_notifier gets called from interrupt context
> > + * it gets a reference to the ppt and sets TIF_UPROBE flag,
> > + */
> > +int uprobe_bkpt_notifier(struct pt_regs *regs)
> > +{
> > +     struct uprobe_process *uproc;
> > +     struct uprobe_probept *ppt;
> > +     struct uprobe_task *utask;
> > +     unsigned long probept;
> > +
> > +     if (!current->mm || !current->mm->uproc)
> > +             /* task is currently not uprobed */
> > +             return 0;
> > +
> > +     uproc = current->mm->uproc;
> > +     utask = current->utask;
> > +     probept = uprobes_get_bkpt_addr(regs);
> 
> Its an address, not a struct uprobe_probept * so call it addr if
> anything.
> 
> > +     ppt = find_probept(uproc, probept);
> > +     if (!ppt)
> > +             return 0;
> > +     get_probept(ppt);
> > +     if (utask) {
> > +             utask->active_ppt = ppt;
> > +             utask->state = UTASK_BP_HIT;
> > +     }
> 
> Right, so all this should die..
> 
> Obtain the address, lookup the vma, get the file, compute the offset
> inside that file -> {inode,offset}

Except we need to stabilize the vma tree to do the lookup and that
currently requires mmap_sem, I guess until we get Nick's per-pte
vma-tree we could fudge that by adding a spinlock around the rb_tree
modifications -- I've got a patch for that somewhere I think.

> Do the lookup in the global tree to obtain the uprobe, and possibly
> execute the probe handler right here in interrupt context, if it fails
> with -EFAULT, continue with the below:
> 
> > +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
> > +     set_thread_flag(TIF_UPROBE);
> > +#endif
> 
> How can we ever get here if the architecture doesn't support uprobes?
> 
> > +     return 1;
> > +} 

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 3/15] 3: uprobes: Slot allocation for Execution out of line(XOL) Srikar Dronamraju
  2010-09-01 20:13   ` Peter Zijlstra
@ 2010-09-02  8:23   ` Peter Zijlstra
  2010-09-02 17:47     ` Srikar Dronamraju
  1 sibling, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-02  8:23 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Wed, 2010-08-25 at 19:11 +0530, Srikar Dronamraju wrote:
> 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. 

An alternative method would be to have 1 slot per cpu, and manage the
slot content using preemption notifiers. That gives you a fixed number
of slots and an unlimited number of probe points.

If the preemption happens to be a migration you need to rewrite the
userspace IP to point to the new slot -- if indeed the task was inside
one when it got preempted -- but that all should be doable.



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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-02  8:23   ` Peter Zijlstra
@ 2010-09-02 17:47     ` Srikar Dronamraju
  2010-09-03  7:26       ` Peter Zijlstra
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-02 17:47 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

> > 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. 
> 
> An alternative method would be to have 1 slot per cpu, and manage the
> slot content using preemption notifiers. That gives you a fixed number
> of slots and an unlimited number of probe points.
> 
> If the preemption happens to be a migration you need to rewrite the
> userspace IP to point to the new slot -- if indeed the task was inside
> one when it got preempted -- but that all should be doable.
> 

Certainly doable but it has its share of drawbacks.
1. On every probe hit we have to copy the instruction into the
slot, so there is a performance penalty. 

2  This might complicate booster probe, because the jump
instruction that follows the original instruction now actually have to
coded every time.

3. Yes migration is an issue esp
-  if a thread of the same process that hit a breakpoint is scheduled into the same cpu and that newly scheduled thread hits a breakpoint. 
- Something similar can happen if a multithreaded process runs on a
  uniprocessor machine.

4. I dont see a need for clearing slots after post processing, but if
we need to clear we then are adding more penalties because not only are
we clearing the slots but the post processing then cant happen in
interrupt context.

5. I think we are covered on the cpu hotplug too, (i.e not sure if we have
to make uprobes cpu hot plug aware.).

6. We would still be allocating a page for the slots. Unless we want
to expand to more slots than available in one page, I dont see the
disadvantages with the current approach.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-02 17:47     ` Srikar Dronamraju
@ 2010-09-03  7:26       ` Peter Zijlstra
  2010-09-06 17:59         ` Srikar Dronamraju
  0 siblings, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03  7:26 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Thu, 2010-09-02 at 23:17 +0530, Srikar Dronamraju wrote:
> > > 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. 
> > 
> > An alternative method would be to have 1 slot per cpu, and manage the
> > slot content using preemption notifiers. That gives you a fixed number
> > of slots and an unlimited number of probe points.
> > 
> > If the preemption happens to be a migration you need to rewrite the
> > userspace IP to point to the new slot -- if indeed the task was inside
> > one when it got preempted -- but that all should be doable.
> > 
> 
> Certainly doable but it has its share of drawbacks.
> 1. On every probe hit we have to copy the instruction into the
> slot, so there is a performance penalty. 

Yeah, although I imagine its nearly free since you need to pay the
cache-miss anyway.

> 2  This might complicate booster probe, because the jump
> instruction that follows the original instruction now actually have to
> coded every time.

Why can't you keep the whole replacement sequence in-tact? Simply copy
it out into the slot each time.

> 3. Yes migration is an issue esp
> -  if a thread of the same process that hit a breakpoint is scheduled into the same cpu and that newly scheduled thread hits a breakpoint. 
> - Something similar can happen if a multithreaded process runs on a
>   uniprocessor machine.

-ENOPARSE ?!

> 4. I dont see a need for clearing slots after post processing, but if
> we need to clear we then are adding more penalties because not only are
> we clearing the slots but the post processing then cant happen in
> interrupt context.

post-processing? you mean the probe handler? Why couldn't that be done
from interrupt context?

> 5. I think we are covered on the cpu hotplug too, (i.e not sure if we have
> to make uprobes cpu hot plug aware.).

Not if you use a slot per cpu and use preemption notifiers, the
preemption notifiers will migrate the slots around.

> 6. We would still be allocating a page for the slots. Unless we want
> to expand to more slots than available in one page, I dont see the
> disadvantages with the current approach.

The current approach limits the number of probes to what fits in a page.
The slot per cpu approach will have no such limit.

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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 4/15] 4: uprobes: x86 specific functions for user space breakpointing Srikar Dronamraju
@ 2010-09-03 10:26   ` Andi Kleen
  2010-09-03 17:48     ` Srikar Dronamraju
  0 siblings, 1 reply; 71+ messages in thread
From: Andi Kleen @ 2010-09-03 10:26 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney

Srikar Dronamraju <srikar@linux.vnet.ibm.com> writes:

Quick high level review. I did not attempt to validate the basic
algorithm, more the kernel interface.

> Provides x86 specific functions for instruction analysis and
> instruction validation and x86 specific pre-processing and
> post-processing of singlestep especially for RIP relative
> instructions. Uses "x86: instruction decoder API" for validation and
> analysis of user space instructions. This analysis is used at the time
> of post-processing of breakpoint hit to do the necessary fix-ups.
> There is support for breakpointing RIP relative instructions. However
> there are still few instructions that cannot be singlestepped.

One general comment here: since with uprobes the instruction
decoder becomes security critical did you do any fuzz tests
on it (e.g. like using it on crashme or on code that has 
been corrupted with a few bitflips) ?

> +typedef u8 user_bkpt_opcode_t;

Maybe it's me, but I would prefer breakpoint instead of bkpt
> +#ifdef CONFIG_X86_32
> +#define is_32bit_app(tsk) 1
> +#else
> +#define is_32bit_app(tsk) (test_tsk_thread_flag(tsk, TIF_IA32))
> +#endif

This probably should be elsewhere.

> +
> +#define UPROBES_FIX_RIP_AX	0x8000
> +#define UPROBES_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)
> +
> +/*
> + * @reg: reflects the saved state of the task
> + * @vaddr: the virtual address to jump to.
> + * Return 0 on success or a -ve number on error.
> + */
> +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 &
> +			(UPROBES_FIX_RIP_AX | UPROBES_FIX_RIP_CX)) != 0);
> +}
> +

Shouldn't all this stuff be in the instruction decoder? 

It seems weird to have the knowledge spread over multiple files.

> +
> +static void report_bad_prefix(void)
> +{
> +	printk(KERN_ERR "uprobes 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, "
> +		"uprobes 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 "uprobes does not currently support probing "
> +		"instructions with the 2-byte opcode 0x0f 0x%2.2x\n", op);
> +}

These functions that just do a single printk seem weird. I would
do that in the caller. Also the message could be shortened I guess
and should just dump the bytes.

> +
> +/**
> + * analyze_insn - instruction analysis including validity and fixups.
> + * @tsk: the probed task.
> + * @user_bkpt: the probepoint information.
> + * Return 0 on success or a -ve number on error.
> + */
> +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))

This check is not fully correct because it's valid to have
32bit code in 64bit programs and vice versa.  The only good
way to check that is to look at the code segment at runtime
though (and it gets complicated if you want to handle LDTs,
but that could be optional). May be difficult to do though.

Also the compat bit is not necessarily set if no system call is
executing. You would rather need to check the exec_domain.
> + */
> +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 = uprobes_read_vm(tsk, (void __user *) sp, &ra, rasize);
> +	if (unlikely(ncopied != rasize))
> +		goto fail;

goto is automatically unlikely and unlikely is deprecated anyways.

-Andi
-- 
ak@linux.intel.com -- Speaking for myself only.

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-01 20:13   ` Peter Zijlstra
@ 2010-09-03 16:40     ` Srikar Dronamraju
  2010-09-03 16:51       ` Peter Zijlstra
  2010-09-03 17:25       ` Peter Zijlstra
  0 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-03 16:40 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

> > +struct uprobes_xol_area {
> > +	spinlock_t lock;	/* protects bitmap and slot (de)allocation*/
> > +	unsigned long *bitmap;	/* 0 = free slot */
> 
> Since you have a static sized bitmap, why not simply declare it here?
> 
> 	DECLARE_BITMAP(bitmap, MAX_UPROBES_XOL_SLOTS;

Okay,  will do.

> 
> > +	/*
> > +	 * 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.
> > +	 */
> 
> Naughty kernel modules we don't care about, but yeah, it appears vma's
> installed using install_special_mapping() can be unmapped by the process
> itself,.. curious. 

Will clean this up.
> 
> Anyway, you could install your own vm_ops and provide a close method to
> track this.
> 
> > +	unsigned long vaddr;		/* Page(s) of instruction slots */
> > +};
> > +

<snipped>

> > +	vma = find_vma(mm, addr);
> > +
> > +	/* Don't expand vma on mremap(). */
> > +	vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
> > +	area->vaddr = vma->vm_start;
> 
> Seems interesting,.. why not use install_special_mapping(), that's what
> the VDSO uses.

Okay, I hadnt looked at install_special_mapping earlier so I will take a
look and incorporate it. However I am not clear at this point what
install_special_mapping is giving us here.  Also install_special_mapping
is already defining its own vm_ops esp a close method thats doesnt seem
to be doing anything. So at this point I am not clear how we are link
the vm_ops, the close method and install_special_mapping.

> 
> > +/*
> > + * xol_alloc_area - Allocate process's uprobes_xol_area.
> > + * This area will be used for storing instructions for execution out of
> > + * line.
> 
> It doesn't actually do that, xol_add_vma() does that, this allocates the
> storage management bits.

Okay, will clean up. 

> 
> > + * Called with mm->uproc->mutex locked.
> 
> There's a nice way to not have to write that:
> 
>   lockdep_assert_held(&mm->uproc->mutex);

Okay, will do.

> 
> I would call that allocate, find would imply a constant operation, but
> you actually change the state.
> 

Okay, 

> > + * Called when holding xol_area->lock
> 
>   lockdep_assert_held(&area->lock);
> 
> > + */
> > +static unsigned long xol_take_insn_slot(struct uprobes_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);
> 
> Since its all serialized by xol_area->lock, why use an atomic bitop?

Okay will use the non-atomic set_bit.

> 
> > +		slot_addr = area->vaddr +
> > +				(slot_nr * UPROBES_XOL_SLOT_BYTES);
> > +		return slot_addr;
> > +	}
> > +
> > +	return 0;
> > +}
> > +

<snipped>

> > +	/*
> > +	 * Initialize the slot if user_bkpt->vaddr points to valid
> > +	 * instruction slot.
> > +	 */
> > +	if (likely(xol_vaddr) && user_bkpt->vaddr) {
> 
> if (!xol_vaddr)
>   goto bail;
> 
> gives nices code, and saves an indent level.
> 
> Also, why would we ever get here with !user_bkpt->vaddr.

For now, user_bkpt->vaddr will always be set when we are here. 
However when we add uretprobe support, we would then get here with
user_bkpt->vaddr being NULL. 

I would drop the check for now, but add it later when we add the return
probe support.

> 
> (fwiw, my fingers hate bkpt, they either want to type bp, or brkpt)
> 

I had renamed the structure from ubp to user_bkpt based on your
comments. I had actually mentioned this in the summary mail that I had
sent on Jan 22 this year. I am fine to rename it to user_bp if that
helps but if you feel ubp is better, I can revert to ubp too.


> > +		len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
> > +						UPROBES_XOL_SLOT_BYTES, 1);
> > +		if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
> > +			printk(KERN_ERR "Failed to copy instruction at %#lx "
> > +					"len = %d\n", user_bkpt->vaddr, len);
> > +	}
> > +
> > +	/*
> > +	 * Update user_bkpt->xol_vaddr after giving a chance for the slot to
> > +	 * be initialized.
> > +	 */
> > +	mb();
> 
> Where is the matching barrier?

I dont want the compiler to reorder the instructions and do the
assignment for user_bkpt to be done before we complete the copy above.

If the assignment happens before we copy the content into the slot,
someother thread that might hit the same probe actually things the slot
is ready and tries to jump to that slot even before the slot is
initialized.

Please let me know if I could have done it differently.

> 
> > +	user_bkpt->xol_vaddr = xol_vaddr;
> > +	return user_bkpt->xol_vaddr;
> > +}
> > +
<snipped>

> 
> > +		spin_unlock_irqrestore(&xol_area->lock, flags);
> > +		found = 1;
> > +	}
> > +
> > +	if (!found)
> > +		printk(KERN_ERR "%s: no XOL vma for slot address %#lx\n",
> > +						__func__, slot_addr);
> 
> funny code flow,.. s/found = 1/return/ and loose the conditional and
> indent?

Okay, will do.

> 
> > +static int xol_validate_vaddr(struct pid *pid, unsigned long vaddr,
> > +				struct uprobes_xol_area *xol_area)
> > +{
> > +	struct task_struct *tsk;
> > +	unsigned long vma_end;
> > +	int result;
> > +
> > +	if (unlikely(!xol_area))
> > +		return 0;
> > +
> > +	tsk = get_pid_task(pid, PIDTYPE_PID);
> > +	if (unlikely(!tsk))
> > +		return -EINVAL;
> > +
> > +	result = validate_address(tsk, vaddr);
> > +	if (result != 0)
> > +		goto validate_end;
> > +
> > +	vma_end = xol_area->vaddr + PAGE_SIZE;
> > +	if (xol_area->vaddr <= vaddr && vaddr < vma_end)
> > +		result = 1;
> > +
> > +validate_end:
> > +	put_task_struct(tsk);
> > +	return result;
> > +}
> 
> This doesn't actually appear used in this patch,.. does it want to live
> elsewhere?

Yes, xol_validate_vaddr gets used in the next patch.  So probably it can
be moved to the next patch.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-01 21:43   ` Peter Zijlstra
  2010-09-02  8:12     ` Peter Zijlstra
@ 2010-09-03 16:42     ` Srikar Dronamraju
  2010-09-03 17:19       ` Peter Zijlstra
  2010-09-03 17:27       ` Peter Zijlstra
  1 sibling, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-03 16:42 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

* Peter Zijlstra <peterz@infradead.org> [2010-09-01 23:43:34]:

> On Wed, 2010-08-25 at 19:12 +0530, Srikar Dronamraju wrote:
> 
> > TODO:
> > 1. Allow multiple probes at a probepoint.
> > 2. Booster probes.
> > 3. Allow probes to be inherited across fork.
> 
> I wouldn't worry about that, focus on inode attached probes and you get
> fork for free.

I am working on the file based probing. It compiles but havent got it to
test it yet, I can post the patch if you are interested. It should
achieve similar to inode probing.

However I would have an issue with making inode based probing the
default.
1. Making all probing based on inode can be a performance hog.

2. Since unlike kernel space, every process has a different space, so
why would we have to insert breakpoints in each of its process space if
we are not interested in them.

3. Ingo has a requirement for allowing normal users to use uprobes thro
perf. When this feature gets implemented, we have to be careful about a
normal users trying to just trace their application resulting in it
hitting performance all other users. 

	For example: one user places a probe on /usr/lib/libc.so: malloc
	- Another normal users looks at the current userspace probes and
	  constructs a program that just does malloc/free just to
	  degrade the performance of the system.

	- user could be interested in just one process which could be
	  calling malloc just 10 times. However during the same time
	  there are 1000 processes which could all together call 100000
	  times during the same time.

So even when we allow file based tracing across the system, it should be
restricted to just the root user.

As we discussed in previous discussions, Inode based tracing wasnt
accepted back in 2006. May be the approach was a problem then but what
was suggested then was pid based tracing.

> > diff --git a/arch/Kconfig b/arch/Kconfig
> > index 87bd26b..c8c8e3f 100644
> > --- a/arch/Kconfig
> > +++ b/arch/Kconfig
> > @@ -49,6 +49,7 @@ config OPTPROBES
> >  
> >  config UPROBES
> >  	bool "User-space probes (EXPERIMENTAL)"
> > +	default n
> >  	depends on ARCH_SUPPORTS_UPROBES
> >  	depends on MMU
> >  	help
> 
> Seems like a weird place for this hunk, does this want to live
> elsewhere?
> 

Okay, will cleanup.

> > +#ifdef CONFIG_UPROBES
> > +	if (unlikely(current->utask))
> > +		uprobe_free_utask(current);
> > +#endif
> 
> A nicer way is to provide flush_uprobes() unconditionally.

Okay  will do

> 
> > +#ifdef CONFIG_UPROBES
> > +	struct uprobe_process *uproc;	/* per mm uprobes info */
> > +#endif
> >  };
> 
> I find the _process postfix a bit weird in this context, how about
> something like:
> 
>   struct mm_uprobes *mm_uprobes;
> 
> instead?

Okay,  will do

> 
> > @@ -1496,6 +1496,9 @@ struct task_struct {
> >  		unsigned long memsw_bytes; /* uncharged mem+swap usage */
> >  	} memcg_batch;
> >  #endif
> > +#ifdef CONFIG_UPROBES
> > +	struct uprobe_task *utask;
> > +#endif
> >  };
> 
> Make that: 
> 
> 	struct uprobe_task_state uprobe_state;
> 
> And provide an empty struct for !CONFIG_UPROBE, see below.
> 

Okay,  will do

> 
> > +/* 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;
> 
> Would fail for pid namespaces I guess...

Can you explain a bit more?

> 
> > +	/* Location of the probepoint */
> > +	unsigned long vaddr;
> > +
> > +	/* Handler to run when the probepoint is hit */
> > +	void (*handler)(struct uprobe*, struct pt_regs*);
> > +};
> 

> > +/*
> > + * uprobe_process -- not a user-visible struct.
> 
> Seems like a weird comment for kernel code..

Okay, will cleanup.

> 
> > +	/* lock held while traversing/modifying uprobe_list and n_ppts */
> > +	spinlock_t pptlist_lock;	/* protects uprobe_list */
> 
> Its customary to write it like:
> 
> 	spinlock_t	 list_lock; /* protects uprobe_list, nr_uprobes */
> 	struct list_head uprobe_list;
> 	int		 nr_uprobes;
> 

Okay,  will do


> > +
> > +	/* number of probept allocated for this process */
> > +	int n_ppts;
> > +
> > +	/*
> > +	 * Manages slots for instruction-copies to be single-stepped
> > +	 * out of line.
> > +	 */
> > +	void *xol_area;
> 
> Why void * and not simply:
> 
> 	struct uprobes_xol_area xol_area;

Okay,  will do


> That struct is small enough and you only get one per mm and saves you an
> allocation/pointer-chase.
> 
> > +};
> > +
> > +/*
> > + * uprobe_probept -- not a user-visible struct.
> > + * A uprobe_probept represents a probepoint.
> > + * Guarded by uproc->lock.
> > + */
> > +struct uprobe_probept {
> 
> you really got a way with names, uprobe_point would be so much better.

Okay, 

> 
> > +	/* 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;
> > +};
> 
> So this thing is a link between the process and the probe, I'm not quite
> sure what you need the refcount for, it seems to me you can only have on
> of these per process/probe combination.
> 
> If you had used inode/offset based probes they would have been unique in
> the system and you could have had an {inode,offset} indexed global tree
> (or possibly a tree per inode, but that would mean adding to the inode
> structure, which I think is best avoided).

Unlike kernel probing, uprobes has a disadvantage.
Lets assume that the request for removing a probepoint when some of the
threads have actually hit the probe. Because the handlers in uprobes can
sleep, we cant remove the probepoint at the same time as the request for
the removing the probe. This is where refcount steps in and helps us to
decide when we can remove the probepoint. Even inoode based
tracing or file based tracing would need it.

> 
> That would also reduce the mm state to purely the xol area, no need to
> manage this process to probe map.
> 
> > +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;
> > +
> > +	enum uprobe_task_state state;
> > +
> > +	struct uprobe_probept *active_ppt;
> > +};
> 
> I would be thinking you can obtain the active probe point from the
> address the task is stuck at and the state seems fairly redundant. Which
> leaves you with the arch state, which afaict is exactly as large as the
> pointer currently in the task struct.

Lets assume the thread is about to singlestep (or has singlestepped)
So the instruction pointer is pointing to one of the slot (or it could
be pointing to some other arbitrary address if it has singlestepped lets
say a jump instruction). 

We could get the active probe point from the address the task is stuck
if the instruction pointers happens to be pointing to slots.  But for
that we would have to maintain a relation between slots and the current
probepoint it is servicing.  I think its easier to cache the
active_probept then try to maintain this relationship. Moreover we are
never certain in some cases where the underlying instruction happened to
be a jump.

state is needed so that we know at do_notify_resume time whether the
thread should singlestep or has already singlestep.
Do you see a way to determine in do_notify_resume if the thread is
singlestepping or not. I dont think the instruction pointer can
determine if we are about to singlestep or have already singlestepped.

<snipped>

> >  	perf_event_fork(p);
> > +#ifdef CONFIG_UPROBES
> > +	if ((current->mm) && !(clone_flags & CLONE_VM)) {
> > +		if (unlikely(current->mm->uproc))
> > +			uprobe_handle_fork(p);
> > +	}
> > +#endif
> >  	return p;
> >  
> >  bad_fork_free_pid:
> 
> All that can be replaced by unconditional functions, simply stub them
> out for !CONFIG_UPROBE.


Okay. 


<snipped>
> >  
> >  /*
> >   * xol_get_insn_slot - If user_bkpt  was not allocated a slot, then
> > - * allocate a slot. If uprobes_insert_bkpt is already called, (i.e
> > + * allocate a slot. If insert_bkpt is already called, (i.e
> >   * user_bkpt.vaddr != 0) then copy the instruction into the slot.
> >   * @user_bkpt: probepoint information
> >   * @xol_area refers the unique per process uprobes_xol_area for
> 
> Wandering hunks, these seem to want to get folded back to wherever the
> original text comes from..
> 

Okay, will check and cleanup.

> > @@ -741,6 +745,642 @@ validate_end:
> >  }
> >  /* end of slot allocation for XOL */
> >  
> > +
> > +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);
> 
> uproc is per mm, why a global mutex?

yes, uproc is per mm but gets created when the first probe on that
process gets registered.
Do you have anyother lock that you have in mind?
I could have used the mmap_sem, but I am not sure if we should be taking
a write lock on the mmap_sem.  

> 
> 
> > +/*
> > + * Fork callback: The current task has spawned a 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.
> > + * This function handles the case where vm is not shared between
> > + * the parent and the child.
> > + *
> > + * TODO:
> > + *	- Provide option for child to inherit uprobes.
> 
> Don't bother with that...

I think it would be needed down the line. Lets keep it in TODO for
now.

> 
> > + */
> > +void uprobe_handle_fork(struct task_struct *child)
> > +{
> > +	struct uprobe_process *uproc;
> > +	struct uprobe_probept *ppt;
> > +	int ret;
> > +
> > +	uproc = current->mm->uproc;
> > +
> > +	/*
> > +	 * New process spawned by parent but not sharing the same mm.
> > +	 * 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.
> > +	 */
> 
> The grand thing about not having any of this process state is that you
> dont need to clean it up either..

If we had done inode based tracing and a process that has breakpoints forks, 
wont we have to recreate the breakpoint metadata for the child process?
I dont see the child calling mmap. When and how would be the probepoint
data, user_bkpt data for the breakpoints, get replicated/linked to the
child?

> > +	if (unlikely(!utask)) {
> > +		utask = add_utask(uproc);
> > +
> > +		/* Failed to allocate utask for the current task. */
> > +		BUG_ON(!utask);
> > +		probept = uprobes_get_bkpt_addr(regs);
> > +		ppt = find_probept(uproc, probept);
> > +
> > +		/*
> > +		 * The probept was refcounted in uprobe_bkpt_notifier;
> > +		 * Hence it would be mysterious to miss ppt now
> > +		 */
> > +		WARN_ON(!ppt);
> > +		utask->active_ppt = ppt;
> > +		utask->state = UTASK_BP_HIT;
> > +	} else
> > +		ppt = utask->active_ppt;
> 
> You can replace this with:
> 
>  addr = instruction_pointer(task_pt_regs(current)) -
> ip_advancement_by_brkpt_insn;

uprobes_get_bkpt_addr does this exactly. However after we have
singlestepped, we cant rely on this. Also how do we know if we have
already singlestepped or not? After singlestepping we may still be
pointing to some slot or the instruction pointer could be pointing
something completely different.

> 
> and then proceed from there like described below to obtain the struct
> uprobe.
> 
> You can infer the SS/HIT state by checking if the user-addr is in the
> XOL area or not.
> 
> > +	if (utask->state == UTASK_BP_HIT) {
> > +		utask->state = UTASK_SSTEP;
> > +		u = ppt->uprobe;
> > +		if (u && u->handler)
> > +			u->handler(u, regs);
> > +
> > +		if (!pre_ssout(ppt, regs))
> > +			arch_uprobe_enable_sstep(regs);
> > +	} else if (utask->state == UTASK_SSTEP) {
> > +		if (sstep_complete(regs, ppt)) {
> > +			put_probept(ppt);
> > +			utask->active_ppt = NULL;
> > +			utask->state = UTASK_RUNNING;
> > +			arch_uprobe_disable_sstep(regs);
> > +		}
> > +	}
> > +}
> > +
> > +/*
> > + * uprobe_bkpt_notifier gets called from interrupt context
> > + * it gets a reference to the ppt and sets TIF_UPROBE flag,
> > + */
> > +int uprobe_bkpt_notifier(struct pt_regs *regs)
> > +{
> > +	struct uprobe_process *uproc;
> > +	struct uprobe_probept *ppt;
> > +	struct uprobe_task *utask;
> > +	unsigned long probept;
> > +
> > +	if (!current->mm || !current->mm->uproc)
> > +		/* task is currently not uprobed */
> > +		return 0;
> > +
> > +	uproc = current->mm->uproc;
> > +	utask = current->utask;
> > +	probept = uprobes_get_bkpt_addr(regs);
> 
> Its an address, not a struct uprobe_probept * so call it addr if
> anything.

Okay, 

> 
> > +	ppt = find_probept(uproc, probept);
> > +	if (!ppt)
> > +		return 0;
> > +	get_probept(ppt);
> > +	if (utask) {
> > +		utask->active_ppt = ppt;
> > +		utask->state = UTASK_BP_HIT;
> > +	}
> 
> Right, so all this should die..
> 
> Obtain the address, lookup the vma, get the file, compute the offset
> inside that file -> {inode,offset}
> 
> Do the lookup in the global tree to obtain the uprobe, and possibly
> execute the probe handler right here in interrupt context, if it fails
> with -EFAULT, continue with the below:

We dropped running the handler in interrupt context based on comments to
my previous postings. It not only makes it complex at the time of
handling the probe but we didnt see a difference when we compare the
time it took to run the handler in task context to running the handler
in interrupt context. I had even posted the timing info when
register_uprobes was exported. 

> 
> > +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
> > +	set_thread_flag(TIF_UPROBE);
> > +#endif
> 
> How can we ever get here if the architecture doesn't support uprobes?

Right, this is not needed, the #ifdefs were added just allow to compile 
kernel/uprobes.o even when  CONFIG_UPROBES wasnt enabled.
There was a comment to one of my earlier posting on LKML about compiling
kernel/uprobe.o when CONFIG_UPROBES wasnt enabled failed.

I am more than happy to remove the #ifdef.

> 
> > +	return 1;
> > +}
> > +

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03 16:40     ` Srikar Dronamraju
@ 2010-09-03 16:51       ` Peter Zijlstra
  2010-09-03 17:26         ` Srikar Dronamraju
  2010-09-03 17:25       ` Peter Zijlstra
  1 sibling, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 16:51 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Fri, 2010-09-03 at 22:10 +0530, Srikar Dronamraju wrote:
> > > +   mb();
> > 
> > Where is the matching barrier?
> 
> I dont want the compiler to reorder the instructions and do the
> assignment for user_bkpt to be done before we complete the copy above.
> 
> If the assignment happens before we copy the content into the slot,
> someother thread that might hit the same probe actually things the slot
> is ready and tries to jump to that slot even before the slot is
> initialized.
> 
> Please let me know if I could have done it differently. 


If you want a compiler barrier, use barrier(), but here you seem to
describe a multi-threaded situation, in which case the observer thread
needs at least a rmb() in order for that mb() to mean anything other
than the compiler barrier it implies.

Also, use smp_* barriers.



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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-03 16:42     ` Srikar Dronamraju
@ 2010-09-03 17:19       ` Peter Zijlstra
  2010-09-06 17:46         ` Srikar Dronamraju
  2010-09-03 17:27       ` Peter Zijlstra
  1 sibling, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 17:19 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Fri, 2010-09-03 at 22:12 +0530, Srikar Dronamraju wrote:
> * Peter Zijlstra <peterz@infradead.org> [2010-09-01 23:43:34]:
> 
> > On Wed, 2010-08-25 at 19:12 +0530, Srikar Dronamraju wrote:
> > 
> > > TODO:
> > > 1. Allow multiple probes at a probepoint.
> > > 2. Booster probes.
> > > 3. Allow probes to be inherited across fork.
> > 
> > I wouldn't worry about that, focus on inode attached probes and you get
> > fork for free.
> 
> I am working on the file based probing. It compiles but havent got it to
> test it yet, I can post the patch if you are interested. It should
> achieve similar to inode probing.
> 
> However I would have an issue with making inode based probing the
> default.
> 1. Making all probing based on inode can be a performance hog.

How so? You can add filters if you want.

> 2. Since unlike kernel space, every process has a different space, so
> why would we have to insert breakpoints in each of its process space if
> we are not interested in them.

You don't have to, but you can. The problem I have with this stuff is
that it makes the pid thing a primary interface, whereas it should be
one of many filter possibilities.

> 3. Ingo has a requirement for allowing normal users to use uprobes thro
> perf. When this feature gets implemented, we have to be careful about a
> normal users trying to just trace their application resulting in it
> hitting performance all other users. 
> 
> 	For example: one user places a probe on /usr/lib/libc.so: malloc
> 	- Another normal users looks at the current userspace probes and
> 	  constructs a program that just does malloc/free just to
> 	  degrade the performance of the system.
> 
> 	- user could be interested in just one process which could be
> 	  calling malloc just 10 times. However during the same time
> 	  there are 1000 processes which could all together call 100000
> 	  times during the same time.

See the filter thing.

> > > +/* 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;
> > 
> > Would fail for pid namespaces I guess...
> 
> Can you explain a bit more?

What pid namespace will you interpret that number in?

> > > +	/* Location of the probepoint */
> > > +	unsigned long vaddr;
> > > +
> > > +	/* Handler to run when the probepoint is hit */
> > > +	void (*handler)(struct uprobe*, struct pt_regs*);
> > > +};


> > > +	/* 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;
> > > +};
> > 
> > So this thing is a link between the process and the probe, I'm not quite
> > sure what you need the refcount for, it seems to me you can only have on
> > of these per process/probe combination.
> > 
> > If you had used inode/offset based probes they would have been unique in
> > the system and you could have had an {inode,offset} indexed global tree
> > (or possibly a tree per inode, but that would mean adding to the inode
> > structure, which I think is best avoided).
> 
> Unlike kernel probing, uprobes has a disadvantage.
> Lets assume that the request for removing a probepoint when some of the
> threads have actually hit the probe. Because the handlers in uprobes can
> sleep, we cant remove the probepoint at the same time as the request for
> the removing the probe. This is where refcount steps in and helps us to
> decide when we can remove the probepoint. Even inoode based
> tracing or file based tracing would need it.

Stick the refcount in the actual struct uprobe instead?

> > 
> > That would also reduce the mm state to purely the xol area, no need to
> > manage this process to probe map.
> > 
> > > +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;
> > > +
> > > +	enum uprobe_task_state state;
> > > +
> > > +	struct uprobe_probept *active_ppt;
> > > +};
> > 
> > I would be thinking you can obtain the active probe point from the
> > address the task is stuck at and the state seems fairly redundant. Which
> > leaves you with the arch state, which afaict is exactly as large as the
> > pointer currently in the task struct.
> 
> Lets assume the thread is about to singlestep (or has singlestepped)
> So the instruction pointer is pointing to one of the slot (or it could
> be pointing to some other arbitrary address if it has singlestepped lets
> say a jump instruction). 
> 
> We could get the active probe point from the address the task is stuck
> if the instruction pointers happens to be pointing to slots.  But for
> that we would have to maintain a relation between slots and the current
> probepoint it is servicing.  I think its easier to cache the
> active_probept then try to maintain this relationship. Moreover we are
> never certain in some cases where the underlying instruction happened to
> be a jump.
> 
> state is needed so that we know at do_notify_resume time whether the
> thread should singlestep or has already singlestep.
> Do you see a way to determine in do_notify_resume if the thread is
> singlestepping or not. I dont think the instruction pointer can
> determine if we are about to singlestep or have already singlestepped.

The to singlestep or not would be implied by the IP pointing to the
start of a slot or not, but yes, I guess that as long as you do
singlestep you need some state.. sucks though. Boosted probes are much
nicer, they don't need that extra arch storage either, they can simply
push/pop the register.

> > > @@ -741,6 +745,642 @@ validate_end:
> > >  }
> > >  /* end of slot allocation for XOL */
> > >  
> > > +
> > > +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);
> > 
> > uproc is per mm, why a global mutex?
> 
> yes, uproc is per mm but gets created when the first probe on that
> process gets registered.
> Do you have anyother lock that you have in mind?
> I could have used the mmap_sem, but I am not sure if we should be taking
> a write lock on the mmap_sem.  

No particular other lock in mind, you could cmpxchg the pointer if
that's all you need it for. The problem is that if you want inode based
probes without a filter this will become a rather hot lock on fork().


> > 
> > > + */
> > > +void uprobe_handle_fork(struct task_struct *child)
> > > +{
> > > +	struct uprobe_process *uproc;
> > > +	struct uprobe_probept *ppt;
> > > +	int ret;
> > > +
> > > +	uproc = current->mm->uproc;
> > > +
> > > +	/*
> > > +	 * New process spawned by parent but not sharing the same mm.
> > > +	 * 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.
> > > +	 */
> > 
> > The grand thing about not having any of this process state is that you
> > dont need to clean it up either..
> 
> If we had done inode based tracing and a process that has breakpoints forks, 
> wont we have to recreate the breakpoint metadata for the child process?
> I dont see the child calling mmap. When and how would be the probepoint
> data, user_bkpt data for the breakpoints, get replicated/linked to the
> child?

What meta-data? You can find the uprobe itself from inode:offset, and
you know the return address from the trap site + orig ins size.

You don't need the probepoint, and there'd be only a single uprobe
instance.

The Xol area can be found at current->mm->xol_area, I don't think you
need more to process a uprobe.

> > > +	if (unlikely(!utask)) {
> > > +		utask = add_utask(uproc);
> > > +
> > > +		/* Failed to allocate utask for the current task. */
> > > +		BUG_ON(!utask);
> > > +		probept = uprobes_get_bkpt_addr(regs);
> > > +		ppt = find_probept(uproc, probept);
> > > +
> > > +		/*
> > > +		 * The probept was refcounted in uprobe_bkpt_notifier;
> > > +		 * Hence it would be mysterious to miss ppt now
> > > +		 */
> > > +		WARN_ON(!ppt);
> > > +		utask->active_ppt = ppt;
> > > +		utask->state = UTASK_BP_HIT;
> > > +	} else
> > > +		ppt = utask->active_ppt;
> > 
> > You can replace this with:
> > 
> >  addr = instruction_pointer(task_pt_regs(current)) -
> > ip_advancement_by_brkpt_insn;
> 
> uprobes_get_bkpt_addr does this exactly. However after we have
> singlestepped, we cant rely on this. Also how do we know if we have
> already singlestepped or not? After singlestepping we may still be
> pointing to some slot or the instruction pointer could be pointing
> something completely different.

if its not the start of a slot, you've already single-stepped. Ideally
you'd directly implement boosted probes, but I realize that's a tad more
work than keeping state.

> > > +	ppt = find_probept(uproc, probept);
> > > +	if (!ppt)
> > > +		return 0;
> > > +	get_probept(ppt);
> > > +	if (utask) {
> > > +		utask->active_ppt = ppt;
> > > +		utask->state = UTASK_BP_HIT;
> > > +	}
> > 
> > Right, so all this should die..
> > 
> > Obtain the address, lookup the vma, get the file, compute the offset
> > inside that file -> {inode,offset}
> > 
> > Do the lookup in the global tree to obtain the uprobe, and possibly
> > execute the probe handler right here in interrupt context, if it fails
> > with -EFAULT, continue with the below:
> 
> We dropped running the handler in interrupt context based on comments to
> my previous postings. It not only makes it complex at the time of
> handling the probe but we didnt see a difference when we compare the
> time it took to run the handler in task context to running the handler
> in interrupt context. I had even posted the timing info when
> register_uprobes was exported. 

Is that because of the singlestep overhead? With boosted probes I would
think it'd be much faster to take 1 trap, deal with it and continue
execution, than to frob tons of kernel code in between.



A bit more about these filter thingies, add a method to struct uprobe,
something like int uprobe::wants_probe(struct task_struct *p) and add a
single bit to task_struct (there's a few bitfields with holes in there).

The on clone()/mmap() call the relevant wants_probe() methods, if one is
true, set the task_struct::has_uprobe flag and install the probes.

If nothing in the process wants probing, you'll never install the probes
and nothing ever triggers, of only one of many tasks in the process gets
tagged, you'll have to look up the probe anyway to know where to
continue, but you can avoid calling the handler.



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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03 16:40     ` Srikar Dronamraju
  2010-09-03 16:51       ` Peter Zijlstra
@ 2010-09-03 17:25       ` Peter Zijlstra
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 17:25 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Fri, 2010-09-03 at 22:10 +0530, Srikar Dronamraju wrote:
> > > +   vma = find_vma(mm, addr);
> > > +
> > > +   /* Don't expand vma on mremap(). */
> > > +   vma->vm_flags |= VM_DONTEXPAND | VM_DONTCOPY;
> > > +   area->vaddr = vma->vm_start;
> > 
> > Seems interesting,.. why not use install_special_mapping(), that's what
> > the VDSO uses.
> 
> Okay, I hadnt looked at install_special_mapping earlier so I will take a
> look and incorporate it. However I am not clear at this point what
> install_special_mapping is giving us here.  Also install_special_mapping
> is already defining its own vm_ops esp a close method thats doesnt seem
> to be doing anything. So at this point I am not clear how we are link
> the vm_ops, the close method and install_special_mapping.

What you're doing might well be the right thing, I was just wondering.

I think that, after thinking about it more, that the shmem file thing
you're doing has the added benefit that the things gets auto-magic
paging, which is a good thing.



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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03 16:51       ` Peter Zijlstra
@ 2010-09-03 17:26         ` Srikar Dronamraju
  2010-09-03 17:41           ` Peter Zijlstra
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-03 17:26 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

* Peter Zijlstra <peterz@infradead.org> [2010-09-03 18:51:54]:

> On Fri, 2010-09-03 at 22:10 +0530, Srikar Dronamraju wrote:
> > > > +   mb();
> > > 
> > > Where is the matching barrier?
> > 
> > I dont want the compiler to reorder the instructions and do the
> > assignment for user_bkpt to be done before we complete the copy above.
> > 
> > If the assignment happens before we copy the content into the slot,
> > someother thread that might hit the same probe actually things the slot
> > is ready and tries to jump to that slot even before the slot is
> > initialized.
> > 
> > Please let me know if I could have done it differently. 
> 
> 
> If you want a compiler barrier, use barrier(), but here you seem to
> describe a multi-threaded situation, in which case the observer thread
> needs at least a rmb() in order for that mb() to mean anything other
> than the compiler barrier it implies.
> 
> Also, use smp_* barriers.
> 
> 
> 

Okay,  would something like this suffice?


static unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
				struct uprobes_xol_area *xol_area)
{
	unsigned long flags, xol_vaddr = 0;
	int len;

	if (unlikely(!xol_area))
		return 0;

	smp_rmb();
	if (user_bkpt->xol_vaddr)
		return user_bkpt->xol_vaddr;

	spin_lock_irqsave(&xol_area->lock, flags);
	xol_vaddr = xol_take_insn_slot(xol_area);
	spin_unlock_irqrestore(&xol_area->lock, flags);

	/*
	 * Initialize the slot if user_bkpt->vaddr points to valid
	 * instruction slot.
	 */
	if (!xol_vaddr)
		return 0;

	len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
					UPROBES_XOL_SLOT_BYTES, 1);
	if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
		printk(KERN_ERR "Failed to copy instruction at %#lx "
				"len = %d\n", user_bkpt->vaddr, len);

	/*
	 * Update user_bkpt->xol_vaddr after giving a chance for the slot to
	 * be initialized.
	 */
	smp_mb();
	user_bkpt->xol_vaddr = xol_vaddr;
	return user_bkpt->xol_vaddr;
}

-- 
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-03 16:42     ` Srikar Dronamraju
  2010-09-03 17:19       ` Peter Zijlstra
@ 2010-09-03 17:27       ` Peter Zijlstra
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 17:27 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Fri, 2010-09-03 at 22:12 +0530, Srikar Dronamraju wrote:
> 
> 3. Ingo has a requirement for allowing normal users to use uprobes thro
> perf. When this feature gets implemented, we have to be careful about a
> normal users trying to just trace their application resulting in it
> hitting performance all other users. 

For that I guess we should have a way to tie a uprobe to a filedesc or
somesuch, that way whenever the owner dies, the probe goes away.

Such probes would also obviuosly get a filter that limits it to tasks of
its own user etc..

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03 17:26         ` Srikar Dronamraju
@ 2010-09-03 17:41           ` Peter Zijlstra
  2010-09-06  5:38             ` Srikar Dronamraju
  0 siblings, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 17:41 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

On Fri, 2010-09-03 at 22:56 +0530, Srikar Dronamraju wrote:
> 
> 
> static unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
>                                 struct uprobes_xol_area *xol_area)
> {
>         unsigned long flags, xol_vaddr = 0;
>         int len;
> 
>         if (unlikely(!xol_area))
>                 return 0;
> 
>         smp_rmb();
>         if (user_bkpt->xol_vaddr)
>                 return user_bkpt->xol_vaddr;
> 
>         spin_lock_irqsave(&xol_area->lock, flags);
>         xol_vaddr = xol_take_insn_slot(xol_area);
>         spin_unlock_irqrestore(&xol_area->lock, flags);
> 
>         /*
>          * Initialize the slot if user_bkpt->vaddr points to valid
>          * instruction slot.
>          */
>         if (!xol_vaddr)
>                 return 0;
> 
>         len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
>                                         UPROBES_XOL_SLOT_BYTES, 1);
>         if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
>                 printk(KERN_ERR "Failed to copy instruction at %#lx "
>                                 "len = %d\n", user_bkpt->vaddr, len);
> 
>         /*
>          * Update user_bkpt->xol_vaddr after giving a chance for the slot to
>          * be initialized.
>          */
>         smp_mb();
>         user_bkpt->xol_vaddr = xol_vaddr;
>         return user_bkpt->xol_vaddr;
> } 

Racy like you won't believe..

Suppose multiple threads hitting the trap at the same time, every thread
will end up failing the check and allocating a new slot for it, at the
end the slowest thread will end up setting the value.



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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-03 10:26   ` Andi Kleen
@ 2010-09-03 17:48     ` Srikar Dronamraju
  2010-09-03 18:00       ` Peter Zijlstra
  2010-09-06  7:53       ` Andi Kleen
  0 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-03 17:48 UTC (permalink / raw)
  To: Andi Kleen
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney

> 
> One general comment here: since with uprobes the instruction
> decoder becomes security critical did you do any fuzz tests
> on it (e.g. like using it on crashme or on code that has 
> been corrupted with a few bitflips) ?

I havent tried any fuzz tests with the instruction decoder. But I am
not sure if Masami has tried that out some of these. 
One question: Do you want to test uprobes with crashme or test
instruction decoder with crashme.

> 
> > +typedef u8 user_bkpt_opcode_t;
> 
> Maybe it's me, but I would prefer breakpoint instead of bkpt

Even Peter wasnt comfortable with user_bkpt. How about user_bp?
i.e the above field would be user_bp_opcode_t. I felt 
user_breakpoint_opcode_t might look long. Also we would have to
rename other structures accordingly like user_bkpt_task_arch_info
would become user_breakpoint_task_arch_info. Do let me know your
choice.


> > +#ifdef CONFIG_X86_32
> > +#define is_32bit_app(tsk) 1
> > +#else
> > +#define is_32bit_app(tsk) (test_tsk_thread_flag(tsk, TIF_IA32))
> > +#endif
> 
> This probably should be elsewhere.

Would this fit in x86 Instruction decoder?

> 
> > +
> > +#define UPROBES_FIX_RIP_AX	0x8000
> > +#define UPROBES_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)
> > +
> > +/*
> > + * @reg: reflects the saved state of the task
> > + * @vaddr: the virtual address to jump to.
> > + * Return 0 on success or a -ve number on error.
> > + */
> > +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 &
> > +			(UPROBES_FIX_RIP_AX | UPROBES_FIX_RIP_CX)) != 0);
> > +}
> > +
> 
> Shouldn't all this stuff be in the instruction decoder? 
> 
> It seems weird to have the knowledge spread over multiple files.

Agree,  Shall move it to instruction decoder.

> 
> > +
> > +static void report_bad_prefix(void)
> > +{
> > +	printk(KERN_ERR "uprobes 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, "
> > +		"uprobes 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 "uprobes does not currently support probing "
> > +		"instructions with the 2-byte opcode 0x0f 0x%2.2x\n", op);
> > +}
> 
> These functions that just do a single printk seem weird. I would
> do that in the caller. Also the message could be shortened I guess
> and should just dump the bytes.
> 

Okay, I can move the printk to the caller, I will try to shorten the
message, Would something like "uprobes: no support for 2-byte
opcode 0x0f 0x%2" look fine?

> > +
> > +/**
> > + * analyze_insn - instruction analysis including validity and fixups.
> > + * @tsk: the probed task.
> > + * @user_bkpt: the probepoint information.
> > + * Return 0 on success or a -ve number on error.
> > + */
> > +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))
> 
> This check is not fully correct because it's valid to have
> 32bit code in 64bit programs and vice versa.  The only good
> way to check that is to look at the code segment at runtime
> though (and it gets complicated if you want to handle LDTs,
> but that could be optional). May be difficult to do though.

validate_insn_32bit is able to identify all valid instructions in a 32
bit app and validate_insn_64bits is a superset of
validate_insn_32bits; i.e it considers valid 32 bit codes as valid too.

Did you get a chance to look at
validate_insn_32bit/validate_insn_64bits? If you feel that
validate_insn_32bit/validate_insn_64bits? are unable to detect
valid codes, then I will certainly rework.

> 
> Also the compat bit is not necessarily set if no system call is
> executing. You would rather need to check the exec_domain.

Okay, I shall check and revert on this.

> > + */
> > +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 = uprobes_read_vm(tsk, (void __user *) sp, &ra, rasize);
> > +	if (unlikely(ncopied != rasize))
> > +		goto fail;
> 
> goto is automatically unlikely and unlikely is deprecated anyways.

Okay, shall remove unlikely from the above.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-03 17:48     ` Srikar Dronamraju
@ 2010-09-03 18:00       ` Peter Zijlstra
  2010-09-06  7:53       ` Andi Kleen
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-03 18:00 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Andi Kleen, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, LKML, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, Andrew Morton, Paul E. McKenney

On Fri, 2010-09-03 at 23:18 +0530, Srikar Dronamraju wrote:
> > Maybe it's me, but I would prefer breakpoint instead of bkpt
> 
> Even Peter wasnt comfortable with user_bkpt. 

My main objection was the uprobe.c and user_bkpt.c splitup, its all
about uprobes, but as to this name, you can simply name it
uprobe_opcode_t, no need to preserve the whole user breakpoint thing at
all.

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03 17:41           ` Peter Zijlstra
@ 2010-09-06  5:38             ` Srikar Dronamraju
  0 siblings, 0 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-06  5:38 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney

> > 
> > static unsigned long xol_get_insn_slot(struct user_bkpt *user_bkpt,
> >                                 struct uprobes_xol_area *xol_area)
> > {
> >         unsigned long flags, xol_vaddr = 0;
> >         int len;
> > 
> >         if (unlikely(!xol_area))
> >                 return 0;
> > 
> >         smp_rmb();
> >         if (user_bkpt->xol_vaddr)
> >                 return user_bkpt->xol_vaddr;
> > 
> >         spin_lock_irqsave(&xol_area->lock, flags);
> >         xol_vaddr = xol_take_insn_slot(xol_area);
> >         spin_unlock_irqrestore(&xol_area->lock, flags);
> > 
> >         /*
> >          * Initialize the slot if user_bkpt->vaddr points to valid
> >          * instruction slot.
> >          */
> >         if (!xol_vaddr)
> >                 return 0;
> > 
> >         len = access_process_vm(current, xol_vaddr, user_bkpt->insn,
> >                                         UPROBES_XOL_SLOT_BYTES, 1);
> >         if (unlikely(len < UPROBES_XOL_SLOT_BYTES))
> >                 printk(KERN_ERR "Failed to copy instruction at %#lx "
> >                                 "len = %d\n", user_bkpt->vaddr, len);
> > 
> >         /*
> >          * Update user_bkpt->xol_vaddr after giving a chance for the slot to
> >          * be initialized.
> >          */
> >         smp_mb();
> >         user_bkpt->xol_vaddr = xol_vaddr;
> >         return user_bkpt->xol_vaddr;
> > } 
> 
> Racy like you won't believe..
> 
> Suppose multiple threads hitting the trap at the same time, every thread
> will end up failing the check and allocating a new slot for it, at the
> end the slowest thread will end up setting the value.
> 

Agree, I shall fix this up.
Since set_bit and clear_bit are atomic, I shall change the
area->lock from a spinlock to a mutex, and have the mutex released
after the slot has been updated with the "single-stepping
instruction".

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-03 17:48     ` Srikar Dronamraju
  2010-09-03 18:00       ` Peter Zijlstra
@ 2010-09-06  7:53       ` Andi Kleen
  2010-09-06 13:44         ` Srikar Dronamraju
  1 sibling, 1 reply; 71+ messages in thread
From: Andi Kleen @ 2010-09-06  7:53 UTC (permalink / raw)
  To: Srikar Dronamraju, Peter Zijlstra, linux-kernel

On Fri, 3 Sep 2010 23:18:32 +0530
Srikar Dronamraju <srikar@linux.vnet.ibm.com> wrote:

[cutting down cc list]

> > 
> > One general comment here: since with uprobes the instruction
> > decoder becomes security critical did you do any fuzz tests
> > on it (e.g. like using it on crashme or on code that has 
> > been corrupted with a few bitflips) ?
> 
> I havent tried any fuzz tests with the instruction decoder. But I am
> not sure if Masami has tried that out some of these. 
> One question: Do you want to test uprobes with crashme or test
> instruction decoder with crashme.

Ideally both, but as a minimum the part that is exposed
to user space, that is uprobes.

BTW if you test it I would test it both with real crashme
and varying legal code that just has a few bits flipped.

> > > +#ifdef CONFIG_X86_32
> > > +#define is_32bit_app(tsk) 1
> > > +#else
> > > +#define is_32bit_app(tsk) (test_tsk_thread_flag(tsk, TIF_IA32))
> > > +#endif
> > 
> > This probably should be elsewhere.
> 
> Would this fit in x86 Instruction decoder?

compat.h probably. 


> Okay, I can move the printk to the caller, I will try to shorten the
> message, Would something like "uprobes: no support for 2-byte
> opcode 0x0f 0x%2" look fine?

Yes that's fine. Optionally you could supply a short
script like scripts/decodecode that feeds it through objdump -d
This might need dumping a few more bytes.


> > This check is not fully correct because it's valid to have
> > 32bit code in 64bit programs and vice versa.  The only good
> > way to check that is to look at the code segment at runtime
> > though (and it gets complicated if you want to handle LDTs,
> > but that could be optional). May be difficult to do though.
> 
> validate_insn_32bit is able to identify all valid instructions in a 32
> bit app and validate_insn_64bits is a superset of
> validate_insn_32bits; i.e it considers valid 32 bit codes as valid
> too.

How can this be? e.g. 32bit has 1 byte INC/DEC but on 64bit
these are REX prefixes and can be in front of nearly anything.
So a super set cannot be correct. It has to be either / or.

> 
> Did you get a chance to look at
> validate_insn_32bit/validate_insn_64bits? If you feel that
> validate_insn_32bit/validate_insn_64bits? are unable to detect
> valid codes, then I will certainly rework.

I don't think you can do a 100% solution because for 100%
you would need to know the code segment the CPU is going
to use later, and that's not possible in advance.

A heuristic is reasonable (and leave out applications
that generate 64bit code from 32bit executables or vice versa)
but you need to test the right personality bits for that.

 
> > Also the compat bit is not necessarily set if no system call is
> > executing. You would rather need to check the exec_domain.
> 
> Okay, I shall check and revert on this.

Hmm actually I double checked and this is a separate bit.
So scratch that, TIF_32BIT is ok to test.

-Andi
-- 
ak@linux.intel.com -- Speaking for myself only.

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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-06  7:53       ` Andi Kleen
@ 2010-09-06 13:44         ` Srikar Dronamraju
  2010-09-06 14:16           ` Andi Kleen
  2010-09-07  0:56           ` Masami Hiramatsu
  0 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-06 13:44 UTC (permalink / raw)
  To: Andi Kleen; +Cc: Peter Zijlstra, linux-kernel, Masami Hiramatsu, Jim Keniston

[adding Masami and Jim to the copy list] 

> > I havent tried any fuzz tests with the instruction decoder. But I am
> > not sure if Masami has tried that out some of these. 
> > One question: Do you want to test uprobes with crashme or test
> > instruction decoder with crashme.
> 
> Ideally both, but as a minimum the part that is exposed
> to user space, that is uprobes.

Okay, I will test uprobes with crashme.

> 
> > 
> > validate_insn_32bit is able to identify all valid instructions in a 32
> > bit app and validate_insn_64bits is a superset of
> > validate_insn_32bits; i.e it considers valid 32 bit codes as valid
> > too.
> 
> How can this be? e.g. 32bit has 1 byte INC/DEC but on 64bit
> these are REX prefixes and can be in front of nearly anything.
> So a super set cannot be correct. It has to be either / or.
> 

You are right, the validate_insn_32bits refers to good_insns_32 and
validate_insn_64bits refers to good_insns_64 to decode 1 byte
instructions. Some instructions like 0x06 and 0x0e seem to be valid in
good_insns_32 but not in good_insns_64. 

> > 
> > Did you get a chance to look at
> > validate_insn_32bit/validate_insn_64bits? If you feel that
> > validate_insn_32bit/validate_insn_64bits? are unable to detect
> > valid codes, then I will certainly rework.
> 
> I don't think you can do a 100% solution because for 100%
> you would need to know the code segment the CPU is going
> to use later, and that's not possible in advance.
> 

I think you are referring to RIP related instructions, this how we
handle them. 
Please correct us if we are wrong, but here is what we do 
- While analyzing the instruction, take into account which register acts
  as the code segment register.

- When interrupted (but before singlestep), copy the contents of the
  register which we think acts as code segment register in our
  above analysis into per-task scratch variable.

- After singlestepping we retrieve the saved per-task scratch
  variable into the corresponding register.

> A heuristic is reasonable (and leave out applications
> that generate 64bit code from 32bit executables or vice versa)
> but you need to test the right personality bits for that.
> 
> 
> > > Also the compat bit is not necessarily set if no system call is
> > > executing. You would rather need to check the exec_domain.
> > 
> > Okay, I shall check and revert on this.
> 
> Hmm actually I double checked and this is a separate bit.
> So scratch that, TIF_32BIT is ok to test.

Okay, Thanks for confirming this.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-06 13:44         ` Srikar Dronamraju
@ 2010-09-06 14:16           ` Andi Kleen
  2010-09-07  0:56           ` Masami Hiramatsu
  1 sibling, 0 replies; 71+ messages in thread
From: Andi Kleen @ 2010-09-06 14:16 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, linux-kernel, Masami Hiramatsu, Jim Keniston

On Mon, 6 Sep 2010 19:14:07 +0530
Srikar Dronamraju <srikar@linux.vnet.ibm.com> wrote:

> [adding Masami and Jim to the copy list] 
> 
> > > I havent tried any fuzz tests with the instruction decoder. But I
> > > am not sure if Masami has tried that out some of these. 
> > > One question: Do you want to test uprobes with crashme or test
> > > instruction decoder with crashme.
> > 
> > Ideally both, but as a minimum the part that is exposed
> > to user space, that is uprobes.
> 
> Okay, I will test uprobes with crashme.

crashme and valid 1/2 bit corrupted code please if possible. I'm 
not sure crashme alone has enough coverage.


> > > 
> > > Did you get a chance to look at
> > > validate_insn_32bit/validate_insn_64bits? If you feel that
> > > validate_insn_32bit/validate_insn_64bits? are unable to detect
> > > valid codes, then I will certainly rework.
> > 
> > I don't think you can do a 100% solution because for 100%
> > you would need to know the code segment the CPU is going
> > to use later, and that's not possible in advance.
> > 
> 
> I think you are referring to RIP related instructions, this how we
> handle them. 

I just meant regarding long mode vs compat mode which defines
whether REX prefixes are valid or not. Because this can
change any time (if the application does a long jump) you
cannot know in advance what it is going to use. But 
it's also very rare to use long jumps at all, so this
can be probably ignored (but should be documented somewhere),
and just guess based on the executable. I just wanted
to point out that it's not a 100% solution.

I don't think you need to care about segment bases either. While
they can be used (16bit Wine or dosemu) it's quite rare
and not supporting uprobes for this is totally reasonable.

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-03 17:19       ` Peter Zijlstra
@ 2010-09-06 17:46         ` Srikar Dronamraju
  2010-09-06 18:15           ` Peter Zijlstra
                             ` (3 more replies)
  0 siblings, 4 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-06 17:46 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

* Peter Zijlstra <peterz@infradead.org> [2010-09-03 19:19:09]:

> > 
> > However I would have an issue with making inode based probing the
> > default.
> > 1. Making all probing based on inode can be a performance hog.
> 
> How so? You can add filters if you want.

The breakpoint exception and singlestep account for a substaintial time
of the uprobes probe handling. With increasing number of breakpoint hits and singlesteps, wouldnt the overall load increase?

> 
> > 2. Since unlike kernel space, every process has a different space, so
> > why would we have to insert breakpoints in each of its process space if
> > we are not interested in them.
> 
> You don't have to, but you can. The problem I have with this stuff is
> that it makes the pid thing a primary interface, whereas it should be
> one of many filter possibilities.

I think the otherway, 
Why instrument a process and filter it out, if we are not interested in it.
While instrumenting kernel, we dont have this flexibility. So
having a pid based filter is the right thing to do for kernel
based tracing.

If we can get the per process based tracing right, we can build
higher lever stuff including the file based tracing easily.

All tools/debuggers in the past have all worked with process based
tracing.

Tools like gdb can actually use the displaced singlestepping
feature that uprobes provides. Some gdb developers have told on LKML
earlier that they would be willing to use displaced singlestepping if
the kernel provides an API that they can use.

Also about the security perspective when allowing normal users use
perf to trace their applications. Using this model, we dont have to
write extra filters to limit them. These filters might allow uprobe
handlers on only tasks belonging to that user. However it still
interrupts task of other users. And as I said earlier, breakpoint
exception and singlestepping actually make a very very substantial part
of the handling. The actual uprobe handler depending on what it does
might actually be very minimal part of the uprobe probe handling.

> > > > +	 */
> > > > +	pid_t pid;
> > > 
> > > Would fail for pid namespaces I guess...
> > 
> > Can you explain a bit more?
> 
> What pid namespace will you interpret that number in?

Same namespace as the requestor. i.e whichever name space
register_uprobe/unregister_uprobe is called.

> > 
> > Unlike kernel probing, uprobes has a disadvantage.
> > Lets assume that the request for removing a probepoint when some of the
> > threads have actually hit the probe. Because the handlers in uprobes can
> > sleep, we cant remove the probepoint at the same time as the request for
> > the removing the probe. This is where refcount steps in and helps us to
> > decide when we can remove the probepoint. Even inoode based
> > tracing or file based tracing would need it.
> 
> Stick the refcount in the actual struct uprobe instead?

What if the called does something like this when one or more
threads are processing the breakpoint.
unregister_uprobe(u);
kfree(u);

In the current implementation, the probepoint structure might be
released much later after the uprobe structure is released.
Unlike uprobe struct, probepoint structure is allocated by uprobes
sub-system and it knows how to release it cleanly. However we dont have
any idea about when and how uprobe struct can be released.

> > 
> > state is needed so that we know at do_notify_resume time whether the
> > thread should singlestep or has already singlestep.
> > Do you see a way to determine in do_notify_resume if the thread is
> > singlestepping or not. I dont think the instruction pointer can
> > determine if we are about to singlestep or have already singlestepped.
> 
> The to singlestep or not would be implied by the IP pointing to the
> start of a slot or not, but yes, I guess that as long as you do
> singlestep you need some state.. sucks though. Boosted probes are much
> nicer, they don't need that extra arch storage either, they can simply
> push/pop the register.

Yes, I agree, we may not need the state after boosted probes.
I am not sure at this time if we can do boosted probes for all
instructions. I think we can discuss about boosted probes later.

> > > > +
> > > > +/* Guards lookup, creation, and deletion of uproc. */
> > > > +static DEFINE_MUTEX(uprobe_mutex);
> > > 
> > > uproc is per mm, why a global mutex?
> > 
> > yes, uproc is per mm but gets created when the first probe on that
> > process gets registered.
> > Do you have anyother lock that you have in mind?
> > I could have used the mmap_sem, but I am not sure if we should be taking
> > a write lock on the mmap_sem.  
> 
> No particular other lock in mind, you could cmpxchg the pointer if
> that's all you need it for. The problem is that if you want inode based
> probes without a filter this will become a rather hot lock on fork().
> 

I can certainly try cmpxchg(). 

> > 
> > We dropped running the handler in interrupt context based on comments to
> > my previous postings. It not only makes it complex at the time of
> > handling the probe but we didnt see a difference when we compare the
> > time it took to run the handler in task context to running the handler
> > in interrupt context. I had even posted the timing info when
> > register_uprobes was exported. 
> 
> Is that because of the singlestep overhead? With boosted probes I would
> think it'd be much faster to take 1 trap, deal with it and continue
> execution, than to frob tons of kernel code in between.
> 

The difference between running handlers in task context and running in
interrupt context is the extra do_notify_resume() that gets called from
task context. But we have more guarantees, flexibility and less
complexity with running the handler from do_notify_resume than
running the handler in interrupt context. Since do_notify_resume is the
only extra thing that gets called (on the way out of the
interrupt), the difference in the penalties between running the
handler in task context and running the handler in interrupt
context is negligible.

I would want to stick with running handlers in task context for now.
We could optimize this later on depending on further results and
uprobes maturing.

> 
> 
> A bit more about these filter thingies, add a method to struct uprobe,
> something like int uprobe::wants_probe(struct task_struct *p) and add a
> single bit to task_struct (there's a few bitfields with holes in there).
> 
> The on clone()/mmap() call the relevant wants_probe() methods, if one is
> true, set the task_struct::has_uprobe flag and install the probes.
> 
> If nothing in the process wants probing, you'll never install the probes
> and nothing ever triggers, of only one of many tasks in the process gets
> tagged, you'll have to look up the probe anyway to know where to
> continue, but you can avoid calling the handler.
> 

Yes, we can avoid calling the handler. but we still have the other
penalties of breakpoint hit, searching the probe, and
singlestepping. Just to clarify, I am not looking at probing per task
but probing per process. So the pid field in uprobe refers to the
tgid.

--
Thanks and Regards
Srikar


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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-03  7:26       ` Peter Zijlstra
@ 2010-09-06 17:59         ` Srikar Dronamraju
  2010-09-06 18:20           ` Peter Zijlstra
  2010-09-06 18:28           ` Peter Zijlstra
  0 siblings, 2 replies; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-06 17:59 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

> > > An alternative method would be to have 1 slot per cpu, and manage the
> > > slot content using preemption notifiers. That gives you a fixed number
> > > of slots and an unlimited number of probe points.
> > > 
> > > If the preemption happens to be a migration you need to rewrite the
> > > userspace IP to point to the new slot -- if indeed the task was inside
> > > one when it got preempted -- but that all should be doable.
> > > 
> > 
> > 3. Yes migration is an issue esp
> > -  if a thread of the same process that hit a breakpoint is scheduled into the same cpu and that newly scheduled thread hits a breakpoint. 
> > - Something similar can happen if a multithreaded process runs on a
> >   uniprocessor machine.
> 
> > 5. I think we are covered on the cpu hotplug too, (i.e not sure if we have
> > to make uprobes cpu hot plug aware.).
> 
> Not if you use a slot per cpu and use preemption notifiers, the
> preemption notifiers will migrate the slots around.


Lets say the thread while singlestepping the process gets
pre-empted. Eventually the cpu might run some other thread of the same
process before picking the first run thread. Or the first run
thread could after migration due to load balancing or whatever end up
running on  a different thread?  How do we handle these cases? 

> 
> > 6. We would still be allocating a page for the slots. Unless we want
> > to expand to more slots than available in one page, I dont see the
> > disadvantages with the current approach.
> 
> The current approach limits the number of probes to what fits in a page.
> The slot per cpu approach will have no such limit.


yes the limit on number of probes is a limitation. For now the
implementation would be straight and easy. We could either rework on the
algorithm or add more pages depending on how often uprobes gets used.

> > 2  This might complicate booster probe, because the jump
> > instruction that follows the original instruction now actually have to
> > coded every time.
> 
> Why can't you keep the whole replacement sequence in-tact? Simply copy
> it out into the slot each time.

Yes, if we use jump absolute then the replacement sequence stays
in-tact.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 17:46         ` Srikar Dronamraju
@ 2010-09-06 18:15           ` Peter Zijlstra
  2010-09-06 18:15           ` Peter Zijlstra
                             ` (2 subsequent siblings)
  3 siblings, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 18:15 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 23:16 +0530, Srikar Dronamraju wrote:
> > > Unlike kernel probing, uprobes has a disadvantage.
> > > Lets assume that the request for removing a probepoint when some of the
> > > threads have actually hit the probe. Because the handlers in uprobes can
> > > sleep, we cant remove the probepoint at the same time as the request for
> > > the removing the probe. This is where refcount steps in and helps us to
> > > decide when we can remove the probepoint. Even inoode based
> > > tracing or file based tracing would need it.
> > 
> > Stick the refcount in the actual struct uprobe instead?
> 
> What if the called does something like this when one or more
> threads are processing the breakpoint.
> unregister_uprobe(u);
> kfree(u);

That's what atomic_inc_unless_zero() and RCU are for.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 17:46         ` Srikar Dronamraju
  2010-09-06 18:15           ` Peter Zijlstra
@ 2010-09-06 18:15           ` Peter Zijlstra
  2010-09-07  6:48             ` Srikar Dronamraju
  2010-09-06 18:25           ` Mathieu Desnoyers
  2010-09-06 20:40           ` Christoph Hellwig
  3 siblings, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 18:15 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 23:16 +0530, Srikar Dronamraju wrote:
> * Peter Zijlstra <peterz@infradead.org> [2010-09-03 19:19:09]:
> 
> > > 
> > > However I would have an issue with making inode based probing the
> > > default.
> > > 1. Making all probing based on inode can be a performance hog.
> > 
> > How so? You can add filters if you want.
> 
> The breakpoint exception and singlestep account for a substaintial
> time
> of the uprobes probe handling. With increasing number of breakpoint
> hits and singlesteps, wouldnt the overall load increase?
> 
You're really not getting it, are you? No, it would result in the exact
same amount of actual breakpoints hit.

> > 
> > > 2. Since unlike kernel space, every process has a different space, so
> > > why would we have to insert breakpoints in each of its process space if
> > > we are not interested in them.
> > 
> > You don't have to, but you can. The problem I have with this stuff is
> > that it makes the pid thing a primary interface, whereas it should be
> > one of many filter possibilities.
> 
> I think the otherway, 
> Why instrument a process and filter it out, if we are not interested in it.
> While instrumenting kernel, we dont have this flexibility. So
> having a pid based filter is the right thing to do for kernel
> based tracing.
> 
> If we can get the per process based tracing right, we can build
> higher lever stuff including the file based tracing easily.

Creating inode based probes on top of pid based probes is terribly ugly.

>  Just to clarify, I am not looking at probing per task
> but probing per process. So the pid field in uprobe refers to the
> tgid. 

Urgh,.. I really oppose the whole pid-centric thing, if that means
process wide and not per task its even worse.



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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-06 17:59         ` Srikar Dronamraju
@ 2010-09-06 18:20           ` Peter Zijlstra
  2010-09-06 18:28           ` Peter Zijlstra
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 18:20 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 23:29 +0530, Srikar Dronamraju wrote:
> 
> Lets say the thread while singlestepping the process gets
> pre-empted. Eventually the cpu might run some other thread of the same
> process before picking the first run thread. Or the first run
> thread could after migration due to load balancing or whatever end up
> running on  a different thread?  How do we handle these cases? 

So assuming we're preempted while the IP is inside the slot:

On the preempt-out we store the slot relative ip (ip - start_of_slot),
on preempt-in we write the replacement instructions in our cpu slot
(could be the same cpu, could be another) and re-position the ip to
point to the same relative position inside that slot, then go!

It really doesn't matter what happens in between.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 17:46         ` Srikar Dronamraju
  2010-09-06 18:15           ` Peter Zijlstra
  2010-09-06 18:15           ` Peter Zijlstra
@ 2010-09-06 18:25           ` Mathieu Desnoyers
  2010-09-06 20:40           ` Christoph Hellwig
  3 siblings, 0 replies; 71+ messages in thread
From: Mathieu Desnoyers @ 2010-09-06 18:25 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

* Srikar Dronamraju (srikar@linux.vnet.ibm.com) wrote:
> * Peter Zijlstra <peterz@infradead.org> [2010-09-03 19:19:09]:
> 
> > > 
> > > However I would have an issue with making inode based probing the
> > > default.
> > > 1. Making all probing based on inode can be a performance hog.
> > 
> > How so? You can add filters if you want.
> 
> The breakpoint exception and singlestep account for a substaintial time
> of the uprobes probe handling. With increasing number of breakpoint hits and
> singlesteps, wouldnt the overall load increase?
> 
> > 
> > > 2. Since unlike kernel space, every process has a different space, so
> > > why would we have to insert breakpoints in each of its process space if
> > > we are not interested in them.
> > 
> > You don't have to, but you can. The problem I have with this stuff is
> > that it makes the pid thing a primary interface, whereas it should be
> > one of many filter possibilities.
> 
> I think the otherway, 
> Why instrument a process and filter it out, if we are not interested in it.
> While instrumenting kernel, we dont have this flexibility. So
> having a pid based filter is the right thing to do for kernel
> based tracing.
> 
> If we can get the per process based tracing right, we can build
> higher lever stuff including the file based tracing easily.
> 
> All tools/debuggers in the past have all worked with process based
> tracing.
> 
> Tools like gdb can actually use the displaced singlestepping
> feature that uprobes provides. Some gdb developers have told on LKML
> earlier that they would be willing to use displaced singlestepping if
> the kernel provides an API that they can use.
> 
> Also about the security perspective when allowing normal users use
> perf to trace their applications. Using this model, we dont have to
> write extra filters to limit them. These filters might allow uprobe
> handlers on only tasks belonging to that user. However it still
> interrupts task of other users. And as I said earlier, breakpoint
> exception and singlestepping actually make a very very substantial part
> of the handling. The actual uprobe handler depending on what it does
> might actually be very minimal part of the uprobe probe handling.

I think we are both partially right in slightly different ways. I think Peter is
right in that the PID should not be mandatory (e.g. specifying a PID of 0 should
apply to all tasks), and you are also right in that being able to apply the
"filter" directly at the executable image level is vital for performance.

So how about this: we can provide both task and inode selection arguments. The
task selection argument can be 0 (apply to all tasks) or non-zero (one task
specifically). The inode argument would be mandatory.

Then, eventually, we can enhance the generic filtering facility so it can be
made aware of filtering shortcuts provided by the instrumentation (in this case,
uprobes would provide a per-tgid filtering shortcut).

Thoughts ?

Mathieu

-- 
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com

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

* Re: [PATCHv11 2.6.36-rc2-tip 3/15]  3: uprobes: Slot allocation for Execution out of line(XOL)
  2010-09-06 17:59         ` Srikar Dronamraju
  2010-09-06 18:20           ` Peter Zijlstra
@ 2010-09-06 18:28           ` Peter Zijlstra
  1 sibling, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 18:28 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 23:29 +0530, Srikar Dronamraju wrote:
> > The current approach limits the number of probes to what fits in a page.
> > The slot per cpu approach will have no such limit.
> 
> 
> yes the limit on number of probes is a limitation. For now the
> implementation would be straight and easy. We could either rework on the
> algorithm or add more pages depending on how often uprobes gets used.


Right, but with the proposed slot-per-cpu we'd be able to have unlimited
active probes within that single page, even with boosted probes,
assuming 16 bytes per instruction:

 push reg
 mov reg,foo
 insn
 pop reg
 jmp

and cacheline alignment we'd end up with 128 bytes per slot, we can
service 32 cpus per page. Which, for now, means that all my machines
need but a single page.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 17:46         ` Srikar Dronamraju
                             ` (2 preceding siblings ...)
  2010-09-06 18:25           ` Mathieu Desnoyers
@ 2010-09-06 20:40           ` Christoph Hellwig
  2010-09-06 21:06             ` Peter Zijlstra
  2010-09-07 12:02             ` Srikar Dronamraju
  3 siblings, 2 replies; 71+ messages in thread
From: Christoph Hellwig @ 2010-09-06 20:40 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney,
	Srivatsa Vaddagiri

On Mon, Sep 06, 2010 at 11:16:42PM +0530, Srikar Dronamraju wrote:
> > You don't have to, but you can. The problem I have with this stuff is
> > that it makes the pid thing a primary interface, whereas it should be
> > one of many filter possibilities.
> 
> I think the otherway, 
> Why instrument a process and filter it out, if we are not interested in it.
> While instrumenting kernel, we dont have this flexibility. So
> having a pid based filter is the right thing to do for kernel
> based tracing.
> 
> If we can get the per process based tracing right, we can build
> higher lever stuff including the file based tracing easily.
> 
> All tools/debuggers in the past have all worked with process based
> tracing.

I have the feeling that you guys are at least partially talking past
each other.

For the "perf probe --add" interface the only sane interface is one by
filename and then symbol / liner number / etc.

But that is just the interface - these probes don't nessecarily have to
be armed and cause global overhead once they are define.  If the
implenmentation is smart enough it will defer arming the probe until
we actually use it, and that will be per-process quite often.

Which btw, brings up two more issues, one in uprobes and one in perf.
For one even in userspace I think the dynamic probes will really just
be the tip of the iceberg and we'll get more bang for the buck from
static traces, which is something that's no supported in uprobes yet.
As a start supporting the dtrace-style sdt.h header would be a great
help, and then we can decide if we need somthing even better on top.

The other things is that perf currently only supports per-kernel pid
recording, while we'd really need per Posix process, which may contain
multiple threads for useful tracing of complex userspace applications.
I also suspect that this will fit the uprobes model much better given
that the probes will be in any given address space.


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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 20:40           ` Christoph Hellwig
@ 2010-09-06 21:06             ` Peter Zijlstra
  2010-09-06 21:12               ` Christoph Hellwig
  2010-09-07 12:02             ` Srikar Dronamraju
  1 sibling, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 21:06 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Srikar Dronamraju, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 16:40 -0400, Christoph Hellwig wrote:

> But that is just the interface - these probes don't nessecarily have to
> be armed and cause global overhead once they are define.  If the
> implenmentation is smart enough it will defer arming the probe until
> we actually use it, and that will be per-process quite often.

The implementation I outlined a few messages ago, would in fact, as you
suggest, avoid arming things when not needed.


> The other things is that perf currently only supports per-kernel pid
> recording, while we'd really need per Posix process, which may contain
> multiple threads for useful tracing of complex userspace applications.
> I also suspect that this will fit the uprobes model much better given
> that the probes will be in any given address space.

perf does report both:

         *      { u32                   pid, tid; } && PERF_SAMPLE_TID

the pid is the process id (thread group leader like) and tid is the
task/thread id.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 21:06             ` Peter Zijlstra
@ 2010-09-06 21:12               ` Christoph Hellwig
  2010-09-06 21:18                 ` Peter Zijlstra
  0 siblings, 1 reply; 71+ messages in thread
From: Christoph Hellwig @ 2010-09-06 21:12 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Christoph Hellwig, Srikar Dronamraju, Ingo Molnar,
	Steven Rostedt, Arnaldo Carvalho de Melo, Linus Torvalds,
	Masami Hiramatsu, Oleg Nesterov, Mark Wielaard,
	Mathieu Desnoyers, Andrew Morton, Naren A Devaiah, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney,
	Srivatsa Vaddagiri

On Mon, Sep 06, 2010 at 11:06:08PM +0200, Peter Zijlstra wrote:
> > The other things is that perf currently only supports per-kernel pid
> > recording, while we'd really need per Posix process, which may contain
> > multiple threads for useful tracing of complex userspace applications.
> > I also suspect that this will fit the uprobes model much better given
> > that the probes will be in any given address space.
> 
> perf does report both:
> 
>          *      { u32                   pid, tid; } && PERF_SAMPLE_TID
> 
> the pid is the process id (thread group leader like) and tid is the
> task/thread id.

It records both, but I haven't found a way to only record samples
or trace things in a Posix Process.

E.g. perf record -p seems to be only per-thread, not per-process.
If that has changes recently everything is fine of course.


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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 21:12               ` Christoph Hellwig
@ 2010-09-06 21:18                 ` Peter Zijlstra
  0 siblings, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-06 21:18 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Srikar Dronamraju, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Mon, 2010-09-06 at 17:12 -0400, Christoph Hellwig wrote:
> On Mon, Sep 06, 2010 at 11:06:08PM +0200, Peter Zijlstra wrote:
> > > The other things is that perf currently only supports per-kernel pid
> > > recording, while we'd really need per Posix process, which may contain
> > > multiple threads for useful tracing of complex userspace applications.
> > > I also suspect that this will fit the uprobes model much better given
> > > that the probes will be in any given address space.
> > 
> > perf does report both:
> > 
> >          *      { u32                   pid, tid; } && PERF_SAMPLE_TID
> > 
> > the pid is the process id (thread group leader like) and tid is the
> > task/thread id.
> 
> It records both, but I haven't found a way to only record samples
> or trace things in a Posix Process.
> 
> E.g. perf record -p seems to be only per-thread, not per-process.
> If that has changes recently everything is fine of course.

Hrm, the record code seems to look up all threads for -p and use only a
single thread for -t, didn't actually try it though so it could be
borken.



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

* Re: [PATCHv11 2.6.36-rc2-tip 4/15]  4: uprobes: x86 specific functions for user space breakpointing.
  2010-09-06 13:44         ` Srikar Dronamraju
  2010-09-06 14:16           ` Andi Kleen
@ 2010-09-07  0:56           ` Masami Hiramatsu
  1 sibling, 0 replies; 71+ messages in thread
From: Masami Hiramatsu @ 2010-09-07  0:56 UTC (permalink / raw)
  To: Srikar Dronamraju; +Cc: Andi Kleen, Peter Zijlstra, linux-kernel, Jim Keniston

(2010/09/06 22:44), Srikar Dronamraju wrote:
> [adding Masami and Jim to the copy list] 
> 
>>> I havent tried any fuzz tests with the instruction decoder. But I am
>>> not sure if Masami has tried that out some of these. 
>>> One question: Do you want to test uprobes with crashme or test
>>> instruction decoder with crashme.

As you can see in kernel tree, x86 insn decoder has a test
which decodes vmlinux and compares results with objdump.
Similar tests had been done for glibc etc. by Jim.
But crashme looks better. :)

>> Ideally both, but as a minimum the part that is exposed
>> to user space, that is uprobes.
> 
> Okay, I will test uprobes with crashme.
> 
>>
>>>
>>> validate_insn_32bit is able to identify all valid instructions in a 32
>>> bit app and validate_insn_64bits is a superset of
>>> validate_insn_32bits; i.e it considers valid 32 bit codes as valid
>>> too.
>>
>> How can this be? e.g. 32bit has 1 byte INC/DEC but on 64bit
>> these are REX prefixes and can be in front of nearly anything.
>> So a super set cannot be correct. It has to be either / or.
>>
> 
> You are right, the validate_insn_32bits refers to good_insns_32 and
> validate_insn_64bits refers to good_insns_64 to decode 1 byte
> instructions. Some instructions like 0x06 and 0x0e seem to be valid in
> good_insns_32 but not in good_insns_64. 

Hmm, if you need to validate all instructions, you'd better to
enhance x86 decoder for checking bad instructions.
I think it can be done mostly by adding inat bitflags.

Thank you,

>>> Did you get a chance to look at
>>> validate_insn_32bit/validate_insn_64bits? If you feel that
>>> validate_insn_32bit/validate_insn_64bits? are unable to detect
>>> valid codes, then I will certainly rework.
>>
>> I don't think you can do a 100% solution because for 100%
>> you would need to know the code segment the CPU is going
>> to use later, and that's not possible in advance.
>>
> 
> I think you are referring to RIP related instructions, this how we
> handle them. 
> Please correct us if we are wrong, but here is what we do 
> - While analyzing the instruction, take into account which register acts
>   as the code segment register.
> 
> - When interrupted (but before singlestep), copy the contents of the
>   register which we think acts as code segment register in our
>   above analysis into per-task scratch variable.
> 
> - After singlestepping we retrieve the saved per-task scratch
>   variable into the corresponding register.
> 
>> A heuristic is reasonable (and leave out applications
>> that generate 64bit code from 32bit executables or vice versa)
>> but you need to test the right personality bits for that.
>>
>>
>>>> Also the compat bit is not necessarily set if no system call is
>>>> executing. You would rather need to check the exec_domain.
>>>
>>> Okay, I shall check and revert on this.
>>
>> Hmm actually I double checked and this is a separate bit.
>> So scratch that, TIF_32BIT is ok to test.
> 
> Okay, Thanks for confirming this.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 18:15           ` Peter Zijlstra
@ 2010-09-07  6:48             ` Srikar Dronamraju
  2010-09-07  9:33               ` Peter Zijlstra
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-07  6:48 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

* Peter Zijlstra <peterz@infradead.org> [2010-09-06 20:15:22]:

> On Mon, 2010-09-06 at 23:16 +0530, Srikar Dronamraju wrote:
> > * Peter Zijlstra <peterz@infradead.org> [2010-09-03 19:19:09]:
> > 
> > > > 
> > > > However I would have an issue with making inode based probing the
> > > > default.
> > > > 1. Making all probing based on inode can be a performance hog.
> > > 
> > > How so? You can add filters if you want.
> > 
> > The breakpoint exception and singlestep account for a substaintial
> > time
> > of the uprobes probe handling. With increasing number of breakpoint
> > hits and singlesteps, wouldnt the overall load increase?
> > 
> You're really not getting it, are you? No, it would result in the exact
> same amount of actual breakpoints hit.

If there is just one instance of traced process for the inode then yes the
number of breakpoints when traced with pid or based on inode would be the
same. However if there are multiple instances of the traced process [example
bash/zsh] (or the inode corresponds to a library that gets mapped into
multiple processes example libc), and the user is interested in tracing
just one instance of the process, then dont wont the inode based tracing
amount to far more number of breakpoints hits?

> 
> > > 
> > > > 2. Since unlike kernel space, every process has a different space, so
> > > > why would we have to insert breakpoints in each of its process space if
> > > > we are not interested in them.
> > > 
> > > You don't have to, but you can. The problem I have with this stuff is
> > > that it makes the pid thing a primary interface, whereas it should be
> > > one of many filter possibilities.
> > 
> > I think the otherway, 
> > Why instrument a process and filter it out, if we are not interested in it.
> > While instrumenting kernel, we dont have this flexibility. So
> > having a pid based filter is the right thing to do for kernel
> > based tracing.
> > 
> > If we can get the per process based tracing right, we can build
> > higher lever stuff including the file based tracing easily.
> 
> Creating inode based probes on top of pid based probes is terribly ugly.

I would disagree. 
Lets consider a user wants to trace his single threaded app say bash for
few heavy used calls in libc say read/select systemcall stub. If this user
wants to keep recording at discreet intervals.  i.e record for 5 minutes,
stop for 5 minutes, record again for 5 minutes, ....  Can you list how you
would go about inode based probe insertion / probe deletions?

> 
> >  Just to clarify, I am not looking at probing per task
> > but probing per process. So the pid field in uprobe refers to the
> > tgid. 
> 
> Urgh,.. I really oppose the whole pid-centric thing, if that means
> process wide and not per task its even worse.
> 

Since breakpoints are shared across the tasks of the same process, we cant do
per-task based tracing. We can only do a per process tracing and filter
per-task which if the request is for per-task tracing and thats what I
think you were alluding to in the filter in one of your mails. I am okay with
filtering per-task within a given process.

--
Thanks and Regards
Srikar


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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-07  6:48             ` Srikar Dronamraju
@ 2010-09-07  9:33               ` Peter Zijlstra
  2010-09-07 11:51                 ` Srikar Dronamraju
  0 siblings, 1 reply; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-07  9:33 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Tue, 2010-09-07 at 12:18 +0530, Srikar Dronamraju wrote:
> > You're really not getting it, are you? No, it would result in the exact
> > same amount of actual breakpoints hit.
> 
> If there is just one instance of traced process for the inode then yes the
> number of breakpoints when traced with pid or based on inode would be the
> same. However if there are multiple instances of the traced process [example
> bash/zsh] (or the inode corresponds to a library that gets mapped into
> multiple processes example libc), and the user is interested in tracing
> just one instance of the process, then dont wont the inode based tracing
> amount to far more number of breakpoints hits? 

Not if your filter function works.

So let me try this again, (assumes boosted probes):

struct uprobe {
	struct inode	*inode;	/* we hold a ref */
	unsigned long	offset;

	int (*handler)(void); /* arguments.. ? */
	int (*filter)(struct task_struct *);

	int		insn_size;		/* size of */
	char		insn[MAX_INSN_SIZE];	/* the original insn */

	int		ret_addr_offset;	/* return addr offset
						   in the slot */
	char		replacement[SLOT_SIZE]; /* replacement
						   instructions */
	
	atomic_t	ref; /* lifetime muck */
	struct rcu_head	rcu;
};

static struct {
	raw_spinlock_t	tree_lock;
	rb_root		tree;
} uprobes;

static void uprobes_add(struct uprobe *uprobe)
{
	/* add to uprobes.tree, sorted on inode:offset */
}

static void uprobes_del(struct uprobe *uprobe)
{
	/* delete from uprobes.tree */
}

static struct uprobe *
uprobes_find_get(struct address_space *mapping, unsigned long offset)
{
	unsigned long flags;
	struct uprobe *uprobe;

	raw_spin_lock_irqsave(&uprobes.treelock, flags);
	uprobe = find_in_tree(&uprobes.tree);
	if (!atomic_inc_not_zero(&uprobe->ref))
		uprobe = NULL;
	raw_spin_unlock_irqrestore(&uprobes.treelock, flags);

	return uprobe;
}

static void __uprobe_free(struct rcu_head *head)
{
	struct uprobe *uprobe = container_of(head, struct uprobe, rcu);

	kfree(uprobe);
}

static void put_uprobe(struct uprobe *uprobe)
{
	if (atomic_dec_and_test(&uprobe->ref))
		call_rcu(&uprobe->rcu, __uprobe_free);
}

static inline int valid_vma(struct vm_area_struct *vma)
{
	if (!vma->vm_file)
		return 0;

	if (vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED) ==
			    (VM_READ|VM_EXEC))
		return 1;

	return 0;
}

int register_uprobe(struct uprobe *uprobe)
{
	struct vm_area_struct *vma;

	inode_get(uprobe->inode);
	atomic_set(1, &uprobe->ref);

	uprobes_add(uprobe); /* add before the rmap walk, so that 
				new mmap()s will find it too */

	for_each_rmap_vma(vma, uprobe->inode->i_mapping) {
		struct mm_struct *mm = vma->vm_mm;
		int install_probe = 0;

		if (!valid_vma(vma))
			continue;

		for_each_task_in_process(p, mm->owner) {
			if (uprobe->filter(p)) {
				p->has_uprobe = 1;
				install_probe = 1;
			}
		}

		if (install_probe) {
			mm->has_uprobes = 1;
			frob_text(uprobe, mm);
		}
	}
}

void unregister_uprobe(struct uprobe *uprobe)
{
	/* pretty much the same, except restore the original text */
	put_uprobe(uprobe);
}

void uprobe_fork(struct task_struct *child)
{
	struct vm_area_struct *vma;

	if (!child->mm->has_uprobes)	
		return;

	for_each_vma(vma, child->mm) {
		struct uprobe *uprobe;

		if (!valid_vma(vma))
			continue;

		for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {
			if (uprobe->filter(child)) {
				child->has_uprobe = 1;
				return;
			}
		}
	}
}

void uprobe_mmap(struct vm_area_struct *vma)
{
	struct uprobe *uprobe;

	if (!valid_vma(vma))
		return;

	for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {
		int install_probe = 0;

		for_each_task_in_process(p, vma->vm_mm->owner) {
			if (uprobe->filter(p)) {
				p->has_uprobe = 1;
				install_probe = 1;
			}
		}

		if (install_probe) {
			mm->has_uprobes = 1;
			frob_text(uprobe, mm);
		}
	}
}

void uprobe_hit(struct pt_regs *regs)
{
	unsigned long addr = instruction_pointer(regs);
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma;
	unsigned long offset;

	down_read(&mm->mmap_sem);
	vma = find_vma(mm, addr);

	if (!valid_vma)
		goto fail;

	offset = addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);

	uprobe = uprobes_find_get(vma->vm_file->f_mapping, offset);
	up_read(&mm->mmap_sem);

	if (!uprobe)
		goto fail;

	if (current->has_uprobe && uprobe->filter(current))
		uprobe->handle();

	ret_addr = addr + uprobe->insn_size;

	cpu = get_cpu()
	slot = get_slot(cpu);
	memcpy(slot, uprobe->replacement, SLOT_SIZE);
	memcpy(slot + uprobe->ret_addr_offset, &ret_addr, sizeof(unsigned
long));
	set_instruction_pointer(regs, uaddr_addr_of(slot));
	put_cpu(); /* preemption notifiers would take it from here */

	put_uprobe(uprobe);
	return;

fail:
	SIGTRAP
}

See, no extra traps, no funny intermediate data structures to manage,
and you get the power of ->filter() to implement whatever policy you
want, including simple process wide things.

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-07  9:33               ` Peter Zijlstra
@ 2010-09-07 11:51                 ` Srikar Dronamraju
  2010-09-07 12:25                   ` Peter Zijlstra
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-07 11:51 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

> > multiple processes example libc), and the user is interested in tracing
> > just one instance of the process, then dont wont the inode based tracing
> > amount to far more number of breakpoints hits? 
> 
> Not if your filter function works.
> 
> So let me try this again, (assumes boosted probes):
> 

> struct uprobe {
> 	struct inode	*inode;	/* we hold a ref */
> 	unsigned long	offset;
> 
> 	int (*handler)(void); /* arguments.. ? */
> 	int (*filter)(struct task_struct *);
> 
> 	int		insn_size;		/* size of */
> 	char		insn[MAX_INSN_SIZE];	/* the original insn */
> 
> 	int		ret_addr_offset;	/* return addr offset
> 						   in the slot */
> 	char		replacement[SLOT_SIZE]; /* replacement
> 						   instructions */
> 	
> 	atomic_t	ref; /* lifetime muck */
> 	struct rcu_head	rcu;
> };


struct uprobe is a input structure. Do we want to have
implementation details in it?

> 
> static struct {
> 	raw_spinlock_t	tree_lock;
> 	rb_root		tree;
> } uprobes;
> 
> static void uprobes_add(struct uprobe *uprobe)
> {
> 	/* add to uprobes.tree, sorted on inode:offset */
> }
> 
> static void uprobes_del(struct uprobe *uprobe)
> {
> 	/* delete from uprobes.tree */
> }
> 
> static struct uprobe *
> uprobes_find_get(struct address_space *mapping, unsigned long offset)
> {
> 	unsigned long flags;
> 	struct uprobe *uprobe;
> 
> 	raw_spin_lock_irqsave(&uprobes.treelock, flags);
> 	uprobe = find_in_tree(&uprobes.tree);

Wouldnt this be a scalability issue on bigger machines?
Every probehit having to parse a global tree to figureout which
uprobe it was seems a overkill.
Consider a 5000 uprobes placed on a 128 box with probes placed on
heavily used functions.

> 	if (!atomic_inc_not_zero(&uprobe->ref))
> 		uprobe = NULL;
> 	raw_spin_unlock_irqrestore(&uprobes.treelock, flags);
> 
> 	return uprobe;
> }
> 
> static void __uprobe_free(struct rcu_head *head)
> {
> 	struct uprobe *uprobe = container_of(head, struct uprobe, rcu);
> 
> 	kfree(uprobe);
> }
> 
> static void put_uprobe(struct uprobe *uprobe)
> {
> 	if (atomic_dec_and_test(&uprobe->ref))
> 		call_rcu(&uprobe->rcu, __uprobe_free);

How are we synchronizing put_uprobe and a thread that has hit the
breakpoint and searching thro global probes list? 

One Nit: On probe hit we increment the ref only few times. However 
we are decrementing everytime. So if two probes occur on two cpus
simultaneously, we have a chance of uprobe being freed after both of
them are handled. (or am I missing something?)

> }
> 
> static inline int valid_vma(struct vm_area_struct *vma)
> {
> 	if (!vma->vm_file)
> 		return 0;
> 
> 	if (vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED) ==
> 			    (VM_READ|VM_EXEC))
> 		return 1;
> 
> 	return 0;
> }
> 
> int register_uprobe(struct uprobe *uprobe)
> {
> 	struct vm_area_struct *vma;
> 
> 	inode_get(uprobe->inode);
> 	atomic_set(1, &uprobe->ref);
> 
> 	uprobes_add(uprobe); /* add before the rmap walk, so that 
> 				new mmap()s will find it too */
> 
> 	for_each_rmap_vma(vma, uprobe->inode->i_mapping) {
I understand that perf top calls perf record in a loop.
For every perf record, we would be looping thro each vma associated with
the inode. 
For a probe on a libc, we would iterate thro all vmas. If the
tracing was per posix process, this may not be needed.

> 		struct mm_struct *mm = vma->vm_mm;
> 		int install_probe = 0;
> 
> 		if (!valid_vma(vma))
> 			continue;
> 
> 		for_each_task_in_process(p, mm->owner) {
> 			if (uprobe->filter(p)) {
> 				p->has_uprobe = 1;
> 				install_probe = 1;
> 			}
> 		}
> 
> 		if (install_probe) {
> 			mm->has_uprobes = 1;
> 			frob_text(uprobe, mm);
> 		}
> 	}
> }
> 
> void unregister_uprobe(struct uprobe *uprobe)
> {
> 	/* pretty much the same, except restore the original text */
> 	put_uprobe(uprobe);
> }
> 
> void uprobe_fork(struct task_struct *child)
> {
> 	struct vm_area_struct *vma;
> 
> 	if (!child->mm->has_uprobes)	
> 		return;
> 
> 	for_each_vma(vma, child->mm) {
> 		struct uprobe *uprobe;
> 
> 		if (!valid_vma(vma))
> 			continue;
> 
> 		for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {

Are you looking at listing of uprobes per vma?
Does it again traverse the global list?

> 			if (uprobe->filter(child)) {
> 				child->has_uprobe = 1;
> 				return;
> 			}
> 		}
> 	}
> }
> 
> void uprobe_mmap(struct vm_area_struct *vma)
> {
> 	struct uprobe *uprobe;
> 
> 	if (!valid_vma(vma))
> 		return;
> 
> 	for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {

For each mmap, we are traversing all elements in the global tree?
What would happen if we have a huge number of uprobes in a system all
from one user on his app. Wont it slow down mmap for all other users?

> 		int install_probe = 0;
> 
> 		for_each_task_in_process(p, vma->vm_mm->owner) {
> 			if (uprobe->filter(p)) {
> 				p->has_uprobe = 1;
> 				install_probe = 1;
> 			}
> 		}
> 
> 		if (install_probe) {
> 			mm->has_uprobes = 1;
> 			frob_text(uprobe, mm);
> 		}
> 	}
> }
> 
> void uprobe_hit(struct pt_regs *regs)
> {
> 	unsigned long addr = instruction_pointer(regs);
> 	struct mm_struct *mm = current->mm;
> 	struct vm_area_struct *vma;
> 	unsigned long offset;
> 
> 	down_read(&mm->mmap_sem);

uprobe_hit I assume is going to be called in interrupt context.
I guess down_read can sleep here.

> 	vma = find_vma(mm, addr);
> 
> 	if (!valid_vma)
> 		goto fail;
> 
> 	offset = addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);

Again for every probehit, we are going through the list of vmas and
checking if it has a probe which I think is unnecessary.

Nit: In some archs, the instruction pointer might be pointing to th next
instruction after a breakpoint hit, we would have to adjust that.

> 	uprobe = uprobes_find_get(vma->vm_file->f_mapping, offset);
> 	up_read(&mm->mmap_sem);
> 
> 	if (!uprobe)
> 		goto fail;
> 
> 	if (current->has_uprobe && uprobe->filter(current))
> 		uprobe->handle();
> 
> 	ret_addr = addr + uprobe->insn_size;
> 
> 	cpu = get_cpu()
> 	slot = get_slot(cpu);
> 	memcpy(slot, uprobe->replacement, SLOT_SIZE);
> 	memcpy(slot + uprobe->ret_addr_offset, &ret_addr, sizeof(unsigned
> long));
> 	set_instruction_pointer(regs, uaddr_addr_of(slot));
> 	put_cpu(); /* preemption notifiers would take it from here */
> 

What if we were pre-empted after this. Would preemption notifiers also
do a copy of instruction to the new slot? If yes, can you please
update me with more pointers.

And I dont know if we can do a boosting for all instructions.
I think even on kprobes we dont do a boosting for all instructions.
Masami, Can you correct me on this?

> 	put_uprobe(uprobe);
> 	return;
> 
> fail:
> 	SIGTRAP
> }
> 
> See, no extra traps, no funny intermediate data structures to manage,
> and you get the power of ->filter() to implement whatever policy you
> want, including simple process wide things.

Yes, I see its advantages and disadvantages,  I feel this
implementation wouldnt scale. Just because we dont want to
housekeep some information, we are looping thro the global tree to
figure out if there is uprobe specific stuff to be done.

--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-06 20:40           ` Christoph Hellwig
  2010-09-06 21:06             ` Peter Zijlstra
@ 2010-09-07 12:02             ` Srikar Dronamraju
  2010-09-07 16:47               ` Mathieu Desnoyers
  1 sibling, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-09-07 12:02 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

> On Mon, Sep 06, 2010 at 11:16:42PM +0530, Srikar Dronamraju wrote:
> > > You don't have to, but you can. The problem I have with this stuff is
> > > that it makes the pid thing a primary interface, whereas it should be
> > > one of many filter possibilities.
> > 
> > I think the otherway, 
> > Why instrument a process and filter it out, if we are not interested in it.
> > While instrumenting kernel, we dont have this flexibility. So
> > having a pid based filter is the right thing to do for kernel
> > based tracing.
> > 
> > If we can get the per process based tracing right, we can build
> > higher lever stuff including the file based tracing easily.
> > 
> > All tools/debuggers in the past have all worked with process based
> > tracing.
> 
> I have the feeling that you guys are at least partially talking past
> each other.
> 
> For the "perf probe --add" interface the only sane interface is one by
> filename and then symbol / liner number / etc.

Agree, probing by file name is a requirement and I am working
towards that end.
> 
> But that is just the interface - these probes don't nessecarily have to
> be armed and cause global overhead once they are define.  If the
> implenmentation is smart enough it will defer arming the probe until
> we actually use it, and that will be per-process quite often.

Agree, That why I am trying to build file-based probing on
pid-based probing.

> 
> Which btw, brings up two more issues, one in uprobes and one in perf.
> For one even in userspace I think the dynamic probes will really just
> be the tip of the iceberg and we'll get more bang for the buck from
> static traces, which is something that's no supported in uprobes yet.
> As a start supporting the dtrace-style sdt.h header would be a great
> help, and then we can decide if we need somthing even better on top.

Yes, Static tracing using dtrace style sdt.h is a cool thing to do.
Already SystemTap has this facility. However I think its probably
better done at perf user interface level.

The way I look at it is perf probe decodes the static markers and asks
uprobes to place probepoints over there.
Do you see a different approach? If yes can you tell what you were
looking at?


--
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-07 11:51                 ` Srikar Dronamraju
@ 2010-09-07 12:25                   ` Peter Zijlstra
  0 siblings, 0 replies; 71+ messages in thread
From: Peter Zijlstra @ 2010-09-07 12:25 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Ingo Molnar, Steven Rostedt, Arnaldo Carvalho de Melo,
	Linus Torvalds, Christoph Hellwig, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, Andrew Morton,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, LKML,
	Paul E. McKenney, Srivatsa Vaddagiri

On Tue, 2010-09-07 at 17:21 +0530, Srikar Dronamraju wrote:
> > > multiple processes example libc), and the user is interested in tracing
> > > just one instance of the process, then dont wont the inode based tracing
> > > amount to far more number of breakpoints hits? 
> > 
> > Not if your filter function works.
> > 
> > So let me try this again, (assumes boosted probes):
> > 
> 
> > struct uprobe {
> > 	struct inode	*inode;	/* we hold a ref */
> > 	unsigned long	offset;
> > 
> > 	int (*handler)(void); /* arguments.. ? */
> > 	int (*filter)(struct task_struct *);
> > 
> > 	int		insn_size;		/* size of */
> > 	char		insn[MAX_INSN_SIZE];	/* the original insn */
> > 
> > 	int		ret_addr_offset;	/* return addr offset
> > 						   in the slot */
> > 	char		replacement[SLOT_SIZE]; /* replacement
> > 						   instructions */
> > 	
> > 	atomic_t	ref; /* lifetime muck */
> > 	struct rcu_head	rcu;
> > };
> 
> 
> struct uprobe is a input structure. Do we want to have
> implementation details in it?

I didn't consider the user-space interface at all, consuming the uprobe
name for that seems silly.

> > static struct uprobe *
> > uprobes_find_get(struct address_space *mapping, unsigned long offset)
> > {
> > 	unsigned long flags;
> > 	struct uprobe *uprobe;
> > 
> > 	raw_spin_lock_irqsave(&uprobes.treelock, flags);
> > 	uprobe = find_in_tree(&uprobes.tree);
> 
> Wouldnt this be a scalability issue on bigger machines?
> Every probehit having to parse a global tree to figureout which
> uprobe it was seems a overkill.
> Consider a 5000 uprobes placed on a 128 box with probes placed on
> heavily used functions.

Use a seqcount, its a read-mostly data structure, its just that the
RB-tree isn't RCU friendly.

> > 	if (!atomic_inc_not_zero(&uprobe->ref))
> > 		uprobe = NULL;
> > 	raw_spin_unlock_irqrestore(&uprobes.treelock, flags);
> > 
> > 	return uprobe;
> > }

> > static void put_uprobe(struct uprobe *uprobe)
> > {
> > 	if (atomic_dec_and_test(&uprobe->ref))
> > 		call_rcu(&uprobe->rcu, __uprobe_free);
> 
> How are we synchronizing put_uprobe and a thread that has hit the
> breakpoint and searching thro global probes list? 

RCU, see the above atomic_inc_not_zero() it will not obtain a reference
after the final put, the object will stay valid until we pass an rcu
quesent state.

> One Nit: On probe hit we increment the ref only few times. However 
> we are decrementing everytime. So if two probes occur on two cpus
> simultaneously, we have a chance of uprobe being freed after both of
> them are handled. (or am I missing something?)

-ENOPARSE.

> > int register_uprobe(struct uprobe *uprobe)
> > {
> > 	struct vm_area_struct *vma;
> > 
> > 	inode_get(uprobe->inode);
> > 	atomic_set(1, &uprobe->ref);
> > 
> > 	uprobes_add(uprobe); /* add before the rmap walk, so that 
> > 				new mmap()s will find it too */
> > 
> > 	for_each_rmap_vma(vma, uprobe->inode->i_mapping) {
> I understand that perf top calls perf record in a loop.

It doesn't, nor do I see the relevance.

> For every perf record, we would be looping thro each vma associated with
> the inode. 
> For a probe on a libc, we would iterate thro all vmas. If the
> tracing was per posix process, this may not be needed.

Feh, probe register should be considered an utter slow path.

We do rmap walks on pages all the time, I can't see it being a problem
here.

> > 		struct mm_struct *mm = vma->vm_mm;
> > 		int install_probe = 0;
> > 
> > 		if (!valid_vma(vma))
> > 			continue;
> > 
> > 		for_each_task_in_process(p, mm->owner) {
> > 			if (uprobe->filter(p)) {
> > 				p->has_uprobe = 1;
> > 				install_probe = 1;
> > 			}
> > 		}
> > 
> > 		if (install_probe) {
> > 			mm->has_uprobes = 1;
> > 			frob_text(uprobe, mm);
> > 		}
> > 	}
> > }


> > void uprobe_fork(struct task_struct *child)
> > {
> > 	struct vm_area_struct *vma;
> > 
> > 	if (!child->mm->has_uprobes)	
> > 		return;
> > 
> > 	for_each_vma(vma, child->mm) {
> > 		struct uprobe *uprobe;
> > 
> > 		if (!valid_vma(vma))
> > 			continue;
> > 
> > 		for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {
> 
> Are you looking at listing of uprobes per vma?
> Does it again traverse the global list?

Yeah, it does a range lookup in the tree [inode:0 - inode:-1). O(log(n))
to find the first entry, O(log(n)) for each consecutive entry, unless we
thread the tree.

Note that read-mostly global data is no problem.

> > 			if (uprobe->filter(child)) {
> > 				child->has_uprobe = 1;
> > 				return;
> > 			}
> > 		}
> > 	}
> > }
> > 
> > void uprobe_mmap(struct vm_area_struct *vma)
> > {
> > 	struct uprobe *uprobe;
> > 
> > 	if (!valid_vma(vma))
> > 		return;
> > 
> > 	for_each_probe_in_mapping(uprobe, vma->vm_file->f_mapping) {
> 
> For each mmap, we are traversing all elements in the global tree?
> What would happen if we have a huge number of uprobes in a system all
> from one user on his app. Wont it slow down mmap for all other users?

Only mmap() of that particular inode, the range lookup would be the
regular O(log(n)) for an empty range.

But again, mmap() is a relative slow path, and you need something like
this anyway if you want to support inode based probes at all.

> > 		int install_probe = 0;
> > 
> > 		for_each_task_in_process(p, vma->vm_mm->owner) {
> > 			if (uprobe->filter(p)) {
> > 				p->has_uprobe = 1;
> > 				install_probe = 1;
> > 			}
> > 		}
> > 
> > 		if (install_probe) {
> > 			mm->has_uprobes = 1;
> > 			frob_text(uprobe, mm);
> > 		}
> > 	}
> > }
> > 
> > void uprobe_hit(struct pt_regs *regs)
> > {
> > 	unsigned long addr = instruction_pointer(regs);
> > 	struct mm_struct *mm = current->mm;
> > 	struct vm_area_struct *vma;
> > 	unsigned long offset;
> > 
> > 	down_read(&mm->mmap_sem);
> 
> uprobe_hit I assume is going to be called in interrupt context.
> I guess down_read can sleep here.

I assumed process context here, but its trivial to make it work from
interrupt context if you want, all we need is a spinlock/seqlock around
the vm_rb mods in __vma_link_rb() and __vma_unlink().

> > 	vma = find_vma(mm, addr);
> > 
> > 	if (!valid_vma)
> > 		goto fail;
> > 
> > 	offset = addr - vma->vm_start + (vma->vm_pgoff << PAGE_SHIFT);
> 
> Again for every probehit, we are going through the list of vmas and
> checking if it has a probe which I think is unnecessary.

O(log(n)) lookup, hardly a problem.

> Nit: In some archs, the instruction pointer might be pointing to th next
> instruction after a breakpoint hit, we would have to adjust that.

Sure..

> > 	uprobe = uprobes_find_get(vma->vm_file->f_mapping, offset);
> > 	up_read(&mm->mmap_sem);
> > 
> > 	if (!uprobe)
> > 		goto fail;
> > 
> > 	if (current->has_uprobe && uprobe->filter(current))
> > 		uprobe->handle();
> > 
> > 	ret_addr = addr + uprobe->insn_size;
> > 
> > 	cpu = get_cpu()
> > 	slot = get_slot(cpu);
> > 	memcpy(slot, uprobe->replacement, SLOT_SIZE);
> > 	memcpy(slot + uprobe->ret_addr_offset, &ret_addr, sizeof(unsigned
> > long));
> > 	set_instruction_pointer(regs, uaddr_addr_of(slot));
> > 	put_cpu(); /* preemption notifiers would take it from here */
> > 
> 
> What if we were pre-empted after this. Would preemption notifiers also
> do a copy of instruction to the new slot? If yes, can you please
> update me with more pointers.

You'd need to keep a copy of the slot in task state I think.

> And I dont know if we can do a boosting for all instructions.
> I think even on kprobes we dont do a boosting for all instructions.
> Masami, Can you correct me on this?

You can do it with single-step if you want, just keep more task state.

> > 	put_uprobe(uprobe);
> > 	return;
> > 
> > fail:
> > 	SIGTRAP
> > }
> > 
> > See, no extra traps, no funny intermediate data structures to manage,
> > and you get the power of ->filter() to implement whatever policy you
> > want, including simple process wide things.
> 
> Yes, I see its advantages and disadvantages,  I feel this
> implementation wouldnt scale. Just because we dont want to
> housekeep some information, we are looping thro the global tree to
> figure out if there is uprobe specific stuff to be done.

Its mostly read-only data (adding/removing probes is rare), its all
O(log(n)), I really don't see a problem with that.

If you really worry about it you could try a hash lookup for the inode
part and keep a tree per probed inode.


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

* Re: [PATCHv11 2.6.36-rc2-tip 5/15]  5: uprobes: Uprobes (un)registration and exception handling.
  2010-09-07 12:02             ` Srikar Dronamraju
@ 2010-09-07 16:47               ` Mathieu Desnoyers
  0 siblings, 0 replies; 71+ messages in thread
From: Mathieu Desnoyers @ 2010-09-07 16:47 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Christoph Hellwig, Peter Zijlstra, Ingo Molnar, Steven Rostedt,
	Arnaldo Carvalho de Melo, Linus Torvalds, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Andrew Morton, Naren A Devaiah,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Ananth N Mavinakayanahalli, LKML, Paul E. McKenney,
	Srivatsa Vaddagiri

* Srikar Dronamraju (srikar@linux.vnet.ibm.com) wrote:
> > On Mon, Sep 06, 2010 at 11:16:42PM +0530, Srikar Dronamraju wrote:
 [...]
> > 
> > Which btw, brings up two more issues, one in uprobes and one in perf.
> > For one even in userspace I think the dynamic probes will really just
> > be the tip of the iceberg and we'll get more bang for the buck from
> > static traces, which is something that's no supported in uprobes yet.
> > As a start supporting the dtrace-style sdt.h header would be a great
> > help, and then we can decide if we need somthing even better on top.
> 
> Yes, Static tracing using dtrace style sdt.h is a cool thing to do.
> Already SystemTap has this facility. However I think its probably
> better done at perf user interface level.

We currently have this feature in UST. We're adding "markers" into the
applications, and a UST daemon talks with an in-process library helper thread to
enable/disable markers and control tracing over unix sockets.

We're currently in the process of moving from markers to the
TRACE_EVENT()+tracepoints infrastructure.

Thanks,

Mathieu

> 
> The way I look at it is perf probe decodes the static markers and asks
> uprobes to place probepoints over there.
> Do you see a different approach? If yes can you tell what you were
> looking at?

-- 
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com

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

* Re: [PATCHv11a 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file.
  2010-08-27 14:21   ` [PATCHv11a " Srikar Dronamraju
@ 2010-10-20  9:56     ` Masami Hiramatsu
  0 siblings, 0 replies; 71+ messages in thread
From: Masami Hiramatsu @ 2010-10-20  9:56 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Christoph Hellwig,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Naren A Devaiah, Jim Keniston, Frederic Weisbecker,
	Frank Ch. Eigler, Ananth N Mavinakayanahalli, Andrew Morton,
	Paul E. McKenney, 2nddept-manager

Hi Srikar,

(2010/08/27 23:21), Srikar Dronamraju wrote:
> Introduces -S/--show_functions option for perf-probe.
> This lists function names in a File. If no file is specified, then lists
> functions in the current running kernel.

Hmm, I think the basic functionality of this patch (I mean
functions in running kernel) could be merged separately
from this series.


> +	OPT_BOOLEAN('S', "show_functions", &params.list_functions,
> +				"Show potential probes."),
> +	OPT_CALLBACK('D', "limit_dsos", NULL, "DSO",
> +				"Limit Dsos.", opt_limit_dsos),

However, I'd rather use --funcs/-F and --dso/-D instead of
above. :)

Thank you,

-- 
Masami HIRAMATSU
2nd Dept. Linux Technology Center
Hitachi, Ltd., Systems Development Laboratory
E-mail: masami.hiramatsu.pt@hitachi.com

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

* Re: [PATCHv11 2.6.36-rc2-tip 0/15]  0: Uprobes Patches
  2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
                   ` (14 preceding siblings ...)
  2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 15/15] 15: perf: Show Potential probe points Srikar Dronamraju
@ 2010-10-29  9:23 ` Christoph Hellwig
  2010-10-29 10:48   ` Srikar Dronamraju
  15 siblings, 1 reply; 71+ messages in thread
From: Christoph Hellwig @ 2010-10-29  9:23 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Naren A Devaiah,
	Christoph Hellwig, Ananth N Mavinakayanahalli, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Andrew Morton, Paul E. McKenney

It's been a while since the last posting.  Did you make any progress on
uprobes, especially allowing to define probes based on a file name?


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

* Re: [PATCHv11 2.6.36-rc2-tip 0/15]  0: Uprobes Patches
  2010-10-29  9:23 ` [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Christoph Hellwig
@ 2010-10-29 10:48   ` Srikar Dronamraju
  2010-11-04 18:45     ` Christoph Hellwig
  0 siblings, 1 reply; 71+ messages in thread
From: Srikar Dronamraju @ 2010-10-29 10:48 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Peter Zijlstra, Ingo Molnar, Steven Rostedt, Randy Dunlap,
	Arnaldo Carvalho de Melo, Linus Torvalds, Naren A Devaiah,
	Ananth N Mavinakayanahalli, Masami Hiramatsu, Oleg Nesterov,
	Mark Wielaard, Mathieu Desnoyers, LKML, Jim Keniston,
	Frederic Weisbecker, Frank Ch. Eigler, Andrew Morton,
	Paul E. McKenney


> It's been a while since the last posting.  Did you make any progress on
> uprobes, especially allowing to define probes based on a file name?
> 

Thanks for checking, I discussed with Peter offline and ironed out most
of the issues. I am thankful for Peter for all the suggestions

I am still getting the inode based uprobes to shape. 
Here is the brief summary of the discussion.

Significant differences from the previous patchset are:

- All probes would be maintained in a global rbtree sorted by inode and
  offset.
- There can be one or more consumers per probe. With each consumer there
  will be one handler and one (optional) filter.
- Filter restricts the processes/tasks that the handler is active.
- uprobe structure is dynamically created when the first consumer
  registers to the probe. It gets deallocated when all consumers
  unregisters from the probe.
- While registering a probe, we walk thro the list of vmas that are
  mapped to the inode, check if the consumer wants to probe the task
  corresponding to the vma and inserts the breakpoint.
- unregistering a probe also does something similar except for deleting
  the probe.
- There will be a hook in mmap/unmap to install probes as and when the
  vma gets loaded into process address space. This hook would walk thro
  the tree of probes for that inode and for each probe, walk thro the
  list of consumers and insert/delete breakpoints accordingly.
- There will be a hook in fork to install probes in newly created
  processes. This hook would walk thro the tree of probes for that inode
  and for each probe, walk thro the list of consumers and insert/delete
  breakpoints accordingly.
- Slots will still hang-out of mm_struct.
- Instead of the per-probe slot, we would have to use a per-thread slot.
  (This slot is for single stepping out of line). On every probehit, the
  slot has to be refreshed with the correct contents. 
- Since probe information is stored as inode:offset, probe
  identification on a breakpoint hit can only happen in task context.

Current issues: Given a vma; finding all tasks that have this vma
mapped. The current solution seems to walk thro all tasks in the system
and check if each task's mm field. But this could be inefficient
especially if the vma is part of all processes like libc etc.
I dont think we can use mm->owner to walk thro all the tasks that map to
this mm_struct.

Slot allocation delays: Using a per-thread slot instead of a per-probe
slot leads to a 3.5X degradation in performance. If we need to use
per-probe slots, we would have to have maintain metadata, i.e the slot
to virtual address mapping per process. However Peter prefers that we
reduce such metadata.

If a sigtrap occurs not because of a uprobe, we queue the signal only in
the task context. This is because we cannot identify the cause for trap
in interrupt context. If we use per-probe slots, we could avoid this.

Uprobes may not be able to trace a long-time running process if its
mapped a file that got updated after the process started. This assumes
the probes were placed after the process was started.

To cover the race where a breakpoint is hit while the probe is being
deleted, Paul has agreed to enhance srcu to work in interrupt context.

>From a perf/ftrace perspective:

Assuming 3551 as pid 
0x0000000000446420 as the vaddr of the probe for zfree
0x0000000000046420 as the offset of zfree from start of text address.
130904 as the inode number for the /bin/zsh

ftrace interface:
Previously:

# echo 'p 3551:0x0000000000446420 %ip %ax' > uprobe_events
# cat uprobe_events
p:uprobes/p_3551_0x0000000000446420 3551:0x0000000000446420 %ip=%ip %ax=%ax

Now:

# echo 'p /path-to-/file:0x0000000000046420 %ip %ax' > uprobe_events
# cat uprobe_events
p:uprobes/p_file_0x0000000000446420 130904:0x0000000000046420 %ip=%ip %ax=%ax


perf interface:
Previously:

# perf probe -p 3551 zfree@zsh
Added new event:
  probe_3551:zfree                       (on 0x446420)
# perf probe --list
probe_3551:zfree                       (on 3551:0x0000000000446420)

Now: 

# perf probe zfree@zsh
Added new event:
  probe_zsh:zfree                      (on 130904:0x46420)
# perf probe --list
probe_zsh:zfree                       (on 130904:0x0000000000046420)

JFYI: v11 patchset code that I last posted to LKML is available at:
git://git.kernel.org/pub/scm/linux/kernel/git/srikar/linux-uprobes.git
 branch : uprobes_v11_patchset 

Peter knows the details/trade-offs of both the designs.(v11 and the
inode based probes)

-- 
Thanks and Regards
Srikar

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

* Re: [PATCHv11 2.6.36-rc2-tip 0/15]  0: Uprobes Patches
  2010-10-29 10:48   ` Srikar Dronamraju
@ 2010-11-04 18:45     ` Christoph Hellwig
  0 siblings, 0 replies; 71+ messages in thread
From: Christoph Hellwig @ 2010-11-04 18:45 UTC (permalink / raw)
  To: Srikar Dronamraju
  Cc: Christoph Hellwig, Peter Zijlstra, Ingo Molnar, Steven Rostedt,
	Randy Dunlap, Arnaldo Carvalho de Melo, Linus Torvalds,
	Naren A Devaiah, Ananth N Mavinakayanahalli, Masami Hiramatsu,
	Oleg Nesterov, Mark Wielaard, Mathieu Desnoyers, LKML,
	Jim Keniston, Frederic Weisbecker, Frank Ch. Eigler,
	Andrew Morton, Paul E. McKenney

On Fri, Oct 29, 2010 at 04:18:58PM +0530, Srikar Dronamraju wrote:
> Current issues: Given a vma; finding all tasks that have this vma
> mapped. The current solution seems to walk thro all tasks in the system
> and check if each task's mm field. But this could be inefficient
> especially if the vma is part of all processes like libc etc.

It's pretty much guaranteed to be inefficient, yes.

> I dont think we can use mm->owner to walk thro all the tasks that map to
> this mm_struct.

I don't see a way around that if we have to find the task by the vma.

You'll have to start with vma->vm_mm->owner and then walk the list
of thread siblings inside the heavy weight process.

> Slot allocation delays: Using a per-thread slot instead of a per-probe
> slot leads to a 3.5X degradation in performance. If we need to use
> per-probe slots, we would have to have maintain metadata, i.e the slot
> to virtual address mapping per process. However Peter prefers that we
> reduce such metadata.

The performance numbers are pretty drastic.  But I'll let Peter comment
on the desire in more detail.  I'm really not in enough touch with this
code anymore to give too useful comments.

> >From a perf/ftrace perspective:

I really prefer the new interface.  But as said before I'm just a user
here and I don't care how it's implemented underneath.  I'll defer to
Peter and others knowing the code in more detail to make the trade offs
between the different low level implementations.


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

end of thread, other threads:[~2010-11-04 18:46 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-25 13:41 [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Srikar Dronamraju
2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 1/15] 1: mm: Move replace_page() / write_protect_page() to mm/memory.c Srikar Dronamraju
2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 2/15] 2: uprobes: Breakpoint insertion/removal in user space applications Srikar Dronamraju
2010-09-01 19:38   ` Peter Zijlstra
2010-08-25 13:41 ` [PATCHv11 2.6.36-rc2-tip 3/15] 3: uprobes: Slot allocation for Execution out of line(XOL) Srikar Dronamraju
2010-09-01 20:13   ` Peter Zijlstra
2010-09-03 16:40     ` Srikar Dronamraju
2010-09-03 16:51       ` Peter Zijlstra
2010-09-03 17:26         ` Srikar Dronamraju
2010-09-03 17:41           ` Peter Zijlstra
2010-09-06  5:38             ` Srikar Dronamraju
2010-09-03 17:25       ` Peter Zijlstra
2010-09-02  8:23   ` Peter Zijlstra
2010-09-02 17:47     ` Srikar Dronamraju
2010-09-03  7:26       ` Peter Zijlstra
2010-09-06 17:59         ` Srikar Dronamraju
2010-09-06 18:20           ` Peter Zijlstra
2010-09-06 18:28           ` Peter Zijlstra
2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 4/15] 4: uprobes: x86 specific functions for user space breakpointing Srikar Dronamraju
2010-09-03 10:26   ` Andi Kleen
2010-09-03 17:48     ` Srikar Dronamraju
2010-09-03 18:00       ` Peter Zijlstra
2010-09-06  7:53       ` Andi Kleen
2010-09-06 13:44         ` Srikar Dronamraju
2010-09-06 14:16           ` Andi Kleen
2010-09-07  0:56           ` Masami Hiramatsu
2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 5/15] 5: uprobes: Uprobes (un)registration and exception handling Srikar Dronamraju
2010-09-01 21:43   ` Peter Zijlstra
2010-09-02  8:12     ` Peter Zijlstra
2010-09-03 16:42     ` Srikar Dronamraju
2010-09-03 17:19       ` Peter Zijlstra
2010-09-06 17:46         ` Srikar Dronamraju
2010-09-06 18:15           ` Peter Zijlstra
2010-09-06 18:15           ` Peter Zijlstra
2010-09-07  6:48             ` Srikar Dronamraju
2010-09-07  9:33               ` Peter Zijlstra
2010-09-07 11:51                 ` Srikar Dronamraju
2010-09-07 12:25                   ` Peter Zijlstra
2010-09-06 18:25           ` Mathieu Desnoyers
2010-09-06 20:40           ` Christoph Hellwig
2010-09-06 21:06             ` Peter Zijlstra
2010-09-06 21:12               ` Christoph Hellwig
2010-09-06 21:18                 ` Peter Zijlstra
2010-09-07 12:02             ` Srikar Dronamraju
2010-09-07 16:47               ` Mathieu Desnoyers
2010-09-03 17:27       ` Peter Zijlstra
2010-09-01 21:46   ` Peter Zijlstra
2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 6/15] 6: uprobes: X86 support for Uprobes Srikar Dronamraju
2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 7/15] 7: uprobes: Uprobes Documentation Srikar Dronamraju
2010-08-25 13:42 ` [PATCHv11 2.6.36-rc2-tip 8/15] 8: tracing: Extract out common code for kprobes/uprobes traceevents Srikar Dronamraju
2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 9/15] 9: tracing: uprobes trace_event interface Srikar Dronamraju
2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 10/15] 10: tracing: config option to enable both kprobe-tracer and uprobe-tracer Srikar Dronamraju
2010-08-26  6:02   ` Masami Hiramatsu
2010-08-27  9:31     ` Srikar Dronamraju
2010-08-27 11:04       ` Masami Hiramatsu
2010-08-27 12:17         ` Srikar Dronamraju
2010-08-27 15:37           ` Masami Hiramatsu
2010-08-27 14:10     ` [PATCHv11a " Srikar Dronamraju
2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 11/15] 11: perf: list symbols in a dso in ascending order Srikar Dronamraju
2010-08-25 23:21   ` Arnaldo Carvalho de Melo
2010-08-26  4:32     ` Srikar Dronamraju
2010-08-30  8:35   ` [tip:perf/core] perf symbols: List symbols in a dso in ascending name order tip-bot for Srikar Dronamraju
2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 12/15] 12: perf: show possible probes in a given file Srikar Dronamraju
2010-08-27 14:21   ` [PATCHv11a " Srikar Dronamraju
2010-10-20  9:56     ` Masami Hiramatsu
2010-08-25 13:43 ` [PATCHv11 2.6.36-rc2-tip 13/15] 13: perf: Loop thro each of the maps in a map_group Srikar Dronamraju
2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 14/15] 14: perf: perf interface for uprobes Srikar Dronamraju
2010-08-25 13:44 ` [PATCHv11 2.6.36-rc2-tip 15/15] 15: perf: Show Potential probe points Srikar Dronamraju
2010-10-29  9:23 ` [PATCHv11 2.6.36-rc2-tip 0/15] 0: Uprobes Patches Christoph Hellwig
2010-10-29 10:48   ` Srikar Dronamraju
2010-11-04 18:45     ` Christoph Hellwig

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.