From: Jonathan Rajotte Date: Wed, 12 Jan 2022 23:18:08 +0000 (-0500) Subject: Fix: liblttng-ctl comm: lttng_event is not packed X-Git-Tag: v2.13.5~46 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=fe5e9e652b42a7fc4a9aecd0c392370b3c9722d6;p=lttng-tools.git Fix: liblttng-ctl comm: lttng_event is not packed Observed issue ============== In `lttcomm_session_msg` the lttng_event struct is marked as LTTNG_PACKED. This statement have no effect as explained in commit [2]. Solution ======== Adopt a similar pattern to the new API with a "serialize" & "create_from_buffer" approach. Most of the complexity is moved to `src/common/event.c` Known drawbacks ========= None. Note ==== Jérémie Galarneau: This patch was extensively modified from the original patch applying against stable-2.12 to accomodate for the use of the lttng_payload utils throughout the liblttng-ctl <-> lttng-sessiond communication code. Some changes were also made to build as C++. Reference ======== [1] https://review.lttng.org/gitweb?p=lttng-tools.git;a=commit;h=7bd95aee4660c6419a4a65429fc27754481e7e90 Signed-off-by: Jonathan Rajotte Signed-off-by: Jérémie Galarneau Change-Id: I35d848519dacb2b119324e88f262aa95951e4ac6 --- diff --git a/include/lttng/event-internal.h b/include/lttng/event-internal.h index b3df4c9c2..db6573535 100644 --- a/include/lttng/event-internal.h +++ b/include/lttng/event-internal.h @@ -14,8 +14,68 @@ #include #include +#include +struct lttng_event_exclusion; struct lttng_userspace_probe_location; +struct lttng_dynamic_buffer; +struct lttng_buffer_view; + +struct lttng_event_comm { + int8_t event_type; + int8_t loglevel_type; + int32_t loglevel; + int8_t enabled; + int32_t pid; + uint32_t flags; + + /* Payload. */ + /* Includes terminator `\0`. */ + uint32_t name_len; + uint32_t exclusion_count; + /* Includes terminator `\0`. */ + uint32_t filter_expression_len; + uint32_t bytecode_len; + + /* Type specific payload. */ + uint32_t userspace_probe_location_len; + uint32_t lttng_event_probe_attr_len; + uint32_t lttng_event_function_attr_len; + + /* + * Contain: + * - name [name_len], + * - exclusions if any + * - char filter_expression[filter_expression_len], + * - unsigned char filter_bytecode[bytecode_len], + * - userspace probe location [userspace_probe_location_len], + * - probe or ftrace based on event type. + */ + + char payload[]; +} LTTNG_PACKED; + +struct lttng_event_exclusion_comm { + /* Includes terminator `\0`. */ + uint32_t len; + char payload []; +} LTTNG_PACKED; + +struct lttng_event_probe_attr_comm { + uint64_t addr; + uint64_t offset; + /* Includes terminator `\0`. */ + uint32_t symbol_name_len; + + char payload[]; +} LTTNG_PACKED; + +struct lttng_event_function_attr_comm { + /* Includes terminator `\0`. */ + uint32_t symbol_name_len; + + char payload[]; +} LTTNG_PACKED; struct lttng_event_extended { /* @@ -36,4 +96,23 @@ struct lttng_event_extended { LTTNG_HIDDEN struct lttng_event *lttng_event_copy(const struct lttng_event *event); +ssize_t lttng_event_create_from_payload(struct lttng_payload_view *view, + struct lttng_event **out_event, + struct lttng_event_exclusion **out_exclusion, + char **out_filter_expression, + struct lttng_bytecode **out_bytecode); + +int lttng_event_serialize(const struct lttng_event *event, + unsigned int exclusion_count, + char **exclusion_list, + char *filter_expression, + size_t bytecode_len, + struct lttng_bytecode *bytecode, + struct lttng_payload *payload); + +enum lttng_error_code lttng_events_create_and_flatten_from_payload( + struct lttng_payload_view *view, + unsigned int count, + struct lttng_event **events); + #endif /* LTTNG_EVENT_INTERNAL_H */ diff --git a/src/bin/lttng-sessiond/client.c b/src/bin/lttng-sessiond/client.c index bdc5680ee..82ace4902 100644 --- a/src/bin/lttng-sessiond/client.c +++ b/src/bin/lttng-sessiond/client.c @@ -610,103 +610,6 @@ static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) return i; } -static int receive_userspace_probe(struct command_ctx *cmd_ctx, int sock, - int *sock_error, struct lttng_event *event) -{ - int fd = -1, ret; - struct lttng_userspace_probe_location *probe_location; - struct lttng_payload probe_location_payload; - struct fd_handle *handle = NULL; - - /* - * Create a payload to store the serialized version of the probe - * location. - */ - lttng_payload_init(&probe_location_payload); - - ret = lttng_dynamic_buffer_set_size(&probe_location_payload.buffer, - cmd_ctx->lsm.u.enable.userspace_probe_location_len); - if (ret) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - - /* - * Receive the probe location. - */ - ret = lttcomm_recv_unix_sock(sock, probe_location_payload.buffer.data, - probe_location_payload.buffer.size); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - - /* - * Receive the file descriptor to the target binary from the client. - */ - DBG("Receiving userspace probe target FD from client ..."); - ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); - if (ret <= 0) { - DBG("Nothing recv() from client userspace probe fd... continuing"); - *sock_error = 1; - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - - handle = fd_handle_create(fd); - if (!handle) { - ret = LTTNG_ERR_NOMEM; - goto error; - } - - /* Transferred to the handle. */ - fd = -1; - - ret = lttng_payload_push_fd_handle(&probe_location_payload, handle); - if (ret) { - ERR("Failed to add userspace probe file descriptor to payload"); - ret = LTTNG_ERR_NOMEM; - goto error; - } - - fd_handle_put(handle); - handle = NULL; - - { - struct lttng_payload_view view = lttng_payload_view_from_payload( - &probe_location_payload, 0, -1); - - /* Extract the probe location from the serialized version. */ - ret = lttng_userspace_probe_location_create_from_payload( - &view, &probe_location); - } - if (ret < 0) { - WARN("Failed to create a userspace probe location from the received buffer"); - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - - /* Attach the probe location to the event. */ - ret = lttng_event_set_userspace_probe_location(event, probe_location); - if (ret) { - ret = LTTNG_ERR_PROBE_LOCATION_INVAL; - goto error; - } - -error: - if (fd >= 0) { - if (close(fd)) { - PERROR("Failed to close userspace probe location binary fd"); - } - } - - fd_handle_put(handle); - lttng_payload_reset(&probe_location_payload); - return ret; -} - static enum lttng_error_code receive_lttng_trigger(struct command_ctx *cmd_ctx, int sock, int *sock_error, @@ -851,6 +754,86 @@ end: return ret_code; } +static enum lttng_error_code receive_lttng_event(struct command_ctx *cmd_ctx, + int sock, + int *sock_error, + struct lttng_event **out_event, + char **out_filter_expression, + struct lttng_bytecode **out_bytecode, + struct lttng_event_exclusion **out_exclusion) +{ + int ret; + size_t event_len; + ssize_t sock_recv_len; + enum lttng_error_code ret_code; + struct lttng_payload event_payload; + + lttng_payload_init(&event_payload); + if (cmd_ctx->lsm.cmd_type == LTTNG_ENABLE_EVENT) { + event_len = (size_t) cmd_ctx->lsm.u.enable.length; + } else if (cmd_ctx->lsm.cmd_type == LTTNG_DISABLE_EVENT) { + event_len = (size_t) cmd_ctx->lsm.u.disable.length; + } else { + abort(); + } + + ret = lttng_dynamic_buffer_set_size(&event_payload.buffer, event_len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + sock_recv_len = lttcomm_recv_unix_sock( + sock, event_payload.buffer.data, event_len); + if (sock_recv_len < 0 || sock_recv_len != event_len) { + ERR("Failed to receive event in command payload"); + *sock_error = 1; + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + /* Receive fds, if any. */ + if (cmd_ctx->lsm.fd_count > 0) { + sock_recv_len = lttcomm_recv_payload_fds_unix_sock( + sock, cmd_ctx->lsm.fd_count, &event_payload); + if (sock_recv_len > 0 && + sock_recv_len != cmd_ctx->lsm.fd_count * sizeof(int)) { + ERR("Failed to receive all file descriptors for event in command payload: expected fd count = %u, ret = %d", + cmd_ctx->lsm.fd_count, (int) ret); + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + *sock_error = 1; + goto end; + } else if (sock_recv_len <= 0) { + ERR("Failed to receive file descriptors for event in command payload: expected fd count = %u, ret = %d", + cmd_ctx->lsm.fd_count, (int) ret); + ret_code = LTTNG_ERR_FATAL; + *sock_error = 1; + goto end; + } + } + + /* Deserialize event. */ + { + struct lttng_payload_view event_view = + lttng_payload_view_from_payload( + &event_payload, 0, -1); + + if (lttng_event_create_from_payload(&event_view, out_event, + out_exclusion, out_filter_expression, + out_bytecode) != event_len) { + ERR("Invalid event received as part of command payload"); + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + } + + ret_code = LTTNG_OK; + +end: + lttng_payload_reset(&event_payload); + return ret_code; +} + /* * Version of setup_lttng_msg() without command header. */ @@ -1400,40 +1383,6 @@ error_add_context: cmd_ctx->lsm.u.disable.channel_name); break; } - case LTTNG_DISABLE_EVENT: - { - - /* - * FIXME: handle filter; for now we just receive the filter's - * bytecode along with the filter expression which are sent by - * liblttng-ctl and discard them. - * - * This fixes an issue where the client may block while sending - * the filter payload and encounter an error because the session - * daemon closes the socket without ever handling this data. - */ - size_t count = cmd_ctx->lsm.u.disable.expression_len + - cmd_ctx->lsm.u.disable.bytecode_len; - - if (count) { - char data[LTTNG_FILTER_MAX_LEN]; - - DBG("Discarding disable event command payload of size %zu", count); - while (count) { - ret = lttcomm_recv_unix_sock(*sock, data, - count > sizeof(data) ? sizeof(data) : count); - if (ret < 0) { - goto error; - } - - count -= (size_t) ret; - } - } - ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm.domain.type, - cmd_ctx->lsm.u.disable.channel_name, - ALIGNED_CONST_PTR(cmd_ctx->lsm.u.disable.event)); - break; - } case LTTNG_ENABLE_CHANNEL: { ret = cmd_enable_channel( @@ -1639,167 +1588,63 @@ error_add_context: break; } case LTTNG_ENABLE_EVENT: + case LTTNG_DISABLE_EVENT: { - struct lttng_event *ev = NULL; - struct lttng_event_exclusion *exclusion = NULL; - struct lttng_bytecode *bytecode = NULL; - char *filter_expression = NULL; - - /* Handle exclusion events and receive it from the client. */ - if (cmd_ctx->lsm.u.enable.exclusion_count > 0) { - size_t count = cmd_ctx->lsm.u.enable.exclusion_count; - - exclusion = zmalloc(sizeof(struct lttng_event_exclusion) + - (count * LTTNG_SYMBOL_NAME_LEN)); - if (!exclusion) { - ret = LTTNG_ERR_EXCLUSION_NOMEM; - goto error; - } - - DBG("Receiving var len exclusion event list from client ..."); - exclusion->count = count; - ret = lttcomm_recv_unix_sock(*sock, exclusion->names, - count * LTTNG_SYMBOL_NAME_LEN); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - free(exclusion); - ret = LTTNG_ERR_EXCLUSION_INVAL; - goto error; - } - } - - /* Get filter expression from client. */ - if (cmd_ctx->lsm.u.enable.expression_len > 0) { - size_t expression_len = - cmd_ctx->lsm.u.enable.expression_len; - - if (expression_len > LTTNG_FILTER_MAX_LEN) { - ret = LTTNG_ERR_FILTER_INVAL; - free(exclusion); - goto error; - } - - filter_expression = zmalloc(expression_len); - if (!filter_expression) { - free(exclusion); - ret = LTTNG_ERR_FILTER_NOMEM; - goto error; - } - - /* Receive var. len. data */ - DBG("Receiving var len filter's expression from client ..."); - ret = lttcomm_recv_unix_sock(*sock, filter_expression, - expression_len); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - free(filter_expression); - free(exclusion); - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - } - - /* Handle filter and get bytecode from client. */ - if (cmd_ctx->lsm.u.enable.bytecode_len > 0) { - size_t bytecode_len = cmd_ctx->lsm.u.enable.bytecode_len; - - if (bytecode_len > LTTNG_FILTER_MAX_LEN) { - ret = LTTNG_ERR_FILTER_INVAL; - free(filter_expression); - free(exclusion); - goto error; - } - - bytecode = zmalloc(bytecode_len); - if (!bytecode) { - free(filter_expression); - free(exclusion); - ret = LTTNG_ERR_FILTER_NOMEM; - goto error; - } - - /* Receive var. len. data */ - DBG("Receiving var len filter's bytecode from client ..."); - ret = lttcomm_recv_unix_sock(*sock, bytecode, bytecode_len); - if (ret <= 0) { - DBG("Nothing recv() from client var len data... continuing"); - *sock_error = 1; - free(filter_expression); - free(bytecode); - free(exclusion); - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } + struct lttng_event *event; + char *filter_expression; + struct lttng_event_exclusion *exclusions; + struct lttng_bytecode *bytecode; + const enum lttng_error_code ret_code = receive_lttng_event( + cmd_ctx, *sock, sock_error, &event, + &filter_expression, &bytecode, &exclusions); - if ((bytecode->len + sizeof(*bytecode)) != bytecode_len) { - free(filter_expression); - free(bytecode); - free(exclusion); - ret = LTTNG_ERR_FILTER_INVAL; - goto error; - } - } - - ev = lttng_event_copy(ALIGNED_CONST_PTR(cmd_ctx->lsm.u.enable.event)); - if (!ev) { - DBG("Failed to copy event: %s", - cmd_ctx->lsm.u.enable.event.name); - free(filter_expression); - free(bytecode); - free(exclusion); - ret = LTTNG_ERR_NOMEM; + if (ret_code != LTTNG_OK) { + ret = (int) ret_code; goto error; } - - if (cmd_ctx->lsm.u.enable.userspace_probe_location_len > 0) { - /* Expect a userspace probe description. */ - ret = receive_userspace_probe(cmd_ctx, *sock, sock_error, ev); - if (ret) { - free(filter_expression); - free(bytecode); - free(exclusion); - lttng_event_destroy(ev); - goto error; - } - } - - ret = cmd_enable_event(cmd_ctx->session, - ALIGNED_CONST_PTR(cmd_ctx->lsm.domain), - cmd_ctx->lsm.u.enable.channel_name, - ev, - filter_expression, bytecode, exclusion, - the_kernel_poll_pipe[1]); - lttng_event_destroy(ev); + /* + * Ownership of filter_expression, exclusions, and bytecode is + * always transferred. + */ + ret = cmd_ctx->lsm.cmd_type == LTTNG_ENABLE_EVENT ? + cmd_enable_event(cmd_ctx, event, + filter_expression, exclusions, + bytecode, + the_kernel_poll_pipe[1]) : + cmd_disable_event(cmd_ctx, event, + filter_expression, bytecode, + exclusions); + lttng_event_destroy(event); break; } case LTTNG_LIST_TRACEPOINTS: { - struct lttng_event *events; - ssize_t nb_events; + enum lttng_error_code ret_code; + size_t original_payload_size; + size_t payload_size; + const size_t command_header_size = sizeof(struct lttcomm_list_command_header); + + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; + } + + original_payload_size = cmd_ctx->reply_payload.buffer.size; session_lock_list(); - nb_events = cmd_list_tracepoints(cmd_ctx->lsm.domain.type, &events); + ret_code = cmd_list_tracepoints(cmd_ctx->lsm.domain.type, + &cmd_ctx->reply_payload); session_unlock_list(); - if (nb_events < 0) { - /* Return value is a negative lttng_error_code. */ - ret = -nb_events; + if (ret_code != LTTNG_OK) { + ret = (int) ret_code; goto error; } - /* - * Setup lttng message with payload size set to the event list size in - * bytes and then copy list into the llm payload. - */ - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, events, - sizeof(struct lttng_event) * nb_events); - free(events); - - if (ret < 0) { - goto setup_error; - } + payload_size = cmd_ctx->reply_payload.buffer.size - + command_header_size - original_payload_size; + update_lttng_msg(cmd_ctx, command_header_size, payload_size); ret = LTTNG_OK; break; @@ -1836,28 +1681,29 @@ error_add_context: } case LTTNG_LIST_SYSCALLS: { - struct lttng_event *events; - ssize_t nb_events; + enum lttng_error_code ret_code; + size_t original_payload_size; + size_t payload_size; + const size_t command_header_size = sizeof(struct lttcomm_list_command_header); - nb_events = cmd_list_syscalls(&events); - if (nb_events < 0) { - /* Return value is a negative lttng_error_code. */ - ret = -nb_events; - goto error; + ret = setup_empty_lttng_msg(cmd_ctx); + if (ret) { + ret = LTTNG_ERR_NOMEM; + goto setup_error; } - /* - * Setup lttng message with payload size set to the event list size in - * bytes and then copy list into the llm payload. - */ - ret = setup_lttng_msg_no_cmd_header(cmd_ctx, events, - sizeof(struct lttng_event) * nb_events); - free(events); + original_payload_size = cmd_ctx->reply_payload.buffer.size; - if (ret < 0) { - goto setup_error; + ret_code = cmd_list_syscalls(&cmd_ctx->reply_payload); + if (ret_code != LTTNG_OK) { + ret = (int) ret_code; + goto error; } + payload_size = cmd_ctx->reply_payload.buffer.size - + command_header_size - original_payload_size; + update_lttng_msg(cmd_ctx, command_header_size, payload_size); + ret = LTTNG_OK; break; } @@ -1984,10 +1830,10 @@ error_add_context: } case LTTNG_LIST_EVENTS: { - ssize_t list_ret; - struct lttcomm_event_command_header cmd_header = {}; + enum lttng_error_code ret_code; size_t original_payload_size; size_t payload_size; + const size_t command_header_size = sizeof(struct lttcomm_list_command_header); ret = setup_empty_lttng_msg(cmd_ctx); if (ret) { @@ -1997,20 +1843,17 @@ error_add_context: original_payload_size = cmd_ctx->reply_payload.buffer.size; - /* Extended infos are included at the end of the payload. */ - list_ret = cmd_list_events(cmd_ctx->lsm.domain.type, + ret_code = cmd_list_events(cmd_ctx->lsm.domain.type, cmd_ctx->session, - cmd_ctx->lsm.u.list.channel_name, - &cmd_ctx->reply_payload); - if (list_ret < 0) { - /* Return value is a negative lttng_error_code. */ - ret = -list_ret; + cmd_ctx->lsm.u.list.channel_name, &cmd_ctx->reply_payload); + if (ret_code != LTTNG_OK) { + ret = (int) ret_code; goto error; } payload_size = cmd_ctx->reply_payload.buffer.size - - sizeof(cmd_header) - original_payload_size; - update_lttng_msg(cmd_ctx, sizeof(cmd_header), payload_size); + command_header_size - original_payload_size; + update_lttng_msg(cmd_ctx, command_header_size, payload_size); ret = LTTNG_OK; break; diff --git a/src/bin/lttng-sessiond/cmd.c b/src/bin/lttng-sessiond/cmd.c index add0a78a8..371bd68fd 100644 --- a/src/bin/lttng-sessiond/cmd.c +++ b/src/bin/lttng-sessiond/cmd.c @@ -21,30 +21,23 @@ #include #include #include -#include -#include #include #include #include #include #include -#include + #include +#include #include #include -#include #include -#include -#include #include -#include -#include +#include #include -#include #include #include #include -#include #include #include @@ -295,150 +288,97 @@ end: return ret; } -static int append_extended_info(const char *filter_expression, - struct lttng_event_exclusion *exclusion, - struct lttng_userspace_probe_location *probe_location, - struct lttng_payload *payload) -{ - int ret = 0; - size_t filter_len = 0; - size_t nb_exclusions = 0; - size_t userspace_probe_location_len = 0; - struct lttcomm_event_extended_header extended_header = {}; - struct lttcomm_event_extended_header *p_extended_header; - const size_t original_payload_size = payload->buffer.size; - - ret = lttng_dynamic_buffer_append(&payload->buffer, &extended_header, - sizeof(extended_header)); - if (ret) { - goto end; - } - - if (filter_expression) { - filter_len = strlen(filter_expression) + 1; - ret = lttng_dynamic_buffer_append(&payload->buffer, - filter_expression, filter_len); - if (ret) { - goto end; - } - } - - if (exclusion) { - const size_t len = exclusion->count * LTTNG_SYMBOL_NAME_LEN; - - nb_exclusions = exclusion->count; - - ret = lttng_dynamic_buffer_append( - &payload->buffer, &exclusion->names, len); - if (ret) { - goto end; - } - } - - if (probe_location) { - const size_t size_before_probe = payload->buffer.size; - - ret = lttng_userspace_probe_location_serialize(probe_location, - payload); - if (ret < 0) { - ret = -1; - goto end; - } - - userspace_probe_location_len = - payload->buffer.size - size_before_probe; - } - - /* Set header fields */ - p_extended_header = (struct lttcomm_event_extended_header *) - (payload->buffer.data + original_payload_size); - - p_extended_header->filter_len = filter_len; - p_extended_header->nb_exclusions = nb_exclusions; - p_extended_header->userspace_probe_location_len = - userspace_probe_location_len; - - ret = 0; -end: - return ret; -} - /* * Create a list of agent domain events. * * Return number of events in list on success or else a negative value. */ -static int list_lttng_agent_events(struct agent *agt, - struct lttng_payload *payload) +static enum lttng_error_code list_lttng_agent_events( + struct agent *agt, struct lttng_payload *reply_payload, + unsigned int *nb_events) { - int nb_events = 0, ret = 0; - const struct agent_event *agent_event; + enum lttng_error_code ret_code; + int ret = 0; + unsigned int local_nb_events = 0; + struct agent_event *event; struct lttng_ht_iter iter; + unsigned long agent_event_count; assert(agt); + assert(reply_payload); DBG3("Listing agent events"); rcu_read_lock(); - cds_lfht_for_each_entry ( - agt->events->ht, &iter.iter, agent_event, node.node) { - struct lttng_event event = { - .enabled = AGENT_EVENT_IS_ENABLED(agent_event), - .loglevel = agent_event->loglevel_value, - .loglevel_type = agent_event->loglevel_type, - }; - - ret = lttng_strncpy(event.name, agent_event->name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing agent events: '%s' exceeds the maximal allowed length of %zu bytes", - agent_event->name, sizeof(event.name)); - ret = -LTTNG_ERR_UNK; - goto end; - } - ret = lttng_dynamic_buffer_append( - &payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } + agent_event_count = lttng_ht_get_count(agt->events); + if (agent_event_count == 0) { + /* Early exit. */ + goto end; + } - nb_events++; + if (agent_event_count > UINT_MAX) { + ret_code = LTTNG_ERR_OVERFLOW; + goto error; } - cds_lfht_for_each_entry ( - agt->events->ht, &iter.iter, agent_event, node.node) { - /* Append extended info. */ - ret = append_extended_info(agent_event->filter_expression, NULL, - NULL, payload); + local_nb_events = (unsigned int) agent_event_count; + + cds_lfht_for_each_entry(agt->events->ht, &iter.iter, event, node.node) { + struct lttng_event *tmp_event = lttng_event_create(); + + if (!tmp_event) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } + + if (lttng_strncpy(tmp_event->name, event->name, sizeof(tmp_event->name))) { + lttng_event_destroy(tmp_event); + ret_code = LTTNG_ERR_FATAL; + goto error; + } + + tmp_event->name[sizeof(tmp_event->name) - 1] = '\0'; + tmp_event->enabled = !!event->enabled_count; + tmp_event->loglevel = event->loglevel_value; + tmp_event->loglevel_type = event->loglevel_type; + + ret = lttng_event_serialize(tmp_event, 0, NULL, + event->filter_expression, 0, NULL, reply_payload); + lttng_event_destroy(tmp_event); if (ret) { - ERR("Failed to append extended event info to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; + ret_code = LTTNG_ERR_FATAL; + goto error; } } - ret = nb_events; end: + ret_code = LTTNG_OK; + *nb_events = local_nb_events; +error: rcu_read_unlock(); - return ret; + return ret_code; } /* * Create a list of ust global domain events. */ -static int list_lttng_ust_global_events(char *channel_name, +static enum lttng_error_code list_lttng_ust_global_events(char *channel_name, struct ltt_ust_domain_global *ust_global, - struct lttng_payload *payload) + struct lttng_payload *reply_payload, + unsigned int *nb_events) { - int ret = 0; - unsigned int nb_events = 0; + enum lttng_error_code ret_code; + int ret; struct lttng_ht_iter iter; - const struct lttng_ht_node_str *node; - const struct ltt_ust_channel *uchan; - const struct ltt_ust_event *uevent; + struct lttng_ht_node_str *node; + struct ltt_ust_channel *uchan; + struct ltt_ust_event *uevent; + unsigned long channel_event_count; + unsigned int local_nb_events = 0; + + assert(reply_payload); + assert(nb_events); DBG("Listing UST global events for channel %s", channel_name); @@ -447,158 +387,184 @@ static int list_lttng_ust_global_events(char *channel_name, lttng_ht_lookup(ust_global->channels, (void *) channel_name, &iter); node = lttng_ht_iter_get_node_str(&iter); if (node == NULL) { - ret = LTTNG_ERR_UST_CHAN_NOT_FOUND; + ret_code = LTTNG_ERR_UST_CHAN_NOT_FOUND; goto end; } uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); - DBG3("Listing UST global events"); + channel_event_count = lttng_ht_get_count(uchan->events); + if (channel_event_count == 0) { + /* Early exit. */ + ret_code = LTTNG_OK; + goto end; + } + + if (channel_event_count > UINT_MAX) { + ret_code = LTTNG_ERR_OVERFLOW; + goto error; + } + + local_nb_events = (unsigned int) channel_event_count; + + DBG3("Listing UST global %d events", *nb_events); cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - struct lttng_event event = {}; + struct lttng_event *tmp_event = NULL; if (uevent->internal) { + /* This event should remain hidden from clients */ + local_nb_events--; continue; } - ret = lttng_strncpy(event.name, uevent->attr.name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing user space tracer events: '%s' exceeds the maximal allowed length of %zu bytes", - uevent->attr.name, sizeof(event.name)); - ret = -LTTNG_ERR_UNK; + tmp_event = lttng_event_create(); + if (!tmp_event) { + ret_code = LTTNG_ERR_NOMEM; goto end; } - event.enabled = uevent->enabled; + if (lttng_strncpy(tmp_event->name, uevent->attr.name, + LTTNG_SYMBOL_NAME_LEN)) { + ret_code = LTTNG_ERR_FATAL; + lttng_event_destroy(tmp_event); + goto end; + } + + tmp_event->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + tmp_event->enabled = uevent->enabled; switch (uevent->attr.instrumentation) { case LTTNG_UST_ABI_TRACEPOINT: - event.type = LTTNG_EVENT_TRACEPOINT; + tmp_event->type = LTTNG_EVENT_TRACEPOINT; break; case LTTNG_UST_ABI_PROBE: - event.type = LTTNG_EVENT_PROBE; + tmp_event->type = LTTNG_EVENT_PROBE; break; case LTTNG_UST_ABI_FUNCTION: - event.type = LTTNG_EVENT_FUNCTION; + tmp_event->type = LTTNG_EVENT_FUNCTION; break; } - event.loglevel = uevent->attr.loglevel; + tmp_event->loglevel = uevent->attr.loglevel; switch (uevent->attr.loglevel_type) { case LTTNG_UST_ABI_LOGLEVEL_ALL: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; + tmp_event->loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL; break; case LTTNG_UST_ABI_LOGLEVEL_RANGE: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; + tmp_event->loglevel_type = LTTNG_EVENT_LOGLEVEL_RANGE; break; case LTTNG_UST_ABI_LOGLEVEL_SINGLE: - event.loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; + tmp_event->loglevel_type = LTTNG_EVENT_LOGLEVEL_SINGLE; break; } - if (uevent->filter) { - event.filter = 1; + tmp_event->filter = 1; } - if (uevent->exclusion) { - event.exclusion = 1; + tmp_event->exclusion = 1; } - ret = lttng_dynamic_buffer_append(&payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - nb_events++; - } - - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - /* Append extended info. */ - ret = append_extended_info(uevent->filter_expression, - uevent->exclusion, NULL, payload); + /* + * We do not care about the filter bytecode and the fd from the + * userspace_probe_location. + */ + ret = lttng_event_serialize(tmp_event, uevent->exclusion ? uevent->exclusion->count : 0, + uevent->exclusion ? (char **) uevent->exclusion ->names : NULL, + uevent->filter_expression, 0, NULL, reply_payload); + lttng_event_destroy(tmp_event); if (ret) { - ERR("Failed to append extended event info to payload"); - ret = -LTTNG_ERR_FATAL; - goto end; + ret_code = LTTNG_ERR_FATAL; + goto error; } } - ret = nb_events; end: + /* nb_events is already set at this point. */ + ret_code = LTTNG_OK; + *nb_events = local_nb_events; +error: rcu_read_unlock(); - return ret; + return ret_code; } /* * Fill lttng_event array of all kernel events in the channel. */ -static int list_lttng_kernel_events(char *channel_name, +static enum lttng_error_code list_lttng_kernel_events(char *channel_name, struct ltt_kernel_session *kernel_session, - struct lttng_payload *payload) + struct lttng_payload *reply_payload, + unsigned int *nb_events) { + enum lttng_error_code ret_code; int ret; - unsigned int nb_event; - const struct ltt_kernel_event *kevent; - const struct ltt_kernel_channel *kchan; + struct ltt_kernel_event *event; + struct ltt_kernel_channel *kchan; + + assert(reply_payload); kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); if (kchan == NULL) { - ret = LTTNG_ERR_KERN_CHAN_NOT_FOUND; - goto error; + ret_code = LTTNG_ERR_KERN_CHAN_NOT_FOUND; + goto end; } - nb_event = kchan->event_count; + *nb_events = kchan->event_count; DBG("Listing events for channel %s", kchan->channel->name); + if (*nb_events == 0) { + ret_code = LTTNG_OK; + goto end; + } + /* Kernel channels */ - cds_list_for_each_entry(kevent, &kchan->events_list.head , list) { - struct lttng_event event = {}; + cds_list_for_each_entry(event, &kchan->events_list.head , list) { + struct lttng_event *tmp_event = lttng_event_create(); - ret = lttng_strncpy(event.name, kevent->event->name, sizeof(event.name)); - if (ret) { - /* Internal error, invalid name. */ - ERR("Invalid event name while listing kernel events: '%s' exceeds the maximal allowed length of %zu bytes", - kevent->event->name, - sizeof(event.name)); - ret = -LTTNG_ERR_UNK; + if (!tmp_event) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + if (lttng_strncpy(tmp_event->name, event->event->name, LTTNG_SYMBOL_NAME_LEN)) { + lttng_event_destroy(tmp_event); + ret_code = LTTNG_ERR_FATAL; goto end; + } - event.enabled = kevent->enabled; - event.filter = (unsigned char) !!kevent->filter_expression; + tmp_event->name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + tmp_event->enabled = event->enabled; + tmp_event->filter = (unsigned char) !!event->filter_expression; - switch (kevent->event->instrumentation) { + switch (event->event->instrumentation) { case LTTNG_KERNEL_ABI_TRACEPOINT: - event.type = LTTNG_EVENT_TRACEPOINT; + tmp_event->type = LTTNG_EVENT_TRACEPOINT; break; case LTTNG_KERNEL_ABI_KRETPROBE: - event.type = LTTNG_EVENT_FUNCTION; - memcpy(&event.attr.probe, &kevent->event->u.kprobe, + tmp_event->type = LTTNG_EVENT_FUNCTION; + memcpy(&tmp_event->attr.probe, &event->event->u.kprobe, sizeof(struct lttng_kernel_abi_kprobe)); break; case LTTNG_KERNEL_ABI_KPROBE: - event.type = LTTNG_EVENT_PROBE; - memcpy(&event.attr.probe, &kevent->event->u.kprobe, + tmp_event->type = LTTNG_EVENT_PROBE; + memcpy(&tmp_event->attr.probe, &event->event->u.kprobe, sizeof(struct lttng_kernel_abi_kprobe)); break; case LTTNG_KERNEL_ABI_UPROBE: - event.type = LTTNG_EVENT_USERSPACE_PROBE; + tmp_event->type = LTTNG_EVENT_USERSPACE_PROBE; break; case LTTNG_KERNEL_ABI_FUNCTION: - event.type = LTTNG_EVENT_FUNCTION; - memcpy(&event.attr.ftrace, &kevent->event->u.ftrace, + tmp_event->type = LTTNG_EVENT_FUNCTION; + memcpy(&(tmp_event->attr.ftrace), &event->event->u.ftrace, sizeof(struct lttng_kernel_abi_function)); break; case LTTNG_KERNEL_ABI_NOOP: - event.type = LTTNG_EVENT_NOOP; + tmp_event->type = LTTNG_EVENT_NOOP; break; case LTTNG_KERNEL_ABI_SYSCALL: - event.type = LTTNG_EVENT_SYSCALL; + tmp_event->type = LTTNG_EVENT_SYSCALL; break; case LTTNG_KERNEL_ABI_ALL: /* fall-through. */ @@ -607,30 +573,40 @@ static int list_lttng_kernel_events(char *channel_name, break; } - ret = lttng_dynamic_buffer_append( - &payload->buffer, &event, sizeof(event)); - if (ret) { - ERR("Failed to append event to payload"); - ret = -LTTNG_ERR_NOMEM; - goto end; + if (event->userspace_probe_location) { + struct lttng_userspace_probe_location *location_copy = + lttng_userspace_probe_location_copy( + event->userspace_probe_location); + + if (!location_copy) { + lttng_event_destroy(tmp_event); + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + ret = lttng_event_set_userspace_probe_location( + tmp_event, location_copy); + if (ret) { + lttng_event_destroy(tmp_event); + lttng_userspace_probe_location_destroy( + location_copy); + ret_code = LTTNG_ERR_INVALID; + goto end; + } } - } - cds_list_for_each_entry(kevent, &kchan->events_list.head , list) { - /* Append extended info. */ - ret = append_extended_info(kevent->filter_expression, NULL, - kevent->userspace_probe_location, payload); + ret = lttng_event_serialize(tmp_event, 0, NULL, + event->filter_expression, 0, NULL, reply_payload); + lttng_event_destroy(tmp_event); if (ret) { - DBG("Error appending extended info message"); - ret = -LTTNG_ERR_FATAL; - goto error; + ret_code = LTTNG_ERR_FATAL; + goto end; } } + ret_code = LTTNG_OK; end: - return nb_event; -error: - return ret; + return ret_code; } /* @@ -1691,15 +1667,33 @@ end: /* * Command LTTNG_DISABLE_EVENT processed by the client thread. */ -int cmd_disable_event(struct ltt_session *session, - enum lttng_domain_type domain, const char *channel_name, - const struct lttng_event *event) +int cmd_disable_event(struct command_ctx *cmd_ctx, + struct lttng_event *event, + char *filter_expression, + struct lttng_bytecode *bytecode, + struct lttng_event_exclusion *exclusion) { int ret; const char *event_name; + const struct ltt_session *session = cmd_ctx->session; + const char *channel_name = cmd_ctx->lsm.u.disable.channel_name; + const enum lttng_domain_type domain = cmd_ctx->lsm.domain.type; DBG("Disable event command for event \'%s\'", event->name); + /* + * Filter and exclusions are simply not handled by the + * disable event command at this time. + * + * FIXME + */ + (void) filter_expression; + (void) exclusion; + + /* Ignore the presence of filter or exclusion for the event */ + event->filter = 0; + event->exclusion = 0; + event_name = event->name; /* Error out on unhandled search criteria */ @@ -1862,6 +1856,9 @@ int cmd_disable_event(struct ltt_session *session, error_unlock: rcu_read_unlock(); error: + free(exclusion); + free(bytecode); + free(filter_expression); return ret; } @@ -2439,16 +2436,35 @@ error: * Command LTTNG_ENABLE_EVENT processed by the client thread. * We own filter, exclusion, and filter_expression. */ -int cmd_enable_event(struct ltt_session *session, - const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, +int cmd_enable_event(struct command_ctx *cmd_ctx, + struct lttng_event *event, char *filter_expression, - struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, + struct lttng_bytecode *bytecode, int wpipe) { - return _cmd_enable_event(session, domain, channel_name, event, - filter_expression, filter, exclusion, wpipe, false); + int ret; + /* + * Copied to ensure proper alignment since 'lsm' is a packed structure. + */ + const struct lttng_domain command_domain = cmd_ctx->lsm.domain; + + /* + * The ownership of the following parameters is transferred to + * _cmd_enable_event: + * + * - filter_expression, + * - bytecode, + * - exclusion + */ + ret = _cmd_enable_event(cmd_ctx->session, + &command_domain, + cmd_ctx->lsm.u.enable.channel_name, event, + filter_expression, bytecode, exclusion, wpipe, false); + filter_expression = NULL; + bytecode = NULL; + exclusion = NULL; + return ret; } /* @@ -2471,46 +2487,81 @@ static int cmd_enable_event_internal(struct ltt_session *session, /* * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. */ -ssize_t cmd_list_tracepoints(enum lttng_domain_type domain, - struct lttng_event **events) +enum lttng_error_code cmd_list_tracepoints(enum lttng_domain_type domain, + struct lttng_payload *reply_payload) { + enum lttng_error_code ret_code; int ret; - ssize_t nb_events = 0; + ssize_t i, nb_events = 0; + struct lttng_event *events = NULL; + struct lttcomm_list_command_header reply_command_header = {}; + size_t reply_command_header_offset; + + assert(reply_payload); + + /* Reserve space for command reply header. */ + reply_command_header_offset = reply_payload->buffer.size; + ret = lttng_dynamic_buffer_set_size(&reply_payload->buffer, + reply_command_header_offset + + sizeof(struct lttcomm_list_command_header)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } switch (domain) { case LTTNG_DOMAIN_KERNEL: - nb_events = kernel_list_events(events); + nb_events = kernel_list_events(&events); if (nb_events < 0) { - ret = LTTNG_ERR_KERN_LIST_FAIL; + ret_code = LTTNG_ERR_KERN_LIST_FAIL; goto error; } break; case LTTNG_DOMAIN_UST: - nb_events = ust_app_list_events(events); + nb_events = ust_app_list_events(&events); if (nb_events < 0) { - ret = LTTNG_ERR_UST_LIST_FAIL; + ret_code = LTTNG_ERR_UST_LIST_FAIL; goto error; } break; case LTTNG_DOMAIN_LOG4J: case LTTNG_DOMAIN_JUL: case LTTNG_DOMAIN_PYTHON: - nb_events = agent_list_events(events, domain); + nb_events = agent_list_events(&events, domain); if (nb_events < 0) { - ret = LTTNG_ERR_UST_LIST_FAIL; + ret_code = LTTNG_ERR_UST_LIST_FAIL; goto error; } break; default: - ret = LTTNG_ERR_UND; + ret_code = LTTNG_ERR_UND; + goto error; + } + + for (i = 0; i < nb_events; i++) { + ret = lttng_event_serialize(&events[i], 0, NULL, NULL, 0, NULL, + reply_payload); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } + } + + if (nb_events > UINT32_MAX) { + ERR("Tracepoint count would overflow the tracepoint listing command's reply"); + ret_code = LTTNG_ERR_OVERFLOW; goto error; } - return nb_events; + /* Update command reply header. */ + reply_command_header.count = (uint32_t) nb_events; + memcpy(reply_payload->buffer.data + reply_command_header_offset, &reply_command_header, + sizeof(reply_command_header)); + ret_code = LTTNG_OK; error: - /* Return negative value to differentiate return code */ - return -ret; + free(events); + return ret_code; } /* @@ -2543,9 +2594,58 @@ error: return -ret; } -ssize_t cmd_list_syscalls(struct lttng_event **events) +enum lttng_error_code cmd_list_syscalls( + struct lttng_payload *reply_payload) { - return syscall_table_list(events); + enum lttng_error_code ret_code; + ssize_t nb_events, i; + int ret; + struct lttng_event *events = NULL; + struct lttcomm_list_command_header reply_command_header = {}; + size_t reply_command_header_offset; + + assert(reply_payload); + + /* Reserve space for command reply header. */ + reply_command_header_offset = reply_payload->buffer.size; + ret = lttng_dynamic_buffer_set_size(&reply_payload->buffer, + reply_command_header_offset + + sizeof(struct lttcomm_list_command_header)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + nb_events = syscall_table_list(&events); + if (nb_events < 0) { + ret_code = (enum lttng_error_code) -nb_events; + goto end; + } + + for (i = 0; i < nb_events; i++) { + ret = lttng_event_serialize(&events[i], 0, NULL, NULL, 0, NULL, + reply_payload); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + if (nb_events > UINT32_MAX) { + ERR("Syscall count would overflow the syscall listing command's reply"); + ret_code = LTTNG_ERR_OVERFLOW; + goto end; + } + + /* Update command reply header. */ + reply_command_header.count = (uint32_t) nb_events; + memcpy(reply_payload->buffer.data + reply_command_header_offset, &reply_command_header, + sizeof(reply_command_header)); + + ret_code = LTTNG_OK; +end: + free(events); + return ret_code; } /* @@ -3690,36 +3790,45 @@ end: /* * Command LTTNG_LIST_EVENTS processed by the client thread. */ -ssize_t cmd_list_events(enum lttng_domain_type domain, - struct ltt_session *session, char *channel_name, - struct lttng_payload *payload) +enum lttng_error_code cmd_list_events(enum lttng_domain_type domain, + struct ltt_session *session, + char *channel_name, + struct lttng_payload *reply_payload) { - int ret = 0; - ssize_t nb_events = 0; - struct lttcomm_event_command_header cmd_header = {}; - const size_t cmd_header_offset = payload->buffer.size; + int buffer_resize_ret; + enum lttng_error_code ret_code = LTTNG_OK; + struct lttcomm_list_command_header reply_command_header = {}; + size_t reply_command_header_offset; + unsigned int nb_events = 0; - ret = lttng_dynamic_buffer_append( - &payload->buffer, &cmd_header, sizeof(cmd_header)); - if (ret) { - ret = LTTNG_ERR_NOMEM; - goto error; + assert(reply_payload); + + /* Reserve space for command reply header. */ + reply_command_header_offset = reply_payload->buffer.size; + buffer_resize_ret = lttng_dynamic_buffer_set_size(&reply_payload->buffer, + reply_command_header_offset + + sizeof(struct lttcomm_list_command_header)); + if (buffer_resize_ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; } switch (domain) { case LTTNG_DOMAIN_KERNEL: if (session->kernel_session != NULL) { - nb_events = list_lttng_kernel_events(channel_name, - session->kernel_session, payload); + ret_code = list_lttng_kernel_events(channel_name, + session->kernel_session, reply_payload, &nb_events); } + break; case LTTNG_DOMAIN_UST: { if (session->ust_session != NULL) { - nb_events = list_lttng_ust_global_events(channel_name, + ret_code = list_lttng_ust_global_events(channel_name, &session->ust_session->domain_global, - payload); + reply_payload, &nb_events); } + break; } case LTTNG_DOMAIN_LOG4J: @@ -3733,27 +3842,32 @@ ssize_t cmd_list_events(enum lttng_domain_type domain, cds_lfht_for_each_entry(session->ust_session->agents->ht, &iter.iter, agt, node.node) { if (agt->domain == domain) { - nb_events = list_lttng_agent_events( - agt, payload); + ret_code = list_lttng_agent_events( + agt, reply_payload, &nb_events); break; } } + rcu_read_unlock(); } break; default: - ret = LTTNG_ERR_UND; - goto error; + ret_code = LTTNG_ERR_UND; + break; } - ((struct lttcomm_event_command_header *) (payload->buffer.data + - cmd_header_offset))->nb_events = (uint32_t) nb_events; + if (nb_events > UINT32_MAX) { + ret_code = LTTNG_ERR_OVERFLOW; + goto end; + } - return nb_events; + /* Update command reply header. */ + reply_command_header.count = (uint32_t) nb_events; + memcpy(reply_payload->buffer.data + reply_command_header_offset, &reply_command_header, + sizeof(reply_command_header)); -error: - /* Return negative value to differentiate return code */ - return -ret; +end: + return ret_code; } /* diff --git a/src/bin/lttng-sessiond/cmd.h b/src/bin/lttng-sessiond/cmd.h index 66897afba..27b10cb34 100644 --- a/src/bin/lttng-sessiond/cmd.h +++ b/src/bin/lttng-sessiond/cmd.h @@ -77,20 +77,21 @@ enum lttng_error_code cmd_process_attr_tracker_get_inclusion_set( struct lttng_process_attr_values **values); /* Event commands */ -int cmd_disable_event(struct ltt_session *session, - enum lttng_domain_type domain, - const char *channel_name, - const struct lttng_event *event); +int cmd_disable_event(struct command_ctx *cmd_ctx, + struct lttng_event *event, + char *filter_expression, + struct lttng_bytecode *filter, + struct lttng_event_exclusion *exclusion); int cmd_add_context(struct ltt_session *session, enum lttng_domain_type domain, char *channel_name, const struct lttng_event_context *ctx, int kwpipe); int cmd_set_filter(struct ltt_session *session, enum lttng_domain_type domain, char *channel_name, struct lttng_event *event, struct lttng_bytecode *bytecode); -int cmd_enable_event(struct ltt_session *session, const struct lttng_domain *domain, - char *channel_name, struct lttng_event *event, +int cmd_enable_event(struct command_ctx *cmd_ctx, + struct lttng_event *event, char *filter_expression, - struct lttng_bytecode *filter, struct lttng_event_exclusion *exclusion, + struct lttng_bytecode *bytecode, int wpipe); /* Trace session action commands */ @@ -108,8 +109,9 @@ int cmd_setup_relayd(struct ltt_session *session); /* Listing commands */ ssize_t cmd_list_domains(struct ltt_session *session, struct lttng_domain **domains); -ssize_t cmd_list_events(enum lttng_domain_type domain, - struct ltt_session *session, char *channel_name, +enum lttng_error_code cmd_list_events(enum lttng_domain_type domain, + struct ltt_session *session, + char *channel_name, struct lttng_payload *payload); enum lttng_error_code cmd_list_channels(enum lttng_domain_type domain, struct ltt_session *session, @@ -120,11 +122,12 @@ void cmd_list_lttng_sessions(struct lttng_session *sessions, size_t session_count, uid_t uid, gid_t gid); ssize_t cmd_list_tracepoint_fields(enum lttng_domain_type domain, struct lttng_event_field **fields); -ssize_t cmd_list_tracepoints(enum lttng_domain_type domain, - struct lttng_event **events); +enum lttng_error_code cmd_list_tracepoints(enum lttng_domain_type domain, + struct lttng_payload *reply_payload); ssize_t cmd_snapshot_list_outputs(struct ltt_session *session, struct lttng_snapshot_output **outputs); -ssize_t cmd_list_syscalls(struct lttng_event **events); +enum lttng_error_code cmd_list_syscalls( + struct lttng_payload *reply_payload); int cmd_data_pending(struct ltt_session *session); diff --git a/src/common/event.c b/src/common/event.c index e4bda2505..f6a24ef1b 100644 --- a/src/common/event.c +++ b/src/common/event.c @@ -5,8 +5,38 @@ * */ -#include +#include "common/compat/string.h" +#include "common/macros.h" +#include "lttng/lttng-error.h" +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include + +struct event_list_element { + struct lttng_event *event; + struct lttng_event_exclusion *exclusions; + char *filter_expression; +}; + +static void event_list_destructor(void *ptr) +{ + struct event_list_element *element = (struct event_list_element *) ptr; + + free(element->filter_expression); + free(element->exclusions); + lttng_event_destroy(element->event); + free(element); +} LTTNG_HIDDEN struct lttng_event *lttng_event_copy(const struct lttng_event *event) @@ -41,3 +71,1136 @@ error: new_event = NULL; goto end; } + +static int lttng_event_probe_attr_serialize( + const struct lttng_event_probe_attr *probe, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + struct lttng_event_probe_attr_comm comm = { 0 }; + + symbol_name_len = lttng_strnlen(probe->symbol_name, LTTNG_SYMBOL_NAME_LEN); + if (symbol_name_len == LTTNG_SYMBOL_NAME_LEN) { + /* Not null-termintated. */ + ret = -1; + goto end; + } + + /* Include the null terminator. */ + symbol_name_len += 1; + + comm.symbol_name_len = (uint32_t) symbol_name_len; + comm.addr = probe->addr; + comm.offset = probe->addr; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret < 0) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append( + &payload->buffer, probe->symbol_name, symbol_name_len); +end: + return ret; +} + +static int lttng_event_function_attr_serialize( + const struct lttng_event_function_attr *function, + struct lttng_payload *payload) +{ + int ret; + size_t symbol_name_len; + struct lttng_event_function_attr_comm comm = { 0 }; + + symbol_name_len = lttng_strnlen(function->symbol_name, LTTNG_SYMBOL_NAME_LEN); + if (symbol_name_len == LTTNG_SYMBOL_NAME_LEN) { + /* Not null-termintated. */ + ret = -1; + goto end; + } + + /* Include the null terminator. */ + symbol_name_len += 1; + + comm.symbol_name_len = (uint32_t) symbol_name_len; + + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret < 0) { + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + function->symbol_name, symbol_name_len); +end: + return ret; +} + +static ssize_t lttng_event_probe_attr_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_probe_attr **probe_attr) +{ + ssize_t ret, offset = 0; + const struct lttng_event_probe_attr_comm *comm; + struct lttng_event_probe_attr *local_attr = NULL; + struct lttng_payload_view comm_view = lttng_payload_view_from_view( + view, offset, sizeof(*comm)); + + if (!lttng_payload_view_is_valid(&comm_view)) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) comm_view.buffer.data; + offset += sizeof(*comm); + + local_attr = (struct lttng_event_probe_attr *) zmalloc( + sizeof(*local_attr)); + if (local_attr == NULL) { + ret = -1; + goto end; + } + + local_attr->addr = comm->addr; + local_attr->offset = comm->offset; + + { + const char *name; + struct lttng_payload_view name_view = + lttng_payload_view_from_view(view, offset, + comm->symbol_name_len); + + if (!lttng_payload_view_is_valid(&name_view)) { + ret = -1; + goto end; + } + + name = name_view.buffer.data; + + if (!lttng_buffer_view_contains_string(&name_view.buffer, name, + comm->symbol_name_len)) { + ret = -1; + goto end; + } + + ret = lttng_strncpy(local_attr->symbol_name, name, + LTTNG_SYMBOL_NAME_LEN); + if (ret) { + ret = -1; + goto end; + } + + offset += comm->symbol_name_len; + } + + *probe_attr = local_attr; + local_attr = NULL; + ret = offset; +end: + return ret; +} + +static ssize_t lttng_event_function_attr_create_from_payload( + struct lttng_payload_view *view, + struct lttng_event_function_attr **function_attr) +{ + ssize_t ret, offset = 0; + const struct lttng_event_function_attr_comm *comm; + struct lttng_event_function_attr *local_attr = NULL; + struct lttng_payload_view comm_view = lttng_payload_view_from_view( + view, offset, sizeof(*comm)); + + if (!lttng_payload_view_is_valid(&comm_view)) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) view->buffer.data; + offset += sizeof(*comm); + + local_attr = (struct lttng_event_function_attr *) zmalloc( + sizeof(*local_attr)); + if (local_attr == NULL) { + ret = -1; + goto end; + } + + { + const char *name; + struct lttng_payload_view name_view = + lttng_payload_view_from_view(view, offset, + comm->symbol_name_len); + + if (!lttng_payload_view_is_valid(&name_view)) { + ret = -1; + goto end; + } + + name = name_view.buffer.data; + + if (!lttng_buffer_view_contains_string(&name_view.buffer, name, + comm->symbol_name_len)) { + ret = -1; + goto end; + } + + ret = lttng_strncpy(local_attr->symbol_name, name, + LTTNG_SYMBOL_NAME_LEN); + if (ret) { + ret = -1; + goto end; + } + + offset += comm->symbol_name_len; + } + + *function_attr = local_attr; + local_attr = NULL; + ret = offset; +end: + return ret; +} + +static ssize_t lttng_event_exclusions_create_from_payload( + struct lttng_payload_view *view, + uint32_t count, + struct lttng_event_exclusion **exclusions) +{ + ssize_t ret, offset = 0; + size_t size = (count * LTTNG_SYMBOL_NAME_LEN); + uint32_t i; + const struct lttng_event_exclusion_comm *comm; + struct lttng_event_exclusion *local_exclusions; + + local_exclusions = (struct lttng_event_exclusion *) zmalloc( + sizeof(struct lttng_event_exclusion) + size); + if (!local_exclusions) { + ret = -1; + goto end; + } + + local_exclusions->count = count; + + for (i = 0; i < count; i++) { + const char *string; + struct lttng_buffer_view string_view; + const struct lttng_buffer_view comm_view = + lttng_buffer_view_from_view(&view->buffer, + offset, sizeof(*comm)); + + if (!lttng_buffer_view_is_valid(&comm_view)) { + ret = -1; + goto end; + } + + comm = (typeof(comm)) comm_view.data; + offset += sizeof(*comm); + + string_view = lttng_buffer_view_from_view( + &view->buffer, offset, comm->len); + + if (!lttng_buffer_view_is_valid(&string_view)) { + ret = -1; + goto end; + } + + string = string_view.data; + + if (!lttng_buffer_view_contains_string( + &string_view, string, comm->len)) { + ret = -1; + goto end; + } + + ret = lttng_strncpy(local_exclusions->names[i], + string, LTTNG_SYMBOL_NAME_LEN); + if (ret) { + ret = -1; + goto end; + } + + offset += comm->len; + } + + *exclusions = local_exclusions; + local_exclusions = NULL; + ret = offset; +end: + free(local_exclusions); + return ret; +} + +ssize_t lttng_event_create_from_payload(struct lttng_payload_view *view, + struct lttng_event **out_event, + struct lttng_event_exclusion **out_exclusion, + char **out_filter_expression, + struct lttng_bytecode **out_bytecode) +{ + ssize_t ret, offset = 0; + struct lttng_event *local_event = NULL; + struct lttng_event_exclusion *local_exclusions = NULL; + struct lttng_bytecode *local_bytecode = NULL; + char *local_filter_expression = NULL; + const struct lttng_event_comm *event_comm; + struct lttng_event_function_attr *local_function_attr = NULL; + struct lttng_event_probe_attr *local_probe_attr = NULL; + struct lttng_userspace_probe_location *local_userspace_probe_location = + NULL; + + /* + * Only event is obligatory, the other output argument are optional and + * depends on what the caller is interested in. + */ + assert(out_event); + assert(view); + + { + struct lttng_payload_view comm_view = + lttng_payload_view_from_view(view, offset, + sizeof(*event_comm)); + + if (!lttng_payload_view_is_valid(&comm_view)) { + ret = -1; + goto end; + } + + /* lttng_event_comm header */ + event_comm = (typeof(event_comm)) comm_view.buffer.data; + offset += sizeof(*event_comm); + } + + local_event = lttng_event_create(); + if (local_event == NULL) { + ret = -1; + goto end; + } + + local_event->type = (enum lttng_event_type) event_comm->event_type; + local_event->loglevel_type = (enum lttng_loglevel_type) event_comm->loglevel_type; + local_event->loglevel = event_comm->loglevel; + local_event->enabled = event_comm->enabled; + local_event->pid = event_comm->pid; + local_event->flags = (enum lttng_event_flag) event_comm->flags; + + { + const char *name; + const struct lttng_buffer_view name_view = + lttng_buffer_view_from_view(&view->buffer, + offset, event_comm->name_len); + + if (!lttng_buffer_view_is_valid(&name_view)) { + ret = -1; + goto end; + } + + name = (const char *) name_view.data; + + if (!lttng_buffer_view_contains_string( + &name_view, name, event_comm->name_len)) { + ret = -1; + goto end; + } + + ret = lttng_strncpy( + local_event->name, name, LTTNG_SYMBOL_NAME_LEN); + if (ret) { + ret = -1; + goto end; + } + + offset += event_comm->name_len; + } + + /* Exclusions */ + if (event_comm->exclusion_count == 0) { + goto deserialize_filter_expression; + } + + { + struct lttng_payload_view exclusions_view = + lttng_payload_view_from_view( + view, offset, -1); + + if (!lttng_payload_view_is_valid(&exclusions_view)) { + ret = -1; + goto end; + } + + ret = lttng_event_exclusions_create_from_payload(&exclusions_view, + event_comm->exclusion_count, &local_exclusions); + if (ret < 0) { + ret = -1; + goto end; + } + offset += ret; + + local_event->exclusion = 1; + } + +deserialize_filter_expression: + + if (event_comm->filter_expression_len == 0) { + if (event_comm->bytecode_len != 0) { + /* + * This is an invalid event payload. + * + * Filter expression without bytecode is possible but + * not the other way around. + * */ + ret = -1; + goto end; + } + goto deserialize_event_type_payload; + } + + { + const char *filter_expression_buffer; + struct lttng_buffer_view filter_expression_view = + lttng_buffer_view_from_view(&view->buffer, offset, + event_comm->filter_expression_len); + + if (!lttng_buffer_view_is_valid(&filter_expression_view)) { + ret = -1; + goto end; + } + + filter_expression_buffer = filter_expression_view.data; + + if (!lttng_buffer_view_contains_string(&filter_expression_view, + filter_expression_buffer, + event_comm->filter_expression_len)) { + ret = -1; + goto end; + } + + local_filter_expression = lttng_strndup( + filter_expression_buffer, + event_comm->filter_expression_len); + if (!local_filter_expression) { + ret = -1; + goto end; + } + + local_event->filter = 1; + + offset += event_comm->filter_expression_len; + } + + if (event_comm->bytecode_len == 0) { + /* + * Filter expression can be present but without bytecode + * when dealing with event listing. + */ + goto deserialize_event_type_payload; + } + + /* Bytecode */ + { + struct lttng_payload_view bytecode_view = + lttng_payload_view_from_view(view, offset, + event_comm->bytecode_len); + + if (!lttng_payload_view_is_valid(&bytecode_view)) { + ret = -1; + goto end; + } + + local_bytecode = (struct lttng_bytecode *) zmalloc( + event_comm->bytecode_len); + if (!local_bytecode) { + ret = -1; + goto end; + } + + memcpy(local_bytecode, bytecode_view.buffer.data, + event_comm->bytecode_len); + if ((local_bytecode->len + sizeof(*local_bytecode)) != + event_comm->bytecode_len) { + ret = -1; + goto end; + } + + offset += event_comm->bytecode_len; + } + +deserialize_event_type_payload: + /* Event type specific payload */ + switch (local_event->type) { + case LTTNG_EVENT_FUNCTION: + /* Fallthrough */ + case LTTNG_EVENT_PROBE: + { + struct lttng_payload_view probe_attr_view = + lttng_payload_view_from_view(view, offset, + event_comm->lttng_event_probe_attr_len); + + if (event_comm->lttng_event_probe_attr_len == 0) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&probe_attr_view)) { + ret = -1; + goto end; + } + + ret = lttng_event_probe_attr_create_from_payload( + &probe_attr_view, &local_probe_attr); + if (ret < 0 || ret != event_comm->lttng_event_probe_attr_len) { + ret = -1; + goto end; + } + + /* Copy to the local event. */ + memcpy(&local_event->attr.probe, local_probe_attr, + sizeof(local_event->attr.probe)); + + offset += ret; + break; + } + case LTTNG_EVENT_FUNCTION_ENTRY: + { + struct lttng_payload_view function_attr_view = + lttng_payload_view_from_view(view, offset, + event_comm->lttng_event_function_attr_len); + + if (event_comm->lttng_event_function_attr_len == 0) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid(&function_attr_view)) { + ret = -1; + goto end; + } + + ret = lttng_event_function_attr_create_from_payload( + &function_attr_view, &local_function_attr); + if (ret < 0 || ret != event_comm->lttng_event_function_attr_len) { + ret = -1; + goto end; + } + + /* Copy to the local event. */ + memcpy(&local_event->attr.ftrace, local_function_attr, + sizeof(local_event->attr.ftrace)); + + offset += ret; + + break; + } + case LTTNG_EVENT_USERSPACE_PROBE: + { + struct lttng_payload_view userspace_probe_location_view = + lttng_payload_view_from_view(view, offset, + event_comm->userspace_probe_location_len); + + if (event_comm->userspace_probe_location_len == 0) { + ret = -1; + goto end; + } + + if (!lttng_payload_view_is_valid( + &userspace_probe_location_view)) { + ret = -1; + goto end; + } + + ret = lttng_userspace_probe_location_create_from_payload( + &userspace_probe_location_view, + &local_userspace_probe_location); + if (ret < 0) { + WARN("Failed to create a userspace probe location from the received buffer"); + ret = -1; + goto end; + } + + if (ret != event_comm->userspace_probe_location_len) { + WARN("Userspace probe location from the received buffer is not the advertised length: header length = %" PRIu32 ", payload length = %lu", event_comm->userspace_probe_location_len, ret); + ret = -1; + goto end; + } + + /* Attach the probe location to the event. */ + ret = lttng_event_set_userspace_probe_location( + local_event, local_userspace_probe_location); + if (ret) { + ret = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + /* + * Userspace probe location object ownership transfered to the + * event object. + */ + local_userspace_probe_location = NULL; + offset += event_comm->userspace_probe_location_len; + break; + } + case LTTNG_EVENT_TRACEPOINT: + /* Fallthrough */ + case LTTNG_EVENT_ALL: + /* Fallthrough */ + case LTTNG_EVENT_SYSCALL: + /* Fallthrough */ + case LTTNG_EVENT_NOOP: + /* Nothing to do here */ + break; + default: + ret = LTTNG_ERR_UND; + goto end; + break; + } + + /* Transfer ownership to the caller. */ + *out_event = local_event; + local_event = NULL; + + if (out_bytecode) { + *out_bytecode = local_bytecode; + local_bytecode = NULL; + } + + if (out_exclusion) { + *out_exclusion = local_exclusions; + local_exclusions = NULL; + } + + if (out_filter_expression) { + *out_filter_expression = local_filter_expression; + local_filter_expression = NULL; + } + + ret = offset; +end: + lttng_event_destroy(local_event); + lttng_userspace_probe_location_destroy(local_userspace_probe_location); + free(local_filter_expression); + free(local_exclusions); + free(local_bytecode); + free(local_function_attr); + free(local_probe_attr); + return ret; +} + +int lttng_event_serialize(const struct lttng_event *event, + unsigned int exclusion_count, + char **exclusion_list, + char *filter_expression, + size_t bytecode_len, + struct lttng_bytecode *bytecode, + struct lttng_payload *payload) +{ + int ret; + unsigned int i; + size_t header_offset, size_before_payload; + size_t name_len; + struct lttng_event_comm event_comm = { 0 }; + struct lttng_event_comm *header; + + assert(event); + assert(payload); + assert(exclusion_count == 0 || exclusion_list); + + /* Save the header location for later in-place header update. */ + header_offset = payload->buffer.size; + + name_len = lttng_strnlen(event->name, LTTNG_SYMBOL_NAME_LEN); + if (name_len == LTTNG_SYMBOL_NAME_LEN) { + /* Event name is not NULL-terminated. */ + ret = -1; + goto end; + } + + /* Add null termination. */ + name_len += 1; + + if (exclusion_count > UINT32_MAX) { + /* Possible overflow. */ + ret = -1; + goto end; + } + + if (bytecode_len > UINT32_MAX) { + /* Possible overflow. */ + ret = -1; + goto end; + } + + event_comm.name_len = (uint32_t) name_len; + event_comm.event_type = (int8_t) event->type; + event_comm.loglevel_type = (int8_t) event->loglevel_type; + event_comm.loglevel = (int32_t) event->loglevel; + event_comm.enabled = (int8_t) event->enabled; + event_comm.pid = (int32_t) event->pid; + event_comm.exclusion_count = (uint32_t) exclusion_count; + event_comm.bytecode_len = (uint32_t) bytecode_len; + event_comm.flags = (int32_t) event->flags; + + if (filter_expression) { + event_comm.filter_expression_len = + strlen(filter_expression) + 1; + } + + /* Header */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &event_comm, sizeof(event_comm)); + if (ret) { + goto end; + } + + /* Event name */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, event->name, name_len); + if (ret) { + goto end; + } + + /* Exclusions */ + for (i = 0; i < exclusion_count; i++) { + const size_t exclusion_len = lttng_strnlen( + *(exclusion_list + i), LTTNG_SYMBOL_NAME_LEN); + const struct lttng_event_exclusion_comm exclusion_header = { + .len = (uint32_t) exclusion_len + 1, + }; + + if (exclusion_len == LTTNG_SYMBOL_NAME_LEN) { + /* Exclusion is not NULL-terminated. */ + ret = -1; + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + &exclusion_header, sizeof(exclusion_header)); + if (ret) { + goto end; + } + + ret = lttng_dynamic_buffer_append(&payload->buffer, + *(exclusion_list + i), exclusion_len + 1); + if (ret) { + goto end; + } + } + + /* Filter expression and its bytecode */ + if (filter_expression) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + filter_expression, + event_comm.filter_expression_len); + if (ret) { + goto end; + } + + /* + * Bytecode can be absent when we serialize to the client + * for listing. + */ + if (bytecode) { + ret = lttng_dynamic_buffer_append(&payload->buffer, + bytecode, bytecode_len); + if (ret) { + goto end; + } + } + } + + size_before_payload = payload->buffer.size; + + /* Event type specific payload */ + switch (event->type) { + case LTTNG_EVENT_FUNCTION: + /* Fallthrough */ + case LTTNG_EVENT_PROBE: + ret = lttng_event_probe_attr_serialize(&event->attr.probe, payload); + if (ret) { + ret = -1; + goto end; + } + + header = (struct lttng_event_comm *) ((char *) payload->buffer.data + + header_offset); + header->lttng_event_probe_attr_len = + payload->buffer.size - size_before_payload; + + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + ret = lttng_event_function_attr_serialize( + &event->attr.ftrace, payload); + if (ret) { + ret = -1; + goto end; + } + + /* Update the lttng_event_function_attr len. */ + header = (struct lttng_event_comm *) ((char *) payload->buffer.data + + header_offset); + header->lttng_event_function_attr_len = + payload->buffer.size - size_before_payload; + + break; + case LTTNG_EVENT_USERSPACE_PROBE: + { + const struct lttng_event_extended *ev_ext = + (const struct lttng_event_extended *) + event->extended.ptr; + + assert(event->extended.ptr); + assert(ev_ext->probe_location); + + size_before_payload = payload->buffer.size; + if (ev_ext->probe_location) { + /* + * lttng_userspace_probe_location_serialize returns the + * number of bytes that were appended to the buffer. + */ + ret = lttng_userspace_probe_location_serialize( + ev_ext->probe_location, payload); + if (ret < 0) { + goto end; + } + + ret = 0; + + /* Update the userspace probe location len. */ + header = (struct lttng_event_comm *) ((char *) payload->buffer.data + + header_offset); + header->userspace_probe_location_len = + payload->buffer.size - size_before_payload; + } + break; + } + case LTTNG_EVENT_TRACEPOINT: + /* Fallthrough */ + case LTTNG_EVENT_ALL: + /* Fallthrough */ + default: + /* Nothing to do here. */ + break; + } + +end: + return ret; +} + +static enum lttng_error_code compute_flattened_size( + struct lttng_dynamic_pointer_array *events, size_t *size) +{ + enum lttng_error_code ret_code; + int ret = 0; + size_t storage_req, event_count, i; + + assert(size); + assert(events); + + event_count = lttng_dynamic_pointer_array_get_count(events); + + /* The basic struct lttng_event */ + storage_req = event_count * sizeof(struct lttng_event); + + for (i = 0; i < event_count; i++) { + int probe_storage_req = 0; + const struct event_list_element *element = (const struct event_list_element *) + lttng_dynamic_pointer_array_get_pointer( + events, i); + const struct lttng_userspace_probe_location *location = NULL; + + location = lttng_event_get_userspace_probe_location( + element->event); + if (location) { + ret = lttng_userspace_probe_location_flatten( + location, NULL); + if (ret < 0) { + ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + + probe_storage_req = ret; + } + + /* The struct·lttng_event_extended */ + storage_req += event_count * + sizeof(struct lttng_event_extended); + + if (element->filter_expression) { + storage_req += strlen(element->filter_expression) + 1; + } + + if (element->exclusions) { + storage_req += element->exclusions->count * + LTTNG_SYMBOL_NAME_LEN; + } + + /* Padding to ensure the flat probe is aligned. */ + storage_req = ALIGN_TO(storage_req, sizeof(uint64_t)); + storage_req += probe_storage_req; + } + + *size = storage_req; + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +/* + * Flatten a list of struct lttng_event. + * + * The buffer that is returned to the API client must contain a "flat" version + * of the events that are returned. In other words, all pointers within an + * lttng_event must point to a location within the returned buffer so that the + * user may free everything by simply calling free() on the returned buffer. + * This is needed in order to maintain API compatibility. + * + * A first pass is performed to compute the size of the buffer that must be + * allocated. A second pass is then performed to setup the returned events so + * that their members always point within the buffer. + * + * The layout of the returned buffer is as follows: + * - struct lttng_event[nb_events], + * - nb_events times the following: + * - struct lttng_event_extended, + * - filter_expression + * - exclusions + * - padding to align to 64-bits + * - flattened version of userspace_probe_location + */ +static enum lttng_error_code flatten_lttng_events( + struct lttng_dynamic_pointer_array *events, + struct lttng_event **flattened_events) +{ + enum lttng_error_code ret_code; + int ret, i; + size_t storage_req; + struct lttng_dynamic_buffer local_flattened_events; + int nb_events; + + assert(events); + assert(flattened_events); + + lttng_dynamic_buffer_init(&local_flattened_events); + nb_events = lttng_dynamic_pointer_array_get_count(events); + + ret_code = compute_flattened_size(events, &storage_req); + if (ret_code != LTTNG_OK) { + goto end; + } + + /* + * We must ensure that "local_flattened_events" is never resized so as + * to preserve the validity of the flattened objects. + */ + ret = lttng_dynamic_buffer_set_capacity( + &local_flattened_events, storage_req); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + /* Start by laying the struct lttng_event */ + for (i = 0; i < nb_events; i++) { + const struct event_list_element *element = (const struct event_list_element *) + lttng_dynamic_pointer_array_get_pointer( + events, i); + + if (!element) { + ret_code = LTTNG_ERR_FATAL; + goto end; + } + + ret = lttng_dynamic_buffer_append(&local_flattened_events, + element->event, sizeof(struct lttng_event)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + for (i = 0; i < nb_events; i++) { + const struct event_list_element *element = (const struct event_list_element *) + lttng_dynamic_pointer_array_get_pointer(events, i); + struct lttng_event *event = (struct lttng_event *) + (local_flattened_events.data + (sizeof(struct lttng_event) * i)); + struct lttng_event_extended *event_extended = + (struct lttng_event_extended *) + (local_flattened_events.data + local_flattened_events.size); + const struct lttng_userspace_probe_location *location = NULL; + + assert(element); + + /* Insert struct lttng_event_extended. */ + ret = lttng_dynamic_buffer_set_size(&local_flattened_events, + local_flattened_events.size + + sizeof(*event_extended)); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + event->extended.ptr = event_extended; + + /* Insert filter expression. */ + if (element->filter_expression) { + const size_t len = strlen(element->filter_expression) + 1; + + event_extended->filter_expression = + local_flattened_events.data + + local_flattened_events.size; + ret = lttng_dynamic_buffer_append( + &local_flattened_events, + element->filter_expression, len); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + /* Insert exclusions. */ + if (element->exclusions) { + event_extended->exclusions.count = + element->exclusions->count; + event_extended->exclusions.strings = + local_flattened_events.data + + local_flattened_events.size; + + ret = lttng_dynamic_buffer_append( + &local_flattened_events, + element->exclusions->names, + element->exclusions->count * + LTTNG_SYMBOL_NAME_LEN); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + } + + /* Insert padding to align to 64-bits. */ + ret = lttng_dynamic_buffer_set_size(&local_flattened_events, + ALIGN_TO(local_flattened_events.size, + sizeof(uint64_t))); + if (ret) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + location = lttng_event_get_userspace_probe_location( + element->event); + if (location) { + event_extended->probe_location = (struct lttng_userspace_probe_location *) + (local_flattened_events.data + local_flattened_events.size); + ret = lttng_userspace_probe_location_flatten( + location, &local_flattened_events); + if (ret < 0) { + ret_code = LTTNG_ERR_PROBE_LOCATION_INVAL; + goto end; + } + } + } + + /* Don't reset local_flattened_events buffer as we return its content. */ + *flattened_events = (struct lttng_event *) local_flattened_events.data; + lttng_dynamic_buffer_init(&local_flattened_events); + ret_code = LTTNG_OK; +end: + lttng_dynamic_buffer_reset(&local_flattened_events); + return ret_code; +} + +static enum lttng_error_code event_list_create_from_payload( + struct lttng_payload_view *view, + unsigned int count, + struct lttng_dynamic_pointer_array *event_list) +{ + enum lttng_error_code ret_code; + int ret; + unsigned int i; + int offset = 0; + + assert(view); + assert(event_list); + + for (i = 0; i < count; i++) { + ssize_t event_size; + struct lttng_payload_view event_view = + lttng_payload_view_from_view(view, offset, -1); + struct event_list_element *element = + (struct event_list_element *) zmalloc(sizeof(*element)); + + if (!element) { + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + /* + * Lifetime and management of the object is now bound to the + * array. + */ + ret = lttng_dynamic_pointer_array_add_pointer( + event_list, element); + if (ret) { + event_list_destructor(element); + ret_code = LTTNG_ERR_NOMEM; + goto end; + } + + /* + * Bytecode is not transmitted on listing in any case we do not + * care about it. + */ + event_size = lttng_event_create_from_payload(&event_view, + &element->event, + &element->exclusions, + &element->filter_expression, NULL); + if (event_size < 0) { + ret_code = LTTNG_ERR_INVALID; + goto end; + } + + offset += event_size; + } + + if (view->buffer.size != offset) { + ret_code = LTTNG_ERR_INVALID_PROTOCOL; + goto end; + } + + ret_code = LTTNG_OK; + +end: + return ret_code; +} + +enum lttng_error_code lttng_events_create_and_flatten_from_payload( + struct lttng_payload_view *payload, + unsigned int count, + struct lttng_event **events) +{ + enum lttng_error_code ret = LTTNG_OK; + struct lttng_dynamic_pointer_array local_events; + + lttng_dynamic_pointer_array_init(&local_events, event_list_destructor); + + /* Deserialize the events. */ + { + struct lttng_payload_view events_view = + lttng_payload_view_from_view(payload, 0, -1); + + ret = event_list_create_from_payload( + &events_view, count, &local_events); + if (ret != LTTNG_OK) { + goto end; + } + } + + ret = flatten_lttng_events(&local_events, events); + if (ret != LTTNG_OK) { + goto end; + } + +end: + lttng_dynamic_pointer_array_reset(&local_events); + return ret; +} diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index a4ea96315..b145aeb06 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -380,36 +380,11 @@ struct lttcomm_session_msg { /* Event data */ struct { char channel_name[LTTNG_SYMBOL_NAME_LEN]; - struct lttng_event event; - /* Length of following filter expression. */ - uint32_t expression_len; - /* Length of following bytecode for filter. */ - uint32_t bytecode_len; - /* Exclusion count (fixed-size strings). */ - uint32_t exclusion_count; - /* Userspace probe location size. */ - uint32_t userspace_probe_location_len; - /* - * After this structure, the following variable-length - * items are transmitted: - * - char exclusion_names[LTTNG_SYMBOL_NAME_LEN][exclusion_count] - * - char filter_expression[expression_len] - * - unsigned char filter_bytecode[bytecode_len] - */ + uint32_t length; } LTTNG_PACKED enable; struct { char channel_name[LTTNG_SYMBOL_NAME_LEN]; - struct lttng_event event; - /* Length of following filter expression. */ - uint32_t expression_len; - /* Length of following bytecode for filter. */ - uint32_t bytecode_len; - /* - * After this structure, the following variable-length - * items are transmitted: - * - unsigned char filter_expression[expression_len] - * - unsigned char filter_bytecode[bytecode_len] - */ + uint32_t length; } LTTNG_PACKED disable; /* Create channel */ struct { @@ -551,14 +526,6 @@ struct lttng_event_exclusion { #define LTTNG_EVENT_EXCLUSION_NAME_AT(_exclusion, _i) \ (&(_exclusion)->names[_i][0]) -/* - * Event command header. - */ -struct lttcomm_event_command_header { - /* Number of events */ - uint32_t nb_events; -} LTTNG_PACKED; - /* * Listing command header. */ diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 92593cfac..fc1d272bf 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -1105,11 +1105,12 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, const char *original_filter_expression, int exclusion_count, char **exclusion_list) { - struct lttcomm_session_msg lsm; + struct lttcomm_session_msg lsm = { .cmd_type = LTTNG_ENABLE_EVENT }; struct lttng_payload payload; - int ret = 0, i; + int ret = 0; unsigned int free_filter_expression = 0; struct filter_parser_ctx *ctx = NULL; + size_t bytecode_len = 0; /* * We have either a filter or some exclusions, so we need to set up @@ -1139,36 +1140,12 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, goto error; } - memset(&lsm, 0, sizeof(lsm)); - - /* If no channel name, send empty string. */ - ret = lttng_strncpy(lsm.u.enable.channel_name, channel_name ?: "", - sizeof(lsm.u.enable.channel_name)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - - lsm.cmd_type = LTTNG_ENABLE_EVENT; if (ev->name[0] == '\0') { /* Enable all events. */ ret = lttng_strncpy(ev->name, "*", sizeof(ev->name)); assert(ret == 0); } - COPY_DOMAIN_PACKED(lsm.domain, handle->domain); - memcpy(&lsm.u.enable.event, ev, sizeof(lsm.u.enable.event)); - - ret = lttng_strncpy(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - - lsm.u.enable.exclusion_count = exclusion_count; - lsm.u.enable.bytecode_len = 0; - /* Parse filter expression. */ if (filter_expression != NULL || handle->domain.type == LTTNG_DOMAIN_JUL || handle->domain.type == LTTNG_DOMAIN_LOG4J @@ -1178,7 +1155,7 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, handle->domain.type == LTTNG_DOMAIN_PYTHON) { char *agent_filter; - /* Setup JUL filter if needed. */ + /* Setup agent filter if needed. */ agent_filter = set_agent_filter(filter_expression, ev); if (!agent_filter) { if (!filter_expression) { @@ -1186,7 +1163,7 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, * No JUL and no filter, just skip * everything below. */ - goto ask_sessiond; + goto serialize; } } else { /* @@ -1199,95 +1176,57 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, } } + if (strnlen(filter_expression, LTTNG_FILTER_MAX_LEN) == + LTTNG_FILTER_MAX_LEN) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto error; + } + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { - goto filter_error; + goto error; } - lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) - + bytecode_get_len(&ctx->bytecode->b); - lsm.u.enable.expression_len = strlen(filter_expression) + 1; + bytecode_len = bytecode_get_len(&ctx->bytecode->b) + + sizeof(ctx->bytecode->b); + if (bytecode_len > LTTNG_FILTER_MAX_LEN) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto error; + } } - ret = lttng_dynamic_buffer_set_capacity(&payload.buffer, - lsm.u.enable.bytecode_len + - lsm.u.enable.expression_len + - LTTNG_SYMBOL_NAME_LEN * - exclusion_count); +serialize: + ret = lttng_event_serialize(ev, exclusion_count, exclusion_list, + filter_expression, bytecode_len, + (ctx && bytecode_len) ? &ctx->bytecode->b : NULL, + &payload); if (ret) { - ret = -LTTNG_ERR_EXCLUSION_NOMEM; - goto mem_error; + ret = -LTTNG_ERR_INVALID; + goto error; } - /* Put exclusion names first in the data. */ - for (i = 0; i < exclusion_count; i++) { - size_t exclusion_len; - - exclusion_len = lttng_strnlen(exclusion_list[i], - LTTNG_SYMBOL_NAME_LEN); - if (exclusion_len == LTTNG_SYMBOL_NAME_LEN) { - /* Exclusion is not NULL-terminated. */ - ret = -LTTNG_ERR_INVALID; - goto mem_error; - } - - ret = lttng_dynamic_buffer_append(&payload.buffer, - exclusion_list[i], exclusion_len); - if (ret) { - goto mem_error; - } - - /* - * Padding the rest of the entry with zeros. Every exclusion - * entries take LTTNG_SYMBOL_NAME_LEN bytes in the buffer. - */ - ret = lttng_dynamic_buffer_set_size(&payload.buffer, - LTTNG_SYMBOL_NAME_LEN * (i + 1)); - if (ret) { - goto mem_error; - } + /* If no channel name, send empty string. */ + ret = lttng_strncpy(lsm.u.enable.channel_name, channel_name ?: "", + sizeof(lsm.u.enable.channel_name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto error; } - /* Add filter expression next. */ - if (filter_expression) { - ret = lttng_dynamic_buffer_append(&payload.buffer, - filter_expression, lsm.u.enable.expression_len); - if (ret) { - goto mem_error; - } - } - /* Add filter bytecode next. */ - if (ctx && lsm.u.enable.bytecode_len != 0) { - ret = lttng_dynamic_buffer_append(&payload.buffer, - &ctx->bytecode->b, lsm.u.enable.bytecode_len); - if (ret) { - goto mem_error; - } - } - if (ev->extended.ptr) { - struct lttng_event_extended *ev_ext = - (struct lttng_event_extended *) ev->extended.ptr; - - if (ev_ext->probe_location) { - /* - * lttng_userspace_probe_location_serialize returns the - * number of bytes that was appended to the buffer. - */ - ret = lttng_userspace_probe_location_serialize( - ev_ext->probe_location, &payload); - if (ret < 0) { - goto mem_error; - } + /* Domain */ + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); - /* - * Set the size of the userspace probe location element - * of the buffer so that the receiving side knows where - * to split it. - */ - lsm.u.enable.userspace_probe_location_len = ret; - } + /* Session name */ + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto error; } + /* Length of the serialized event. */ + lsm.u.enable.length = (uint32_t) payload.buffer.size; + { struct lttng_payload_view view = lttng_payload_view_from_payload( &payload, 0, -1); @@ -1295,7 +1234,7 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, int fd_to_send; if (fd_count < 0) { - goto mem_error; + goto error; } assert(fd_count == 0 || fd_count == 1); @@ -1304,26 +1243,27 @@ int lttng_enable_event_with_exclusions(struct lttng_handle *handle, lttng_payload_view_pop_fd_handle(&view); if (!h) { - goto mem_error; + goto error; } fd_to_send = fd_handle_get_fd(h); fd_handle_put(h); } + lsm.fd_count = fd_count; + ret = lttng_ctl_ask_sessiond_fds_varlen(&lsm, fd_count ? &fd_to_send : NULL, fd_count, view.buffer.size ? view.buffer.data : NULL, view.buffer.size, NULL, NULL, 0); } -mem_error: +error: if (filter_expression && ctx) { filter_bytecode_free(ctx); filter_ir_free(ctx); filter_parser_ctx_free(ctx); } -filter_error: if (free_filter_expression) { /* * The filter expression has been replaced and must be freed as @@ -1332,28 +1272,31 @@ filter_error: */ free(filter_expression); } -error: /* * Return directly to the caller and don't ask the sessiond since * something went wrong in the parsing of data above. */ lttng_payload_reset(&payload); return ret; - -ask_sessiond: - ret = lttng_ctl_ask_sessiond(&lsm, NULL); - return ret; } int lttng_disable_event_ext(struct lttng_handle *handle, struct lttng_event *ev, const char *channel_name, const char *original_filter_expression) { - struct lttcomm_session_msg lsm; - char *varlen_data; + struct lttcomm_session_msg lsm = { .cmd_type = LTTNG_DISABLE_EVENT }; + struct lttng_payload payload; int ret = 0; unsigned int free_filter_expression = 0; struct filter_parser_ctx *ctx = NULL; + size_t bytecode_len = 0; + + /* + * We have either a filter or some exclusions, so we need to set up + * a variable-length payload from where to send the data. + */ + lttng_payload_init(&payload); + /* * Cast as non-const since we may replace the filter expression * by a dynamically allocated string. Otherwise, the original @@ -1376,48 +1319,7 @@ int lttng_disable_event_ext(struct lttng_handle *handle, goto error; } - memset(&lsm, 0, sizeof(lsm)); - - /* If no channel name, send empty string. */ - ret = lttng_strncpy(lsm.u.disable.channel_name, channel_name ?: "", - sizeof(lsm.u.disable.channel_name)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - - lsm.cmd_type = LTTNG_DISABLE_EVENT; - - COPY_DOMAIN_PACKED(lsm.domain, handle->domain); - memcpy(&lsm.u.disable.event, ev, sizeof(lsm.u.disable.event)); - - ret = lttng_strncpy(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - if (ret) { - ret = -LTTNG_ERR_INVALID; - goto error; - } - - lsm.u.disable.bytecode_len = 0; - - /* - * For the JUL domain, a filter is enforced except for the - * disable all event. This is done to avoid having the event in - * all sessions thus filtering by logger name. - */ - if (filter_expression == NULL && - (handle->domain.type != LTTNG_DOMAIN_JUL && - handle->domain.type != LTTNG_DOMAIN_LOG4J && - handle->domain.type != LTTNG_DOMAIN_PYTHON)) { - goto ask_sessiond; - } - - /* - * We have a filter, so we need to set up a variable-length - * memory block from where to send the data. - */ - - /* Parse filter expression */ + /* Parse filter expression. */ if (filter_expression != NULL || handle->domain.type == LTTNG_DOMAIN_JUL || handle->domain.type == LTTNG_DOMAIN_LOG4J || handle->domain.type == LTTNG_DOMAIN_PYTHON) { @@ -1426,7 +1328,7 @@ int lttng_disable_event_ext(struct lttng_handle *handle, handle->domain.type == LTTNG_DOMAIN_PYTHON) { char *agent_filter; - /* Setup JUL filter if needed. */ + /* Setup agent filter if needed. */ agent_filter = set_agent_filter(filter_expression, ev); if (!agent_filter) { if (!filter_expression) { @@ -1434,11 +1336,11 @@ int lttng_disable_event_ext(struct lttng_handle *handle, * No JUL and no filter, just skip * everything below. */ - goto ask_sessiond; + goto serialize; } } else { /* - * With a JUL filter, the original filter has + * With an agent filter, the original filter has * been added to it thus replace the filter * expression. */ @@ -1447,48 +1349,92 @@ int lttng_disable_event_ext(struct lttng_handle *handle, } } + if (strnlen(filter_expression, LTTNG_FILTER_MAX_LEN) == + LTTNG_FILTER_MAX_LEN) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto error; + } + ret = filter_parser_ctx_create_from_filter_expression(filter_expression, &ctx); if (ret) { - goto filter_error; + goto error; } - lsm.u.enable.bytecode_len = sizeof(ctx->bytecode->b) - + bytecode_get_len(&ctx->bytecode->b); - lsm.u.enable.expression_len = strlen(filter_expression) + 1; + bytecode_len = bytecode_get_len(&ctx->bytecode->b) + + sizeof(ctx->bytecode->b); + if (bytecode_len > LTTNG_FILTER_MAX_LEN) { + ret = -LTTNG_ERR_FILTER_INVAL; + goto error; + } } - varlen_data = zmalloc(lsm.u.disable.bytecode_len - + lsm.u.disable.expression_len); - if (!varlen_data) { - ret = -LTTNG_ERR_EXCLUSION_NOMEM; - goto mem_error; +serialize: + ret = lttng_event_serialize(ev, 0, NULL, + filter_expression, bytecode_len, + (ctx && bytecode_len) ? &ctx->bytecode->b : NULL, + &payload); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto error; } - /* Add filter expression. */ - if (lsm.u.disable.expression_len != 0) { - memcpy(varlen_data, - filter_expression, - lsm.u.disable.expression_len); + /* If no channel name, send empty string. */ + ret = lttng_strncpy(lsm.u.disable.channel_name, channel_name ?: "", + sizeof(lsm.u.disable.channel_name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto error; } - /* Add filter bytecode next. */ - if (ctx && lsm.u.disable.bytecode_len != 0) { - memcpy(varlen_data - + lsm.u.disable.expression_len, - &ctx->bytecode->b, - lsm.u.disable.bytecode_len); + + /* Domain */ + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + + /* Session name */ + ret = lttng_strncpy(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + if (ret) { + ret = -LTTNG_ERR_INVALID; + goto error; } - ret = lttng_ctl_ask_sessiond_varlen_no_cmd_header(&lsm, varlen_data, - lsm.u.disable.bytecode_len + lsm.u.disable.expression_len, NULL); - free(varlen_data); + /* Length of the serialized event. */ + lsm.u.disable.length = (uint32_t) payload.buffer.size; -mem_error: + { + struct lttng_payload_view view = lttng_payload_view_from_payload( + &payload, 0, -1); + int fd_count = lttng_payload_view_get_fd_handle_count(&view); + int fd_to_send; + + if (fd_count < 0) { + goto error; + } + + assert(fd_count == 0 || fd_count == 1); + if (fd_count == 1) { + struct fd_handle *h = + lttng_payload_view_pop_fd_handle(&view); + + if (!h) { + goto error; + } + + fd_to_send = fd_handle_get_fd(h); + fd_handle_put(h); + } + + ret = lttng_ctl_ask_sessiond_fds_varlen(&lsm, + fd_count ? &fd_to_send : NULL, fd_count, + view.buffer.size ? view.buffer.data : NULL, + view.buffer.size, NULL, NULL, 0); + } + +error: if (filter_expression && ctx) { filter_bytecode_free(ctx); filter_ir_free(ctx); filter_parser_ctx_free(ctx); } -filter_error: if (free_filter_expression) { /* * The filter expression has been replaced and must be freed as @@ -1497,15 +1443,11 @@ filter_error: */ free(filter_expression); } -error: /* * Return directly to the caller and don't ask the sessiond since * something went wrong in the parsing of data above. */ - return ret; - -ask_sessiond: - ret = lttng_ctl_ask_sessiond(&lsm, NULL); + lttng_payload_reset(&payload); return ret; } @@ -1726,23 +1668,64 @@ end: int lttng_list_tracepoints(struct lttng_handle *handle, struct lttng_event **events) { - int ret; - struct lttcomm_session_msg lsm; + enum lttng_error_code ret_code; + int ret, total_payload_received; + char *reception_buffer = NULL; + struct lttcomm_session_msg lsm = { .cmd_type = LTTNG_LIST_TRACEPOINTS }; + struct lttcomm_list_command_header *cmd_header = NULL; + size_t cmd_header_len; + unsigned int nb_events = 0; - if (handle == NULL) { - return -LTTNG_ERR_INVALID; - } + if (handle == NULL) { + ret = -LTTNG_ERR_INVALID; + goto end; + } - memset(&lsm, 0, sizeof(lsm)); - lsm.cmd_type = LTTNG_LIST_TRACEPOINTS; - COPY_DOMAIN_PACKED(lsm.domain, handle->domain); + COPY_DOMAIN_PACKED(lsm.domain, handle->domain); - ret = lttng_ctl_ask_sessiond(&lsm, (void **) events); - if (ret < 0) { - return ret; + ret = lttng_ctl_ask_sessiond_fds_varlen(&lsm, NULL, 0, NULL, 0, + (void **) &reception_buffer, (void **) &cmd_header, + &cmd_header_len); + if (ret < 0) { + goto end; + } + + total_payload_received = ret; + + if (!cmd_header) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (cmd_header->count > INT_MAX) { + ret = -LTTNG_ERR_OVERFLOW; + goto end; + } + + nb_events = (unsigned int) cmd_header->count; + + { + struct lttng_buffer_view events_view = + lttng_buffer_view_init(reception_buffer, 0, + total_payload_received); + struct lttng_payload_view events_payload_view = + lttng_payload_view_from_buffer_view( + &events_view, 0, -1); + + ret_code = lttng_events_create_and_flatten_from_payload( + &events_payload_view, nb_events, events); + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto end; + } } - return ret / sizeof(struct lttng_event); + ret = (int) nb_events; + +end: + free(cmd_header); + free(reception_buffer); + return ret; } /* @@ -1782,24 +1765,65 @@ int lttng_list_tracepoint_fields(struct lttng_handle *handle, */ int lttng_list_syscalls(struct lttng_event **events) { - int ret; - struct lttcomm_session_msg lsm; + enum lttng_error_code ret_code; + int ret, total_payload_received; + char *reception_buffer = NULL; + struct lttcomm_session_msg lsm = {}; + struct lttcomm_list_command_header *cmd_header = NULL; + size_t cmd_header_len; + uint32_t nb_events = 0; + + if (!events) { + ret = -LTTNG_ERR_INVALID; + goto end; + } + + lsm.cmd_type = LTTNG_LIST_SYSCALLS; + /* Force kernel domain for system calls. */ + lsm.domain.type = LTTNG_DOMAIN_KERNEL; + + ret = lttng_ctl_ask_sessiond_fds_varlen(&lsm, NULL, 0, NULL, 0, + (void **) &reception_buffer, (void **) &cmd_header, + &cmd_header_len); + if (ret < 0) { + goto end; + } + total_payload_received = ret; + + if (!cmd_header) { + ret = -LTTNG_ERR_UNK; + goto end; + } + + if (cmd_header->count > INT_MAX) { + ret = -LTTNG_ERR_OVERFLOW; + goto end; + } + + nb_events = (unsigned int) cmd_header->count; + + { + const struct lttng_buffer_view events_view = + lttng_buffer_view_init(reception_buffer, 0, + total_payload_received); + struct lttng_payload_view events_payload_view = + lttng_payload_view_from_buffer_view( + &events_view, 0, -1); + + ret_code = lttng_events_create_and_flatten_from_payload( + &events_payload_view, nb_events, events); + if (ret_code != LTTNG_OK) { + ret = -ret_code; + goto end; + } + } + + ret = (int) nb_events; - if (!events) { - return -LTTNG_ERR_INVALID; - } - - memset(&lsm, 0, sizeof(lsm)); - lsm.cmd_type = LTTNG_LIST_SYSCALLS; - /* Force kernel domain for system calls. */ - lsm.domain.type = LTTNG_DOMAIN_KERNEL; - - ret = lttng_ctl_ask_sessiond(&lsm, (void **) events); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_event); +end: + free(reception_buffer); + free(cmd_header); + return ret; } /* @@ -2353,30 +2377,21 @@ int lttng_list_events(struct lttng_handle *handle, { int ret; struct lttcomm_session_msg lsm = {}; - const struct lttcomm_event_command_header *cmd_header = NULL; - uint32_t nb_events, i; - const void *comm_ext_at; - struct lttng_dynamic_buffer listing; - size_t storage_req; - struct lttng_payload payload; - struct lttng_payload payload_copy; + struct lttng_payload reply; struct lttng_payload_view lsm_view = lttng_payload_view_init_from_buffer( (const char *) &lsm, 0, sizeof(lsm)); - struct lttng_buffer_view cmd_header_view; - struct lttng_buffer_view cmd_payload_view; - struct lttng_buffer_view flat_events_view; - struct lttng_buffer_view ext_view; + unsigned int nb_events = 0; - /* Safety check. An handle and channel name are mandatory */ + /* Safety check. An handle and channel name are mandatory. */ if (handle == NULL || channel_name == NULL) { ret = -LTTNG_ERR_INVALID; goto end; } - lttng_payload_init(&payload); - lttng_payload_init(&payload_copy); + lttng_payload_init(&reply); + /* Initialize command parameters. */ lsm.cmd_type = LTTNG_LIST_EVENTS; ret = lttng_strncpy(lsm.session.name, handle->session_name, sizeof(lsm.session.name)); @@ -2394,275 +2409,52 @@ int lttng_list_events(struct lttng_handle *handle, COPY_DOMAIN_PACKED(lsm.domain, handle->domain); - ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &payload); + /* Execute command against the session daemon. */ + ret = lttng_ctl_ask_sessiond_payload(&lsm_view, &reply); if (ret < 0) { goto end; } - /* - * A copy of the payload is performed since it will be - * consumed twice. Consuming the same payload twice is invalid since - * it will cause any received file descriptor to become "shared" - * between different instances of the resulting objects. - */ - ret = lttng_payload_copy(&payload, &payload_copy); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } - - cmd_header_view = lttng_buffer_view_from_dynamic_buffer( - &payload.buffer, 0, sizeof(*cmd_header)); - if (!lttng_buffer_view_is_valid(&cmd_header_view)) { - ret = -LTTNG_ERR_INVALID_PROTOCOL; - goto end; - } - - cmd_header = (typeof(cmd_header)) cmd_header_view.data; - - /* Set number of events and free command header */ - nb_events = cmd_header->nb_events; - if (nb_events > INT_MAX) { - ret = -LTTNG_ERR_OVERFLOW; - goto end; - } - - cmd_payload_view = lttng_buffer_view_from_dynamic_buffer( - &payload.buffer, sizeof(*cmd_header), -1); - - /* - * The buffer that is returned must contain a "flat" version of - * the events that are returned. In other words, all pointers - * within an lttng_event must point to a location within the returned - * buffer so that the user may free everything by simply calling free() - * on the returned buffer. This is needed in order to maintain API - * compatibility. - * - * A first pass is performed to compute the size of the buffer that - * must be allocated. A second pass is then performed to setup - * the returned events so that their members always point within the - * buffer. - * - * The layout of the returned buffer is as follows: - * - struct lttng_event[nb_events], - * - nb_events times the following: - * - struct lttng_event_extended, - * - flattened version of userspace_probe_location - * - filter_expression - * - exclusions - * - padding to align to 64-bits - */ - ext_view = lttng_buffer_view_from_view(&cmd_payload_view, - nb_events * sizeof(struct lttng_event), -1); - comm_ext_at = ext_view.data; - storage_req = nb_events * sizeof(struct lttng_event); { - struct lttng_payload_view payload_view = - lttng_payload_view_from_payload(&payload, 0, -1); - - for (i = 0; i < nb_events; i++) { - const struct lttcomm_event_extended_header *ext_comm = - (struct lttcomm_event_extended_header *) - comm_ext_at; - int probe_storage_req = 0; - - comm_ext_at += sizeof(*ext_comm); - comm_ext_at += ext_comm->filter_len; - comm_ext_at += ext_comm->nb_exclusions * - LTTNG_SYMBOL_NAME_LEN; - - if (ext_comm->userspace_probe_location_len) { - struct lttng_userspace_probe_location - *probe_location = NULL; - struct lttng_payload_view probe_location_view = lttng_payload_view_from_view( - &payload_view, - (const char *) comm_ext_at - - payload_view.buffer.data, - ext_comm->userspace_probe_location_len); - - if (!lttng_payload_view_is_valid(&probe_location_view)) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto end; - } + const struct lttcomm_list_command_header *cmd_reply_header = + NULL; + const struct lttng_payload_view cmd_reply_header_view = + lttng_payload_view_from_payload(&reply, 0, + sizeof(*cmd_reply_header)); - /* - * Create a temporary userspace probe location - * to determine the size needed by a "flattened" - * version of that same probe location. - */ - ret = lttng_userspace_probe_location_create_from_payload( - &probe_location_view, - &probe_location); - if (ret < 0) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto end; - } - - ret = lttng_userspace_probe_location_flatten( - probe_location, NULL); - lttng_userspace_probe_location_destroy( - probe_location); - if (ret < 0) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto end; - } - - probe_storage_req = ret; - comm_ext_at += ext_comm->userspace_probe_location_len; - } - - storage_req += sizeof(struct lttng_event_extended); - storage_req += ext_comm->filter_len; - storage_req += ext_comm->nb_exclusions * - LTTNG_SYMBOL_NAME_LEN; - /* Padding to ensure the flat probe is aligned. */ - storage_req = ALIGN_TO(storage_req, sizeof(uint64_t)); - storage_req += probe_storage_req; + if (!lttng_payload_view_is_valid(&cmd_reply_header_view)) { + ret = -LTTNG_ERR_INVALID_PROTOCOL; + goto end; } - } - lttng_dynamic_buffer_init(&listing); - /* - * We must ensure that "listing" is never resized so as to preserve - * the validity of the flattened objects. - */ - ret = lttng_dynamic_buffer_set_capacity(&listing, storage_req); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto end; - } + cmd_reply_header = (const struct lttcomm_list_command_header *) + cmd_reply_header_view.buffer + .data; + if (cmd_reply_header->count > INT_MAX) { + ret = -LTTNG_ERR_OVERFLOW; + goto end; + } - cmd_payload_view = lttng_buffer_view_from_dynamic_buffer( - &payload_copy.buffer, sizeof(*cmd_header), -1); - flat_events_view = lttng_buffer_view_from_view(&cmd_payload_view, 0, - nb_events * sizeof(struct lttng_event)); - ret = lttng_dynamic_buffer_append_view(&listing, &flat_events_view); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto free_dynamic_buffer; + nb_events = (unsigned int) cmd_reply_header->count; } - ext_view = lttng_buffer_view_from_view(&cmd_payload_view, - nb_events * sizeof(struct lttng_event), -1); - comm_ext_at = ext_view.data; - { - struct lttng_payload_view payload_copy_view = - lttng_payload_view_from_payload( - &payload_copy, 0, -1); - - for (i = 0; i < nb_events; i++) { - struct lttng_event *event = (typeof(event))( - listing.data + - (sizeof(struct lttng_event) * i)); - const struct lttcomm_event_extended_header *ext_comm = - (typeof(ext_comm)) comm_ext_at; - struct lttng_event_extended *event_extended = - (typeof(event_extended))(listing.data + - listing.size); - - /* Insert struct lttng_event_extended. */ - ret = lttng_dynamic_buffer_set_size(&listing, - listing.size + sizeof(*event_extended)); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto free_dynamic_buffer; - } - event->extended.ptr = event_extended; - - comm_ext_at += sizeof(*ext_comm); - - /* Insert filter expression. */ - if (ext_comm->filter_len) { - event_extended->filter_expression = - listing.data + listing.size; - ret = lttng_dynamic_buffer_append(&listing, - comm_ext_at, - ext_comm->filter_len); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto free_dynamic_buffer; - } - comm_ext_at += ext_comm->filter_len; - } - - /* Insert exclusions. */ - if (ext_comm->nb_exclusions) { - event_extended->exclusions.count = - ext_comm->nb_exclusions; - event_extended->exclusions.strings = - listing.data + listing.size; - - ret = lttng_dynamic_buffer_append(&listing, - comm_ext_at, - ext_comm->nb_exclusions * - LTTNG_SYMBOL_NAME_LEN); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto free_dynamic_buffer; - } - comm_ext_at += ext_comm->nb_exclusions * - LTTNG_SYMBOL_NAME_LEN; - } - - /* Insert padding to align to 64-bits. */ - ret = lttng_dynamic_buffer_set_size(&listing, - ALIGN_TO(listing.size, - sizeof(uint64_t))); - if (ret) { - ret = -LTTNG_ERR_NOMEM; - goto free_dynamic_buffer; - } - - /* Insert flattened userspace probe location. */ - if (ext_comm->userspace_probe_location_len) { - struct lttng_userspace_probe_location - *probe_location = NULL; - struct lttng_payload_view probe_location_view = lttng_payload_view_from_view( - &payload_copy_view, - (const char *) comm_ext_at - - payload_copy_view.buffer.data, - ext_comm->userspace_probe_location_len); - - if (!lttng_payload_view_is_valid(&probe_location_view)) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto free_dynamic_buffer; - } - - ret = lttng_userspace_probe_location_create_from_payload( - &probe_location_view, - &probe_location); - if (ret < 0) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto free_dynamic_buffer; - } - - event_extended->probe_location = (struct lttng_userspace_probe_location - *) (listing.data + - listing.size); - ret = lttng_userspace_probe_location_flatten( - probe_location, &listing); - lttng_userspace_probe_location_destroy( - probe_location); - if (ret < 0) { - ret = -LTTNG_ERR_PROBE_LOCATION_INVAL; - goto free_dynamic_buffer; - } + enum lttng_error_code ret_code; + struct lttng_payload_view cmd_reply_payload = lttng_payload_view_from_payload( + &reply, + sizeof(struct lttcomm_list_command_header), -1); - comm_ext_at += ext_comm->userspace_probe_location_len; - } + ret_code = lttng_events_create_and_flatten_from_payload( + &cmd_reply_payload, nb_events, events); + if (ret_code != LTTNG_OK) { + ret = -((int) ret_code); + goto end; } } - /* Don't reset listing buffer as we return its content. */ - *events = (struct lttng_event *) listing.data; - lttng_dynamic_buffer_init(&listing); ret = (int) nb_events; -free_dynamic_buffer: - lttng_dynamic_buffer_reset(&listing); end: - lttng_payload_reset(&payload); - lttng_payload_reset(&payload_copy); + lttng_payload_reset(&reply); return ret; }