All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing
@ 2015-11-06 13:49 Wang Nan
  2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
                   ` (7 more replies)
  0 siblings, 8 replies; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung; +Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan

This patchset is based on Arnaldo's perf/core.

Previous version can be found from [1].

Patch 1/7 - 4/7 are error message related patches.

Patch 5/7 - 7/7 are testing related patches.

v1 -> v2:

 - Use enum for error code.

 - Eliminate for-loop, directly get error message from array using
   array index.

 - Classify error reason of program loading failure.

 - Improve message when version mismatching.

[1] http://lkml.kernel.org/r/1446697622-4072-1-git-send-email-wangnan0@huawei.com

Wang Nan (7):
  bpf tools: Improve libbpf error reporting
  bpf tools: Add new API bpf_object__get_kversion()
  perf tools: Make fetch_kernel_version() public available
  perf tools: Improve BPF related error messages output
  perf test: Enforce LLVM test: update basic BPF test program
  perf test: Enforce LLVM test: add kbuild test
  perf test: Add 'perf test BPF'

 tools/lib/bpf/libbpf.c                    | 167 +++++++++++++++++-------
 tools/lib/bpf/libbpf.h                    |  21 +++
 tools/perf/tests/Build                    |  17 ++-
 tools/perf/tests/bpf-script-example.c     |   4 +
 tools/perf/tests/bpf-script-test-kbuild.c |  21 +++
 tools/perf/tests/bpf.c                    | 209 ++++++++++++++++++++++++++++++
 tools/perf/tests/builtin-test.c           |   4 +
 tools/perf/tests/llvm.c                   | 137 +++++++++++++++-----
 tools/perf/tests/llvm.h                   |  18 +++
 tools/perf/tests/tests.h                  |   1 +
 tools/perf/util/bpf-loader.c              | 143 +++++++++++++++++---
 tools/perf/util/bpf-loader.h              |  32 +++++
 tools/perf/util/llvm-utils.c              |  49 +++----
 tools/perf/util/parse-events.c            |  11 +-
 tools/perf/util/util.c                    |  31 +++++
 tools/perf/util/util.h                    |   8 ++
 16 files changed, 735 insertions(+), 138 deletions(-)
 create mode 100644 tools/perf/tests/bpf-script-test-kbuild.c
 create mode 100644 tools/perf/tests/bpf.c
 create mode 100644 tools/perf/tests/llvm.h

-- 
1.8.3.4


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

* [PATCH v2 1/7] bpf tools: Improve libbpf error reporting
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-06 14:06   ` Arnaldo Carvalho de Melo
  2015-11-08  7:32   ` [tip:perf/urgent] " tip-bot for Wang Nan
  2015-11-06 13:49 ` [PATCH v2 2/7] bpf tools: Add new API bpf_object__get_kversion() Wang Nan
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

In this patch, a series libbpf specific error numbers and
libbpf_strerror() are created to help reporting error to caller.
Functions are updated to pass correct error number through macro
CHECK_ERR().

All users of bpf_object__open{_buffer}() and bpf_program__title()
in perf are modified accordingly. In addition, due to error code
changing, bpf__strerror_load() also modified to use new error code.

bpf__strerror_head() is also changed accordingly so it can parse
libbpf error. bpf_loader_strerror() is introduced for it, and will
be improved by following patch.

load_program() is improved not to dump log buffer if it is empty. log
buffer is also used to deduce whether the error is caused by invalid
program or other problem.

v1 -> v2:

 - Using macro for error code.

 - Fetch error message based on array index, eliminate for-loop.

 - Use log buffer to detect the reason of failure. 3 new error code
   are introduced to replace LIBBPF_ERRNO__LOAD.

In v1:

# perf record -e ./test_ill_program.o ls
event syntax error: './test_ill_program.o'
                     \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
[SKIP]

# perf record -e ./test_kversion_nomatch_program.o ls
event syntax error: './test_kversion_nomatch_program.o'
                     \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
[SKIP]

# perf record -e ./test_big_program.o ls
event syntax error: './test_big_program.o'
                     \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
[SKIP]

In v2:

# perf record -e ./test_ill_program.o ls
event syntax error: './test_ill_program.o'
                     \___ Kernel verifier blocks program loading
[SKIP]

# perf record -e ./test_kversion_nomatch_program.o
event syntax error: './test_kversion_nomatch_program.o'
                     \___ Incorrect kernel version
[SKIP]
(Will be further improved by following patches)

# perf record -e ./test_big_program.o
event syntax error: './test_big_program.o'
                     \___ Program too big
[SKIP]

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---
 tools/lib/bpf/libbpf.c         | 159 ++++++++++++++++++++++++++++-------------
 tools/lib/bpf/libbpf.h         |  20 ++++++
 tools/perf/tests/llvm.c        |   2 +-
 tools/perf/util/bpf-loader.c   |  33 +++++++--
 tools/perf/util/parse-events.c |   4 +-
 5 files changed, 159 insertions(+), 59 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 9f3c8cf..07b492d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -61,6 +61,60 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 	__pr_debug = debug;
 }
 
+#define STRERR_BUFSIZE  128
+
+#define ERRNO_OFFSET(e)		((e) - __LIBBPF_ERRNO__START)
+#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(LIBBPF_ERRNO__##c)
+#define NR_ERRNO	(__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
+
+static const char *libbpf_strerror_table[NR_ERRNO] = {
+	[ERRCODE_OFFSET(LIBELF)]	= "Something wrong in libelf",
+	[ERRCODE_OFFSET(FORMAT)]	= "BPF object format invalid",
+	[ERRCODE_OFFSET(KVERSION)]	= "'version' section incorrect or lost",
+	[ERRCODE_OFFSET(ENDIAN)]	= "Endian missmatch",
+	[ERRCODE_OFFSET(INTERNAL)]	= "Internal error in libbpf",
+	[ERRCODE_OFFSET(RELOC)]		= "Relocation failed",
+	[ERRCODE_OFFSET(VERIFY)]	= "Kernel verifier blocks program loading",
+	[ERRCODE_OFFSET(PROG2BIG)]	= "Program too big",
+	[ERRCODE_OFFSET(KVER)]		= "Incorrect kernel version",
+};
+
+int libbpf_strerror(int err, char *buf, size_t size)
+{
+	if (!buf || !size)
+		return -1;
+
+	err = err > 0 ? err : -err;
+
+	if (err < __LIBBPF_ERRNO__START) {
+		int ret;
+
+		ret = strerror_r(err, buf, size);
+		buf[size - 1] = '\0';
+		return ret;
+	}
+
+	if (err < __LIBBPF_ERRNO__END) {
+		const char *msg;
+
+		msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
+		snprintf(buf, size, "%s", msg);
+		buf[size - 1] = '\0';
+		return 0;
+	}
+
+	snprintf(buf, size, "Unknown libbpf error %d", err);
+	buf[size - 1] = '\0';
+	return -1;
+}
+
+#define CHECK_ERR(action, err, out) do {	\
+	err = action;			\
+	if (err)			\
+		goto out;		\
+} while(0)
+
+
 /* Copied from tools/perf/util/util.h */
 #ifndef zfree
 # define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
@@ -258,7 +312,7 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
 	if (!obj) {
 		pr_warning("alloc memory failed for %s\n", path);
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	strcpy(obj->path, path);
@@ -305,7 +359,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 
 	if (obj_elf_valid(obj)) {
 		pr_warning("elf init: internal error\n");
-		return -EEXIST;
+		return -LIBBPF_ERRNO__LIBELF;
 	}
 
 	if (obj->efile.obj_buf_sz > 0) {
@@ -331,14 +385,14 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 	if (!obj->efile.elf) {
 		pr_warning("failed to open %s as ELF file\n",
 				obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__LIBELF;
 		goto errout;
 	}
 
 	if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
 		pr_warning("failed to get EHDR from %s\n",
 				obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__FORMAT;
 		goto errout;
 	}
 	ep = &obj->efile.ehdr;
@@ -346,7 +400,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 	if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
 		pr_warning("%s is not an eBPF object file\n",
 			obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__FORMAT;
 		goto errout;
 	}
 
@@ -374,14 +428,14 @@ bpf_object__check_endianness(struct bpf_object *obj)
 			goto mismatch;
 		break;
 	default:
-		return -EINVAL;
+		return -LIBBPF_ERRNO__ENDIAN;
 	}
 
 	return 0;
 
 mismatch:
 	pr_warning("Error: endianness mismatch.\n");
-	return -EINVAL;
+	return -LIBBPF_ERRNO__ENDIAN;
 }
 
 static int
@@ -402,7 +456,7 @@ bpf_object__init_kversion(struct bpf_object *obj,
 
 	if (size != sizeof(kver)) {
 		pr_warning("invalid kver section in %s\n", obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__FORMAT;
 	}
 	memcpy(&kver, data, sizeof(kver));
 	obj->kern_version = kver;
@@ -444,7 +498,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 	if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
 		pr_warning("failed to get e_shstrndx from %s\n",
 			   obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__FORMAT;
 	}
 
 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
@@ -456,7 +510,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (gelf_getshdr(scn, &sh) != &sh) {
 			pr_warning("failed to get section header from %s\n",
 				   obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 
@@ -464,7 +518,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (!name) {
 			pr_warning("failed to get section name from %s\n",
 				   obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 
@@ -472,7 +526,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (!data) {
 			pr_warning("failed to get section data from %s(%s)\n",
 				   name, obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 		pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
@@ -495,7 +549,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			if (obj->efile.symbols) {
 				pr_warning("bpf: multiple SYMTAB in %s\n",
 					   obj->path);
-				err = -EEXIST;
+				err = -LIBBPF_ERRNO__FORMAT;
 			} else
 				obj->efile.symbols = data;
 		} else if ((sh.sh_type == SHT_PROGBITS) &&
@@ -504,7 +558,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			err = bpf_object__add_program(obj, data->d_buf,
 						      data->d_size, name, idx);
 			if (err) {
-				char errmsg[128];
+				char errmsg[STRERR_BUFSIZE];
+
 				strerror_r(-err, errmsg, sizeof(errmsg));
 				pr_warning("failed to alloc program %s (%s): %s",
 					   name, obj->path, errmsg);
@@ -576,7 +631,7 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 
 		if (!gelf_getrel(data, i, &rel)) {
 			pr_warning("relocation: failed to get %d reloc\n", i);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		insn_idx = rel.r_offset / sizeof(struct bpf_insn);
@@ -587,20 +642,20 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 				 &sym)) {
 			pr_warning("relocation: symbol %"PRIx64" not found\n",
 				   GELF_R_SYM(rel.r_info));
-			return -EINVAL;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
 			pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
 				   insn_idx, insns[insn_idx].code);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		map_idx = sym.st_value / sizeof(struct bpf_map_def);
 		if (map_idx >= nr_maps) {
 			pr_warning("bpf relocation: map_idx %d large than %d\n",
 				   (int)map_idx, (int)nr_maps - 1);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		prog->reloc_desc[i].insn_idx = insn_idx;
@@ -683,7 +738,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds)
 		if (insn_idx >= (int)prog->insns_cnt) {
 			pr_warning("relocation out of range: '%s'\n",
 				   prog->section_name);
-			return -ERANGE;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 		insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
 		insns[insn_idx].imm = map_fds[map_idx];
@@ -721,7 +776,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 
 	if (!obj_elf_valid(obj)) {
 		pr_warning("Internal error: elf object is closed\n");
-		return -EINVAL;
+		return -LIBBPF_ERRNO__INTERNAL;
 	}
 
 	for (i = 0; i < obj->efile.nr_reloc; i++) {
@@ -734,21 +789,21 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 
 		if (shdr->sh_type != SHT_REL) {
 			pr_warning("internal error at %d\n", __LINE__);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__INTERNAL;
 		}
 
 		prog = bpf_object__find_prog_by_idx(obj, idx);
 		if (!prog) {
 			pr_warning("relocation failed: no %d section\n",
 				   idx);
-			return -ENOENT;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		err = bpf_program__collect_reloc(prog, nr_maps,
 						 shdr, data,
 						 obj->efile.symbols);
 		if (err)
-			return -EINVAL;
+			return err;
 	}
 	return 0;
 }
@@ -777,13 +832,23 @@ load_program(struct bpf_insn *insns, int insns_cnt,
 		goto out;
 	}
 
-	ret = -EINVAL;
+	ret = -LIBBPF_ERRNO__LOAD;
 	pr_warning("load bpf program failed: %s\n", strerror(errno));
 
-	if (log_buf) {
+	if (log_buf && log_buf[0] != '\0') {
+		ret = -LIBBPF_ERRNO__VERIFY;
 		pr_warning("-- BEGIN DUMP LOG ---\n");
 		pr_warning("\n%s\n", log_buf);
 		pr_warning("-- END LOG --\n");
+	} else {
+		if (insns_cnt >= BPF_MAXINSNS) {
+			pr_warning("Program too large (%d insns), at most %d insns\n",
+				   insns_cnt, BPF_MAXINSNS);
+			ret = -LIBBPF_ERRNO__PROG2BIG;
+		} else if (log_buf) {
+			pr_warning("log buffer is empty\n");
+			ret = -LIBBPF_ERRNO__KVER;
+		}
 	}
 
 out:
@@ -831,7 +896,7 @@ static int bpf_object__validate(struct bpf_object *obj)
 	if (obj->kern_version == 0) {
 		pr_warning("%s doesn't provide kernel version\n",
 			   obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__KVERSION;
 	}
 	return 0;
 }
@@ -840,32 +905,28 @@ static struct bpf_object *
 __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
 	struct bpf_object *obj;
+	int err;
 
 	if (elf_version(EV_CURRENT) == EV_NONE) {
 		pr_warning("failed to init libelf for %s\n", path);
-		return NULL;
+		return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
 	}
 
 	obj = bpf_object__new(path, obj_buf, obj_buf_sz);
-	if (!obj)
-		return NULL;
+	if (IS_ERR(obj))
+		return obj;
 
-	if (bpf_object__elf_init(obj))
-		goto out;
-	if (bpf_object__check_endianness(obj))
-		goto out;
-	if (bpf_object__elf_collect(obj))
-		goto out;
-	if (bpf_object__collect_reloc(obj))
-		goto out;
-	if (bpf_object__validate(obj))
-		goto out;
+	CHECK_ERR(bpf_object__elf_init(obj), err, out);
+	CHECK_ERR(bpf_object__check_endianness(obj), err, out);
+	CHECK_ERR(bpf_object__elf_collect(obj), err, out);
+	CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
+	CHECK_ERR(bpf_object__validate(obj), err, out);
 
 	bpf_object__elf_finish(obj);
 	return obj;
 out:
 	bpf_object__close(obj);
-	return NULL;
+	return ERR_PTR(err);
 }
 
 struct bpf_object *bpf_object__open(const char *path)
@@ -922,6 +983,8 @@ int bpf_object__unload(struct bpf_object *obj)
 
 int bpf_object__load(struct bpf_object *obj)
 {
+	int err;
+
 	if (!obj)
 		return -EINVAL;
 
@@ -931,18 +994,16 @@ int bpf_object__load(struct bpf_object *obj)
 	}
 
 	obj->loaded = true;
-	if (bpf_object__create_maps(obj))
-		goto out;
-	if (bpf_object__relocate(obj))
-		goto out;
-	if (bpf_object__load_progs(obj))
-		goto out;
+
+	CHECK_ERR(bpf_object__create_maps(obj), err, out);
+	CHECK_ERR(bpf_object__relocate(obj), err, out);
+	CHECK_ERR(bpf_object__load_progs(obj), err, out);
 
 	return 0;
 out:
 	bpf_object__unload(obj);
 	pr_warning("failed to load object '%s'\n", obj->path);
-	return -EINVAL;
+	return err;
 }
 
 void bpf_object__close(struct bpf_object *obj)
@@ -990,7 +1051,7 @@ const char *
 bpf_object__get_name(struct bpf_object *obj)
 {
 	if (!obj)
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	return obj->path;
 }
 
@@ -1043,7 +1104,7 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
 		title = strdup(title);
 		if (!title) {
 			pr_warning("failed to strdup program title\n");
-			return NULL;
+			return ERR_PTR(-ENOMEM);
 		}
 	}
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bc80af0..30a40e9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -10,6 +10,26 @@
 
 #include <stdio.h>
 #include <stdbool.h>
+#include <linux/err.h>
+
+enum libbpf_errno {
+	__LIBBPF_ERRNO__START = 4000,
+
+	/* Something wrong in libelf */
+	LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
+	LIBBPF_ERRNO__FORMAT,	/* BPF object format invalid */
+	LIBBPF_ERRNO__KVERSION,	/* Incorrect or no 'version' section */
+	LIBBPF_ERRNO__ENDIAN,	/* Endian missmatch */
+	LIBBPF_ERRNO__INTERNAL,	/* Internal error in libbpf */
+	LIBBPF_ERRNO__RELOC,	/* Relocation failed */
+	LIBBPF_ERRNO__LOAD,	/* Load program failure for unknown reason */
+	LIBBPF_ERRNO__VERIFY,	/* Kernel verifier blocks program loading */
+	LIBBPF_ERRNO__PROG2BIG,	/* Program too big */
+	LIBBPF_ERRNO__KVER,	/* Incorrect kernel version */
+	__LIBBPF_ERRNO__END,
+};
+
+int libbpf_strerror(int err, char *buf, size_t size);
 
 /*
  * In include/linux/compiler-gcc.h, __printf is defined. However
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 512d362..8f713f6 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -27,7 +27,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 	struct bpf_object *obj;
 
 	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
-	if (!obj)
+	if (IS_ERR(obj))
 		return -1;
 	bpf_object__close(obj);
 	return 0;
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 0c5d174..c46256b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -59,9 +59,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 	} else
 		obj = bpf_object__open(filename);
 
-	if (!obj) {
+	if (IS_ERR(obj)) {
 		pr_debug("bpf: failed to load %s\n", filename);
-		return ERR_PTR(-EINVAL);
+		return obj;
 	}
 
 	return obj;
@@ -96,9 +96,9 @@ config_bpf_program(struct bpf_program *prog)
 	int err;
 
 	config_str = bpf_program__title(prog, false);
-	if (!config_str) {
+	if (IS_ERR(config_str)) {
 		pr_debug("bpf: unable to get title for program\n");
-		return -EINVAL;
+		return PTR_ERR(config_str);
 	}
 
 	priv = calloc(sizeof(*priv), 1);
@@ -308,13 +308,34 @@ int bpf__foreach_tev(struct bpf_object *obj,
 	return 0;
 }
 
+static int
+bpf_loader_strerror(int err, char *buf, size_t size)
+{
+	char sbuf[STRERR_BUFSIZE];
+	const char *msg;
+
+	if (!buf || !size)
+		return -1;
+
+	err = err > 0 ? err : -err;
+
+	if (err >= __LIBBPF_ERRNO__START)
+		return libbpf_strerror(err, buf, size);
+
+	msg = strerror_r(err, sbuf, sizeof(sbuf));
+	snprintf(buf, size, "%s", msg);
+	buf[size - 1] = '\0';
+	return 0;
+}
+
 #define bpf__strerror_head(err, buf, size) \
 	char sbuf[STRERR_BUFSIZE], *emsg;\
 	if (!size)\
 		return 0;\
 	if (err < 0)\
 		err = -err;\
-	emsg = strerror_r(err, sbuf, sizeof(sbuf));\
+	bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
+	emsg = sbuf;\
 	switch (err) {\
 	default:\
 		scnprintf(buf, size, "%s", emsg);\
@@ -345,8 +366,6 @@ int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
 		       int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
-	bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?",
-			    emsg)
 	bpf__strerror_end(buf, size);
 	return 0;
 }
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index bee6058..c75b25d 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -632,11 +632,11 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
 	struct bpf_object *obj;
 
 	obj = bpf__prepare_load(bpf_file_name, source);
-	if (IS_ERR(obj) || !obj) {
+	if (IS_ERR(obj)) {
 		char errbuf[BUFSIZ];
 		int err;
 
-		err = obj ? PTR_ERR(obj) : -EINVAL;
+		err = PTR_ERR(obj);
 
 		if (err == -ENOTSUP)
 			snprintf(errbuf, sizeof(errbuf),
-- 
1.8.3.4


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

* [PATCH v2 2/7] bpf tools: Add new API bpf_object__get_kversion()
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
  2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-08  7:32   ` [tip:perf/urgent] bpf tools: Add new API bpf_object__get_kversion () tip-bot for Wang Nan
  2015-11-06 13:49 ` [PATCH v2 3/7] perf tools: Make fetch_kernel_version() public available Wang Nan
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

bpf_object__get_kversion() can be used to fetch value of object's
'version' section. Following patch will use it for error reporting.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---
 tools/lib/bpf/libbpf.c | 8 ++++++++
 tools/lib/bpf/libbpf.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 07b492d..e176bad 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1055,6 +1055,14 @@ bpf_object__get_name(struct bpf_object *obj)
 	return obj->path;
 }
 
+unsigned int
+bpf_object__get_kversion(struct bpf_object *obj)
+{
+	if (!obj)
+		return 0;
+	return obj->kern_version;
+}
+
 struct bpf_program *
 bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
 {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 30a40e9..c9a9aef 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -56,6 +56,7 @@ void bpf_object__close(struct bpf_object *object);
 int bpf_object__load(struct bpf_object *obj);
 int bpf_object__unload(struct bpf_object *obj);
 const char *bpf_object__get_name(struct bpf_object *obj);
+unsigned int bpf_object__get_kversion(struct bpf_object *obj);
 
 struct bpf_object *bpf_object__next(struct bpf_object *prev);
 #define bpf_object__for_each_safe(pos, tmp)			\
-- 
1.8.3.4


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

* [PATCH v2 3/7] perf tools: Make fetch_kernel_version() public available
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
  2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
  2015-11-06 13:49 ` [PATCH v2 2/7] bpf tools: Add new API bpf_object__get_kversion() Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-06 13:55   ` [PATCH v2 3/7 fix] " Wang Nan
  2015-11-06 13:49 ` [PATCH v2 4/7] perf tools: Improve BPF related error messages output Wang Nan
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

There are 2 places in llvm-utils.c which find kernel version
information through uname. This patch extracts uname related code
into one fetch_kernel_version() function and put it into util.h
so other part of code can use it.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/util/llvm-utils.c | 49 +++++++++++++++-----------------------------
 tools/perf/util/util.c       | 31 ++++++++++++++++++++++++++++
 tools/perf/util/util.h       |  3 +++
 3 files changed, 50 insertions(+), 33 deletions(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 8ee25be..00724d4 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -4,7 +4,6 @@
  */
 
 #include <stdio.h>
-#include <sys/utsname.h>
 #include "util.h"
 #include "debug.h"
 #include "llvm-utils.h"
@@ -216,18 +215,19 @@ static int detect_kbuild_dir(char **kbuild_dir)
 	const char *suffix_dir = "";
 
 	char *autoconf_path;
-	struct utsname utsname;
 
 	int err;
 
 	if (!test_dir) {
-		err = uname(&utsname);
-		if (err) {
-			pr_warning("uname failed: %s\n", strerror(errno));
+		/* _UTSNAME_LENGTH is 65 */
+		char release[128];
+
+		err = fetch_kernel_version(NULL, release,
+					   sizeof(release));
+		if (err)
 			return -EINVAL;
-		}
 
-		test_dir = utsname.release;
+		test_dir = release;
 		prefix_dir = "/lib/modules/";
 		suffix_dir = "/build";
 	}
@@ -325,38 +325,18 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
 	pr_debug("include option is set to %s\n", *kbuild_include_opts);
 }
 
-static unsigned long
-fetch_kernel_version(void)
-{
-	struct utsname utsname;
-	int version, patchlevel, sublevel, err;
-
-	if (uname(&utsname))
-		return 0;
-
-	err = sscanf(utsname.release, "%d.%d.%d",
-		     &version, &patchlevel, &sublevel);
-
-	if (err != 3) {
-		pr_debug("Unablt to get kernel version from uname '%s'\n",
-			 utsname.release);
-		return 0;
-	}
-
-	return (version << 16) + (patchlevel << 8) + sublevel;
-}
-
 int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		      size_t *p_obj_buf_sz)
 {
+	size_t obj_buf_sz;
+	void *obj_buf = NULL;
 	int err, nr_cpus_avail;
-	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
+	unsigned int kernel_version;
 	char linux_version_code_str[64];
 	const char *clang_opt = llvm_param.clang_opt;
-	const char *template = llvm_param.clang_bpf_cmd_template;
+	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
 	char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
-	void *obj_buf = NULL;
-	size_t obj_buf_sz;
+	const char *template = llvm_param.clang_bpf_cmd_template;
 
 	if (!template)
 		template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
@@ -388,8 +368,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d",
 		 nr_cpus_avail);
 
+	if (fetch_kernel_version(&kernel_version, NULL, 0))
+		kernel_version = 0;
+
 	snprintf(linux_version_code_str, sizeof(linux_version_code_str),
-		 "0x%lx", fetch_kernel_version());
+		 "0x%x", kernel_version);
 
 	force_set_env("NR_CPUS", nr_cpus_avail_str);
 	force_set_env("LINUX_VERSION_CODE", linux_version_code_str);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index cd12c25..c0ee81c 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -3,6 +3,7 @@
 #include "debug.h"
 #include <api/fs/fs.h>
 #include <sys/mman.h>
+#include <sys/utsname.h>
 #ifdef HAVE_BACKTRACE_SUPPORT
 #include <execinfo.h>
 #endif
@@ -665,3 +666,33 @@ bool find_process(const char *name)
 	closedir(dir);
 	return ret ? false : true;
 }
+
+int
+fetch_kernel_version(unsigned int *puint, char *str,
+		     size_t str_size)
+{
+	struct utsname utsname;
+	int version, patchlevel, sublevel, err;
+
+	if (uname(&utsname))
+		return -1;
+
+	if (str && str_size) {
+		strncpy(str, utsname.release, str_size);
+		str[str_size - 1] = '\0';
+	}
+
+	err = sscanf(utsname.release, "%d.%d.%d",
+		     &version, &patchlevel, &sublevel);
+
+	if (err != 3) {
+		pr_debug("Unablt to get kernel version from uname '%s'\n",
+			 utsname.release);
+		return -1;
+	}
+
+	if (puint)
+		*puint = (version << 16) + (patchlevel << 8) + sublevel;
+	return 0;
+}
+
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 4cfb913..2665126 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -350,4 +350,7 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int
 
 int get_stack_size(const char *str, unsigned long *_size);
 
+int fetch_kernel_version(unsigned int *puint,
+			 char *str, size_t str_sz);
+
 #endif /* GIT_COMPAT_UTIL_H */
-- 
1.8.3.4


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

* [PATCH v2 4/7] perf tools: Improve BPF related error messages output
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
                   ` (2 preceding siblings ...)
  2015-11-06 13:49 ` [PATCH v2 3/7] perf tools: Make fetch_kernel_version() public available Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-06 13:58   ` [PATCH v2 4/7 fix] " Wang Nan
  2015-11-06 13:49 ` [PATCH v2 5/7] perf test: Enforce LLVM test: update basic BPF test program Wang Nan
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

A series of bpf loader related error code is introduced to help error
delivering. Functions are improved to return those new error code.
Functions which return pointers are adjusted to encode error code into
return value using "ERR_PTR".

bpf_loader_strerror() is improved to convert those error message to
string. It detected the value of error code and calls libbpf_strerror()
and strerror_r() accordingly, so caller don't need to consider checking
the range of error code.

In bpf__strerror_load(), print kernel version of running kernel and
the object's 'version' section to notify user how to fix his/her
program.

v1 -> v2:
 Use macro for error code.

 Fetch error message based on array index, eliminate for-loop.

 Print version strings.

Before:

# perf record -e ./test_kversion_nomatch_program.o sleep 1
event syntax error: './test_kversion_nomatch_program.o'
                     \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
[SKIP]

After:

# perf record -e ./test_kversion_nomatch_program.o ls
event syntax error: './test_kversion_nomatch_program.o'
                     \___ 'version' (4.4.0) doesn't match running kernel (4.3.0)
[SKIP]

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---
 tools/perf/util/bpf-loader.c   | 92 +++++++++++++++++++++++++++++++++++-------
 tools/perf/util/bpf-loader.h   | 22 ++++++++++
 tools/perf/util/parse-events.c |  7 ++--
 tools/perf/util/util.h         |  5 +++
 4 files changed, 109 insertions(+), 17 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index c46256b..19b496b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -53,7 +53,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 
 		err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
 		if (err)
-			return ERR_PTR(err);
+			return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
 		obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename);
 		free(obj_buf);
 	} else
@@ -113,14 +113,14 @@ config_bpf_program(struct bpf_program *prog)
 	if (err < 0) {
 		pr_debug("bpf: '%s' is not a valid config string\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__CONFIG;
 		goto errout;
 	}
 
 	if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
 		pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
 			 config_str, PERF_BPF_PROBE_GROUP);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__GROUP;
 		goto errout;
 	} else if (!pev->group)
 		pev->group = strdup(PERF_BPF_PROBE_GROUP);
@@ -132,9 +132,9 @@ config_bpf_program(struct bpf_program *prog)
 	}
 
 	if (!pev->event) {
-		pr_debug("bpf: '%s': event name is missing\n",
+		pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__EVENTNAME;
 		goto errout;
 	}
 	pr_debug("bpf: config '%s' is ok\n", config_str);
@@ -285,7 +285,7 @@ int bpf__foreach_tev(struct bpf_object *obj,
 				(void **)&priv);
 		if (err || !priv) {
 			pr_debug("bpf: failed to get private field\n");
-			return -EINVAL;
+			return -BPF_LOADER_ERRNO__INTERNAL;
 		}
 
 		pev = &priv->pev;
@@ -308,6 +308,18 @@ int bpf__foreach_tev(struct bpf_object *obj,
 	return 0;
 }
 
+#define ERRNO_OFFSET(e)		((e) - __BPF_LOADER_ERRNO__START)
+#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
+#define NR_ERRNO	(__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
+
+static const char *bpf_loader_strerror_table[NR_ERRNO] = {
+	[ERRCODE_OFFSET(CONFIG)]	= "Invalid config string",
+	[ERRCODE_OFFSET(GROUP)]		= "Invalid group name",
+	[ERRCODE_OFFSET(EVENTNAME)]	= "No event name found in config string",
+	[ERRCODE_OFFSET(INTERNAL)]	= "BPF loader internal error",
+	[ERRCODE_OFFSET(COMPILE)]	= "Error when compiling BPF scriptlet",
+};
+
 static int
 bpf_loader_strerror(int err, char *buf, size_t size)
 {
@@ -322,16 +334,27 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	if (err >= __LIBBPF_ERRNO__START)
 		return libbpf_strerror(err, buf, size);
 
-	msg = strerror_r(err, sbuf, sizeof(sbuf));
-	snprintf(buf, size, "%s", msg);
+	if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) {
+		msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)];
+		snprintf(buf, size, "%s", msg);
+		buf[size - 1] = '\0';
+		return 0;
+	}
+
+	if (err >= __BPF_LOADER_ERRNO__END)
+		snprintf(buf, size, "Unknown bpf loader error %d", err);
+	else
+		snprintf(buf, size, "%s",
+			 strerror_r(err, sbuf, sizeof(sbuf)));
+
 	buf[size - 1] = '\0';
