Rename "tracing session" -> "recording session"
[lttng-tools.git] / src / bin / lttng / uprobe.c
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 */
30 static
31 int 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
125 free_binary_path:
126 free(tentative_binary_path);
127 alloc_error:
128 free(command_search_path);
129 end:
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 */
146 static
147 int 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
189 error:
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 */
198 LTTNG_HIDDEN
199 int parse_userspace_probe_opts(const char *opt,
200 struct lttng_userspace_probe_location **probe_location)
201 {
202 int ret = CMD_SUCCESS;
203 size_t num_token = 0;
204 char *target_path = NULL;
205 char *unescaped_target_path = NULL;
206 char *real_target_path = NULL;
207 char *symbol_name = NULL, *probe_name = NULL, *provider_name = NULL;
208 struct lttng_userspace_probe_location *probe_location_local = NULL;
209 struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL;
210 struct lttng_dynamic_pointer_array tokens;
211
212 assert(opt);
213
214 /*
215 * userspace probe fields are separated by ':'.
216 */
217 ret = strutils_split(opt, ':', true, &tokens);
218 if (ret == 0) {
219 num_token = lttng_dynamic_pointer_array_get_count(&tokens);
220 }
221
222 /*
223 * Early sanity check that the number of parameter is between 2 and 4
224 * inclusively.
225 * elf:PATH:SYMBOL
226 * std:PATH:PROVIDER_NAME:PROBE_NAME
227 * PATH:SYMBOL (same behavior as ELF)
228 */
229 if (ret < 0 || num_token < 2 || num_token > 4) {
230 ret = CMD_ERROR;
231 goto end;
232 }
233
234 /*
235 * Looking up the first parameter will tell the technique to use to
236 * interpret the userspace probe/function description.
237 */
238 switch (num_token) {
239 case 2:
240 /* When the probe type is omitted we assume ELF for now. */
241 case 3:
242 if (num_token == 3 && strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "elf") == 0) {
243 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
244 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
245 } else if (num_token == 2) {
246 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 0);
247 symbol_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
248 } else {
249 ret = CMD_ERROR;
250 goto end;
251 }
252 lookup_method =
253 lttng_userspace_probe_location_lookup_method_function_elf_create();
254 if (!lookup_method) {
255 WARN("Failed to create ELF lookup method");
256 ret = CMD_ERROR;
257 goto end;
258 }
259 break;
260 case 4:
261 if (strcmp(lttng_dynamic_pointer_array_get_pointer(&tokens, 0), "sdt") == 0) {
262 target_path = lttng_dynamic_pointer_array_get_pointer(&tokens, 1);
263 provider_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 2);
264 probe_name = lttng_dynamic_pointer_array_get_pointer(&tokens, 3);
265 } else {
266 ret = CMD_ERROR;
267 goto end;
268 }
269 lookup_method =
270 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
271 if (!lookup_method) {
272 WARN("Failed to create SDT lookup method");
273 ret = CMD_ERROR;
274 goto end;
275 }
276 break;
277 default:
278 ret = CMD_ERROR;
279 goto end;
280 }
281
282 /* strutils_unescape_string allocates a new char *. */
283 unescaped_target_path = strutils_unescape_string(target_path, 0);
284 if (!unescaped_target_path) {
285 ret = CMD_ERROR;
286 goto end;
287 }
288
289 /*
290 * If there is not forward slash in the path. Walk the $PATH else
291 * expand.
292 */
293 if (strchr(unescaped_target_path, '/') == NULL) {
294 /* Walk the $PATH variable to find the targeted binary. */
295 real_target_path = zmalloc(LTTNG_PATH_MAX * sizeof(char));
296 if (!real_target_path) {
297 PERROR("Error allocating path buffer");
298 ret = CMD_ERROR;
299 goto end;
300 }
301 ret = walk_command_search_path(unescaped_target_path, real_target_path);
302 if (ret) {
303 ERR("Binary not found.");
304 ret = CMD_ERROR;
305 goto end;
306 }
307 } else {
308 /*
309 * Expand references to `/./` and `/../`. This function does not check
310 * if the file exists. This call returns an allocated buffer on
311 * success.
312 */
313 real_target_path = utils_expand_path_keep_symlink(unescaped_target_path);
314 if (!real_target_path) {
315 ERR("Error expanding the path to binary.");
316 ret = CMD_ERROR;
317 goto end;
318 }
319
320 /*
321 * Check if the file exists using access(2), If it does not,
322 * return an error.
323 */
324 ret = access(real_target_path, F_OK);
325 if (ret) {
326 ERR("Cannot find binary at path: %s.", real_target_path);
327 ret = CMD_ERROR;
328 goto end;
329 }
330 }
331
332 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method)) {
333 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF:
334 /*
335 * Check for common mistakes in userspace probe description syntax.
336 */
337 ret = warn_userspace_probe_syntax(symbol_name);
338 if (ret) {
339 goto end;
340 }
341
342 probe_location_local = lttng_userspace_probe_location_function_create(
343 real_target_path, symbol_name, lookup_method);
344 if (!probe_location_local) {
345 WARN("Failed to create function probe location");
346 ret = CMD_ERROR;
347 goto end;
348 }
349
350 /* Ownership transferred to probe_location. */
351 lookup_method = NULL;
352 break;
353 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT:
354 probe_location_local = lttng_userspace_probe_location_tracepoint_create(
355 real_target_path, provider_name, probe_name, lookup_method);
356 if (!probe_location_local) {
357 WARN("Failed to create function probe location");
358 ret = CMD_ERROR;
359 goto end;
360 }
361
362 /* Ownership transferred to probe_location. */
363 lookup_method = NULL;
364 break;
365 default:
366 ret = CMD_ERROR;
367 goto end;
368 }
369
370 /*
371 * Everything went fine, transfer ownership of probe location to
372 * caller.
373 */
374 *probe_location = probe_location_local;
375 probe_location_local = NULL;
376
377 end:
378 lttng_userspace_probe_location_destroy(probe_location_local);
379 lttng_userspace_probe_location_lookup_method_destroy(lookup_method);
380 lttng_dynamic_pointer_array_reset(&tokens);
381 /*
382 * Freeing both char * here makes the error handling simplier. free()
383 * performs not action if the pointer is NULL.
384 */
385 free(real_target_path);
386 free(unescaped_target_path);
387
388 return ret;
389 }
This page took 0.046784 seconds and 4 git commands to generate.