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