public inbox for systemtap@sourceware.org
 help / color / mirror / Atom feed
* [PATCH -tip 0/8] perf-probe updates
@ 2010-01-05 22:40 Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API Masami Hiramatsu
                   ` (7 more replies)
  0 siblings, 8 replies; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:40 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE

Hi Ingo,

Here are several bugfixes and updates of perf-probe and
kprobe-tracer.
This updates includes --line option support which you are
waiting for :-).

For kprobe-tracer, I decided to drop $argN support, because
the ABI for each function strongly depends on not only the
architecture but also the API of the function and gcc options.
Anyway, we already have perf-probe which allows us to find
register/memory assignment of each arguments.


Here are updated todo list.

Long-term TODOs (future features):
  - Support lazy string matching(glob?) for selecting probing
    line
  - Support sys_perf_counter_open (for non-root users)
  - Support tracing static variables (non global)
  - Support variable types from debuginfo (e.g. char, int, ...)
  - Support fields of data structures (var->field)
  - Support array (var[N])
  - Support dynamic array-indexing (var[var2])
  - Support string/dynamic arrays (*var, var[N..M])
  - Support force type-casting ((type)var)
  - Support the type of return value

Miscs:
  - Better support for probes on modules
  - Move onto libdw/libdwfl
  - Storing file name/line number information in the
    kernel for listing events
  

Thank you,

---

Masami Hiramatsu (8):
      perf probe: Support --line option to show probable source-code lines
      perf tools: Enhance glob string matching
      perf tools: Support tracepoint glob matching
      perf probe: Show probe list in pager
      [CLEANUP] perf probe: Remove newline from die()
      x86/ptrace: Remove unused regs_get_argument_nth API
      tracing/kprobe: Drop function argument access syntax
      tracing/kprobe: Update example output in documentation


 Documentation/trace/kprobetrace.txt     |   48 ++++---
 arch/x86/include/asm/ptrace.h           |    4 -
 arch/x86/kernel/ptrace.c                |   24 ----
 kernel/trace/trace_kprobe.c             |   18 ---
 tools/perf/Documentation/perf-probe.txt |   20 +++
 tools/perf/builtin-probe.c              |   80 ++++++++++--
 tools/perf/util/parse-events.c          |   11 +-
 tools/perf/util/probe-event.c           |  103 ++++++++++++++++
 tools/perf/util/probe-event.h           |    2 
 tools/perf/util/probe-finder.c          |  203 +++++++++++++++++++++++++++++--
 tools/perf/util/probe-finder.h          |   31 +++++
 tools/perf/util/string.c                |   65 +++++++++-
 12 files changed, 507 insertions(+), 102 deletions(-)

-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division
e-mail: mhiramat@redhat.com

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

* [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
@ 2010-01-05 22:40 ` Masami Hiramatsu
  2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 2/8] tracing/kprobe: Drop function argument access syntax Masami Hiramatsu
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:40 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Roland McGrath, Oleg Nesterov, Mahesh Salgaonkar,
	Benjamin Herrenschmidt, Michael Neuling, Steven Rostedt,
	linuxppc-dev

Because of dropping function argument syntax from kprobe-tracer,
we don't need this API anymore.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Neuling <mikey@neuling.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: linuxppc-dev@ozlabs.org
---

 arch/x86/include/asm/ptrace.h |    4 ----
 arch/x86/kernel/ptrace.c      |   24 ------------------------
 2 files changed, 0 insertions(+), 28 deletions(-)

diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 9d369f6..2010280 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -274,10 +274,6 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
 		return 0;
 }
 
-/* Get Nth argument at function call */
-extern unsigned long regs_get_argument_nth(struct pt_regs *regs,
-					   unsigned int n);
-
 /*
  * These are defined as per linux/ptrace.h, which see.
  */
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 017d937..73554a3 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -140,30 +140,6 @@ static const int arg_offs_table[] = {
 #endif
 };
 
-/**
- * regs_get_argument_nth() - get Nth argument at function call
- * @regs:	pt_regs which contains registers at function entry.
- * @n:		argument number.
- *
- * regs_get_argument_nth() returns @n th argument of a function call.
- * Since usually the kernel stack will be changed right after function entry,
- * you must use this at function entry. If the @n th entry is NOT in the
- * kernel stack or pt_regs, this returns 0.
- */
-unsigned long regs_get_argument_nth(struct pt_regs *regs, unsigned int n)
-{
-	if (n < ARRAY_SIZE(arg_offs_table))
-		return *(unsigned long *)((char *)regs + arg_offs_table[n]);
-	else {
-		/*
-		 * The typical case: arg n is on the stack.
-		 * (Note: stack[0] = return address, so skip it)
-		 */
-		n -= ARRAY_SIZE(arg_offs_table);
-		return regs_get_kernel_stack_nth(regs, 1 + n);
-	}
-}
-
 /*
  * does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 5/8] perf probe: Show probe list in pager
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
                   ` (2 preceding siblings ...)
  2010-01-05 22:40 ` [PATCH -tip 1/8] tracing/kprobe: Update example output in documentation Masami Hiramatsu
@ 2010-01-05 22:40 ` Masami Hiramatsu
  2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 22:41 ` [PATCH -tip 8/8] perf probe: Support --line option to show probable source-code lines Masami Hiramatsu
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:40 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Show probe list in pager, because the list can be longer than
a page.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/util/probe-event.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 8e532d9..a22141a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -37,6 +37,7 @@
 #include "string.h"
 #include "strlist.h"
 #include "debug.h"
+#include "cache.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
@@ -455,6 +456,8 @@ void show_perf_probe_events(void)
 	struct strlist *rawlist;
 	struct str_node *ent;
 
+	setup_pager();
+
 	fd = open_kprobe_events(O_RDONLY, 0);
 	rawlist = get_trace_kprobe_event_rawlist(fd);
 	close(fd);


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 1/8] tracing/kprobe: Update example output in  documentation
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 2/8] tracing/kprobe: Drop function argument access syntax Masami Hiramatsu
@ 2010-01-05 22:40 ` Masami Hiramatsu
  2010-01-06 18:40   ` Steven Rostedt
  2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 5/8] perf probe: Show probe list in pager Masami Hiramatsu
                   ` (4 subsequent siblings)
  7 siblings, 2 replies; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:40 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Steven Rostedt,
	Frederic Weisbecker

Update example output in documentation accroding to current
implementation.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
---

 Documentation/trace/kprobetrace.txt |   33 +++++++++++++++++----------------
 1 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index 47aabee..c3eff6f 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -97,23 +97,24 @@ recording return value as "myretprobe" event.
 
   cat /sys/kernel/debug/tracing/events/kprobes/myprobe/format
 name: myprobe
-ID: 75
+ID: 780
 format:
-	field:unsigned short common_type;	offset:0;	size:2;
-	field:unsigned char common_flags;	offset:2;	size:1;
-	field:unsigned char common_preempt_count;	offset:3;	size:1;
-	field:int common_pid;	offset:4;	size:4;
-	field:int common_tgid;	offset:8;	size:4;
-
-	field: unsigned long ip;	offset:16;tsize:8;
-	field: int nargs;	offset:24;tsize:4;
-	field: unsigned long dfd;	offset:32;tsize:8;
-	field: unsigned long filename;	offset:40;tsize:8;
-	field: unsigned long flags;	offset:48;tsize:8;
-	field: unsigned long mode;	offset:56;tsize:8;
-
-print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->ip, REC->dfd, REC->filename, REC->flags, REC->mode
-
+        field:unsigned short common_type;       offset:0;       size:2; signed:0;
+        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
+        field:unsigned char common_preempt_count;       offset:3; size:1;signed:0;
+        field:int common_pid;   offset:4;       size:4; signed:1;
+        field:int common_lock_depth;    offset:8;       size:4; signed:1;
+
+        field:unsigned long __probe_ip; offset:12;      size:4; signed:0;
+        field:int __probe_nargs;        offset:16;      size:4; signed:1;
+        field:unsigned long dfd;        offset:20;      size:4; signed:0;
+        field:unsigned long filename;   offset:24;      size:4; signed:0;
+        field:unsigned long flags;      offset:28;      size:4; signed:0;
+        field:unsigned long mode;       offset:32;      size:4; signed:0;
+
+
+print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->__probe_ip,
+REC->dfd, REC->filename, REC->flags, REC->mode
 
  You can see that the event has 4 arguments as in the expressions you specified.
 


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 2/8] tracing/kprobe: Drop function argument access syntax
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API Masami Hiramatsu
@ 2010-01-05 22:40 ` Masami Hiramatsu
  2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 22:40 ` [PATCH -tip 1/8] tracing/kprobe: Update example output in documentation Masami Hiramatsu
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:40 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Steven Rostedt, Roland McGrath, Oleg Nesterov, Mahesh Salgaonkar,
	Benjamin Herrenschmidt, Michael Neuling, linuxppc-dev

Drop function argument access syntax, because the function arguments
are depends on not only architecture but also compile-options and
function API. And now, we have perf-probe for finding register/memory
assigned to each argument.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Neuling <mikey@neuling.org>
Cc: linuxppc-dev@ozlabs.org
---

 Documentation/trace/kprobetrace.txt |   21 ++++++++++-----------
 kernel/trace/trace_kprobe.c         |   18 +-----------------
 2 files changed, 11 insertions(+), 28 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index c3eff6f..f30978e 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -37,15 +37,12 @@ Synopsis of kprobe_events
   @SYM[+|-offs]	: Fetch memory at SYM +|- offs (SYM should be a data symbol)
   $stackN	: Fetch Nth entry of stack (N >= 0)
   $stack	: Fetch stack address.
-  $argN		: Fetch function argument. (N >= 0)(*)
-  $retval	: Fetch return value.(**)
-  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(***)
+  $retval	: Fetch return value.(*)
+  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
   NAME=FETCHARG: Set NAME as the argument name of FETCHARG.
 
-  (*) aN may not correct on asmlinkaged functions and at the middle of
-      function body.
-  (**) only for return probe.
-  (***) this is useful for fetching a field of data structures.
+  (*) only for return probe.
+  (**) this is useful for fetching a field of data structures.
 
 
 Per-Probe Event Filtering
@@ -82,11 +79,14 @@ Usage examples
 To add a probe as a new event, write a new definition to kprobe_events
 as below.
 
-  echo p:myprobe do_sys_open dfd=$arg0 filename=$arg1 flags=$arg2 mode=$arg3 > /sys/kernel/debug/tracing/kprobe_events
+  echo p:myprobe do_sys_open dfd=%ax filename=%dx flags=%cx mode=+4($stack) > /sys/kernel/debug/tracing/kprobe_events
 
  This sets a kprobe on the top of do_sys_open() function with recording
-1st to 4th arguments as "myprobe" event. As this example shows, users can
-choose more familiar names for each arguments.
+1st to 4th arguments as "myprobe" event. Note, which register/stack entry is
+assigned to each function argument depends on arch-specific ABI. If you unsure
+the ABI, please try to use probe subcommand of perf-tools (you can find it
+under tools/perf/).
+As this example shows, users can choose more familiar names for each arguments.
 
   echo r:myretprobe do_sys_open $retval >> /sys/kernel/debug/tracing/kprobe_events
 
@@ -147,4 +147,3 @@ events, you need to enable it.
 returns from SYMBOL(e.g. "sys_open+0x1b/0x1d <- do_sys_open" means kernel
 returns from do_sys_open to sys_open+0x1b).
 
-
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 47f54ab..7ac728d 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -91,11 +91,6 @@ static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr)
 	return retval;
 }
 
-static __kprobes unsigned long fetch_argument(struct pt_regs *regs, void *num)
-{
-	return regs_get_argument_nth(regs, (unsigned int)((unsigned long)num));
-}
-
 static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs,
 					      void *dummy)
 {
@@ -231,9 +226,7 @@ static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff)
 {
 	int ret = -EINVAL;
 
-	if (ff->func == fetch_argument)
-		ret = snprintf(buf, n, "$arg%lu", (unsigned long)ff->data);
-	else if (ff->func == fetch_register) {
+	if (ff->func == fetch_register) {
 		const char *name;
 		name = regs_query_register_name((unsigned int)((long)ff->data));
 		ret = snprintf(buf, n, "%%%s", name);
@@ -489,14 +482,6 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
 			}
 		} else
 			ret = -EINVAL;
-	} else if (strncmp(arg, "arg", 3) == 0 && isdigit(arg[3])) {
-		ret = strict_strtoul(arg + 3, 10, &param);
-		if (ret || param > PARAM_MAX_ARGS)
-			ret = -EINVAL;
-		else {
-			ff->func = fetch_argument;
-			ff->data = (void *)param;
-		}
 	} else
 		ret = -EINVAL;
 	return ret;
@@ -611,7 +596,6 @@ static int create_trace_probe(int argc, char **argv)
 	 *  - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS]
 	 *  - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS]
 	 * Fetch args:
-	 *  $argN	: fetch Nth of function argument. (N:0-)
 	 *  $retval	: fetch return value
 	 *  $stack	: fetch stack address
 	 *  $stackN	: fetch Nth of stack (N:0-)


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 4/8] [CLEANUP] perf probe: Remove newline from die()
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
                   ` (4 preceding siblings ...)
  2010-01-05 22:41 ` [PATCH -tip 8/8] perf probe: Support --line option to show probable source-code lines Masami Hiramatsu
@ 2010-01-05 22:41 ` Masami Hiramatsu
  2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 22:41 ` [PATCH -tip 7/8] perf tools: Enhance glob string matching Masami Hiramatsu
  2010-01-05 23:23 ` [PATCH -tip 6/8] perf tools: Support tracepoint glob matching Masami Hiramatsu
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:41 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Remove newline from die(), because it is automatically added.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/util/probe-finder.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 4b852c0..6402798 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -402,11 +402,11 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
 	} else if (op == DW_OP_regx) {
 		regn = loc->lr_number;
 	} else
-		die("Dwarf_OP %d is not supported.\n", op);
+		die("Dwarf_OP %d is not supported.", op);
 
 	regs = get_arch_regstr(regn);
 	if (!regs)
-		die("%lld exceeds max register number.\n", regn);
+		die("%lld exceeds max register number.", regn);
 
 	if (deref)
 		ret = snprintf(pf->buf, pf->len,
@@ -438,7 +438,7 @@ static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
 	return ;
 error:
 	die("Failed to find the location of %s at this address.\n"
-	    " Perhaps, it has been optimized out.\n", pf->var);
+	    " Perhaps, it has been optimized out.", pf->var);
 }
 
 static int variable_callback(struct die_link *dlink, void *data)
@@ -476,7 +476,7 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
 	/* Search child die for local variables and parameters. */
 	ret = search_die_from_children(sp_die, variable_callback, pf);
 	if (!ret)
