18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> 48c2ecf20Sopenharmony_ci * Copyright (C) 2015, Huawei Inc. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <errno.h> 88c2ecf20Sopenharmony_ci#include <limits.h> 98c2ecf20Sopenharmony_ci#include <stdio.h> 108c2ecf20Sopenharmony_ci#include <stdlib.h> 118c2ecf20Sopenharmony_ci#include <unistd.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 158c2ecf20Sopenharmony_ci#include "debug.h" 168c2ecf20Sopenharmony_ci#include "llvm-utils.h" 178c2ecf20Sopenharmony_ci#include "config.h" 188c2ecf20Sopenharmony_ci#include "util.h" 198c2ecf20Sopenharmony_ci#include <sys/wait.h> 208c2ecf20Sopenharmony_ci#include <subcmd/exec-cmd.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ 238c2ecf20Sopenharmony_ci "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ 248c2ecf20Sopenharmony_ci "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ 258c2ecf20Sopenharmony_ci "$CLANG_OPTIONS $PERF_BPF_INC_OPTIONS $KERNEL_INC_OPTIONS " \ 268c2ecf20Sopenharmony_ci "-Wno-unused-value -Wno-pointer-sign " \ 278c2ecf20Sopenharmony_ci "-working-directory $WORKING_DIR " \ 288c2ecf20Sopenharmony_ci "-c \"$CLANG_SOURCE\" -target bpf $CLANG_EMIT_LLVM -O2 -o - $LLVM_OPTIONS_PIPE" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct llvm_param llvm_param = { 318c2ecf20Sopenharmony_ci .clang_path = "clang", 328c2ecf20Sopenharmony_ci .llc_path = "llc", 338c2ecf20Sopenharmony_ci .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE, 348c2ecf20Sopenharmony_ci .clang_opt = NULL, 358c2ecf20Sopenharmony_ci .opts = NULL, 368c2ecf20Sopenharmony_ci .kbuild_dir = NULL, 378c2ecf20Sopenharmony_ci .kbuild_opts = NULL, 388c2ecf20Sopenharmony_ci .user_set_param = false, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciint perf_llvm_config(const char *var, const char *value) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci if (!strstarts(var, "llvm.")) 448c2ecf20Sopenharmony_ci return 0; 458c2ecf20Sopenharmony_ci var += sizeof("llvm.") - 1; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!strcmp(var, "clang-path")) 488c2ecf20Sopenharmony_ci llvm_param.clang_path = strdup(value); 498c2ecf20Sopenharmony_ci else if (!strcmp(var, "clang-bpf-cmd-template")) 508c2ecf20Sopenharmony_ci llvm_param.clang_bpf_cmd_template = strdup(value); 518c2ecf20Sopenharmony_ci else if (!strcmp(var, "clang-opt")) 528c2ecf20Sopenharmony_ci llvm_param.clang_opt = strdup(value); 538c2ecf20Sopenharmony_ci else if (!strcmp(var, "kbuild-dir")) 548c2ecf20Sopenharmony_ci llvm_param.kbuild_dir = strdup(value); 558c2ecf20Sopenharmony_ci else if (!strcmp(var, "kbuild-opts")) 568c2ecf20Sopenharmony_ci llvm_param.kbuild_opts = strdup(value); 578c2ecf20Sopenharmony_ci else if (!strcmp(var, "dump-obj")) 588c2ecf20Sopenharmony_ci llvm_param.dump_obj = !!perf_config_bool(var, value); 598c2ecf20Sopenharmony_ci else if (!strcmp(var, "opts")) 608c2ecf20Sopenharmony_ci llvm_param.opts = strdup(value); 618c2ecf20Sopenharmony_ci else { 628c2ecf20Sopenharmony_ci pr_debug("Invalid LLVM config option: %s\n", value); 638c2ecf20Sopenharmony_ci return -1; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci llvm_param.user_set_param = true; 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic int 708c2ecf20Sopenharmony_cisearch_program(const char *def, const char *name, 718c2ecf20Sopenharmony_ci char *output) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci char *env, *path, *tmp = NULL; 748c2ecf20Sopenharmony_ci char buf[PATH_MAX]; 758c2ecf20Sopenharmony_ci int ret; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci output[0] = '\0'; 788c2ecf20Sopenharmony_ci if (def && def[0] != '\0') { 798c2ecf20Sopenharmony_ci if (def[0] == '/') { 808c2ecf20Sopenharmony_ci if (access(def, F_OK) == 0) { 818c2ecf20Sopenharmony_ci strlcpy(output, def, PATH_MAX); 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci } else if (def[0] != '\0') 858c2ecf20Sopenharmony_ci name = def; 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci env = getenv("PATH"); 898c2ecf20Sopenharmony_ci if (!env) 908c2ecf20Sopenharmony_ci return -1; 918c2ecf20Sopenharmony_ci env = strdup(env); 928c2ecf20Sopenharmony_ci if (!env) 938c2ecf20Sopenharmony_ci return -1; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = -ENOENT; 968c2ecf20Sopenharmony_ci path = strtok_r(env, ":", &tmp); 978c2ecf20Sopenharmony_ci while (path) { 988c2ecf20Sopenharmony_ci scnprintf(buf, sizeof(buf), "%s/%s", path, name); 998c2ecf20Sopenharmony_ci if (access(buf, F_OK) == 0) { 1008c2ecf20Sopenharmony_ci strlcpy(output, buf, PATH_MAX); 1018c2ecf20Sopenharmony_ci ret = 0; 1028c2ecf20Sopenharmony_ci break; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci path = strtok_r(NULL, ":", &tmp); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci free(env); 1088c2ecf20Sopenharmony_ci return ret; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define READ_SIZE 4096 1128c2ecf20Sopenharmony_cistatic int 1138c2ecf20Sopenharmony_ciread_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci int err = 0; 1168c2ecf20Sopenharmony_ci void *buf = NULL; 1178c2ecf20Sopenharmony_ci FILE *file = NULL; 1188c2ecf20Sopenharmony_ci size_t read_sz = 0, buf_sz = 0; 1198c2ecf20Sopenharmony_ci char serr[STRERR_BUFSIZE]; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci file = popen(cmd, "r"); 1228c2ecf20Sopenharmony_ci if (!file) { 1238c2ecf20Sopenharmony_ci pr_err("ERROR: unable to popen cmd: %s\n", 1248c2ecf20Sopenharmony_ci str_error_r(errno, serr, sizeof(serr))); 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci while (!feof(file) && !ferror(file)) { 1298c2ecf20Sopenharmony_ci /* 1308c2ecf20Sopenharmony_ci * Make buf_sz always have obe byte extra space so we 1318c2ecf20Sopenharmony_ci * can put '\0' there. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci if (buf_sz - read_sz < READ_SIZE + 1) { 1348c2ecf20Sopenharmony_ci void *new_buf; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci buf_sz = read_sz + READ_SIZE + 1; 1378c2ecf20Sopenharmony_ci new_buf = realloc(buf, buf_sz); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!new_buf) { 1408c2ecf20Sopenharmony_ci pr_err("ERROR: failed to realloc memory\n"); 1418c2ecf20Sopenharmony_ci err = -ENOMEM; 1428c2ecf20Sopenharmony_ci goto errout; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci buf = new_buf; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci read_sz += fread(buf + read_sz, 1, READ_SIZE, file); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (buf_sz - read_sz < 1) { 1518c2ecf20Sopenharmony_ci pr_err("ERROR: internal error\n"); 1528c2ecf20Sopenharmony_ci err = -EINVAL; 1538c2ecf20Sopenharmony_ci goto errout; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (ferror(file)) { 1578c2ecf20Sopenharmony_ci pr_err("ERROR: error occurred when reading from pipe: %s\n", 1588c2ecf20Sopenharmony_ci str_error_r(errno, serr, sizeof(serr))); 1598c2ecf20Sopenharmony_ci err = -EIO; 1608c2ecf20Sopenharmony_ci goto errout; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci err = WEXITSTATUS(pclose(file)); 1648c2ecf20Sopenharmony_ci file = NULL; 1658c2ecf20Sopenharmony_ci if (err) { 1668c2ecf20Sopenharmony_ci err = -EINVAL; 1678c2ecf20Sopenharmony_ci goto errout; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* 1718c2ecf20Sopenharmony_ci * If buf is string, give it terminal '\0' to make our life 1728c2ecf20Sopenharmony_ci * easier. If buf is not string, that '\0' is out of space 1738c2ecf20Sopenharmony_ci * indicated by read_sz so caller won't even notice it. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci ((char *)buf)[read_sz] = '\0'; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!p_buf) 1788c2ecf20Sopenharmony_ci free(buf); 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci *p_buf = buf; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (p_read_sz) 1838c2ecf20Sopenharmony_ci *p_read_sz = read_sz; 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cierrout: 1878c2ecf20Sopenharmony_ci if (file) 1888c2ecf20Sopenharmony_ci pclose(file); 1898c2ecf20Sopenharmony_ci free(buf); 1908c2ecf20Sopenharmony_ci if (p_buf) 1918c2ecf20Sopenharmony_ci *p_buf = NULL; 1928c2ecf20Sopenharmony_ci if (p_read_sz) 1938c2ecf20Sopenharmony_ci *p_read_sz = 0; 1948c2ecf20Sopenharmony_ci return err; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline void 1988c2ecf20Sopenharmony_ciforce_set_env(const char *var, const char *value) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci if (value) { 2018c2ecf20Sopenharmony_ci setenv(var, value, 1); 2028c2ecf20Sopenharmony_ci pr_debug("set env: %s=%s\n", var, value); 2038c2ecf20Sopenharmony_ci } else { 2048c2ecf20Sopenharmony_ci unsetenv(var); 2058c2ecf20Sopenharmony_ci pr_debug("unset env: %s\n", var); 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void 2108c2ecf20Sopenharmony_civersion_notice(void) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci pr_err( 2138c2ecf20Sopenharmony_ci" \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" 2148c2ecf20Sopenharmony_ci" \tYou may want to try git trunk:\n" 2158c2ecf20Sopenharmony_ci" \t\tgit clone http://llvm.org/git/llvm.git\n" 2168c2ecf20Sopenharmony_ci" \t\t and\n" 2178c2ecf20Sopenharmony_ci" \t\tgit clone http://llvm.org/git/clang.git\n\n" 2188c2ecf20Sopenharmony_ci" \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" 2198c2ecf20Sopenharmony_ci" \tdebian/ubuntu:\n" 2208c2ecf20Sopenharmony_ci" \t\thttp://llvm.org/apt\n\n" 2218c2ecf20Sopenharmony_ci" \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" 2228c2ecf20Sopenharmony_ci" \toption in [llvm] section of ~/.perfconfig to:\n\n" 2238c2ecf20Sopenharmony_ci" \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS $PERF_BPF_INC_OPTIONS \\\n" 2248c2ecf20Sopenharmony_ci" \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" 2258c2ecf20Sopenharmony_ci" \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" 2268c2ecf20Sopenharmony_ci" \t(Replace /path/to/llc with path to your llc)\n\n" 2278c2ecf20Sopenharmony_ci); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int detect_kbuild_dir(char **kbuild_dir) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci const char *test_dir = llvm_param.kbuild_dir; 2338c2ecf20Sopenharmony_ci const char *prefix_dir = ""; 2348c2ecf20Sopenharmony_ci const char *suffix_dir = ""; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* _UTSNAME_LENGTH is 65 */ 2378c2ecf20Sopenharmony_ci char release[128]; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci char *autoconf_path; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci int err; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (!test_dir) { 2448c2ecf20Sopenharmony_ci err = fetch_kernel_version(NULL, release, 2458c2ecf20Sopenharmony_ci sizeof(release)); 2468c2ecf20Sopenharmony_ci if (err) 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci test_dir = release; 2508c2ecf20Sopenharmony_ci prefix_dir = "/lib/modules/"; 2518c2ecf20Sopenharmony_ci suffix_dir = "/build"; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h", 2558c2ecf20Sopenharmony_ci prefix_dir, test_dir, suffix_dir); 2568c2ecf20Sopenharmony_ci if (err < 0) 2578c2ecf20Sopenharmony_ci return -ENOMEM; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (access(autoconf_path, R_OK) == 0) { 2608c2ecf20Sopenharmony_ci free(autoconf_path); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir, 2638c2ecf20Sopenharmony_ci suffix_dir); 2648c2ecf20Sopenharmony_ci if (err < 0) 2658c2ecf20Sopenharmony_ci return -ENOMEM; 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci pr_debug("%s: Couldn't find \"%s\", missing kernel-devel package?.\n", 2698c2ecf20Sopenharmony_ci __func__, autoconf_path); 2708c2ecf20Sopenharmony_ci free(autoconf_path); 2718c2ecf20Sopenharmony_ci return -ENOENT; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic const char *kinc_fetch_script = 2758c2ecf20Sopenharmony_ci"#!/usr/bin/env sh\n" 2768c2ecf20Sopenharmony_ci"if ! test -d \"$KBUILD_DIR\"\n" 2778c2ecf20Sopenharmony_ci"then\n" 2788c2ecf20Sopenharmony_ci" exit 1\n" 2798c2ecf20Sopenharmony_ci"fi\n" 2808c2ecf20Sopenharmony_ci"if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n" 2818c2ecf20Sopenharmony_ci"then\n" 2828c2ecf20Sopenharmony_ci" exit 1\n" 2838c2ecf20Sopenharmony_ci"fi\n" 2848c2ecf20Sopenharmony_ci"TMPDIR=`mktemp -d`\n" 2858c2ecf20Sopenharmony_ci"if test -z \"$TMPDIR\"\n" 2868c2ecf20Sopenharmony_ci"then\n" 2878c2ecf20Sopenharmony_ci" exit 1\n" 2888c2ecf20Sopenharmony_ci"fi\n" 2898c2ecf20Sopenharmony_ci"cat << EOF > $TMPDIR/Makefile\n" 2908c2ecf20Sopenharmony_ci"obj-y := dummy.o\n" 2918c2ecf20Sopenharmony_ci"\\$(obj)/%.o: \\$(src)/%.c\n" 2928c2ecf20Sopenharmony_ci"\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n" 2938c2ecf20Sopenharmony_ci"\t\\$(CC) -c -o \\$@ \\$<\n" 2948c2ecf20Sopenharmony_ci"EOF\n" 2958c2ecf20Sopenharmony_ci"touch $TMPDIR/dummy.c\n" 2968c2ecf20Sopenharmony_ci"make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n" 2978c2ecf20Sopenharmony_ci"RET=$?\n" 2988c2ecf20Sopenharmony_ci"rm -rf $TMPDIR\n" 2998c2ecf20Sopenharmony_ci"exit $RET\n"; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_civoid llvm__get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci static char *saved_kbuild_dir; 3048c2ecf20Sopenharmony_ci static char *saved_kbuild_include_opts; 3058c2ecf20Sopenharmony_ci int err; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!kbuild_dir || !kbuild_include_opts) 3088c2ecf20Sopenharmony_ci return; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci *kbuild_dir = NULL; 3118c2ecf20Sopenharmony_ci *kbuild_include_opts = NULL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci if (saved_kbuild_dir && saved_kbuild_include_opts && 3148c2ecf20Sopenharmony_ci !IS_ERR(saved_kbuild_dir) && !IS_ERR(saved_kbuild_include_opts)) { 3158c2ecf20Sopenharmony_ci *kbuild_dir = strdup(saved_kbuild_dir); 3168c2ecf20Sopenharmony_ci *kbuild_include_opts = strdup(saved_kbuild_include_opts); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (*kbuild_dir && *kbuild_include_opts) 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci zfree(kbuild_dir); 3228c2ecf20Sopenharmony_ci zfree(kbuild_include_opts); 3238c2ecf20Sopenharmony_ci /* 3248c2ecf20Sopenharmony_ci * Don't fall through: it may breaks saved_kbuild_dir and 3258c2ecf20Sopenharmony_ci * saved_kbuild_include_opts if detect them again when 3268c2ecf20Sopenharmony_ci * memory is low. 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { 3328c2ecf20Sopenharmony_ci pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); 3338c2ecf20Sopenharmony_ci pr_debug("Skip kbuild options detection.\n"); 3348c2ecf20Sopenharmony_ci goto errout; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci err = detect_kbuild_dir(kbuild_dir); 3388c2ecf20Sopenharmony_ci if (err) { 3398c2ecf20Sopenharmony_ci pr_warning( 3408c2ecf20Sopenharmony_ci"WARNING:\tunable to get correct kernel building directory.\n" 3418c2ecf20Sopenharmony_ci"Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" 3428c2ecf20Sopenharmony_ci" \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" 3438c2ecf20Sopenharmony_ci" \tdetection.\n\n"); 3448c2ecf20Sopenharmony_ci goto errout; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); 3488c2ecf20Sopenharmony_ci force_set_env("KBUILD_DIR", *kbuild_dir); 3498c2ecf20Sopenharmony_ci force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts); 3508c2ecf20Sopenharmony_ci err = read_from_pipe(kinc_fetch_script, 3518c2ecf20Sopenharmony_ci (void **)kbuild_include_opts, 3528c2ecf20Sopenharmony_ci NULL); 3538c2ecf20Sopenharmony_ci if (err) { 3548c2ecf20Sopenharmony_ci pr_warning( 3558c2ecf20Sopenharmony_ci"WARNING:\tunable to get kernel include directories from '%s'\n" 3568c2ecf20Sopenharmony_ci"Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n" 3578c2ecf20Sopenharmony_ci" \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n" 3588c2ecf20Sopenharmony_ci" \toption in [llvm] to \"\" to suppress this detection.\n\n", 3598c2ecf20Sopenharmony_ci *kbuild_dir); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci zfree(kbuild_dir); 3628c2ecf20Sopenharmony_ci goto errout; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci pr_debug("include option is set to %s\n", *kbuild_include_opts); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci saved_kbuild_dir = strdup(*kbuild_dir); 3688c2ecf20Sopenharmony_ci saved_kbuild_include_opts = strdup(*kbuild_include_opts); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (!saved_kbuild_dir || !saved_kbuild_include_opts) { 3718c2ecf20Sopenharmony_ci zfree(&saved_kbuild_dir); 3728c2ecf20Sopenharmony_ci zfree(&saved_kbuild_include_opts); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci return; 3758c2ecf20Sopenharmony_cierrout: 3768c2ecf20Sopenharmony_ci saved_kbuild_dir = ERR_PTR(-EINVAL); 3778c2ecf20Sopenharmony_ci saved_kbuild_include_opts = ERR_PTR(-EINVAL); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint llvm__get_nr_cpus(void) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci static int nr_cpus_avail = 0; 3838c2ecf20Sopenharmony_ci char serr[STRERR_BUFSIZE]; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (nr_cpus_avail > 0) 3868c2ecf20Sopenharmony_ci return nr_cpus_avail; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); 3898c2ecf20Sopenharmony_ci if (nr_cpus_avail <= 0) { 3908c2ecf20Sopenharmony_ci pr_err( 3918c2ecf20Sopenharmony_ci"WARNING:\tunable to get available CPUs in this system: %s\n" 3928c2ecf20Sopenharmony_ci" \tUse 128 instead.\n", str_error_r(errno, serr, sizeof(serr))); 3938c2ecf20Sopenharmony_ci nr_cpus_avail = 128; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci return nr_cpus_avail; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_civoid llvm__dump_obj(const char *path, void *obj_buf, size_t size) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci char *obj_path = strdup(path); 4018c2ecf20Sopenharmony_ci FILE *fp; 4028c2ecf20Sopenharmony_ci char *p; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (!obj_path) { 4058c2ecf20Sopenharmony_ci pr_warning("WARNING: Not enough memory, skip object dumping\n"); 4068c2ecf20Sopenharmony_ci return; 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci p = strrchr(obj_path, '.'); 4108c2ecf20Sopenharmony_ci if (!p || (strcmp(p, ".c") != 0)) { 4118c2ecf20Sopenharmony_ci pr_warning("WARNING: invalid llvm source path: '%s', skip object dumping\n", 4128c2ecf20Sopenharmony_ci obj_path); 4138c2ecf20Sopenharmony_ci goto out; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci p[1] = 'o'; 4178c2ecf20Sopenharmony_ci fp = fopen(obj_path, "wb"); 4188c2ecf20Sopenharmony_ci if (!fp) { 4198c2ecf20Sopenharmony_ci pr_warning("WARNING: failed to open '%s': %s, skip object dumping\n", 4208c2ecf20Sopenharmony_ci obj_path, strerror(errno)); 4218c2ecf20Sopenharmony_ci goto out; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pr_debug("LLVM: dumping %s\n", obj_path); 4258c2ecf20Sopenharmony_ci if (fwrite(obj_buf, size, 1, fp) != 1) 4268c2ecf20Sopenharmony_ci pr_debug("WARNING: failed to write to file '%s': %s, skip object dumping\n", obj_path, strerror(errno)); 4278c2ecf20Sopenharmony_ci fclose(fp); 4288c2ecf20Sopenharmony_ciout: 4298c2ecf20Sopenharmony_ci free(obj_path); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ciint llvm__compile_bpf(const char *path, void **p_obj_buf, 4338c2ecf20Sopenharmony_ci size_t *p_obj_buf_sz) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci size_t obj_buf_sz; 4368c2ecf20Sopenharmony_ci void *obj_buf = NULL; 4378c2ecf20Sopenharmony_ci int err, nr_cpus_avail; 4388c2ecf20Sopenharmony_ci unsigned int kernel_version; 4398c2ecf20Sopenharmony_ci char linux_version_code_str[64]; 4408c2ecf20Sopenharmony_ci const char *clang_opt = llvm_param.clang_opt; 4418c2ecf20Sopenharmony_ci char clang_path[PATH_MAX], llc_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64]; 4428c2ecf20Sopenharmony_ci char serr[STRERR_BUFSIZE]; 4438c2ecf20Sopenharmony_ci char *kbuild_dir = NULL, *kbuild_include_opts = NULL, 4448c2ecf20Sopenharmony_ci *perf_bpf_include_opts = NULL; 4458c2ecf20Sopenharmony_ci const char *template = llvm_param.clang_bpf_cmd_template; 4468c2ecf20Sopenharmony_ci char *pipe_template = NULL; 4478c2ecf20Sopenharmony_ci const char *opts = llvm_param.opts; 4488c2ecf20Sopenharmony_ci char *command_echo = NULL, *command_out; 4498c2ecf20Sopenharmony_ci char *perf_include_dir = system_path(PERF_INCLUDE_DIR); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (path[0] != '-' && realpath(path, abspath) == NULL) { 4528c2ecf20Sopenharmony_ci err = errno; 4538c2ecf20Sopenharmony_ci pr_err("ERROR: problems with path %s: %s\n", 4548c2ecf20Sopenharmony_ci path, str_error_r(err, serr, sizeof(serr))); 4558c2ecf20Sopenharmony_ci return -err; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!template) 4598c2ecf20Sopenharmony_ci template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci err = search_program(llvm_param.clang_path, 4628c2ecf20Sopenharmony_ci "clang", clang_path); 4638c2ecf20Sopenharmony_ci if (err) { 4648c2ecf20Sopenharmony_ci pr_err( 4658c2ecf20Sopenharmony_ci"ERROR:\tunable to find clang.\n" 4668c2ecf20Sopenharmony_ci"Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" 4678c2ecf20Sopenharmony_ci" \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n"); 4688c2ecf20Sopenharmony_ci version_notice(); 4698c2ecf20Sopenharmony_ci return -ENOENT; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * This is an optional work. Even it fail we can continue our 4748c2ecf20Sopenharmony_ci * work. Needn't to check error return. 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci llvm__get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci nr_cpus_avail = llvm__get_nr_cpus(); 4798c2ecf20Sopenharmony_ci snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", 4808c2ecf20Sopenharmony_ci nr_cpus_avail); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (fetch_kernel_version(&kernel_version, NULL, 0)) 4838c2ecf20Sopenharmony_ci kernel_version = 0; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci snprintf(linux_version_code_str, sizeof(linux_version_code_str), 4868c2ecf20Sopenharmony_ci "0x%x", kernel_version); 4878c2ecf20Sopenharmony_ci if (asprintf(&perf_bpf_include_opts, "-I%s/bpf", perf_include_dir) < 0) 4888c2ecf20Sopenharmony_ci goto errout; 4898c2ecf20Sopenharmony_ci force_set_env("NR_CPUS", nr_cpus_avail_str); 4908c2ecf20Sopenharmony_ci force_set_env("LINUX_VERSION_CODE", linux_version_code_str); 4918c2ecf20Sopenharmony_ci force_set_env("CLANG_EXEC", clang_path); 4928c2ecf20Sopenharmony_ci force_set_env("CLANG_OPTIONS", clang_opt); 4938c2ecf20Sopenharmony_ci force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); 4948c2ecf20Sopenharmony_ci force_set_env("PERF_BPF_INC_OPTIONS", perf_bpf_include_opts); 4958c2ecf20Sopenharmony_ci force_set_env("WORKING_DIR", kbuild_dir ? : "."); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (opts) { 4988c2ecf20Sopenharmony_ci err = search_program(llvm_param.llc_path, "llc", llc_path); 4998c2ecf20Sopenharmony_ci if (err) { 5008c2ecf20Sopenharmony_ci pr_err("ERROR:\tunable to find llc.\n" 5018c2ecf20Sopenharmony_ci "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" 5028c2ecf20Sopenharmony_ci " \tand 'llc-path' option in [llvm] section of ~/.perfconfig.\n"); 5038c2ecf20Sopenharmony_ci version_notice(); 5048c2ecf20Sopenharmony_ci goto errout; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci err = -ENOMEM; 5088c2ecf20Sopenharmony_ci if (asprintf(&pipe_template, "%s -emit-llvm | %s -march=bpf %s -filetype=obj -o -", 5098c2ecf20Sopenharmony_ci template, llc_path, opts) < 0) { 5108c2ecf20Sopenharmony_ci pr_err("ERROR:\tnot enough memory to setup command line\n"); 5118c2ecf20Sopenharmony_ci goto errout; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci template = pipe_template; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci /* 5198c2ecf20Sopenharmony_ci * Since we may reset clang's working dir, path of source file 5208c2ecf20Sopenharmony_ci * should be transferred into absolute path, except we want 5218c2ecf20Sopenharmony_ci * stdin to be source file (testing). 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_ci force_set_env("CLANG_SOURCE", 5248c2ecf20Sopenharmony_ci (path[0] == '-') ? path : abspath); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci pr_debug("llvm compiling command template: %s\n", template); 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* 5298c2ecf20Sopenharmony_ci * Below, substitute control characters for values that can cause the 5308c2ecf20Sopenharmony_ci * echo to misbehave, then substitute the values back. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci err = -ENOMEM; 5338c2ecf20Sopenharmony_ci if (asprintf(&command_echo, "echo -n \a%s\a", template) < 0) 5348c2ecf20Sopenharmony_ci goto errout; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci#define SWAP_CHAR(a, b) do { if (*p == a) *p = b; } while (0) 5378c2ecf20Sopenharmony_ci for (char *p = command_echo; *p; p++) { 5388c2ecf20Sopenharmony_ci SWAP_CHAR('<', '\001'); 5398c2ecf20Sopenharmony_ci SWAP_CHAR('>', '\002'); 5408c2ecf20Sopenharmony_ci SWAP_CHAR('"', '\003'); 5418c2ecf20Sopenharmony_ci SWAP_CHAR('\'', '\004'); 5428c2ecf20Sopenharmony_ci SWAP_CHAR('|', '\005'); 5438c2ecf20Sopenharmony_ci SWAP_CHAR('&', '\006'); 5448c2ecf20Sopenharmony_ci SWAP_CHAR('\a', '"'); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci err = read_from_pipe(command_echo, (void **) &command_out, NULL); 5478c2ecf20Sopenharmony_ci if (err) 5488c2ecf20Sopenharmony_ci goto errout; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci for (char *p = command_out; *p; p++) { 5518c2ecf20Sopenharmony_ci SWAP_CHAR('\001', '<'); 5528c2ecf20Sopenharmony_ci SWAP_CHAR('\002', '>'); 5538c2ecf20Sopenharmony_ci SWAP_CHAR('\003', '"'); 5548c2ecf20Sopenharmony_ci SWAP_CHAR('\004', '\''); 5558c2ecf20Sopenharmony_ci SWAP_CHAR('\005', '|'); 5568c2ecf20Sopenharmony_ci SWAP_CHAR('\006', '&'); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci#undef SWAP_CHAR 5598c2ecf20Sopenharmony_ci pr_debug("llvm compiling command : %s\n", command_out); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci err = read_from_pipe(template, &obj_buf, &obj_buf_sz); 5628c2ecf20Sopenharmony_ci if (err) { 5638c2ecf20Sopenharmony_ci pr_err("ERROR:\tunable to compile %s\n", path); 5648c2ecf20Sopenharmony_ci pr_err("Hint:\tCheck error message shown above.\n"); 5658c2ecf20Sopenharmony_ci pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); 5668c2ecf20Sopenharmony_ci pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); 5678c2ecf20Sopenharmony_ci pr_err(" \twith proper -I and -D options.\n"); 5688c2ecf20Sopenharmony_ci goto errout; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci free(command_echo); 5728c2ecf20Sopenharmony_ci free(command_out); 5738c2ecf20Sopenharmony_ci free(kbuild_dir); 5748c2ecf20Sopenharmony_ci free(kbuild_include_opts); 5758c2ecf20Sopenharmony_ci free(perf_bpf_include_opts); 5768c2ecf20Sopenharmony_ci free(perf_include_dir); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (!p_obj_buf) 5798c2ecf20Sopenharmony_ci free(obj_buf); 5808c2ecf20Sopenharmony_ci else 5818c2ecf20Sopenharmony_ci *p_obj_buf = obj_buf; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (p_obj_buf_sz) 5848c2ecf20Sopenharmony_ci *p_obj_buf_sz = obj_buf_sz; 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_cierrout: 5878c2ecf20Sopenharmony_ci free(command_echo); 5888c2ecf20Sopenharmony_ci free(kbuild_dir); 5898c2ecf20Sopenharmony_ci free(kbuild_include_opts); 5908c2ecf20Sopenharmony_ci free(obj_buf); 5918c2ecf20Sopenharmony_ci free(perf_bpf_include_opts); 5928c2ecf20Sopenharmony_ci free(perf_include_dir); 5938c2ecf20Sopenharmony_ci free(pipe_template); 5948c2ecf20Sopenharmony_ci if (p_obj_buf) 5958c2ecf20Sopenharmony_ci *p_obj_buf = NULL; 5968c2ecf20Sopenharmony_ci if (p_obj_buf_sz) 5978c2ecf20Sopenharmony_ci *p_obj_buf_sz = 0; 5988c2ecf20Sopenharmony_ci return err; 5998c2ecf20Sopenharmony_ci} 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ciint llvm__search_clang(void) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci char clang_path[PATH_MAX]; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return search_program(llvm_param.clang_path, "clang", clang_path); 6068c2ecf20Sopenharmony_ci} 607