/* * Copyright (C) 2017 - Jérémie Galarneau * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License, version 2.1 only, * as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include enum lttng_userspace_probe_location_lookup_method_type lttng_userspace_probe_location_lookup_method_get_type( const struct lttng_userspace_probe_location_lookup_method *lookup_method) { return lookup_method ? lookup_method->type : LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_UNKNOWN; } void lttng_userspace_probe_location_lookup_method_destroy( struct lttng_userspace_probe_location_lookup_method *lookup_method) { if (!lookup_method){ return; } free(lookup_method); } struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_lookup_method_function_elf_create(void) { struct lttng_userspace_probe_location_lookup_method *ret = NULL; struct lttng_userspace_probe_location_lookup_method_elf *elf_method; elf_method = zmalloc(sizeof(*elf_method)); if (!elf_method) { PERROR("zmalloc"); goto end; } ret = &elf_method->parent; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; end: return ret; } struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(void) { struct lttng_userspace_probe_location_lookup_method *ret = NULL; struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; sdt_method = zmalloc(sizeof(*sdt_method)); if (!sdt_method) { PERROR("zmalloc"); goto end; } ret = &sdt_method->parent; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; end: return ret; } enum lttng_userspace_probe_location_type lttng_userspace_probe_location_get_type( const struct lttng_userspace_probe_location *location) { return location ? location->type : LTTNG_USERSPACE_PROBE_LOCATION_TYPE_UNKNOWN; } static void lttng_userspace_probe_location_function_destroy( struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location_function *location_function = NULL; assert(location); location_function = container_of(location, struct lttng_userspace_probe_location_function, parent); assert(location_function); free(location_function->function_name); free(location_function->binary_path); if (location_function->binary_fd >= 0) { if (close(location_function->binary_fd)) { PERROR("close"); } } free(location); } static void lttng_userspace_probe_location_tracepoint_destroy( struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location_tracepoint *location_tracepoint = NULL; assert(location); location_tracepoint = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); assert(location_tracepoint); free(location_tracepoint->probe_name); free(location_tracepoint->provider_name); free(location_tracepoint->binary_path); if (location_tracepoint->binary_fd >= 0) { if (close(location_tracepoint->binary_fd)) { PERROR("close"); } } free(location); } void lttng_userspace_probe_location_destroy( struct lttng_userspace_probe_location *location) { if (!location) { return; } lttng_userspace_probe_location_lookup_method_destroy( location->lookup_method); switch (location->type) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: lttng_userspace_probe_location_function_destroy(location); break; case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: lttng_userspace_probe_location_tracepoint_destroy(location); break; default: abort(); } } static struct lttng_userspace_probe_location * lttng_userspace_probe_location_function_create_no_check(const char *binary_path, const char *function_name, struct lttng_userspace_probe_location_lookup_method *lookup_method, bool open_binary) { int binary_fd = -1; char *function_name_copy = NULL, *binary_path_copy = NULL; struct lttng_userspace_probe_location *ret = NULL; struct lttng_userspace_probe_location_function *location; if (open_binary) { binary_fd = open(binary_path, O_RDONLY); if (binary_fd < 0) { PERROR("Error opening the binary"); goto error; } } else { binary_fd = -1; } function_name_copy = lttng_strndup(function_name, LTTNG_SYMBOL_NAME_LEN); if (!function_name_copy) { PERROR("Error duplicating the function name"); goto error; } binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); if (!binary_path_copy) { PERROR("Error duplicating the function name"); goto error; } location = zmalloc(sizeof(*location)); if (!location) { PERROR("Error allocating userspace probe location"); goto error; } location->function_name = function_name_copy; location->binary_path = binary_path_copy; location->binary_fd = binary_fd; location->instrumentation_type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY; ret = &location->parent; ret->lookup_method = lookup_method; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION; goto end; error: free(function_name_copy); free(binary_path_copy); if (binary_fd >= 0) { if (close(binary_fd)) { PERROR("Error closing binary fd in error path"); } } end: return ret; } static struct lttng_userspace_probe_location * lttng_userspace_probe_location_tracepoint_create_no_check(const char *binary_path, const char *provider_name, const char *probe_name, struct lttng_userspace_probe_location_lookup_method *lookup_method, bool open_binary) { int binary_fd = -1; char *probe_name_copy = NULL; char *provider_name_copy = NULL; char *binary_path_copy = NULL; struct lttng_userspace_probe_location *ret = NULL; struct lttng_userspace_probe_location_tracepoint *location; if (open_binary) { binary_fd = open(binary_path, O_RDONLY); if (binary_fd < 0) { PERROR("open"); goto error; } } else { binary_fd = -1; } probe_name_copy = lttng_strndup(probe_name, LTTNG_SYMBOL_NAME_LEN); if (!probe_name_copy) { PERROR("lttng_strndup"); goto error; } provider_name_copy = lttng_strndup(provider_name, LTTNG_SYMBOL_NAME_LEN); if (!provider_name_copy) { PERROR("lttng_strndup"); goto error; } binary_path_copy = lttng_strndup(binary_path, LTTNG_PATH_MAX); if (!binary_path_copy) { PERROR("lttng_strndup"); goto error; } location = zmalloc(sizeof(*location)); if (!location) { PERROR("zmalloc"); goto error; } location->probe_name = probe_name_copy; location->provider_name = provider_name_copy; location->binary_path = binary_path_copy; location->binary_fd = binary_fd; ret = &location->parent; ret->lookup_method = lookup_method; ret->type = LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT; goto end; error: free(probe_name_copy); free(provider_name_copy); free(binary_path_copy); if (binary_fd >= 0) { if (close(binary_fd)) { PERROR("Error closing binary fd in error path"); } } end: return ret; } struct lttng_userspace_probe_location * lttng_userspace_probe_location_function_create(const char *binary_path, const char *function_name, struct lttng_userspace_probe_location_lookup_method *lookup_method) { struct lttng_userspace_probe_location *ret = NULL; if (!binary_path || !function_name) { ERR("Invalid argument(s)"); goto end; } switch (lttng_userspace_probe_location_lookup_method_get_type( lookup_method)) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: break; default: /* Invalid probe location lookup method. */ goto end; } ret = lttng_userspace_probe_location_function_create_no_check( binary_path, function_name, lookup_method, true); end: return ret; } struct lttng_userspace_probe_location * lttng_userspace_probe_location_tracepoint_create(const char *binary_path, const char *provider_name, const char *probe_name, struct lttng_userspace_probe_location_lookup_method *lookup_method) { struct lttng_userspace_probe_location *ret = NULL; if (!binary_path || !probe_name || !provider_name) { ERR("Invalid argument(s)"); goto end; } switch (lttng_userspace_probe_location_lookup_method_get_type( lookup_method)) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: break; default: /* Invalid probe location lookup method. */ goto end; } ret = lttng_userspace_probe_location_tracepoint_create_no_check( binary_path, provider_name, probe_name, lookup_method, true); end: return ret; } static struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_lookup_method_function_elf_copy( const struct lttng_userspace_probe_location_lookup_method *lookup_method) { struct lttng_userspace_probe_location_lookup_method *parent = NULL; struct lttng_userspace_probe_location_lookup_method_elf *elf_method; assert(lookup_method); assert(lookup_method->type == LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF); elf_method = zmalloc(sizeof(*elf_method)); if (!elf_method) { PERROR("Error allocating ELF userspace probe lookup method"); goto error; } elf_method->parent.type = lookup_method->type; parent = &elf_method->parent; goto end; error: parent = NULL; end: return parent; } static struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( struct lttng_userspace_probe_location_lookup_method *lookup_method) { struct lttng_userspace_probe_location_lookup_method *parent = NULL; struct lttng_userspace_probe_location_lookup_method_sdt *sdt_method; assert(lookup_method); assert(lookup_method->type == LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT); sdt_method = zmalloc(sizeof(*sdt_method)); if (!sdt_method) { PERROR("zmalloc"); goto error; } sdt_method->parent.type = lookup_method->type; parent = &sdt_method->parent; goto end; error: parent = NULL; end: return parent; } static struct lttng_userspace_probe_location * lttng_userspace_probe_location_function_copy( const struct lttng_userspace_probe_location *location) { enum lttng_userspace_probe_location_lookup_method_type lookup_type; struct lttng_userspace_probe_location *new_location = NULL; struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; const char *binary_path = NULL; const char *function_name = NULL; int fd, new_fd; assert(location); assert(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); /* Get probe location fields */ binary_path = lttng_userspace_probe_location_function_get_binary_path(location); if (!binary_path) { ERR("Userspace probe binary path is NULL"); goto error; } function_name = lttng_userspace_probe_location_function_get_function_name(location); if (!function_name) { ERR("Userspace probe function name is NULL"); goto error; } /* Duplicate the binary fd */ fd = lttng_userspace_probe_location_function_get_binary_fd(location); if (fd == -1) { ERR("Error getting file descriptor to binary"); goto error; } new_fd = dup(fd); if (new_fd == -1) { PERROR("Error duplicating file descriptor to binary"); goto error; } /* * Duplicate probe location method fields */ lookup_type = lttng_userspace_probe_location_lookup_method_get_type( location->lookup_method); switch (lookup_type) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_copy( location->lookup_method); if (!lookup_method) { goto close_fd; } break; default: /* Invalid probe location lookup method. */ goto close_fd; } /* Create the probe_location */ new_location = lttng_userspace_probe_location_function_create_no_check( binary_path, function_name, lookup_method, false); if (!new_location) { goto destroy_lookup_method; } /* Set the duplicated fd to the new probe_location */ if (lttng_userspace_probe_location_function_set_binary_fd(new_location, new_fd) < 0) { goto destroy_probe_location; } goto end; destroy_probe_location: lttng_userspace_probe_location_destroy(new_location); destroy_lookup_method: lttng_userspace_probe_location_lookup_method_destroy(lookup_method); close_fd: if (close(new_fd) < 0) { PERROR("Error closing duplicated file descriptor in error path"); } error: new_location = NULL; end: return new_location; } static struct lttng_userspace_probe_location * lttng_userspace_probe_location_tracepoint_copy( const struct lttng_userspace_probe_location *location) { enum lttng_userspace_probe_location_lookup_method_type lookup_type; struct lttng_userspace_probe_location *new_location = NULL; struct lttng_userspace_probe_location_lookup_method *lookup_method = NULL; const char *binary_path = NULL; const char *probe_name = NULL; const char *provider_name = NULL; int fd, new_fd; assert(location); assert(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); /* Get probe location fields */ binary_path = lttng_userspace_probe_location_tracepoint_get_binary_path(location); if (!binary_path) { ERR("Userspace probe binary path is NULL"); goto error; } probe_name = lttng_userspace_probe_location_tracepoint_get_probe_name(location); if (!probe_name) { ERR("Userspace probe probe name is NULL"); goto error; } provider_name = lttng_userspace_probe_location_tracepoint_get_provider_name(location); if (!provider_name) { ERR("Userspace probe provider name is NULL"); goto error; } /* Duplicate the binary fd */ fd = lttng_userspace_probe_location_tracepoint_get_binary_fd(location); if (fd == -1) { ERR("Error getting file descriptor to binary"); goto error; } new_fd = dup(fd); if (new_fd == -1) { PERROR("Error duplicating file descriptor to binary"); goto error; } /* * Duplicate probe location method fields */ lookup_type = lttng_userspace_probe_location_lookup_method_get_type( location->lookup_method); switch (lookup_type) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: lookup_method = lttng_userspace_probe_location_lookup_method_tracepoint_sdt_copy( location->lookup_method); if (!lookup_method) { goto close_fd; } break; default: /* Invalid probe location lookup method. */ goto close_fd; } /* Create the probe_location */ new_location = lttng_userspace_probe_location_tracepoint_create_no_check( binary_path, provider_name, probe_name, lookup_method, false); if (!new_location) { goto destroy_lookup_method; } /* Set the duplicated fd to the new probe_location */ if (lttng_userspace_probe_location_tracepoint_set_binary_fd(new_location, new_fd) < 0) { goto destroy_probe_location; } goto end; destroy_probe_location: lttng_userspace_probe_location_destroy(new_location); destroy_lookup_method: lttng_userspace_probe_location_lookup_method_destroy(lookup_method); close_fd: if (close(new_fd) < 0) { PERROR("Error closing duplicated file descriptor in error path"); } error: new_location = NULL; end: return new_location; } const char *lttng_userspace_probe_location_function_get_binary_path( const struct lttng_userspace_probe_location *location) { const char *ret = NULL; struct lttng_userspace_probe_location_function *function_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { ERR("Invalid argument(s)"); goto end; } function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); ret = function_location->binary_path; end: return ret; } const char *lttng_userspace_probe_location_tracepoint_get_binary_path( const struct lttng_userspace_probe_location *location) { const char *ret = NULL; struct lttng_userspace_probe_location_tracepoint *tracepoint_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { ERR("Invalid argument(s)"); goto end; } tracepoint_location = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); ret = tracepoint_location->binary_path; end: return ret; } const char *lttng_userspace_probe_location_function_get_function_name( const struct lttng_userspace_probe_location *location) { const char *ret = NULL; struct lttng_userspace_probe_location_function *function_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { ERR("Invalid argument(s)"); goto end; } function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); ret = function_location->function_name; end: return ret; } const char *lttng_userspace_probe_location_tracepoint_get_probe_name( const struct lttng_userspace_probe_location *location) { const char *ret = NULL; struct lttng_userspace_probe_location_tracepoint *tracepoint_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { ERR("Invalid argument(s)"); goto end; } tracepoint_location = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); ret = tracepoint_location->probe_name; end: return ret; } const char *lttng_userspace_probe_location_tracepoint_get_provider_name( const struct lttng_userspace_probe_location *location) { const char *ret = NULL; struct lttng_userspace_probe_location_tracepoint *tracepoint_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { ERR("Invalid argument(s)"); goto end; } tracepoint_location = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); ret = tracepoint_location->provider_name; end: return ret; } int lttng_userspace_probe_location_function_get_binary_fd( const struct lttng_userspace_probe_location *location) { int ret = -1; struct lttng_userspace_probe_location_function *function_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { ERR("Invalid argument(s)"); goto end; } function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); ret = function_location->binary_fd; end: return ret; } enum lttng_userspace_probe_location_function_instrumentation_type lttng_userspace_probe_location_function_get_instrumentation_type( const struct lttng_userspace_probe_location *location) { enum lttng_userspace_probe_location_function_instrumentation_type type; struct lttng_userspace_probe_location_function *function_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { ERR("Invalid argument(s)"); type = LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_UNKNOWN; goto end; } function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); type = function_location->instrumentation_type; end: return type; } enum lttng_userspace_probe_location_status lttng_userspace_probe_location_function_set_instrumentation_type( const struct lttng_userspace_probe_location *location, enum lttng_userspace_probe_location_function_instrumentation_type instrumentation_type) { enum lttng_userspace_probe_location_status status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_OK; struct lttng_userspace_probe_location_function *function_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION || instrumentation_type != LTTNG_USERSPACE_PROBE_LOCATION_FUNCTION_INSTRUMENTATION_TYPE_ENTRY) { ERR("Invalid argument(s)"); status = LTTNG_USERSPACE_PROBE_LOCATION_STATUS_INVALID; goto end; } function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); function_location->instrumentation_type = instrumentation_type; end: return status; } int lttng_userspace_probe_location_tracepoint_get_binary_fd( const struct lttng_userspace_probe_location *location) { int ret = -1; struct lttng_userspace_probe_location_tracepoint *tracepoint_location; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { ERR("Invalid argument(s)"); goto end; } tracepoint_location = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); ret = tracepoint_location->binary_fd; end: return ret; } static struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_function_get_lookup_method( const struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location_lookup_method *ret = NULL; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION) { ERR("Invalid argument(s)"); goto end; } ret = location->lookup_method; end: return ret; } static struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_tracepoint_get_lookup_method( const struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location_lookup_method *ret = NULL; if (!location || lttng_userspace_probe_location_get_type(location) != LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT) { ERR("Invalid argument(s)"); goto end; } ret = location->lookup_method; end: return ret; } const struct lttng_userspace_probe_location_lookup_method * lttng_userspace_probe_location_get_lookup_method( const struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location_lookup_method *ret = NULL; assert(location); switch (location->type) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: ret = lttng_userspace_probe_location_function_get_lookup_method( location); break; case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: ret = lttng_userspace_probe_location_tracepoint_get_lookup_method( location); break; default: ERR("Unknowned lookup method."); break; } return ret; } static int lttng_userspace_probe_location_lookup_method_serialize( struct lttng_userspace_probe_location_lookup_method *method, struct lttng_dynamic_buffer *buffer) { int ret; struct lttng_userspace_probe_location_lookup_method_comm lookup_method_comm; lookup_method_comm.type = (int8_t) (method ? method->type : LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT); if (buffer) { ret = lttng_dynamic_buffer_append(buffer, &lookup_method_comm, sizeof(lookup_method_comm)); if (ret) { goto end; } } ret = sizeof(lookup_method_comm); end: return ret; } static int lttng_userspace_probe_location_function_serialize( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer, int *binary_fd) { int ret; size_t function_name_len, binary_path_len; struct lttng_userspace_probe_location_function *location_function; struct lttng_userspace_probe_location_function_comm location_function_comm; assert(location); assert(lttng_userspace_probe_location_get_type(location) == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); location_function = container_of(location, struct lttng_userspace_probe_location_function, parent); if (!location_function->function_name || !location_function->binary_path) { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_fd && location_function->binary_fd < 0) { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_fd) { *binary_fd = location_function->binary_fd; } function_name_len = strlen(location_function->function_name); if (function_name_len == 0) { ret = -LTTNG_ERR_INVALID; goto end; } binary_path_len = strlen(location_function->binary_path); if (binary_path_len == 0) { ret = -LTTNG_ERR_INVALID; goto end; } location_function_comm.function_name_len = function_name_len + 1; location_function_comm.binary_path_len = binary_path_len + 1; if (buffer) { ret = lttng_dynamic_buffer_append(buffer, &location_function_comm, sizeof(location_function_comm)); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } ret = lttng_dynamic_buffer_append(buffer, location_function->function_name, location_function_comm.function_name_len); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } ret = lttng_dynamic_buffer_append(buffer, location_function->binary_path, location_function_comm.binary_path_len); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } } ret = sizeof(location_function_comm) + location_function_comm.function_name_len + location_function_comm.binary_path_len; end: return ret; } static int lttng_userspace_probe_location_tracepoint_serialize( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer, int *binary_fd) { int ret; size_t probe_name_len, provider_name_len, binary_path_len; struct lttng_userspace_probe_location_tracepoint *location_tracepoint; struct lttng_userspace_probe_location_tracepoint_comm location_tracepoint_comm; assert(location); assert(lttng_userspace_probe_location_get_type(location) == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); location_tracepoint = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); if (!location_tracepoint->probe_name || !location_tracepoint->provider_name || !location_tracepoint->binary_path) { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_fd && location_tracepoint->binary_fd < 0) { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_fd) { *binary_fd = location_tracepoint->binary_fd; } probe_name_len = strlen(location_tracepoint->probe_name); if (probe_name_len == 0) { ret = -LTTNG_ERR_INVALID; goto end; } provider_name_len = strlen(location_tracepoint->provider_name); if (provider_name_len == 0) { ret = -LTTNG_ERR_INVALID; goto end; } binary_path_len = strlen(location_tracepoint->binary_path); if (binary_path_len == 0) { ret = -LTTNG_ERR_INVALID; goto end; } location_tracepoint_comm.probe_name_len = probe_name_len + 1; location_tracepoint_comm.provider_name_len = provider_name_len + 1; location_tracepoint_comm.binary_path_len = binary_path_len + 1; if (buffer) { ret = lttng_dynamic_buffer_append(buffer, &location_tracepoint_comm, sizeof(location_tracepoint_comm)); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } ret = lttng_dynamic_buffer_append(buffer, location_tracepoint->probe_name, location_tracepoint_comm.probe_name_len); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } ret = lttng_dynamic_buffer_append(buffer, location_tracepoint->provider_name, location_tracepoint_comm.provider_name_len); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } ret = lttng_dynamic_buffer_append(buffer, location_tracepoint->binary_path, location_tracepoint_comm.binary_path_len); if (ret) { ret = -LTTNG_ERR_INVALID; goto end; } } ret = sizeof(location_tracepoint_comm) + location_tracepoint_comm.probe_name_len + location_tracepoint_comm.provider_name_len + location_tracepoint_comm.binary_path_len; end: return ret; } LTTNG_HIDDEN int lttng_userspace_probe_location_serialize( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer, int *binary_fd) { int ret, buffer_use = 0; struct lttng_userspace_probe_location_comm location_generic_comm; if (!location) { ERR("Invalid argument(s)"); ret = -LTTNG_ERR_INVALID; goto end; } memset(&location_generic_comm, 0, sizeof(location_generic_comm)); location_generic_comm.type = (int8_t) location->type; if (buffer) { ret = lttng_dynamic_buffer_append(buffer, &location_generic_comm, sizeof(location_generic_comm)); if (ret) { goto end; } } buffer_use += sizeof(location_generic_comm); switch (lttng_userspace_probe_location_get_type(location)) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: ret = lttng_userspace_probe_location_function_serialize( location, buffer, binary_fd); break; case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: ret = lttng_userspace_probe_location_tracepoint_serialize( location, buffer, binary_fd); break; default: ERR("Unsupported probe location type"); ret = -LTTNG_ERR_INVALID; goto end; } if (ret < 0) { goto end; } buffer_use += ret; ret = lttng_userspace_probe_location_lookup_method_serialize( location->lookup_method, buffer); if (ret < 0) { goto end; } ret += buffer_use; end: return ret; } static int lttng_userspace_probe_location_function_create_from_buffer( const struct lttng_buffer_view *buffer, struct lttng_userspace_probe_location **location) { struct lttng_userspace_probe_location_function_comm *location_function_comm; const char *function_name_src, *binary_path_src; char *function_name = NULL, *binary_path = NULL; int ret = 0; assert(buffer); assert(buffer->data); assert(location); location_function_comm = (struct lttng_userspace_probe_location_function_comm *) buffer->data; const size_t expected_size = sizeof(*location_function_comm) + location_function_comm->function_name_len + location_function_comm->binary_path_len; if (buffer->size < expected_size) { ret = -LTTNG_ERR_INVALID; goto end; } function_name_src = buffer->data + sizeof(*location_function_comm); binary_path_src = function_name_src + location_function_comm->function_name_len; if (function_name_src[location_function_comm->function_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_path_src[location_function_comm->binary_path_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } function_name = lttng_strndup(function_name_src, LTTNG_SYMBOL_NAME_LEN); if (!function_name) { PERROR("lttng_strndup"); ret = -LTTNG_ERR_NOMEM; goto end; } binary_path = lttng_strndup(binary_path_src, LTTNG_PATH_MAX); if (!binary_path) { PERROR("lttng_strndup"); ret = -LTTNG_ERR_NOMEM; goto end; } *location = lttng_userspace_probe_location_function_create_no_check( binary_path, function_name, NULL, false); if (!(*location)) { ret = -LTTNG_ERR_INVALID; goto end; } ret = (int) expected_size; end: free(function_name); free(binary_path); return ret; } static int lttng_userspace_probe_location_tracepoint_create_from_buffer( const struct lttng_buffer_view *buffer, struct lttng_userspace_probe_location **location) { struct lttng_userspace_probe_location_tracepoint_comm *location_tracepoint_comm; const char *probe_name_src, *provider_name_src, *binary_path_src; char *probe_name = NULL, *provider_name = NULL, *binary_path = NULL; int ret = 0; assert(buffer); assert(buffer->data); assert(location); location_tracepoint_comm = (struct lttng_userspace_probe_location_tracepoint_comm *) buffer->data; const size_t expected_size = sizeof(*location_tracepoint_comm) + location_tracepoint_comm->probe_name_len + location_tracepoint_comm->provider_name_len + location_tracepoint_comm->binary_path_len; if (buffer->size < expected_size) { ret = -LTTNG_ERR_INVALID; goto end; } probe_name_src = buffer->data + sizeof(*location_tracepoint_comm); provider_name_src = probe_name_src + location_tracepoint_comm->probe_name_len; binary_path_src = provider_name_src + location_tracepoint_comm->provider_name_len; if (probe_name_src[location_tracepoint_comm->probe_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } if (provider_name_src[location_tracepoint_comm->provider_name_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } if (binary_path_src[location_tracepoint_comm->binary_path_len - 1] != '\0') { ret = -LTTNG_ERR_INVALID; goto end; } probe_name = lttng_strndup(probe_name_src, LTTNG_SYMBOL_NAME_LEN); if (!probe_name) { PERROR("lttng_strndup"); goto end; } provider_name = lttng_strndup(provider_name_src, LTTNG_SYMBOL_NAME_LEN); if (!provider_name) { PERROR("lttng_strndup"); goto end; } binary_path = lttng_strndup(binary_path_src, LTTNG_SYMBOL_NAME_LEN); if (!binary_path) { PERROR("lttng_strndup"); goto end; } *location = lttng_userspace_probe_location_tracepoint_create_no_check( binary_path, provider_name, probe_name, NULL, false); if (!(*location)) { ret = -LTTNG_ERR_INVALID; goto end; } ret = (int) expected_size; end: free(probe_name); free(provider_name); free(binary_path); return ret; } static int lttng_userspace_probe_location_lookup_method_create_from_buffer( struct lttng_buffer_view *buffer, struct lttng_userspace_probe_location_lookup_method **lookup_method) { int ret; struct lttng_userspace_probe_location_lookup_method_comm *lookup_comm; enum lttng_userspace_probe_location_lookup_method_type type; assert(buffer); assert(buffer->data); assert(lookup_method); if (buffer->size < sizeof(*lookup_comm)) { ret = -LTTNG_ERR_INVALID; goto end; } lookup_comm = (struct lttng_userspace_probe_location_lookup_method_comm *) buffer->data; type = (enum lttng_userspace_probe_location_lookup_method_type) lookup_comm->type; switch (type) { case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_DEFAULT: *lookup_method = NULL; break; case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF: *lookup_method = lttng_userspace_probe_location_lookup_method_function_elf_create(); if (!(*lookup_method)) { ret = -LTTNG_ERR_INVALID; goto end; } break; case LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT: *lookup_method = lttng_userspace_probe_location_lookup_method_tracepoint_sdt_create(); if (!(*lookup_method)) { ret = -LTTNG_ERR_INVALID; goto end; } break; default: ret = -LTTNG_ERR_INVALID; goto end; } ret = sizeof(*lookup_comm); end: return ret; } LTTNG_HIDDEN int lttng_userspace_probe_location_create_from_buffer( const struct lttng_buffer_view *buffer, struct lttng_userspace_probe_location **location) { struct lttng_userspace_probe_location_lookup_method *lookup_method; struct lttng_userspace_probe_location_comm *probe_location_comm; enum lttng_userspace_probe_location_type type; struct lttng_buffer_view lookup_method_view; int consumed = 0; int ret; assert(buffer); assert(buffer->data); assert(location); lookup_method = NULL; if (buffer->size <= sizeof(*probe_location_comm)) { ret = -LTTNG_ERR_INVALID; goto end; } probe_location_comm = (struct lttng_userspace_probe_location_comm *) buffer->data; type = (enum lttng_userspace_probe_location_type) probe_location_comm->type; consumed += sizeof(*probe_location_comm); switch (type) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: { struct lttng_buffer_view view = lttng_buffer_view_from_view( buffer, consumed, buffer->size - consumed); ret = lttng_userspace_probe_location_function_create_from_buffer( &view, location); if (ret < 0) { goto end; } break; } case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: { struct lttng_buffer_view view = lttng_buffer_view_from_view( buffer, consumed, buffer->size - consumed); ret = lttng_userspace_probe_location_tracepoint_create_from_buffer( &view, location); if (ret < 0) { goto end; } break; } default: ret = -LTTNG_ERR_INVALID; goto end; } consumed += ret; if (buffer->size <= consumed) { ret = -LTTNG_ERR_INVALID; goto end; } lookup_method_view = lttng_buffer_view_from_view(buffer, consumed, buffer->size - consumed); ret = lttng_userspace_probe_location_lookup_method_create_from_buffer( &lookup_method_view, &lookup_method); if (ret < 0) { ret = -LTTNG_ERR_INVALID; goto end; } assert(lookup_method); (*location)->lookup_method = lookup_method; lookup_method = NULL; ret += consumed; end: return ret; } LTTNG_HIDDEN int lttng_userspace_probe_location_function_set_binary_fd( struct lttng_userspace_probe_location *location, int binary_fd) { int ret = 0; struct lttng_userspace_probe_location_function *function_location; assert(location); assert(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION); function_location = container_of(location, struct lttng_userspace_probe_location_function, parent); if (function_location->binary_fd >= 0) { ret = close(function_location->binary_fd); if (ret) { PERROR("close"); ret = -LTTNG_ERR_INVALID; goto end; } } function_location->binary_fd = binary_fd; end: return ret; } LTTNG_HIDDEN int lttng_userspace_probe_location_tracepoint_set_binary_fd( struct lttng_userspace_probe_location *location, int binary_fd) { int ret = 0; struct lttng_userspace_probe_location_tracepoint *tracepoint_location; assert(location); assert(location->type == LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT); tracepoint_location = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); if (tracepoint_location->binary_fd >= 0) { ret = close(tracepoint_location->binary_fd); if (ret) { PERROR("close"); ret = -LTTNG_ERR_INVALID; goto end; } } tracepoint_location->binary_fd = binary_fd; end: return ret; } static int lttng_userspace_probe_location_function_flatten( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer) { struct lttng_userspace_probe_location_lookup_method_elf flat_lookup_method; struct lttng_userspace_probe_location_function *probe_function; struct lttng_userspace_probe_location_function flat_probe; size_t function_name_len, binary_path_len; size_t padding_needed = 0; char *flat_probe_start; int storage_needed = 0; int ret; assert(location); if (location->lookup_method && location->lookup_method->type != LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF) { ret = -LTTNG_ERR_INVALID; goto end; } probe_function = container_of(location, struct lttng_userspace_probe_location_function, parent); assert(probe_function->function_name); assert(probe_function->binary_path); storage_needed += sizeof(struct lttng_userspace_probe_location_function); function_name_len = strlen(probe_function->function_name) + 1; binary_path_len = strlen(probe_function->binary_path) + 1; storage_needed += function_name_len + binary_path_len; /* * The lookup method is aligned to 64-bit within the buffer. * This is needed even if there is no lookup method since * the next structure in the buffer probably needs to be * aligned too (depending on the arch). */ padding_needed = ALIGN_TO(storage_needed, sizeof(uint64_t)) - storage_needed; storage_needed += padding_needed; if (location->lookup_method) { /* NOTE: elf look-up method is assumed here. */ storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf); } if (!buffer) { ret = storage_needed; goto end; } if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { ret = lttng_dynamic_buffer_set_capacity(buffer, buffer->size + storage_needed); if (ret) { goto end; } } memset(&flat_probe, 0, sizeof(flat_probe)); flat_probe_start = buffer->data + buffer->size; flat_probe.parent.type = location->type; /* * The lookup method, if present, is the last element in the flat * representation of the probe. */ if (location->lookup_method) { flat_probe.parent.lookup_method = (struct lttng_userspace_probe_location_lookup_method *) (flat_probe_start + sizeof(flat_probe) + function_name_len + binary_path_len + padding_needed); } else { flat_probe.parent.lookup_method = NULL; } flat_probe.function_name = flat_probe_start + sizeof(flat_probe); flat_probe.binary_path = flat_probe.function_name + function_name_len; flat_probe.binary_fd = -1; ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe)); if (ret) { goto end; } ret = lttng_dynamic_buffer_append(buffer, probe_function->function_name, function_name_len); if (ret) { goto end; } ret = lttng_dynamic_buffer_append(buffer, probe_function->binary_path, binary_path_len); if (ret) { goto end; } /* Insert padding before the lookup method. */ ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed); if (ret) { goto end; } if (!location->lookup_method) { /* Not an error, the default method is used. */ ret = storage_needed; goto end; } memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); flat_lookup_method.parent.type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_FUNCTION_ELF; ret = lttng_dynamic_buffer_append(buffer, &flat_lookup_method, sizeof(flat_lookup_method)); if (ret) { goto end; } ret = storage_needed; end: return ret; } static int lttng_userspace_probe_location_tracepoint_flatten( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer) { struct lttng_userspace_probe_location_lookup_method_sdt flat_lookup_method; struct lttng_userspace_probe_location_tracepoint *probe_tracepoint; struct lttng_userspace_probe_location_tracepoint flat_probe; size_t probe_name_len, provider_name_len, binary_path_len; size_t padding_needed = 0; int storage_needed = 0; char *flat_probe_start; int ret = 0; assert(location); /* Only SDT tracepoints are supported at the moment */ if (location->lookup_method && location->lookup_method->type != LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT) { ret = -LTTNG_ERR_INVALID; goto end; } probe_tracepoint = container_of(location, struct lttng_userspace_probe_location_tracepoint, parent); assert(probe_tracepoint->probe_name); assert(probe_tracepoint->provider_name); assert(probe_tracepoint->binary_path); /* Compute the storage space needed to flatten the probe location */ storage_needed += sizeof(struct lttng_userspace_probe_location_tracepoint); probe_name_len = strlen(probe_tracepoint->probe_name) + 1; provider_name_len = strlen(probe_tracepoint->provider_name) + 1; binary_path_len = strlen(probe_tracepoint->binary_path) + 1; storage_needed += probe_name_len + provider_name_len + binary_path_len; /* * The lookup method is aligned to 64-bit within the buffer. * This is needed even if there is no lookup method since * the next structure in the buffer probably needs to be * aligned too (depending on the arch). */ padding_needed = ALIGN_TO(storage_needed, sizeof(uint64_t)) - storage_needed; storage_needed += padding_needed; if (location->lookup_method) { /* NOTE: elf look-up method is assumed here. */ storage_needed += sizeof(struct lttng_userspace_probe_location_lookup_method_elf); } /* * If the caller set buffer to NULL, return the size of the needed buffer. */ if (!buffer) { ret = storage_needed; goto end; } if (lttng_dynamic_buffer_get_capacity_left(buffer) < storage_needed) { ret = lttng_dynamic_buffer_set_capacity(buffer, buffer->size + storage_needed); if (ret) { goto end; } } memset(&flat_probe, 0, sizeof(flat_probe)); flat_probe_start = buffer->data + buffer->size; flat_probe.parent.type = location->type; /* * The lookup method, if present, is the last element in the flat * representation of the probe. */ if (location->lookup_method) { flat_probe.parent.lookup_method = (struct lttng_userspace_probe_location_lookup_method *) (flat_probe_start + sizeof(flat_probe) + probe_name_len + provider_name_len + binary_path_len + padding_needed); } else { flat_probe.parent.lookup_method = NULL; } flat_probe.probe_name = flat_probe_start + sizeof(flat_probe); flat_probe.provider_name = flat_probe.probe_name + probe_name_len; flat_probe.binary_path = flat_probe.provider_name + provider_name_len; flat_probe.binary_fd = -1; ret = lttng_dynamic_buffer_append(buffer, &flat_probe, sizeof(flat_probe)); if (ret) { goto end; } /* Append all the fields to the buffer */ ret = lttng_dynamic_buffer_append(buffer, probe_tracepoint->probe_name, probe_name_len); if (ret) { goto end; } ret = lttng_dynamic_buffer_append(buffer, probe_tracepoint->provider_name, provider_name_len); if (ret) { goto end; } ret = lttng_dynamic_buffer_append(buffer, probe_tracepoint->binary_path, binary_path_len); if (ret) { goto end; } /* Insert padding before the lookup method. */ ret = lttng_dynamic_buffer_set_size(buffer, buffer->size + padding_needed); if (ret) { goto end; } if (!location->lookup_method) { /* Not an error, the default method is used. */ ret = storage_needed; goto end; } memset(&flat_lookup_method, 0, sizeof(flat_lookup_method)); flat_lookup_method.parent.type = LTTNG_USERSPACE_PROBE_LOCATION_LOOKUP_METHOD_TYPE_TRACEPOINT_SDT; ret = lttng_dynamic_buffer_append(buffer, &flat_lookup_method, sizeof(flat_lookup_method)); if (ret) { goto end; } ret = storage_needed; end: return ret; } LTTNG_HIDDEN int lttng_userspace_probe_location_flatten( const struct lttng_userspace_probe_location *location, struct lttng_dynamic_buffer *buffer) { int ret; if (!location) { ret = -LTTNG_ERR_INVALID; goto end; } /* Only types currently supported. */ switch (location->type) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: ret = lttng_userspace_probe_location_function_flatten(location, buffer); break; case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: ret = lttng_userspace_probe_location_tracepoint_flatten(location, buffer); break; default: ret = -LTTNG_ERR_INVALID; goto end; } end: return ret; } LTTNG_HIDDEN struct lttng_userspace_probe_location *lttng_userspace_probe_location_copy( const struct lttng_userspace_probe_location *location) { struct lttng_userspace_probe_location *new_location = NULL; enum lttng_userspace_probe_location_type type; if (!location) { goto err; } type = lttng_userspace_probe_location_get_type(location); switch (type) { case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_FUNCTION: new_location = lttng_userspace_probe_location_function_copy(location); if (!new_location) { goto err; } break; case LTTNG_USERSPACE_PROBE_LOCATION_TYPE_TRACEPOINT: new_location = lttng_userspace_probe_location_tracepoint_copy(location); if (!new_location) { goto err; } break; default: new_location = NULL; goto err; } err: return new_location; }