2015-05-24 08:28:46

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 00/15] perf bpf: Probing with local variable

This is the 2nd version of perf bpf: Probing with local variable,
original discusions: https://lkml.org/lkml/2015/5/5/260.

Patches based on https://lkml.org/lkml/2015/5/17/84 (perf tools:
introduce 'perf bpf' command to load eBPF programs).

v1-v2:

- Copy bpf byte-code related headers to tools/lib/bpf.

- Combine pt_regs offset to arch_regs_table, and add
calling_regs_table.

- Support $params without debuginfo and use the same keyword for
generating bpf prologue without debuginfo.

- Save bpf prologue intermediate result to stack, so we can use the
4 regs(R2,R3,R4,R5) for variable passing according to bpf calling
convention.

- Move bpf prologue code to tools/lib/bpf.

- Sample codes in patch instead of comments.

- Fix code style problems mentioned by Alexei and Masami.

Thanks.

He Kuang (15):
perf tools: Add lib/bpf to cscope target list
perf bpf: Support custom vmlinux path
perf bpf: Save pt_regs info from debuginfo
perf tools: Add functions to get calling regs
perf tools: Add pt_regs offsets and calling regs for x86
bpf tools: Add headers for generating bpf bytecode
bpf tools: Convert arglist to bpf prologue
bpf tools: Fetch calling regs to bpf arglist
perf probe: Support $params without debuginfo
perf bpf: Process debuginfo for generating bpf prologue
perf bpf: Synthesize vars to generate bpf prologue
perf bpf: Generate bpf prologue without debuginfo
perf bpf: Combine bpf prologue and bpf prog
samples/bpf: Add sample for testing bpf fetch args
samples/bpf: Add sample for no-debuginfo case

samples/bpf/Makefile | 2 +
samples/bpf/sample_bpf_fetch_args.c | 62 +++++++
samples/bpf/sample_bpf_fetch_args_without_debug.c | 49 ++++++
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/bpf.h | 187 +++++++++++++++++++++
tools/lib/bpf/gen_prologue.c | 127 ++++++++++++++
tools/lib/bpf/libbpf.c | 27 +++
tools/lib/bpf/libbpf.h | 14 ++
tools/perf/Makefile.perf | 2 +-
tools/perf/arch/x86/util/dwarf-regs.c | 100 ++++++++---
tools/perf/builtin-bpf.c | 3 +
tools/perf/util/bpf-loader.c | 27 +++
tools/perf/util/include/dwarf-regs.h | 15 ++
tools/perf/util/probe-event.c | 121 ++++++++++++++
tools/perf/util/probe-event.h | 3 +
tools/perf/util/probe-finder.c | 193 ++++++++++++++++++++++
tools/perf/util/probe-finder.h | 4 +
17 files changed, 915 insertions(+), 23 deletions(-)
create mode 100644 samples/bpf/sample_bpf_fetch_args.c
create mode 100644 samples/bpf/sample_bpf_fetch_args_without_debug.c
create mode 100644 tools/lib/bpf/gen_prologue.c

--
1.8.5.2


2015-05-24 08:28:40

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 01/15] perf tools: Add lib/bpf to cscope target list

Add lib/bpf folder to cscope list.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/Makefile.perf | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index c69821c..7b77d14 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -444,7 +444,7 @@ INSTALL_DOC_TARGETS += quick-install-doc quick-install-man quick-install-html
$(DOC_TARGETS):
$(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) $(@:doc=all)

-TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol
+TAG_FOLDERS= . ../lib/traceevent ../lib/api ../lib/symbol ../lib/bpf
TAG_FILES= ../../include/uapi/linux/perf_event.h

TAGS:
--
1.8.5.2

2015-05-24 08:30:25

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 02/15] perf bpf: Support custom vmlinux path

Make bpf command support -k option, when inserting probe point which
needs debuginfo, a customized vmlinux path can be specified.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/builtin-bpf.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/tools/perf/builtin-bpf.c b/tools/perf/builtin-bpf.c
index 4ef294a..9ea34b3 100644
--- a/tools/perf/builtin-bpf.c
+++ b/tools/perf/builtin-bpf.c
@@ -11,6 +11,7 @@
#include "builtin.h"
#include "perf.h"
#include "debug.h"
+#include "util/symbol.h"
#include "parse-options.h"
#include "bpf-loader.h"

@@ -30,6 +31,8 @@ static struct bpf_cmd bpf_cmds[];
struct option bpf_options[] = {
OPT_INCR('v', "verbose", &verbose, "be more verbose "
"(show debug information)"),
+ OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
+ "file", "vmlinux pathname"),
OPT_END()
};

--
1.8.5.2

2015-05-24 08:30:14

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 03/15] perf bpf: Save pt_regs info from debuginfo

Save reg number in function convert_variable_location() instead of the
register string name, so we can fetch the target register from bpf
context register later.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/include/dwarf-regs.h | 13 +++++++++++++
tools/perf/util/probe-event.h | 1 +
tools/perf/util/probe-finder.c | 11 +++++++++++
3 files changed, 25 insertions(+)

diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 8f14965..566ff6d 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -2,7 +2,20 @@
#define _PERF_DWARF_REGS_H_

#ifdef HAVE_DWARF_SUPPORT
+struct arch_regs_info {
+ const char *name; /* Architecture dependent register string */
+ int offset; /* Reg offset in struct pt_regs */
+ int size; /* Reg size */
+};
+
+#define ARCH_REGS_INFO(r, pt_reg_name) \
+ {.name = r, \
+ .offset = offsetof(struct pt_regs, pt_reg_name), \
+ .size = sizeof(((struct pt_regs *)0)->pt_reg_name)} \
+
const char *get_arch_regstr(unsigned int n);
+int get_arch_reg_offset(unsigned int n);
+int get_arch_reg_size(unsigned int n);
#endif

#endif
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index d6b7834..6c19395 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -29,6 +29,7 @@ struct probe_trace_arg {
char *value; /* Base value */
char *type; /* Type name */
struct probe_trace_arg_ref *ref; /* Referencing offset */
+ unsigned int regn; /* Regn from dwarf */
};

/* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index ee27b74..681af00 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -159,6 +159,16 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
return ref;
}

+int __attribute__ ((weak))
+get_arch_reg_offset(unsigned int n __maybe_unused) {
+ return -1;
+}
+
+int __attribute__ ((weak))
+get_arch_reg_size(unsigned int n __maybe_unused) {
+ return -1;
+}
+
/*
* Convert a location into trace_arg.
* If tvar == NULL, this just checks variable can be converted.
@@ -260,6 +270,7 @@ static_var:
return -ERANGE;
}

+ tvar->regn = regn;
tvar->value = strdup(regs);
if (tvar->value == NULL)
return -ENOMEM;
--
1.8.5.2

2015-05-24 08:30:22

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 04/15] perf tools: Add functions to get calling regs

For generating function formal parameters without debuginfo, add
function to get names and offsets of architecture dependent calling
regs.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/include/dwarf-regs.h | 2 ++
tools/perf/util/probe-finder.c | 10 ++++++++++
2 files changed, 12 insertions(+)

diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
index 566ff6d..dc02243 100644
--- a/tools/perf/util/include/dwarf-regs.h
+++ b/tools/perf/util/include/dwarf-regs.h
@@ -16,6 +16,8 @@ struct arch_regs_info {
const char *get_arch_regstr(unsigned int n);
int get_arch_reg_offset(unsigned int n);
int get_arch_reg_size(unsigned int n);
+const char *get_arch_calling_reg_str(unsigned int n);
+int get_arch_calling_reg_offset(unsigned int n);
#endif

#endif
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 681af00..4de7649 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -169,6 +169,16 @@ get_arch_reg_size(unsigned int n __maybe_unused) {
return -1;
}

+const char __attribute__ ((weak))
+*get_arch_calling_reg_str(unsigned int n __maybe_unused) {
+ return NULL;
+}
+
+int __attribute__ ((weak))
+get_arch_calling_reg_offset(unsigned int n __maybe_unused) {
+ return -1;
+}
+
/*
* Convert a location into trace_arg.
* If tvar == NULL, this just checks variable can be converted.
--
1.8.5.2

2015-05-24 08:30:20

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 05/15] perf tools: Add pt_regs offsets and calling regs for x86

Combine pt_regs offset of each registers into arch_regs_table. And add
architecture dependent calling_regs_table.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/arch/x86/util/dwarf-regs.c | 100 +++++++++++++++++++++++++++-------
1 file changed, 79 insertions(+), 21 deletions(-)

diff --git a/tools/perf/arch/x86/util/dwarf-regs.c b/tools/perf/arch/x86/util/dwarf-regs.c
index be22dd4..c7f2309 100644
--- a/tools/perf/arch/x86/util/dwarf-regs.c
+++ b/tools/perf/arch/x86/util/dwarf-regs.c
@@ -22,54 +22,112 @@

#include <stddef.h>
#include <dwarf-regs.h>
+#include <string.h>
+#include <linux/ptrace.h>

/*
* Generic dwarf analysis helpers
*/

