diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 5cfb420..2bd5f37 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,17 +32,24 @@ 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.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.c: tests/bpf-script-example.c
+$(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_prog[] =' >> $@
 	$(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 ';' >> $@
+
 perf-$(CONFIG_X86) += perf-time-to-tsc.o
 ifdef CONFIG_AUXTRACE
 perf-$(CONFIG_X86) += insn-x86.o
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/bpf-script-test-kbuild.c b/tools/perf/tests/bpf-script-test-kbuild.c
new file mode 100644
index 0000000..a11f589
--- /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(struct pt_regs *ctx)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+int _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index e256c12..64aaab68 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -143,7 +143,8 @@ int test__bpf(void)
 		return TEST_SKIP;
 	}
 
-	test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz);
+	test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, LLVM_TESTCASE_BASE);
+
 	if (!obj_buf || !obj_buf_sz) {
 		if (verbose == 0)
 			fprintf(stderr, " (fix 'perf test LLVM' first)");
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index fd5fdb0..75cd99f 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -9,6 +9,22 @@
 #include "debug.h"
 #include "llvm.h"
 
+#define SHARED_BUF_INIT_SIZE	(1 << 20)
+struct llvm_testcase {
+	const char *source;
+	const char *errmsg;
+	struct test_llvm__bpf_result *result;
+	bool tried;
+} llvm_testcases[NR_LLVM_TESTCASES + 1] = {
+	[LLVM_TESTCASE_BASE]	= {.source = test_llvm__bpf_prog,
+				   .errmsg = "Basic LLVM compiling failed",
+				   .tried = false},
+	[LLVM_TESTCASE_KBUILD]	= {.source = test_llvm__bpf_test_kbuild_prog,
+				   .errmsg = "llvm.kbuild-dir can be fixed",
+				   .tried = false},
+	{.source = NULL}
+};
+
 static int perf_config_cb(const char *var, const char *val,
 			  void *arg __maybe_unused)
 {
@@ -36,7 +52,7 @@ static int test__bpf_parsing(void *obj_buf __maybe_unused,
 #endif
 
 static char *
-compose_source(void)
+compose_source(const char *raw_source)
 {
 	struct utsname utsname;
 	int version, patchlevel, sublevel, err;
@@ -56,25 +72,27 @@ compose_source(void)
 
 	version_code = (version << 16) + (patchlevel << 8) + sublevel;
 	err = asprintf(&code, "#define LINUX_VERSION_CODE 0x%08lx;\n%s",
-		       version_code, test_llvm__bpf_prog);
+		       version_code, raw_source);
 	if (err < 0)
 		return NULL;
 
 	return code;
 }
 
-#define SHARED_BUF_INIT_SIZE	(1 << 20)
-struct test_llvm__bpf_result *p_test_llvm__bpf_result;
 
-int test__llvm(void)
+static int __test__llvm(int i)
 {
-	char *tmpl_new, *clang_opt_new;
 	void *obj_buf;
 	size_t obj_buf_sz;
 	int err, old_verbose;
-	char *source;
+	const char *tmpl_old, *clang_opt_old;
+	char *tmpl_new, *clang_opt_new, *source;
+	const char *raw_source = llvm_testcases[i].source;
+	struct test_llvm__bpf_result *result = llvm_testcases[i].result;
 
 	perf_config(perf_config_cb, NULL);
+	clang_opt_old = llvm_param.clang_opt;
+	tmpl_old = llvm_param.clang_bpf_cmd_template;
 
 	/*
 	 * Skip this test if user's .perfconfig doesn't set [llvm] section
@@ -99,15 +117,17 @@ int test__llvm(void)
 	if (!llvm_param.clang_opt)
 		llvm_param.clang_opt = strdup("");
 
-	source = compose_source();
+	source = compose_source(raw_source);
 	if (!source) {
 		pr_err("Failed to compose source code\n");
 		return -1;
 	}
 
 	/* Quote __EOF__ so strings in source won't be expanded by shell */
-	err = asprintf(&tmpl_new, "cat << '__EOF__' | %s\n%s\n__EOF__\n",
-		       llvm_param.clang_bpf_cmd_template, source);
+	err = asprintf(&tmpl_new, "cat << '__EOF__' | %s %s \n%s\n__EOF__\n",
+		       llvm_param.clang_bpf_cmd_template,
+		       !old_verbose ? "2>/dev/null" : "",
+		       source);
 	free(source);
 	source = NULL;
 	if (err < 0) {
@@ -123,73 +143,123 @@ int test__llvm(void)
 	llvm_param.clang_opt = clang_opt_new;
 	err = llvm__compile_bpf("-", &obj_buf, &obj_buf_sz);
 
+	free((void *)llvm_param.clang_bpf_cmd_template);
+	free((void *)llvm_param.clang_opt);
+	llvm_param.clang_bpf_cmd_template = tmpl_old;
+	llvm_param.clang_opt = clang_opt_old;
+
 	verbose = old_verbose;
-	if (err) {
-		if (!verbose)
-			fprintf(stderr, " (use -v to see error message)");
+	if (err)
 		return -1;
-	}
 
 	err = test__bpf_parsing(obj_buf, obj_buf_sz);
-	if (!err && p_test_llvm__bpf_result) {
+	if (!err && result) {
 		if (obj_buf_sz > SHARED_BUF_INIT_SIZE) {
 			pr_err("Resulting object too large\n");
 		} else {
-			p_test_llvm__bpf_result->size = obj_buf_sz;
-			memcpy(p_test_llvm__bpf_result->object,
-			       obj_buf, obj_buf_sz);
+			result->size = obj_buf_sz;
+			memcpy(result->object, obj_buf, obj_buf_sz);
 		}
 	}
 	free(obj_buf);
 	return err;
 }
 
+int test__llvm(void)
+{
+	int i, ret;
+
+	for (i = 0; llvm_testcases[i].source; i++) {
+		ret = __test__llvm(i);
+		if (i == 0 && ret) {
+			/*
+			 * First testcase tests basic LLVM compiling. If it
+			 * fails, no need to check others.
+			 */
+			if (!verbose)
+				fprintf(stderr, " (use -v to see error message)");
+			return ret;
+		} else if (ret) {
+			if (!verbose && llvm_testcases[i].errmsg)
+				fprintf(stderr, " (%s)", llvm_testcases[i].errmsg);
+			return 0;
+		}
+	}
+	return 0;
+}
+
 void test__llvm_prepare(void)
 {
-	p_test_llvm__bpf_result = mmap(NULL, SHARED_BUF_INIT_SIZE,
-				       PROT_READ | PROT_WRITE,
-				       MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-	if (!p_test_llvm__bpf_result)
-		return;
-	memset((void *)p_test_llvm__bpf_result, '\0', SHARED_BUF_INIT_SIZE);
+	int i;
+
+	for (i = 0; llvm_testcases[i].source; i++) {
+		struct test_llvm__bpf_result *result;
+
+		result = mmap(NULL, SHARED_BUF_INIT_SIZE,
+			      PROT_READ | PROT_WRITE,
+			      MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+		if (!result)
+			return;
+		memset((void *)result, '\0', SHARED_BUF_INIT_SIZE);
+
+		llvm_testcases[i].result = result;
+	}
 }
 
 void test__llvm_cleanup(void)
 {
-	unsigned long boundary, buf_end;
+	int i;
 
-	if (!p_test_llvm__bpf_result)
-		return;
-	if (p_test_llvm__bpf_result->size == 0) {
-		munmap((void *)p_test_llvm__bpf_result, SHARED_BUF_INIT_SIZE);
-		p_test_llvm__bpf_result = NULL;
-		return;
-	}
+	for (i = 0; llvm_testcases[i].source; i++) {
+		struct test_llvm__bpf_result *result;
+		unsigned long boundary, buf_end;
 
-	buf_end = (unsigned long)p_test_llvm__bpf_result + SHARED_BUF_INIT_SIZE;
+		result = llvm_testcases[i].result;
+		llvm_testcases[i].tried = true;
 
-	boundary = (unsigned long)(p_test_llvm__bpf_result);
-	boundary += p_test_llvm__bpf_result->size;
-	boundary = (boundary + (page_size - 1)) &
+		if (!result)
+			continue;
+
+		if (result->size == 0) {
+			munmap((void *)result, SHARED_BUF_INIT_SIZE);
+			result = NULL;
+			llvm_testcases[i].result = NULL;
+			continue;
+		}
+
+		buf_end = (unsigned long)result + SHARED_BUF_INIT_SIZE;
+
+		boundary = (unsigned long)(result);
+		boundary += result->size;
+		boundary = (boundary + (page_size - 1)) &
 			(~((unsigned long)page_size - 1));
-	munmap((void *)boundary, buf_end - boundary);
+		munmap((void *)boundary, buf_end - boundary);
+	}
 }
 
 void
-test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz)
+test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, int index)
 {
+	struct test_llvm__bpf_result *result;
+
 	*p_obj_buf = NULL;
 	*p_obj_buf_sz = 0;
 
-	if (!p_test_llvm__bpf_result) {
+	if (index > NR_LLVM_TESTCASES)
+		return;
+
+	result = llvm_testcases[index].result;
+
+	if (!result && !llvm_testcases[index].tried) {
 		test__llvm_prepare();
 		test__llvm();
 		test__llvm_cleanup();
 	}
 
-	if (!p_test_llvm__bpf_result)
+	result = llvm_testcases[index].result;
+	if (!result)
 		return;
 
-	*p_obj_buf = p_test_llvm__bpf_result->object;
-	*p_obj_buf_sz = p_test_llvm__bpf_result->size;
+	*p_obj_buf = result->object;
+	*p_obj_buf_sz = result->size;
 }
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index 2fd7ed6..78ec01d 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -8,8 +8,14 @@ struct test_llvm__bpf_result {
 	char object[];
 };
 
-extern struct test_llvm__bpf_result *p_test_llvm__bpf_result;
 extern const char test_llvm__bpf_prog[];
-void test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz);
+extern const char test_llvm__bpf_test_kbuild_prog[];
+
+enum test_llvm__testcase {
+	LLVM_TESTCASE_BASE,
+	LLVM_TESTCASE_KBUILD,
+	NR_LLVM_TESTCASES,
+};
+void test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, int index);
 
 #endif
