From 757c48a28258e64fe251b6803ccdba7898590d70 Mon Sep 17 00:00:00 2001 From: Simon Marchi Date: Mon, 2 Dec 2019 15:20:38 -0500 Subject: [PATCH 1/1] actions: introduce snapshot session action MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This patch introduces the API for the "snapshot session" action. A snapshot session action is created using the lttng_action_snapshot_session_create function. It is mandatory to set a session name using lttng_action_snapshot_session_set_session_name before using the action in a trigger. It is possible, but optional, to provide a snapshot name with lttng_action_snapshot_session_set_snapshot_name. The patch adds the code for serializing the action and deserializing it on the sessiond side, but not the code for executing it. Change-Id: I2b76680d44bf69eb705f2a238fffef2519b82534 Signed-off-by: Simon Marchi Signed-off-by: Jérémie Galarneau --- include/Makefile.am | 2 + include/lttng/action/action.h | 1 + .../lttng/action/snapshot-session-internal.h | 29 ++ include/lttng/action/snapshot-session.h | 67 +++ include/lttng/lttng.h | 1 + include/lttng/snapshot-internal.h | 2 +- src/common/Makefile.am | 1 + src/common/actions/action.c | 7 + src/common/actions/snapshot-session.c | 422 ++++++++++++++++++ src/common/snapshot.c | 29 +- src/common/snapshot.h | 10 +- 11 files changed, 553 insertions(+), 18 deletions(-) create mode 100644 include/lttng/action/snapshot-session-internal.h create mode 100644 include/lttng/action/snapshot-session.h create mode 100644 src/common/actions/snapshot-session.c diff --git a/include/Makefile.am b/include/Makefile.am index 21efe4076..af89a3372 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -123,6 +123,7 @@ lttngactioninclude_HEADERS= \ lttng/action/action.h \ lttng/action/notify.h \ lttng/action/rotate-session.h \ + lttng/action/snapshot-session.h \ lttng/action/start-session.h \ lttng/action/stop-session.h @@ -148,6 +149,7 @@ noinst_HEADERS = \ lttng/action/action-internal.h \ lttng/action/notify-internal.h \ lttng/action/rotate-session-internal.h \ + lttng/action/snapshot-session-internal.h \ lttng/action/start-session-internal.h \ lttng/action/stop-session-internal.h \ lttng/condition/condition-internal.h \ diff --git a/include/lttng/action/action.h b/include/lttng/action/action.h index 03f2964f3..3cd2460f8 100644 --- a/include/lttng/action/action.h +++ b/include/lttng/action/action.h @@ -20,6 +20,7 @@ enum lttng_action_type { LTTNG_ACTION_TYPE_START_SESSION = 1, LTTNG_ACTION_TYPE_STOP_SESSION = 2, LTTNG_ACTION_TYPE_ROTATE_SESSION = 3, + LTTNG_ACTION_TYPE_SNAPSHOT_SESSION = 4, }; enum lttng_action_status { diff --git a/include/lttng/action/snapshot-session-internal.h b/include/lttng/action/snapshot-session-internal.h new file mode 100644 index 000000000..87fbfa4d5 --- /dev/null +++ b/include/lttng/action/snapshot-session-internal.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H +#define LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H + +#include + +#include + +struct lttng_action; +struct lttng_payload_view; + +/* + * Create a "snapshot session" action from a payload view. + * + * On success, return the number of bytes consumed from `view`, and the created + * action in `*action`. On failure, return -1. + */ +LTTNG_HIDDEN +extern ssize_t lttng_action_snapshot_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **action); + +#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_INTERNAL_H */ diff --git a/include/lttng/action/snapshot-session.h b/include/lttng/action/snapshot-session.h new file mode 100644 index 000000000..f8d7ee366 --- /dev/null +++ b/include/lttng/action/snapshot-session.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#ifndef LTTNG_ACTION_SNAPSHOT_SESSION_H +#define LTTNG_ACTION_SNAPSHOT_SESSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct lttng_action; +struct lttng_snapshot_output; + +/* + * Create a newly allocated snapshot-session action object. + * + * A snapshot session action object must have a session name set to be + * considered valid when used with a trigger object (lttng_trigger). A name can + * be set using `lttng_action_snapshot_session_set_session_name`. + * + * Returns a new action on success, NULL on failure. This action must be + * destroyed using lttng_action_destroy(). + */ +extern struct lttng_action *lttng_action_snapshot_session_create(void); + +/* + * Set the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION. + */ +extern enum lttng_action_status lttng_action_snapshot_session_set_session_name( + struct lttng_action *action, const char *session_name); + +/* + * Get the session name of an lttng_action object of type + * LTTNG_ACTION_TYPE_SNAPSHOT_SESSION. + */ +extern enum lttng_action_status lttng_action_snapshot_session_get_session_name( + const struct lttng_action *action, const char **session_name); + +/* + * Set an explicit snapshot output for this snapshot session action. + * + * The given snapshot output will be used instead of the session's + * default snapshot output. + * + * This function takes ownership of the given snapshot output. + */ +extern enum lttng_action_status lttng_action_snapshot_session_set_output( + struct lttng_action *action, + struct lttng_snapshot_output *output); + +/* + * Get the explicit snapshot output for this snapshot session action. + */ +extern enum lttng_action_status lttng_action_snapshot_session_get_output( + const struct lttng_action *action, + const struct lttng_snapshot_output **output); + +#ifdef __cplusplus +} +#endif + +#endif /* LTTNG_ACTION_SNAPSHOT_SESSION_H */ diff --git a/include/lttng/lttng.h b/include/lttng/lttng.h index b3465cb93..f22fad553 100644 --- a/include/lttng/lttng.h +++ b/include/lttng/lttng.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/include/lttng/snapshot-internal.h b/include/lttng/snapshot-internal.h index 72d492237..8012a1e4e 100644 --- a/include/lttng/snapshot-internal.h +++ b/include/lttng/snapshot-internal.h @@ -50,7 +50,7 @@ struct lttng_snapshot_output_list { size_t count; /* - * Containes snapshot output object. + * Contains snapshot output object. */ struct lttng_snapshot_output *array; }; diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 5b186ce1f..f8a207534 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -30,6 +30,7 @@ libcommon_la_SOURCES = \ actions/action.c \ actions/notify.c \ actions/rotate-session.c \ + actions/snapshot-session.c \ actions/start-session.c \ actions/stop-session.c \ buffer-usage.c \ diff --git a/src/common/actions/action.c b/src/common/actions/action.c index c6b57b9cf..7325a532c 100644 --- a/src/common/actions/action.c +++ b/src/common/actions/action.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,8 @@ static const char *lttng_action_type_string(enum lttng_action_type action_type) return "NOTIFY"; case LTTNG_ACTION_TYPE_ROTATE_SESSION: return "ROTATE_SESSION"; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + return "SNAPSHOT_SESSION"; case LTTNG_ACTION_TYPE_START_SESSION: return "START_SESSION"; case LTTNG_ACTION_TYPE_STOP_SESSION: @@ -139,6 +142,10 @@ ssize_t lttng_action_create_from_payload(struct lttng_payload_view *view, create_from_payload_cb = lttng_action_rotate_session_create_from_payload; break; + case LTTNG_ACTION_TYPE_SNAPSHOT_SESSION: + create_from_payload_cb = + lttng_action_snapshot_session_create_from_payload; + break; case LTTNG_ACTION_TYPE_START_SESSION: create_from_payload_cb = lttng_action_start_session_create_from_payload; diff --git a/src/common/actions/snapshot-session.c b/src/common/actions/snapshot-session.c new file mode 100644 index 000000000..9e8e43577 --- /dev/null +++ b/src/common/actions/snapshot-session.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2019 Simon Marchi + * + * SPDX-License-Identifier: LGPL-2.1-only + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IS_SNAPSHOT_SESSION_ACTION(action) \ + (lttng_action_get_type_const(action) == LTTNG_ACTION_TYPE_SNAPSHOT_SESSION) + +struct lttng_action_snapshot_session { + struct lttng_action parent; + + /* Owned by this. */ + char *session_name; + + /* + * When non-NULL, use this custom output when taking the snapshot, + * rather than the session's registered snapshot output. + * + * Owned by this. + */ + struct lttng_snapshot_output *output; +}; + +struct lttng_action_snapshot_session_comm { + /* All string lengths include the trailing \0. */ + uint32_t session_name_len; + uint32_t snapshot_output_len; + + /* + * Variable data (all strings are null-terminated): + * + * - session name string + * - snapshot output object + * + */ + char data[]; +} LTTNG_PACKED; + +static struct lttng_action_snapshot_session * +action_snapshot_session_from_action(struct lttng_action *action) +{ + assert(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static const struct lttng_action_snapshot_session * +action_snapshot_session_from_action_const(const struct lttng_action *action) +{ + assert(action); + + return container_of( + action, struct lttng_action_snapshot_session, parent); +} + +static bool lttng_action_snapshot_session_validate(struct lttng_action *action) +{ + bool valid = false; + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + /* A non-empty session name is mandatory. */ + if (!action_snapshot_session->session_name || + strlen(action_snapshot_session->session_name) == 0) { + goto end; + } + + if (action_snapshot_session->output && + !lttng_snapshot_output_validate(action_snapshot_session->output)) { + goto end; + } + + valid = true; +end: + return valid; +} + +static bool lttng_action_snapshot_session_is_equal( + const struct lttng_action *_a, const struct lttng_action *_b) +{ + bool is_equal = false; + const struct lttng_action_snapshot_session *a, *b; + + a = action_snapshot_session_from_action_const(_a); + b = action_snapshot_session_from_action_const(_b); + + /* Action is not valid if this is not true. */ + assert(a->session_name); + assert(b->session_name); + if (strcmp(a->session_name, b->session_name)) { + goto end; + } + + if (a->output && b->output && + !lttng_snapshot_output_is_equal(a->output, b->output)) { + goto end; + } else if (!!a->output != !!b->output) { + goto end; + } + + is_equal = true; +end: + return is_equal; +} + +static size_t serialize_strlen(const char *s) +{ + + size_t len = 0; + + if (s) { + len = strlen(s) + 1; + } + + return len; +} + +static int lttng_action_snapshot_session_serialize( + struct lttng_action *action, struct lttng_payload *payload) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + struct lttng_action_snapshot_session_comm comm = {}; + int ret; + size_t size_before_comm; + + assert(action); + assert(payload); + + size_before_comm = payload->buffer.size; + size_before_comm = size_before_comm + sizeof(comm); + + action_snapshot_session = action_snapshot_session_from_action(action); + comm.session_name_len = + serialize_strlen(action_snapshot_session->session_name); + + /* Add header. */ + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); + if (ret) { + goto end; + } + + assert(action_snapshot_session->session_name); + DBG("Serializing snapshot session action: session-name: %s", + action_snapshot_session->session_name); + + /* Add session name. */ + ret = lttng_dynamic_buffer_append(&payload->buffer, + action_snapshot_session->session_name, + comm.session_name_len); + if (ret) { + goto end; + } + + /* Serialize the snapshot output object, if any. */ + if (action_snapshot_session->output) { + const size_t size_before_output = payload->buffer.size; + struct lttng_action_snapshot_session_comm *comm_in_payload; + + ret = lttng_snapshot_output_serialize( + action_snapshot_session->output, + payload); + if (ret) { + goto end; + } + + /* Adjust action length in header. */ + comm_in_payload = (typeof(comm_in_payload))( + payload->buffer.data + size_before_comm); + comm_in_payload->snapshot_output_len = + payload->buffer.size - size_before_output; + } + +end: + return ret; +} + +static void lttng_action_snapshot_session_destroy(struct lttng_action *action) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + + if (!action) { + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + lttng_snapshot_output_destroy(action_snapshot_session->output); + free(action_snapshot_session); + +end: + return; +} + +ssize_t lttng_action_snapshot_session_create_from_payload( + struct lttng_payload_view *view, + struct lttng_action **p_action) +{ + ssize_t consumed_len; + const struct lttng_action_snapshot_session_comm *comm; + const char *variable_data; + struct lttng_action *action; + enum lttng_action_status status; + struct lttng_snapshot_output *snapshot_output = NULL; + + action = lttng_action_snapshot_session_create(); + if (!action) { + goto error; + } + + comm = (typeof(comm)) view->buffer.data; + variable_data = (const char *) &comm->data; + + consumed_len = sizeof(struct lttng_action_snapshot_session_comm); + + if (!lttng_buffer_view_contains_string( + &view->buffer, variable_data, comm->session_name_len)) { + goto error; + } + + status = lttng_action_snapshot_session_set_session_name( + action, variable_data); + if (status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + variable_data += comm->session_name_len; + consumed_len += comm->session_name_len; + + /* If there is a snapshot output object, deserialize it. */ + if (comm->snapshot_output_len > 0) { + ssize_t snapshot_output_consumed_len; + enum lttng_action_status action_status; + struct lttng_payload_view snapshot_output_buffer_view = + lttng_payload_view_from_view(view, consumed_len, + comm->snapshot_output_len); + + if (!snapshot_output_buffer_view.buffer.data) { + fprintf(stderr, "Failed to create buffer view for snapshot output.\n"); + goto error; + } + + snapshot_output_consumed_len = + lttng_snapshot_output_create_from_payload( + &snapshot_output_buffer_view, + &snapshot_output); + if (snapshot_output_consumed_len != comm->snapshot_output_len) { + fprintf(stderr, + "Failed to deserialize snapshot output object: " + "consumed-len: %zd, expected-len: %" PRIu32, + snapshot_output_consumed_len, + comm->snapshot_output_len); + goto error; + } + + action_status = lttng_action_snapshot_session_set_output( + action, snapshot_output); + if (action_status != LTTNG_ACTION_STATUS_OK) { + goto error; + } + + /* Ownership has been transferred to the action. */ + snapshot_output = NULL; + } + + variable_data += comm->snapshot_output_len; + consumed_len += comm->snapshot_output_len; + *p_action = action; + action = NULL; + + goto end; + +error: + consumed_len = -1; + +end: + lttng_action_snapshot_session_destroy(action); + lttng_snapshot_output_destroy(snapshot_output); + + return consumed_len; +} + +struct lttng_action *lttng_action_snapshot_session_create(void) +{ + struct lttng_action *action; + + action = zmalloc(sizeof(struct lttng_action_snapshot_session)); + if (!action) { + goto end; + } + + lttng_action_init(action, LTTNG_ACTION_TYPE_SNAPSHOT_SESSION, + lttng_action_snapshot_session_validate, + lttng_action_snapshot_session_serialize, + lttng_action_snapshot_session_is_equal, + lttng_action_snapshot_session_destroy); + +end: + return action; +} + +enum lttng_action_status lttng_action_snapshot_session_set_session_name( + struct lttng_action *action, const char *session_name) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name || + strlen(session_name) == 0) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + free(action_snapshot_session->session_name); + + action_snapshot_session->session_name = strdup(session_name); + if (!action_snapshot_session->session_name) { + status = LTTNG_ACTION_STATUS_ERROR; + goto end; + } + + status = LTTNG_ACTION_STATUS_OK; +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_session_name( + const struct lttng_action *action, const char **session_name) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !session_name) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->session_name) { + *session_name = action_snapshot_session->session_name; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_set_output( + struct lttng_action *action, + struct lttng_snapshot_output *output) +{ + struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action) || !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action(action); + + lttng_snapshot_output_destroy(action_snapshot_session->output); + action_snapshot_session->output = output; + + status = LTTNG_ACTION_STATUS_OK; + +end: + return status; +} + +enum lttng_action_status lttng_action_snapshot_session_get_output( + const struct lttng_action *action, + const struct lttng_snapshot_output **output) +{ + const struct lttng_action_snapshot_session *action_snapshot_session; + enum lttng_action_status status; + + if (!action || !IS_SNAPSHOT_SESSION_ACTION(action)|| !output) { + status = LTTNG_ACTION_STATUS_INVALID; + goto end; + } + + action_snapshot_session = action_snapshot_session_from_action_const(action); + + if (action_snapshot_session->output) { + *output = action_snapshot_session->output; + status = LTTNG_ACTION_STATUS_OK; + } else { + status = LTTNG_ACTION_STATUS_UNSET; + } + +end: + return status; +} diff --git a/src/common/snapshot.c b/src/common/snapshot.c index 9d1627fcb..c2efcc02b 100644 --- a/src/common/snapshot.c +++ b/src/common/snapshot.c @@ -5,8 +5,8 @@ * */ -#include -#include +#include +#include #include #include #include @@ -92,7 +92,7 @@ struct lttng_snapshot_output_comm { LTTNG_HIDDEN int lttng_snapshot_output_serialize( const struct lttng_snapshot_output *output, - struct lttng_dynamic_buffer *buf) + struct lttng_payload *payload) { struct lttng_snapshot_output_comm comm; int ret; @@ -105,17 +105,20 @@ int lttng_snapshot_output_serialize( goto end; } - ret = lttng_strncpy(comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url)); + ret = lttng_strncpy( + comm.ctrl_url, output->ctrl_url, sizeof(comm.ctrl_url)); if (ret) { goto end; } - ret = lttng_strncpy(comm.data_url, output->data_url, sizeof(comm.data_url)); + ret = lttng_strncpy( + comm.data_url, output->data_url, sizeof(comm.data_url)); if (ret) { goto end; } - ret = lttng_dynamic_buffer_append(buf, &comm, sizeof(comm)); + ret = lttng_dynamic_buffer_append( + &payload->buffer, &comm, sizeof(comm)); if (ret) { goto end; } @@ -125,15 +128,15 @@ end: } LTTNG_HIDDEN -ssize_t lttng_snapshot_output_create_from_buffer( - const struct lttng_buffer_view *view, +ssize_t lttng_snapshot_output_create_from_payload( + struct lttng_payload_view *view, struct lttng_snapshot_output **output_p) { const struct lttng_snapshot_output_comm *comm; struct lttng_snapshot_output *output = NULL; int ret; - if (view->size != sizeof(*comm)) { + if (view->buffer.size != sizeof(*comm)) { ret = -1; goto end; } @@ -144,7 +147,7 @@ ssize_t lttng_snapshot_output_create_from_buffer( goto end; } - comm = (const struct lttng_snapshot_output_comm *) view->data; + comm = (typeof(comm)) view->buffer.data; output->id = comm->id; output->max_size = comm->max_size; @@ -154,12 +157,14 @@ ssize_t lttng_snapshot_output_create_from_buffer( goto end; } - ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, sizeof(output->ctrl_url)); + ret = lttng_strncpy(output->ctrl_url, comm->ctrl_url, + sizeof(output->ctrl_url)); if (ret) { goto end; } - ret = lttng_strncpy(output->data_url, comm->data_url, sizeof(output->data_url)); + ret = lttng_strncpy(output->data_url, comm->data_url, + sizeof(output->data_url)); if (ret) { goto end; } diff --git a/src/common/snapshot.h b/src/common/snapshot.h index 95a63e13b..58e855767 100644 --- a/src/common/snapshot.h +++ b/src/common/snapshot.h @@ -12,8 +12,8 @@ #include -struct lttng_buffer_view; -struct lttng_dynamic_buffer; +struct lttng_payload_view; +struct lttng_payload; struct lttng_snapshot_output; LTTNG_HIDDEN @@ -27,11 +27,11 @@ bool lttng_snapshot_output_is_equal( LTTNG_HIDDEN int lttng_snapshot_output_serialize( const struct lttng_snapshot_output *output, - struct lttng_dynamic_buffer *buf); + struct lttng_payload *payload); LTTNG_HIDDEN -ssize_t lttng_snapshot_output_create_from_buffer( - const struct lttng_buffer_view *view, +ssize_t lttng_snapshot_output_create_from_payload( + struct lttng_payload_view *view, struct lttng_snapshot_output **output_p); #endif /* COMMON_SNAPSHOT_H */ -- 2.34.1