+#ifndef __x86_64__
#define X86_32_MAX_REGS 8
-const char *x86_32_regs_table[X86_32_MAX_REGS] = {
- "%ax",
+const struct arch_regs_info x86_32_regs_table[X86_32_MAX_REGS] = {
+ ARCH_REGS_INFO("%ax", eax),
+ ARCH_REGS_INFO("%cx", ecx),
+ ARCH_REGS_INFO("%dx", edx),
+ ARCH_REGS_INFO("%bx", ebx),
+ ARCH_REGS_INFO("$stack", esp), /* Stack address instead of %sp */
+ ARCH_REGS_INFO("%bp", ebp),
+ ARCH_REGS_INFO("%si", esi),
+ ARCH_REGS_INFO("%di", edi),
+};
+
+#define X86_32_MAX_CALLING_REGS (2)
+const char *x86_32_calling_regs_table[] = {
"%cx",
"%dx",
- "%bx",
- "$stack", /* Stack address instead of %sp */
- "%bp",
- "%si",
- "%di",
};

+#else
#define X86_64_MAX_REGS 16
-const char *x86_64_regs_table[X86_64_MAX_REGS] = {
- "%ax",
+const struct arch_regs_info x86_64_regs_table[X86_64_MAX_REGS] = {
+ ARCH_REGS_INFO("%ax", rax),
+ ARCH_REGS_INFO("%dx", rdx),
+ ARCH_REGS_INFO("%cx", rcx),
+ ARCH_REGS_INFO("%bx", rbx),
+ ARCH_REGS_INFO("%si", rsi),
+ ARCH_REGS_INFO("%di", rdi),
+ ARCH_REGS_INFO("%bp", rbp),
+ ARCH_REGS_INFO("%sp", rsp),
+ ARCH_REGS_INFO("%r8", r8),
+ ARCH_REGS_INFO("%r9", r9),
+ ARCH_REGS_INFO("%r10", r10),
+ ARCH_REGS_INFO("%r11", r11),
+ ARCH_REGS_INFO("%r12", r12),
+ ARCH_REGS_INFO("%r13", r13),
+ ARCH_REGS_INFO("%r14", r14),
+ ARCH_REGS_INFO("%r15", r15),
+};
+
+#define X86_64_MAX_CALLING_REGS (6)
+const char *x86_64_calling_regs_table[] = {
+ "%di",
+ "%si",
"%dx",
"%cx",
- "%bx",
- "%si",
- "%di",
- "%bp",
- "%sp",
"%r8",
"%r9",
- "%r10",
- "%r11",
- "%r12",
- "%r13",
- "%r14",
- "%r15",
};

+#endif
+
/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define ARCH_MAX_REGS X86_64_MAX_REGS
#define arch_regs_table x86_64_regs_table
+#define ARCH_MAX_CALLING_REGS X86_64_MAX_CALLING_REGS
+#define arch_calling_regs_table x86_64_calling_regs_table
#else
#define ARCH_MAX_REGS X86_32_MAX_REGS
#define arch_regs_table x86_32_regs_table
+#define ARCH_MAX_CALLING_REGS X86_32_MAX_CALLING_REGS
+#define arch_calling_regs_table x86_32_calling_regs_table
#endif

/* Return architecture dependent register string (for kprobe-tracer) */
const char *get_arch_regstr(unsigned int n)
{
- return (n <= ARCH_MAX_REGS) ? arch_regs_table[n] : NULL;
+ return (n < ARCH_MAX_REGS) ? arch_regs_table[n].name : NULL;
+}
+
+int get_arch_reg_offset(unsigned int n)
+{
+ return (n < ARCH_MAX_REGS) ? arch_regs_table[n].offset : -1;
+}
+
+int get_arch_reg_size(unsigned int n)
+{
+ return (n < ARCH_MAX_REGS) ? arch_regs_table[n].size : -1;
+}
+
+const char *get_arch_calling_reg_str(unsigned int n)
+{
+ return (n < ARCH_MAX_CALLING_REGS) ?
+ arch_calling_regs_table[n] : NULL;
+}
+
+int get_arch_calling_reg_offset(unsigned int n)
+{
+ int i;
+
+ if (n >= ARCH_MAX_CALLING_REGS)
+ return -1;
+
+ for (i = 0; i < ARCH_MAX_REGS; i++) {
+ if (!strcmp(arch_calling_regs_table[n],
+ arch_regs_table[i].name))
+ return arch_regs_table[i].offset;
+ }
+
+ return -1;
}
--
1.8.5.2

2015-05-24 08:30:06

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 06/15] bpf tools: Add headers for generating bpf bytecode

Copying bpf instruction macros from sample/bpf/libbpf.h, and register
alias from include/linux/ftrace.h, so we can generating bpf bytecode by
using these macros.

Signed-off-by: He Kuang <[email protected]>
---
tools/lib/bpf/bpf.h | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 187 insertions(+)

diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index fb2a613..7e3e83a 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -21,4 +21,191 @@ int bpf_load_program(enum bpf_prog_type type, struct bpf_insn *insns,
u32 kern_version, char *log_buf,
size_t log_buf_sz);

+/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
+
+#define BPF_ALU64_REG(OP, DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+#define BPF_ALU32_REG(OP, DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
+
+#define BPF_ALU64_IMM(OP, DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+#define BPF_ALU32_IMM(OP, DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Short form of mov, dst_reg = src_reg */
+
+#define BPF_MOV64_REG(DST, SRC) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = 0 })
+
+/* Short form of mov, dst_reg = imm32 */
+
+#define BPF_MOV64_IMM(DST, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ALU64 | BPF_MOV | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
+#define BPF_LD_IMM64(DST, IMM) \
+ BPF_LD_IMM64_RAW(DST, 0, IMM)
+
+#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
+ (((struct bpf_insn) { \
+ .code = BPF_LD | BPF_DW | BPF_IMM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = 0, \
+ .imm = (__u32) (IMM) }), \
+ ((struct bpf_insn) { \
+ .code = 0, /* zero is reserved opcode */ \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = ((__u64) (IMM)) >> 32 }))
+
+#ifndef BPF_PSEUDO_MAP_FD
+# define BPF_PSEUDO_MAP_FD 1
+#endif
+
+/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
+#define BPF_LD_MAP_FD(DST, MAP_FD) \
+ BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
+
+
+/* Direct packet access, R0 = *(uint *) (skb->data + imm32) */
+
+#define BPF_LD_ABS(SIZE, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = IMM })
+
+/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
+
+#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
+
+#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
+
+#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
+ ((struct bpf_insn) { \
+ .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Conditional jumps against registers,
+ if (dst_reg 'op' src_reg) goto pc + off16 */
+
+#define BPF_JMP_REG(OP, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
+/* Conditional jumps against immediates,
+ if (dst_reg 'op' imm32) goto pc + off16 */
+
+#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Raw code statement block */
+
+#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
+ ((struct bpf_insn) { \
+ .code = CODE, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = IMM })
+
+/* Program exit */
+
+#define BPF_EXIT_INSN() \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_EXIT, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = 0 })
+
+/* Function call */
+
+#define BPF_EMIT_CALL(FUNC) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP | BPF_CALL, \
+ .dst_reg = 0, \
+ .src_reg = 0, \
+ .off = 0, \
+ .imm = FUNC })
+
+/* ArgX, context and stack frame pointer register positions. Note,
+ * Arg1, Arg2, Arg3, etc are used as argument mappings of function
+ * calls in BPF_CALL instruction.
+ */
+#define BPF_REG_ARG1 BPF_REG_1
+#define BPF_REG_ARG2 BPF_REG_2
+#define BPF_REG_ARG3 BPF_REG_3
+#define BPF_REG_ARG4 BPF_REG_4
+#define BPF_REG_ARG5 BPF_REG_5
+#define BPF_REG_CTX BPF_REG_6
+#define BPF_REG_FP BPF_REG_10
+
#endif
--
1.8.5.2

2015-05-24 08:30:09

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 07/15] bpf tools: Convert arglist to bpf prologue

When all arguments in bpf config section are collected in register and
offset form, this patch will fetch them from bpf context register and
place them as bpf input parameters.

By bpf calling convention, we restrict the max fetched arg number to 4.

Bpf prologue is generated as the following steps:
1. alloc dst address in stack -> r1
2. set size -> r2
3. fetch base register and offset -> r3
4. call BPF_FUNC_probe_read
5. loop 1
6. save intermediate result and process next arg
7. restore intermediate result to arg2~5

Signed-off-by: He Kuang <[email protected]>
---
tools/lib/bpf/Build | 2 +-
tools/lib/bpf/gen_prologue.c | 108 +++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 10 ++++
3 files changed, 119 insertions(+), 1 deletion(-)
create mode 100644 tools/lib/bpf/gen_prologue.c