-		die("Failed to find '%s' in this function.\n", pf->var);
+		die("Failed to find '%s' in this function.", pf->var);
 }
 
 /* Get a frame base on the address */
@@ -602,7 +602,7 @@ static void find_by_line(struct probe_finder *pf)
 		ret = search_die_from_children(pf->cu_die,
 					       probeaddr_callback, pf);
 		if (ret == 0)
-			die("Probe point is not found in subprograms.\n");
+			die("Probe point is not found in subprograms.");
 		/* Continuing, because target line might be inlined. */
 	}
 	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
@@ -661,7 +661,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)
 				    !die_inlined_subprogram(lk->die))
 					goto found;
 			}
-			die("Failed to find real subprogram.\n");
+			die("Failed to find real subprogram.");
 found:
 			/* Get offset from subprogram */
 			ret = die_within_subprogram(lk->die, pf->addr, &offs);


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 8/8] perf probe: Support --line option to show probable  source-code lines
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
                   ` (3 preceding siblings ...)
  2010-01-05 22:40 ` [PATCH -tip 5/8] perf probe: Show probe list in pager Masami Hiramatsu
@ 2010-01-05 22:41 ` Masami Hiramatsu
  2010-01-06 14:31   ` Masami Hiramatsu
  2010-01-05 22:41 ` [PATCH -tip 4/8] [CLEANUP] perf probe: Remove newline from die() Masami Hiramatsu
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:41 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Add --line option to support showing probable source-code lines.

perf probe --line SRC:LN[-LN|+NUM]
 or
perf probe --line FUNC[:LN[-LN|+NUM]]

This option shows source-code with line number if the line can be
probed. Lines without line number (and blue color) means that the
line can not be probed, because debuginfo doesn't have the
information of those lines.

The argument specifies the range of lines, "source.c:100-120" shows
lines between 100th to l20th in source.c file. And "func:10+20"
shows 20 lines from 10th line of func function.

e.g.
 # ./perf probe --line kernel/sched.c:1080
 <kernel/sched.c:1080>
          *
          * called with rq->lock held and irqs disabled
          */
         static void hrtick_start(struct rq *rq, u64 delay)
         {
                struct hrtimer *timer = &rq->hrtick_timer;
   1086         ktime_t time = ktime_add_ns(timer->base->get_time(), delay);

                hrtimer_set_expires(timer, time);

   1090         if (rq == this_rq()) {
   1091                 hrtimer_restart(timer);
   1092         } else if (!rq->hrtick_csd_pending) {
   1093                 __smp_call_function_single(cpu_of(rq), &rq->hrtick_csd,
   1094                 rq->hrtick_csd_pending = 1;

If you specifying function name, this shows function-relative line number.
 # ./perf probe --line schedule
 <schedule:0>
         asmlinkage void __sched schedule(void)
      1  {
                struct task_struct *prev, *next;
                unsigned long *switch_count;
                struct rq *rq;
                int cpu;

         need_resched:
                preempt_disable();
      9         cpu = smp_processor_id();
     10         rq = cpu_rq(cpu);
     11         rcu_sched_qs(cpu);
     12         prev = rq->curr;
     13         switch_count = &prev->nivcsw;

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/Documentation/perf-probe.txt |   20 +++
 tools/perf/builtin-probe.c              |   80 +++++++++++--
 tools/perf/util/probe-event.c           |  100 ++++++++++++++++
 tools/perf/util/probe-event.h           |    2 
 tools/perf/util/probe-finder.c          |  191 ++++++++++++++++++++++++++++++-
 tools/perf/util/probe-finder.h          |   31 +++++
 6 files changed, 404 insertions(+), 20 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 250e391..2de3407 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -15,6 +15,8 @@ or
 'perf probe' [options] --del='[GROUP:]EVENT' [...]
 or
 'perf probe' --list
+or
+'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
 
 DESCRIPTION
 -----------
@@ -45,6 +47,11 @@ OPTIONS
 --list::
 	List up current probe events.
 
+-L::
+--line=::
+	Show source code lines which can be probed. This needs an argument
+	which specifies a range of the source code.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -56,6 +63,19 @@ Probe points are defined by following syntax.
 It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
 
+LINE SYNTAX
+-----------
+Line range is descripted by following syntax.
+
+ "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]"
+
+FUNC specifies the function name of showing lines. 'RLN' is the start line
+number from function entry line, and 'RLN2' is the end line number. As same as
+probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
+and 'ALN2' is end line number in the file. It is also possible to specify how
+many lines to show by using 'NUM'.
+So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index c1e6774..803d3af 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,11 +55,13 @@ static struct {
 	bool need_dwarf;
 	bool list_events;
 	bool force_add;
+	bool show_lines;
 	int nr_probe;
 	struct probe_point probes[MAX_PROBES];
 	struct strlist *dellist;
 	struct perf_session *psession;
 	struct map *kmap;
+	struct line_range line_range;
 } session;
 
 
@@ -116,6 +118,15 @@ static int opt_del_probe_event(const struct option *opt __used,
 	return 0;
 }
 
+static int opt_show_lines(const struct option *opt __used,
+			  const char *str, int unset __used)
+{
+	if (str)
+		parse_line_range_desc(str, &session.line_range);
+	INIT_LIST_HEAD(&session.line_range.line_list);
+	session.show_lines = true;
+	return 0;
+}
 /* Currently just checking function name from symbol map */
 static void evaluate_probe_point(struct probe_point *pp)
 {
@@ -144,6 +155,7 @@ static const char * const probe_usage[] = {
 	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
 	"perf probe [<options>] --del '[GROUP:]EVENT' ...",
 	"perf probe --list",
+	"perf probe --line 'LINEDESC'",
 	NULL
 };
 
@@ -182,9 +194,34 @@ static const struct option options[] = {
 		opt_add_probe_event),
 	OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
 		    " with existing name"),
+#ifndef NO_LIBDWARF
+	OPT_CALLBACK('L', "line", NULL,
+		     "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
+		     "Show source code lines.", opt_show_lines),
+#endif
 	OPT_END()
 };
 
+/* Initialize symbol maps for vmlinux */
+static void init_vmlinux(void)
+{
+	symbol_conf.sort_by_name = true;
+	if (symbol_conf.vmlinux_name == NULL)
+		symbol_conf.try_vmlinux_path = true;
+	else
+		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
+	if (symbol__init() < 0)
+		die("Failed to init symbol map.");
+	session.psession = perf_session__new(NULL, O_WRONLY, false);
+	if (session.psession == NULL)
+		die("Failed to init perf_session.");
+	session.kmap = map_groups__find_by_name(&session.psession->kmaps,
+						MAP__FUNCTION,
+						"[kernel.kallsyms]");
+	if (!session.kmap)
+		die("Could not find kernel map.\n");
+}
+
 int cmd_probe(int argc, const char **argv, const char *prefix __used)
 {
 	int i, ret;
@@ -203,7 +240,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		parse_probe_event_argv(argc, argv);
 	}
 
-	if ((!session.nr_probe && !session.dellist && !session.list_events))
+	if ((!session.nr_probe && !session.dellist && !session.list_events &&
+	     !session.show_lines))
 		usage_with_options(probe_usage, options);
 
 	if (debugfs_valid_mountpoint(debugfs_path) < 0)
@@ -215,10 +253,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 				   " --add/--del.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (session.show_lines) {
+			pr_warning("  Error: Don't use --list with --line.\n");
+			usage_with_options(probe_usage, options);
+		}
 		show_perf_probe_events();
 		return 0;
 	}
 
+#ifndef NO_LIBDWARF
+	if (session.show_lines) {
+		if (session.nr_probe != 0 || session.dellist) {
+			pr_warning("  Error: Don't use --line with"
+				   " --add/--del.\n");
+			usage_with_options(probe_usage, options);
+		}
+		init_vmlinux();
+		fd = open_vmlinux();
+		if (fd < 0)
+			die("Could not open debuginfo file.");
+		ret = find_line_range(fd, &session.line_range);
+		if (ret <= 0)
+			die("Source line is not found.\n");
+		close(fd);
+		show_line_range(&session.line_range);
+		return 0;
+	}
+#endif
+
 	if (session.dellist) {
 		del_trace_kprobe_events(session.dellist);
 		strlist__delete(session.dellist);
@@ -226,20 +288,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			return 0;
 	}
 
-	/* Initialize symbol maps for vmlinux */
-	symbol_conf.sort_by_name = true;
-	if (symbol_conf.vmlinux_name == NULL)
-		symbol_conf.try_vmlinux_path = true;
-	if (symbol__init() < 0)
-		die("Failed to init symbol map.");
-	session.psession = perf_session__new(NULL, O_WRONLY, false);
-	if (session.psession == NULL)
-		die("Failed to init perf_session.");
-	session.kmap = map_groups__find_by_name(&session.psession->kmaps,
-						MAP__FUNCTION,
-						"[kernel.kallsyms]");
-	if (!session.kmap)
-		die("Could not find kernel map.\n");
+	/* Add probes */
+	init_vmlinux();
 
 	if (session.need_dwarf)
 #ifdef NO_LIBDWARF
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a22141a..71b0dd5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -38,6 +38,7 @@
 #include "strlist.h"
 #include "debug.h"
 #include "cache.h"
+#include "color.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
@@ -63,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
+void parse_line_range_desc(const char *arg, struct line_range *lr)
+{
+	const char *ptr;
+	char *tmp;
+	/*
+	 * <Syntax>
+	 * SRC:SLN[+NUM|-ELN]
+	 * FUNC[:SLN[+NUM|-ELN]]
+	 */
+	ptr = strchr(arg, ':');
+	if (ptr) {
+		lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0);
+		if (*tmp == '+')
+			lr->end = lr->start + (unsigned int)strtoul(tmp + 1,
+								    &tmp, 0);
+		else if (*tmp == '-')
+			lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0);
+		else
+			lr->end = 0;
+		pr_debug("Line range is %u to %u\n", lr->start, lr->end);
+		if (lr->end && lr->start > lr->end)
+			semantic_error("Start line must be smaller"
+				       " than end line.");
+		if (*tmp != '\0')
+			semantic_error("Tailing with invalid character '%d'.",
+				       *tmp);
+		tmp = strndup(arg, (ptr - arg));
+	} else
+		tmp = strdup(arg);
+
+	if (strchr(tmp, '.'))
+		lr->file = tmp;
+	else
+		lr->function = tmp;
+}
+
 /* Check the name is good for event/group */
 static bool check_event_name(const char *name)
 {
@@ -678,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist)
 	close(fd);
 }
 
+#define LINEBUF_SIZE 256
+
+static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+{
+	char buf[LINEBUF_SIZE];
+	const char *color = PERF_COLOR_BLUE;
+
+	if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+		goto error;
+	if (!skip) {
+		if (show_num)
+			fprintf(stdout, "%7u  %s", l, buf);
+		else
+			color_fprintf(stdout, color, "         %s", buf);
+	}
+
+	while (strlen(buf) == LINEBUF_SIZE - 1 &&
+	       buf[LINEBUF_SIZE - 2] != '\n') {
+		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+			goto error;
+		if (!skip) {
+			if (show_num)
+				fprintf(stdout, "%s", buf);
+			else
+				color_fprintf(stdout, color, "%s", buf);
+		}
+	}
+	return;
+error:
+	if (feof(fp))
+		die("Source file is shorter than expected.");
+	else
+		die("File read error: %s", strerror(errno));
+}
+
+void show_line_range(struct line_range *lr)
+{
+	unsigned int l = 1;
+	struct line_node *ln;
+	FILE *fp;
+
+	setup_pager();
+
+	if (lr->function)
+		fprintf(stdout, "<%s:%d>\n", lr->function,
+			lr->start - lr->offset);
+	else
+		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
+
+	fp = fopen(lr->path, "r");
+	if (fp == NULL)
+		die("Failed to open %s: %s", lr->path, strerror(errno));
+	/* Skip to starting line number */
+	while (l < lr->start)
+		show_one_line(fp, l++, true, false);
+
+	list_for_each_entry(ln, &lr->line_list, list) {
+		while (ln->line > l)
+			show_one_line(fp, (l++) - lr->offset, false, false);
+		show_one_line(fp, (l++) - lr->offset, false, true);
+	}
+	fclose(fp);
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 7f1d499..711287d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -5,6 +5,7 @@
 #include "probe-finder.h"
 #include "strlist.h"
 
+extern void parse_line_range_desc(const char *arg, struct line_range *lr);
 extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
 				   bool *need_dwarf);
 extern int synthesize_perf_probe_point(struct probe_point *pp);
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
 				    bool force_add);
 extern void del_trace_kprobe_events(struct strlist *dellist);
 extern void show_perf_probe_events(void);
+extern void show_line_range(struct line_range *lr);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6402798..1b2124d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
 	return found;
 }
 
+static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
+{
+	Dwarf_Signed cnt, i;
+	char **srcs;
+	int ret = 0;
+
+	if (!buf || !fno)
+		return -EINVAL;
+
+	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
+	if (ret == DW_DLV_OK) {
+		if ((Dwarf_Unsigned)cnt > fno - 1) {
+			*buf = strdup(srcs[fno - 1]);
+			ret = 0;
+			pr_debug("found filename: %s\n", *buf);
+		} else
+			ret = -ENOENT;
+		for (i = 0; i < cnt; i++)
+			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
+		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
+	} else
+		ret = -EINVAL;
+	return ret;
+}
+
 /* Compare diename and tname */
 static int die_compare_name(Dwarf_Die dw_die, const char *tname)
 {
@@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data)
 }
 
 /* Find probe point from its line number */
-static void find_by_line(struct probe_finder *pf)
+static void find_probe_point_by_line(struct probe_finder *pf)
 {
 	Dwarf_Signed cnt, i, clm;
 	Dwarf_Line *lines;
@@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)
 				pf->fno = die_get_decl_file(dlink->die);
 				pf->lno = die_get_decl_line(dlink->die)
 					 + pp->line;
-				find_by_line(pf);
+				find_probe_point_by_line(pf);
 				return 1;
 			}
 			if (die_inlined_subprogram(dlink->die)) {
@@ -673,7 +698,7 @@ found:
 	return 0;
 }
 
-static void find_by_func(struct probe_finder *pf)
+static void find_probe_point_by_func(struct probe_finder *pf)
 {
 	search_die_from_children(pf->cu_die, probefunc_callback, pf);
 }
@@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp)
 			if (ret == DW_DLV_NO_ENTRY)
 				pf.cu_base = 0;
 			if (pp->function)
-				find_by_func(&pf);
+				find_probe_point_by_func(&pf);
 			else {
 				pf.lno = pp->line;
-				find_by_line(&pf);
+				find_probe_point_by_line(&pf);
 			}
 		}
 		dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
@@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp)
 	return pp->found;
 }
 
+
+static void line_range_add_line(struct line_range *lr, unsigned int line)
+{
+	struct line_node *ln;
+	struct list_head *p;
+
+	/* Reverse search, because new line will be the last one */
+	list_for_each_entry_reverse(ln, &lr->line_list, list) {
+		if (ln->line < line) {
+			p = &ln->list;
+			goto found;
+		} else if (ln->line == line)	/* Already exist */
+			return ;
+	}
+	/* List is empty, or the smallest entry */
+	p = &lr->line_list;
+found:
+	pr_debug("Debug: add a line %u\n", line);
+	ln = zalloc(sizeof(struct line_node));
+	DIE_IF(ln == NULL);
+	ln->line = line;
+	INIT_LIST_HEAD(&ln->list);
+	list_add(&ln->list, p);
+}
+
+/* Find line range from its line number */
+static void find_line_range_by_line(struct line_finder *lf)
+{
+	Dwarf_Signed cnt, i;
+	Dwarf_Line *lines;
+	Dwarf_Unsigned lineno = 0;
+	Dwarf_Unsigned fno;
+	Dwarf_Addr addr;
+	int ret;
+
+	ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+
+	for (i = 0; i < cnt; i++) {
+		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (fno != lf->fno)
+			continue;
+
+		ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (lf->lno_s > lineno || lf->lno_e < lineno)
+			continue;
+
+		/* Filter line in the function address range */
+		if (lf->addr_s && lf->addr_e) {
+			ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
+			DIE_IF(ret != DW_DLV_OK);
+			if (lf->addr_s > addr || lf->addr_e <= addr)
+				continue;
+		}
+		line_range_add_line(lf->lr, (unsigned int)lineno);
+	}
+	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
+	if (!list_empty(&lf->lr->line_list))
+		lf->found = 1;
+}
+
+/* Search function from function name */
+static int linefunc_callback(struct die_link *dlink, void *data)
+{
+	struct line_finder *lf = (struct line_finder *)data;
+	struct line_range *lr = lf->lr;
+	Dwarf_Half tag;
+	int ret;
+
+	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
+	DIE_IF(ret == DW_DLV_ERROR);
+	if (tag == DW_TAG_subprogram &&
+	    die_compare_name(dlink->die, lr->function) == 0) {
+		/* Get the address range of this function */
+		ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
+		if (ret == DW_DLV_OK)
+			ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY) {
+			lf->addr_s = 0;
+			lf->addr_e = 0;
+		}
+
+		lf->fno = die_get_decl_file(dlink->die);
+		lr->offset = die_get_decl_line(dlink->die);;
+		lf->lno_s = lr->offset + lr->start;
+		if (!lr->end)
+			lf->lno_e = (Dwarf_Unsigned)-1;
+		else
+			lf->lno_e = lr->offset + lr->end;
+		lr->start = lf->lno_s;
+		lr->end = lf->lno_e;
+		find_line_range_by_line(lf);
+		/* If we find a target function, this should be end. */
+		lf->found = 1;
+		return 1;
+	}
+	return 0;
+}
+
+static void find_line_range_by_func(struct line_finder *lf)
+{
+	search_die_from_children(lf->cu_die, linefunc_callback, lf);
+}
+
+int find_line_range(int fd, struct line_range *lr)
+{
+	Dwarf_Half addr_size = 0;
+	Dwarf_Unsigned next_cuh = 0;
+	int ret;
+	struct line_finder lf = {.lr = lr};
+
+	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
+	if (ret != DW_DLV_OK)
+		return -ENOENT;
+
+	while (!lf.found) {
+		/* Search CU (Compilation Unit) */
+		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
+			&addr_size, &next_cuh, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY)
+			break;
+
+		/* Get the DIE(Debugging Information Entry) of this CU */
+		ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+
+		/* Check if target file is included. */
+		if (lr->file)
+			lf.fno = cu_find_fileno(lf.cu_die, lr->file);
+
+		if (!lr->file || lf.fno) {
+			if (lr->function)
+				find_line_range_by_func(&lf);
+			else {
+				lf.lno_s = lr->start;
+				if (!lr->end)
+					lf.lno_e = (Dwarf_Unsigned)-1;
+				else
+					lf.lno_e = lr->end;
+				find_line_range_by_line(&lf);
+			}
+			/* Get the real file path */
+			if (lf.found)
+				cu_get_filename(lf.cu_die, lf.fno, &lr->path);
+		}
+		dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
+	}
+	ret = dwarf_finish(__dw_debug, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+	return lf.found;
+}
+
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index e3f3968..972b386 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -34,8 +34,26 @@ struct probe_point {
 	char			*probes[MAX_PROBES];	/* Output buffers (will be allocated)*/
 };
 
+/* Line number container */
+struct line_node {
+	struct list_head	list;
+	unsigned int		line;
+};
+
+/* Line range */
+struct line_range {
+	char			*file;			/* File name */
+	char			*function;		/* Function name */
+	unsigned int		start;			/* Start line number */
+	unsigned int		end;			/* End line number */
+	unsigned int		offset;			/* Start line offset */
+	char			*path;			/* Real path name */
+	struct list_head	line_list;		/* Visible lines */
+};
+
 #ifndef NO_LIBDWARF
 extern int find_probepoint(int fd, struct probe_point *pp);
+extern int find_line_range(int fd, struct line_range *lr);
 
 /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */
 #ifndef _MIPS_SZLONG
@@ -62,6 +80,19 @@ struct probe_finder {
 	char			*buf;			/* Current output buffer */
 	int			len;			/* Length of output buffer */
 };
+
+struct line_finder {
+	struct line_range	*lr;			/* Target line range */
+
+	Dwarf_Unsigned		fno;			/* File number */
+	Dwarf_Unsigned		lno_s;			/* Start line number */
+	Dwarf_Unsigned		lno_e;			/* End line number */
+	Dwarf_Addr		addr_s;			/* Start address */
+	Dwarf_Addr		addr_e;			/* End address */
+	Dwarf_Die		cu_die;			/* Current CU */
+	int			found;
+};
+
 #endif /* NO_LIBDWARF */
 
 #endif /*_PROBE_FINDER_H */


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 7/8] perf tools: Enhance glob string matching
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
                   ` (5 preceding siblings ...)
  2010-01-05 22:41 ` [PATCH -tip 4/8] [CLEANUP] perf probe: Remove newline from die() Masami Hiramatsu
@ 2010-01-05 22:41 ` Masami Hiramatsu
  2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  2010-01-05 23:23 ` [PATCH -tip 6/8] perf tools: Support tracepoint glob matching Masami Hiramatsu
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 22:41 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Enhance strglobmatch() for supporting character classes([CHARS],
complementation and ranges are also supported) and escaped special
characters (\*, \? etc).

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/util/string.c |   65 +++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 5352d7d..c397d4f 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -227,16 +227,73 @@ fail:
 	return NULL;
 }
 
-/* Glob expression pattern matching */
+/* Character class matching */
+static bool __match_charclass(const char *pat, char c, const char **npat)
+{
+	bool complement = false, ret = true;
+
+	if (*pat == '!') {
+		complement = true;
+		pat++;
+	}
+	if (*pat++ == c)	/* First character is special */
+		goto end;
+
+	while (*pat && *pat != ']') {	/* Matching */
+		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
+			if (*(pat - 1) <= c && c <= *(pat + 1))
+				goto end;
+			if (*(pat - 1) > *(pat + 1))
+				goto error;
+			pat += 2;
+		} else if (*pat++ == c)
+			goto end;
+	}
+	if (!*pat)
+		goto error;
+	ret = false;
+
+end:
+	while (*pat && *pat != ']')	/* Searching closing */
+		pat++;
+	if (!*pat)
+		goto error;
+	*npat = pat + 1;
+	return complement ? !ret : ret;
+
+error:
+	return false;
+}
+
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
 bool strglobmatch(const char *str, const char *pat)
 {
 	while (*str && *pat && *pat != '*') {
-		if (*pat == '?') {
+		if (*pat == '?') {	/* Matches any single character */
 			str++;
 			pat++;
-		} else
-			if (*str++ != *pat++)
+			continue;
+		} else if (*pat == '[')	/* Character classes/Ranges */
+			if (__match_charclass(pat + 1, *str, &pat)) {
+				str++;
+				continue;
+			} else
 				return false;
+		else if (*pat == '\\') /* Escaped char match as normal char */
+			pat++;
+		if (*str++ != *pat++)
+			return false;
 	}
 	/* Check wild card */
 	if (*pat == '*') {


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip 6/8] perf tools: Support tracepoint glob matching
  2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
                   ` (6 preceding siblings ...)
  2010-01-05 22:41 ` [PATCH -tip 7/8] perf tools: Enhance glob string matching Masami Hiramatsu
@ 2010-01-05 23:23 ` Masami Hiramatsu
  2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  7 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-05 23:23 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Support glob wildcard when selecting tracepoint events by -e option.
