aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util/llvm-utils.c
diff options
context:
space:
mode:
authorWang Nan <wangnan0@huawei.com>2015-06-11 06:31:09 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2015-08-07 09:16:59 -0400
commit4cea3a9cb30a962fa759fcb081fb83351113d9c4 (patch)
tree6eaae7b42e635694a375afd719c32f9a2f0f5a04 /tools/perf/util/llvm-utils.c
parentaa61fd05ca79666c973d5b15e0f91ecdc7dcfa21 (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.c226
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
47static int
48search_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
90static int
91read_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
163errout:
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
174static inline void
175force_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
186static void
187version_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
207int 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;
264errout:
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}