diff --git a/tools/lib/bpf/Build b/tools/lib/bpf/Build
index d874975..c910805 100644
--- a/tools/lib/bpf/Build
+++ b/tools/lib/bpf/Build
@@ -1 +1 @@
-libbpf-y := libbpf.o bpf.o
+libbpf-y := libbpf.o bpf.o gen_prologue.o
diff --git a/tools/lib/bpf/gen_prologue.c b/tools/lib/bpf/gen_prologue.c
new file mode 100644
index 0000000..ca8aa1c
--- /dev/null
+++ b/tools/lib/bpf/gen_prologue.c
@@ -0,0 +1,108 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <dwarf-regs.h>
+#include <debug.h>
+
+#include "libbpf.h"
+#include "bpf.h"
+
+#define BPF_REG_SIZE (8)
+
+unsigned int bpf_prologue_arg_deref(int offset, int stack_index, int depth,
+ char *new_prog, bool last)
+{
+ struct bpf_insn tmp_insns[6] = {};
+ struct bpf_insn *insn = tmp_insns;
+ unsigned int size;
+
+ if (!depth) {
+ /* base register */
+ if (offset < 0)
+ return 0;
+
+ /* load ctx to r3 */
+ *insn++ = BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_CTX, offset);
+
+ if (!last) {
+ /* r7 for stack space */
+ *insn++ = BPF_MOV64_REG(BPF_REG_7, BPF_REG_FP);
+ *insn++ = BPF_ALU64_IMM(BPF_ADD, BPF_REG_7,
+ -BPF_REG_SIZE * stack_index);
+ /* clear stack */
+ *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_8,
+ -BPF_REG_SIZE * stack_index);
+ } else {
+ /* store r3 to stack */
+ *insn++ = BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_3,
+ -BPF_REG_SIZE * stack_index);
+ }
+ } else {
+ /* refs */
+ /* r3 += offset */
+ *insn++ = BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, offset);
+
+ /* r2 = size */
+ *insn++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_2, 0x0008);
+
+ /* r1 = r7 */
+ *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_7);
+
+ /* call BPF_FUNC_probe_read
+ * r1: ptr to stack
+ * r2: stack size
+ * r3: unsafe ptr
+ */
+ *insn++ = BPF_EMIT_CALL(BPF_FUNC_probe_read);
+
+ if (!last)
+ /* fetch stack intermediate to r3 */
+ *insn++ = BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_FP,
+ -BPF_REG_SIZE * stack_index);
+ }
+
+ size = sizeof(*insn) * (insn - tmp_insns);
+ if (new_prog)
+ memcpy(new_prog, tmp_insns, size);
+
+ return size;
+}
+
+unsigned int bpf_prologue_begin(char *new_prog)
+{
+ struct bpf_insn *new_insn;
+
+ new_insn = (struct bpf_insn *)new_prog;
+
+ if (new_insn) {
+ /* save arg1 to ctx */
+ *new_insn++ = BPF_MOV64_REG(BPF_REG_CTX, BPF_REG_ARG1);
+ /* set r8 to 0 */
+ *new_insn++ = BPF_ALU64_IMM(BPF_MOV, BPF_REG_8, 0);
+ } else
+ new_insn += 2;
+
+ return (char *)new_insn - new_prog;
+}
+
+unsigned int bpf_prologue_end(char *new_prog, int nargs)
+{
+ struct bpf_insn *new_insn;
+ int i;
+
+ new_insn = (struct bpf_insn *)new_prog;
+
+ nargs = min(BPF_PROLOGUE_NRARGS_MAX, nargs);
+ for (i = 0; i < nargs; i++) {
+ /* result in stack, move to r2~r5 */
+ if (new_prog)
+ *new_insn = BPF_LDX_MEM(BPF_DW,
+ BPF_REG_ARG2 + i,
+ BPF_REG_FP,
+ -BPF_REG_SIZE * (i + 1));
+ new_insn++;
+ }
+
+ return (char *)new_insn - new_prog;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 31ff5d9..cf22a6a 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -63,4 +63,14 @@ struct bpf_map_def {
unsigned int max_entries;
};

+#define BPF_PROLOGUE_NRARGS_MAX (4)
+
+unsigned int bpf_prologue_begin(char *new_prog);
+unsigned int bpf_prologue_end(char *new_prog, int nargs);
+unsigned int bpf_prologue_arg_deref(int offset,
+ int stack_index,
+ int depth,
+ char *new_prog,
+ bool last);
+
#endif
--
1.8.5.2

2015-05-24 08:30:28

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 08/15] bpf tools: Fetch calling regs to bpf arglist

For generating bpf prologue which fetches function formal parameters
when debuginfo is not provided.

Signed-off-by: He Kuang <[email protected]>
---
tools/lib/bpf/gen_prologue.c | 19 +++++++++++++++++++
tools/lib/bpf/libbpf.h | 2 ++
2 files changed, 21 insertions(+)

diff --git a/tools/lib/bpf/gen_prologue.c b/tools/lib/bpf/gen_prologue.c
index ca8aa1c..d25693a 100644
--- a/tools/lib/bpf/gen_prologue.c
+++ b/tools/lib/bpf/gen_prologue.c
@@ -106,3 +106,22 @@ unsigned int bpf_prologue_end(char *new_prog, int nargs)

return (char *)new_insn - new_prog;
}
+
+unsigned int bpf_prologue_formal_parameters(char *new_prog, int offset,
+ int index)
+{
+ struct bpf_insn *new_insn;
+
+ if (index >= BPF_PROLOGUE_NRARGS_MAX)
+ return 0;
+
+ new_insn = (struct bpf_insn *)new_prog;
+
+ /* load ctx to r3 */
+ if (new_prog)
+ *new_insn = BPF_LDX_MEM(BPF_DW, BPF_REG_ARG2 + index,
+ BPF_REG_ARG1, offset);
+ new_insn++;
+
+ return (char *)new_insn - new_prog;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index cf22a6a..9d6d4f7 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -72,5 +72,7 @@ unsigned int bpf_prologue_arg_deref(int offset,
int depth,
char *new_prog,
bool last);
+unsigned int bpf_prologue_formal_parameters(char *new_prog, int offset,
+ int index);

#endif
--
1.8.5.2

2015-05-24 08:30:38

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

When probing at function entry, fallback $params to calling regs if no
debuginfo is provided.

Before this path:
$ perf probe -v --add='generic_perform_write $params'
...
Added new event:
Writing event: p:probe/generic_perform_write _stext+1246632 $params
[86152.161204] Parse error at argument[0]. (-22)
Failed to write event: Invalid argument
Error: Failed to add events. Reason: Invalid argument (Code: -22)

After this patch:
$ perf probe -v --add='generic_perform_write $params'
...
Could not open debuginfo. Try to use symbols.
...
Added new event:
Writing event: p:probe/generic_perform_write _stext+1246632 %di %si %dx %cx %r8 %r9
probe:generic_perform_write (on generic_perform_write with $params)

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

perf record -e probe:generic_perform_write -aR sleep 1

$ perf record -e probe:generic_perform_write dd if=/dev/zero of=/mnt/data/test bs=4k count=3

$ perf script
dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x0 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x1000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x2000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/probe-event.c | 42 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index d05b77c..7f9f431 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -46,6 +46,7 @@
#include "probe-event.h"
#include "probe-finder.h"
#include "session.h"
+#include <dwarf-regs.h>

#define MAX_CMDLEN 256
#define PERFPROBE_GROUP "probe"
@@ -286,6 +287,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
clear_probe_trace_event(tevs + i);
}

+static bool perf_probe_is_function_entry(struct perf_probe_event *pev)
+{
+ if (pev->point.file || pev->point.line || pev->point.lazy_line)
+ return false;
+
+ return true;
+}
+
#ifdef HAVE_DWARF_SUPPORT
/*
* Some binaries like glibc have special symbols which are on the symbol
@@ -1225,6 +1234,33 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
return 0;
}

+static char *parse_perf_probe_param(void)
+{
+ int i = 0;
+ struct strbuf sb;
+ bool first = true;
+ const char *reg_str;
+
+ strbuf_init(&sb, 16);
+
+ while (1) {
+ reg_str = get_arch_calling_reg_str(i++);
+ if (!reg_str)
+ break;
+
+ if (first) {
+ strbuf_addf(&sb, "%s", reg_str);
+ first = false;
+ } else
+ strbuf_addf(&sb, " %s", reg_str);
+ }
+
+ if (first)
+ strbuf_add(&sb, "", 1);
+
+ return strbuf_detach(&sb, NULL);
+}
+
/* Parse perf-probe event argument */
static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
{
@@ -2543,6 +2579,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto nomem_out;
}
for (i = 0; i < tev->nargs; i++) {
+ if (perf_probe_is_function_entry(pev) &&
+ !strcmp(pev->args[i].var, "$params")) {
+ tev->args[i].value = parse_perf_probe_param();
+ continue;
+ }
+
if (pev->args[i].name)
tev->args[i].name =
strdup_or_goto(pev->args[i].name,
--
1.8.5.2

2015-05-24 08:30:01

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 10/15] perf bpf: Process debuginfo for generating bpf prologue

Process debuginfo for bpf prologue, the process function is copied and
modified from debuginfo__find_trace_events(), with a different callback
function for generating bpf prologue bytecode.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/probe-event.c | 27 ++++++++++++
tools/perf/util/probe-event.h | 2 +
tools/perf/util/probe-finder.c | 93 ++++++++++++++++++++++++++++++++++++++++++
tools/perf/util/probe-finder.h | 4 ++
4 files changed, 126 insertions(+)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 7f9f431..ccbf4d9 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -914,6 +914,33 @@ out:
return ret;
}

+int get_bpf_prologue(struct perf_probe_event *pev, char **result, int *count)
+{
+ int ret;
+ struct debuginfo *dinfo;
+ bool need_dwarf;
+
+ ret = init_symbol_maps(false);
+ if (ret < 0)
+ return ret;
+
+ need_dwarf = perf_probe_event_need_dwarf(pev);
+
+ dinfo = open_debuginfo(NULL, !need_dwarf);
+
+ if (!dinfo) {
+ if (need_dwarf)
+ return -ENOENT;
+ pr_debug("Could not open debuginfo. Try to use symbols.\n");
+ return 0;
+ }
+
+ pr_debug("Try to generate bpf prologue from debuginfo.\n");
+
+ ret = debuginfo__find_bpf_prologue(dinfo, pev, result, count);
+
+ return ret;
+}
#else /* !HAVE_DWARF_SUPPORT */

static int
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 6c19395..3eb0183 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -106,6 +106,8 @@ extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
size_t len);
+extern int get_bpf_prologue(struct perf_probe_event *pev,
+ char **result, int *count);

/* Check the perf_probe_event needs debuginfo */
extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 4de7649..6785eab 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -39,6 +39,7 @@
#include "util.h"
#include "symbol.h"
#include "probe-finder.h"
+#include <bpf/libbpf.h>

/* Kprobe tracer basic type is up to u64 */
#define MAX_BASIC_TYPE_BITS 64
@@ -1177,6 +1178,70 @@ static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
return n;
}

