diff options
author | Wang Nan <wangnan0@huawei.com> | 2015-06-11 06:31:09 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2015-08-07 09:16:59 -0400 |
commit | 4cea3a9cb30a962fa759fcb081fb83351113d9c4 (patch) | |
tree | 6eaae7b42e635694a375afd719c32f9a2f0f5a04 /tools/perf/util/llvm-utils.c | |
parent | aa61fd05ca79666c973d5b15e0f91ecdc7dcfa21 (diff) |
perf tools: Call clang to compile C source to object code
This is the core patch for supporting eBPF on-the-fly compiling, does
the following work:
1. Search clang compiler using search_program().
2. Run command template defined in llvm-bpf-cmd-template option in
[llvm] config section using read_from_pipe(). Patch of clang and
source code path is injected into shell command using environment
variable using force_set_env().
Commiter notice:
When building with DEBUG=1 we get a compiler error that gets fixed with
the same approach described in commit b236512280fb:
perf kmem: Fix compiler warning about may be accessing uninitialized variable
The last argument to strtok_r doesn't need to be initialized, its
just a placeholder to make this routine reentrant, but gcc doesn't know
about that and complains, breaking the build, fix it by setting it to
NULL.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/n/1436445342-1402-14-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/llvm-utils.c')
-rw-r--r-- | tools/perf/util/llvm-utils.c | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/tools/perf/util/llvm-utils.c b/tools/perf/util/llvm-utils.c index 472e8cd69361..5ae11249baad 100644 --- a/tools/perf/util/llvm-utils.c +++ b/tools/perf/util/llvm-utils.c | |||
@@ -43,3 +43,229 @@ int perf_llvm_config(const char *var, const char *value) | |||
43 | return -1; | 43 | return -1; |
44 | return 0; | 44 | return 0; |
45 | } | 45 | } |
46 | |||
47 | static int | ||
48 | search_program(const char *def, const char *name, | ||
49 | char *output) | ||
50 | { | ||
51 | char *env, *path, *tmp = NULL; | ||
52 | char buf[PATH_MAX]; | ||
53 | int ret; | ||
54 | |||
55 | output[0] = '\0'; | ||
56 | if (def && def[0] != '\0') { | ||
57 | if (def[0] == '/') { | ||
58 | if (access(def, F_OK) == 0) { | ||
59 | strlcpy(output, def, PATH_MAX); | ||
60 | return 0; | ||
61 | } | ||
62 | } else if (def[0] != '\0') | ||
63 | name = def; | ||
64 | } | ||
65 | |||
66 | env = getenv("PATH"); | ||
67 | if (!env) | ||
68 | return -1; | ||
69 | env = strdup(env); | ||
70 | if (!env) | ||
71 | return -1; | ||
72 | |||
73 | ret = -ENOENT; | ||
74 | path = strtok_r(env, ":", &tmp); | ||
75 | while (path) { | ||
76 | scnprintf(buf, sizeof(buf), "%s/%s", path, name); | ||
77 | if (access(buf, F_OK) == 0) { | ||
78 | strlcpy(output, buf, PATH_MAX); | ||
79 | ret = 0; | ||
80 | break; | ||
81 | } | ||
82 | path = strtok_r(NULL, ":", &tmp); | ||
83 | } | ||
84 | |||
85 | free(env); | ||
86 | return ret; | ||
87 | } | ||
88 | |||
89 | #define READ_SIZE 4096 | ||
90 | static int | ||
91 | read_from_pipe(const char *cmd, void **p_buf, size_t *p_read_sz) | ||
92 | { | ||
93 | int err = 0; | ||
94 | void *buf = NULL; | ||
95 | FILE *file = NULL; | ||
96 | size_t read_sz = 0, buf_sz = 0; | ||
97 | |||
98 | file = popen(cmd, "r"); | ||
99 | if (!file) { | ||
100 | pr_err("ERROR: unable to popen cmd: %s\n", | ||
101 | strerror(errno)); | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | |||
105 | while (!feof(file) && !ferror(file)) { | ||
106 | /* | ||
107 | * Make buf_sz always have obe byte extra space so we | ||
108 | * can put '\0' there. | ||
109 | */ | ||
110 | if (buf_sz - read_sz < READ_SIZE + 1) { | ||
111 | void *new_buf; | ||
112 | |||
113 | buf_sz = read_sz + READ_SIZE + 1; | ||
114 | new_buf = realloc(buf, buf_sz); | ||
115 | |||
116 | if (!new_buf) { | ||
117 | pr_err("ERROR: failed to realloc memory\n"); | ||
118 | err = -ENOMEM; | ||
119 | goto errout; | ||
120 | } | ||
121 | |||
122 | buf = new_buf; | ||
123 | } | ||
124 | read_sz += fread(buf + read_sz, 1, READ_SIZE, file); | ||
125 | } | ||
126 | |||
127 | if (buf_sz - read_sz < 1) { | ||
128 | pr_err("ERROR: internal error\n"); | ||
129 | err = -EINVAL; | ||
130 | goto errout; | ||
131 | } | ||
132 | |||
133 | if (ferror(file)) { | ||
134 | pr_err("ERROR: error occurred when reading from pipe: %s\n", | ||
135 | strerror(errno)); | ||
136 | err = -EIO; | ||
137 | goto errout; | ||
138 | } | ||
139 | |||
140 | err = WEXITSTATUS(pclose(file)); | ||
141 | file = NULL; | ||
142 | if (err) { | ||
143 | err = -EINVAL; | ||
144 | goto errout; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * If buf is string, give it terminal '\0' to make our life | ||
149 | * easier. If buf is not string, that '\0' is out of space | ||
150 | * indicated by read_sz so caller won't even notice it. | ||
151 | */ | ||
152 | ((char *)buf)[read_sz] = '\0'; | ||
153 | |||
154 | if (!p_buf) | ||
155 | free(buf); | ||
156 | else | ||
157 | *p_buf = buf; | ||
158 | |||
159 | if (p_read_sz) | ||
160 | *p_read_sz = read_sz; | ||
161 | return 0; | ||
162 | |||
163 | errout: | ||
164 | if (file) | ||
165 | pclose(file); | ||
166 | free(buf); | ||
167 | if (p_buf) | ||
168 | *p_buf = NULL; | ||
169 | if (p_read_sz) | ||
170 | *p_read_sz = 0; | ||
171 | return err; | ||
172 | } | ||
173 | |||
174 | static inline void | ||
175 | force_set_env(const char *var, const char *value) | ||
176 | { | ||
177 | if (value) { | ||
178 | setenv(var, value, 1); | ||
179 | pr_debug("set env: %s=%s\n", var, value); | ||
180 | } else { | ||
181 | unsetenv(var); | ||
182 | pr_debug("unset env: %s\n", var); | ||
183 | } | ||
184 | } | ||
185 | |||
186 | static void | ||
187 | version_notice(void) | ||
188 | { | ||
189 | pr_err( | ||
190 | " \tLLVM 3.7 or newer is required. Which can be found from http://llvm.org\n" | ||
191 | " \tYou may want to try git trunk:\n" | ||
192 | " \t\tgit clone http://llvm.org/git/llvm.git\n" | ||
193 | " \t\t and\n" | ||
194 | " \t\tgit clone http://llvm.org/git/clang.git\n\n" | ||
195 | " \tOr fetch the latest clang/llvm 3.7 from pre-built llvm packages for\n" | ||
196 | " \tdebian/ubuntu:\n" | ||
197 | " \t\thttp://llvm.org/apt\n\n" | ||
198 | " \tIf you are using old version of clang, change 'clang-bpf-cmd-template'\n" | ||
199 | " \toption in [llvm] section of ~/.perfconfig to:\n\n" | ||
200 | " \t \"$CLANG_EXEC $CLANG_OPTIONS $KERNEL_INC_OPTIONS \\\n" | ||
201 | " \t -working-directory $WORKING_DIR -c $CLANG_SOURCE \\\n" | ||
202 | " \t -emit-llvm -o - | /path/to/llc -march=bpf -filetype=obj -o -\"\n" | ||
203 | " \t(Replace /path/to/llc with path to your llc)\n\n" | ||
204 | ); | ||
205 | } | ||
206 | |||
207 | int llvm__compile_bpf(const char *path, void **p_obj_buf, | ||
208 | size_t *p_obj_buf_sz) | ||
209 | { | ||
210 | int err; | ||
211 | char clang_path[PATH_MAX]; | ||
212 | const char *clang_opt = llvm_param.clang_opt; | ||
213 | const char *template = llvm_param.clang_bpf_cmd_template; | ||
214 | void *obj_buf = NULL; | ||
215 | size_t obj_buf_sz; | ||
216 | |||
217 | if (!template) | ||
218 | template = CLANG_BPF_CMD_DEFAULT_TEMPLATE; | ||
219 | |||
220 | err = search_program(llvm_param.clang_path, | ||
221 | "clang", clang_path); | ||
222 | if (err) { | ||
223 | pr_err( | ||
224 | "ERROR:\tunable to find clang.\n" | ||
225 | "Hint:\tTry to install latest clang/llvm to support BPF. Check your $PATH\n" | ||
226 | " \tand 'clang-path' option in [llvm] section of ~/.perfconfig.\n"); | ||
227 | version_notice(); | ||
228 | return -ENOENT; | ||
229 | } | ||
230 | |||
231 | force_set_env("CLANG_EXEC", clang_path); | ||
232 | force_set_env("CLANG_OPTIONS", clang_opt); | ||
233 | force_set_env("KERNEL_INC_OPTIONS", NULL); | ||
234 | force_set_env("WORKING_DIR", "."); | ||
235 | |||
236 | /* | ||
237 | * Since we may reset clang's working dir, path of source file | ||
238 | * should be transferred into absolute path, except we want | ||
239 | * stdin to be source file (testing). | ||
240 | */ | ||
241 | force_set_env("CLANG_SOURCE", | ||
242 | (path[0] == '-') ? path : | ||
243 | make_nonrelative_path(path)); | ||
244 | |||
245 | pr_debug("llvm compiling command template: %s\n", template); | ||
246 | err = read_from_pipe(template, &obj_buf, &obj_buf_sz); | ||
247 | if (err) { | ||
248 | pr_err("ERROR:\tunable to compile %s\n", path); | ||
249 | pr_err("Hint:\tCheck error message shown above.\n"); | ||
250 | pr_err("Hint:\tYou can also pre-compile it into .o using:\n"); | ||
251 | pr_err(" \t\tclang -target bpf -O2 -c %s\n", path); | ||
252 | pr_err(" \twith proper -I and -D options.\n"); | ||
253 | goto errout; | ||
254 | } | ||
255 | |||
256 | if (!p_obj_buf) | ||
257 | free(obj_buf); | ||
258 | else | ||
259 | *p_obj_buf = obj_buf; | ||
260 | |||
261 | if (p_obj_buf_sz) | ||
262 | *p_obj_buf_sz = obj_buf_sz; | ||
263 | return 0; | ||
264 | errout: | ||
265 | free(obj_buf); | ||
266 | if (p_obj_buf) | ||
267 | *p_obj_buf = NULL; | ||
268 | if (p_obj_buf_sz) | ||
269 | *p_obj_buf_sz = 0; | ||
270 | return err; | ||
271 | } | ||