-	return 0;
+	return -1;
 }
 
 #define bpf__strerror_head(err, buf, size) \
 	char sbuf[STRERR_BUFSIZE], *emsg;\
-	if (!size)\
-		return 0;\
+if (!size)\
+return 0;\
 	if (err < 0)\
 		err = -err;\
 	bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
@@ -351,21 +374,62 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	}\
 	buf[size - 1] = '\0';
 
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size)
+{
+	size_t n;
+	int ret;
+
+	n = snprintf(buf, size, "Failed to load %s%s: ",
+			 filename, source ? " from source" : "");
+	if (n >= size) {
+		buf[size - 1] = '\0';
+		return 0;
+	}
+	buf += n;
+	size -= n;
+
+	ret = bpf_loader_strerror(err, buf, size);
+	buf[size - 1] = '\0';
+	return ret;
+}
+
 int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 			int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
 	bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
-	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n");
-	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n");
+	bpf__strerror_entry(EACCES, "You need to be root");
+	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
+	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
 	bpf__strerror_end(buf, size);
 	return 0;
 }
 
-int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
+int bpf__strerror_load(struct bpf_object *obj,
 		       int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
+	case LIBBPF_ERRNO__KVER: {
+		unsigned int obj_kver = bpf_object__get_kversion(obj);
+		unsigned int real_kver;
+
+		if (fetch_kernel_version(&real_kver, NULL, 0)) {
+			scnprintf(buf, size, "Unable to fetch kernel version");
+			break;
+		}
+
+		if (obj_kver != real_kver) {
+			scnprintf(buf, size,
+				  "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")",
+				  KVER_PARAM(obj_kver),
+				  KVER_PARAM(real_kver));
+			break;
+		}
+
+		scnprintf(buf, size, "Failed to load program for unknown reason");
+		break;
+	}
 	bpf__strerror_end(buf, size);
 	return 0;
 }
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index ccd8d7f..2c39710 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -8,9 +8,21 @@
 #include <linux/compiler.h>
 #include <linux/err.h>
 #include <string.h>
+#include <bpf/libbpf.h>
 #include "probe-event.h"
 #include "debug.h"
 
+enum bpf_loader_errno {
+	__BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100,
+	/* Invalid config string */
+	BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START,
+	BPF_LOADER_ERRNO__GROUP,	/* Invalid group name */
+	BPF_LOADER_ERRNO__EVENTNAME,	/* Event name is missing */
+	BPF_LOADER_ERRNO__INTERNAL,	/* BPF loader internal error */
+	BPF_LOADER_ERRNO__COMPILE,	/* Error when compiling BPF scriptlet */
+	__BPF_LOADER_ERRNO__END,
+};
+
 struct bpf_object;
 #define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
 
@@ -19,6 +31,8 @@ typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
 
 #ifdef HAVE_LIBBPF_SUPPORT
 struct bpf_object *bpf__prepare_load(const char *filename, bool source);
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size);
 
 void bpf__clear(void);
 
@@ -67,6 +81,14 @@ __bpf_strerror(char *buf, size_t size)
 	return 0;
 }
 
+int bpf__strerror_prepare_load(const char *filename __maybe_unused,
+			       bool source __maybe_unused,
+			       int err __maybe_unused,
+			       char *buf, size_t size)
+{
+	return __bpf_strerror(buf, size);
+}
+
 static inline int
 bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 		    int err __maybe_unused,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c75b25d..e48d9da 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -642,9 +642,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
 			snprintf(errbuf, sizeof(errbuf),
 				 "BPF support is not compiled");
 		else
-			snprintf(errbuf, sizeof(errbuf),
-				 "BPF object file '%s' is invalid",
-				 bpf_file_name);
+			bpf__strerror_prepare_load(bpf_file_name,
+						   source,
+						   -err, errbuf,
+						   sizeof(errbuf));
 
 		data->error->help = strdup("(add -v to see detail)");
 		data->error->str = strdup(errbuf);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 2665126..dcc6590 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -352,5 +352,10 @@ int get_stack_size(const char *str, unsigned long *_size);
 
 int fetch_kernel_version(unsigned int *puint,
 			 char *str, size_t str_sz);
+#define KVER_VERSION(x)		(((x) >> 16) & 0xff)
+#define KVER_PATCHLEVEL(x)	(((x) >> 8) & 0xff)
+#define KVER_SUBLEVEL(x)	((x) & 0xff)
+#define KVER_FMT	"%d.%d.%d"
+#define KVER_PARAM(x)	KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x)
 
 #endif /* GIT_COMPAT_UTIL_H */
-- 
1.8.3.4


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

* [PATCH v2 5/7] perf test: Enforce LLVM test: update basic BPF test program
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
                   ` (3 preceding siblings ...)
  2015-11-06 13:49 ` [PATCH v2 4/7] perf tools: Improve BPF related error messages output Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-08  7:34   ` [tip:perf/urgent] perf test: Enhance the " tip-bot for Wang Nan
  2015-11-06 13:49 ` [PATCH v2 6/7] perf test: Enforce LLVM test: add kbuild test Wang Nan
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan, He Kuang,
	Arnaldo Carvalho de Melo

This patch replaces the original toy BPF program with previous introduced
bpf-script-example.c. Dynamically embedded it into 'llvm-src-base.c'.

The newly introduced BPF program attaches a BPF program to
'sys_epoll_pwait()'. perf itself never use that syscall, so further test
can verify their result with it. The program would generate 1 sample
in every 2 calls of epoll_pwait() system call.

Since the resuling BPF object is useful, test_llvm__fetch_bpf_obj() is
introduced for creating BPF objects for source. llvm test is rewritten
according to it.

Signed-off-by: He Kuang <hekuang@huawei.com>
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 tools/perf/tests/Build                |   9 ++-
 tools/perf/tests/bpf-script-example.c |   4 ++
 tools/perf/tests/llvm.c               | 131 ++++++++++++++++++++++++++--------
 tools/perf/tests/llvm.h               |  16 +++++
 4 files changed, 129 insertions(+), 31 deletions(-)
 create mode 100644 tools/perf/tests/llvm.h

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 50de225..6c095b3 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,9 +31,16 @@ perf-y += sample-parsing.o
 perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
-perf-y += llvm.o
+perf-y += llvm.o llvm-src-base.o
 perf-y += topology.o
 
+$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
+	$(call rule_mkdir)
+	$(Q)echo '#include <tests/llvm.h>' > $@
+	$(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@
+	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+	$(Q)echo ';' >> $@
+
 ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 endif
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
index 410a70b..0ec9c2c 100644
--- a/tools/perf/tests/bpf-script-example.c
+++ b/tools/perf/tests/bpf-script-example.c
@@ -1,3 +1,7 @@
+/*
+ * bpf-script-example.c
+ * Test basic LLVM building
+ */
 #ifndef LINUX_VERSION_CODE
 # error Need LINUX_VERSION_CODE
 # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 8f713f6..05683c5 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -2,6 +2,7 @@
 #include <bpf/libbpf.h>
 #include <util/llvm-utils.h>
 #include <util/cache.h>
+#include "llvm.h"
 #include "tests.h"
 #include "debug.h"
 
@@ -11,16 +12,6 @@ static int perf_config_cb(const char *var, const char *val,
 	return perf_default_config(var, val, arg);
 }
 
-/*
- * Randomly give it a "version" section since we don't really load it
- * into kernel
- */
-static const char test_bpf_prog[] =
-	"__attribute__((section(\"do_fork\"), used)) "
-	"int fork(void *ctx) {return 0;} "
-	"char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
-	"int _version __attribute__((section(\"version\"), used)) = 0x40100;";
-
 #ifdef HAVE_LIBBPF_SUPPORT
 static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 {
@@ -28,25 +19,47 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 
 	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
 	if (IS_ERR(obj))
-		return -1;
+		return TEST_FAIL;
 	bpf_object__close(obj);
-	return 0;
+	return TEST_OK;
 }
 #else
 static int test__bpf_parsing(void *obj_buf __maybe_unused,
 			     size_t obj_buf_sz __maybe_unused)
 {
 	pr_debug("Skip bpf parsing\n");
-	return 0;
+	return TEST_OK;
 }
 #endif
 
-int test__llvm(void)
+static struct {
+	const char *source;
+	const char *desc;
+} bpf_source_table[__LLVM_TESTCASE_MAX] = {
+	[LLVM_TESTCASE_BASE] = {
+		.source = test_llvm__bpf_base_prog,
+		.desc = "Basic BPF llvm compiling test",
+	},
+};
+
+
+int
+test_llvm__fetch_bpf_obj(void **p_obj_buf,
+			 size_t *p_obj_buf_sz,
+			 enum test_llvm__testcase index,
+			 bool force)
 {
-	char *tmpl_new, *clang_opt_new;
-	void *obj_buf;
-	size_t obj_buf_sz;
-	int err, old_verbose;
+	const char *source;
+	const char *desc;
+	const char *tmpl_old, *clang_opt_old;
+	char *tmpl_new = NULL, *clang_opt_new = NULL;
+	int err, old_verbose, ret = TEST_FAIL;
+
+	if (index >= __LLVM_TESTCASE_MAX)
+		return TEST_FAIL;
+
+	source = bpf_source_table[index].source;
+	desc = bpf_source_table[index].desc;
 
 	perf_config(perf_config_cb, NULL);
 
@@ -54,42 +67,100 @@ int test__llvm(void)
 	 * Skip this test if user's .perfconfig doesn't set [llvm] section
 	 * and clang is not found in $PATH, and this is not perf test -v
 	 */
-	if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) {
+	if (!force && (verbose == 0 &&
+		       !llvm_param.user_set_param &&
+		       llvm__search_clang())) {
 		pr_debug("No clang and no verbosive, skip this test\n");
 		return TEST_SKIP;
 	}
 
-	old_verbose = verbose;
 	/*
 	 * llvm is verbosity when error. Suppress all error output if
 	 * not 'perf test -v'.
 	 */
+	old_verbose = verbose;
 	if (verbose == 0)
 		verbose = -1;
 
+	*p_obj_buf = NULL;
+	*p_obj_buf_sz = 0;
+
 	if (!llvm_param.clang_bpf_cmd_template)
-		return -1;
+		goto out;
 
 	if (!llvm_param.clang_opt)
 		llvm_param.clang_opt = strdup("");
 
-	err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
-		       llvm_param.clang_bpf_cmd_template);
+	err = asprintf(&tmpl_new, "echo '%s' | %s%s", source,
+		       llvm_param.clang_bpf_cmd_template,
+		       old_verbose ? "" : " 2>/dev/null");
 	if (err < 0)
-		return -1;
+		goto out;
 	err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
 	if (err < 0)
-		return -1;
+		goto out;
 
+	tmpl_old = llvm_param.clang_bpf_cmd_template;
 	llvm_param.clang_bpf_cmd_template = tmpl_new;
+	clang_opt_old = llvm_param.clang_opt;
 	llvm_param.clang_opt = clang_opt_new;
-	err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
+
+	err = llvm__compile_bpf("-", p_obj_buf, p_obj_buf_sz);
+
+	llvm_param.clang_bpf_cmd_template = tmpl_old;
+	llvm_param.clang_opt = clang_opt_old;
 
 	verbose = old_verbose;
 	if (err)
-		return TEST_FAIL;
+		goto out;
+
+	ret = TEST_OK;
+out:
+	free(tmpl_new);
+	free(clang_opt_new);
+	if (ret != TEST_OK)
+		pr_debug("Failed to compile test case: '%s'\n", desc);
+	return ret;
+}
 