+static int generate_bpf_prologue(Dwarf_Die *sc_die, struct probe_finder *pf)
+{
+ struct trace_event_finder *tf =
+ container_of(pf, struct trace_event_finder, pf);
+ struct probe_trace_event *tev;
+ struct perf_probe_arg *args;
+ int ret, i;
+ Dwarf_Die vr_die;
+
+ /* Check number of tevs */
+ if (tf->ntevs == tf->max_tevs) {
+ pr_warning("Too many( > %d) probe point found.\n",
+ tf->max_tevs);
+ return -ERANGE;
+ }
+ tev = &tf->tevs[tf->ntevs++];
+
+ /* Expand special probe argument if exist */
+ args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS);
+ if (args == NULL)
+ return -ENOMEM;
+
+ ret = expand_probe_args(sc_die, pf, args);
+ if (ret <= 0)
+ goto end;
+
+ /* restrict nargs <= BPF_PROLOGUE_NRARGS_MAX */
+ if (ret > BPF_PROLOGUE_NRARGS_MAX)
+ ret = BPF_PROLOGUE_NRARGS_MAX;
+
+ tev->nargs = ret;
+ tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
+ if (tev->args == NULL) {
+ ret = -ENOMEM;
+ goto end;
+ }
+
+ /* Find each argument */
+ for (i = 0; i < tev->nargs; i++) {
+ pf->pvar = &args[i];
+ pf->tvar = &tev->args[i];
+
+ /* Search child die for local variables and parameters. */
+ if (!die_find_variable_at(sc_die, pf->pvar->var,
+ pf->addr, &vr_die)) {
+ /* Search again in global variables */
+ if (!die_find_variable_at(&pf->cu_die, pf->pvar->var,
+ 0, &vr_die)) {
+ pr_warning("Failed to find '%s' in this function.\n",
+ pf->pvar->var);
+ ret = -ENOENT;
+ }
+ }
+
+ if (ret >= 0)
+ ret = convert_variable(&vr_die, pf);
+ if (ret != 0)
+ break;
+ }
+end:
+ free(args);
+ return ret;
+}
+
/* Add a found probe point into trace event list */
static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
{
@@ -1261,6 +1326,34 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
return (ret < 0) ? ret : tf.ntevs;
}

+#define BPF_MAX_TEVS (1)
+int debuginfo__find_bpf_prologue(struct debuginfo *dbg,
+ struct perf_probe_event *pev,
+ char **result, int *count)
+{
+ struct trace_event_finder tf = {
+ .pf = {.pev = pev, .callback = generate_bpf_prologue},
+ .mod = dbg->mod, .max_tevs = BPF_MAX_TEVS};
+ struct probe_trace_event *tevs;
+ int ret;
+
+ /* Allocate result tevs array */
+ tevs = zalloc(sizeof(struct probe_trace_event) * BPF_MAX_TEVS);
+ if (tevs == NULL)
+ return -ENOMEM;
+
+ tf.tevs = tevs;
+ tf.ntevs = 0;
+
+ ret = debuginfo__find_probes(dbg, &tf.pf);
+
+ *result = NULL;
+ *count = 0;
+
+ free(tevs);
+ return ret;
+}
+
#define MAX_VAR_LEN 64

/* Collect available variables in this scope */
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index f53553d..f046c63 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -43,6 +43,10 @@ extern int debuginfo__find_trace_events(struct debuginfo *dbg,
struct probe_trace_event **tevs,
int max_tevs);

+extern int debuginfo__find_bpf_prologue(struct debuginfo *dbg,
+ struct perf_probe_event *pev,
+ char **result, int *count);
+
/* Find a perf_probe_point from debuginfo */
extern int debuginfo__find_probe_point(struct debuginfo *dbg,
unsigned long addr,
--
1.8.5.2

2015-05-24 08:30:36

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 11/15] perf bpf: Synthesize vars to generate bpf prologue

After gethered all vars represented in trace_probe_arg, we can now use
functions in libbpf to generate the args prologue.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/probe-finder.c | 83 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 81 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index 6785eab..455ff0f 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -1242,6 +1242,67 @@ end:
return ret;
}

+/*
+ * Convert args to bpf prologue
+ * 1) First pass for calculating the new program length:
+ * synthesize_probe_bpf(arg, nargs, NULL, &new_len);
+ *
+ * 2) 2nd pass do the actually convert
+ * new_prog = malloc(sizeof(struct bpf_insn) * new_len);
+ * synthesize_probe_bpf(arg, nargs, new_prog, &new_len);
+ */
+static int synthesize_probe_bpf(struct probe_trace_arg *arg,
+ int nargs,
+ char *new_prog,
+ int *new_len)
+{
+ int i;
+ char *new_insn = new_prog;
+
+ if (nargs == 0) {
+ *new_len = 0;
+ return 0;
+ }
+
+ new_insn += bpf_prologue_begin(new_prog);
+ /* Find each argument */
+ for (i = 0; i < nargs; i++) {
+ struct probe_trace_arg_ref *ref = arg[i].ref;
+ unsigned int regn = arg[i].regn;
+
+ /* Print argument value */
+ if (arg[i].value[0] == '@' && ref) {
+ /* TODO parse other arguments */
+ pr_debug("%d%+ld", regn, ref->offset);
+ } else {
+ int depth = 0;
+
+ new_insn += bpf_prologue_arg_deref(
+ get_arch_reg_offset(regn),
+ i + 1,
+ depth++,
+ new_prog ? new_insn : NULL,
+ !ref);
+
+ while (ref) {
+ new_insn += bpf_prologue_arg_deref(
+ ref->offset,
+ i + 1,
+ depth++,
+ new_prog ? new_insn : NULL,
+ !ref->next);
+ ref = ref->next;
+ }
+ }
+ }
+
+ new_insn += bpf_prologue_end(new_prog ? new_insn : NULL,
+ nargs);
+ *new_len = new_insn - new_prog;
+
+ return 0;
+}
+
/* Add a found probe point into trace event list */
static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
{
@@ -1335,6 +1396,8 @@ int debuginfo__find_bpf_prologue(struct debuginfo *dbg,
.pf = {.pev = pev, .callback = generate_bpf_prologue},
.mod = dbg->mod, .max_tevs = BPF_MAX_TEVS};
struct probe_trace_event *tevs;
+ struct probe_trace_event *tev;
+ char *new_prog;
int ret;

/* Allocate result tevs array */
@@ -1347,9 +1410,25 @@ int debuginfo__find_bpf_prologue(struct debuginfo *dbg,

ret = debuginfo__find_probes(dbg, &tf.pf);

- *result = NULL;
- *count = 0;
+ tev = &tevs[0];
+
+ synthesize_probe_bpf(tev->args, tev->nargs, NULL, count);
+ if (*count == 0)
+ goto end;
+
+ new_prog = malloc(*count);
+ if (!new_prog) {
+ ret = -ENOMEM;
+ goto end;
+ }

+ synthesize_probe_bpf(tev->args, tev->nargs, new_prog, count);
+
+ /* assign result to tvar */
+ *result = (char *)new_prog;
+
+end:
+ /* release tevs */
free(tevs);
return ret;
}
--
1.8.5.2

2015-05-24 08:30:33

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 12/15] perf bpf: Generate bpf prologue without debuginfo

When probing at function entry, fallback to generate bpf prologue by
calling args if '$param' is the only args and no debuginfo is provided.

Signed-off-by: He Kuang <[email protected]>
---
tools/perf/util/probe-event.c | 54 ++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)

diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index ccbf4d9..0de5879 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -46,6 +46,7 @@
#include "probe-event.h"
#include "probe-finder.h"
#include "session.h"
+#include <bpf/libbpf.h>
#include <dwarf-regs.h>

#define MAX_CMDLEN 256
@@ -287,6 +288,45 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
clear_probe_trace_event(tevs + i);
}

+static int convert_parameters_to_bpf_prologue(char *new_prog, int *new_len)
+{
+ char *new_insn = new_prog;
+ int i;
+ int offset;
+
+ /* Find each argument */
+ for (i = 0; i < BPF_PROLOGUE_NRARGS_MAX; i++) {
+ offset = get_arch_calling_reg_offset(i);
+ if (offset < 0)
+ break;
+
+ new_insn += bpf_prologue_formal_parameters(
+ new_prog ? new_insn : NULL,
+ offset, i);
+ }
+ *new_len = new_insn - new_prog;
+
+ return 0;
+}
+
+static int get_bpf_prologue_default(char **result, int *count)
+{
+ char *new_prog;
+
+ convert_parameters_to_bpf_prologue(NULL, count);
+ if (*count == 0)
+ return 0;
+
+ new_prog = malloc(*count);
+ if (!new_prog)
+ return -ENOMEM;
+
+ convert_parameters_to_bpf_prologue(new_prog, count);
+ *result = (char *)new_prog;
+
+ return 0;
+}
+
static bool perf_probe_is_function_entry(struct perf_probe_event *pev)
{
if (pev->point.file || pev->point.line || pev->point.lazy_line)
@@ -931,7 +971,13 @@ int get_bpf_prologue(struct perf_probe_event *pev, char **result, int *count)
if (!dinfo) {
if (need_dwarf)
return -ENOENT;
- pr_debug("Could not open debuginfo. Try to use symbols.\n");
+ pr_debug("Could not open debuginfo. Generate default prologue.\n");
+
+ if (perf_probe_is_function_entry(pev) &&
+ pev->nargs == 1 &&
+ !strcmp(pev->args[0].var, "$params"))
+ return get_bpf_prologue_default(result, count);
+
return 0;
}

@@ -943,6 +989,12 @@ int get_bpf_prologue(struct perf_probe_event *pev, char **result, int *count)
}
#else /* !HAVE_DWARF_SUPPORT */

+int get_bpf_prologue(struct perf_probe_event *pev __maybe_unused,
+ char **result, int *count)
+{
+ return -ENOTSUP;
+}
+
static int
find_perf_probe_point_from_dwarf(struct probe_trace_point *tp __maybe_unused,
struct perf_probe_point *pp __maybe_unused,
--
1.8.5.2

2015-05-24 08:29:38

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 13/15] perf bpf: Combine bpf prologue and bpf prog

