mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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>
			
			
This commit is contained in:
		
							parent
							
								
									d3e0ce3930
								
							
						
					
					
						commit
						b31de018a6
					
				
					 4 changed files with 129 additions and 31 deletions
				
			
		| 
						 | 
					@ -31,9 +31,16 @@ perf-y += sample-parsing.o
 | 
				
			||||||
perf-y += parse-no-sample-id-all.o
 | 
					perf-y += parse-no-sample-id-all.o
 | 
				
			||||||
perf-y += kmod-path.o
 | 
					perf-y += kmod-path.o
 | 
				
			||||||
perf-y += thread-map.o
 | 
					perf-y += thread-map.o
 | 
				
			||||||
perf-y += llvm.o
 | 
					perf-y += llvm.o llvm-src-base.o
 | 
				
			||||||
perf-y += topology.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))
 | 
					ifeq ($(ARCH),$(filter $(ARCH),x86 arm arm64))
 | 
				
			||||||
perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 | 
					perf-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,7 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * bpf-script-example.c
 | 
				
			||||||
 | 
					 * Test basic LLVM building
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
#ifndef LINUX_VERSION_CODE
 | 
					#ifndef LINUX_VERSION_CODE
 | 
				
			||||||
# error Need 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'
 | 
					# error Example: for 4.2 kernel, put 'clang-opt="-DLINUX_VERSION_CODE=0x40200" into llvm section of ~/.perfconfig'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
#include <bpf/libbpf.h>
 | 
					#include <bpf/libbpf.h>
 | 
				
			||||||
#include <util/llvm-utils.h>
 | 
					#include <util/llvm-utils.h>
 | 
				
			||||||
#include <util/cache.h>
 | 
					#include <util/cache.h>
 | 
				
			||||||
 | 
					#include "llvm.h"
 | 
				
			||||||
#include "tests.h"
 | 
					#include "tests.h"
 | 
				
			||||||
#include "debug.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);
 | 
						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
 | 
					#ifdef HAVE_LIBBPF_SUPPORT
 | 
				
			||||||
static int test__bpf_parsing(void *obj_buf, size_t obj_buf_sz)
 | 
					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);
 | 
						obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, NULL);
 | 
				
			||||||
	if (IS_ERR(obj))
 | 
						if (IS_ERR(obj))
 | 
				
			||||||
		return -1;
 | 
							return TEST_FAIL;
 | 
				
			||||||
	bpf_object__close(obj);
 | 
						bpf_object__close(obj);
 | 
				
			||||||
	return 0;
 | 
						return TEST_OK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
static int test__bpf_parsing(void *obj_buf __maybe_unused,
 | 
					static int test__bpf_parsing(void *obj_buf __maybe_unused,
 | 
				
			||||||
			     size_t obj_buf_sz __maybe_unused)
 | 
								     size_t obj_buf_sz __maybe_unused)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	pr_debug("Skip bpf parsing\n");
 | 
						pr_debug("Skip bpf parsing\n");
 | 
				
			||||||
	return 0;
 | 
						return TEST_OK;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#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;
 | 
						const char *source;
 | 
				
			||||||
	void *obj_buf;
 | 
						const char *desc;
 | 
				
			||||||
	size_t obj_buf_sz;
 | 
						const char *tmpl_old, *clang_opt_old;
 | 
				
			||||||
	int err, old_verbose;
 | 
						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);
 | 
						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
 | 
						 * 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
 | 
						 * 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");
 | 
							pr_debug("No clang and no verbosive, skip this test\n");
 | 
				
			||||||
		return TEST_SKIP;
 | 
							return TEST_SKIP;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	old_verbose = verbose;
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * llvm is verbosity when error. Suppress all error output if
 | 
						 * llvm is verbosity when error. Suppress all error output if
 | 
				
			||||||
	 * not 'perf test -v'.
 | 
						 * not 'perf test -v'.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
 | 
						old_verbose = verbose;
 | 
				
			||||||
	if (verbose == 0)
 | 
						if (verbose == 0)
 | 
				
			||||||
		verbose = -1;
 | 
							verbose = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*p_obj_buf = NULL;
 | 
				
			||||||
 | 
						*p_obj_buf_sz = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!llvm_param.clang_bpf_cmd_template)
 | 
						if (!llvm_param.clang_bpf_cmd_template)
 | 
				
			||||||
		return -1;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!llvm_param.clang_opt)
 | 
						if (!llvm_param.clang_opt)
 | 
				
			||||||
		llvm_param.clang_opt = strdup("");
 | 
							llvm_param.clang_opt = strdup("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = asprintf(&tmpl_new, "echo '%s' | %s", test_bpf_prog,
 | 
						err = asprintf(&tmpl_new, "echo '%s' | %s%s", source,
 | 
				
			||||||
		       llvm_param.clang_bpf_cmd_template);
 | 
							       llvm_param.clang_bpf_cmd_template,
 | 
				
			||||||
 | 
							       old_verbose ? "" : " 2>/dev/null");
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return -1;
 | 
							goto out;
 | 
				
			||||||
	err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
 | 
						err = asprintf(&clang_opt_new, "-xc %s", llvm_param.clang_opt);
 | 
				
			||||||
	if (err < 0)
 | 
						if (err < 0)
 | 
				
			||||||
		return -1;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmpl_old = llvm_param.clang_bpf_cmd_template;
 | 
				
			||||||
	llvm_param.clang_bpf_cmd_template = tmpl_new;
 | 
						llvm_param.clang_bpf_cmd_template = tmpl_new;
 | 
				
			||||||
 | 
						clang_opt_old = llvm_param.clang_opt;
 | 
				
			||||||
	llvm_param.clang_opt = clang_opt_new;
 | 
						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;
 | 
						verbose = old_verbose;
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		return TEST_FAIL;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = test__bpf_parsing(obj_buf, obj_buf_sz);
 | 
						ret = TEST_OK;
 | 
				
			||||||
	free(obj_buf);
 | 
					out:
 | 
				
			||||||
	return err;
 | 
						free(tmpl_new);
 | 
				
			||||||
 | 
						free(clang_opt_new);
 | 
				
			||||||
 | 
						if (ret != TEST_OK)
 | 
				
			||||||
 | 
							pr_debug("Failed to compile test case: '%s'\n", desc);
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								tools/perf/tests/llvm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								tools/perf/tests/llvm.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -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
 | 
				
			||||||
		Loading…
	
		Reference in a new issue