-	err = test__bpf_parsing(obj_buf, obj_buf_sz);
-	free(obj_buf);
-	return err;
+int test__llvm(void)
+{
+	enum test_llvm__testcase i;
+
+	for (i = 0; i < __LLVM_TESTCASE_MAX; i++) {
+		int ret;
+		void *obj_buf = NULL;
+		size_t obj_buf_sz = 0;
+
+		ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+					       i, false);
+
+		if (ret == TEST_OK) {
+			ret = test__bpf_parsing(obj_buf, obj_buf_sz);
+			if (ret != TEST_OK)
+				pr_debug("Failed to parse test case '%s'\n",
+					 bpf_source_table[i].desc);
+		}
+		free(obj_buf);
+
+		switch (ret) {
+		case TEST_SKIP:
+			return TEST_SKIP;
+		case TEST_OK:
+			break;
+		default:
+			/*
+			 * Test 0 is the basic LLVM test. If test 0
+			 * fail, the basic LLVM support not functional
+			 * so the whole test should fail. If other test
+			 * case fail, it can be fixed by adjusting
+			 * config so don't report error.
+			 */
+			if (i == 0)
+				return TEST_FAIL;
+			else
+				return TEST_SKIP;
+		}
+	}
+	return TEST_OK;
 }
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
new file mode 100644
index 0000000..bd63cee
--- /dev/null
+++ b/tools/perf/tests/llvm.h
@@ -0,0 +1,16 @@
+#ifndef PERF_TEST_LLVM_H
+#define PERF_TEST_LLVM_H
+
+#include <stddef.h> /* for size_t */
+#include <stdbool.h> /* for bool */
+
+extern const char test_llvm__bpf_base_prog[];
+
+enum test_llvm__testcase {
+	LLVM_TESTCASE_BASE,
+	__LLVM_TESTCASE_MAX,
+};
+
+int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz,
+			     enum test_llvm__testcase index, bool force);
+#endif
-- 
1.8.3.4


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

* [PATCH v2 6/7] perf test: Enforce LLVM test: add kbuild test
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
                   ` (4 preceding siblings ...)
  2015-11-06 13:49 ` [PATCH v2 5/7] perf test: Enforce LLVM test: update basic BPF test program Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-08  7:34   ` [tip:perf/urgent] perf test: Enhance the LLVM tests: " tip-bot for Wang Nan
  2015-11-06 13:49 ` [PATCH v2 7/7] perf test: Add 'perf test BPF' Wang Nan
  2015-11-06 19:20 ` [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Arnaldo Carvalho de Melo
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

This patch adds a kbuild testcase to check whether kernel headers
can be correctly found.

For example:
 # mv /lib/modules/4.3.0-rc5{,.bak}
 # perf test LLVM

   38: Test LLVM searching and compiling                        : Skip

 # perf test -v LLVM
 ...
 <stdin>:11:10: fatal error: 'uapi/linux/fs.h' file not found
 #include <uapi/linux/fs.h>
          ^
 1 error generated.
 ERROR:	unable to compile -
 Hint:	Check error message shown above.
 Hint:	You can also pre-compile it into .o using:
     		clang -target bpf -O2 -c -
	with proper -I and -D options.
 Failed to compile test case: 'Test kbuild searching'
 test child finished with -2

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 tools/perf/tests/Build                    |  9 ++++++++-
 tools/perf/tests/bpf-script-test-kbuild.c | 21 +++++++++++++++++++++
 tools/perf/tests/llvm.c                   |  4 ++++
 tools/perf/tests/llvm.h                   |  2 ++
 4 files changed, 35 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/tests/bpf-script-test-kbuild.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 6c095b3..a47b211 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,7 +31,7 @@ perf-y += sample-parsing.o
 perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
-perf-y += llvm.o llvm-src-base.o
+perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
@@ -41,6 +41,13 @@ $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
 	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
 	$(Q)echo ';' >> $@
 
+$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
+	$(call rule_mkdir)
+	$(Q)echo '#include <tests/llvm.h>' > $@
+	$(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@
+	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+	$(Q)echo ';' >> $@
+
 ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 endif
diff --git a/tools/perf/tests/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c
new file mode 100644
index 0000000..3626924
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-kbuild.c
@@ -0,0 +1,21 @@
+/*
+ * bpf-script-test-kbuild.c
+ * Test include from kernel header
+ */
+#ifndef LINUX_VERSION_CODE
+# error Need LINUX_VERSION_CODE
+# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
+#endif
+#define SEC(NAME) __attribute__((section(NAME), used))
+
+#include <uapi/linux/fs.h>
+#include <uapi/asm/ptrace.h>
+
+SEC("func=vfs_llseek")
+int bpf_func__vfs_llseek(void *ctx)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 05683c5..bc4cf50 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -40,6 +40,10 @@ static struct {
 		.source = test_llvm__bpf_base_prog,
 		.desc = "Basic BPF llvm compiling test",
 	},
+	[LLVM_TESTCASE_KBUILD] = {
+		.source = test_llvm__bpf_test_kbuild_prog,
+		.desc = "Test kbuild searching",
+	},
 };
 
 
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index bd63cee..d91d8f4 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -5,9 +5,11 @@
 #include <stdbool.h> /* for bool */
 
 extern const char test_llvm__bpf_base_prog[];
+extern const char test_llvm__bpf_test_kbuild_prog[];
 
 enum test_llvm__testcase {
 	LLVM_TESTCASE_BASE,
+	LLVM_TESTCASE_KBUILD,
 	__LLVM_TESTCASE_MAX,
 };
 
-- 
1.8.3.4


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

* [PATCH v2 7/7] perf test: Add 'perf test BPF'
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
                   ` (5 preceding siblings ...)
  2015-11-06 13:49 ` [PATCH v2 6/7] perf test: Enforce LLVM test: add kbuild test Wang Nan
@ 2015-11-06 13:49 ` Wang Nan
  2015-11-08  7:34   ` [tip:perf/urgent] " tip-bot for Wang Nan
  2015-11-06 19:20 ` [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Arnaldo Carvalho de Melo
  7 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:49 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo, Alexei Starovoitov

This patch adds BPF testcase for testing BPF event filtering.

By utilizing the result of 'perf test LLVM', this patch compiles the
eBPF sample program then test it ability. The BPF script in 'perf test
LLVM' let only 50% samples generated by epoll_pwait() be captured.
This patch runs that system call for 111 times, so the resule should
contain 56 samples.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
---
 tools/perf/tests/Build          |   1 +
 tools/perf/tests/bpf.c          | 209 ++++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/builtin-test.c |   4 +
 tools/perf/tests/tests.h        |   1 +
 tools/perf/util/bpf-loader.c    |  24 ++++-
 tools/perf/util/bpf-loader.h    |  10 ++
 6 files changed, 248 insertions(+), 1 deletion(-)
 create mode 100644 tools/perf/tests/bpf.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index a47b211..f41ebf8 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
 perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o
+perf-y += bpf.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
new file mode 100644
index 0000000..ec16f78
--- /dev/null
+++ b/tools/perf/tests/bpf.c
@@ -0,0 +1,209 @@
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <util/bpf-loader.h>
+#include <util/evlist.h>
+#include "tests.h"
+#include "llvm.h"
+#include "debug.h"
+#define NR_ITERS       111
+
+#ifdef HAVE_LIBBPF_SUPPORT
+
+static int epoll_pwait_loop(void)
+{
+	int i;
+
+	/* Should fail NR_ITERS times */
+	for (i = 0; i < NR_ITERS; i++)
+		epoll_pwait(-(i + 1), NULL, 0, 0, NULL);
+	return 0;
+}
+
+static struct {
+	enum test_llvm__testcase prog_id;
+	const char *desc;
+	const char *name;
+	const char *msg_compile_fail;
+	const char *msg_load_fail;
+	int (*target_func)(void);
+	int expect_result;
+} bpf_testcase_table[] = {
+	{
+		LLVM_TESTCASE_BASE,
+		"Test basic BPF filtering",
+		"[basic_bpf_test]",
+		"fix 'perf test LLVM' first",
+		"load bpf object failed",
+		&epoll_pwait_loop,
+		(NR_ITERS + 1) / 2,
+	},
+};
+
+static int do_test(struct bpf_object *obj, int (*func)(void),
+		   int expect)
+{
+	struct record_opts opts = {
+		.target = {
+			.uid = UINT_MAX,
+			.uses_mmap = true,
+		},
+		.freq	      = 0,
+		.mmap_pages   = 256,
+		.default_interval = 1,
+	};
+
+	char pid[16];
+	char sbuf[STRERR_BUFSIZE];
+	struct perf_evlist *evlist;
+	int i, ret = TEST_FAIL, err = 0, count = 0;
+
+	struct parse_events_evlist parse_evlist;
+	struct parse_events_error parse_error;
+
+	bzero(&parse_error, sizeof(parse_error));
+	bzero(&parse_evlist, sizeof(parse_evlist));
+	parse_evlist.error = &parse_error;
+	INIT_LIST_HEAD(&parse_evlist.list);
+
+	err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
+	if (err || list_empty(&parse_evlist.list)) {
+		pr_debug("Failed to add events selected by BPF\n");
+		if (!err)
+			return TEST_FAIL;
+	}
+
+	snprintf(pid, sizeof(pid), "%d", getpid());
+	pid[sizeof(pid) - 1] = '\0';
+	opts.target.tid = opts.target.pid = pid;
+
+	/* Instead of perf_evlist__new_default, don't add default events */
+	evlist = perf_evlist__new();
+	if (!evlist) {
+		pr_debug("No ehough memory to create evlist\n");
+		return TEST_FAIL;
+	}
+
+	err = perf_evlist__create_maps(evlist, &opts.target);
+	if (err < 0) {
+		pr_debug("Not enough memory to create thread/cpu maps\n");
+		goto out_delete_evlist;
+	}
+
+	perf_evlist__splice_list_tail(evlist, &parse_evlist.list);
+	evlist->nr_groups = parse_evlist.nr_groups;
+
+	perf_evlist__config(evlist, &opts);
+
+	err = perf_evlist__open(evlist);
+	if (err < 0) {
+		pr_debug("perf_evlist__open: %s\n",
+			 strerror_r(errno, sbuf, sizeof(sbuf)));
+		goto out_delete_evlist;
+	}
+
+	err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
+	if (err < 0) {
+		pr_debug("perf_evlist__mmap: %s\n",
+			 strerror_r(errno, sbuf, sizeof(sbuf)));
+		goto out_delete_evlist;
+	}
+
+	perf_evlist__enable(evlist);
+	(*func)();
+	perf_evlist__disable(evlist);
+
+	for (i = 0; i < evlist->nr_mmaps; i++) {
+		union perf_event *event;
+
+		while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+			const u32 type = event->header.type;
+
+			if (type == PERF_RECORD_SAMPLE)
+				count ++;
+		}
+	}
+
+	if (count != expect)
+		pr_debug("BPF filter result incorrect\n");
+
+	ret = TEST_OK;
+
+out_delete_evlist:
+	perf_evlist__delete(evlist);
+	return ret;
+}
+
+static struct bpf_object *
+prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+	struct bpf_object *obj;
+
+	obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name);
+	if (IS_ERR(obj)) {
+		pr_debug("Compile BPF program failed.\n");
+		return NULL;
+	}
+	return obj;
+}
+
+static int __test__bpf(int index)
+{
+	int ret;
+	void *obj_buf;
+	size_t obj_buf_sz;
+	struct bpf_object *obj;
+
+	ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+				       bpf_testcase_table[index].prog_id,
+				       true);
+	if (ret != TEST_OK || !obj_buf || !obj_buf_sz) {
+		pr_debug("Unable to get BPF object, %s\n",
+			 bpf_testcase_table[index].msg_compile_fail);
+		if (index == 0)
+			return TEST_SKIP;
+		else
+			return TEST_FAIL;
+	}
+
+	obj = prepare_bpf(obj_buf, obj_buf_sz,
+			  bpf_testcase_table[index].name);
+	if (!obj) {
+		ret = TEST_FAIL;
+		goto out;
+	}
+
+	ret = do_test(obj,
+		      bpf_testcase_table[index].target_func,
+		      bpf_testcase_table[index].expect_result);
+out:
+	bpf__clear();
+	return ret;
+}
+
+int test__bpf(void)
+{
+	unsigned int i;
+	int err;
+
+	if (geteuid() != 0) {
+		pr_debug("Only root can run BPF test\n");
+		return TEST_SKIP;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
+		err = __test__bpf(i);
+
+		if (err != TEST_OK)
+			return err;
+	}
+
+	return TEST_OK;
+}
+
+#else
+int test__bpf(void)
+{
+	pr_debug("Skip BPF test because BPF support is not compiled\n");
+	return TEST_SKIP;
+}
+#endif
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 66f72d3..7b0120a 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -166,6 +166,10 @@ static struct test generic_tests[] = {
 		.func = test_session_topology,
 	},
 	{
+		.desc = "Test BPF filter",
+		.func = test__bpf,
+	},
+	{
 		.func = NULL,
 	},
 };
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index c804869..3c8734a 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -66,6 +66,7 @@ int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 int test__thread_map(void);
 int test__llvm(void);
+int test__bpf(void);
 int test_session_topology(void);
 
 #if defined(__arm__) || defined(__aarch64__)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 19b496b..636a4d7 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -34,10 +34,32 @@ struct bpf_prog_priv {
 	struct perf_probe_event pev;
 };
 
+static bool libbpf_initialized;
+
+struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+	struct bpf_object *obj;
+
+	if (!libbpf_initialized) {
+		libbpf_set_print(libbpf_warning,
+				 libbpf_info,
+				 libbpf_debug);
+		libbpf_initialized = true;
+	}
+
+	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name);
+	if (IS_ERR(obj)) {
+		pr_debug("bpf: failed to load buffer\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return obj;
+}
+
 struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 {
 	struct bpf_object *obj;
-	static bool libbpf_initialized;
 
 	if (!libbpf_initialized) {
 		libbpf_set_print(libbpf_warning,
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 2c39710..bc82133 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -34,6 +34,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source);
 int bpf__strerror_prepare_load(const char *filename, bool source,
 			       int err, char *buf, size_t size);
 
+struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz,
+					    const char *name);
+
 void bpf__clear(void);
 
 int bpf__probe(struct bpf_object *obj);
@@ -55,6 +58,13 @@ bpf__prepare_load(const char *filename __maybe_unused,
 	return ERR_PTR(-ENOTSUP);
 }
 
+static inline struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf __maybe_unused,
+					   size_t obj_buf_sz __maybe_unused)
+{
+	return ERR_PTR(-ENOTSUP);
+}
+
 static inline void bpf__clear(void) { }
 
 static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;}
-- 
1.8.3.4


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

* [PATCH v2 3/7 fix] perf tools: Make fetch_kernel_version() public available
  2015-11-06 13:49 ` [PATCH v2 3/7] perf tools: Make fetch_kernel_version() public available Wang Nan
@ 2015-11-06 13:55   ` Wang Nan
  2015-11-08  7:33     ` [tip:perf/urgent] perf tools: Make fetch_kernel_version() publicly available tip-bot for Wang Nan
  0 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:55 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

There are 2 places in llvm-utils.c which find kernel version
information through uname. This patch extracts uname related code
into one fetch_kernel_version() function and put it into util.h
so other part of code can use it.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---

An blank line is detected in tools/perf/util/util.c. Resend to remove it.

---
 tools/perf/util/llvm-utils.c | 49 +++++++++++++++-----------------------------
 tools/perf/util/util.c       | 30 +++++++++++++++++++++++++++
 tools/perf/util/util.h       |  3 +++
 3 files changed, 49 insertions(+), 33 deletions(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 8ee25be..00724d4 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -4,7 +4,6 @@
  */
 
 #include <stdio.h>
-#include <sys/utsname.h>
 #include "util.h"
 #include "debug.h"
 #include "llvm-utils.h"
@@ -216,18 +215,19 @@ static int detect_kbuild_dir(char **kbuild_dir)
 	const char *suffix_dir = "";
 
 	char *autoconf_path;
-	struct utsname utsname;
 
 	int err;
 
 	if (!test_dir) {
-		err = uname(&utsname);
-		if (err) {
-			pr_warning("uname failed: %s\n", strerror(errno));
+		/* _UTSNAME_LENGTH is 65 */
+		char release[128];
+
+		err = fetch_kernel_version(NULL, release,
+					   sizeof(release));
+		if (err)
 			return -EINVAL;
-		}
 
-		test_dir = utsname.release;
+		test_dir = release;
 		prefix_dir = "/lib/modules/";
 		suffix_dir = "/build";
 	}
@@ -325,38 +325,18 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
 	pr_debug("include option is set to %s\n", *kbuild_include_opts);
 }
 
-static unsigned long
-fetch_kernel_version(void)
-{
-	struct utsname utsname;
-	int version, patchlevel, sublevel, err;
-
-	if (uname(&utsname))
-		return 0;
-
-	err = sscanf(utsname.release, "%d.%d.%d",
-		     &version, &patchlevel, &sublevel);
-
-	if (err != 3) {
-		pr_debug("Unablt to get kernel version from uname '%s'\n",
-			 utsname.release);
-		return 0;
-	}
-
-	return (version << 16) + (patchlevel << 8) + sublevel;
-}
-
 int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		      size_t *p_obj_buf_sz)
 {
+	size_t obj_buf_sz;
+	void *obj_buf = NULL;
 	int err, nr_cpus_avail;
-	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
+	unsigned int kernel_version;
 	char linux_version_code_str[64];
 	const char *clang_opt = llvm_param.clang_opt;
-	const char *template = llvm_param.clang_bpf_cmd_template;
+	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
 	char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
-	void *obj_buf = NULL;
-	size_t obj_buf_sz;
+	const char *template = llvm_param.clang_bpf_cmd_template;
 
 	if (!template)
 		template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
@@ -388,8 +368,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d",
 		 nr_cpus_avail);
 
+	if (fetch_kernel_version(&kernel_version, NULL, 0))
+		kernel_version = 0;
+
 	snprintf(linux_version_code_str, sizeof(linux_version_code_str),
-		 "0x%lx", fetch_kernel_version());
+		 "0x%x", kernel_version);
 
 	force_set_env("NR_CPUS", nr_cpus_avail_str);
 	force_set_env("LINUX_VERSION_CODE", linux_version_code_str);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index cd12c25..47b1e36 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -3,6 +3,7 @@
 #include "debug.h"
 #include <api/fs/fs.h>
 #include <sys/mman.h>
+#include <sys/utsname.h>
 #ifdef HAVE_BACKTRACE_SUPPORT
 #include <execinfo.h>
 #endif
@@ -665,3 +666,32 @@ bool find_process(const char *name)
 	closedir(dir);
 	return ret ? false : true;
 }
+
+int
+fetch_kernel_version(unsigned int *puint, char *str,
+		     size_t str_size)
+{
+	struct utsname utsname;
+	int version, patchlevel, sublevel, err;
+
+	if (uname(&utsname))
+		return -1;
+
+	if (str && str_size) {
+		strncpy(str, utsname.release, str_size);
+		str[str_size - 1] = '\0';
+	}
+
+	err = sscanf(utsname.release, "%d.%d.%d",
+		     &version, &patchlevel, &sublevel);
+
+	if (err != 3) {
+		pr_debug("Unablt to get kernel version from uname '%s'\n",
+			 utsname.release);
+		return -1;
+	}
+
+	if (puint)
+		*puint = (version << 16) + (patchlevel << 8) + sublevel;
+	return 0;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 4cfb913..2665126 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -350,4 +350,7 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int
 
 int get_stack_size(const char *str, unsigned long *_size);
 
+int fetch_kernel_version(unsigned int *puint,
+			 char *str, size_t str_sz);
+
 #endif /* GIT_COMPAT_UTIL_H */
-- 
1.8.3.4


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

* [PATCH v2 4/7 fix] perf tools: Improve BPF related error messages output
  2015-11-06 13:49 ` [PATCH v2 4/7] perf tools: Improve BPF related error messages output Wang Nan
