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