Combine bpf prologue before attaching bpf progs to perf probe event. If
prologue is generated, it will be pasted in front of the original bpf
prog.

Signed-off-by: He Kuang <[email protected]>
---
tools/lib/bpf/libbpf.c | 27 +++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 2 ++
tools/perf/util/bpf-loader.c | 27 +++++++++++++++++++++++++++
3 files changed, 56 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 89c725a..c0b792f 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1087,3 +1087,30 @@ int bpf_prog_get_fd(struct bpf_prog_handler *handler, int *pfd)
*pfd = prog->fd;
return 0;
}
+
+int bpf_obj_prologue(struct bpf_prog_handler *handler, char *result, int size)
+{
+ struct bpf_program *prog;
+ int new_count;
+ int count = size / sizeof(struct bpf_insn);
+
+ prog = handler_to_prog(handler);
+ if (!prog)
+ return -EINVAL;
+
+ new_count = prog->insns_cnt + count;
+ result = realloc(result,
+ new_count * sizeof(struct bpf_insn));
+ if (!result)
+ return -ENOMEM;
+
+ memcpy(result + count * sizeof(struct bpf_insn),
+ prog->insns,
+ prog->insns_cnt * sizeof(struct bpf_insn));
+
+ free(prog->insns);
+ prog->insns = (struct bpf_insn *)result;
+ prog->insns_cnt = new_count;
+
+ return 0;
+}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 9d6d4f7..7df58fd 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -53,6 +53,8 @@ int bpf_prog_get_title(struct bpf_prog_handler *handler,

int bpf_prog_get_fd(struct bpf_prog_handler *handler, int *pfd);

+int bpf_obj_prologue(struct bpf_prog_handler *handler, char *result, int count);
+
/*
* packed attribute is unnecessary for 'bpf_map_def'.
*/
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index b793c69..3f93d04 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -168,6 +168,31 @@ void bpf_clear(void)
bpf_close_object(params.objects[i]);
}

+static int bpf_prologue(void)
+{
+ int i, ret;
+
+ for (i = 0; i < (int)params.nr_progs; i++) {
+ struct bpf_prog_handler *prog =
+ params.progs[i].prog;
+ struct perf_probe_event *pevent =
+ params.progs[i].pevent;
+ int count;
+ char *result;
+
+ ret = get_bpf_prologue(pevent, &result, &count);
+ if (ret)
+ return ret;
+
+ if (count > 0)
+ ret = bpf_obj_prologue(prog, result, count);
+ else
+ pr_debug("bpf: no prologue generated\n");
+ }
+
+ return ret;
+}
+
static bool is_probing = false;

int bpf_unprobe(void)
@@ -220,6 +245,8 @@ int bpf_load(void)
size_t i;
int err;

+ bpf_prologue();
+
for (i = 0; i < params.nr_objects; i++) {
err = bpf_load_object(params.objects[i]);
if (err) {
--
1.8.5.2

2015-05-24 08:28:58

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 14/15] samples/bpf: Add sample for testing bpf fetch args

Sample code for testing bpf fetch args.

Works as following steps:

$ perf bpf record --object sample_bpf_fetch_args.o -- dd if=/dev/zero of=/mnt/data/test bs=4k count=3

show result in ringbuffer:

$ perf script
dd 1176 [000] 87706.975390: perf_bpf_probe:generic_perform_write: (ffffffff811307ea) a_ops=0xffffffff81a200e0 bytes=0x1000 page=0xffff88007c61fd80 pos=0
dd 1176 [000] 87706.975390: perf_bpf_probe:generic_perform_write: (ffffffff811307ea) a_ops=0xffffffff81a200e0 bytes=0x1000 page=0xffffea0001c44800 pos=4096
dd 1176 [000] 87706.975390: perf_bpf_probe:generic_perform_write: (ffffffff811307ea) a_ops=0xffffffff81a200e0 bytes=0x1000 page=0xffffea0001ed24c0 pos=8192
dd 1176 [000] 87706.975390: perf_bpf_probe:submit_bio: (ffffffff81310cdf) count=0x3000 rw=1 bio=0xffff88007c237a80

show result in bpf prog:

$ cat /sys/kernel/debug/tracing/trace |grep dd
dd-1176 [000] d... 87778.531511: : generic_perform_write(1): a_ops=ffffffff81a200e0, bytes=4096
dd-1176 [000] d... 87778.531550: : generic_perform_write(2): page =ffff88007c61fd80, pos =0
dd-1176 [000] d... 87778.532142: : generic_perform_write(1): a_ops=ffffffff81a200e0, bytes=4096
dd-1176 [000] d... 87778.532154: : generic_perform_write(2): page =ffffea0001c44800, pos =4096
dd-1176 [000] d... 87778.532342: : generic_perform_write(1): a_ops=ffffffff81a200e0, bytes=4096
dd-1176 [000] d... 87778.532354: : generic_perform_write(2): page =ffffea0001ed24c0, pos =8192
dd-1176 [000] d.h. 87778.533049: : submit_bio count=12288, rw=1, bio=ffff88007c237a80

Signed-off-by: He Kuang <[email protected]>
---
samples/bpf/Makefile | 1 +
samples/bpf/sample_bpf_fetch_args.c | 62 +++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+)
create mode 100644 samples/bpf/sample_bpf_fetch_args.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 8fdbd73..dc0b0e8 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -30,6 +30,7 @@ always += tracex2_kern.o
always += tracex3_kern.o
always += tracex4_kern.o
always += tcbpf1_kern.o
+always += sample_bpf_fetch_args.o

HOSTCFLAGS += -I$(objtree)/usr/include

diff --git a/samples/bpf/sample_bpf_fetch_args.c b/samples/bpf/sample_bpf_fetch_args.c
new file mode 100644
index 0000000..5d2c80c
--- /dev/null
+++ b/samples/bpf/sample_bpf_fetch_args.c
@@ -0,0 +1,62 @@
+/*
+ Sample code for bpf_fetch_args().
+*/
+
+#include <linux/writeback.h>
+#include <linux/blkdev.h>
+
+#include <uapi/linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+/*
+ * Prototype:
+ * ssize_t generic_perform_write(struct file *file,
+ * struct iov_iter *i, loff_t pos)
+ * Local variables:
+ * const struct address_space_operations *a_ops;
+ * unsigned long bytes;
+ * struct page *page;
+ * loff_t pos;
+ */
+SEC("generic_perform_write=generic_perform_write+122 file->f_mapping->a_ops bytes page pos")
+int NODE_generic_perform_write(struct pt_regs *ctx,
+ void *a_ops,
+ void *bytes,
+ void *page,
+ void *pos)
+{
+ /* Too many args for bpf_trace_printk, show in 2 lines */
+ char fmt1[] = "generic_perform_write(1): a_ops=%p, bytes=%lu\n";
+ char fmt2[] = "generic_perform_write(2): page =%p, pos =%lu\n";
+
+ bpf_trace_printk(fmt1, sizeof(fmt1),
+ a_ops, (unsigned long)bytes);
+ bpf_trace_printk(fmt2, sizeof(fmt2),
+ page, (unsigned long)pos);
+
+ return 1;
+}
+
+/*
+ * Prototype:
+ * void submit_bio(int rw, struct bio *bio)
+ * Local variables:
+ * unsigned int count;
+ */
+SEC("submit_bio=submit_bio+63 count rw bio")
+int NODE_submit_bio(struct pt_regs *ctx,
+ void *count,
+ void *rw,
+ void *bio)
+{
+ char fmt[] = "submit_bio count=%u, rw=%d, bio=%p\n";
+
+ bpf_trace_printk(fmt, sizeof(fmt),
+ (unsigned int)count, (int)rw, bio);
+
+ return 1;
+}
+
+char _license[] SEC("license") = "GPL";
+u32 _version SEC("version") = LINUX_VERSION_CODE;
--
1.8.5.2

2015-05-24 08:29:01

by He Kuang

[permalink] [raw]
Subject: [RFC PATCH v2 15/15] samples/bpf: Add sample for no-debuginfo case

Sample code for testing bpf fetch args without debuginfo.

Works as following steps:

$ perf bpf record --object /mnt/9p/sample_bpf_fetch_args_without_debug.o -- dd if=/dev/zero of=/mnt/data/test bs=4k count=3

show result in ringbuffer:
$ perf script
dd 1183 [000] 88000.240137: perf_bpf_probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c36cb00 arg2=0xffff88007c167e70 arg3=0x0 arg4=0x0 arg5=0x55617215 arg6=0x1839010
dd 1183 [000] 88000.240137: perf_bpf_probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c36cb00 arg2=0xffff88007c167e70 arg3=0x1000 arg4=0x0 arg5=0x55617215 arg6=0x1839010
dd 1183 [000] 88000.240137: perf_bpf_probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c36cb00 arg2=0xffff88007c167e70 arg3=0x2000 arg4=0x0 arg5=0x55617215 arg6=0x1839010
dd 1183 [000] 88000.240137: perf_bpf_probe:submit_bio: (ffffffff81310ca0) arg1=0x1 arg2=0xffff88007c9d8000 arg3=0x8b9 arg4=0x8ba arg5=0x1b8e0 arg6=0xffff88007fc1b8e0

show result in bpf prog:

$ cat /sys/kernel/debug/tracing/trace | grep dd-
dd-1183 [000] d... 88072.038971: : generic_perform_write: file=ffff88007c36cb00, i=ffff88007c167e70, pos=0
dd-1183 [000] d... 88072.039625: : generic_perform_write: file=ffff88007c36cb00, i=ffff88007c167e70, pos=4096
dd-1183 [000] d... 88072.039833: : generic_perform_write: file=ffff88007c36cb00, i=ffff88007c167e70, pos=8192
dd-1183 [000] d... 88072.040533: : submit_bio rw=1, bio=ffff88007c9d8000