Without this patch, perf-tools supports 'GROUP:*:record' syntax for
selecting all tracepoints under GROUP group. With this patch, user
can choose tracepoints more flexible by using partial wildcard,
e.g. 'block:*bio*:record'.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/util/parse-events.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 609d5a9..05d0c5c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -450,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name,
 /* sys + ':' + event + ':' + flags*/
 #define MAX_EVOPT_LEN	(MAX_EVENT_LENGTH * 2 + 2 + 128)
 static enum event_result
-parse_subsystem_tracepoint_event(char *sys_name, char *flags)
+parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
+				char *flags)
 {
 	char evt_path[MAXPATHLEN];
 	struct dirent *evt_ent;
@@ -474,6 +475,9 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
 		    || !strcmp(evt_ent->d_name, "filter"))
 			continue;
 
+		if (!strglobmatch(evt_ent->d_name, evt_exp))
+			continue;
+
 		len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
 			       evt_ent->d_name, flags ? ":" : "",
 			       flags ?: "");
@@ -522,9 +526,10 @@ static enum event_result parse_tracepoint_event(const char **strp,
 	if (evt_length >= MAX_EVENT_LENGTH)
 		return EVT_FAILED;
 
-	if (!strcmp(evt_name, "*")) {
+	if (strpbrk(evt_name, "*?")) {
 		*strp = evt_name + evt_length;
-		return parse_subsystem_tracepoint_event(sys_name, flags);
+		return parse_multiple_tracepoint_event(sys_name, evt_name,
+						       flags);
 	} else
 		return parse_single_tracepoint_event(sys_name, evt_name,
 						     evt_length, flags,


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* Re: [PATCH -tip 8/8] perf probe: Support --line option to show probable  source-code lines
  2010-01-05 22:41 ` [PATCH -tip 8/8] perf probe: Support --line option to show probable source-code lines Masami Hiramatsu
@ 2010-01-06 14:31   ` Masami Hiramatsu
  2010-01-06 14:39     ` [PATCH -tip] " Masami Hiramatsu
  0 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-06 14:31 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ingo Molnar, lkml, Frederic Weisbecker, Arnaldo Carvalho de Melo,
	systemtap, DLE, Paul Mackerras, Peter Zijlstra, Mike Galbraith

Masami Hiramatsu wrote:
> Add --line option to support showing probable source-code lines.
> 
> perf probe --line SRC:LN[-LN|+NUM]
>  or
> perf probe --line FUNC[:LN[-LN|+NUM]]
> 
> This option shows source-code with line number if the line can be
> probed. Lines without line number (and blue color) means that the
> line can not be probed, because debuginfo doesn't have the
> information of those lines.
> 
> The argument specifies the range of lines, "source.c:100-120" shows
> lines between 100th to l20th in source.c file. And "func:10+20"
> shows 20 lines from 10th line of func function.

Oops, I found this will conflict with Arnaldo's patch.
I'll update this on his patches.

Thank you,
-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* [PATCH -tip] perf probe: Support --line option to show probable  source-code lines
  2010-01-06 14:31   ` Masami Hiramatsu
@ 2010-01-06 14:39     ` Masami Hiramatsu
  2010-01-13 10:37       ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  0 siblings, 1 reply; 20+ messages in thread
From: Masami Hiramatsu @ 2010-01-06 14:39 UTC (permalink / raw)
  To: Ingo Molnar, lkml
  Cc: Frederic Weisbecker, Arnaldo Carvalho de Melo, systemtap, DLE,
	Masami Hiramatsu, Ingo Molnar, Frederic Weisbecker,
	Paul Mackerras, Arnaldo Carvalho de Melo, Peter Zijlstra,
	Mike Galbraith

Add --line option to support showing probable source-code lines.

perf probe --line SRC:LN[-LN|+NUM]
 or
perf probe --line FUNC[:LN[-LN|+NUM]]

This option shows source-code with line number if the line can be
probed. Lines without line number (and blue color) means that the
line can not be probed, because debuginfo doesn't have the
information of those lines.

The argument specifies the range of lines, "source.c:100-120" shows
lines between 100th to l20th in source.c file. And "func:10+20"
shows 20 lines from 10th line of func function.

e.g.
 # ./perf probe --line kernel/sched.c:1080
 <kernel/sched.c:1080>
          *
          * called with rq->lock held and irqs disabled
          */
         static void hrtick_start(struct rq *rq, u64 delay)
         {
                struct hrtimer *timer = &rq->hrtick_timer;
   1086         ktime_t time = ktime_add_ns(timer->base->get_time(), delay);

                hrtimer_set_expires(timer, time);

   1090         if (rq == this_rq()) {
   1091                 hrtimer_restart(timer);
   1092         } else if (!rq->hrtick_csd_pending) {
   1093                 __smp_call_function_single(cpu_of(rq), &rq->hrtick_csd,
   1094                 rq->hrtick_csd_pending = 1;

If you specifying function name, this shows function-relative line number.
 # ./perf probe --line schedule
 <schedule:0>
         asmlinkage void __sched schedule(void)
      1  {
                struct task_struct *prev, *next;
                unsigned long *switch_count;
                struct rq *rq;
                int cpu;

         need_resched:
                preempt_disable();
      9         cpu = smp_processor_id();
     10         rq = cpu_rq(cpu);
     11         rcu_sched_qs(cpu);
     12         prev = rq->curr;
     13         switch_count = &prev->nivcsw;

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
---

 tools/perf/Documentation/perf-probe.txt |   20 +++
 tools/perf/builtin-probe.c              |   76 ++++++++++--
 tools/perf/util/probe-event.c           |  100 ++++++++++++++++
 tools/perf/util/probe-event.h           |    2 
 tools/perf/util/probe-finder.c          |  191 ++++++++++++++++++++++++++++++-
 tools/perf/util/probe-finder.h          |   31 +++++
 6 files changed, 402 insertions(+), 18 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 250e391..2de3407 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -15,6 +15,8 @@ or
 'perf probe' [options] --del='[GROUP:]EVENT' [...]
 or
 'perf probe' --list
+or
+'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
 
 DESCRIPTION
 -----------
@@ -45,6 +47,11 @@ OPTIONS
 --list::
 	List up current probe events.
 
+-L::
+--line=::
+	Show source code lines which can be probed. This needs an argument
+	which specifies a range of the source code.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -56,6 +63,19 @@ Probe points are defined by following syntax.
 It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
 
+LINE SYNTAX
+-----------
+Line range is descripted by following syntax.
+
+ "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]"
+
+FUNC specifies the function name of showing lines. 'RLN' is the start line
+number from function entry line, and 'RLN2' is the end line number. As same as
+probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
+and 'ALN2' is end line number in the file. It is also possible to specify how
+many lines to show by using 'NUM'.
+So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index ffdd3fe..1d3a99e 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,11 +55,13 @@ static struct {
 	bool need_dwarf;
 	bool list_events;
 	bool force_add;
+	bool show_lines;
 	int nr_probe;
 	struct probe_point probes[MAX_PROBES];
 	struct strlist *dellist;
 	struct perf_session *psession;
 	struct map *kmap;
+	struct line_range line_range;
 } session;
 
 
@@ -116,6 +118,15 @@ static int opt_del_probe_event(const struct option *opt __used,
 	return 0;
 }
 
+static int opt_show_lines(const struct option *opt __used,
+			  const char *str, int unset __used)
+{
+	if (str)
+		parse_line_range_desc(str, &session.line_range);
+	INIT_LIST_HEAD(&session.line_range.line_list);
+	session.show_lines = true;
+	return 0;
+}
 /* Currently just checking function name from symbol map */
 static void evaluate_probe_point(struct probe_point *pp)
 {
@@ -144,6 +155,7 @@ static const char * const probe_usage[] = {
 	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
 	"perf probe [<options>] --del '[GROUP:]EVENT' ...",
 	"perf probe --list",
+	"perf probe --line 'LINEDESC'",
 	NULL
 };
 
@@ -182,9 +194,32 @@ static const struct option options[] = {
 		opt_add_probe_event),
 	OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
 		    " with existing name"),
+#ifndef NO_LIBDWARF
+	OPT_CALLBACK('L', "line", NULL,
+		     "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
+		     "Show source code lines.", opt_show_lines),
+#endif
 	OPT_END()
 };
 
+/* Initialize symbol maps for vmlinux */
+static void init_vmlinux(void)
+{
+	symbol_conf.sort_by_name = true;
+	if (symbol_conf.vmlinux_name == NULL)
+		symbol_conf.try_vmlinux_path = true;
+	else
+		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
+	if (symbol__init() < 0)
+		die("Failed to init symbol map.");
+	session.psession = perf_session__new(NULL, O_WRONLY, false);
+	if (session.psession == NULL)
+		die("Failed to init perf_session.");
+	session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
+	if (!session.kmap)
+		die("Could not find kernel map.\n");
+}
+
 int cmd_probe(int argc, const char **argv, const char *prefix __used)
 {
 	int i, ret;
@@ -203,7 +238,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		parse_probe_event_argv(argc, argv);
 	}
 
-	if ((!session.nr_probe && !session.dellist && !session.list_events))
+	if ((!session.nr_probe && !session.dellist && !session.list_events &&
+	     !session.show_lines))
 		usage_with_options(probe_usage, options);
 
 	if (debugfs_valid_mountpoint(debugfs_path) < 0)
@@ -215,10 +251,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 				   " --add/--del.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (session.show_lines) {
+			pr_warning("  Error: Don't use --list with --line.\n");
+			usage_with_options(probe_usage, options);
+		}
 		show_perf_probe_events();
 		return 0;
 	}
 
+#ifndef NO_LIBDWARF
+	if (session.show_lines) {
+		if (session.nr_probe != 0 || session.dellist) {
+			pr_warning("  Error: Don't use --line with"
+				   " --add/--del.\n");
+			usage_with_options(probe_usage, options);
+		}
+		init_vmlinux();
+		fd = open_vmlinux();
+		if (fd < 0)
+			die("Could not open debuginfo file.");
+		ret = find_line_range(fd, &session.line_range);
+		if (ret <= 0)
+			die("Source line is not found.\n");
+		close(fd);
+		show_line_range(&session.line_range);
+		return 0;
+	}
+#endif
+
 	if (session.dellist) {
 		del_trace_kprobe_events(session.dellist);
 		strlist__delete(session.dellist);
@@ -226,18 +286,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			return 0;
 	}
 
-	/* Initialize symbol maps for vmlinux */
-	symbol_conf.sort_by_name = true;
-	if (symbol_conf.vmlinux_name == NULL)
-		symbol_conf.try_vmlinux_path = true;
-	if (symbol__init() < 0)
-		die("Failed to init symbol map.");
-	session.psession = perf_session__new(NULL, O_WRONLY, false);
-	if (session.psession == NULL)
-		die("Failed to init perf_session.");
-	session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
-	if (!session.kmap)
-		die("Could not find kernel map.\n");
+	/* Add probes */
+	init_vmlinux();
 
 	if (session.need_dwarf)
 #ifdef NO_LIBDWARF
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a22141a..71b0dd5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -38,6 +38,7 @@
 #include "strlist.h"
 #include "debug.h"
 #include "cache.h"
+#include "color.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
@@ -63,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
+void parse_line_range_desc(const char *arg, struct line_range *lr)
+{
+	const char *ptr;
+	char *tmp;
+	/*
+	 * <Syntax>
+	 * SRC:SLN[+NUM|-ELN]
+	 * FUNC[:SLN[+NUM|-ELN]]
+	 */
+	ptr = strchr(arg, ':');
+	if (ptr) {
+		lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0);
+		if (*tmp == '+')
+			lr->end = lr->start + (unsigned int)strtoul(tmp + 1,
+								    &tmp, 0);
+		else if (*tmp == '-')
+			lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0);
+		else
+			lr->end = 0;
+		pr_debug("Line range is %u to %u\n", lr->start, lr->end);
+		if (lr->end && lr->start > lr->end)
+			semantic_error("Start line must be smaller"
+				       " than end line.");
+		if (*tmp != '\0')
+			semantic_error("Tailing with invalid character '%d'.",
+				       *tmp);
+		tmp = strndup(arg, (ptr - arg));
+	} else
+		tmp = strdup(arg);
+
+	if (strchr(tmp, '.'))
+		lr->file = tmp;
+	else
+		lr->function = tmp;
+}
+
 /* Check the name is good for event/group */
 static bool check_event_name(const char *name)
 {
@@ -678,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist)
 	close(fd);
 }
 
+#define LINEBUF_SIZE 256
+
+static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+{
+	char buf[LINEBUF_SIZE];
+	const char *color = PERF_COLOR_BLUE;
+
+	if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+		goto error;
+	if (!skip) {
+		if (show_num)
+			fprintf(stdout, "%7u  %s", l, buf);
+		else
+			color_fprintf(stdout, color, "         %s", buf);
+	}
+
+	while (strlen(buf) == LINEBUF_SIZE - 1 &&
+	       buf[LINEBUF_SIZE - 2] != '\n') {
+		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+			goto error;
+		if (!skip) {
+			if (show_num)
+				fprintf(stdout, "%s", buf);
+			else
+				color_fprintf(stdout, color, "%s", buf);
+		}
+	}
+	return;
+error:
+	if (feof(fp))
+		die("Source file is shorter than expected.");
+	else
+		die("File read error: %s", strerror(errno));
+}
+
+void show_line_range(struct line_range *lr)
+{
+	unsigned int l = 1;
+	struct line_node *ln;
+	FILE *fp;
+
+	setup_pager();
+
+	if (lr->function)
+		fprintf(stdout, "<%s:%d>\n", lr->function,
+			lr->start - lr->offset);
+	else
+		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
+
+	fp = fopen(lr->path, "r");
+	if (fp == NULL)
+		die("Failed to open %s: %s", lr->path, strerror(errno));
+	/* Skip to starting line number */
+	while (l < lr->start)
+		show_one_line(fp, l++, true, false);
+
+	list_for_each_entry(ln, &lr->line_list, list) {
+		while (ln->line > l)
+			show_one_line(fp, (l++) - lr->offset, false, false);
+		show_one_line(fp, (l++) - lr->offset, false, true);
+	}
+	fclose(fp);
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 7f1d499..711287d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -5,6 +5,7 @@
 #include "probe-finder.h"
 #include "strlist.h"
 
+extern void parse_line_range_desc(const char *arg, struct line_range *lr);
 extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
 				   bool *need_dwarf);
 extern int synthesize_perf_probe_point(struct probe_point *pp);
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
 				    bool force_add);
 extern void del_trace_kprobe_events(struct strlist *dellist);
 extern void show_perf_probe_events(void);
+extern void show_line_range(struct line_range *lr);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6402798..1b2124d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
 	return found;
 }
 
+static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
+{
+	Dwarf_Signed cnt, i;
+	char **srcs;
+	int ret = 0;
+
+	if (!buf || !fno)
+		return -EINVAL;
+
+	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
+	if (ret == DW_DLV_OK) {
+		if ((Dwarf_Unsigned)cnt > fno - 1) {
+			*buf = strdup(srcs[fno - 1]);
+			ret = 0;
+			pr_debug("found filename: %s\n", *buf);
+		} else
+			ret = -ENOENT;
+		for (i = 0; i < cnt; i++)
+			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
+		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
+	} else
+		ret = -EINVAL;
+	return ret;
+}
+
 /* Compare diename and tname */
 static int die_compare_name(Dwarf_Die dw_die, const char *tname)
 {
@@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data)
 }
 
 /* Find probe point from its line number */
-static void find_by_line(struct probe_finder *pf)
+static void find_probe_point_by_line(struct probe_finder *pf)
 {
 	Dwarf_Signed cnt, i, clm;
 	Dwarf_Line *lines;
@@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)
 				pf->fno = die_get_decl_file(dlink->die);
 				pf->lno = die_get_decl_line(dlink->die)
 					 + pp->line;
-				find_by_line(pf);
+				find_probe_point_by_line(pf);
 				return 1;
 			}
 			if (die_inlined_subprogram(dlink->die)) {
@@ -673,7 +698,7 @@ found:
 	return 0;
 }
 
-static void find_by_func(struct probe_finder *pf)
+static void find_probe_point_by_func(struct probe_finder *pf)
 {
 	search_die_from_children(pf->cu_die, probefunc_callback, pf);
 }
@@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp)
 			if (ret == DW_DLV_NO_ENTRY)
 				pf.cu_base = 0;
 			if (pp->function)
-				find_by_func(&pf);
+				find_probe_point_by_func(&pf);
 			else {
 				pf.lno = pp->line;
-				find_by_line(&pf);
+				find_probe_point_by_line(&pf);
 			}
 		}
 		dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
@@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp)
 	return pp->found;
 }
 
+
+static void line_range_add_line(struct line_range *lr, unsigned int line)
+{
+	struct line_node *ln;
+	struct list_head *p;
+
+	/* Reverse search, because new line will be the last one */
+	list_for_each_entry_reverse(ln, &lr->line_list, list) {
+		if (ln->line < line) {
+			p = &ln->list;
+			goto found;
+		} else if (ln->line == line)	/* Already exist */
+			return ;
+	}
+	/* List is empty, or the smallest entry */
+	p = &lr->line_list;
+found:
+	pr_debug("Debug: add a line %u\n", line);
+	ln = zalloc(sizeof(struct line_node));
+	DIE_IF(ln == NULL);
+	ln->line = line;
+	INIT_LIST_HEAD(&ln->list);
+	list_add(&ln->list, p);
+}
+
+/* Find line range from its line number */
+static void find_line_range_by_line(struct line_finder *lf)
+{
+	Dwarf_Signed cnt, i;
+	Dwarf_Line *lines;
+	Dwarf_Unsigned lineno = 0;
+	Dwarf_Unsigned fno;
+	Dwarf_Addr addr;
+	int ret;
+
+	ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+
+	for (i = 0; i < cnt; i++) {
+		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (fno != lf->fno)
+			continue;
+
+		ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (lf->lno_s > lineno || lf->lno_e < lineno)
+			continue;
+
+		/* Filter line in the function address range */
+		if (lf->addr_s && lf->addr_e) {
+			ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
+			DIE_IF(ret != DW_DLV_OK);
+			if (lf->addr_s > addr || lf->addr_e <= addr)
+				continue;
+		}
+		line_range_add_line(lf->lr, (unsigned int)lineno);
+	}
+	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
+	if (!list_empty(&lf->lr->line_list))
+		lf->found = 1;
+}
+
+/* Search function from function name */
+static int linefunc_callback(struct die_link *dlink, void *data)
+{
+	struct line_finder *lf = (struct line_finder *)data;
+	struct line_range *lr = lf->lr;
+	Dwarf_Half tag;
+	int ret;
+
+	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
+	DIE_IF(ret == DW_DLV_ERROR);
+	if (tag == DW_TAG_subprogram &&
+	    die_compare_name(dlink->die, lr->function) == 0) {
+		/* Get the address range of this function */
+		ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
+		if (ret == DW_DLV_OK)
+			ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY) {
+			lf->addr_s = 0;
+			lf->addr_e = 0;
+		}
+
+		lf->fno = die_get_decl_file(dlink->die);
+		lr->offset = die_get_decl_line(dlink->die);;
+		lf->lno_s = lr->offset + lr->start;
+		if (!lr->end)
+			lf->lno_e = (Dwarf_Unsigned)-1;
+		else
+			lf->lno_e = lr->offset + lr->end;
+		lr->start = lf->lno_s;
+		lr->end = lf->lno_e;
+		find_line_range_by_line(lf);
+		/* If we find a target function, this should be end. */
+		lf->found = 1;
+		return 1;
+	}
+	return 0;
+}
+
+static void find_line_range_by_func(struct line_finder *lf)
+{
+	search_die_from_children(lf->cu_die, linefunc_callback, lf);
+}
+
+int find_line_range(int fd, struct line_range *lr)
+{
+	Dwarf_Half addr_size = 0;
+	Dwarf_Unsigned next_cuh = 0;
+	int ret;
+	struct line_finder lf = {.lr = lr};
+
+	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
+	if (ret != DW_DLV_OK)
+		return -ENOENT;
+
+	while (!lf.found) {
+		/* Search CU (Compilation Unit) */
+		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
+			&addr_size, &next_cuh, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY)
+			break;
+
+		/* Get the DIE(Debugging Information Entry) of this CU */
+		ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+
+		/* Check if target file is included. */
+		if (lr->file)
+			lf.fno = cu_find_fileno(lf.cu_die, lr->file);
+
+		if (!lr->file || lf.fno) {
+			if (lr->function)
+				find_line_range_by_func(&lf);
+			else {
+				lf.lno_s = lr->start;
+				if (!lr->end)
+					lf.lno_e = (Dwarf_Unsigned)-1;
+				else
+					lf.lno_e = lr->end;
+				find_line_range_by_line(&lf);
+			}
+			/* Get the real file path */
+			if (lf.found)
+				cu_get_filename(lf.cu_die, lf.fno, &lr->path);
+		}
+		dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
+	}
+	ret = dwarf_finish(__dw_debug, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+	return lf.found;
+}
+
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index e3f3968..972b386 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -34,8 +34,26 @@ struct probe_point {
 	char			*probes[MAX_PROBES];	/* Output buffers (will be allocated)*/
 };
 
+/* Line number container */
+struct line_node {
+	struct list_head	list;
+	unsigned int		line;
+};
+
+/* Line range */
+struct line_range {
+	char			*file;			/* File name */
+	char			*function;		/* Function name */
+	unsigned int		start;			/* Start line number */
+	unsigned int		end;			/* End line number */
+	unsigned int		offset;			/* Start line offset */
+	char			*path;			/* Real path name */
+	struct list_head	line_list;		/* Visible lines */
+};
+
 #ifndef NO_LIBDWARF
 extern int find_probepoint(int fd, struct probe_point *pp);
+extern int find_line_range(int fd, struct line_range *lr);
 
 /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */
 #ifndef _MIPS_SZLONG
@@ -62,6 +80,19 @@ struct probe_finder {
 	char			*buf;			/* Current output buffer */
 	int			len;			/* Length of output buffer */
 };
+
+struct line_finder {
+	struct line_range	*lr;			/* Target line range */
+
+	Dwarf_Unsigned		fno;			/* File number */
+	Dwarf_Unsigned		lno_s;			/* Start line number */
+	Dwarf_Unsigned		lno_e;			/* End line number */
+	Dwarf_Addr		addr_s;			/* Start address */
+	Dwarf_Addr		addr_e;			/* End address */
+	Dwarf_Die		cu_die;			/* Current CU */
+	int			found;
+};
+
 #endif /* NO_LIBDWARF */
 
 #endif /*_PROBE_FINDER_H */


-- 
Masami Hiramatsu

Software Engineer
Hitachi Computer Products (America), Inc.
Software Solutions Division

e-mail: mhiramat@redhat.com

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

* Re: [PATCH -tip 1/8] tracing/kprobe: Update example output in  documentation
  2010-01-05 22:40 ` [PATCH -tip 1/8] tracing/kprobe: Update example output in documentation Masami Hiramatsu
@ 2010-01-06 18:40   ` Steven Rostedt
  2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
  1 sibling, 0 replies; 20+ messages in thread
From: Steven Rostedt @ 2010-01-06 18:40 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ingo Molnar, lkml, Frederic Weisbecker, Arnaldo Carvalho de Melo,
	systemtap, DLE

On Tue, 2010-01-05 at 17:46 -0500, Masami Hiramatsu wrote:
> Update example output in documentation accroding to current
> implementation.
> 
> Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
> Cc: Ingo Molnar <mingo@elte.hu>
> Cc: Steven Rostedt <rostedt@goodmis.org>

Acked-by: Steven Rostedt <rostedt@goodmis.org>

-- Steve

> Cc: Frederic Weisbecker <fweisbec@gmail.com>
> ---
> 
>  Documentation/trace/kprobetrace.txt |   33 +++++++++++++++++----------------
>  1 files changed, 17 insertions(+), 16 deletions(-)
> 
> diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
> index 47aabee..c3eff6f 100644
> --- a/Documentation/trace/kprobetrace.txt
> +++ b/Documentation/trace/kprobetrace.txt
> @@ -97,23 +97,24 @@ recording return value as "myretprobe" event.
>  
>    cat /sys/kernel/debug/tracing/events/kprobes/myprobe/format
>  name: myprobe
> -ID: 75
> +ID: 780
>  format:
> -	field:unsigned short common_type;	offset:0;	size:2;
> -	field:unsigned char common_flags;	offset:2;	size:1;
> -	field:unsigned char common_preempt_count;	offset:3;	size:1;
> -	field:int common_pid;	offset:4;	size:4;
> -	field:int common_tgid;	offset:8;	size:4;
> -
> -	field: unsigned long ip;	offset:16;tsize:8;
> -	field: int nargs;	offset:24;tsize:4;
> -	field: unsigned long dfd;	offset:32;tsize:8;
> -	field: unsigned long filename;	offset:40;tsize:8;
> -	field: unsigned long flags;	offset:48;tsize:8;
> -	field: unsigned long mode;	offset:56;tsize:8;
> -
> -print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->ip, REC->dfd, REC->filename, REC->flags, REC->mode
> -
> +        field:unsigned short common_type;       offset:0;       size:2; signed:0;
> +        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
> +        field:unsigned char common_preempt_count;       offset:3; size:1;signed:0;
> +        field:int common_pid;   offset:4;       size:4; signed:1;
> +        field:int common_lock_depth;    offset:8;       size:4; signed:1;
> +
> +        field:unsigned long __probe_ip; offset:12;      size:4; signed:0;
> +        field:int __probe_nargs;        offset:16;      size:4; signed:1;
> +        field:unsigned long dfd;        offset:20;      size:4; signed:0;
> +        field:unsigned long filename;   offset:24;      size:4; signed:0;
> +        field:unsigned long flags;      offset:28;      size:4; signed:0;
> +        field:unsigned long mode;       offset:32;      size:4; signed:0;
> +
> +
> +print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->__probe_ip,
> +REC->dfd, REC->filename, REC->flags, REC->mode
>  
>   You can see that the event has 4 arguments as in the expressions you specified.
>  
> 
> 


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

* [tip:perf/core] perf probe: Show probe list in pager
  2010-01-05 22:40 ` [PATCH -tip 5/8] perf probe: Show probe list in pager Masami Hiramatsu
@ 2010-01-13 10:35   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:35 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, paulus, acme, hpa, mingo, efault, peterz, fweisbec,
	dle-develop, tglx, mhiramat, mingo, systemtap

Commit-ID:  72041334b8c75ae7e1da2f17ba2b7afee8f2abd7
Gitweb:     http://git.kernel.org/tip/72041334b8c75ae7e1da2f17ba2b7afee8f2abd7
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:47:10 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:13 +0100

perf probe: Show probe list in pager

Show probe list in pager, because the list can be longer than
a page.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100105224710.19431.61542.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 tools/perf/util/probe-event.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 8e532d9..a22141a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -37,6 +37,7 @@
 #include "string.h"
 #include "strlist.h"
 #include "debug.h"
+#include "cache.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
@@ -455,6 +456,8 @@ void show_perf_probe_events(void)
 	struct strlist *rawlist;
 	struct str_node *ent;
 
+	setup_pager();
+
 	fd = open_kprobe_events(O_RDONLY, 0);
 	rawlist = get_trace_kprobe_event_rawlist(fd);
 	close(fd);

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

* [tip:perf/core] tracing/kprobe: Update example output in documentation
  2010-01-05 22:40 ` [PATCH -tip 1/8] tracing/kprobe: Update example output in documentation Masami Hiramatsu
  2010-01-06 18:40   ` Steven Rostedt
@ 2010-01-13 10:35   ` tip-bot for Masami Hiramatsu
  1 sibling, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:35 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, acme, hpa, mingo, fweisbec, dle-develop, rostedt,
	tglx, mhiramat, mingo, systemtap

Commit-ID:  ec3a9039601af210fca4650d229621fe5a21df0b
Gitweb:     http://git.kernel.org/tip/ec3a9039601af210fca4650d229621fe5a21df0b
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:46:41 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:11 +0100

tracing/kprobe: Update example output in documentation

Update example output in documentation according to current
implementation.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
LKML-Reference: <20100105224641.19431.34967.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 Documentation/trace/kprobetrace.txt |   33 +++++++++++++++++----------------
 1 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index 47aabee..c3eff6f 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -97,23 +97,24 @@ recording return value as "myretprobe" event.
 
   cat /sys/kernel/debug/tracing/events/kprobes/myprobe/format
 name: myprobe
-ID: 75
+ID: 780
 format:
-	field:unsigned short common_type;	offset:0;	size:2;
-	field:unsigned char common_flags;	offset:2;	size:1;
-	field:unsigned char common_preempt_count;	offset:3;	size:1;
-	field:int common_pid;	offset:4;	size:4;
-	field:int common_tgid;	offset:8;	size:4;
-
-	field: unsigned long ip;	offset:16;tsize:8;
-	field: int nargs;	offset:24;tsize:4;
-	field: unsigned long dfd;	offset:32;tsize:8;
-	field: unsigned long filename;	offset:40;tsize:8;
-	field: unsigned long flags;	offset:48;tsize:8;
-	field: unsigned long mode;	offset:56;tsize:8;
-
-print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->ip, REC->dfd, REC->filename, REC->flags, REC->mode
-
+        field:unsigned short common_type;       offset:0;       size:2; signed:0;
+        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
+        field:unsigned char common_preempt_count;       offset:3; size:1;signed:0;
+        field:int common_pid;   offset:4;       size:4; signed:1;
+        field:int common_lock_depth;    offset:8;       size:4; signed:1;
+
+        field:unsigned long __probe_ip; offset:12;      size:4; signed:0;
+        field:int __probe_nargs;        offset:16;      size:4; signed:1;
+        field:unsigned long dfd;        offset:20;      size:4; signed:0;
+        field:unsigned long filename;   offset:24;      size:4; signed:0;
+        field:unsigned long flags;      offset:28;      size:4; signed:0;
+        field:unsigned long mode;       offset:32;      size:4; signed:0;
+
+
+print fmt: "(%lx) dfd=%lx filename=%lx flags=%lx mode=%lx", REC->__probe_ip,
+REC->dfd, REC->filename, REC->flags, REC->mode
 
  You can see that the event has 4 arguments as in the expressions you specified.
 

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

* [tip:perf/core] tracing/kprobe: Drop function argument access syntax
  2010-01-05 22:40 ` [PATCH -tip 2/8] tracing/kprobe: Drop function argument access syntax Masami Hiramatsu
@ 2010-01-13 10:35   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:35 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, mingo, fweisbec, dle-develop, rostedt, oleg, tglx,
	mhiramat, systemtap, hpa, linux-kernel, mahesh, roland, benh,
	mikey, mingo

Commit-ID:  14640106f243a3b29944d7198569090fa6546f2d
Gitweb:     http://git.kernel.org/tip/14640106f243a3b29944d7198569090fa6546f2d
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:46:48 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:12 +0100

tracing/kprobe: Drop function argument access syntax

Drop function argument access syntax, because the function
arguments depend on not only architecture but also
compile-options and function API. And now, we have perf-probe
for finding register/memory assigned to each argument.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Neuling <mikey@neuling.org>
Cc: linuxppc-dev@ozlabs.org
LKML-Reference: <20100105224648.19431.52309.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 Documentation/trace/kprobetrace.txt |   21 ++++++++++-----------
 kernel/trace/trace_kprobe.c         |   18 +-----------------
 2 files changed, 11 insertions(+), 28 deletions(-)

diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index c3eff6f..f30978e 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -37,15 +37,12 @@ Synopsis of kprobe_events
   @SYM[+|-offs]	: Fetch memory at SYM +|- offs (SYM should be a data symbol)
   $stackN	: Fetch Nth entry of stack (N >= 0)
   $stack	: Fetch stack address.
-  $argN		: Fetch function argument. (N >= 0)(*)
-  $retval	: Fetch return value.(**)
-  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(***)
+  $retval	: Fetch return value.(*)
+  +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**)
   NAME=FETCHARG: Set NAME as the argument name of FETCHARG.
 
-  (*) aN may not correct on asmlinkaged functions and at the middle of
-      function body.
-  (**) only for return probe.
-  (***) this is useful for fetching a field of data structures.
+  (*) only for return probe.
+  (**) this is useful for fetching a field of data structures.
 
 
 Per-Probe Event Filtering
@@ -82,11 +79,14 @@ Usage examples
 To add a probe as a new event, write a new definition to kprobe_events
 as below.
 
-  echo p:myprobe do_sys_open dfd=$arg0 filename=$arg1 flags=$arg2 mode=$arg3 > /sys/kernel/debug/tracing/kprobe_events
+  echo p:myprobe do_sys_open dfd=%ax filename=%dx flags=%cx mode=+4($stack) > /sys/kernel/debug/tracing/kprobe_events
 
  This sets a kprobe on the top of do_sys_open() function with recording
-1st to 4th arguments as "myprobe" event. As this example shows, users can
-choose more familiar names for each arguments.
+1st to 4th arguments as "myprobe" event. Note, which register/stack entry is
+assigned to each function argument depends on arch-specific ABI. If you unsure
+the ABI, please try to use probe subcommand of perf-tools (you can find it
+under tools/perf/).
+As this example shows, users can choose more familiar names for each arguments.
 
   echo r:myretprobe do_sys_open $retval >> /sys/kernel/debug/tracing/kprobe_events
 
@@ -147,4 +147,3 @@ events, you need to enable it.
 returns from SYMBOL(e.g. "sys_open+0x1b/0x1d <- do_sys_open" means kernel
 returns from do_sys_open to sys_open+0x1b).
 
-
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 47f54ab..7ac728d 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -91,11 +91,6 @@ static __kprobes unsigned long fetch_memory(struct pt_regs *regs, void *addr)
 	return retval;
 }
 
-static __kprobes unsigned long fetch_argument(struct pt_regs *regs, void *num)
-{
-	return regs_get_argument_nth(regs, (unsigned int)((unsigned long)num));
-}
-
 static __kprobes unsigned long fetch_retvalue(struct pt_regs *regs,
 					      void *dummy)
 {
@@ -231,9 +226,7 @@ static int probe_arg_string(char *buf, size_t n, struct fetch_func *ff)
 {
 	int ret = -EINVAL;
 
-	if (ff->func == fetch_argument)
-		ret = snprintf(buf, n, "$arg%lu", (unsigned long)ff->data);
-	else if (ff->func == fetch_register) {
+	if (ff->func == fetch_register) {
 		const char *name;
 		name = regs_query_register_name((unsigned int)((long)ff->data));
 		ret = snprintf(buf, n, "%%%s", name);
@@ -489,14 +482,6 @@ static int parse_probe_vars(char *arg, struct fetch_func *ff, int is_return)
 			}
 		} else
 			ret = -EINVAL;
-	} else if (strncmp(arg, "arg", 3) == 0 && isdigit(arg[3])) {
-		ret = strict_strtoul(arg + 3, 10, &param);
-		if (ret || param > PARAM_MAX_ARGS)
-			ret = -EINVAL;
-		else {
-			ff->func = fetch_argument;
-			ff->data = (void *)param;
-		}
 	} else
 		ret = -EINVAL;
 	return ret;
@@ -611,7 +596,6 @@ static int create_trace_probe(int argc, char **argv)
 	 *  - Add kprobe: p[:[GRP/]EVENT] KSYM[+OFFS]|KADDR [FETCHARGS]
 	 *  - Add kretprobe: r[:[GRP/]EVENT] KSYM[+0] [FETCHARGS]
 	 * Fetch args:
-	 *  $argN	: fetch Nth of function argument. (N:0-)
 	 *  $retval	: fetch return value
 	 *  $stack	: fetch stack address
 	 *  $stackN	: fetch Nth of stack (N:0-)

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

* [tip:perf/core] x86/ptrace: Remove unused regs_get_argument_nth API
  2010-01-05 22:40 ` [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API Masami Hiramatsu
@ 2010-01-13 10:36   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:36 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, mingo, fweisbec, dle-develop, rostedt, oleg, tglx,
	mhiramat, systemtap, hpa, linux-kernel, mahesh, roland, benh,
	mikey, mingo

Commit-ID:  aa5add93e92019018e905146f8c3d3f8e3c08300
Gitweb:     http://git.kernel.org/tip/aa5add93e92019018e905146f8c3d3f8e3c08300
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:46:56 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:12 +0100

x86/ptrace: Remove unused regs_get_argument_nth API

Because of dropping function argument syntax from kprobe-tracer,
we don't need this API anymore.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Mahesh Salgaonkar <mahesh@linux.vnet.ibm.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Michael Neuling <mikey@neuling.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: linuxppc-dev@ozlabs.org
LKML-Reference: <20100105224656.19431.92588.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 arch/x86/include/asm/ptrace.h |    4 ----
 arch/x86/kernel/ptrace.c      |   24 ------------------------
 2 files changed, 0 insertions(+), 28 deletions(-)

diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
index 9d369f6..2010280 100644
--- a/arch/x86/include/asm/ptrace.h
+++ b/arch/x86/include/asm/ptrace.h
@@ -274,10 +274,6 @@ static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs,
 		return 0;
 }
 
-/* Get Nth argument at function call */
-extern unsigned long regs_get_argument_nth(struct pt_regs *regs,
-					   unsigned int n);
-
 /*
  * These are defined as per linux/ptrace.h, which see.
  */
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 017d937..73554a3 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -140,30 +140,6 @@ static const int arg_offs_table[] = {
 #endif
 };
 
-/**
- * regs_get_argument_nth() - get Nth argument at function call
- * @regs:	pt_regs which contains registers at function entry.
- * @n:		argument number.
- *
- * regs_get_argument_nth() returns @n th argument of a function call.
- * Since usually the kernel stack will be changed right after function entry,
- * you must use this at function entry. If the @n th entry is NOT in the
- * kernel stack or pt_regs, this returns 0.
- */
-unsigned long regs_get_argument_nth(struct pt_regs *regs, unsigned int n)
-{
-	if (n < ARRAY_SIZE(arg_offs_table))
-		return *(unsigned long *)((char *)regs + arg_offs_table[n]);
-	else {
-		/*
-		 * The typical case: arg n is on the stack.
-		 * (Note: stack[0] = return address, so skip it)
-		 */
-		n -= ARRAY_SIZE(arg_offs_table);
-		return regs_get_kernel_stack_nth(regs, 1 + n);
-	}
-}
-
 /*
  * does not yet catch signals sent when the child dies.
  * in exit.c or in signal.c.

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

* [tip:perf/core] perf probe: Remove newline from die()
  2010-01-05 22:41 ` [PATCH -tip 4/8] [CLEANUP] perf probe: Remove newline from die() Masami Hiramatsu
@ 2010-01-13 10:36   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:36 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, paulus, acme, hpa, mingo, efault, peterz, fweisbec,
	dle-develop, tglx, mhiramat, mingo, systemtap

Commit-ID:  bbaa46fac6d1c652bfa6282420d36a44bdc53b64
Gitweb:     http://git.kernel.org/tip/bbaa46fac6d1c652bfa6282420d36a44bdc53b64
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:47:03 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:13 +0100

perf probe: Remove newline from die()

Remove newline from die(), because it is automatically added.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100105224703.19431.42475.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 tools/perf/util/probe-finder.c |   12 ++++++------
 1 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 4b852c0..6402798 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -402,11 +402,11 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf)
 	} else if (op == DW_OP_regx) {
 		regn = loc->lr_number;
 	} else
