2 * Copyright (C) 2020 EfficiOS, Inc.
4 * SPDX-License-Identifier: GPL-2.0-only
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"
17 #include <sys/types.h>
21 * Walk the directories in the PATH environment variable to find the target
22 * binary passed as parameter.
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;
29 static int walk_command_search_path(const char *binary
, char *binary_full_path
)
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
;
38 command_search_path
= lttng_secure_getenv("PATH");
39 if (!command_search_path
) {
45 * Duplicate the $PATH string as the char pointer returned by getenv() should
48 command_search_path
= strdup(command_search_path
);
49 if (!command_search_path
) {
55 * This char array is used to concatenate path to binary to look for
58 tentative_binary_path
= calloc
<char>(LTTNG_PATH_MAX
);
59 if (!tentative_binary_path
) {
64 curr_search_dir
= command_search_path
;
67 * Split on ':'. The return value of this call points to the
70 curr_search_dir_end
= strchr(curr_search_dir
, ':');
71 if (curr_search_dir_end
!= nullptr) {
73 * Add a NULL byte to the end of the first token so it
74 * can be used as a string.
76 curr_search_dir_end
[0] = '\0';
79 /* Empty the tentative path */
80 memset(tentative_binary_path
, 0, LTTNG_PATH_MAX
* sizeof(char));
83 * Build the tentative path to the binary using the current
84 * search directory and the name of the binary.
87 tentative_binary_path
, LTTNG_PATH_MAX
, "%s/%s", curr_search_dir
, binary
);
89 goto free_binary_path
;
91 if (ret
< LTTNG_PATH_MAX
) {
93 * Use STAT(2) to see if the file exists.
95 ret
= stat(tentative_binary_path
, &stat_output
);
98 * Verify that it is a regular file or a
99 * symlink and not a special file (e.g.
102 if (S_ISREG(stat_output
.st_mode
) || S_ISLNK(stat_output
.st_mode
)) {
104 * Found a match, set the out parameter
105 * and return success.
107 ret
= lttng_strncpy(binary_full_path
,
108 tentative_binary_path
,
111 ERR("Source path does not fit "
112 "in destination buffer.");
114 goto free_binary_path
;
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);
123 free(tentative_binary_path
);
125 free(command_search_path
);
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.
136 * Here are examples of these unsupported formats for the --userspace-probe
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
143 static int warn_userspace_probe_syntax(const char *symbol
)
147 /* Check if the symbol field is an hex address. */
148 ret
= sscanf(symbol
, "0x%*x");
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
;
156 /* Check if the symbol field is an decimal address. */
157 ret
= sscanf(symbol
, "%*u");
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
;
165 /* Check if the symbol field is symbol+hex_offset. */
166 ret
= sscanf(symbol
, "%*[^+]+0x%*x");
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
;
174 /* Check if the symbol field is symbol+decimal_offset. */
175 ret
= sscanf(symbol
, "%*[^+]+%*u");
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
;
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.
194 int parse_userspace_probe_opts(const char *opt
,
195 struct lttng_userspace_probe_location
**probe_location
)
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
;
210 * userspace probe fields are separated by ':'.
212 ret
= strutils_split(opt
, ':', true, &tokens
);
214 num_token
= lttng_dynamic_pointer_array_get_count(&tokens
);
218 * Early sanity check that the number of parameter is between 2 and 4
221 * std:PATH:PROVIDER_NAME:PROBE_NAME
222 * PATH:SYMBOL (same behavior as ELF)
224 if (ret
< 0 || num_token
< 2 || num_token
> 4) {
230 * Looking up the first parameter will tell the technique to use to
231 * interpret the userspace probe/function description.
235 /* When the probe type is omitted we assume ELF for now. */
237 if (num_token
== 3 &&
238 strcmp((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 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);
249 lookup_method
= lttng_userspace_probe_location_lookup_method_function_elf_create();
250 if (!lookup_method
) {
251 WARN("Failed to create ELF lookup method");
257 if (strcmp((const char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 0),
259 target_path
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 1);
261 (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 2);
262 probe_name
= (char *) lttng_dynamic_pointer_array_get_pointer(&tokens
, 3);
268 lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create();
269 if (!lookup_method
) {
270 WARN("Failed to create SDT lookup method");
280 /* strutils_unescape_string allocates a new char *. */
281 unescaped_target_path
= strutils_unescape_string(target_path
, 0);
282 if (!unescaped_target_path
) {
288 * If there is not forward slash in the path. Walk the $PATH else
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");
299 ret
= walk_command_search_path(unescaped_target_path
, real_target_path
);
301 ERR("Binary not found.");
307 * Expand references to `/./` and `/../`. This function does not check
308 * if the file exists. This call returns an allocated buffer on
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.");
319 * Check if the file exists using access(2), If it does not,
322 ret
= access(real_target_path
, F_OK
);
324 ERR("Cannot find binary at path: %s.", real_target_path
);
330 switch (lttng_userspace_probe_location_lookup_method_get_type(lookup_method
)) {
331 case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF
:
333 * Check for common mistakes in userspace probe description syntax.
335 ret
= warn_userspace_probe_syntax(symbol_name
);
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");
348 /* Ownership transferred to probe_location. */
349 lookup_method
= nullptr;
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");
360 /* Ownership transferred to probe_location. */
361 lookup_method
= nullptr;
369 * Everything went fine, transfer ownership of probe location to
372 *probe_location
= probe_location_local
;
373 probe_location_local
= nullptr;
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
);
380 * Freeing both char * here makes the error handling simplier. free()
381 * performs not action if the pointer is NULL.
383 free(real_target_path
);
384 free(unescaped_target_path
);