liblttng-ctl: use export list to define exported symbols
[lttng-tools.git] / src / bin / lttng / uprobe.c
CommitLineData
0c4e727b
JR
1/*
2 * Copyright (C) 2020 EfficiOS, Inc.
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7
8#include "uprobe.h"
9
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <unistd.h>
13
14#include "common/compat/getenv.h"
15#include "common/string-utils/string-utils.h"
16#include "common/utils.h"
17#include "lttng/constant.h"
18
19#include "command.h"
20
21/*
22 * Walk the directories in the PATH environment variable to find the target
23 * binary passed as parameter.
24 *
25 * On success, the full path of the binary is copied in binary_full_path out
26 * parameter. This buffer is allocated by the caller and must be at least
27 * LTTNG_PATH_MAX bytes long.
28 * On failure, returns -1;
29 */
30static
31int walk_command_search_path(const char *binary, char *binary_full_path)
32{
33 char *tentative_binary_path = NULL;
34 char *command_search_path = NULL;
35 char *curr_search_dir_end = NULL;
36 char *curr_search_dir = NULL;
37 struct stat stat_output;
38 int ret = 0;
39
40 command_search_path = lttng_secure_getenv("PATH");
41 if (!command_search_path) {
42 ret = -1;
43 goto end;
44 }
45
46 /*
47 * Duplicate the $PATH string as the char pointer returned by getenv() should
48 * not be modified.
49 */
50 command_search_path = strdup(command_search_path);
51 if (!command_search_path) {
52 ret = -1;
53 goto end;
54 }
55
56 /*
57 * This char array is used to concatenate path to binary to look for
58 * the binary.
59 */
60 tentative_binary_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
61 if (!tentative_binary_path) {
62 ret = -1;
63 goto alloc_error;
64 }
65
66 curr_search_dir = command_search_path;
67 do {
68 /*
69 * Split on ':'. The return value of this call points to the
70 * matching character.
71 */
72 curr_search_dir_end = strchr(curr_search_dir, ':');
73 if (curr_search_dir_end != NULL) {
74 /*
75 * Add a NULL byte to the end of the first token so it
76 * can be used as a string.
77 */
78 curr_search_dir_end[0] = '\0';
79 }
80
81 /* Empty the tentative path */
82 memset(tentative_binary_path, 0, LTTNG_PATH_MAX * sizeof(char));
83
84 /*
85 * Build the tentative path to the binary using the current
86 * search directory and the name of the binary.
87 */
88 ret = snprintf(tentative_binary_path, LTTNG_PATH_MAX, "%s/%s",
89 curr_search_dir, binary);
90 if (ret < 0) {
91 goto free_binary_path;
92 }
93 if (ret < LTTNG_PATH_MAX) {
94 /*
95 * Use STAT(2) to see if the file exists.
96 */
97 ret = stat(tentative_binary_path, &stat_output);
98 if (ret == 0) {
99 /*
100 * Verify that it is a regular file or a
101 * symlink and not a special file (e.g.
102 * device).
103 */
104 if (S_ISREG(stat_output.st_mode)
105 || S_ISLNK(stat_output.st_mode)) {
106 /*
107 * Found a match, set the out parameter
108 * and return success.
109 */
110 ret = lttng_strncpy(binary_full_path,
111 tentative_binary_path,
112 LTTNG_PATH_MAX);
113 if (ret == -1) {
114 ERR("Source path does not fit "
115 "in destination buffer.");
116 }
117 goto free_binary_path;
118 }
119 }
120 }
121 /* Go to the next entry in the $PATH variable. */
122 curr_search_dir = curr_search_dir_end + 1;
123 } while (curr_search_dir_end != NULL);
124
125free_binary_path:
126 free(tentative_binary_path);
127alloc_error:
128 free(command_search_path);
129end:
130 return ret;
131}
132
133/*
134 * Check if the symbol field passed by the user is in fact an address or an
135 * offset from a symbol. Those two instrumentation types are not supported yet.
136 * It's expected to be a common mistake because of the existing --probe option
137 * that does support these formats.
138 *
139 * Here are examples of these unsupported formats for the --userspace-probe
140 * option:
141 * elf:/path/to/binary:0x400430
142 * elf:/path/to/binary:4194364
143 * elf:/path/to/binary:my_symbol+0x323
144 * elf:/path/to/binary:my_symbol+43
145 */
146static
147int warn_userspace_probe_syntax(const char *symbol)
148{
149 int ret;
150
151 /* Check if the symbol field is an hex address. */
152 ret = sscanf(symbol, "0x%*x");
153 if (ret > 0) {
154 /* If there is a match, print a warning and return an error. */
155 ERR("Userspace probe on address not supported yet.");
156 ret = CMD_UNSUPPORTED;
157 goto error;
158 }
159
160 /* Check if the symbol field is an decimal address. */
161 ret = sscanf(symbol, "%*u");
162 if (ret > 0) {
163 /* If there is a match, print a warning and return an error. */
164 ERR("Userspace probe on address not supported yet.");
165 ret = CMD_UNSUPPORTED;
166 goto error;
167 }
168
169 /* Check if the symbol field is symbol+hex_offset. */
170 ret = sscanf(symbol, "%*[^+]+0x%*x");
171 if (ret > 0) {
172 /* If there is a match, print a warning and return an error. */
173 ERR("Userspace probe on symbol+offset not supported yet.");
174 ret = CMD_UNSUPPORTED;
175 goto error;
176 }
177
178 /* Check if the symbol field is symbol+decimal_offset. */
179 ret = sscanf(symbol, "%*[^+]+%*u");
180 if (ret > 0) {
181 /* If there is a match, print a warning and return an error. */
182 ERR("Userspace probe on symbol+offset not supported yet.");
183 ret = CMD_UNSUPPORTED;
184 goto error;
185 }
186
187 ret = 0;
188
189error:
190 return ret;
191}
192
193/*
194 * Parse userspace probe options
195 * Set the userspace probe fields in the lttng_event struct and set the
196 * target_path to the path to the binary.
197 */
0c4e727b
JR
198int parse_userspace_probe_opts(const char *opt,
199 struct lttng_userspace_probe_location **probe_location)
200{
201 int ret = CMD_SUCCESS;
e358ddd5 202 size_t num_token = 0;
0c4e727b
JR
203 char *target_path = NULL;
204 char *unescaped_target_path = NULL;
205 char *real_target_path = NULL;
206 char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
207 struct lttng_userspace_probe_location *probe_location_local = NULL;
208 struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
e358ddd5 209 struct lttng_dynamic_pointer_array tokens;
0c4e727b 210
a0377dfe 211 LTTNG_ASSERT(opt);
0c4e727b
JR
212
213 /*
214 * userspace probe fields are separated by ':'.
215 */
e358ddd5
JG
216 ret = strutils_split(opt, ':', true, &tokens);
217 if (ret == 0) {
218 num_token = lttng_dynamic_pointer_array_get_count(&tokens);
219 }
0c4e727b
JR
220
221 /*
222 * Early sanity check that the number of parameter is between 2 and 4
223 * inclusively.
224 * elf:PATH:SYMBOL
225 * std:PATH:PROVIDER_NAME:PROBE_NAME
226 * PATH:SYMBOL (same behavior as ELF)
227 */
e358ddd5 228 if (ret < 0 || num_token < 2 || num_token > 4) {
0c4e727b
JR
229 ret = CMD_ERROR;
230 goto end;
231 }
232
233 /*
234 * Looking up the first parameter will tell the technique to use to
235 * interpret the userspace probe/function description.
236 */
237 switch (num_token) {
238 case 2:
239 /* When the probe type is omitted we assume ELF for now. */
240 case 3:
e358ddd5
JG
241 if (num_token == 3 && strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "elf") == 0) {
242 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
243 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
0c4e727b 244 } else if (num_token == 2) {
e358ddd5
JG
245 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 0);
246 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
0c4e727b
JR
247 } else {
248 ret = CMD_ERROR;
249 goto end;
250 }
251 lookup_method =
252 lttng_userspace_probe_location_lookup_method_function_elf_create();
253 if (!lookup_method) {
254 WARN("Failed to create ELF lookup method");
255 ret = CMD_ERROR;
256 goto end;
257 }
258 break;
259 case 4:
e358ddd5
JG
260 if (strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "sdt") == 0) {
261 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
262 provider_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
263 probe_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 3);
0c4e727b
JR
264 } else {
265 ret = CMD_ERROR;
266 goto end;
267 }
268 lookup_method =
269 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
270 if (!lookup_method) {
271 WARN("Failed to create SDT lookup method");
272 ret = CMD_ERROR;
273 goto end;
274 }
275 break;
276 default:
277 ret = CMD_ERROR;
278 goto end;
279 }
280
281 /* strutils_unescape_string allocates a new char *. */
282 unescaped_target_path = strutils_unescape_string(target_path, 0);
283 if (!unescaped_target_path) {
284 ret = CMD_ERROR;
285 goto end;
286 }
287
288 /*
289 * If there is not forward slash in the path. Walk the $PATH else
290 * expand.
291 */
292 if (strchr(unescaped_target_path, '/') == NULL) {
293 /* Walk the $PATH variable to find the targeted binary. */
294 real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
295 if (!real_target_path) {
296 PERROR("Error allocating path buffer");
297 ret = CMD_ERROR;
298 goto end;
299 }
300 ret = walk_command_search_path(unescaped_target_path, real_target_path);
301 if (ret) {
302 ERR("Binary not found.");
303 ret = CMD_ERROR;
304 goto end;
305 }
306 } else {
307 /*
308 * Expand references to `/./` and `/../`. This function does not check
309 * if the file exists. This call returns an allocated buffer on
310 * success.
311 */
312 real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
313 if (!real_target_path) {
314 ERR("Error expanding the path to binary.");
315 ret = CMD_ERROR;
316 goto end;
317 }
318
319 /*
320 * Check if the file exists using access(2), If it does not,
321 * return an error.
322 */
323 ret = access(real_target_path, F_OK);
324 if (ret) {
325 ERR("Cannot find binary at path: %s.", real_target_path);
326 ret = CMD_ERROR;
327 goto end;
328 }
329 }
330
331 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
332 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
333 /*
334 * Check for common mistakes in userspace probe description syntax.
335 */
336 ret = warn_userspace_probe_syntax(symbol_name);
337 if (ret) {
338 goto end;
339 }
340
341 probe_location_local = lttng_userspace_probe_location_function_create(
342 real_target_path, symbol_name, lookup_method);
343 if (!probe_location_local) {
344 WARN("Failed to create function probe location");
345 ret = CMD_ERROR;
346 goto end;
347 }
348
349 /* Ownership transferred to probe_location. */
350 lookup_method = NULL;
351 break;
352 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
353 probe_location_local = lttng_userspace_probe_location_tracepoint_create(
354 real_target_path, provider_name, probe_name, lookup_method);
355 if (!probe_location_local) {
356 WARN("Failed to create function probe location");
357 ret = CMD_ERROR;
358 goto end;
359 }
360
361 /* Ownership transferred to probe_location. */
362 lookup_method = NULL;
363 break;
364 default:
365 ret = CMD_ERROR;
366 goto end;
367 }
368
369 /*
370 * Everything went fine, transfer ownership of probe location to
371 * caller.
372 */
373 *probe_location = probe_location_local;
374 probe_location_local = NULL;
375
376end:
377 lttng_userspace_probe_location_destroy(probe_location_local);
378 lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
e358ddd5 379 lttng_dynamic_pointer_array_reset(&tokens);
0c4e727b
JR
380 /*
381 * Freeing both char * here makes the error handling simplier. free()
382 * performs not action if the pointer is NULL.
383 */
384 free(real_target_path);
385 free(unescaped_target_path);
386
387 return ret;
388}
This page took 0.03838 seconds and 4 git commands to generate.