-		die("Dwarf_OP %d is not supported.\n", op);
+		die("Dwarf_OP %d is not supported.", op);
 
 	regs = get_arch_regstr(regn);
 	if (!regs)
-		die("%lld exceeds max register number.\n", regn);
+		die("%lld exceeds max register number.", regn);
 
 	if (deref)
 		ret = snprintf(pf->buf, pf->len,
@@ -438,7 +438,7 @@ static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf)
 	return ;
 error:
 	die("Failed to find the location of %s at this address.\n"
-	    " Perhaps, it has been optimized out.\n", pf->var);
+	    " Perhaps, it has been optimized out.", pf->var);
 }
 
 static int variable_callback(struct die_link *dlink, void *data)
@@ -476,7 +476,7 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf)
 	/* Search child die for local variables and parameters. */
 	ret = search_die_from_children(sp_die, variable_callback, pf);
 	if (!ret)
-		die("Failed to find '%s' in this function.\n", pf->var);
+		die("Failed to find '%s' in this function.", pf->var);
 }
 
 /* Get a frame base on the address */
@@ -602,7 +602,7 @@ static void find_by_line(struct probe_finder *pf)
 		ret = search_die_from_children(pf->cu_die,
 					       probeaddr_callback, pf);
 		if (ret == 0)