@ 2015-11-06 13:58   ` Wang Nan
  2015-11-08  7:33     ` [tip:perf/urgent] perf bpf: Improve BPF related error messages tip-bot for Wang Nan
  0 siblings, 1 reply; 19+ messages in thread
From: Wang Nan @ 2015-11-06 13:58 UTC (permalink / raw)
  To: acme, namhyung
  Cc: lizefan, pi3orama, linux-kernel, jolsa, Wang Nan,
	Arnaldo Carvalho de Melo

A series of bpf loader related error code is introduced to help error
delivering. Functions are improved to return those new error code.
Functions which return pointers are adjusted to encode error code into
return value using "ERR_PTR".

bpf_loader_strerror() is improved to convert those error message to
string. It detected the value of error code and calls libbpf_strerror()
and strerror_r() accordingly, so caller don't need to consider checking
the range of error code.

In bpf__strerror_load(), print kernel version of running kernel and
the object's 'version' section to notify user how to fix his/her
program.

v1 -> v2:
 Use macro for error code.

 Fetch error message based on array index, eliminate for-loop.

 Print version strings.

Before:

# perf record -e ./test_kversion_nomatch_program.o sleep 1
event syntax error: './test_kversion_nomatch_program.o'
                     \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
[SKIP]

After:

# perf record -e ./test_kversion_nomatch_program.o ls
event syntax error: './test_kversion_nomatch_program.o'
                     \___ 'version' (4.4.0) doesn't match running kernel (4.3.0)
[SKIP]

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
---

Previous patch breaks idention of 2 lines. Resend for fixing that.

---
 tools/perf/util/bpf-loader.c   | 88 ++++++++++++++++++++++++++++++++++++------
 tools/perf/util/bpf-loader.h   | 22 +++++++++++
 tools/perf/util/parse-events.c |  7 ++--
 tools/perf/util/util.h         |  5 +++
 4 files changed, 107 insertions(+), 15 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index c46256b..e3afa1b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -53,7 +53,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 
 		err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
 		if (err)
-			return ERR_PTR(err);
+			return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
 		obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename);
 		free(obj_buf);
 	} else
@@ -113,14 +113,14 @@ config_bpf_program(struct bpf_program *prog)
 	if (err < 0) {
 		pr_debug("bpf: '%s' is not a valid config string\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__CONFIG;
 		goto errout;
 	}
 
 	if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
 		pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
 			 config_str, PERF_BPF_PROBE_GROUP);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__GROUP;
 		goto errout;
 	} else if (!pev->group)
 		pev->group = strdup(PERF_BPF_PROBE_GROUP);
@@ -132,9 +132,9 @@ config_bpf_program(struct bpf_program *prog)
 	}
 
 	if (!pev->event) {
-		pr_debug("bpf: '%s': event name is missing\n",
+		pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__EVENTNAME;
 		goto errout;
 	}
 	pr_debug("bpf: config '%s' is ok\n", config_str);
@@ -285,7 +285,7 @@ int bpf__foreach_tev(struct bpf_object *obj,
 				(void **)&priv);
 		if (err || !priv) {
 			pr_debug("bpf: failed to get private field\n");
-			return -EINVAL;
+			return -BPF_LOADER_ERRNO__INTERNAL;
 		}
 
 		pev = &priv->pev;
@@ -308,6 +308,18 @@ int bpf__foreach_tev(struct bpf_object *obj,
 	return 0;
 }
 
+#define ERRNO_OFFSET(e)		((e) - __BPF_LOADER_ERRNO__START)
+#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
+#define NR_ERRNO	(__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
+
+static const char *bpf_loader_strerror_table[NR_ERRNO] = {
+	[ERRCODE_OFFSET(CONFIG)]	= "Invalid config string",
+	[ERRCODE_OFFSET(GROUP)]		= "Invalid group name",
+	[ERRCODE_OFFSET(EVENTNAME)]	= "No event name found in config string",
+	[ERRCODE_OFFSET(INTERNAL)]	= "BPF loader internal error",
+	[ERRCODE_OFFSET(COMPILE)]	= "Error when compiling BPF scriptlet",
+};
+
 static int
 bpf_loader_strerror(int err, char *buf, size_t size)
 {
@@ -322,10 +334,21 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	if (err >= __LIBBPF_ERRNO__START)
 		return libbpf_strerror(err, buf, size);
 
-	msg = strerror_r(err, sbuf, sizeof(sbuf));
-	snprintf(buf, size, "%s", msg);
+	if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) {
+		msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)];
+		snprintf(buf, size, "%s", msg);
+		buf[size - 1] = '\0';
+		return 0;
+	}
+
+	if (err >= __BPF_LOADER_ERRNO__END)
+		snprintf(buf, size, "Unknown bpf loader error %d", err);
+	else
+		snprintf(buf, size, "%s",
+			 strerror_r(err, sbuf, sizeof(sbuf)));
+
 	buf[size - 1] = '\0';
-	return 0;
+	return -1;
 }
 
 #define bpf__strerror_head(err, buf, size) \
@@ -351,21 +374,62 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	}\
 	buf[size - 1] = '\0';
 
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size)
+{
+	size_t n;
+	int ret;
+
+	n = snprintf(buf, size, "Failed to load %s%s: ",
+			 filename, source ? " from source" : "");
+	if (n >= size) {
+		buf[size - 1] = '\0';
+		return 0;
+	}
+	buf += n;
+	size -= n;
+
+	ret = bpf_loader_strerror(err, buf, size);
+	buf[size - 1] = '\0';
+	return ret;
+}
+
 int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 			int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
 	bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
-	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n");
-	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n");
+	bpf__strerror_entry(EACCES, "You need to be root");
+	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
+	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
 	bpf__strerror_end(buf, size);
 	return 0;
 }
 
-int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
+int bpf__strerror_load(struct bpf_object *obj,
 		       int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
+	case LIBBPF_ERRNO__KVER: {
+		unsigned int obj_kver = bpf_object__get_kversion(obj);
+		unsigned int real_kver;
+
+		if (fetch_kernel_version(&real_kver, NULL, 0)) {
+			scnprintf(buf, size, "Unable to fetch kernel version");
+			break;
+		}
+
+		if (obj_kver != real_kver) {
+			scnprintf(buf, size,
+				  "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")",
+				  KVER_PARAM(obj_kver),
+				  KVER_PARAM(real_kver));
+			break;
+		}
+
+		scnprintf(buf, size, "Failed to load program for unknown reason");
+		break;
+	}
 	bpf__strerror_end(buf, size);
 	return 0;
 }
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index ccd8d7f..2c39710 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -8,9 +8,21 @@
 #include <linux/compiler.h>
 #include <linux/err.h>
 #include <string.h>
+#include <bpf/libbpf.h>
 #include "probe-event.h"
 #include "debug.h"
 
+enum bpf_loader_errno {
+	__BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100,
+	/* Invalid config string */
+	BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START,
+	BPF_LOADER_ERRNO__GROUP,	/* Invalid group name */
+	BPF_LOADER_ERRNO__EVENTNAME,	/* Event name is missing */
+	BPF_LOADER_ERRNO__INTERNAL,	/* BPF loader internal error */
+	BPF_LOADER_ERRNO__COMPILE,	/* Error when compiling BPF scriptlet */
+	__BPF_LOADER_ERRNO__END,
+};
+
 struct bpf_object;
 #define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
 
@@ -19,6 +31,8 @@ typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
 
 #ifdef HAVE_LIBBPF_SUPPORT
 struct bpf_object *bpf__prepare_load(const char *filename, bool source);
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size);
 
 void bpf__clear(void);
 
@@ -67,6 +81,14 @@ __bpf_strerror(char *buf, size_t size)
 	return 0;
 }
 
+int bpf__strerror_prepare_load(const char *filename __maybe_unused,
+			       bool source __maybe_unused,
+			       int err __maybe_unused,
+			       char *buf, size_t size)
+{
+	return __bpf_strerror(buf, size);
+}
+
 static inline int
 bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 		    int err __maybe_unused,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c75b25d..e48d9da 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -642,9 +642,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
 			snprintf(errbuf, sizeof(errbuf),
 				 "BPF support is not compiled");
 		else
-			snprintf(errbuf, sizeof(errbuf),
-				 "BPF object file '%s' is invalid",
-				 bpf_file_name);
+			bpf__strerror_prepare_load(bpf_file_name,
+						   source,
+						   -err, errbuf,
+						   sizeof(errbuf));
 
 		data->error->help = strdup("(add -v to see detail)");
 		data->error->str = strdup(errbuf);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 2665126..dcc6590 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -352,5 +352,10 @@ int get_stack_size(const char *str, unsigned long *_size);
 
 int fetch_kernel_version(unsigned int *puint,
 			 char *str, size_t str_sz);
+#define KVER_VERSION(x)		(((x) >> 16) & 0xff)
+#define KVER_PATCHLEVEL(x)	(((x) >> 8) & 0xff)
+#define KVER_SUBLEVEL(x)	((x) & 0xff)
+#define KVER_FMT	"%d.%d.%d"
+#define KVER_PARAM(x)	KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x)
 
 #endif /* GIT_COMPAT_UTIL_H */
-- 
1.8.3.4


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

* Re: [PATCH v2 1/7] bpf tools: Improve libbpf error reporting
  2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
@ 2015-11-06 14:06   ` Arnaldo Carvalho de Melo
  2015-11-08  7:32   ` [tip:perf/urgent] " tip-bot for Wang Nan
  1 sibling, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2015-11-06 14:06 UTC (permalink / raw)
  To: Wang Nan; +Cc: namhyung, lizefan, pi3orama, linux-kernel, jolsa

Em Fri, Nov 06, 2015 at 01:49:37PM +0000, Wang Nan escreveu:
> In this patch, a series libbpf specific error numbers and
> libbpf_strerror() are created to help reporting error to caller.
> Functions are updated to pass correct error number through macro
> CHECK_ERR().
> 
> All users of bpf_object__open{_buffer}() and bpf_program__title()
> in perf are modified accordingly. In addition, due to error code
> changing, bpf__strerror_load() also modified to use new error code.
> 
> bpf__strerror_head() is also changed accordingly so it can parse
> libbpf error. bpf_loader_strerror() is introduced for it, and will
> be improved by following patch.
> 
> load_program() is improved not to dump log buffer if it is empty. log
> buffer is also used to deduce whether the error is caused by invalid
> program or other problem.
> 
> v1 -> v2:
> 
>  - Using macro for error code.
> 
>  - Fetch error message based on array index, eliminate for-loop.
> 
>  - Use log buffer to detect the reason of failure. 3 new error code
>    are introduced to replace LIBBPF_ERRNO__LOAD.

Thanks a lot for taking into account, from what I quickly skimmed, all
the review comments, really appreciated!

Applying and retesting!

- Arnaldo

