From f2c1f0d46e0ed64f57a4f3bc3a5d6c5b6d743b77 Mon Sep 17 00:00:00 2001 From: Mathieu Desnoyers Date: Wed, 18 Dec 2019 12:07:16 -0500 Subject: [PATCH] lttng-ctl: Expose sessiond cmd_clear_session command MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Co-developed-by: Jonathan Rajotte Signed-off-by: Jonathan Rajotte Signed-off-by: Mathieu Desnoyers Change-Id: Id9d169daaa1c20912a6c105b4473c3f9f2ba3b49 Signed-off-by: Jérémie Galarneau --- include/Makefile.am | 4 +- include/lttng/clear-handle.h | 89 ++++++ include/lttng/clear.h | 70 +++++ src/common/sessiond-comm/sessiond-comm.h | 3 +- src/lib/lttng-ctl/Makefile.am | 2 +- src/lib/lttng-ctl/clear.c | 347 +++++++++++++++++++++++ 6 files changed, 512 insertions(+), 3 deletions(-) create mode 100644 include/lttng/clear-handle.h create mode 100644 include/lttng/clear.h create mode 100644 src/lib/lttng-ctl/clear.c diff --git a/include/Makefile.am b/include/Makefile.am index 9f8bf2301..ab5aa5375 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -113,7 +113,9 @@ lttnginclude_HEADERS = \ lttng/location.h \ lttng/userspace-probe.h \ lttng/session-descriptor.h \ - lttng/destruction-handle.h + lttng/destruction-handle.h \ + lttng/clear.h \ + lttng/clear-handle.h lttngactioninclude_HEADERS= \ lttng/action/action.h \ diff --git a/include/lttng/clear-handle.h b/include/lttng/clear-handle.h new file mode 100644 index 000000000..ea9edd456 --- /dev/null +++ b/include/lttng/clear-handle.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * Copyright (C) 2019 - Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CLEAR_HANDLE_H +#define LTTNG_CLEAR_HANDLE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Handle used to represent a specific instance of session clear + * operation. + */ +struct lttng_clear_handle; + +/* + * Negative values indicate errors. Values >= 0 indicate success. + */ +enum lttng_clear_handle_status { + LTTNG_CLEAR_HANDLE_STATUS_ERROR = -2, + LTTNG_CLEAR_HANDLE_STATUS_INVALID = -1, + LTTNG_CLEAR_HANDLE_STATUS_OK = 0, + LTTNG_CLEAR_HANDLE_STATUS_COMPLETED = 1, + LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT = 2, +}; + +/* + * Destroy an lttng_clear_handle. + * The handle should be discarded after this call. + */ +extern void lttng_clear_handle_destroy(struct lttng_clear_handle *handle); + +/* + * Wait for a session clear operation to complete. + * + * A negative timeout_ms value can be used to wait indefinitely. + * + * Returns LTTNG_CLEAR_HANDLE_STATUS_COMPLETED if the session clear + * operation was completed. LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT is returned + * to indicate that the wait timed out. + * On error, one of the negative lttng_clear_handle_status is returned. + * + * Note: This function returning a success status does not mean that + * the clear operation itself succeeded; it indicates that the _wait_ + * operation completed successfully. + */ +extern enum lttng_clear_handle_status + lttng_clear_handle_wait_for_completion( + struct lttng_clear_handle *handle, int timeout_ms); + +/* + * Get the result of a session clear operation. + * + * This function must be used on a clear handle which was successfully waited + * on. + * + * Returns LTTNG_CLEAR_HANDLE_STATUS_OK if the result of the session + * clear operation could be obtained. Check the value of 'result' to + * determine if the session clear operation completed successfully or not. + * + * On error, one of the negative lttng_clear_handle_status is returned. + * Returns LTTNG_CLEAR_HANDLE_STATUS_INVALID if the clear operation + * was not waited-on using the handle or if the arguments of the function are + * invalid (e.g. NULL). + */ +extern enum lttng_clear_handle_status + lttng_clear_handle_get_result( + const struct lttng_clear_handle *handle, + enum lttng_error_code *result); + +#endif /* LTTNG_CLEAR_HANDLE_H */ diff --git a/include/lttng/clear.h b/include/lttng/clear.h new file mode 100644 index 000000000..68c0cd48f --- /dev/null +++ b/include/lttng/clear.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * Copyright (C) 2019 - Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LTTNG_CLEAR_H +#define LTTNG_CLEAR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_clear_handle; + +/* + * Clear a tracing session. + * + * Clear the data buffers and trace data. + * + * For sessions saving trace data to disk and streaming over the network to a + * relay daemon, the buffers content and existing stream files are cleared when + * the clear command is issued. + * + * For snapshot sessions (flight recorder), only the buffer content is cleared. + * Prior snapshots are individually recorded to disk, and are therefore + * untouched by this "clear" command. + * + * For live sessions streaming over network to a relay daemon, the buffers + * will be cleared and the files on the relay daemon side will be cleared as + * well. However, any active live trace viewer currently reading an existing + * trace packet will be able to proceed to read that packet entirely before + * skipping over cleared stream data. + * + * The clear command guarantees that no trace data produced before this function + * is called will be present in the resulting trace. + * + * Trace data produced between the moment this function is called and when it + * returns might be present in the resulting trace. + * + * Provides an lttng_clear_handle which can be used to wait for the completion + * of the session's clear. + * + * Return LTTNG_OK on success else a negative LTTng error code. The returned + * handle is owned by the caller and must be free'd using + * lttng_clear_handle_destroy(). + * + * Important error codes: + * LTTNG_ERR_CLEAR_RELAY_DISALLOWED + * LTTNG_ERR_CLEAR_NOT_AVAILABLE_RELAY + * LTTNG_ERR_CLEAR_FAIL_CONSUMER +*/ +extern enum lttng_error_code lttng_clear_session(const char *session_name, + struct lttng_clear_handle **handle); + +#endif /* LTTNG_CLEAR_H */ diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index 8c2a47538..cbb79821d 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -107,7 +107,8 @@ enum lttcomm_sessiond_command { LTTNG_ROTATION_GET_INFO = 46, LTTNG_ROTATION_SET_SCHEDULE = 47, LTTNG_SESSION_LIST_ROTATION_SCHEDULES = 48, - LTTNG_CREATE_SESSION_EXT = 49 + LTTNG_CREATE_SESSION_EXT = 49, + LTTNG_CLEAR_SESSION = 50, }; enum lttcomm_relayd_command { diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am index f79c26e04..bbf3d5b1e 100644 --- a/src/lib/lttng-ctl/Makefile.am +++ b/src/lib/lttng-ctl/Makefile.am @@ -6,7 +6,7 @@ lib_LTLIBRARIES = liblttng-ctl.la liblttng_ctl_la_SOURCES = lttng-ctl.c snapshot.c lttng-ctl-helper.h \ lttng-ctl-health.c save.c load.c deprecated-symbols.c \ - channel.c rotate.c event.c destruction-handle.c + channel.c rotate.c event.c destruction-handle.c clear.c liblttng_ctl_la_LDFLAGS = \ $(LT_NO_UNDEFINED) diff --git a/src/lib/lttng-ctl/clear.c b/src/lib/lttng-ctl/clear.c new file mode 100644 index 000000000..4219f441d --- /dev/null +++ b/src/lib/lttng-ctl/clear.c @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2019 - Jérémie Galarneau + * Copyright (C) 2019 - Mathieu Desnoyers + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License, version 2.1 only, + * as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _LGPL_SOURCE +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lttng-ctl-helper.h" + +enum communication_state { + COMMUNICATION_STATE_RECEIVE_LTTNG_MSG, + COMMUNICATION_STATE_RECEIVE_COMMAND_HEADER, + COMMUNICATION_STATE_END, + COMMUNICATION_STATE_ERROR, +}; + +struct lttng_clear_handle { + LTTNG_OPTIONAL(enum lttng_error_code) clear_return_code; + struct { + int socket; + struct lttng_poll_event events; + size_t bytes_left_to_receive; + enum communication_state state; + struct lttng_dynamic_buffer buffer; + LTTNG_OPTIONAL(size_t) data_size; + } communication; +}; + +void lttng_clear_handle_destroy(struct lttng_clear_handle *handle) +{ + int ret; + + if (!handle) { + return; + } + + if (handle->communication.socket >= 0) { + ret = close(handle->communication.socket); + if (ret) { + PERROR("Failed to close lttng-sessiond command socket"); + } + } + lttng_poll_clean(&handle->communication.events); + lttng_dynamic_buffer_reset(&handle->communication.buffer); + free(handle); +} + +static +struct lttng_clear_handle *lttng_clear_handle_create(int sessiond_socket) +{ + int ret; + struct lttng_clear_handle *handle = zmalloc(sizeof(*handle)); + + if (!handle) { + goto end; + } + lttng_dynamic_buffer_init(&handle->communication.buffer); + handle->communication.socket = sessiond_socket; + ret = lttng_poll_create(&handle->communication.events, 1, 0); + if (ret) { + goto error; + } + + ret = lttng_poll_add(&handle->communication.events, sessiond_socket, + LPOLLIN | LPOLLHUP | LPOLLRDHUP | LPOLLERR); + if (ret) { + goto error; + } + + handle->communication.bytes_left_to_receive = + sizeof(struct lttcomm_lttng_msg); + handle->communication.state = COMMUNICATION_STATE_RECEIVE_LTTNG_MSG; +end: + return handle; +error: + lttng_clear_handle_destroy(handle); + return NULL; +} + +static +int handle_state_transition(struct lttng_clear_handle *handle) +{ + int ret = 0; + + assert(handle->communication.bytes_left_to_receive == 0); + + switch (handle->communication.state) { + case COMMUNICATION_STATE_RECEIVE_LTTNG_MSG: + { + const struct lttcomm_lttng_msg *msg = + (typeof(msg)) handle->communication.buffer.data; + + LTTNG_OPTIONAL_SET(&handle->clear_return_code, + (enum lttng_error_code) msg->ret_code); + if (handle->clear_return_code.value != LTTNG_OK) { + handle->communication.state = COMMUNICATION_STATE_END; + break; + } else if (msg->cmd_header_size != 0 || msg->data_size != 0) { + handle->communication.state = COMMUNICATION_STATE_ERROR; + ret = -1; + break; + } + + handle->communication.state = COMMUNICATION_STATE_END; + handle->communication.bytes_left_to_receive = 0; + LTTNG_OPTIONAL_SET(&handle->communication.data_size, 0); + ret = lttng_dynamic_buffer_set_size( + &handle->communication.buffer, 0); + assert(!ret); + break; + } + default: + abort(); + } + + /* Clear reception buffer on state transition. */ + if (lttng_dynamic_buffer_set_size(&handle->communication.buffer, 0)) { + abort(); + } + return ret; +} + +static +int handle_incoming_data(struct lttng_clear_handle *handle) +{ + int ret; + ssize_t comm_ret; + const size_t original_buffer_size = handle->communication.buffer.size; + + /* Reserve space for reception. */ + ret = lttng_dynamic_buffer_set_size(&handle->communication.buffer, + original_buffer_size + handle->communication.bytes_left_to_receive); + if (ret) { + goto end; + } + + comm_ret = lttcomm_recv_unix_sock(handle->communication.socket, + handle->communication.buffer.data + original_buffer_size, + handle->communication.bytes_left_to_receive); + if (comm_ret <= 0) { + ret = -1; + goto end; + } + + handle->communication.bytes_left_to_receive -= comm_ret; + if (handle->communication.bytes_left_to_receive == 0) { + ret = handle_state_transition(handle); + } else { + ret = lttng_dynamic_buffer_set_size( + &handle->communication.buffer, + original_buffer_size + comm_ret); + } +end: + return ret; +} + +extern enum lttng_clear_handle_status + lttng_clear_handle_wait_for_completion( + struct lttng_clear_handle *handle, int timeout_ms) +{ + int ret; + enum lttng_clear_handle_status status; + unsigned long time_left_ms = 0; + const bool has_timeout = timeout_ms > 0; + struct timespec initial_time; + + if (handle->communication.state == COMMUNICATION_STATE_ERROR) { + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } else if (handle->communication.state == COMMUNICATION_STATE_END) { + status = LTTNG_CLEAR_HANDLE_STATUS_COMPLETED; + goto end; + } + if (has_timeout) { + ret = lttng_clock_gettime(CLOCK_MONOTONIC, &initial_time); + if (ret) { + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + time_left_ms = (unsigned long) timeout_ms; + } + + while (handle->communication.state != COMMUNICATION_STATE_END && + (time_left_ms || !has_timeout)) { + int ret; + uint32_t revents; + struct timespec current_time, diff; + unsigned long diff_ms; + + ret = lttng_poll_wait(&handle->communication.events, + has_timeout ? time_left_ms : -1); + if (ret == 0) { + /* timeout */ + break; + } else if (ret < 0) { + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + + /* The sessiond connection socket is the only monitored fd. */ + revents = LTTNG_POLL_GETEV(&handle->communication.events, 0); + if (revents & LPOLLIN) { + ret = handle_incoming_data(handle); + if (ret) { + handle->communication.state = + COMMUNICATION_STATE_ERROR; + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + } else { + handle->communication.state = COMMUNICATION_STATE_ERROR; + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + if (!has_timeout) { + continue; + } + + ret = lttng_clock_gettime(CLOCK_MONOTONIC, ¤t_time); + if (ret) { + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + diff = timespec_abs_diff(initial_time, current_time); + ret = timespec_to_ms(diff, &diff_ms); + if (ret) { + ERR("Failed to compute elapsed time while waiting for completion"); + status = LTTNG_CLEAR_HANDLE_STATUS_ERROR; + goto end; + } + DBG("%lums elapsed while waiting for session clear completion", + diff_ms); + diff_ms = max_t(unsigned long, diff_ms, 1); + diff_ms = min_t(unsigned long, diff_ms, time_left_ms); + time_left_ms -= diff_ms; + } + + status = handle->communication.state == COMMUNICATION_STATE_END ? + LTTNG_CLEAR_HANDLE_STATUS_COMPLETED : + LTTNG_CLEAR_HANDLE_STATUS_TIMEOUT; +end: + return status; +} + +extern enum lttng_clear_handle_status + lttng_clear_handle_get_result( + const struct lttng_clear_handle *handle, + enum lttng_error_code *result) +{ + enum lttng_clear_handle_status status = + LTTNG_CLEAR_HANDLE_STATUS_OK; + + if (!handle->clear_return_code.is_set) { + status = LTTNG_CLEAR_HANDLE_STATUS_INVALID; + goto end; + } + *result = handle->clear_return_code.value; +end: + return status; +} + +/* + * Clear the session + */ +enum lttng_error_code lttng_clear_session(const char *session_name, + struct lttng_clear_handle **_handle) +{ + enum lttng_error_code ret_code = LTTNG_OK; + struct lttng_clear_handle *handle = NULL; + struct lttcomm_session_msg lsm = { + .cmd_type = LTTNG_CLEAR_SESSION, + }; + int sessiond_socket = -1; + ssize_t comm_ret; + int ret; + + if (session_name == NULL) { + ret_code = LTTNG_ERR_INVALID; + goto error; + } + ret = lttng_strncpy(lsm.session.name, session_name, + sizeof(lsm.session.name)); + if (ret) { + ret_code = LTTNG_ERR_INVALID; + goto error; + } + ret = connect_sessiond(); + if (ret < 0) { + ret_code = LTTNG_ERR_NO_SESSIOND; + goto error; + } else { + sessiond_socket = ret; + } + handle = lttng_clear_handle_create(sessiond_socket); + if (!handle) { + ret_code = LTTNG_ERR_NOMEM; + goto error; + } + comm_ret = lttcomm_send_creds_unix_sock(sessiond_socket, &lsm, sizeof(lsm)); + if (comm_ret < 0) { + ret_code = LTTNG_ERR_FATAL; + goto error; + } + sessiond_socket = -1; + +error: + /* Transfer the handle to the caller. */ + if (_handle) { + *_handle = handle; + handle = NULL; + } + if (sessiond_socket >= 0) { + ret = close(sessiond_socket); + if (ret < 0) { + PERROR("Failed to close the LTTng session daemon connection socket"); + } + } + if (handle) { + lttng_clear_handle_destroy(handle); + } + return ret_code; +} -- 2.34.1