-			die("Probe point is not found in subprograms.\n");
+			die("Probe point is not found in subprograms.");
 		/* Continuing, because target line might be inlined. */
 	}
 	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
@@ -661,7 +661,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)
 				    !die_inlined_subprogram(lk->die))
 					goto found;
 			}
-			die("Failed to find real subprogram.\n");
+			die("Failed to find real subprogram.");
 found:
 			/* Get offset from subprogram */
 			ret = die_within_subprogram(lk->die, pf->addr, &offs);

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

* [tip:perf/core] perf tools: Enhance glob string matching
  2010-01-05 22:41 ` [PATCH -tip 7/8] perf tools: Enhance glob string matching Masami Hiramatsu
@ 2010-01-13 10:36   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:36 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, paulus, acme, hpa, mingo, efault, peterz, fweisbec,
	dle-develop, tglx, mhiramat, mingo, systemtap

Commit-ID:  6964cd2c8efe6e048401f1fe3952a06c563c34c1
Gitweb:     http://git.kernel.org/tip/6964cd2c8efe6e048401f1fe3952a06c563c34c1
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:47:24 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:14 +0100

perf tools: Enhance glob string matching

Enhance strglobmatch() for supporting character classes([CHARS],
complementation and ranges are also supported) and escaped
special characters (\*, \? etc).

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100105224724.19431.56271.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 tools/perf/util/string.c |   65 +++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 5352d7d..c397d4f 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -227,16 +227,73 @@ fail:
 	return NULL;
 }
 
-/* Glob expression pattern matching */
+/* Character class matching */
+static bool __match_charclass(const char *pat, char c, const char **npat)
+{
+	bool complement = false, ret = true;
+
+	if (*pat == '!') {
+		complement = true;
+		pat++;
+	}
+	if (*pat++ == c)	/* First character is special */
+		goto end;
+
+	while (*pat && *pat != ']') {	/* Matching */
+		if (*pat == '-' && *(pat + 1) != ']') {	/* Range */
+			if (*(pat - 1) <= c && c <= *(pat + 1))
+				goto end;
+			if (*(pat - 1) > *(pat + 1))
+				goto error;
+			pat += 2;
+		} else if (*pat++ == c)
+			goto end;
+	}
+	if (!*pat)
+		goto error;
+	ret = false;
+
+end:
+	while (*pat && *pat != ']')	/* Searching closing */
+		pat++;
+	if (!*pat)
+		goto error;
+	*npat = pat + 1;
+	return complement ? !ret : ret;
+
+error:
+	return false;
+}
+
+/**
+ * strglobmatch - glob expression pattern matching
+ * @str: the target string to match
+ * @pat: the pattern string to match
+ *
+ * This returns true if the @str matches @pat. @pat can includes wildcards
+ * ('*','?') and character classes ([CHARS], complementation and ranges are
+ * also supported). Also, this supports escape character ('\') to use special
+ * characters as normal character.
+ *
+ * Note: if @pat syntax is broken, this always returns false.
+ */
 bool strglobmatch(const char *str, const char *pat)
 {
 	while (*str && *pat && *pat != '*') {
-		if (*pat == '?') {
+		if (*pat == '?') {	/* Matches any single character */
 			str++;
 			pat++;
-		} else
-			if (*str++ != *pat++)
+			continue;
+		} else if (*pat == '[')	/* Character classes/Ranges */
+			if (__match_charclass(pat + 1, *str, &pat)) {
+				str++;
+				continue;
+			} else
 				return false;
+		else if (*pat == '\\') /* Escaped char match as normal char */
+			pat++;
+		if (*str++ != *pat++)
+			return false;
 	}
 	/* Check wild card */
 	if (*pat == '*') {

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

* [tip:perf/core] perf tools: Support tracepoint glob matching
  2010-01-05 23:23 ` [PATCH -tip 6/8] perf tools: Support tracepoint glob matching Masami Hiramatsu