> In v1:
> 
> # perf record -e ./test_ill_program.o ls
> event syntax error: './test_ill_program.o'
>                      \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
> [SKIP]
> 
> # perf record -e ./test_kversion_nomatch_program.o ls
> event syntax error: './test_kversion_nomatch_program.o'
>                      \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
> [SKIP]
> 
> # perf record -e ./test_big_program.o ls
> event syntax error: './test_big_program.o'
>                      \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
> [SKIP]
> 
> In v2:
> 
> # perf record -e ./test_ill_program.o ls
> event syntax error: './test_ill_program.o'
>                      \___ Kernel verifier blocks program loading
> [SKIP]
> 
> # perf record -e ./test_kversion_nomatch_program.o
> event syntax error: './test_kversion_nomatch_program.o'
>                      \___ Incorrect kernel version
> [SKIP]
> (Will be further improved by following patches)
> 
> # perf record -e ./test_big_program.o
> event syntax error: './test_big_program.o'
>                      \___ Program too big
> [SKIP]
> 
> Signed-off-by: Wang Nan <wangnan0@huawei.com>
> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
> Cc: Namhyung Kim <namhyung@kernel.org>
> ---
>  tools/lib/bpf/libbpf.c         | 159 ++++++++++++++++++++++++++++-------------
>  tools/lib/bpf/libbpf.h         |  20 ++++++
>  tools/perf/tests/llvm.c        |   2 +-
>  tools/perf/util/bpf-loader.c   |  33 +++++++--
>  tools/perf/util/parse-events.c |   4 +-
>  5 files changed, 159 insertions(+), 59 deletions(-)
> 
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 9f3c8cf..07b492d 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -61,6 +61,60 @@ void libbpf_set_print(libbpf_print_fn_t warn,
>  	__pr_debug = debug;
>  }
>  
> +#define STRERR_BUFSIZE  128
> +
> +#define ERRNO_OFFSET(e)		((e) - __LIBBPF_ERRNO__START)
> +#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(LIBBPF_ERRNO__##c)
> +#define NR_ERRNO	(__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
> +
> +static const char *libbpf_strerror_table[NR_ERRNO] = {
> +	[ERRCODE_OFFSET(LIBELF)]	= "Something wrong in libelf",
> +	[ERRCODE_OFFSET(FORMAT)]	= "BPF object format invalid",
> +	[ERRCODE_OFFSET(KVERSION)]	= "'version' section incorrect or lost",
> +	[ERRCODE_OFFSET(ENDIAN)]	= "Endian missmatch",
> +	[ERRCODE_OFFSET(INTERNAL)]	= "Internal error in libbpf",
> +	[ERRCODE_OFFSET(RELOC)]		= "Relocation failed",
> +	[ERRCODE_OFFSET(VERIFY)]	= "Kernel verifier blocks program loading",
> +	[ERRCODE_OFFSET(PROG2BIG)]	= "Program too big",
> +	[ERRCODE_OFFSET(KVER)]		= "Incorrect kernel version",
> +};
> +
> +int libbpf_strerror(int err, char *buf, size_t size)
> +{
> +	if (!buf || !size)
> +		return -1;
> +
> +	err = err > 0 ? err : -err;
> +
> +	if (err < __LIBBPF_ERRNO__START) {
> +		int ret;
> +
> +		ret = strerror_r(err, buf, size);
> +		buf[size - 1] = '\0';
> +		return ret;
> +	}
> +
> +	if (err < __LIBBPF_ERRNO__END) {
> +		const char *msg;
> +
> +		msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
> +		snprintf(buf, size, "%s", msg);
> +		buf[size - 1] = '\0';
> +		return 0;
> +	}
> +
> +	snprintf(buf, size, "Unknown libbpf error %d", err);
> +	buf[size - 1] = '\0';
> +	return -1;
> +}
> +
> +#define CHECK_ERR(action, err, out) do {	\
> +	err = action;			\
> +	if (err)			\
> +		goto out;		\
> +} while(0)
> +
> +
>  /* Copied from tools/perf/util/util.h */
>  #ifndef zfree
>  # define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
> @@ -258,7 +312,7 @@ static struct bpf_object *bpf_object__new(const char *path,
>  	obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
>  	if (!obj) {
>  		pr_warning("alloc memory failed for %s\n", path);
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  	}
>  
>  	strcpy(obj->path, path);
> @@ -305,7 +359,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
>  
>  	if (obj_elf_valid(obj)) {
>  		pr_warning("elf init: internal error\n");
> -		return -EEXIST;
> +		return -LIBBPF_ERRNO__LIBELF;
>  	}
>  
>  	if (obj->efile.obj_buf_sz > 0) {
> @@ -331,14 +385,14 @@ static int bpf_object__elf_init(struct bpf_object *obj)
>  	if (!obj->efile.elf) {
>  		pr_warning("failed to open %s as ELF file\n",
>  				obj->path);
> -		err = -EINVAL;
> +		err = -LIBBPF_ERRNO__LIBELF;
>  		goto errout;
>  	}
>  
>  	if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
>  		pr_warning("failed to get EHDR from %s\n",
>  				obj->path);
> -		err = -EINVAL;
> +		err = -LIBBPF_ERRNO__FORMAT;
>  		goto errout;
>  	}
>  	ep = &obj->efile.ehdr;
> @@ -346,7 +400,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
>  	if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
>  		pr_warning("%s is not an eBPF object file\n",
>  			obj->path);
> -		err = -EINVAL;
> +		err = -LIBBPF_ERRNO__FORMAT;
>  		goto errout;
>  	}
>  
> @@ -374,14 +428,14 @@ bpf_object__check_endianness(struct bpf_object *obj)
>  			goto mismatch;
>  		break;
>  	default:
> -		return -EINVAL;
> +		return -LIBBPF_ERRNO__ENDIAN;
>  	}
>  
>  	return 0;
>  
>  mismatch:
>  	pr_warning("Error: endianness mismatch.\n");
> -	return -EINVAL;
> +	return -LIBBPF_ERRNO__ENDIAN;
>  }
>  
>  static int
> @@ -402,7 +456,7 @@ bpf_object__init_kversion(struct bpf_object *obj,
>  
>  	if (size != sizeof(kver)) {
>  		pr_warning("invalid kver section in %s\n", obj->path);
> -		return -EINVAL;
> +		return -LIBBPF_ERRNO__FORMAT;
>  	}
>  	memcpy(&kver, data, sizeof(kver));
>  	obj->kern_version = kver;
> @@ -444,7 +498,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  	if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
>  		pr_warning("failed to get e_shstrndx from %s\n",
>  			   obj->path);
> -		return -EINVAL;
> +		return -LIBBPF_ERRNO__FORMAT;
>  	}
>  
>  	while ((scn = elf_nextscn(elf, scn)) != NULL) {
> @@ -456,7 +510,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  		if (gelf_getshdr(scn, &sh) != &sh) {
>  			pr_warning("failed to get section header from %s\n",
>  				   obj->path);
> -			err = -EINVAL;
> +			err = -LIBBPF_ERRNO__FORMAT;
>  			goto out;
>  		}
>  
> @@ -464,7 +518,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  		if (!name) {
>  			pr_warning("failed to get section name from %s\n",
>  				   obj->path);
> -			err = -EINVAL;
> +			err = -LIBBPF_ERRNO__FORMAT;
>  			goto out;
>  		}
>  
> @@ -472,7 +526,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  		if (!data) {
>  			pr_warning("failed to get section data from %s(%s)\n",
>  				   name, obj->path);
> -			err = -EINVAL;
> +			err = -LIBBPF_ERRNO__FORMAT;
>  			goto out;
>  		}
>  		pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
> @@ -495,7 +549,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  			if (obj->efile.symbols) {
>  				pr_warning("bpf: multiple SYMTAB in %s\n",
>  					   obj->path);
> -				err = -EEXIST;
> +				err = -LIBBPF_ERRNO__FORMAT;
>  			} else
>  				obj->efile.symbols = data;
>  		} else if ((sh.sh_type == SHT_PROGBITS) &&
> @@ -504,7 +558,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
>  			err = bpf_object__add_program(obj, data->d_buf,
>  						      data->d_size, name, idx);
>  			if (err) {
> -				char errmsg[128];
> +				char errmsg[STRERR_BUFSIZE];
> +
>  				strerror_r(-err, errmsg, sizeof(errmsg));
>  				pr_warning("failed to alloc program %s (%s): %s",
>  					   name, obj->path, errmsg);
> @@ -576,7 +631,7 @@ bpf_program__collect_reloc(struct bpf_program *prog,
>  
>  		if (!gelf_getrel(data, i, &rel)) {
>  			pr_warning("relocation: failed to get %d reloc\n", i);
> -			return -EINVAL;
> +			return -LIBBPF_ERRNO__FORMAT;
>  		}
>  
>  		insn_idx = rel.r_offset / sizeof(struct bpf_insn);
> @@ -587,20 +642,20 @@ bpf_program__collect_reloc(struct bpf_program *prog,
>  				 &sym)) {
>  			pr_warning("relocation: symbol %"PRIx64" not found\n",
>  				   GELF_R_SYM(rel.r_info));
> -			return -EINVAL;
> +			return -LIBBPF_ERRNO__FORMAT;
>  		}
>  
>  		if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
>  			pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
>  				   insn_idx, insns[insn_idx].code);
> -			return -EINVAL;
> +			return -LIBBPF_ERRNO__RELOC;
>  		}
>  
>  		map_idx = sym.st_value / sizeof(struct bpf_map_def);
>  		if (map_idx >= nr_maps) {
>  			pr_warning("bpf relocation: map_idx %d large than %d\n",
>  				   (int)map_idx, (int)nr_maps - 1);
> -			return -EINVAL;
> +			return -LIBBPF_ERRNO__RELOC;
>  		}
>  
>  		prog->reloc_desc[i].insn_idx = insn_idx;
> @@ -683,7 +738,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds)
>  		if (insn_idx >= (int)prog->insns_cnt) {
>  			pr_warning("relocation out of range: '%s'\n",
>  				   prog->section_name);
> -			return -ERANGE;
> +			return -LIBBPF_ERRNO__RELOC;
>  		}
>  		insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
>  		insns[insn_idx].imm = map_fds[map_idx];
> @@ -721,7 +776,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
>  
>  	if (!obj_elf_valid(obj)) {
>  		pr_warning("Internal error: elf object is closed\n");
> -		return -EINVAL;
> +		return -LIBBPF_ERRNO__INTERNAL;
>  	}
>  
>  	for (i = 0; i < obj->efile.nr_reloc; i++) {
> @@ -734,21 +789,21 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
>  
>  		if (shdr->sh_type != SHT_REL) {
>  			pr_warning("internal error at %d\n", __LINE__);
> -			return -EINVAL;
> +			return -LIBBPF_ERRNO__INTERNAL;
>  		}
>  
>  		prog = bpf_object__find_prog_by_idx(obj, idx);
>  		if (!prog) {
>  			pr_warning("relocation failed: no %d section\n",
>  				   idx);
> -			return -ENOENT;
> +			return -LIBBPF_ERRNO__RELOC;
>  		}
>  
>  		err = bpf_program__collect_reloc(prog, nr_maps,
>  						 shdr, data,
>  						 obj->efile.symbols);
>  		if (err)
> -			return -EINVAL;
> +			return err;
>  	}
>  	return 0;
>  }
> @@ -777,13 +832,23 @@ load_program(struct bpf_insn *insns, int insns_cnt,
>  		goto out;
>  	}
>  
> -	ret = -EINVAL;
> +	ret = -LIBBPF_ERRNO__LOAD;
>  	pr_warning("load bpf program failed: %s\n", strerror(errno));
>  
> -	if (log_buf) {
> +	if (log_buf && log_buf[0] != '\0') {
> +		ret = -LIBBPF_ERRNO__VERIFY;
>  		pr_warning("-- BEGIN DUMP LOG ---\n");
>  		pr_warning("\n%s\n", log_buf);
>  		pr_warning("-- END LOG --\n");
> +	} else {
> +		if (insns_cnt >= BPF_MAXINSNS) {
> +			pr_warning("Program too large (%d insns), at most %d insns\n",
> +				   insns_cnt, BPF_MAXINSNS);
> +			ret = -LIBBPF_ERRNO__PROG2BIG;
> +		} else if (log_buf) {
> +			pr_warning("log buffer is empty\n");
> +			ret = -LIBBPF_ERRNO__KVER;
> +		}
>  	}
>  
>  out:
> @@ -831,7 +896,7 @@ static int bpf_object__validate(struct bpf_object *obj)
>  	if (obj->kern_version == 0) {
>  		pr_warning("%s doesn't provide kernel version\n",
>  			   obj->path);
> -		return -EINVAL;
> +		return -LIBBPF_ERRNO__KVERSION;
>  	}
>  	return 0;
>  }
> @@ -840,32 +905,28 @@ static struct bpf_object *
>  __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
>  {
>  	struct bpf_object *obj;
> +	int err;
>  
>  	if (elf_version(EV_CURRENT) == EV_NONE) {
>  		pr_warning("failed to init libelf for %s\n", path);
> -		return NULL;
> +		return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
>  	}
>  
>  	obj = bpf_object__new(path, obj_buf, obj_buf_sz);
> -	if (!obj)
> -		return NULL;
> +	if (IS_ERR(obj))
> +		return obj;
>  
> -	if (bpf_object__elf_init(obj))
> -		goto out;
> -	if (bpf_object__check_endianness(obj))
> -		goto out;
> -	if (bpf_object__elf_collect(obj))
> -		goto out;
> -	if (bpf_object__collect_reloc(obj))
> -		goto out;
> -	if (bpf_object__validate(obj))
> -		goto out;
> +	CHECK_ERR(bpf_object__elf_init(obj), err, out);
> +	CHECK_ERR(bpf_object__check_endianness(obj), err, out);
> +	CHECK_ERR(bpf_object__elf_collect(obj), err, out);
> +	CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
> +	CHECK_ERR(bpf_object__validate(obj), err, out);
>  
>  	bpf_object__elf_finish(obj);
>  	return obj;
>  out:
>  	bpf_object__close(obj);
> -	return NULL;
> +	return ERR_PTR(err);
>  }
>  
>  struct bpf_object *bpf_object__open(const char *path)
> @@ -922,6 +983,8 @@ int bpf_object__unload(struct bpf_object *obj)
>  
>  int bpf_object__load(struct bpf_object *obj)
>  {
> +	int err;
> +
>  	if (!obj)
>  		return -EINVAL;
>  
> @@ -931,18 +994,16 @@ int bpf_object__load(struct bpf_object *obj)
>  	}
>  
>  	obj->loaded = true;
> -	if (bpf_object__create_maps(obj))
> -		goto out;
> -	if (bpf_object__relocate(obj))
> -		goto out;
> -	if (bpf_object__load_progs(obj))
> -		goto out;
> +
> +	CHECK_ERR(bpf_object__create_maps(obj), err, out);
> +	CHECK_ERR(bpf_object__relocate(obj), err, out);
> +	CHECK_ERR(bpf_object__load_progs(obj), err, out);
>  
>  	return 0;
>  out:
>  	bpf_object__unload(obj);
>  	pr_warning("failed to load object '%s'\n", obj->path);
> -	return -EINVAL;
> +	return err;
>  }
>  
>  void bpf_object__close(struct bpf_object *obj)
> @@ -990,7 +1051,7 @@ const char *
>  bpf_object__get_name(struct bpf_object *obj)
>  {
>  	if (!obj)
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  	return obj->path;
>  }
>  
> @@ -1043,7 +1104,7 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
>  		title = strdup(title);
>  		if (!title) {
>  			pr_warning("failed to strdup program title\n");
> -			return NULL;
> +			return ERR_PTR(-ENOMEM);
>  		}
>  	}
>  
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index bc80af0..30a40e9 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -10,6 +10,26 @@
>  
>  #include <stdio.h>
>  #include <stdbool.h>
> +#include <linux/err.h>
> +
> +enum libbpf_errno {
> +	__LIBBPF_ERRNO__START = 4000,
> +
> +	/* Something wrong in libelf */
> +	LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
> +	LIBBPF_ERRNO__FORMAT,	/* BPF object format invalid */
> +	LIBBPF_ERRNO__KVERSION,	/* Incorrect or no 'version' section */
> +	LIBBPF_ERRNO__ENDIAN,	/* Endian missmatch */
> +	LIBBPF_ERRNO__INTERNAL,	/* Internal error in libbpf */
> +	LIBBPF_ERRNO__RELOC,	/* Relocation failed */
> +	LIBBPF_ERRNO__LOAD,	/* Load program failure for unknown reason */
> +	LIBBPF_ERRNO__VERIFY,	/* Kernel verifier blocks program loading */
> +	LIBBPF_ERRNO__PROG2BIG,	/* Program too big */
> +	LIBBPF_ERRNO__KVER,	/* Incorrect kernel version */
> +	__LIBBPF_ERRNO__END,
> +};
> +
> +int libbpf_strerror(int err, char *buf, size_t size);
>  
>  /*
>   * In include/linux/compiler-gcc.h, __printf is defined. However
> diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
> index 512d362..8f713f6 100644
> --- a/tools/perf/tests/llvm.c
> +++ b/tools/perf/tests/llvm.c
> @@ -27,7 +27,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
>  	struct bpf_object *obj;
>  
>  	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
> -	if (!obj)
> +	if (IS_ERR(obj))
>  		return -1;
>  	bpf_object__close(obj);
>  	return 0;
> diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
> index 0c5d174..c46256b 100644
> --- a/tools/perf/util/bpf-loader.c
> +++ b/tools/perf/util/bpf-loader.c
> @@ -59,9 +59,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
>  	} else
>  		obj = bpf_object__open(filename);
>  
> -	if (!obj) {
> +	if (IS_ERR(obj)) {
>  		pr_debug("bpf: failed to load %s\n", filename);
> -		return ERR_PTR(-EINVAL);
> +		return obj;
>  	}
>  
>  	return obj;
> @@ -96,9 +96,9 @@ config_bpf_program(struct bpf_program *prog)
>  	int err;
>  
>  	config_str = bpf_program__title(prog, false);
> -	if (!config_str) {
> +	if (IS_ERR(config_str)) {
>  		pr_debug("bpf: unable to get title for program\n");
> -		return -EINVAL;
> +		return PTR_ERR(config_str);
>  	}
>  
>  	priv = calloc(sizeof(*priv), 1);
> @@ -308,13 +308,34 @@ int bpf__foreach_tev(struct bpf_object *obj,
>  	return 0;
>  }
>  
> +static int
> +bpf_loader_strerror(int err, char *buf, size_t size)
> +{
> +	char sbuf[STRERR_BUFSIZE];
> +	const char *msg;
> +
> +	if (!buf || !size)
> +		return -1;
> +
> +	err = err > 0 ? err : -err;
> +
> +	if (err >= __LIBBPF_ERRNO__START)
> +		return libbpf_strerror(err, buf, size);
> +
> +	msg = strerror_r(err, sbuf, sizeof(sbuf));
> +	snprintf(buf, size, "%s", msg);
> +	buf[size - 1] = '\0';
> +	return 0;
> +}
> +
>  #define bpf__strerror_head(err, buf, size) \
>  	char sbuf[STRERR_BUFSIZE], *emsg;\
>  	if (!size)\
>  		return 0;\
>  	if (err < 0)\
>  		err = -err;\
> -	emsg = strerror_r(err, sbuf, sizeof(sbuf));\
> +	bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
> +	emsg = sbuf;\
>  	switch (err) {\
>  	default:\
>  		scnprintf(buf, size, "%s", emsg);\
> @@ -345,8 +366,6 @@ int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
>  		       int err, char *buf, size_t size)
>  {
>  	bpf__strerror_head(err, buf, size);
> -	bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?",
> -			    emsg)
>  	bpf__strerror_end(buf, size);
>  	return 0;
>  }
> diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
> index bee6058..c75b25d 100644
> --- a/tools/perf/util/parse-events.c
> +++ b/tools/perf/util/parse-events.c
> @@ -632,11 +632,11 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
>  	struct bpf_object *obj;
>  
>  	obj = bpf__prepare_load(bpf_file_name, source);
> -	if (IS_ERR(obj) || !obj) {
> +	if (IS_ERR(obj)) {
>  		char errbuf[BUFSIZ];
>  		int err;
>  
> -		err = obj ? PTR_ERR(obj) : -EINVAL;
> +		err = PTR_ERR(obj);
>  
>  		if (err == -ENOTSUP)
>  			snprintf(errbuf, sizeof(errbuf),
> -- 
> 1.8.3.4

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

* Re: [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing
  2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
                   ` (6 preceding siblings ...)
  2015-11-06 13:49 ` [PATCH v2 7/7] perf test: Add 'perf test BPF' Wang Nan
@ 2015-11-06 19:20 ` Arnaldo Carvalho de Melo
  7 siblings, 0 replies; 19+ messages in thread
From: Arnaldo Carvalho de Melo @ 2015-11-06 19:20 UTC (permalink / raw)
  To: Wang Nan
  Cc: namhyung, lizefan, pi3orama, linux-kernel, jolsa, Ingo Molnar,
	Alexei Starovoitov

Em Fri, Nov 06, 2015 at 01:49:36PM +0000, Wang Nan escreveu:
> This patchset is based on Arnaldo's perf/core.
> 
> Previous version can be found from [1].
> 
> Patch 1/7 - 4/7 are error message related patches.
> 
> Patch 5/7 - 7/7 are testing related patches.
> 

I'll try to fix this one and then push all this, looking really good!

- Arnaldo

- make_no_libbpf: cd . && make -f Makefile DESTDIR=/tmp/tmp.qfPojSrh2g NO_LIBBPF=1
cd . && make -f Makefile DESTDIR=/tmp/tmp.qfPojSrh2g NO_LIBBPF=1
  BUILD:   Doing 'make -j4' parallel build

Auto-detecting system features:
...                         dwarf: [ on  ]
...                         glibc: [ on  ]
...                          gtk2: [ on  ]
...                      libaudit: [ on  ]
...                        libbfd: [ on  ]
...                        libelf: [ on  ]
...                       libnuma: [ on  ]
...        numa_num_possible_cpus: [ on  ]
...                       libperl: [ on  ]
...                     libpython: [ on  ]
...                      libslang: [ on  ]
...                     libunwind: [ on  ]
...            libdw-dwarf-unwind: [ on  ]
...                          zlib: [ on  ]
...                          lzma: [ on  ]
...                     get_cpuid: [ on  ]
...                           bpf: [ on  ]

  GEN      common-cmds.h
  CC       fixdep.o
  LD       fixdep-in.o
  FLEX     util/parse-events-flex.c
<SNIP>
  FLEX     util/pmu-flex.c
  CC       util/pmu-bison.o
  CC       util/parse-events.o
  CC       ui/browsers/annotate.o
  CC       ui/tui/setup.o
In file included from util/parse-events.c:14:0:
util/bpf-loader.h:84:5: error: no previous prototype for ‘bpf__strerror_prepare_load’ [-Werror=missing-prototypes]
 int bpf__strerror_prepare_load(const char *filename __maybe_unused,
     ^
cc1: all warnings being treated as errors
mv: cannot stat ‘util/.parse-events.o.tmp’: No such file or directory
make[6]: *** [util/parse-events.o] Error 1
make[5]: *** [util] Error 2
make[5]: *** Waiting for unfinished jobs....
  CC       ui/browsers/hists.o
  LD       bench/perf-in.o
  CC       tests/builtin-test.o
  CC       ui/tui/util.o
  CC       ui/tui/helpline.o
  CC       tests/parse-events.o
  CC       ui/tui/progress.o
  CC       ui/browsers/map.o
  LD       ui/tui/libperf-in.o
  CC       ui/browsers/scripts.o
  CC       ui/browsers/header.o
  GEN      python/perf.so
  CC       tests/dso-data.o
  CC       tests/attr.o
  LD       ui/browsers/libperf-in.o
  CC       tests/vmlinux-kallsyms.o
  LD       ui/libperf-in.o
make[4]: *** [libperf-in.o] Error 2
make[4]: *** Waiting for unfinished jobs....
  CC       perf.o
  CC       tests/openat-syscall.o
In file included from perf.c:18:0:
util/bpf-loader.h:84:5: error: no previous prototype for ‘bpf__strerror_prepare_load’ [-Werror=missing-prototypes]
 int bpf__strerror_prepare_load(const char *filename __maybe_unused,
     ^
cc1: all warnings being treated as errors
mv: cannot stat ‘./.perf.o.tmp’: No such file or directory
make[5]: *** [perf.o] Error 1
make[5]: *** Waiting for unfinished jobs....
  CC       tests/openat-syscall-all-cpus.o
  CC       tests/openat-syscall-tp-fields.o
  CC       tests/mmap-basic.o
  CC       tests/perf-record.o
  CC       tests/evsel-roundtrip-name.o
  CC       tests/evsel-tp-sched.o
  CC       tests/fdarray.o
  CC       tests/pmu.o
  CC       tests/hists_common.o
  CC       tests/hists_link.o
  CC       tests/hists_filter.o
  CC       tests/hists_output.o
  CC       tests/hists_cumulate.o
  CC       tests/python-use.o
  CC       tests/bp_signal.o
  CC       tests/bp_signal_overflow.o
  CC       tests/task-exit.o
  CC       tests/sw-clock.o
  CC       tests/mmap-thread-lookup.o
  CC       tests/thread-mg-share.o
  CC       tests/switch-tracking.o
  CC       tests/keep-tracking.o
  CC       tests/code-reading.o
  CC       tests/sample-parsing.o
  CC       tests/parse-no-sample-id-all.o
  CC       tests/kmod-path.o
  CC       tests/thread-map.o
  CC       tests/llvm.o
  CC       tests/topology.o
  CC       tests/dwarf-unwind.o
  LD       tests/perf-in.o
make[4]: *** [perf-in.o] Error 2
make[3]: *** [all] Error 2
  test: test -x ./perf
make[2]: *** [make_no_libbpf] Error 1
make[1]: *** [all] Error 2
make: *** [build-test] Error 2
make: Leaving directory `/home/acme/git/linux/tools/perf'

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

* [tip:perf/urgent] bpf tools: Improve libbpf error reporting
  2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
  2015-11-06 14:06   ` Arnaldo Carvalho de Melo
@ 2015-11-08  7:32   ` tip-bot for Wang Nan
  1 sibling, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:32 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, linux-kernel, mingo, wangnan0, hpa, jolsa, namhyung, lizefan, acme

Commit-ID:  6371ca3b541c82d8aa6a9002bd52d92bcdda5944
Gitweb:     http://git.kernel.org/tip/6371ca3b541c82d8aa6a9002bd52d92bcdda5944
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:49:37 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 15:52:41 -0300

bpf tools: Improve libbpf error reporting

In this patch, a series of libbpf specific error numbers and
libbpf_strerror() are introduced to help reporting errors.

Functions are updated to pass correct the error number through the
CHECK_ERR() macro.

All users of bpf_object__open{_buffer}() and bpf_program__title() in
perf are modified accordingly. In addition, due to the error codes
changing, bpf__strerror_load() is also modified to use them.

bpf__strerror_head() is also changed accordingly so it can parse libbpf
errors. bpf_loader_strerror() is introduced for that purpose, and will
be improved by the following patch.

load_program() is improved not to dump log buffer if it is empty. log
buffer is also used to deduce whether the error was caused by an invalid
program or other problem.

v1 -> v2:

 - Using macro for error code.

 - Fetch error message based on array index, eliminate for-loop.

 - Use log buffer to detect the reason of failure. 3 new error code
   are introduced to replace LIBBPF_ERRNO__LOAD.

In v1:

  # perf record -e ./test_ill_program.o ls
  event syntax error: './test_ill_program.o'
                       \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
  SKIP

  # perf record -e ./test_kversion_nomatch_program.o ls
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
  SKIP

  # perf record -e ./test_big_program.o ls
  event syntax error: './test_big_program.o'
                       \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
  SKIP

  In v2:

  # perf record -e ./test_ill_program.o ls
  event syntax error: './test_ill_program.o'
                       \___ Kernel verifier blocks program loading
  SKIP

  # perf record -e ./test_kversion_nomatch_program.o
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ Incorrect kernel version
  SKIP
  (Will be further improved by following patches)

  # perf record -e ./test_big_program.o
  event syntax error: './test_big_program.o'
                       \___ Program too big
  SKIP

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-2-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/lib/bpf/libbpf.c         | 159 ++++++++++++++++++++++++++++-------------
 tools/lib/bpf/libbpf.h         |  20 ++++++
 tools/perf/tests/llvm.c        |   2 +-
 tools/perf/util/bpf-loader.c   |  33 +++++++--
 tools/perf/util/parse-events.c |   4 +-
 5 files changed, 159 insertions(+), 59 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 9f3c8cf..07b492d 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -61,6 +61,60 @@ void libbpf_set_print(libbpf_print_fn_t warn,
 	__pr_debug = debug;
 }
 
+#define STRERR_BUFSIZE  128
+
+#define ERRNO_OFFSET(e)		((e) - __LIBBPF_ERRNO__START)
+#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(LIBBPF_ERRNO__##c)
+#define NR_ERRNO	(__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
+
+static const char *libbpf_strerror_table[NR_ERRNO] = {
+	[ERRCODE_OFFSET(LIBELF)]	= "Something wrong in libelf",
+	[ERRCODE_OFFSET(FORMAT)]	= "BPF object format invalid",
+	[ERRCODE_OFFSET(KVERSION)]	= "'version' section incorrect or lost",
+	[ERRCODE_OFFSET(ENDIAN)]	= "Endian missmatch",
+	[ERRCODE_OFFSET(INTERNAL)]	= "Internal error in libbpf",
+	[ERRCODE_OFFSET(RELOC)]		= "Relocation failed",
+	[ERRCODE_OFFSET(VERIFY)]	= "Kernel verifier blocks program loading",
+	[ERRCODE_OFFSET(PROG2BIG)]	= "Program too big",
+	[ERRCODE_OFFSET(KVER)]		= "Incorrect kernel version",
+};
+
+int libbpf_strerror(int err, char *buf, size_t size)
+{
+	if (!buf || !size)
+		return -1;
+
+	err = err > 0 ? err : -err;
+
+	if (err < __LIBBPF_ERRNO__START) {
+		int ret;
+
+		ret = strerror_r(err, buf, size);
+		buf[size - 1] = '\0';
+		return ret;
+	}
+
+	if (err < __LIBBPF_ERRNO__END) {
+		const char *msg;
+
+		msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
+		snprintf(buf, size, "%s", msg);
+		buf[size - 1] = '\0';
+		return 0;
+	}
+
+	snprintf(buf, size, "Unknown libbpf error %d", err);
+	buf[size - 1] = '\0';
+	return -1;
+}
+
+#define CHECK_ERR(action, err, out) do {	\
+	err = action;			\
+	if (err)			\
+		goto out;		\
+} while(0)
+
+
 /* Copied from tools/perf/util/util.h */
 #ifndef zfree
 # define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