Signed-off-by: He Kuang <[email protected]>
---
samples/bpf/Makefile | 1 +
samples/bpf/sample_bpf_fetch_args_without_debug.c | 49 +++++++++++++++++++++++
2 files changed, 50 insertions(+)
create mode 100644 samples/bpf/sample_bpf_fetch_args_without_debug.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index dc0b0e8..282c959 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -31,6 +31,7 @@ always += tracex3_kern.o
always += tracex4_kern.o
always += tcbpf1_kern.o
always += sample_bpf_fetch_args.o
+always += sample_bpf_fetch_args_without_debug.o

HOSTCFLAGS += -I$(objtree)/usr/include

diff --git a/samples/bpf/sample_bpf_fetch_args_without_debug.c b/samples/bpf/sample_bpf_fetch_args_without_debug.c
new file mode 100644
index 0000000..69519ed
--- /dev/null
+++ b/samples/bpf/sample_bpf_fetch_args_without_debug.c
@@ -0,0 +1,49 @@
+/*
+ Sample code for bpf_fetch_args() without debuginfo.
+*/
+
+#include <linux/writeback.h>
+#include <linux/blkdev.h>
+
+#include <uapi/linux/bpf.h>
+#include <linux/version.h>
+#include "bpf_helpers.h"
+
+/*
+ * Prototype:
+ * ssize_t generic_perform_write(struct file *file,
+ * struct iov_iter *i, loff_t pos)
+ */
+SEC("generic_perform_write=generic_perform_write $params")
+int NODE_generic_perform_write(struct pt_regs *ctx,
+ void *file,
+ void *i,
+ void *pos)
+{
+ char fmt[] = "generic_perform_write: file=%p, i=%p, pos=%lu\n";
+
+ bpf_trace_printk(fmt, sizeof(fmt),
+ file, i, (unsigned long)pos);
+
+ return 1;
+}
+
+/*
+ * Prototype:
+ * void submit_bio(int rw, struct bio *bio)
+ */
+SEC("submit_bio=submit_bio $params")
+int NODE_submit_bio(struct pt_regs *ctx,
+ void *rw,
+ void *bio)
+{
+ char fmt[] = "submit_bio rw=%d, bio=%p\n";
+
+ bpf_trace_printk(fmt, sizeof(fmt),
+ (int)rw, bio);
+
+ return 1;
+}
+
+char _license[] SEC("license") = "GPL";
+u32 _version SEC("version") = LINUX_VERSION_CODE;
--
1.8.5.2

Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

On 2015/05/24 17:28, He Kuang wrote:
> When probing at function entry, fallback $params to calling regs if no
> debuginfo is provided.
>
> Before this path:
> $ perf probe -v --add='generic_perform_write $params'
> ...
> Added new event:
> Writing event: p:probe/generic_perform_write _stext+1246632 $params
> [86152.161204] Parse error at argument[0]. (-22)
> Failed to write event: Invalid argument
> Error: Failed to add events. Reason: Invalid argument (Code: -22)
>
> After this patch:
> $ perf probe -v --add='generic_perform_write $params'
> ...
> Could not open debuginfo. Try to use symbols.
> ...
> Added new event:
> Writing event: p:probe/generic_perform_write _stext+1246632 %di %si %dx %cx %r8 %r9
> probe:generic_perform_write (on generic_perform_write with $params)
>
> You can now use it in all perf tools, such as:
>
> perf record -e probe:generic_perform_write -aR sleep 1
>
> $ perf record -e probe:generic_perform_write dd if=/dev/zero of=/mnt/data/test bs=4k count=3

NAK, this should not work on x86-32 and we don't know how many registers are used.
I think $params special handler should be used only with debuginfo, since $params
ensures user to save function parameters with its name. If we can't do that, we
should accept that.

If you need to handle register arguments, you should introduce new $regparams instead
of $params. (however, that still not work correctly on x86-32)

Hmm, we also need $regs to record all registers.

Thank you,