@ 2010-01-13 10:36   ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:36 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, paulus, acme, hpa, mingo, efault, peterz, fweisbec,
	dle-develop, tglx, mhiramat, mingo, systemtap

Commit-ID:  fb1d2edf7ee25a26ad0b238d0ee335a3b28b7aa3
Gitweb:     http://git.kernel.org/tip/fb1d2edf7ee25a26ad0b238d0ee335a3b28b7aa3
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Tue, 5 Jan 2010 17:47:17 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:13 +0100

perf tools: Support tracepoint glob matching

Support glob wildcard when selecting tracepoint events by -e
option. Without this patch, perf-tools supports 'GROUP:*:record'
syntax for selecting all tracepoints under GROUP group.

With this patch, user can choose tracepoints more flexibly by using
partial wildcards, e.g. 'block:*bio*:record'.

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100105224717.19431.68972.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 tools/perf/util/parse-events.c |   11 ++++++++---
 1 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 609d5a9..05d0c5c 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -450,7 +450,8 @@ parse_single_tracepoint_event(char *sys_name,
 /* sys + ':' + event + ':' + flags*/
 #define MAX_EVOPT_LEN	(MAX_EVENT_LENGTH * 2 + 2 + 128)
 static enum event_result
-parse_subsystem_tracepoint_event(char *sys_name, char *flags)
+parse_multiple_tracepoint_event(char *sys_name, const char *evt_exp,
+				char *flags)
 {
 	char evt_path[MAXPATHLEN];
 	struct dirent *evt_ent;
@@ -474,6 +475,9 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags)
 		    || !strcmp(evt_ent->d_name, "filter"))
 			continue;
 
