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