@@ -258,7 +312,7 @@ static struct bpf_object *bpf_object__new(const char *path,
 	obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
 	if (!obj) {
 		pr_warning("alloc memory failed for %s\n", path);
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 	}
 
 	strcpy(obj->path, path);
@@ -305,7 +359,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 
 	if (obj_elf_valid(obj)) {
 		pr_warning("elf init: internal error\n");
-		return -EEXIST;
+		return -LIBBPF_ERRNO__LIBELF;
 	}
 
 	if (obj->efile.obj_buf_sz > 0) {
@@ -331,14 +385,14 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 	if (!obj->efile.elf) {
 		pr_warning("failed to open %s as ELF file\n",
 				obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__LIBELF;
 		goto errout;
 	}
 
 	if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
 		pr_warning("failed to get EHDR from %s\n",
 				obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__FORMAT;
 		goto errout;
 	}
 	ep = &obj->efile.ehdr;
@@ -346,7 +400,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
 	if ((ep->e_type != ET_REL) || (ep->e_machine != 0)) {
 		pr_warning("%s is not an eBPF object file\n",
 			obj->path);
-		err = -EINVAL;
+		err = -LIBBPF_ERRNO__FORMAT;
 		goto errout;
 	}
 
@@ -374,14 +428,14 @@ bpf_object__check_endianness(struct bpf_object *obj)
 			goto mismatch;
 		break;
 	default:
-		return -EINVAL;
+		return -LIBBPF_ERRNO__ENDIAN;
 	}
 
 	return 0;
 
 mismatch:
 	pr_warning("Error: endianness mismatch.\n");
-	return -EINVAL;
+	return -LIBBPF_ERRNO__ENDIAN;
 }
 
 static int
@@ -402,7 +456,7 @@ bpf_object__init_kversion(struct bpf_object *obj,
 
 	if (size != sizeof(kver)) {
 		pr_warning("invalid kver section in %s\n", obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__FORMAT;
 	}
 	memcpy(&kver, data, sizeof(kver));
 	obj->kern_version = kver;
@@ -444,7 +498,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 	if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
 		pr_warning("failed to get e_shstrndx from %s\n",
 			   obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__FORMAT;
 	}
 
 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
@@ -456,7 +510,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (gelf_getshdr(scn, &sh) != &sh) {
 			pr_warning("failed to get section header from %s\n",
 				   obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 
@@ -464,7 +518,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (!name) {
 			pr_warning("failed to get section name from %s\n",
 				   obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 
@@ -472,7 +526,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		if (!data) {
 			pr_warning("failed to get section data from %s(%s)\n",
 				   name, obj->path);
-			err = -EINVAL;
+			err = -LIBBPF_ERRNO__FORMAT;
 			goto out;
 		}
 		pr_debug("section %s, size %ld, link %d, flags %lx, type=%d\n",
@@ -495,7 +549,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			if (obj->efile.symbols) {
 				pr_warning("bpf: multiple SYMTAB in %s\n",
 					   obj->path);
-				err = -EEXIST;
+				err = -LIBBPF_ERRNO__FORMAT;
 			} else
 				obj->efile.symbols = data;
 		} else if ((sh.sh_type == SHT_PROGBITS) &&
@@ -504,7 +558,8 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 			err = bpf_object__add_program(obj, data->d_buf,
 						      data->d_size, name, idx);
 			if (err) {
-				char errmsg[128];
+				char errmsg[STRERR_BUFSIZE];
+
 				strerror_r(-err, errmsg, sizeof(errmsg));
 				pr_warning("failed to alloc program %s (%s): %s",
 					   name, obj->path, errmsg);
@@ -576,7 +631,7 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 
 		if (!gelf_getrel(data, i, &rel)) {
 			pr_warning("relocation: failed to get %d reloc\n", i);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		insn_idx = rel.r_offset / sizeof(struct bpf_insn);
@@ -587,20 +642,20 @@ bpf_program__collect_reloc(struct bpf_program *prog,
 				 &sym)) {
 			pr_warning("relocation: symbol %"PRIx64" not found\n",
 				   GELF_R_SYM(rel.r_info));
-			return -EINVAL;
+			return -LIBBPF_ERRNO__FORMAT;
 		}
 
 		if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
 			pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
 				   insn_idx, insns[insn_idx].code);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		map_idx = sym.st_value / sizeof(struct bpf_map_def);
 		if (map_idx >= nr_maps) {
 			pr_warning("bpf relocation: map_idx %d large than %d\n",
 				   (int)map_idx, (int)nr_maps - 1);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		prog->reloc_desc[i].insn_idx = insn_idx;
@@ -683,7 +738,7 @@ bpf_program__relocate(struct bpf_program *prog, int *map_fds)
 		if (insn_idx >= (int)prog->insns_cnt) {
 			pr_warning("relocation out of range: '%s'\n",
 				   prog->section_name);
-			return -ERANGE;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 		insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
 		insns[insn_idx].imm = map_fds[map_idx];
@@ -721,7 +776,7 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 
 	if (!obj_elf_valid(obj)) {
 		pr_warning("Internal error: elf object is closed\n");
-		return -EINVAL;
+		return -LIBBPF_ERRNO__INTERNAL;
 	}
 
 	for (i = 0; i < obj->efile.nr_reloc; i++) {
@@ -734,21 +789,21 @@ static int bpf_object__collect_reloc(struct bpf_object *obj)
 
 		if (shdr->sh_type != SHT_REL) {
 			pr_warning("internal error at %d\n", __LINE__);
-			return -EINVAL;
+			return -LIBBPF_ERRNO__INTERNAL;
 		}
 
 		prog = bpf_object__find_prog_by_idx(obj, idx);
 		if (!prog) {
 			pr_warning("relocation failed: no %d section\n",
 				   idx);
-			return -ENOENT;
+			return -LIBBPF_ERRNO__RELOC;
 		}
 
 		err = bpf_program__collect_reloc(prog, nr_maps,
 						 shdr, data,
 						 obj->efile.symbols);
 		if (err)
-			return -EINVAL;
+			return err;
 	}
 	return 0;
 }
@@ -777,13 +832,23 @@ load_program(struct bpf_insn *insns, int insns_cnt,
 		goto out;
 	}
 
-	ret = -EINVAL;
+	ret = -LIBBPF_ERRNO__LOAD;
 	pr_warning("load bpf program failed: %s\n", strerror(errno));
 
-	if (log_buf) {
+	if (log_buf && log_buf[0] != '\0') {
+		ret = -LIBBPF_ERRNO__VERIFY;
 		pr_warning("-- BEGIN DUMP LOG ---\n");
 		pr_warning("\n%s\n", log_buf);
 		pr_warning("-- END LOG --\n");
+	} else {
+		if (insns_cnt >= BPF_MAXINSNS) {
+			pr_warning("Program too large (%d insns), at most %d insns\n",
+				   insns_cnt, BPF_MAXINSNS);
+			ret = -LIBBPF_ERRNO__PROG2BIG;
+		} else if (log_buf) {
+			pr_warning("log buffer is empty\n");
+			ret = -LIBBPF_ERRNO__KVER;
+		}
 	}
 
 out:
@@ -831,7 +896,7 @@ static int bpf_object__validate(struct bpf_object *obj)
 	if (obj->kern_version == 0) {
 		pr_warning("%s doesn't provide kernel version\n",
 			   obj->path);
-		return -EINVAL;
+		return -LIBBPF_ERRNO__KVERSION;
 	}
 	return 0;
 }
@@ -840,32 +905,28 @@ static struct bpf_object *
 __bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz)
 {
 	struct bpf_object *obj;
+	int err;
 
 	if (elf_version(EV_CURRENT) == EV_NONE) {
 		pr_warning("failed to init libelf for %s\n", path);
-		return NULL;
+		return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
 	}
 
 	obj = bpf_object__new(path, obj_buf, obj_buf_sz);
-	if (!obj)
-		return NULL;
+	if (IS_ERR(obj))
+		return obj;
 
-	if (bpf_object__elf_init(obj))
-		goto out;
-	if (bpf_object__check_endianness(obj))
-		goto out;
-	if (bpf_object__elf_collect(obj))
-		goto out;
-	if (bpf_object__collect_reloc(obj))
-		goto out;
-	if (bpf_object__validate(obj))
-		goto out;
+	CHECK_ERR(bpf_object__elf_init(obj), err, out);
+	CHECK_ERR(bpf_object__check_endianness(obj), err, out);
+	CHECK_ERR(bpf_object__elf_collect(obj), err, out);
+	CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
+	CHECK_ERR(bpf_object__validate(obj), err, out);
 
 	bpf_object__elf_finish(obj);
 	return obj;
 out:
 	bpf_object__close(obj);
-	return NULL;
+	return ERR_PTR(err);
 }
 
 struct bpf_object *bpf_object__open(const char *path)
@@ -922,6 +983,8 @@ int bpf_object__unload(struct bpf_object *obj)
 
 int bpf_object__load(struct bpf_object *obj)
 {
+	int err;
+
 	if (!obj)
 		return -EINVAL;
 
@@ -931,18 +994,16 @@ int bpf_object__load(struct bpf_object *obj)
 	}
 
 	obj->loaded = true;
-	if (bpf_object__create_maps(obj))
-		goto out;
-	if (bpf_object__relocate(obj))
-		goto out;
-	if (bpf_object__load_progs(obj))
-		goto out;
+
+	CHECK_ERR(bpf_object__create_maps(obj), err, out);
+	CHECK_ERR(bpf_object__relocate(obj), err, out);
+	CHECK_ERR(bpf_object__load_progs(obj), err, out);
 
 	return 0;
 out:
 	bpf_object__unload(obj);
 	pr_warning("failed to load object '%s'\n", obj->path);
-	return -EINVAL;
+	return err;
 }
 
 void bpf_object__close(struct bpf_object *obj)
@@ -990,7 +1051,7 @@ const char *
 bpf_object__get_name(struct bpf_object *obj)
 {
 	if (!obj)
-		return NULL;
+		return ERR_PTR(-EINVAL);
 	return obj->path;
 }
 
@@ -1043,7 +1104,7 @@ const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
 		title = strdup(title);
 		if (!title) {
 			pr_warning("failed to strdup program title\n");
-			return NULL;
+			return ERR_PTR(-ENOMEM);
 		}
 	}
 
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index bc80af0..30a40e9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -10,6 +10,26 @@
 
 #include <stdio.h>
 #include <stdbool.h>
+#include <linux/err.h>
+
+enum libbpf_errno {
+	__LIBBPF_ERRNO__START = 4000,
+
+	/* Something wrong in libelf */
+	LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
+	LIBBPF_ERRNO__FORMAT,	/* BPF object format invalid */
+	LIBBPF_ERRNO__KVERSION,	/* Incorrect or no 'version' section */
+	LIBBPF_ERRNO__ENDIAN,	/* Endian missmatch */
+	LIBBPF_ERRNO__INTERNAL,	/* Internal error in libbpf */
+	LIBBPF_ERRNO__RELOC,	/* Relocation failed */
+	LIBBPF_ERRNO__LOAD,	/* Load program failure for unknown reason */
+	LIBBPF_ERRNO__VERIFY,	/* Kernel verifier blocks program loading */
+	LIBBPF_ERRNO__PROG2BIG,	/* Program too big */
+	LIBBPF_ERRNO__KVER,	/* Incorrect kernel version */
+	__LIBBPF_ERRNO__END,
+};
+
+int libbpf_strerror(int err, char *buf, size_t size);
 
 /*
  * In include/linux/compiler-gcc.h, __printf is defined. However
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 512d362..8f713f6 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -27,7 +27,7 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 	struct bpf_object *obj;
 
 	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
-	if (!obj)
+	if (IS_ERR(obj))
 		return -1;
 	bpf_object__close(obj);
 	return 0;
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 0c5d174..c46256b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -59,9 +59,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 	} else
 		obj = bpf_object__open(filename);
 
-	if (!obj) {
+	if (IS_ERR(obj)) {
 		pr_debug("bpf: failed to load %s\n", filename);
-		return ERR_PTR(-EINVAL);
+		return obj;
 	}
 
 	return obj;
@@ -96,9 +96,9 @@ config_bpf_program(struct bpf_program *prog)
 	int err;
 
 	config_str = bpf_program__title(prog, false);
-	if (!config_str) {
+	if (IS_ERR(config_str)) {
 		pr_debug("bpf: unable to get title for program\n");
-		return -EINVAL;
+		return PTR_ERR(config_str);
 	}
 
 	priv = calloc(sizeof(*priv), 1);
@@ -308,13 +308,34 @@ int bpf__foreach_tev(struct bpf_object *obj,
 	return 0;
 }
 
+static int
+bpf_loader_strerror(int err, char *buf, size_t size)
+{
+	char sbuf[STRERR_BUFSIZE];
+	const char *msg;
+
+	if (!buf || !size)
+		return -1;
+
+	err = err > 0 ? err : -err;
+
+	if (err >= __LIBBPF_ERRNO__START)
+		return libbpf_strerror(err, buf, size);
+
+	msg = strerror_r(err, sbuf, sizeof(sbuf));
+	snprintf(buf, size, "%s", msg);
+	buf[size - 1] = '\0';
+	return 0;
+}
+
 #define bpf__strerror_head(err, buf, size) \
 	char sbuf[STRERR_BUFSIZE], *emsg;\
 	if (!size)\
 		return 0;\
 	if (err < 0)\
 		err = -err;\
-	emsg = strerror_r(err, sbuf, sizeof(sbuf));\
+	bpf_loader_strerror(err, sbuf, sizeof(sbuf));\
+	emsg = sbuf;\
 	switch (err) {\
 	default:\
 		scnprintf(buf, size, "%s", emsg);\
@@ -345,8 +366,6 @@ int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
 		       int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
-	bpf__strerror_entry(EINVAL, "%s: Are you root and runing a CONFIG_BPF_SYSCALL kernel?",
-			    emsg)
 	bpf__strerror_end(buf, size);
 	return 0;
 }
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index bee6058..c75b25d 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -632,11 +632,11 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
 	struct bpf_object *obj;
 
 	obj = bpf__prepare_load(bpf_file_name, source);
-	if (IS_ERR(obj) || !obj) {
+	if (IS_ERR(obj)) {
 		char errbuf[BUFSIZ];
 		int err;
 
-		err = obj ? PTR_ERR(obj) : -EINVAL;
+		err = PTR_ERR(obj);
 
 		if (err == -ENOTSUP)
 			snprintf(errbuf, sizeof(errbuf),

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

* [tip:perf/urgent] bpf tools: Add new API bpf_object__get_kversion ()
  2015-11-06 13:49 ` [PATCH v2 2/7] bpf tools: Add new API bpf_object__get_kversion() Wang Nan
@ 2015-11-08  7:32   ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:32 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, namhyung, lizefan, hpa, linux-kernel, wangnan0, mingo, jolsa, acme

Commit-ID:  45825d8ab8ef6287f5d05aea141419d8d4278852
Gitweb:     http://git.kernel.org/tip/45825d8ab8ef6287f5d05aea141419d8d4278852
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:49:38 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 15:56:41 -0300

bpf tools: Add new API bpf_object__get_kversion()

bpf_object__get_kversion() can be used to fetch value of object's
'version' section. Following patch will use it for error reporting.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/lib/bpf/libbpf.c | 8 ++++++++
 tools/lib/bpf/libbpf.h | 1 +
 2 files changed, 9 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 07b492d..e176bad 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1055,6 +1055,14 @@ bpf_object__get_name(struct bpf_object *obj)
 	return obj->path;
 }
 
+unsigned int
+bpf_object__get_kversion(struct bpf_object *obj)
+{
+	if (!obj)
+		return 0;
+	return obj->kern_version;
+}
+
 struct bpf_program *
 bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
 {
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index 30a40e9..c9a9aef 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -56,6 +56,7 @@ void bpf_object__close(struct bpf_object *object);
 int bpf_object__load(struct bpf_object *obj);
 int bpf_object__unload(struct bpf_object *obj);
 const char *bpf_object__get_name(struct bpf_object *obj);
+unsigned int bpf_object__get_kversion(struct bpf_object *obj);
 
 struct bpf_object *bpf_object__next(struct bpf_object *prev);
 #define bpf_object__for_each_safe(pos, tmp)			\

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

* [tip:perf/urgent] perf tools: Make fetch_kernel_version() publicly available
  2015-11-06 13:55   ` [PATCH v2 3/7 fix] " Wang Nan
@ 2015-11-08  7:33     ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, wangnan0, acme, mingo, linux-kernel, lizefan, namhyung, jolsa, hpa

Commit-ID:  07bc5c699a3d8fe5e26dbcd72e4103c7988055ba
Gitweb:     http://git.kernel.org/tip/07bc5c699a3d8fe5e26dbcd72e4103c7988055ba
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:55:35 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 15:57:18 -0300

perf tools: Make fetch_kernel_version() publicly available

There are 2 places in llvm-utils.c which find kernel version information
through uname. This patch extracts the uname related code into a
fetch_kernel_version() function and puts it into util.h so it can be
reused.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446818135-87310-1-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/llvm-utils.c | 49 +++++++++++++++-----------------------------
 tools/perf/util/util.c       | 30 +++++++++++++++++++++++++++
 tools/perf/util/util.h       |  3 +++
 3 files changed, 49 insertions(+), 33 deletions(-)

diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c
index 8ee25be..00724d4 100644
--- a/tools/perf/util/llvm-utils.c
+++ b/tools/perf/util/llvm-utils.c
@@ -4,7 +4,6 @@
  */
 
 #include <stdio.h>
-#include <sys/utsname.h>
 #include "util.h"
 #include "debug.h"
 #include "llvm-utils.h"
@@ -216,18 +215,19 @@ static int detect_kbuild_dir(char **kbuild_dir)
 	const char *suffix_dir = "";
 
 	char *autoconf_path;
-	struct utsname utsname;
 
 	int err;
 
 	if (!test_dir) {
-		err = uname(&utsname);
-		if (err) {
-			pr_warning("uname failed: %s\n", strerror(errno));
+		/* _UTSNAME_LENGTH is 65 */
+		char release[128];
+
+		err = fetch_kernel_version(NULL, release,
+					   sizeof(release));
+		if (err)
 			return -EINVAL;
-		}
 
-		test_dir = utsname.release;
+		test_dir = release;
 		prefix_dir = "/lib/modules/";
 		suffix_dir = "/build";
 	}
@@ -325,38 +325,18 @@ get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts)
 	pr_debug("include option is set to %s\n", *kbuild_include_opts);
 }
 
-static unsigned long
-fetch_kernel_version(void)
-{
-	struct utsname utsname;
-	int version, patchlevel, sublevel, err;
-
-	if (uname(&utsname))
-		return 0;
-
-	err = sscanf(utsname.release, "%d.%d.%d",
-		     &version, &patchlevel, &sublevel);
-
-	if (err != 3) {
-		pr_debug("Unablt to get kernel version from uname '%s'\n",
-			 utsname.release);
-		return 0;
-	}
-
-	return (version << 16) + (patchlevel << 8) + sublevel;
-}
-
 int llvm__compile_bpf(const char *path, void **p_obj_buf,
 		      size_t *p_obj_buf_sz)
 {
+	size_t obj_buf_sz;
+	void *obj_buf = NULL;
 	int err, nr_cpus_avail;
-	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
+	unsigned int kernel_version;
 	char linux_version_code_str[64];
 	const char *clang_opt = llvm_param.clang_opt;
-	const char *template = llvm_param.clang_bpf_cmd_template;
+	char clang_path[PATH_MAX], nr_cpus_avail_str[64];
 	char *kbuild_dir = NULL, *kbuild_include_opts = NULL;
-	void *obj_buf = NULL;
-	size_t obj_buf_sz;
+	const char *template = llvm_param.clang_bpf_cmd_template;
 
 	if (!template)
 		template = CLANG_BPF_CMD_DEFAULT_TEMPLATE;
@@ -388,8 +368,11 @@ int llvm__compile_bpf(const char *path, void **p_obj_buf,
 	snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d",
 		 nr_cpus_avail);
 
+	if (fetch_kernel_version(&kernel_version, NULL, 0))
+		kernel_version = 0;
+
 	snprintf(linux_version_code_str, sizeof(linux_version_code_str),
-		 "0x%lx", fetch_kernel_version());
+		 "0x%x", kernel_version);
 
 	force_set_env("NR_CPUS", nr_cpus_avail_str);
 	force_set_env("LINUX_VERSION_CODE", linux_version_code_str);
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index cd12c25..47b1e36 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -3,6 +3,7 @@
 #include "debug.h"
 #include <api/fs/fs.h>
 #include <sys/mman.h>
+#include <sys/utsname.h>
 #ifdef HAVE_BACKTRACE_SUPPORT
 #include <execinfo.h>
 #endif
@@ -665,3 +666,32 @@ bool find_process(const char *name)
 	closedir(dir);
 	return ret ? false : true;
 }
+
+int
+fetch_kernel_version(unsigned int *puint, char *str,
+		     size_t str_size)
+{
+	struct utsname utsname;
+	int version, patchlevel, sublevel, err;
+
+	if (uname(&utsname))
+		return -1;
+
+	if (str && str_size) {
+		strncpy(str, utsname.release, str_size);
+		str[str_size - 1] = '\0';
+	}
+
+	err = sscanf(utsname.release, "%d.%d.%d",
+		     &version, &patchlevel, &sublevel);
+
+	if (err != 3) {
+		pr_debug("Unablt to get kernel version from uname '%s'\n",
+			 utsname.release);
+		return -1;
+	}
+
+	if (puint)
+		*puint = (version << 16) + (patchlevel << 8) + sublevel;
+	return 0;
+}
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 4cfb913..2665126 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -350,4 +350,7 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int
 
 int get_stack_size(const char *str, unsigned long *_size);
 
+int fetch_kernel_version(unsigned int *puint,
+			 char *str, size_t str_sz);
+
 #endif /* GIT_COMPAT_UTIL_H */

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

* [tip:perf/urgent] perf bpf: Improve BPF related error messages
  2015-11-06 13:58   ` [PATCH v2 4/7 fix] " Wang Nan
@ 2015-11-08  7:33     ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:33 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: acme, wangnan0, mingo, namhyung, tglx, linux-kernel, hpa, jolsa, lizefan

Commit-ID:  d3e0ce393057cfa907a0c4fe7b1ff56d5c30cca5
Gitweb:     http://git.kernel.org/tip/d3e0ce393057cfa907a0c4fe7b1ff56d5c30cca5
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:58:09 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 17:14:20 -0300

perf bpf: Improve BPF related error messages

A series of bpf loader related error codes were introduced to help error
reporting. Functions were improved to return these new error codes.

Functions which return pointers were adjusted to encode error codes into
return value using the ERR_PTR() interface.

bpf_loader_strerror() was improved to convert these error messages to
strings. It checks the error codes and calls libbpf_strerror() and
strerror_r() accordingly, so caller don't need to consider checking the
range of the error code.

In bpf__strerror_load(), print kernel version of running kernel and the
object's 'version' section to notify user how to fix his/her program.

v1 -> v2:
 Use macro for error code.

 Fetch error message based on array index, eliminate for-loop.

 Print version strings.

Before:

  # perf record -e ./test_kversion_nomatch_program.o sleep 1
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ Failed to load program: Validate your program and check 'license'/'version' sections in your object
  SKIP

  After:

  # perf record -e ./test_kversion_nomatch_program.o ls
  event syntax error: './test_kversion_nomatch_program.o'
                       \___ 'version' (4.4.0) doesn't match running kernel (4.3.0)
  SKIP

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446818289-87444-1-git-send-email-wangnan0@huawei.com
[ Add 'static inline' to bpf__strerror_prepare_load() when LIBBPF is disabled ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/bpf-loader.c   | 88 ++++++++++++++++++++++++++++++++++++------
 tools/perf/util/bpf-loader.h   | 23 +++++++++++
 tools/perf/util/parse-events.c |  7 ++--
 tools/perf/util/util.h         |  5 +++
 4 files changed, 108 insertions(+), 15 deletions(-)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index c46256b..e3afa1b 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -53,7 +53,7 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 
 		err = llvm__compile_bpf(filename, &obj_buf, &obj_buf_sz);
 		if (err)
-			return ERR_PTR(err);
+			return ERR_PTR(-BPF_LOADER_ERRNO__COMPILE);
 		obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, filename);
 		free(obj_buf);
 	} else
@@ -113,14 +113,14 @@ config_bpf_program(struct bpf_program *prog)
 	if (err < 0) {
 		pr_debug("bpf: '%s' is not a valid config string\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__CONFIG;
 		goto errout;
 	}
 
 	if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
 		pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
 			 config_str, PERF_BPF_PROBE_GROUP);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__GROUP;
 		goto errout;
 	} else if (!pev->group)
 		pev->group = strdup(PERF_BPF_PROBE_GROUP);
@@ -132,9 +132,9 @@ config_bpf_program(struct bpf_program *prog)
 	}
 
 	if (!pev->event) {
-		pr_debug("bpf: '%s': event name is missing\n",
+		pr_debug("bpf: '%s': event name is missing. Section name should be 'key=value'\n",
 			 config_str);
-		err = -EINVAL;
+		err = -BPF_LOADER_ERRNO__EVENTNAME;
 		goto errout;
 	}
 	pr_debug("bpf: config '%s' is ok\n", config_str);
@@ -285,7 +285,7 @@ int bpf__foreach_tev(struct bpf_object *obj,
 				(void **)&priv);
 		if (err || !priv) {
 			pr_debug("bpf: failed to get private field\n");
-			return -EINVAL;
+			return -BPF_LOADER_ERRNO__INTERNAL;
 		}
 
 		pev = &priv->pev;
@@ -308,6 +308,18 @@ int bpf__foreach_tev(struct bpf_object *obj,
 	return 0;
 }
 
+#define ERRNO_OFFSET(e)		((e) - __BPF_LOADER_ERRNO__START)
+#define ERRCODE_OFFSET(c)	ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
+#define NR_ERRNO	(__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
+
+static const char *bpf_loader_strerror_table[NR_ERRNO] = {
+	[ERRCODE_OFFSET(CONFIG)]	= "Invalid config string",
+	[ERRCODE_OFFSET(GROUP)]		= "Invalid group name",
+	[ERRCODE_OFFSET(EVENTNAME)]	= "No event name found in config string",
+	[ERRCODE_OFFSET(INTERNAL)]	= "BPF loader internal error",
+	[ERRCODE_OFFSET(COMPILE)]	= "Error when compiling BPF scriptlet",
+};
+
 static int
 bpf_loader_strerror(int err, char *buf, size_t size)
 {
@@ -322,10 +334,21 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	if (err >= __LIBBPF_ERRNO__START)
 		return libbpf_strerror(err, buf, size);
 
-	msg = strerror_r(err, sbuf, sizeof(sbuf));
-	snprintf(buf, size, "%s", msg);
+	if (err >= __BPF_LOADER_ERRNO__START && err < __BPF_LOADER_ERRNO__END) {
+		msg = bpf_loader_strerror_table[ERRNO_OFFSET(err)];
+		snprintf(buf, size, "%s", msg);
+		buf[size - 1] = '\0';
+		return 0;
+	}
+
+	if (err >= __BPF_LOADER_ERRNO__END)
+		snprintf(buf, size, "Unknown bpf loader error %d", err);
+	else
+		snprintf(buf, size, "%s",
+			 strerror_r(err, sbuf, sizeof(sbuf)));
+
 	buf[size - 1] = '\0';
-	return 0;
+	return -1;
 }
 
 #define bpf__strerror_head(err, buf, size) \
@@ -351,21 +374,62 @@ bpf_loader_strerror(int err, char *buf, size_t size)
 	}\
 	buf[size - 1] = '\0';
 
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size)
+{
+	size_t n;
+	int ret;
+
+	n = snprintf(buf, size, "Failed to load %s%s: ",
+			 filename, source ? " from source" : "");
+	if (n >= size) {
+		buf[size - 1] = '\0';
+		return 0;
+	}
+	buf += n;
+	size -= n;
+
+	ret = bpf_loader_strerror(err, buf, size);
+	buf[size - 1] = '\0';
+	return ret;
+}
+
 int bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 			int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
 	bpf__strerror_entry(EEXIST, "Probe point exist. Try use 'perf probe -d \"*\"'");
-	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0\n");
-	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file\n");
+	bpf__strerror_entry(EACCES, "You need to be root");
+	bpf__strerror_entry(EPERM, "You need to be root, and /proc/sys/kernel/kptr_restrict should be 0");
+	bpf__strerror_entry(ENOENT, "You need to check probing points in BPF file");
 	bpf__strerror_end(buf, size);
 	return 0;
 }
 
-int bpf__strerror_load(struct bpf_object *obj __maybe_unused,
+int bpf__strerror_load(struct bpf_object *obj,
 		       int err, char *buf, size_t size)
 {
 	bpf__strerror_head(err, buf, size);
+	case LIBBPF_ERRNO__KVER: {
+		unsigned int obj_kver = bpf_object__get_kversion(obj);
+		unsigned int real_kver;
+
+		if (fetch_kernel_version(&real_kver, NULL, 0)) {
+			scnprintf(buf, size, "Unable to fetch kernel version");
+			break;
+		}
+
+		if (obj_kver != real_kver) {
+			scnprintf(buf, size,
+				  "'version' ("KVER_FMT") doesn't match running kernel ("KVER_FMT")",
+				  KVER_PARAM(obj_kver),
+				  KVER_PARAM(real_kver));
+			break;
+		}
+
+		scnprintf(buf, size, "Failed to load program for unknown reason");
+		break;
+	}
 	bpf__strerror_end(buf, size);
 	return 0;
 }
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index ccd8d7f..5eb3629 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -8,9 +8,21 @@
 #include <linux/compiler.h>
 #include <linux/err.h>
 #include <string.h>
+#include <bpf/libbpf.h>
 #include "probe-event.h"
 #include "debug.h"
 
+enum bpf_loader_errno {
+	__BPF_LOADER_ERRNO__START = __LIBBPF_ERRNO__START - 100,
+	/* Invalid config string */
+	BPF_LOADER_ERRNO__CONFIG = __BPF_LOADER_ERRNO__START,
+	BPF_LOADER_ERRNO__GROUP,	/* Invalid group name */
+	BPF_LOADER_ERRNO__EVENTNAME,	/* Event name is missing */
+	BPF_LOADER_ERRNO__INTERNAL,	/* BPF loader internal error */
+	BPF_LOADER_ERRNO__COMPILE,	/* Error when compiling BPF scriptlet */
+	__BPF_LOADER_ERRNO__END,
+};
+
 struct bpf_object;
 #define PERF_BPF_PROBE_GROUP "perf_bpf_probe"
 
@@ -19,6 +31,8 @@ typedef int (*bpf_prog_iter_callback_t)(struct probe_trace_event *tev,
 
 #ifdef HAVE_LIBBPF_SUPPORT
 struct bpf_object *bpf__prepare_load(const char *filename, bool source);
+int bpf__strerror_prepare_load(const char *filename, bool source,
+			       int err, char *buf, size_t size);
 
 void bpf__clear(void);
 
@@ -67,6 +81,15 @@ __bpf_strerror(char *buf, size_t size)
 	return 0;
 }
 
+static inline
+int bpf__strerror_prepare_load(const char *filename __maybe_unused,
+			       bool source __maybe_unused,
+			       int err __maybe_unused,
+			       char *buf, size_t size)
+{
+	return __bpf_strerror(buf, size);
+}
+
 static inline int
 bpf__strerror_probe(struct bpf_object *obj __maybe_unused,
 		    int err __maybe_unused,
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index c75b25d..e48d9da 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -642,9 +642,10 @@ int parse_events_load_bpf(struct parse_events_evlist *data,
 			snprintf(errbuf, sizeof(errbuf),
 				 "BPF support is not compiled");
 		else
-			snprintf(errbuf, sizeof(errbuf),
-				 "BPF object file '%s' is invalid",
-				 bpf_file_name);
+			bpf__strerror_prepare_load(bpf_file_name,
+						   source,
+						   -err, errbuf,
+						   sizeof(errbuf));
 
 		data->error->help = strdup("(add -v to see detail)");
 		data->error->str = strdup(errbuf);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 2665126..dcc6590 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -352,5 +352,10 @@ int get_stack_size(const char *str, unsigned long *_size);
 
 int fetch_kernel_version(unsigned int *puint,
 			 char *str, size_t str_sz);
+#define KVER_VERSION(x)		(((x) >> 16) & 0xff)
+#define KVER_PATCHLEVEL(x)	(((x) >> 8) & 0xff)
+#define KVER_SUBLEVEL(x)	((x) & 0xff)
+#define KVER_FMT	"%d.%d.%d"
+#define KVER_PARAM(x)	KVER_VERSION(x), KVER_PATCHLEVEL(x), KVER_SUBLEVEL(x)
 
 #endif /* GIT_COMPAT_UTIL_H */

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

* [tip:perf/urgent] perf test: Enhance the LLVM test: update basic BPF test program
  2015-11-06 13:49 ` [PATCH v2 5/7] perf test: Enforce LLVM test: update basic BPF test program Wang Nan
@ 2015-11-08  7:34   ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:34 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: lizefan, hpa, mingo, acme, linux-kernel, namhyung, jolsa,
	wangnan0, hekuang, tglx

Commit-ID:  b31de018a6284a25e0fdfeb028e724f8417ec3b1
Gitweb:     http://git.kernel.org/tip/b31de018a6284a25e0fdfeb028e724f8417ec3b1
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:49:41 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 17:49:24 -0300

perf test: Enhance the LLVM test: update basic BPF test program

This patch replaces the original toy BPF program with the previously
introduced bpf-script-example.c. Dynamically embeddeding it into
'llvm-src-base.c'.

The newly introduced BPF program attaches a BPF program to
'sys_epoll_pwait()'. perf itself never use that syscall, so further test
can verify their result with it. The program would generate 1 sample in
every 2 calls of epoll_pwait() system call.

Since the resulting BPF object is useful per se for further tests,
test_llvm__fetch_bpf_obj() is introduced for creating BPF objects from
source. The LLVM test was rewritten to use it.

Committer note:

Running it:

  [root@zoo wb]# perf test -v LLVM
  35: Test LLVM searching and compiling                        :
  --- start ---
  test child forked, pid 17740
  Kernel build dir is set to /lib/modules/4.3.0-rc1+/build
  set env: KBUILD_DIR=/lib/modules/4.3.0-rc1+/build
  unset env: KBUILD_OPTS
  include option is set to  -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated  -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h
  set env: NR_CPUS=4
  set env: LINUX_VERSION_CODE=0x40300
  set env: CLANG_EXEC=/usr/libexec/icecc/bin/clang
  set env: CLANG_OPTIONS=-xc
  set env: KERNEL_INC_OPTIONS= -nostdinc -isystem /usr/lib/gcc/x86_64-redhat-linux/4.9.2/include -I/home/git/linux/arch/x86/include -Iarch/x86/include/generated/uapi -Iarch/x86/include/generated  -I/home/git/linux/include -Iinclude -I/home/git/linux/arch/x86/include/uapi -Iarch/x86/include/generated/uapi -I/home/git/linux/include/uapi -Iinclude/generated/uapi -include /home/git/linux/include/linux/kconfig.h
  set env: WORKING_DIR=/lib/modules/4.3.0-rc1+/build
  set env: CLANG_SOURCE=-
  llvm compiling command template: echo '/*
   * bpf-script-example.c
   * Test basic LLVM building
   */
  #ifndef LINUX_VERSION_CODE
  # error Need LINUX_VERSION_CODE
  # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
  #endif
  #define BPF_ANY 0
  #define BPF_MAP_TYPE_ARRAY 2
  #define BPF_FUNC_map_lookup_elem 1
  #define BPF_FUNC_map_update_elem 2

  static void *(*bpf_map_lookup_elem)(void *map, void *key) =
	  (void *) BPF_FUNC_map_lookup_elem;
  static void *(*bpf_map_update_elem)(void *map, void *key, void *value, int flags) =
	  (void *) BPF_FUNC_map_update_elem;

  struct bpf_map_def {
	  unsigned int type;
	  unsigned int key_size;
	  unsigned int value_size;
	  unsigned int max_entries;
  };

  #define SEC(NAME) __attribute__((section(NAME), used))
  struct bpf_map_def SEC("maps") flip_table = {
	  .type = BPF_MAP_TYPE_ARRAY,
	  .key_size = sizeof(int),
	  .value_size = sizeof(int),
	  .max_entries = 1,
  };

  SEC("func=sys_epoll_pwait")
  int bpf_func__sys_epoll_pwait(void *ctx)
  {
	  int ind =0;
	  int *flag = bpf_map_lookup_elem(&flip_table, &ind);
	  int new_flag;
	  if (!flag)
		  return 0;
	  /* flip flag and store back */
	  new_flag = !*flag;
	  bpf_map_update_elem(&flip_table, &ind, &new_flag, BPF_ANY);
	  return new_flag;
  }
  char _license[] SEC("license") = "GPL";
  int _version SEC("version") = LINUX_VERSION_CODE;
  ' | $CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS -DLINUX_VERSION_CODE=$LINUX_VERSION_CODE $CLANG_OPTIONS $KERNEL_INC_OPTIONS -Wno-unused-value -Wno-pointer-sign -working-directory $WORKING_DIR -c "$CLANG_SOURCE" -target bpf -O2 -o -
  test child finished with 0
  ---- end ----
  Test LLVM searching and compiling: Ok
  [root@zoo wb]#

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-6-git-send-email-wangnan0@huawei.com
Signed-off-by: He Kuang <hekuang@huawei.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/Build                |   9 ++-
 tools/perf/tests/bpf-script-example.c |   4 ++
 tools/perf/tests/llvm.c               | 131 ++++++++++++++++++++++++++--------
 tools/perf/tests/llvm.h               |  16 +++++
 4 files changed, 129 insertions(+), 31 deletions(-)

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 50de225..6c095b3 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,9 +31,16 @@ perf-y += sample-parsing.o
 perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
-perf-y += llvm.o
+perf-y += llvm.o llvm-src-base.o
 perf-y += topology.o
 
+$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
+	$(call rule_mkdir)
+	$(Q)echo '#include <tests/llvm.h>' > $@
+	$(Q)echo 'const char test_llvm__bpf_base_prog[] =' >> $@
+	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+	$(Q)echo ';' >> $@
+
 ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 endif
diff --git a/tools/perf/tests/bpf-script-example.c b/tools/perf/tests/bpf-script-example.c
index 410a70b..0ec9c2c 100644
--- a/tools/perf/tests/bpf-script-example.c
+++ b/tools/perf/tests/bpf-script-example.c
@@ -1,3 +1,7 @@
+/*
+ * bpf-script-example.c
+ * Test basic LLVM building
+ */
 #ifndef LINUX_VERSION_CODE
 # error Need LINUX_VERSION_CODE
 # error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 8f713f6..05683c5 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -2,6 +2,7 @@
 #include <bpf/libbpf.h>
 #include <util/llvm-utils.h>
 #include <util/cache.h>
+#include "llvm.h"
 #include "tests.h"
 #include "debug.h"
 
@@ -11,16 +12,6 @@ static int perf_config_cb(const char *var, const char *val,
 	return perf_default_config(var, val, arg);
 }
 
-/*
- * Randomly give it a "version" section since we don't really load it
- * into kernel
- */
-static const char test_bpf_prog[] =
-	"__attribute__((section(\"do_fork\"), used)) "
-	"int fork(void *ctx) {return 0;} "
-	"char _license[] __attribute__((section(\"license\"), used)) = \"GPL\";"
-	"int _version __attribute__((section(\"version\"), used)) = 0x40100;";
-
 #ifdef HAVE_LIBBPF_SUPPORT
 static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 {
@@ -28,25 +19,47 @@ static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 
 	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
 	if (IS_ERR(obj))
-		return -1;
+		return TEST_FAIL;
 	bpf_object__close(obj);
-	return 0;
+	return TEST_OK;
 }
 #else
 static int test__bpf_parsing(void *obj_buf __maybe_unused,
 			     size_t obj_buf_sz __maybe_unused)
 {
 	pr_debug("Skip bpf parsing\n");
-	return 0;
+	return TEST_OK;
 }
 #endif
 
-int test__llvm(void)
+static struct {
+	const char *source;
+	const char *desc;
+} bpf_source_table[__LLVM_TESTCASE_MAX] = {
+	[LLVM_TESTCASE_BASE] = {
+		.source = test_llvm__bpf_base_prog,
+		.desc = "Basic BPF llvm compiling test",
+	},
+};
+
+
+int
+test_llvm__fetch_bpf_obj(void **p_obj_buf,
+			 size_t *p_obj_buf_sz,
+			 enum test_llvm__testcase index,
+			 bool force)
 {
-	char *tmpl_new, *clang_opt_new;
-	void *obj_buf;
-	size_t obj_buf_sz;
-	int err, old_verbose;
+	const char *source;
+	const char *desc;
+	const char *tmpl_old, *clang_opt_old;
+	char *tmpl_new = NULL, *clang_opt_new = NULL;
+	int err, old_verbose, ret = TEST_FAIL;
+
+	if (index >= __LLVM_TESTCASE_MAX)
+		return TEST_FAIL;
+
+	source = bpf_source_table[index].source;
+	desc = bpf_source_table[index].desc;
 
 	perf_config(perf_config_cb, NULL);
 
@@ -54,42 +67,100 @@ int test__llvm(void)
 	 * Skip this test if user's .perfconfig doesn't set [llvm] section
 	 * and clang is not found in $PATH, and this is not perf test -v
 	 */
-	if (verbose == 0 && !llvm_param.user_set_param && llvm__search_clang()) {
+	if (!force && (verbose == 0 &&
+		       !llvm_param.user_set_param &&
+		       llvm__search_clang())) {
 		pr_debug("No clang and no verbosive, skip this test\n");
 		return TEST_SKIP;
 	}
 
-	old_verbose = verbose;
 	/*
 	 * llvm is verbosity when error. Suppress all error output if
 	 * not 'perf test -v'.
 	 */
+	old_verbose = verbose;
 	if (verbose == 0)
 		verbose = -1;
 
+	*p_obj_buf = NULL;
+	*p_obj_buf_sz = 0;
+
 	if (!llvm_param.clang_bpf_cmd_template)
-		return -1;
+		goto out;
 
 	if (!llvm_param.clang_opt)
 		llvm_param.clang_opt = strdup("");
 
-	err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
-		       llvm_param.clang_bpf_cmd_template);
+	err = asprintf(&tmpl_new, "echo '%s' | %s%s", source,
+		       llvm_param.clang_bpf_cmd_template,
+		       old_verbose ? "" : " 2>/dev/null");
 	if (err < 0)
-		return -1;
+		goto out;
 	err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
 	if (err < 0)
-		return -1;
+		goto out;
 
+	tmpl_old = llvm_param.clang_bpf_cmd_template;
 	llvm_param.clang_bpf_cmd_template = tmpl_new;
+	clang_opt_old = llvm_param.clang_opt;
 	llvm_param.clang_opt = clang_opt_new;
-	err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
+
+	err = llvm__compile_bpf("-", p_obj_buf, p_obj_buf_sz);
+
+	llvm_param.clang_bpf_cmd_template = tmpl_old;
+	llvm_param.clang_opt = clang_opt_old;
 
 	verbose = old_verbose;
 	if (err)
-		return TEST_FAIL;
+		goto out;
+
+	ret = TEST_OK;
+out:
+	free(tmpl_new);
+	free(clang_opt_new);
+	if (ret != TEST_OK)
+		pr_debug("Failed to compile test case: '%s'\n", desc);
+	return ret;
+}
 
-	err = test__bpf_parsing(obj_buf, obj_buf_sz);
-	free(obj_buf);
-	return err;
+int test__llvm(void)
+{
+	enum test_llvm__testcase i;
+
+	for (i = 0; i < __LLVM_TESTCASE_MAX; i++) {
+		int ret;
+		void *obj_buf = NULL;
+		size_t obj_buf_sz = 0;
+
+		ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+					       i, false);
+
+		if (ret == TEST_OK) {
+			ret = test__bpf_parsing(obj_buf, obj_buf_sz);
+			if (ret != TEST_OK)
+				pr_debug("Failed to parse test case '%s'\n",
+					 bpf_source_table[i].desc);
+		}
+		free(obj_buf);
+
+		switch (ret) {
+		case TEST_SKIP:
+			return TEST_SKIP;
+		case TEST_OK:
+			break;
+		default:
+			/*
+			 * Test 0 is the basic LLVM test. If test 0
+			 * fail, the basic LLVM support not functional
+			 * so the whole test should fail. If other test
+			 * case fail, it can be fixed by adjusting
+			 * config so don't report error.
+			 */
+			if (i == 0)
+				return TEST_FAIL;
+			else
+				return TEST_SKIP;
+		}
+	}
+	return TEST_OK;
 }
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
new file mode 100644
index 0000000..bd63cee
--- /dev/null
+++ b/tools/perf/tests/llvm.h
@@ -0,0 +1,16 @@
+#ifndef PERF_TEST_LLVM_H
+#define PERF_TEST_LLVM_H
+
+#include <stddef.h> /* for size_t */
+#include <stdbool.h> /* for bool */
+
+extern const char test_llvm__bpf_base_prog[];
+
+enum test_llvm__testcase {
+	LLVM_TESTCASE_BASE,
+	__LLVM_TESTCASE_MAX,
+};
+
+int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz,
+			     enum test_llvm__testcase index, bool force);
+#endif

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

* [tip:perf/urgent] perf test: Enhance the LLVM tests: add kbuild test
  2015-11-06 13:49 ` [PATCH v2 6/7] perf test: Enforce LLVM test: add kbuild test Wang Nan
@ 2015-11-08  7:34   ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:34 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: tglx, namhyung, linux-kernel, hpa, jolsa, wangnan0, acme, mingo, lizefan

Commit-ID:  7af3f3d55b80cce40ad94b6b8e173dccedaf25e6
Gitweb:     http://git.kernel.org/tip/7af3f3d55b80cce40ad94b6b8e173dccedaf25e6
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:49:42 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 17:49:50 -0300

perf test: Enhance the LLVM tests: add kbuild test

This patch adds a kbuild testcase to check whether kernel headers can be
correctly found.

For example:
  # mv /lib/modules/4.3.0-rc5{,.bak}
  # perf test LLVM

    38: Test LLVM searching and compiling                        : Skip

  # perf test -v LLVM
  ...
  <stdin>:11:10: fatal error: 'uapi/linux/fs.h' file not found
  #include <uapi/linux/fs.h>
          ^
  1 error generated.
  ERROR:	unable to compile -
  Hint:	Check error message shown above.
  Hint:	You can also pre-compile it into .o using:
     		 clang -target bpf -O2 -c -
	 with proper -I and -D options.
  Failed to compile test case: 'Test kbuild searching'
  test child finished with -2

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-7-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/Build                    |  9 ++++++++-
 tools/perf/tests/bpf-script-test-kbuild.c | 21 +++++++++++++++++++++
 tools/perf/tests/llvm.c                   |  4 ++++
 tools/perf/tests/llvm.h                   |  2 ++
 4 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 6c095b3..a47b211 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -31,7 +31,7 @@ perf-y += sample-parsing.o
 perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
-perf-y += llvm.o llvm-src-base.o
+perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
@@ -41,6 +41,13 @@ $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
 	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
 	$(Q)echo ';' >> $@
 
+$(OUTPUT)tests/llvm-src-kbuild.c: tests/bpf-script-test-kbuild.c
+	$(call rule_mkdir)
+	$(Q)echo '#include <tests/llvm.h>' > $@
+	$(Q)echo 'const char test_llvm__bpf_test_kbuild_prog[] =' >> $@
+	$(Q)sed -e 's/"/\\"/g' -e 's/\(.*\)/"\1\\n"/g' $< >> $@
+	$(Q)echo ';' >> $@
+
 ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 endif
diff --git a/tools/perf/tests/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c
new file mode 100644
index 0000000..3626924
--- /dev/null
+++ b/tools/perf/tests/bpf-script-test-kbuild.c
@@ -0,0 +1,21 @@
+/*
+ * bpf-script-test-kbuild.c
+ * Test include from kernel header
+ */
+#ifndef LINUX_VERSION_CODE
+# error Need LINUX_VERSION_CODE
+# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
+#endif
+#define SEC(NAME) __attribute__((section(NAME), used))
+
+#include <uapi/linux/fs.h>
+#include <uapi/asm/ptrace.h>
+
+SEC("func=vfs_llseek")
+int bpf_func__vfs_llseek(void *ctx)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 05683c5..bc4cf50 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -40,6 +40,10 @@ static struct {
 		.source = test_llvm__bpf_base_prog,
 		.desc = "Basic BPF llvm compiling test",
 	},
+	[LLVM_TESTCASE_KBUILD] = {
+		.source = test_llvm__bpf_test_kbuild_prog,
+		.desc = "Test kbuild searching",
+	},
 };
 
 
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index bd63cee..d91d8f4 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -5,9 +5,11 @@
 #include <stdbool.h> /* for bool */
 
 extern const char test_llvm__bpf_base_prog[];
+extern const char test_llvm__bpf_test_kbuild_prog[];
 
 enum test_llvm__testcase {
 	LLVM_TESTCASE_BASE,
+	LLVM_TESTCASE_KBUILD,
 	__LLVM_TESTCASE_MAX,
 };
 

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

* [tip:perf/urgent] perf test: Add 'perf test BPF'
  2015-11-06 13:49 ` [PATCH v2 7/7] perf test: Add 'perf test BPF' Wang Nan
@ 2015-11-08  7:34   ` tip-bot for Wang Nan
  0 siblings, 0 replies; 19+ messages in thread
From: tip-bot for Wang Nan @ 2015-11-08  7:34 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: wangnan0, hpa, acme, ast, jolsa, mingo, namhyung, tglx,
	linux-kernel, lizefan

Commit-ID:  ba1fae431e74bb427a699187434142fd3fe98390
Gitweb:     http://git.kernel.org/tip/ba1fae431e74bb427a699187434142fd3fe98390
Author:     Wang Nan <wangnan0@huawei.com>
AuthorDate: Fri, 6 Nov 2015 13:49:43 +0000
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Fri, 6 Nov 2015 17:50:03 -0300

perf test: Add 'perf test BPF'

This patch adds BPF testcase for testing BPF event filtering.

By utilizing the result of 'perf test LLVM', this patch compiles the
eBPF sample program then test its ability. The BPF script in 'perf test
LLVM' lets only 50% samples generated by epoll_pwait() to be captured.
This patch runs that system call for 111 times, so the result should
contain 56 samples.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-8-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/tests/Build          |   1 +
 tools/perf/tests/bpf.c          | 209 ++++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/builtin-test.c |   4 +
 tools/perf/tests/tests.h        |   1 +
 tools/perf/util/bpf-loader.c    |  24 ++++-
 tools/perf/util/bpf-loader.h    |  10 ++
 6 files changed, 248 insertions(+), 1 deletion(-)

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index a47b211..f41ebf8 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
 perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o
+perf-y += bpf.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
new file mode 100644
index 0000000..ec16f78
--- /dev/null
+++ b/tools/perf/tests/bpf.c
@@ -0,0 +1,209 @@
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <util/bpf-loader.h>
+#include <util/evlist.h>
+#include "tests.h"
+#include "llvm.h"
+#include "debug.h"
+#define NR_ITERS       111
+
+#ifdef HAVE_LIBBPF_SUPPORT
+
+static int epoll_pwait_loop(void)
+{
+	int i;
+
+	/* Should fail NR_ITERS times */
+	for (i = 0; i < NR_ITERS; i++)
+		epoll_pwait(-(i + 1), NULL, 0, 0, NULL);
+	return 0;
+}
+
+static struct {
+	enum test_llvm__testcase prog_id;
+	const char *desc;
+	const char *name;
+	const char *msg_compile_fail;
+	const char *msg_load_fail;
+	int (*target_func)(void);
+	int expect_result;
+} bpf_testcase_table[] = {
+	{
+		LLVM_TESTCASE_BASE,
+		"Test basic BPF filtering",
+		"[basic_bpf_test]",
+		"fix 'perf test LLVM' first",
+		"load bpf object failed",
+		&epoll_pwait_loop,
+		(NR_ITERS + 1) / 2,
+	},
+};
+
+static int do_test(struct bpf_object *obj, int (*func)(void),
+		   int expect)
+{
+	struct record_opts opts = {
+		.target = {
+			.uid = UINT_MAX,
+			.uses_mmap = true,
+		},
+		.freq	      = 0,
+		.mmap_pages   = 256,
+		.default_interval = 1,
+	};
+
+	char pid[16];
+	char sbuf[STRERR_BUFSIZE];
+	struct perf_evlist *evlist;
+	int i, ret = TEST_FAIL, err = 0, count = 0;
+
+	struct parse_events_evlist parse_evlist;
+	struct parse_events_error parse_error;
+
+	bzero(&parse_error, sizeof(parse_error));
+	bzero(&parse_evlist, sizeof(parse_evlist));
+	parse_evlist.error = &parse_error;
+	INIT_LIST_HEAD(&parse_evlist.list);
+
+	err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
+	if (err || list_empty(&parse_evlist.list)) {
+		pr_debug("Failed to add events selected by BPF\n");
+		if (!err)
+			return TEST_FAIL;
+	}
+
+	snprintf(pid, sizeof(pid), "%d", getpid());
+	pid[sizeof(pid) - 1] = '\0';
+	opts.target.tid = opts.target.pid = pid;
+
+	/* Instead of perf_evlist__new_default, don't add default events */
+	evlist = perf_evlist__new();
+	if (!evlist) {
+		pr_debug("No ehough memory to create evlist\n");
+		return TEST_FAIL;
+	}
+
+	err = perf_evlist__create_maps(evlist, &opts.target);
+	if (err < 0) {
+		pr_debug("Not enough memory to create thread/cpu maps\n");
+		goto out_delete_evlist;
+	}
+
+	perf_evlist__splice_list_tail(evlist, &parse_evlist.list);
+	evlist->nr_groups = parse_evlist.nr_groups;
+
+	perf_evlist__config(evlist, &opts);
+
+	err = perf_evlist__open(evlist);
+	if (err < 0) {
+		pr_debug("perf_evlist__open: %s\n",
+			 strerror_r(errno, sbuf, sizeof(sbuf)));
+		goto out_delete_evlist;
+	}
+
+	err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
+	if (err < 0) {
+		pr_debug("perf_evlist__mmap: %s\n",
+			 strerror_r(errno, sbuf, sizeof(sbuf)));
+		goto out_delete_evlist;
+	}
+
+	perf_evlist__enable(evlist);
+	(*func)();
+	perf_evlist__disable(evlist);
+
+	for (i = 0; i < evlist->nr_mmaps; i++) {
+		union perf_event *event;
+
+		while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+			const u32 type = event->header.type;
+
+			if (type == PERF_RECORD_SAMPLE)
+				count ++;
+		}
+	}
+
+	if (count != expect)
+		pr_debug("BPF filter result incorrect\n");
+
+	ret = TEST_OK;
+
+out_delete_evlist:
+	perf_evlist__delete(evlist);
+	return ret;
+}
+
+static struct bpf_object *
+prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+	struct bpf_object *obj;
+
+	obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name);
+	if (IS_ERR(obj)) {
+		pr_debug("Compile BPF program failed.\n");
+		return NULL;
+	}
+	return obj;
+}
+
+static int __test__bpf(int index)
+{
+	int ret;
+	void *obj_buf;
+	size_t obj_buf_sz;
+	struct bpf_object *obj;
+
+	ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+				       bpf_testcase_table[index].prog_id,
+				       true);
+	if (ret != TEST_OK || !obj_buf || !obj_buf_sz) {
+		pr_debug("Unable to get BPF object, %s\n",
+			 bpf_testcase_table[index].msg_compile_fail);
+		if (index == 0)
+			return TEST_SKIP;
+		else
+			return TEST_FAIL;
+	}
+
+	obj = prepare_bpf(obj_buf, obj_buf_sz,
+			  bpf_testcase_table[index].name);
+	if (!obj) {
+		ret = TEST_FAIL;
+		goto out;
+	}
+
+	ret = do_test(obj,
+		      bpf_testcase_table[index].target_func,
+		      bpf_testcase_table[index].expect_result);
+out:
+	bpf__clear();
+	return ret;
+}
+
+int test__bpf(void)
+{
+	unsigned int i;
+	int err;
+
+	if (geteuid() != 0) {
+		pr_debug("Only root can run BPF test\n");
+		return TEST_SKIP;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
+		err = __test__bpf(i);
+
+		if (err != TEST_OK)
+			return err;
+	}
+
+	return TEST_OK;
+}
+
+#else
+int test__bpf(void)
+{
+	pr_debug("Skip BPF test because BPF support is not compiled\n");
+	return TEST_SKIP;
+}
+#endif
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 66f72d3..7b0120a 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -166,6 +166,10 @@ static struct test generic_tests[] = {
 		.func = test_session_topology,
 	},
 	{
+		.desc = "Test BPF filter",
+		.func = test__bpf,
+	},
+	{
 		.func = NULL,
 	},
 };
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index c804869..3c8734a 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -66,6 +66,7 @@ int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 int test__thread_map(void);
 int test__llvm(void);
+int test__bpf(void);
 int test_session_topology(void);
 
 #if defined(__arm__) || defined(__aarch64__)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index e3afa1b..4c50411 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -34,10 +34,32 @@ struct bpf_prog_priv {
 	struct perf_probe_event pev;
 };
 
+static bool libbpf_initialized;
+
+struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+	struct bpf_object *obj;
+
+	if (!libbpf_initialized) {
+		libbpf_set_print(libbpf_warning,
+				 libbpf_info,
+				 libbpf_debug);
+		libbpf_initialized = true;
+	}
+
+	obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name);
+	if (IS_ERR(obj)) {
+		pr_debug("bpf: failed to load buffer\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	return obj;
+}
+
 struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 {
 	struct bpf_object *obj;
-	static bool libbpf_initialized;
 
 	if (!libbpf_initialized) {
 		libbpf_set_print(libbpf_warning,
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index 5eb3629..9caf3ae 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -34,6 +34,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source);
 int bpf__strerror_prepare_load(const char *filename, bool source,
 			       int err, char *buf, size_t size);
 
+struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz,
+					    const char *name);
+
 void bpf__clear(void);
 
 int bpf__probe(struct bpf_object *obj);
@@ -55,6 +58,13 @@ bpf__prepare_load(const char *filename __maybe_unused,
 	return ERR_PTR(-ENOTSUP);
 }
 
+static inline struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf __maybe_unused,
+					   size_t obj_buf_sz __maybe_unused)
+{
+	return ERR_PTR(-ENOTSUP);
+}
+
 static inline void bpf__clear(void) { }
 
 static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;}

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

end of thread, other threads:[~2015-11-08  7:35 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-11-06 13:49 [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Wang Nan
2015-11-06 13:49 ` [PATCH v2 1/7] bpf tools: Improve libbpf error reporting Wang Nan
2015-11-06 14:06   ` Arnaldo Carvalho de Melo
2015-11-08  7:32   ` [tip:perf/urgent] " tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 2/7] bpf tools: Add new API bpf_object__get_kversion() Wang Nan
2015-11-08  7:32   ` [tip:perf/urgent] bpf tools: Add new API bpf_object__get_kversion () tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 3/7] perf tools: Make fetch_kernel_version() public available Wang Nan
2015-11-06 13:55   ` [PATCH v2 3/7 fix] " Wang Nan
2015-11-08  7:33     ` [tip:perf/urgent] perf tools: Make fetch_kernel_version() publicly available tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 4/7] perf tools: Improve BPF related error messages output Wang Nan
2015-11-06 13:58   ` [PATCH v2 4/7 fix] " Wang Nan
2015-11-08  7:33     ` [tip:perf/urgent] perf bpf: Improve BPF related error messages tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 5/7] perf test: Enforce LLVM test: update basic BPF test program Wang Nan
2015-11-08  7:34   ` [tip:perf/urgent] perf test: Enhance the " tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 6/7] perf test: Enforce LLVM test: add kbuild test Wang Nan
2015-11-08  7:34   ` [tip:perf/urgent] perf test: Enhance the LLVM tests: " tip-bot for Wang Nan
2015-11-06 13:49 ` [PATCH v2 7/7] perf test: Add 'perf test BPF' Wang Nan
2015-11-08  7:34   ` [tip:perf/urgent] " tip-bot for Wang Nan
2015-11-06 19:20 ` [PATCH v2 0/7] perf tools: improve BPF related error delivering and testing Arnaldo Carvalho de Melo

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.