+		if (!strglobmatch(evt_ent->d_name, evt_exp))
+			continue;
+
 		len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s%s%s", sys_name,
 			       evt_ent->d_name, flags ? ":" : "",
 			       flags ?: "");
@@ -522,9 +526,10 @@ static enum event_result parse_tracepoint_event(const char **strp,
 	if (evt_length >= MAX_EVENT_LENGTH)
 		return EVT_FAILED;
 
-	if (!strcmp(evt_name, "*")) {
+	if (strpbrk(evt_name, "*?")) {
 		*strp = evt_name + evt_length;
-		return parse_subsystem_tracepoint_event(sys_name, flags);
+		return parse_multiple_tracepoint_event(sys_name, evt_name,
+						       flags);
 	} else
 		return parse_single_tracepoint_event(sys_name, evt_name,
 						     evt_length, flags,

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

* [tip:perf/core] perf probe: Support --line option to show probable source-code lines
  2010-01-06 14:39     ` [PATCH -tip] " Masami Hiramatsu
@ 2010-01-13 10:37       ` tip-bot for Masami Hiramatsu
  0 siblings, 0 replies; 20+ messages in thread
From: tip-bot for Masami Hiramatsu @ 2010-01-13 10:37 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, paulus, acme, hpa, mingo, efault, peterz, fweisbec,
	dle-develop, tglx, mhiramat, mingo, systemtap

Commit-ID:  631c9def804b2c92b5cca04fb9ff7b5df9e35094
Gitweb:     http://git.kernel.org/tip/631c9def804b2c92b5cca04fb9ff7b5df9e35094
Author:     Masami Hiramatsu <mhiramat@redhat.com>
AuthorDate: Wed, 6 Jan 2010 09:45:34 -0500
Committer:  Ingo Molnar <mingo@elte.hu>
CommitDate: Wed, 13 Jan 2010 10:09:14 +0100

perf probe: Support --line option to show probable source-code lines

Add --line option to support showing probable source-code lines.

  perf probe --line SRC:LN[-LN|+NUM]
   or
  perf probe --line FUNC[:LN[-LN|+NUM]]

This option shows source-code with line number if the line can
be probed. Lines without line number (and blue color) means that
the line can not be probed, because debuginfo doesn't have the
information of those lines.

The argument specifies the range of lines, "source.c:100-120"
shows lines between 100th to l20th in source.c file. And
"func:10+20" shows 20 lines from 10th line of func function.

e.g.
 # ./perf probe --line kernel/sched.c:1080
 <kernel/sched.c:1080>
          *
          * called with rq->lock held and irqs disabled
          */
         static void hrtick_start(struct rq *rq, u64 delay)
         {
                struct hrtimer *timer = &rq->hrtick_timer;
   1086         ktime_t time = ktime_add_ns(timer->base->get_time(), delay);

                hrtimer_set_expires(timer, time);

   1090         if (rq == this_rq()) {
   1091                 hrtimer_restart(timer);
   1092         } else if (!rq->hrtick_csd_pending) {
   1093                 __smp_call_function_single(cpu_of(rq), &rq->hrtick_csd,
   1094                 rq->hrtick_csd_pending = 1;

If you specifying function name, this shows function-relative
line number.

 # ./perf probe --line schedule
 <schedule:0>
         asmlinkage void __sched schedule(void)
      1  {
                struct task_struct *prev, *next;
                unsigned long *switch_count;
                struct rq *rq;
                int cpu;

         need_resched:
                preempt_disable();
      9         cpu = smp_processor_id();
     10         rq = cpu_rq(cpu);
     11         rcu_sched_qs(cpu);
     12         prev = rq->curr;
     13         switch_count = &prev->nivcsw;

Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: systemtap <systemtap@sources.redhat.com>
Cc: DLE <dle-develop@lists.sourceforge.net>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
LKML-Reference: <20100106144534.27218.77939.stgit@dhcp-100-2-132.bos.redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
---
 tools/perf/Documentation/perf-probe.txt |   20 ++++
 tools/perf/builtin-probe.c              |   76 ++++++++++--
 tools/perf/util/probe-event.c           |  100 ++++++++++++++++
 tools/perf/util/probe-event.h           |    2 +
 tools/perf/util/probe-finder.c          |  191 ++++++++++++++++++++++++++++++-
 tools/perf/util/probe-finder.h          |   31 +++++
 6 files changed, 402 insertions(+), 18 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 250e391..2de3407 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -15,6 +15,8 @@ or
 'perf probe' [options] --del='[GROUP:]EVENT' [...]
 or
 'perf probe' --list
+or
+'perf probe' --line='FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]'
 
 DESCRIPTION
 -----------
@@ -45,6 +47,11 @@ OPTIONS
 --list::
 	List up current probe events.
 
+-L::
+--line=::
+	Show source code lines which can be probed. This needs an argument
+	which specifies a range of the source code.
+
 PROBE SYNTAX
 ------------
 Probe points are defined by following syntax.
@@ -56,6 +63,19 @@ Probe points are defined by following syntax.
 It is also possible to specify a probe point by the source line number by using 'SRC:ALN' syntax, where 'SRC' is the source file path and 'ALN' is the line number.
 'ARG' specifies the arguments of this probe point. You can use the name of local variable, or kprobe-tracer argument format (e.g. $retval, %ax, etc).
 
+LINE SYNTAX
+-----------
+Line range is descripted by following syntax.
+
+ "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]"
+
+FUNC specifies the function name of showing lines. 'RLN' is the start line
+number from function entry line, and 'RLN2' is the end line number. As same as
+probe syntax, 'SRC' means the source file path, 'ALN' is start line number,
+and 'ALN2' is end line number in the file. It is also possible to specify how
+many lines to show by using 'NUM'.
+So, "source.c:100-120" shows lines between 100th to l20th in source.c file. And "func:10+20" shows 20 lines from 10th line of func function.
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1]
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index ffdd3fe..1d3a99e 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -55,11 +55,13 @@ static struct {
 	bool need_dwarf;
 	bool list_events;
 	bool force_add;
+	bool show_lines;
 	int nr_probe;
 	struct probe_point probes[MAX_PROBES];
 	struct strlist *dellist;
 	struct perf_session *psession;
 	struct map *kmap;
+	struct line_range line_range;
 } session;
 
 
@@ -116,6 +118,15 @@ static int opt_del_probe_event(const struct option *opt __used,
 	return 0;
 }
 
+static int opt_show_lines(const struct option *opt __used,
+			  const char *str, int unset __used)
+{
+	if (str)
+		parse_line_range_desc(str, &session.line_range);
+	INIT_LIST_HEAD(&session.line_range.line_list);
+	session.show_lines = true;
+	return 0;
+}
 /* Currently just checking function name from symbol map */
 static void evaluate_probe_point(struct probe_point *pp)
 {
@@ -144,6 +155,7 @@ static const char * const probe_usage[] = {
 	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
 	"perf probe [<options>] --del '[GROUP:]EVENT' ...",
 	"perf probe --list",
+	"perf probe --line 'LINEDESC'",
 	NULL
 };
 
@@ -182,9 +194,32 @@ static const struct option options[] = {
 		opt_add_probe_event),
 	OPT_BOOLEAN('f', "force", &session.force_add, "forcibly add events"
 		    " with existing name"),
+#ifndef NO_LIBDWARF
+	OPT_CALLBACK('L', "line", NULL,
+		     "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
+		     "Show source code lines.", opt_show_lines),
+#endif
 	OPT_END()
 };
 
+/* Initialize symbol maps for vmlinux */
+static void init_vmlinux(void)
+{
+	symbol_conf.sort_by_name = true;
+	if (symbol_conf.vmlinux_name == NULL)
+		symbol_conf.try_vmlinux_path = true;
+	else
+		pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name);
+	if (symbol__init() < 0)
+		die("Failed to init symbol map.");
+	session.psession = perf_session__new(NULL, O_WRONLY, false);
+	if (session.psession == NULL)
+		die("Failed to init perf_session.");
+	session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
+	if (!session.kmap)
+		die("Could not find kernel map.\n");
+}
+
 int cmd_probe(int argc, const char **argv, const char *prefix __used)
 {
 	int i, ret;
@@ -203,7 +238,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 		parse_probe_event_argv(argc, argv);
 	}
 
-	if ((!session.nr_probe && !session.dellist && !session.list_events))
+	if ((!session.nr_probe && !session.dellist && !session.list_events &&
+	     !session.show_lines))
 		usage_with_options(probe_usage, options);
 
 	if (debugfs_valid_mountpoint(debugfs_path) < 0)
@@ -215,10 +251,34 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 				   " --add/--del.\n");
 			usage_with_options(probe_usage, options);
 		}
+		if (session.show_lines) {
+			pr_warning("  Error: Don't use --list with --line.\n");
+			usage_with_options(probe_usage, options);
+		}
 		show_perf_probe_events();
 		return 0;
 	}
 
+#ifndef NO_LIBDWARF
+	if (session.show_lines) {
+		if (session.nr_probe != 0 || session.dellist) {
+			pr_warning("  Error: Don't use --line with"
+				   " --add/--del.\n");
+			usage_with_options(probe_usage, options);
+		}
+		init_vmlinux();
+		fd = open_vmlinux();
+		if (fd < 0)
+			die("Could not open debuginfo file.");
+		ret = find_line_range(fd, &session.line_range);
+		if (ret <= 0)
+			die("Source line is not found.\n");
+		close(fd);
+		show_line_range(&session.line_range);
+		return 0;
+	}
+#endif
+
 	if (session.dellist) {
 		del_trace_kprobe_events(session.dellist);
 		strlist__delete(session.dellist);
@@ -226,18 +286,8 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
 			return 0;
 	}
 
-	/* Initialize symbol maps for vmlinux */
-	symbol_conf.sort_by_name = true;
-	if (symbol_conf.vmlinux_name == NULL)
-		symbol_conf.try_vmlinux_path = true;
-	if (symbol__init() < 0)
-		die("Failed to init symbol map.");
-	session.psession = perf_session__new(NULL, O_WRONLY, false);
-	if (session.psession == NULL)
-		die("Failed to init perf_session.");
-	session.kmap = session.psession->vmlinux_maps[MAP__FUNCTION];
-	if (!session.kmap)
-		die("Could not find kernel map.\n");
+	/* Add probes */
+	init_vmlinux();
 
 	if (session.need_dwarf)
 #ifdef NO_LIBDWARF
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a22141a..71b0dd5 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -38,6 +38,7 @@
 #include "strlist.h"
 #include "debug.h"
 #include "cache.h"
+#include "color.h"
 #include "parse-events.h"  /* For debugfs_path */
 #include "probe-event.h"
 
@@ -63,6 +64,42 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
 	return ret;
 }
 
+void parse_line_range_desc(const char *arg, struct line_range *lr)
+{
+	const char *ptr;
+	char *tmp;
+	/*
+	 * <Syntax>
+	 * SRC:SLN[+NUM|-ELN]
+	 * FUNC[:SLN[+NUM|-ELN]]
+	 */
+	ptr = strchr(arg, ':');
+	if (ptr) {
+		lr->start = (unsigned int)strtoul(ptr + 1, &tmp, 0);
+		if (*tmp == '+')
+			lr->end = lr->start + (unsigned int)strtoul(tmp + 1,
+								    &tmp, 0);
+		else if (*tmp == '-')
+			lr->end = (unsigned int)strtoul(tmp + 1, &tmp, 0);
+		else
+			lr->end = 0;
+		pr_debug("Line range is %u to %u\n", lr->start, lr->end);
+		if (lr->end && lr->start > lr->end)
+			semantic_error("Start line must be smaller"
+				       " than end line.");
+		if (*tmp != '\0')
+			semantic_error("Tailing with invalid character '%d'.",
+				       *tmp);
+		tmp = strndup(arg, (ptr - arg));
+	} else
+		tmp = strdup(arg);
+
+	if (strchr(tmp, '.'))
+		lr->file = tmp;
+	else
+		lr->function = tmp;
+}
+
 /* Check the name is good for event/group */
 static bool check_event_name(const char *name)
 {
@@ -678,3 +715,66 @@ void del_trace_kprobe_events(struct strlist *dellist)
 	close(fd);
 }
 
+#define LINEBUF_SIZE 256
+
+static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
+{
+	char buf[LINEBUF_SIZE];
+	const char *color = PERF_COLOR_BLUE;
+
+	if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+		goto error;
+	if (!skip) {
+		if (show_num)
+			fprintf(stdout, "%7u  %s", l, buf);
+		else
+			color_fprintf(stdout, color, "         %s", buf);
+	}
+
+	while (strlen(buf) == LINEBUF_SIZE - 1 &&
+	       buf[LINEBUF_SIZE - 2] != '\n') {
+		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
+			goto error;
+		if (!skip) {
+			if (show_num)
+				fprintf(stdout, "%s", buf);
+			else
+				color_fprintf(stdout, color, "%s", buf);
+		}
+	}
+	return;
+error:
+	if (feof(fp))
+		die("Source file is shorter than expected.");
+	else
+		die("File read error: %s", strerror(errno));
+}
+
+void show_line_range(struct line_range *lr)
+{
+	unsigned int l = 1;
+	struct line_node *ln;
+	FILE *fp;
+
+	setup_pager();
+
+	if (lr->function)
+		fprintf(stdout, "<%s:%d>\n", lr->function,
+			lr->start - lr->offset);
+	else
+		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);
+
+	fp = fopen(lr->path, "r");
+	if (fp == NULL)
+		die("Failed to open %s: %s", lr->path, strerror(errno));
+	/* Skip to starting line number */
+	while (l < lr->start)
+		show_one_line(fp, l++, true, false);
+
+	list_for_each_entry(ln, &lr->line_list, list) {
+		while (ln->line > l)
+			show_one_line(fp, (l++) - lr->offset, false, false);
+		show_one_line(fp, (l++) - lr->offset, false, true);
+	}
+	fclose(fp);
+}
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 7f1d499..711287d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -5,6 +5,7 @@
 #include "probe-finder.h"
 #include "strlist.h"
 
