Commit | Line | Data |
---|---|---|
aa61fd05 WN |
1 | /* |
2 | * Copyright (C) 2015, Wang Nan <wangnan0@huawei.com> | |
3 | * Copyright (C) 2015, Huawei Inc. | |
4 | */ | |
5 | ||
78478269 | 6 | #include <limits.h> |
aa61fd05 | 7 | #include <stdio.h> |
78478269 | 8 | #include <stdlib.h> |
aa61fd05 WN |
9 | #include "debug.h" |
10 | #include "llvm-utils.h" | |
aa61fd05 WN |
11 | |
12 | #define CLANG_BPF_CMD_DEFAULT_TEMPLATE \ | |
59f41af9 | 13 | "$CLANG_EXEC -D__KERNEL__ -D__NR_CPUS__=$NR_CPUS "\ |
4a4f66a1 | 14 | "-DLINUX_VERSION_CODE=$LINUX_VERSION_CODE " \ |
59f41af9 WN |
15 | "$CLANG_OPTIONS $KERNEL_INC_OPTIONS " \ |
16 | "-Wno-unused-value -Wno-pointer-sign " \ | |
17 | "-working-directory $WORKING_DIR " \ | |
18 | "-c \"$CLANG_SOURCE\" -target bpf -O2 -o -" | |
aa61fd05 WN |
19 | |
20 | struct llvm_param llvm_param = { | |
21 | .clang_path = "clang", | |
22 | .clang_bpf_cmd_template = CLANG_BPF_CMD_DEFAULT_TEMPLATE, | |
23 | .clang_opt = NULL, | |
24 | .kbuild_dir = NULL, | |
25 | .kbuild_opts = NULL, | |
9bc898c7 | 26 | .user_set_param = false, |
aa61fd05 WN |
27 | }; |
28 | ||
29 | int perf_llvm_config(const char *var, const char *value) | |
30 | { | |
31 | if (prefixcmp(var, "llvm.")) | |
32 | return 0; | |
33 | var += sizeof("llvm.") - 1; | |
34 | ||
35 | if (!strcmp(var, "clang-path")) | |
36 | llvm_param.clang_path = strdup(value); | |
37 | else if (!strcmp(var, "clang-bpf-cmd-template")) | |
38 | llvm_param.clang_bpf_cmd_template = strdup(value); | |
39 | else if (!strcmp(var, "clang-opt")) | |
40 | llvm_param.clang_opt = strdup(value); | |
41 | else if (!strcmp(var, "kbuild-dir")) | |
42 | llvm_param.kbuild_dir = strdup(value); | |
43 | else if (!strcmp(var, "kbuild-opts")) | |
44 | llvm_param.kbuild_opts = strdup(value); | |
45 | else | |
46 | return -1; | |
9bc898c7 | 47 | llvm_param.user_set_param = true; |
aa61fd05 WN |
48 | return 0; |
49 | } | |
4cea3a9c WN |
50 | |
51 | static int | |
52 | search_program(const char *def, const char *name, | |
53 | char *output) | |
54 | { | |
55 | char *env, *path, *tmp = NULL; | |
56 | char buf[PATH_MAX]; | |
57 | int ret; | |
58 | ||
59 | output[0] = '\0'; | |
60 | if (def && def[0] != '\0') { | |
61 | if (def[0] == '/') { | |
62 | if (access(def, F_OK) == 0) { | |
63 | strlcpy(output, def, PATH_MAX); | |
64 | return 0; | |
65 | } | |
66 | } else if (def[0] != '\0') | |
67 | name = def; | |
68 | } | |
69 | ||
70 | env = getenv("PATH"); | |
71 | if (!env) | |
72 | return -1; | |
73 | env = strdup(env); | |
74 | if (!env) | |
75 | return -1; | |
76 | ||
77 | ret = -ENOENT; | |
78 | path = strtok_r(env, ":", &tmp); | |
79 | while (path) { | |
80 | scnprintf(buf, sizeof(buf), "%s/%s", path, name); | |
81 | if (access(buf, F_OK) == 0) { | |
82 | strlcpy(output, buf, PATH_MAX); | |
83 | ret = 0; | |
84 | break; | |
85 | } | |
86 | path = strtok_r(NULL, ":", &tmp); | |
87 | } | |
88 | ||
89 | free(env); | |
90 | return ret; | |
91 | } | |
92 | ||
93 | #define READ_SIZE 4096 | |
94 | static int | |
95 | read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) | |
96 | { | |
97 | int err = 0; | |
98 | void *buf = NULL; | |
99 | FILE *file = NULL; | |
100 | size_t read_sz = 0, buf_sz = 0; | |
76267147 | 101 | char serr[STRERR_BUFSIZE]; |
4cea3a9c WN |
102 | |
103 | file = popen(cmd, "r"); | |
104 | if (!file) { | |
105 | pr_err("ERROR: unable to popen cmd: %s\n", | |
76267147 | 106 | strerror_r(errno, serr, sizeof(serr))); |
4cea3a9c WN |
107 | return -EINVAL; |
108 | } | |
109 | ||
110 | while (!feof(file) && !ferror(file)) { | |
111 | /* | |
112 | * Make buf_sz always have obe byte extra space so we | |
113 | * can put '\0' there. | |
114 | */ | |
115 | if (buf_sz - read_sz < READ_SIZE + 1) { | |
116 | void *new_buf; | |
117 | ||
118 | buf_sz = read_sz + READ_SIZE + 1; | |
119 | new_buf = realloc(buf, buf_sz); | |
120 | ||
121 | if (!new_buf) { | |
122 | pr_err("ERROR: failed to realloc memory\n"); | |
123 | err = -ENOMEM; | |
124 | goto errout; | |
125 | } | |
126 | ||
127 | buf = new_buf; | |
128 | } | |
129 | read_sz += fread(buf + read_sz, 1, READ_SIZE, file); | |
130 | } | |
131 | ||
132 | if (buf_sz - read_sz < 1) { | |
133 | pr_err("ERROR: internal error\n"); | |
134 | err = -EINVAL; | |
135 | goto errout; | |
136 | } | |
137 | ||
138 | if (ferror(file)) { | |
139 | pr_err("ERROR: error occurred when reading from pipe: %s\n", | |
76267147 | 140 | strerror_r(errno, serr, sizeof(serr))); |
4cea3a9c WN |
141 | err = -EIO; |
142 | goto errout; | |
143 | } | |
144 | ||
145 | err = WEXITSTATUS(pclose(file)); | |
146 | file = NULL; | |
147 | if (err) { | |
148 | err = -EINVAL; | |
149 | goto errout; | |
150 | } | |
151 | ||
152 | /* | |
153 | * If buf is string, give it terminal '\0' to make our life | |
154 | * easier. If buf is not string, that '\0' is out of space | |
155 | * indicated by read_sz so caller won't even notice it. | |
156 | */ | |
157 | ((char *)buf)[read_sz] = '\0'; | |
158 | ||
159 | if (!p_buf) | |
160 | free(buf); | |
161 | else | |
162 | *p_buf = buf; | |
163 | ||
164 | if (p_read_sz) | |
165 | *p_read_sz = read_sz; | |
166 | return 0; | |
167 | ||
168 | errout: | |
169 | if (file) | |
170 | pclose(file); | |
171 | free(buf); | |
172 | if (p_buf) | |
173 | *p_buf = NULL; | |
174 | if (p_read_sz) | |
175 | *p_read_sz = 0; | |
176 | return err; | |
177 | } | |
178 | ||
179 | static inline void | |
180 | force_set_env(const char *var, const char *value) | |
181 | { | |
182 | if (value) { | |
183 | setenv(var, value, 1); | |
184 | pr_debug("set env: %s=%s\n", var, value); | |
185 | } else { | |
186 | unsetenv(var); | |
187 | pr_debug("unset env: %s\n", var); | |
188 | } | |
189 | } | |
190 | ||
191 | static void | |
192 | version_notice(void) | |
193 | { | |
194 | pr_err( | |
195 | " \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" | |
196 | " \tYou may want to try git trunk:\n" | |
197 | " \t\tgit clone http://llvm.org/git/llvm.git\n" | |
198 | " \t\t and\n" | |
199 | " \t\tgit clone http://llvm.org/git/clang.git\n\n" | |
200 | " \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" | |
201 | " \tdebian/ubuntu:\n" | |
202 | " \t\thttp://llvm.org/apt\n\n" | |
203 | " \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" | |
204 | " \toption in [llvm] section of ~/.perfconfig to:\n\n" | |
205 | " \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n" | |
206 | " \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" | |
207 | " \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" | |
208 | " \t(Replace /path/to/llc with path to your llc)\n\n" | |
209 | ); | |
210 | } | |
211 | ||
d325d788 WN |
212 | static int detect_kbuild_dir(char **kbuild_dir) |
213 | { | |
214 | const char *test_dir = llvm_param.kbuild_dir; | |
215 | const char *prefix_dir = ""; | |
216 | const char *suffix_dir = ""; | |
217 | ||
218 | char *autoconf_path; | |
d325d788 WN |
219 | |
220 | int err; | |
221 | ||
222 | if (!test_dir) { | |
07bc5c69 WN |
223 | /* _UTSNAME_LENGTH is 65 */ |
224 | char release[128]; | |
225 | ||
226 | err = fetch_kernel_version(NULL, release, | |
227 | sizeof(release)); | |
228 | if (err) | |
d325d788 | 229 | return -EINVAL; |
d325d788 | 230 | |
07bc5c69 | 231 | test_dir = release; |
d325d788 WN |
232 | prefix_dir = "/lib/modules/"; |
233 | suffix_dir = "/build"; | |
234 | } | |
235 | ||
236 | err = asprintf(&autoconf_path, "%s%s%s/include/generated/autoconf.h", | |
237 | prefix_dir, test_dir, suffix_dir); | |
238 | if (err < 0) | |
239 | return -ENOMEM; | |
240 | ||
241 | if (access(autoconf_path, R_OK) == 0) { | |
242 | free(autoconf_path); | |
243 | ||
244 | err = asprintf(kbuild_dir, "%s%s%s", prefix_dir, test_dir, | |
245 | suffix_dir); | |
246 | if (err < 0) | |
247 | return -ENOMEM; | |
248 | return 0; | |
249 | } | |
250 | free(autoconf_path); | |
251 | return -ENOENT; | |
252 | } | |
253 | ||
0c6d18bf WN |
254 | static const char *kinc_fetch_script = |
255 | "#!/usr/bin/env sh\n" | |
256 | "if ! test -d \"$KBUILD_DIR\"\n" | |
257 | "then\n" | |
258 | " exit -1\n" | |
259 | "fi\n" | |
260 | "if ! test -f \"$KBUILD_DIR/include/generated/autoconf.h\"\n" | |
261 | "then\n" | |
262 | " exit -1\n" | |
263 | "fi\n" | |
264 | "TMPDIR=`mktemp -d`\n" | |
265 | "if test -z \"$TMPDIR\"\n" | |
266 | "then\n" | |
267 | " exit -1\n" | |
268 | "fi\n" | |
269 | "cat << EOF > $TMPDIR/Makefile\n" | |
270 | "obj-y := dummy.o\n" | |
271 | "\\$(obj)/%.o: \\$(src)/%.c\n" | |
272 | "\t@echo -n \"\\$(NOSTDINC_FLAGS) \\$(LINUXINCLUDE) \\$(EXTRA_CFLAGS)\"\n" | |
273 | "EOF\n" | |
274 | "touch $TMPDIR/dummy.c\n" | |
275 | "make -s -C $KBUILD_DIR M=$TMPDIR $KBUILD_OPTS dummy.o 2>/dev/null\n" | |
276 | "RET=$?\n" | |
277 | "rm -rf $TMPDIR\n" | |
278 | "exit $RET\n"; | |
279 | ||
d325d788 | 280 | static inline void |
0c6d18bf | 281 | get_kbuild_opts(char **kbuild_dir, char **kbuild_include_opts) |
d325d788 WN |
282 | { |
283 | int err; | |
284 | ||
0c6d18bf | 285 | if (!kbuild_dir || !kbuild_include_opts) |
d325d788 WN |
286 | return; |
287 | ||
288 | *kbuild_dir = NULL; | |
0c6d18bf | 289 | *kbuild_include_opts = NULL; |
d325d788 WN |
290 | |
291 | if (llvm_param.kbuild_dir && !llvm_param.kbuild_dir[0]) { | |
292 | pr_debug("[llvm.kbuild-dir] is set to \"\" deliberately.\n"); | |
293 | pr_debug("Skip kbuild options detection.\n"); | |
294 | return; | |
295 | } | |
296 | ||
297 | err = detect_kbuild_dir(kbuild_dir); | |
298 | if (err) { | |
299 | pr_warning( | |
300 | "WARNING:\tunable to get correct kernel building directory.\n" | |
301 | "Hint:\tSet correct kbuild directory using 'kbuild-dir' option in [llvm]\n" | |
302 | " \tsection of ~/.perfconfig or set it to \"\" to suppress kbuild\n" | |
303 | " \tdetection.\n\n"); | |
304 | return; | |
305 | } | |
0c6d18bf WN |
306 | |
307 | pr_debug("Kernel build dir is set to %s\n", *kbuild_dir); | |
308 | force_set_env("KBUILD_DIR", *kbuild_dir); | |
309 | force_set_env("KBUILD_OPTS", llvm_param.kbuild_opts); | |
310 | err = read_from_pipe(kinc_fetch_script, | |
311 | (void **)kbuild_include_opts, | |
312 | NULL); | |
313 | if (err) { | |
314 | pr_warning( | |
315 | "WARNING:\tunable to get kernel include directories from '%s'\n" | |
316 | "Hint:\tTry set clang include options using 'clang-bpf-cmd-template'\n" | |
317 | " \toption in [llvm] section of ~/.perfconfig and set 'kbuild-dir'\n" | |
318 | " \toption in [llvm] to \"\" to suppress this detection.\n\n", | |
319 | *kbuild_dir); | |
320 | ||
321 | free(*kbuild_dir); | |
322 | *kbuild_dir = NULL; | |
323 | return; | |
324 | } | |
325 | ||
326 | pr_debug("include option is set to %s\n", *kbuild_include_opts); | |
d325d788 WN |
327 | } |
328 | ||
4cea3a9c WN |
329 | int llvm__compile_bpf(const char *path, void **p_obj_buf, |
330 | size_t *p_obj_buf_sz) | |
331 | { | |
07bc5c69 WN |
332 | size_t obj_buf_sz; |
333 | void *obj_buf = NULL; | |
59f41af9 | 334 | int err, nr_cpus_avail; |
07bc5c69 | 335 | unsigned int kernel_version; |
4a4f66a1 | 336 | char linux_version_code_str[64]; |
4cea3a9c | 337 | const char *clang_opt = llvm_param.clang_opt; |
78478269 ACM |
338 | char clang_path[PATH_MAX], abspath[PATH_MAX], nr_cpus_avail_str[64]; |
339 | char serr[STRERR_BUFSIZE]; | |
0c6d18bf | 340 | char *kbuild_dir = NULL, *kbuild_include_opts = NULL; |
07bc5c69 | 341 | const char *template = llvm_param.clang_bpf_cmd_template; |
4cea3a9c | 342 | |
78478269 ACM |
343 | if (path[0] != '-' && realpath(path, abspath) == NULL) { |
344 | err = errno; | |
345 | pr_err("ERROR: problems with path %s: %s\n", | |
346 | path, strerror_r(err, serr, sizeof(serr))); | |
347 | return -err; | |
348 | } | |
349 | ||
4cea3a9c WN |
350 | if (!template) |
351 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; | |
352 | ||
353 | err = search_program(llvm_param.clang_path, | |
354 | "clang", clang_path); | |
355 | if (err) { | |
356 | pr_err( | |
357 | "ERROR:\tunable to find clang.\n" | |
358 | "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" | |
359 | " \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n"); | |
360 | version_notice(); | |
361 | return -ENOENT; | |
362 | } | |
363 | ||
d325d788 WN |
364 | /* |
365 | * This is an optional work. Even it fail we can continue our | |
366 | * work. Needn't to check error return. | |
367 | */ | |
0c6d18bf | 368 | get_kbuild_opts(&kbuild_dir, &kbuild_include_opts); |
d325d788 | 369 | |
59f41af9 WN |
370 | nr_cpus_avail = sysconf(_SC_NPROCESSORS_CONF); |
371 | if (nr_cpus_avail <= 0) { | |
372 | pr_err( | |
373 | "WARNING:\tunable to get available CPUs in this system: %s\n" | |
76267147 | 374 | " \tUse 128 instead.\n", strerror_r(errno, serr, sizeof(serr))); |
59f41af9 WN |
375 | nr_cpus_avail = 128; |
376 | } | |
377 | snprintf(nr_cpus_avail_str, sizeof(nr_cpus_avail_str), "%d", | |
378 | nr_cpus_avail); | |
379 | ||
07bc5c69 WN |
380 | if (fetch_kernel_version(&kernel_version, NULL, 0)) |
381 | kernel_version = 0; | |
382 | ||
4a4f66a1 | 383 | snprintf(linux_version_code_str, sizeof(linux_version_code_str), |
07bc5c69 | 384 | "0x%x", kernel_version); |
4a4f66a1 | 385 | |
59f41af9 | 386 | force_set_env("NR_CPUS", nr_cpus_avail_str); |
4a4f66a1 | 387 | force_set_env("LINUX_VERSION_CODE", linux_version_code_str); |
4cea3a9c WN |
388 | force_set_env("CLANG_EXEC", clang_path); |
389 | force_set_env("CLANG_OPTIONS", clang_opt); | |
0c6d18bf | 390 | force_set_env("KERNEL_INC_OPTIONS", kbuild_include_opts); |
d325d788 | 391 | force_set_env("WORKING_DIR", kbuild_dir ? : "."); |
4cea3a9c WN |
392 | |
393 | /* | |
394 | * Since we may reset clang's working dir, path of source file | |
395 | * should be transferred into absolute path, except we want | |
396 | * stdin to be source file (testing). | |
397 | */ | |
398 | force_set_env("CLANG_SOURCE", | |
78478269 | 399 | (path[0] == '-') ? path : abspath); |
4cea3a9c WN |
400 | |
401 | pr_debug("llvm compiling command template: %s\n", template); | |
402 | err = read_from_pipe(template, &obj_buf, &obj_buf_sz); | |
403 | if (err) { | |
404 | pr_err("ERROR:\tunable to compile %s\n", path); | |
405 | pr_err("Hint:\tCheck error message shown above.\n"); | |
406 | pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); | |
407 | pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); | |
408 | pr_err(" \twith proper -I and -D options.\n"); | |
409 | goto errout; | |
410 | } | |
411 | ||
d325d788 | 412 | free(kbuild_dir); |
0c6d18bf | 413 | free(kbuild_include_opts); |
4cea3a9c WN |
414 | if (!p_obj_buf) |
415 | free(obj_buf); | |
416 | else | |
417 | *p_obj_buf = obj_buf; | |
418 | ||
419 | if (p_obj_buf_sz) | |
420 | *p_obj_buf_sz = obj_buf_sz; | |
421 | return 0; | |
422 | errout: | |
d325d788 | 423 | free(kbuild_dir); |
0c6d18bf | 424 | free(kbuild_include_opts); |
4cea3a9c WN |
425 | free(obj_buf); |
426 | if (p_obj_buf) | |
427 | *p_obj_buf = NULL; | |
428 | if (p_obj_buf_sz) | |
429 | *p_obj_buf_sz = 0; | |
430 | return err; | |
431 | } | |
9bc898c7 WN |
432 | |
433 | int llvm__search_clang(void) | |
434 | { | |
435 | char clang_path[PATH_MAX]; | |
436 | ||
437 | return search_program(llvm_param.clang_path, "clang", clang_path); | |
438 | } |