From: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Ingo Molnar <mingo@kernel.org>
Cc: linux-kernel@vger.kernel.org,
Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Hemant Kumar <hemant@linux.vnet.ibm.com>,
Peter Zijlstra <peterz@infradead.org>,
Wang Nan <wangnan0@huawei.com>,
Yauheni Kaliuta <yauheni.kaliuta@redhat.com>,
Arnaldo Carvalho de Melo <acme@redhat.com>
Subject: [PATCH 18/19] perf uprobe: Skip prologue if program compiled without optimization
Date: Thu, 1 Sep 2016 13:45:44 -0300 [thread overview]
Message-ID: <1472748345-23417-19-git-send-email-acme@kernel.org> (raw)
In-Reply-To: <1472748345-23417-1-git-send-email-acme@kernel.org>
From: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
The function prologue prepares stack and registers before executing
function logic.
When target program is compiled without optimization, function parameter
information is only valid after the prologue.
When we probe entrypc of the function, and try to record a function
parameter, it contains a garbage value.
For example:
$ vim test.c
#include <stdio.h>
void foo(int i)
{
printf("i: %d\n", i);
}
int main()
{
foo(42);
return 0;
}
$ gcc -g test.c -o test
$ objdump -dl test | less
foo():
/home/ravi/test.c:4
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub -bashx10,%rsp
40053e: 89 7d fc mov %edi,-0x4(%rbp)
/home/ravi/test.c:5
400541: 8b 45 fc mov -0x4(%rbp),%eax
...
...
main():
/home/ravi/test.c:9
400558: 55 push %rbp
400559: 48 89 e5 mov %rsp,%rbp
/home/ravi/test.c:10
40055c: bf 2a 00 00 00 mov -bashx2a,%edi
400561: e8 d0 ff ff ff callq 400536 <foo>
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000536 i=-12(%sp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 5778 [001] 4918.562027: probe_test:foo: (400536) i=0
Here variable 'i' is passed via stack which is pushed on stack at
0x40053e. But we are probing at 0x400536.
To resolve this issues, we need to probe on next instruction after
prologue. gdb and systemtap also does same thing. I've implemented this
patch based on approach systemtap has used.
After applying patch:
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000541 i=-4(%bp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 6300 [001] 5877.879327: probe_test:foo: (400541) i=42
No need to skip prologue for optimized case since debug info is correct
for each instructions for -O2 -g. For more details please visit:
https://bugzilla.redhat.com/show_bug.cgi?id=612253#c6
Changes in v2:
- Skipping prologue only when any ARG is either C variable, $params or
$vars.
- Probe on line(:1) may not be always possible. Recommend only address
to force probe on function entry.
Committer notes:
Testing it with 'perf trace':
# perf probe -x ./test foo i
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000526 i=-12(%sp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400526) i=0)
#
After the patch:
# perf probe -d *:*
Removed event: probe_test:foo
# perf probe -x ./test foo i
Target program is compiled without optimization. Skipping prologue.
Probe on address 0x400526 to force probing at the function entry.
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000531 i=-4(%bp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400531) i=42)
#
Reported-by: Michael Petlan <mpetlan@redhat.com>
Report-Link: https://www.mail-archive.com/linux-perf-users@vger.kernel.org/msg02348.html
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1299021
Link: http://lkml.kernel.org/r/1470214725-5023-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
[ Rename 'die' to 'cu_die' to avoid shadowing a die() definition on at least centos 5, Debian 7 and ubuntu:12.04.5]
[ Use PRIx64 instead of lx to format a Dwarf_Addr, aka long long unsigned int, fixing the build on 32-bit systems ]
[ dwarf_getsrclines() expects a size_t * argument ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
tools/perf/util/probe-finder.c | 165 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 508b61c6e5fb..003ecadae35d 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -907,6 +907,170 @@ static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
}
+static bool var_has_loclist(Dwarf_Die *cu_die)
+{
+ Dwarf_Attribute loc;
+ int tag = dwarf_tag(cu_die);
+
+ if (tag != DW_TAG_formal_parameter &&
+ tag != DW_TAG_variable)
+ return false;
+
+ return (dwarf_attr_integrate(cu_die, DW_AT_location, &loc) &&
+ dwarf_whatform(&loc) == DW_FORM_sec_offset);
+}
+
+/*
+ * For any object in given CU whose DW_AT_location is a location list,
+ * target program is compiled with optimization.
+ */
+static bool optimized_target(Dwarf_Die *cu_die)
+{
+ Dwarf_Die tmp_die;
+
+ if (var_has_loclist(cu_die))
+ return true;
+
+ if (!dwarf_child(cu_die, &tmp_die) && optimized_target(&tmp_die))
+ return true;
+
+ if (!dwarf_siblingof(cu_die, &tmp_die) && optimized_target(&tmp_die))
+ return true;
+
+ return false;
+}
+
+static bool get_entrypc_idx(Dwarf_Lines *lines, unsigned long nr_lines,
+ Dwarf_Addr pf_addr, unsigned long *entrypc_idx)
+{
+ unsigned long i;
+ Dwarf_Addr addr;
+
+ for (i = 0; i < nr_lines; i++) {
+ if (dwarf_lineaddr(dwarf_onesrcline(lines, i), &addr))
+ return false;
+
+ if (addr == pf_addr) {
+ *entrypc_idx = i;
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool get_postprologue_addr(unsigned long entrypc_idx,
+ Dwarf_Lines *lines,
+ unsigned long nr_lines,
+ Dwarf_Addr highpc,
+ Dwarf_Addr *postprologue_addr)
+{
+ unsigned long i;
+ int entrypc_lno, lno;
+ Dwarf_Line *line;
+ Dwarf_Addr addr;
+ bool p_end;
+
+ /* entrypc_lno is actual source line number */
+ line = dwarf_onesrcline(lines, entrypc_idx);
+ if (dwarf_lineno(line, &entrypc_lno))
+ return false;
+
+ for (i = entrypc_idx; i < nr_lines; i++) {
+ line = dwarf_onesrcline(lines, i);
+
+ if (dwarf_lineaddr(line, &addr) ||
+ dwarf_lineno(line, &lno) ||
+ dwarf_lineprologueend(line, &p_end))
+ return false;
+
+ /* highpc is exclusive. [entrypc,highpc) */
+ if (addr >= highpc)
+ break;
+
+ /* clang supports prologue-end marker */
+ if (p_end)
+ break;
+
+ /* Actual next line in source */
+ if (lno != entrypc_lno)
+ break;
+
+ /*
+ * Single source line can have multiple line records.
+ * For Example,
+ * void foo() { printf("hello\n"); }
+ * contains two line records. One points to declaration and
+ * other points to printf() line. Variable 'lno' won't get
+ * incremented in this case but 'i' will.
+ */
+ if (i != entrypc_idx)
+ break;
+ }
+
+ dwarf_lineaddr(line, postprologue_addr);
+ if (*postprologue_addr >= highpc)
+ dwarf_lineaddr(dwarf_onesrcline(lines, i - 1),
+ postprologue_addr);
+
+ return true;
+}
+
+static void __skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ size_t nr_lines = 0;
+ unsigned long entrypc_idx = 0;
+ Dwarf_Lines *lines = NULL;
+ Dwarf_Addr postprologue_addr;
+ Dwarf_Addr highpc;
+
+ if (dwarf_highpc(sp_die, &highpc))
+ return;
+
+ if (dwarf_getsrclines(&pf->cu_die, &lines, &nr_lines))
+ return;
+
+ if (!get_entrypc_idx(lines, nr_lines, pf->addr, &entrypc_idx))
+ return;
+
+ if (!get_postprologue_addr(entrypc_idx, lines, nr_lines,
+ highpc, &postprologue_addr))
+ return;
+
+ pf->addr = postprologue_addr;
+}
+
+static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf)
+{
+ struct perf_probe_point *pp = &pf->pev->point;
+
+ /* Not uprobe? */
+ if (!pf->pev->uprobes)
+ return;
+
+ /* Compiled with optimization? */
+ if (optimized_target(&pf->cu_die))
+ return;
+
+ /* Don't know entrypc? */
+ if (!pf->addr)
+ return;
+
+ /* Only FUNC and FUNC@SRC are eligible. */
+ if (!pp->function || pp->line || pp->retprobe || pp->lazy_line ||
+ pp->offset || pp->abs_address)
+ return;
+
+ /* Not interested in func parameter? */
+ if (!perf_probe_with_var(pf->pev))
+ return;
+
+ pr_info("Target program is compiled without optimization. Skipping prologue.\n"
+ "Probe on address 0x%" PRIx64 " to force probing at the function entry.\n\n",
+ pf->addr);
+
+ __skip_prologue(sp_die, pf);
+}
+
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
{
struct probe_finder *pf = data;
@@ -969,6 +1133,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
if (pp->lazy_line)
param->retval = find_probe_point_lazy(sp_die, pf);
else {
+ skip_prologue(sp_die, pf);
pf->addr += pp->offset;
/* TODO: Check the address in this function */
param->retval = call_probe_finder(sp_die, pf);
--
2.7.4
next prev parent reply other threads:[~2016-09-01 16:47 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-09-01 16:45 [GIT PULL 00/19] perf/core improvements and fixes Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 01/19] perf probe: Remove unused tracing_dir variable Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 02/19] perf tools: Fix error handling of lzma decompression Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 06/19] perf symbols: Demangle symbols for synthesized @plt entries Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 07/19] perf config: Show default report configuration in example and docs Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 08/19] perf probe: Show trace event definition Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 12/19] perf test vmlinux: Clarify which -v lines are errors or warning Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 13/19] perf test vmlinux: Avoid printing headers for empty lists Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 14/19] perf test vmlinux: Tolerate symbol aliases Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 15/19] perf symbols: Check symbol_conf.allow_aliases for kallsyms loading too Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 16/19] perf symbols: Fixup symbol sizes before picking best ones Arnaldo Carvalho de Melo
2016-09-01 16:45 ` [PATCH 17/19] perf probe: Add helper function to check if probe with variable Arnaldo Carvalho de Melo
2016-09-01 16:45 ` Arnaldo Carvalho de Melo [this message]
2016-09-01 16:45 ` [PATCH 19/19] perf probe: Move dwarf specific functions to dwarf-aux.c Arnaldo Carvalho de Melo
2016-09-05 13:16 ` [GIT PULL 00/19] perf/core improvements and fixes Ingo Molnar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1472748345-23417-19-git-send-email-acme@kernel.org \
--to=acme@kernel.org \
--cc=acme@redhat.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=hemant@linux.vnet.ibm.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=peterz@infradead.org \
--cc=ravi.bangoria@linux.vnet.ibm.com \
--cc=wangnan0@huawei.com \
--cc=yauheni.kaliuta@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.