+extern void parse_line_range_desc(const char *arg, struct line_range *lr);
 extern void parse_perf_probe_event(const char *str, struct probe_point *pp,
 				   bool *need_dwarf);
 extern int synthesize_perf_probe_point(struct probe_point *pp);
@@ -15,6 +16,7 @@ extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes,
 				    bool force_add);
 extern void del_trace_kprobe_events(struct strlist *dellist);
 extern void show_perf_probe_events(void);
+extern void show_line_range(struct line_range *lr);
 
 /* Maximum index number of event-name postfix */
 #define MAX_EVENT_INDEX	1024
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6402798..1b2124d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -140,6 +140,31 @@ static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname)
 	return found;
 }
 
+static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf)
+{
+	Dwarf_Signed cnt, i;
+	char **srcs;
+	int ret = 0;
+
+	if (!buf || !fno)
+		return -EINVAL;
+
+	ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error);
+	if (ret == DW_DLV_OK) {
+		if ((Dwarf_Unsigned)cnt > fno - 1) {
+			*buf = strdup(srcs[fno - 1]);
+			ret = 0;
+			pr_debug("found filename: %s\n", *buf);
+		} else
+			ret = -ENOENT;
+		for (i = 0; i < cnt; i++)
+			dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING);
+		dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST);
+	} else
+		ret = -EINVAL;
+	return ret;
+}
+
 /* Compare diename and tname */
 static int die_compare_name(Dwarf_Die dw_die, const char *tname)
 {
@@ -567,7 +592,7 @@ static int probeaddr_callback(struct die_link *dlink, void *data)
 }
 
 /* Find probe point from its line number */
-static void find_by_line(struct probe_finder *pf)
+static void find_probe_point_by_line(struct probe_finder *pf)
 {
 	Dwarf_Signed cnt, i, clm;
 	Dwarf_Line *lines;
@@ -626,7 +651,7 @@ static int probefunc_callback(struct die_link *dlink, void *data)
 				pf->fno = die_get_decl_file(dlink->die);
 				pf->lno = die_get_decl_line(dlink->die)
 					 + pp->line;
-				find_by_line(pf);
+				find_probe_point_by_line(pf);
 				return 1;
 			}
 			if (die_inlined_subprogram(dlink->die)) {
@@ -673,7 +698,7 @@ found:
 	return 0;
 }
 
-static void find_by_func(struct probe_finder *pf)
+static void find_probe_point_by_func(struct probe_finder *pf)
 {
 	search_die_from_children(pf->cu_die, probefunc_callback, pf);
 }
@@ -714,10 +739,10 @@ int find_probepoint(int fd, struct probe_point *pp)
 			if (ret == DW_DLV_NO_ENTRY)
 				pf.cu_base = 0;
 			if (pp->function)
-				find_by_func(&pf);
+				find_probe_point_by_func(&pf);
 			else {
 				pf.lno = pp->line;
-				find_by_line(&pf);
+				find_probe_point_by_line(&pf);
 			}
 		}
 		dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE);
@@ -728,3 +753,159 @@ int find_probepoint(int fd, struct probe_point *pp)
 	return pp->found;
 }
 
+
+static void line_range_add_line(struct line_range *lr, unsigned int line)
+{
+	struct line_node *ln;
+	struct list_head *p;
+
+	/* Reverse search, because new line will be the last one */
+	list_for_each_entry_reverse(ln, &lr->line_list, list) {
+		if (ln->line < line) {
+			p = &ln->list;
+			goto found;
+		} else if (ln->line == line)	/* Already exist */
+			return ;
+	}
+	/* List is empty, or the smallest entry */
+	p = &lr->line_list;
+found:
+	pr_debug("Debug: add a line %u\n", line);
+	ln = zalloc(sizeof(struct line_node));
+	DIE_IF(ln == NULL);
+	ln->line = line;
+	INIT_LIST_HEAD(&ln->list);
+	list_add(&ln->list, p);
+}
+
+/* Find line range from its line number */
+static void find_line_range_by_line(struct line_finder *lf)
+{
+	Dwarf_Signed cnt, i;
+	Dwarf_Line *lines;
+	Dwarf_Unsigned lineno = 0;
+	Dwarf_Unsigned fno;
+	Dwarf_Addr addr;
+	int ret;
+
+	ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+
+	for (i = 0; i < cnt; i++) {
+		ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (fno != lf->fno)
+			continue;
+
+		ret = dwarf_lineno(lines[i], &lineno, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+		if (lf->lno_s > lineno || lf->lno_e < lineno)
+			continue;
+
+		/* Filter line in the function address range */
+		if (lf->addr_s && lf->addr_e) {
+			ret = dwarf_lineaddr(lines[i], &addr, &__dw_error);
+			DIE_IF(ret != DW_DLV_OK);
+			if (lf->addr_s > addr || lf->addr_e <= addr)
+				continue;
+		}
+		line_range_add_line(lf->lr, (unsigned int)lineno);
+	}
+	dwarf_srclines_dealloc(__dw_debug, lines, cnt);
+	if (!list_empty(&lf->lr->line_list))
+		lf->found = 1;
+}
+
+/* Search function from function name */
+static int linefunc_callback(struct die_link *dlink, void *data)
+{
+	struct line_finder *lf = (struct line_finder *)data;
+	struct line_range *lr = lf->lr;
+	Dwarf_Half tag;
+	int ret;
+
+	ret = dwarf_tag(dlink->die, &tag, &__dw_error);
+	DIE_IF(ret == DW_DLV_ERROR);
+	if (tag == DW_TAG_subprogram &&
+	    die_compare_name(dlink->die, lr->function) == 0) {
+		/* Get the address range of this function */
+		ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error);
+		if (ret == DW_DLV_OK)
+			ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY) {
+			lf->addr_s = 0;
+			lf->addr_e = 0;
+		}
+
+		lf->fno = die_get_decl_file(dlink->die);
+		lr->offset = die_get_decl_line(dlink->die);;
+		lf->lno_s = lr->offset + lr->start;
+		if (!lr->end)
+			lf->lno_e = (Dwarf_Unsigned)-1;
+		else
+			lf->lno_e = lr->offset + lr->end;
+		lr->start = lf->lno_s;
+		lr->end = lf->lno_e;
+		find_line_range_by_line(lf);
+		/* If we find a target function, this should be end. */
+		lf->found = 1;
+		return 1;
+	}
+	return 0;
+}
+
+static void find_line_range_by_func(struct line_finder *lf)
+{
+	search_die_from_children(lf->cu_die, linefunc_callback, lf);
+}
+
+int find_line_range(int fd, struct line_range *lr)
+{
+	Dwarf_Half addr_size = 0;
+	Dwarf_Unsigned next_cuh = 0;
+	int ret;
+	struct line_finder lf = {.lr = lr};
+
+	ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error);
+	if (ret != DW_DLV_OK)
+		return -ENOENT;
+
+	while (!lf.found) {
+		/* Search CU (Compilation Unit) */
+		ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL,
+			&addr_size, &next_cuh, &__dw_error);
+		DIE_IF(ret == DW_DLV_ERROR);
+		if (ret == DW_DLV_NO_ENTRY)
+			break;
+
+		/* Get the DIE(Debugging Information Entry) of this CU */
+		ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error);
+		DIE_IF(ret != DW_DLV_OK);
+
+		/* Check if target file is included. */
+		if (lr->file)
+			lf.fno = cu_find_fileno(lf.cu_die, lr->file);
+
+		if (!lr->file || lf.fno) {
+			if (lr->function)
+				find_line_range_by_func(&lf);
+			else {
+				lf.lno_s = lr->start;
+				if (!lr->end)
+					lf.lno_e = (Dwarf_Unsigned)-1;
+				else
+					lf.lno_e = lr->end;
+				find_line_range_by_line(&lf);
+			}
+			/* Get the real file path */
+			if (lf.found)
+				cu_get_filename(lf.cu_die, lf.fno, &lr->path);
+		}
+		dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE);
+	}
+	ret = dwarf_finish(__dw_debug, &__dw_error);
+	DIE_IF(ret != DW_DLV_OK);
+	return lf.found;
+}
+
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index e3f3968..972b386 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -34,8 +34,26 @@ struct probe_point {
 	char			*probes[MAX_PROBES];	/* Output buffers (will be allocated)*/
 };
 
+/* Line number container */
+struct line_node {
+	struct list_head	list;
+	unsigned int		line;
+};
+
+/* Line range */
+struct line_range {
+	char			*file;			/* File name */
+	char			*function;		/* Function name */
+	unsigned int		start;			/* Start line number */
+	unsigned int		end;			/* End line number */
+	unsigned int		offset;			/* Start line offset */
+	char			*path;			/* Real path name */
+	struct list_head	line_list;		/* Visible lines */
+};
+
 #ifndef NO_LIBDWARF
 extern int find_probepoint(int fd, struct probe_point *pp);
+extern int find_line_range(int fd, struct line_range *lr);
 
 /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */
 #ifndef _MIPS_SZLONG
@@ -62,6 +80,19 @@ struct probe_finder {
 	char			*buf;			/* Current output buffer */
 	int			len;			/* Length of output buffer */
 };
+
+struct line_finder {
+	struct line_range	*lr;			/* Target line range */
+
+	Dwarf_Unsigned		fno;			/* File number */
+	Dwarf_Unsigned		lno_s;			/* Start line number */
+	Dwarf_Unsigned		lno_e;			/* End line number */
+	Dwarf_Addr		addr_s;			/* Start address */
+	Dwarf_Addr		addr_e;			/* End address */
+	Dwarf_Die		cu_die;			/* Current CU */
+	int			found;
+};
+
 #endif /* NO_LIBDWARF */
 
 #endif /*_PROBE_FINDER_H */

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

end of thread, other threads:[~2010-01-13 10:37 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-01-05 22:40 [PATCH -tip 0/8] perf-probe updates Masami Hiramatsu
2010-01-05 22:40 ` [PATCH -tip 3/8] x86/ptrace: Remove unused regs_get_argument_nth API Masami Hiramatsu
2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:40 ` [PATCH -tip 2/8] tracing/kprobe: Drop function argument access syntax Masami Hiramatsu
2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:40 ` [PATCH -tip 1/8] tracing/kprobe: Update example output in documentation Masami Hiramatsu
2010-01-06 18:40   ` Steven Rostedt
2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:40 ` [PATCH -tip 5/8] perf probe: Show probe list in pager Masami Hiramatsu
2010-01-13 10:35   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:41 ` [PATCH -tip 8/8] perf probe: Support --line option to show probable source-code lines Masami Hiramatsu
2010-01-06 14:31   ` Masami Hiramatsu
2010-01-06 14:39     ` [PATCH -tip] " Masami Hiramatsu
2010-01-13 10:37       ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:41 ` [PATCH -tip 4/8] [CLEANUP] perf probe: Remove newline from die() Masami Hiramatsu
2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 22:41 ` [PATCH -tip 7/8] perf tools: Enhance glob string matching Masami Hiramatsu
2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu
2010-01-05 23:23 ` [PATCH -tip 6/8] perf tools: Support tracepoint glob matching Masami Hiramatsu
2010-01-13 10:36   ` [tip:perf/core] " tip-bot for Masami Hiramatsu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).