>
> $ perf script
> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x0 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x1000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x2000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>
> Signed-off-by: He Kuang <[email protected]>
> ---
> tools/perf/util/probe-event.c | 42 ++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 42 insertions(+)
>
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index d05b77c..7f9f431 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -46,6 +46,7 @@
> #include "probe-event.h"
> #include "probe-finder.h"
> #include "session.h"
> +#include <dwarf-regs.h>
>
> #define MAX_CMDLEN 256
> #define PERFPROBE_GROUP "probe"
> @@ -286,6 +287,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
> clear_probe_trace_event(tevs + i);
> }
>
> +static bool perf_probe_is_function_entry(struct perf_probe_event *pev)
> +{
> + if (pev->point.file || pev->point.line || pev->point.lazy_line)
> + return false;
> +
> + return true;
> +}
> +
> #ifdef HAVE_DWARF_SUPPORT
> /*
> * Some binaries like glibc have special symbols which are on the symbol
> @@ -1225,6 +1234,33 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
> return 0;
> }
>
> +static char *parse_perf_probe_param(void)
> +{
> + int i = 0;
> + struct strbuf sb;
> + bool first = true;
> + const char *reg_str;
> +
> + strbuf_init(&sb, 16);
> +
> + while (1) {
> + reg_str = get_arch_calling_reg_str(i++);
> + if (!reg_str)
> + break;
> +
> + if (first) {
> + strbuf_addf(&sb, "%s", reg_str);
> + first = false;
> + } else
> + strbuf_addf(&sb, " %s", reg_str);
> + }
> +
> + if (first)
> + strbuf_add(&sb, "", 1);
> +
> + return strbuf_detach(&sb, NULL);
> +}
> +
> /* Parse perf-probe event argument */
> static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
> {
> @@ -2543,6 +2579,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
> goto nomem_out;
> }
> for (i = 0; i < tev->nargs; i++) {
> + if (perf_probe_is_function_entry(pev) &&
> + !strcmp(pev->args[i].var, "$params")) {
> + tev->args[i].value = parse_perf_probe_param();
> + continue;
> + }
> +
> if (pev->args[i].name)
> tev->args[i].name =
> strdup_or_goto(pev->args[i].name,
>


--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]

Subject: Re: [RFC PATCH v2 03/15] perf bpf: Save pt_regs info from debuginfo

On 2015/05/24 17:27, He Kuang wrote:
> Save reg number in function convert_variable_location() instead of the
> register string name, so we can fetch the target register from bpf
> context register later.

This is not needed because you can also get reg number from reg string
afterwards.

Thank you,

>
> Signed-off-by: He Kuang <[email protected]>
> ---
> tools/perf/util/include/dwarf-regs.h | 13 +++++++++++++
> tools/perf/util/probe-event.h | 1 +
> tools/perf/util/probe-finder.c | 11 +++++++++++
> 3 files changed, 25 insertions(+)
>
> diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
> index 8f14965..566ff6d 100644
> --- a/tools/perf/util/include/dwarf-regs.h
> +++ b/tools/perf/util/include/dwarf-regs.h
> @@ -2,7 +2,20 @@
> #define _PERF_DWARF_REGS_H_
>
> #ifdef HAVE_DWARF_SUPPORT
> +struct arch_regs_info {
> + const char *name; /* Architecture dependent register string */
> + int offset; /* Reg offset in struct pt_regs */
> + int size; /* Reg size */
> +};
> +
> +#define ARCH_REGS_INFO(r, pt_reg_name) \
> + {.name = r, \
> + .offset = offsetof(struct pt_regs, pt_reg_name), \
> + .size = sizeof(((struct pt_regs *)0)->pt_reg_name)} \
> +
> const char *get_arch_regstr(unsigned int n);
> +int get_arch_reg_offset(unsigned int n);
> +int get_arch_reg_size(unsigned int n);
> #endif
>
> #endif
> diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
> index d6b7834..6c19395 100644
> --- a/tools/perf/util/probe-event.h
> +++ b/tools/perf/util/probe-event.h
> @@ -29,6 +29,7 @@ struct probe_trace_arg {
> char *value; /* Base value */
> char *type; /* Type name */
> struct probe_trace_arg_ref *ref; /* Referencing offset */
> + unsigned int regn; /* Regn from dwarf */
> };
>
> /* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
> diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
> index ee27b74..681af00 100644
> --- a/tools/perf/util/probe-finder.c
> +++ b/tools/perf/util/probe-finder.c
> @@ -159,6 +159,16 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
> return ref;
> }
>
> +int __attribute__ ((weak))
> +get_arch_reg_offset(unsigned int n __maybe_unused) {
> + return -1;
> +}
> +
> +int __attribute__ ((weak))
> +get_arch_reg_size(unsigned int n __maybe_unused) {
> + return -1;
> +}
> +
> /*
> * Convert a location into trace_arg.
> * If tvar == NULL, this just checks variable can be converted.
> @@ -260,6 +270,7 @@ static_var:
> return -ERANGE;
> }
>
> + tvar->regn = regn;
> tvar->value = strdup(regs);
> if (tvar->value == NULL)
> return -ENOMEM;
>


--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]

2015-05-25 07:39:47

by He Kuang

[permalink] [raw]
Subject: Re: [RFC PATCH v2 03/15] perf bpf: Save pt_regs info from debuginfo



On 2015/5/24 21:31, Masami Hiramatsu wrote:
> On 2015/05/24 17:27, He Kuang wrote:
>> Save reg number in function convert_variable_location() instead of the
>> register string name, so we can fetch the target register from bpf
>> context register later.
>
> This is not needed because you can also get reg number from reg string
> afterwards.
>
> Thank you,

Ok, thanks
>
>>
>> Signed-off-by: He Kuang <[email protected]>
>> ---
>> tools/perf/util/include/dwarf-regs.h | 13 +++++++++++++
>> tools/perf/util/probe-event.h | 1 +
>> tools/perf/util/probe-finder.c | 11 +++++++++++
>> 3 files changed, 25 insertions(+)
>>
>> diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h
>> index 8f14965..566ff6d 100644
>> --- a/tools/perf/util/include/dwarf-regs.h
>> +++ b/tools/perf/util/include/dwarf-regs.h
>> @@ -2,7 +2,20 @@
>> #define _PERF_DWARF_REGS_H_
>>
>> #ifdef HAVE_DWARF_SUPPORT
>> +struct arch_regs_info {
>> + const char *name; /* Architecture dependent register string */
>> + int offset; /* Reg offset in struct pt_regs */
>> + int size; /* Reg size */
>> +};
>> +
>> +#define ARCH_REGS_INFO(r, pt_reg_name) \
>> + {.name = r, \
>> + .offset = offsetof(struct pt_regs, pt_reg_name), \
>> + .size = sizeof(((struct pt_regs *)0)->pt_reg_name)} \
>> +
>> const char *get_arch_regstr(unsigned int n);
>> +int get_arch_reg_offset(unsigned int n);
>> +int get_arch_reg_size(unsigned int n);
>> #endif
>>
>> #endif
>> diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
>> index d6b7834..6c19395 100644
>> --- a/tools/perf/util/probe-event.h
>> +++ b/tools/perf/util/probe-event.h
>> @@ -29,6 +29,7 @@ struct probe_trace_arg {
>> char *value; /* Base value */
>> char *type; /* Type name */
>> struct probe_trace_arg_ref *ref; /* Referencing offset */
>> + unsigned int regn; /* Regn from dwarf */
>> };
>>
>> /* kprobe-tracer and uprobe-tracer tracing event (point + arg) */
>> diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
>> index ee27b74..681af00 100644
>> --- a/tools/perf/util/probe-finder.c
>> +++ b/tools/perf/util/probe-finder.c
>> @@ -159,6 +159,16 @@ static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
>> return ref;
>> }
>>
>> +int __attribute__ ((weak))
>> +get_arch_reg_offset(unsigned int n __maybe_unused) {
>> + return -1;
>> +}
>> +
>> +int __attribute__ ((weak))
>> +get_arch_reg_size(unsigned int n __maybe_unused) {
>> + return -1;
>> +}
>> +
>> /*
>> * Convert a location into trace_arg.
>> * If tvar == NULL, this just checks variable can be converted.
>> @@ -260,6 +270,7 @@ static_var:
>> return -ERANGE;
>> }
>>
>> + tvar->regn = regn;
>> tvar->value = strdup(regs);
>> if (tvar->value == NULL)
>> return -ENOMEM;
>>
>
>

2015-05-25 08:34:13

by He Kuang

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

hi,

On 2015/5/24 16:49, Masami Hiramatsu wrote:
> On 2015/05/24 17:28, He Kuang wrote:
>> When probing at function entry, fallback $params to calling regs if no
>> debuginfo is provided.
>>
>> Before this path:
>> $ perf probe -v --add='generic_perform_write $params'
>> ...
>> Added new event:
>> Writing event: p:probe/generic_perform_write _stext+1246632 $params
>> [86152.161204] Parse error at argument[0]. (-22)
>> Failed to write event: Invalid argument
>> Error: Failed to add events. Reason: Invalid argument (Code: -22)
>>
>> After this patch:
>> $ perf probe -v --add='generic_perform_write $params'
>> ...
>> Could not open debuginfo. Try to use symbols.
>> ...
>> Added new event:
>> Writing event: p:probe/generic_perform_write _stext+1246632 %di %si %dx %cx %r8 %r9
>> probe:generic_perform_write (on generic_perform_write with $params)
>>
>> You can now use it in all perf tools, such as:
>>
>> perf record -e probe:generic_perform_write -aR sleep 1
>>
>> $ perf record -e probe:generic_perform_write dd if=/dev/zero of=/mnt/data/test bs=4k count=3
>
> NAK, this should not work on x86-32 and we don't know how many registers are used.
> I think $params special handler should be used only with debuginfo, since $params
> ensures user to save function parameters with its name. If we can't do that, we
> should accept that.
>
> If you need to handle register arguments, you should introduce new $regparams instead

Yes, $regparam is more appropriate.

> of $params. (however, that still not work correctly on x86-32)
>
> Hmm, we also need $regs to record all registers.

Right, I learnt regparm(3) is mandatory in x86_32, according to rules,
the first three args will go to regparm(ax, dx, cx). But we should not
refer arg1~3 to ax, dx, cx because of 64bit parameters (other reasons?).

Consider this keyword is used for generating bpf prologue which fetches
formal parameters when no debuginfo is provided, for this purpose, we can:
1) We just help fetch the $regs or $regparms(If the keyword is
$regparms, ax/dx/cx is fetched, nothing related to args) to bpf arglists
and leave the rest things to bpf prog writer.

2) Keep that on platforms like x86_64 and skip this feature on
platforms like x86_32.

or any other suggestions?

Thanks

>
> Thank you,
>
>
>>
>> $ perf script
>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x0 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x1000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x2000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>>
>> Signed-off-by: He Kuang <[email protected]>
>> ---
>> tools/perf/util/probe-event.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 42 insertions(+)
>>
>> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
>> index d05b77c..7f9f431 100644
>> --- a/tools/perf/util/probe-event.c
>> +++ b/tools/perf/util/probe-event.c
>> @@ -46,6 +46,7 @@
>> #include "probe-event.h"
>> #include "probe-finder.h"
>> #include "session.h"
>> +#include <dwarf-regs.h>
>>
>> #define MAX_CMDLEN 256
>> #define PERFPROBE_GROUP "probe"
>> @@ -286,6 +287,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
>> clear_probe_trace_event(tevs + i);
>> }
>>
>> +static bool perf_probe_is_function_entry(struct perf_probe_event *pev)
>> +{
>> + if (pev->point.file || pev->point.line || pev->point.lazy_line)
>> + return false;
>> +
>> + return true;
>> +}
>> +
>> #ifdef HAVE_DWARF_SUPPORT
>> /*
>> * Some binaries like glibc have special symbols which are on the symbol
>> @@ -1225,6 +1234,33 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
>> return 0;
>> }
>>
>> +static char *parse_perf_probe_param(void)
>> +{
>> + int i = 0;
>> + struct strbuf sb;
>> + bool first = true;
>> + const char *reg_str;
>> +
>> + strbuf_init(&sb, 16);
>> +
>> + while (1) {
>> + reg_str = get_arch_calling_reg_str(i++);
>> + if (!reg_str)
>> + break;
>> +
>> + if (first) {
>> + strbuf_addf(&sb, "%s", reg_str);
>> + first = false;
>> + } else
>> + strbuf_addf(&sb, " %s", reg_str);
>> + }
>> +
>> + if (first)
>> + strbuf_add(&sb, "", 1);
>> +
>> + return strbuf_detach(&sb, NULL);
>> +}
>> +
>> /* Parse perf-probe event argument */
>> static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
>> {
>> @@ -2543,6 +2579,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
>> goto nomem_out;
>> }
>> for (i = 0; i < tev->nargs; i++) {
>> + if (perf_probe_is_function_entry(pev) &&
>> + !strcmp(pev->args[i].var, "$params")) {
>> + tev->args[i].value = parse_perf_probe_param();
>> + continue;
>> + }
>> +
>> if (pev->args[i].name)
>> tev->args[i].name =
>> strdup_or_goto(pev->args[i].name,
>>
>
>

Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

On 2015/05/25 17:33, He Kuang wrote:
> hi,
>
> On 2015/5/24 16:49, Masami Hiramatsu wrote:
>> On 2015/05/24 17:28, He Kuang wrote:
>>> When probing at function entry, fallback $params to calling regs if no
>>> debuginfo is provided.
>>>
>>> Before this path:
>>> $ perf probe -v --add='generic_perform_write $params'
>>> ...
>>> Added new event:
>>> Writing event: p:probe/generic_perform_write _stext+1246632 $params
>>> [86152.161204] Parse error at argument[0]. (-22)
>>> Failed to write event: Invalid argument
>>> Error: Failed to add events. Reason: Invalid argument (Code: -22)
>>>
>>> After this patch:
>>> $ perf probe -v --add='generic_perform_write $params'
>>> ...
>>> Could not open debuginfo. Try to use symbols.
>>> ...
>>> Added new event:
>>> Writing event: p:probe/generic_perform_write _stext+1246632 %di %si %dx %cx %r8 %r9
>>> probe:generic_perform_write (on generic_perform_write with $params)
>>>
>>> You can now use it in all perf tools, such as:
>>>
>>> perf record -e probe:generic_perform_write -aR sleep 1
>>>
>>> $ perf record -e probe:generic_perform_write dd if=/dev/zero of=/mnt/data/test bs=4k count=3
>>
>> NAK, this should not work on x86-32 and we don't know how many registers are used.
>> I think $params special handler should be used only with debuginfo, since $params
>> ensures user to save function parameters with its name. If we can't do that, we
>> should accept that.
>>
>> If you need to handle register arguments, you should introduce new $regparams instead
>
> Yes, $regparam is more appropriate.

thanks :)

>> of $params. (however, that still not work correctly on x86-32)
>>
>> Hmm, we also need $regs to record all registers.
>
> Right, I learnt regparm(3) is mandatory in x86_32, according to rules,
> the first three args will go to regparm(ax, dx, cx). But we should not
> refer arg1~3 to ax, dx, cx because of 64bit parameters (other reasons?).

Also, if the function is asmlinkage, its arguments are passed via stack.

> Consider this keyword is used for generating bpf prologue which fetches
> formal parameters when no debuginfo is provided, for this purpose, we can:
> 1) We just help fetch the $regs or $regparms(If the keyword is
> $regparms, ax/dx/cx is fetched, nothing related to args) to bpf arglists
> and leave the rest things to bpf prog writer.
>
> 2) Keep that on platforms like x86_64 and skip this feature on
> platforms like x86_32.

