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