I like 1), since such arch-dependent behavior looks confusing.

>
> or any other suggestions?

Actually, I'm working on the perf-probe cache enhancement (and it will be perf-cache)
which allows you to cache the result of debuginfo analysis under buildid-cache.
So, if the build-id is same on remote machines, you can transfer the cache to the
remote machine and reuse it. This means you don't need debuginfo in the remote machine
unless you're using the same binary/bpf.

Thank you,

>
> Thanks
>
>>
>> Thank you,
>>
>>
>>>
>>> $ perf script
>>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x0 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x1000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>>> dd 1149 [000] 18574.762652: probe:generic_perform_write: (ffffffff81130770) arg1=0xffff88007c37f600 arg2=0xffff88007ca87e70 arg3=0x2000 arg4=0x0 arg5=0x556062e3 arg6=0x12a8010
>>>
>>> Signed-off-by: He Kuang <[email protected]>
>>> ---
>>> tools/perf/util/probe-event.c | 42 ++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 42 insertions(+)
>>>
>>> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
>>> index d05b77c..7f9f431 100644
>>> --- a/tools/perf/util/probe-event.c
>>> +++ b/tools/perf/util/probe-event.c
>>> @@ -46,6 +46,7 @@
>>> #include "probe-event.h"
>>> #include "probe-finder.h"
>>> #include "session.h"
>>> +#include <dwarf-regs.h>
>>>
>>> #define MAX_CMDLEN 256
>>> #define PERFPROBE_GROUP "probe"
>>> @@ -286,6 +287,14 @@ static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs)
>>> clear_probe_trace_event(tevs + i);
>>> }
>>>
>>> +static bool perf_probe_is_function_entry(struct perf_probe_event *pev)
>>> +{
>>> + if (pev->point.file || pev->point.line || pev->point.lazy_line)
>>> + return false;
>>> +
>>> + return true;
>>> +}
>>> +
>>> #ifdef HAVE_DWARF_SUPPORT
>>> /*
>>> * Some binaries like glibc have special symbols which are on the symbol
>>> @@ -1225,6 +1234,33 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
>>> return 0;
>>> }
>>>
>>> +static char *parse_perf_probe_param(void)
>>> +{
>>> + int i = 0;
>>> + struct strbuf sb;
>>> + bool first = true;
>>> + const char *reg_str;
>>> +
>>> + strbuf_init(&sb, 16);
>>> +
>>> + while (1) {
>>> + reg_str = get_arch_calling_reg_str(i++);
>>> + if (!reg_str)
>>> + break;
>>> +
>>> + if (first) {
>>> + strbuf_addf(&sb, "%s", reg_str);
>>> + first = false;
>>> + } else
>>> + strbuf_addf(&sb, " %s", reg_str);
>>> + }
>>> +
>>> + if (first)
>>> + strbuf_add(&sb, "", 1);
>>> +
>>> + return strbuf_detach(&sb, NULL);
>>> +}
>>> +
>>> /* Parse perf-probe event argument */
>>> static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
>>> {
>>> @@ -2543,6 +2579,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
>>> goto nomem_out;
>>> }
>>> for (i = 0; i < tev->nargs; i++) {
>>> + if (perf_probe_is_function_entry(pev) &&
>>> + !strcmp(pev->args[i].var, "$params")) {
>>> + tev->args[i].value = parse_perf_probe_param();
>>> + continue;
>>> + }
>>> +
>>> if (pev->args[i].name)
>>> tev->args[i].name =
>>> strdup_or_goto(pev->args[i].name,
>>>
>>
>>
>
>


--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]

2015-05-25 12:46:47

by Arnaldo Carvalho de Melo

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

Em Mon, May 25, 2015 at 09:22:50PM +0900, Masami Hiramatsu escreveu:
> Actually, I'm working on the perf-probe cache enhancement (and it will be perf-cache)
> which allows you to cache the result of debuginfo analysis under buildid-cache.
> So, if the build-id is same on remote machines, you can transfer the cache to the
> remote machine and reuse it.

> This means you don't need debuginfo in the remote machine unless
> you're using the same binary/bpf.

I think this should read:

"This means you don't need debuginfo in the remote machine unless you're
using a _different_ binary/bpf"

or, alternatively:

"This means you don't need debuginfo in the remote machine _as long as_
you're using the same binary bpf"

No?

- Arnaldo

Subject: Re: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

On 2015/05/25 21:46, Arnaldo Carvalho de Melo wrote:
> Em Mon, May 25, 2015 at 09:22:50PM +0900, Masami Hiramatsu escreveu:
>> Actually, I'm working on the perf-probe cache enhancement (and it will be perf-cache)
>> which allows you to cache the result of debuginfo analysis under buildid-cache.
>> So, if the build-id is same on remote machines, you can transfer the cache to the
>> remote machine and reuse it.
>
>> This means you don't need debuginfo in the remote machine unless
>> you're using the same binary/bpf.
>
> I think this should read:
>
> "This means you don't need debuginfo in the remote machine unless you're
> using a _different_ binary/bpf"

Oops, right. unless different one, is correct.

>
> or, alternatively:
>
> "This means you don't need debuginfo in the remote machine _as long as_
> you're using the same binary bpf"

Yes, thanks!

>
> No?
>
> - Arnaldo
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
>


--
Masami HIRAMATSU
Linux Technology Research Center, System Productivity Research Dept.
Center for Technology Innovation - Systems Engineering
Hitachi, Ltd., Research & Development Group
E-mail: [email protected]

2015-05-26 17:50:54

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

On 5/25/15 1:33 AM, He Kuang wrote:
> Right, I learnt regparm(3) is mandatory in x86_32, according to rules,
> the first three args will go to regparm(ax, dx, cx). But we should not
> refer arg1~3 to ax, dx, cx because of 64bit parameters (other reasons?).
>
> Consider this keyword is used for generating bpf prologue which fetches
> formal parameters when no debuginfo is provided, for this purpose, we can:
> 1) We just help fetch the $regs or $regparms(If the keyword is
> $regparms, ax/dx/cx is fetched, nothing related to args) to bpf arglists
> and leave the rest things to bpf prog writer.
>
> 2) Keep that on platforms like x86_64 and skip this feature on
> platforms like x86_32.
>
> or any other suggestions?

Single argument like $regparam or whatever name cannot work on all
architectures, that's why in the very beginning I suggested
'func(long, char, void*)' syntax to describe arguments when debuginfo
is not available. Calling convention for scalars is simple enough on
all major architectures. x64_64 - trivial, i64_32 - a bit more involved,
but simple enough so that list of types of arguments is enough to figure
out which register or register pair or stack should be used to fetch
argN.

2015-05-26 17:53:54

by Alexei Starovoitov

[permalink] [raw]
Subject: Re: [RFC PATCH v2 15/15] samples/bpf: Add sample for no-debuginfo case

On 5/24/15 1:28 AM, He Kuang wrote:
> +SEC("generic_perform_write=generic_perform_write $params")
> +int NODE_generic_perform_write(struct pt_regs *ctx,
> + void *file,
> + void *i,
> + void *pos)

instead:
SEC("generic_perform_write(void*, void*, void*)")
int prog(struct pt_regs *ctx, void *fild, void *i, void *pos)

would be arch-independent.

2015-05-27 02:28:26

by He Kuang

[permalink] [raw]
Subject: Re: [RFC PATCH v2 09/15] perf probe: Support $params without debuginfo

hi, Alexei

On 2015/5/27 1:50, Alexei Starovoitov wrote:
> On 5/25/15 1:33 AM, He Kuang wrote:
>> Right, I learnt regparm(3) is mandatory in x86_32, according to rules,
>> the first three args will go to regparm(ax, dx, cx). But we should not
>> refer arg1~3 to ax, dx, cx because of 64bit parameters (other reasons?).
>>
>> Consider this keyword is used for generating bpf prologue which fetches
>> formal parameters when no debuginfo is provided, for this purpose, we can:
>> 1) We just help fetch the $regs or $regparms(If the keyword is
>> $regparms, ax/dx/cx is fetched, nothing related to args) to bpf arglists
>> and leave the rest things to bpf prog writer.
>>
>> 2) Keep that on platforms like x86_64 and skip this feature on
>> platforms like x86_32.
>>
>> or any other suggestions?
>
> Single argument like $regparam or whatever name cannot work on all
> architectures, that's why in the very beginning I suggested
> 'func(long, char, void*)' syntax to describe arguments when debuginfo
> is not available. Calling convention for scalars is simple enough on
> all major architectures. x64_64 - trivial, i64_32 - a bit more involved,
> but simple enough so that list of types of arguments is enough to figure
> out which register or register pair or stack should be used to fetch
> argN.
>
>
As Masami has reminded, the use of 'asmlinkage' forces regparm=0, and
we can't destinguish them without debuginfo, so 'func(long, char,
void*)' syntax not work in everywhere.

In fact, all the context infos are there in bpf prog(pt_regs in arg1).
To the non-debuginfo case, without the help of prologue, user steps
following flow to fetch params:

1. pt_regs(arg1) + architecture => calling regs

2. calling regs + function prototype(SEC) + gcc attributes(like
asmlinkage) => formal parameters

'$regparms' do the 1st step, though not a full workaround. But for the
lack of gcc attributes, it seems we can't do the 2nd step. Any ideas?

Thanks