From d0b96690836f4b876096f3dc14801f8e25281a77 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 19 Feb 2013 15:17:09 -0500 Subject: [PATCH 1/1] Move UST registry into sessiond and implement notifiers Notify now comes from the UST tracer to register event in a new registry in the session daemon storing UST channel and events. This registry is used to send back event and channel IDs to the tracer and the session UUID. It will also be used later on to generate metadata which will completely remove metadata generation from the application. This introduces a new thread in the session daemon being the thread managing application notification using a new socket (notify socket). This thread is in ust-thread.c/.h and spawned at startup. Needs to be use in locked-step with LTTng-UST commit: "Move UST registry into sessiond and implement notifiers" Acked-by: Mathieu Desnoyers Signed-off-by: David Goulet --- src/bin/lttng-sessiond/Makefile.am | 6 +- src/bin/lttng-sessiond/lttng-sessiond.h | 10 + src/bin/lttng-sessiond/main.c | 314 +++++--- src/bin/lttng-sessiond/ust-app.c | 633 ++++++++++++---- src/bin/lttng-sessiond/ust-app.h | 103 ++- src/bin/lttng-sessiond/ust-clock.h | 83 +++ src/bin/lttng-sessiond/ust-consumer.c | 4 +- src/bin/lttng-sessiond/ust-metadata.c | 676 ++++++++++++++++++ src/bin/lttng-sessiond/ust-registry.c | 372 ++++++++++ src/bin/lttng-sessiond/ust-registry.h | 205 ++++++ src/bin/lttng-sessiond/ust-thread.c | 168 +++++ src/bin/lttng-sessiond/ust-thread.h | 34 + src/common/compat/uuid.h | 7 + src/common/consumer.c | 1 - src/common/defaults.h | 20 +- src/common/sessiond-comm/sessiond-comm.h | 26 +- .../filter/filter-visitor-generate-bytecode.c | 7 +- src/lib/lttng-ctl/lttng-ctl.c | 2 +- 18 files changed, 2367 insertions(+), 304 deletions(-) create mode 100644 src/bin/lttng-sessiond/ust-clock.h create mode 100644 src/bin/lttng-sessiond/ust-metadata.c create mode 100644 src/bin/lttng-sessiond/ust-registry.c create mode 100644 src/bin/lttng-sessiond/ust-registry.h create mode 100644 src/bin/lttng-sessiond/ust-thread.c create mode 100644 src/bin/lttng-sessiond/ust-thread.h diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am index 0964c9402..26a2d13a0 100644 --- a/src/bin/lttng-sessiond/Makefile.am +++ b/src/bin/lttng-sessiond/Makefile.am @@ -9,7 +9,7 @@ bin_PROGRAMS = lttng-sessiond lttng_sessiond_SOURCES = utils.c utils.h \ trace-kernel.c trace-kernel.h \ kernel.c kernel.h \ - ust-ctl.h ust-app.h trace-ust.h \ + ust-ctl.h ust-app.h trace-ust.h ust-thread.h \ context.c context.h \ channel.c channel.h \ event.c event.h \ @@ -26,7 +26,9 @@ lttng_sessiond_SOURCES = utils.c utils.h \ testpoint.h if HAVE_LIBLTTNG_UST_CTL -lttng_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h +lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-registry.h ust-app.c \ + ust-consumer.c ust-consumer.h ust-thread.c \ + ust-metadata.c ust-clock.h endif # Add main.c at the end for compile order diff --git a/src/bin/lttng-sessiond/lttng-sessiond.h b/src/bin/lttng-sessiond/lttng-sessiond.h index 63e9be998..9258f38a7 100644 --- a/src/bin/lttng-sessiond/lttng-sessiond.h +++ b/src/bin/lttng-sessiond/lttng-sessiond.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "session.h" @@ -64,4 +65,13 @@ struct ust_cmd_queue { struct cds_wfq_queue queue; }; +/* + * This pipe is used to inform the thread managing application notify + * communication that a command is queued and ready to be processed. + */ +extern int apps_cmd_notify_pipe[2]; + +int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size); +int sessiond_check_thread_quit_pipe(int fd, uint32_t events); + #endif /* _LTT_SESSIOND_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index e9529f1bf..2698fb46c 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -37,7 +37,6 @@ #include #include -#include #include #include #include @@ -61,14 +60,12 @@ #include "fd-limit.h" #include "health.h" #include "testpoint.h" +#include "ust-thread.h" #define CONSUMERD_FILE "lttng-consumerd" /* Const values */ -const char default_home_dir[] = DEFAULT_HOME_DIR; const char default_tracing_group[] = DEFAULT_TRACING_GROUP; -const char default_ust_sock_dir[] = DEFAULT_UST_SOCK_DIR; -const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE; const char *progname; const char *opt_tracing_group; @@ -149,8 +146,11 @@ static int thread_quit_pipe[2] = { -1, -1 }; */ static int apps_cmd_pipe[2] = { -1, -1 }; +int apps_cmd_notify_pipe[2] = { -1, -1 }; + /* Pthread, Mutexes and Semaphores */ static pthread_t apps_thread; +static pthread_t apps_notify_thread; static pthread_t reg_apps_thread; static pthread_t client_thread; static pthread_t kernel_thread; @@ -279,15 +279,11 @@ void setup_consumerd_path(void) /* * Create a poll set with O_CLOEXEC and add the thread quit pipe to the set. */ -static int create_thread_poll_set(struct lttng_poll_event *events, - unsigned int size) +int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size) { int ret; - if (events == NULL || size == 0) { - ret = -1; - goto error; - } + assert(events); ret = lttng_poll_create(events, size, LTTNG_CLOEXEC); if (ret < 0) { @@ -295,7 +291,7 @@ static int create_thread_poll_set(struct lttng_poll_event *events, } /* Add quit pipe */ - ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN); + ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN | LPOLLERR); if (ret < 0) { goto error; } @@ -311,7 +307,7 @@ error: * * Return 1 if it was triggered else 0; */ -static int check_thread_quit_pipe(int fd, uint32_t events) +int sessiond_check_thread_quit_pipe(int fd, uint32_t events) { if (fd == thread_quit_pipe[0] && (events & LPOLLIN)) { return 1; @@ -721,7 +717,7 @@ static void *thread_manage_kernel(void *data) /* Clean events object. We are about to populate it again. */ lttng_poll_clean(&events); - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error_poll_create; } @@ -771,7 +767,7 @@ static void *thread_manage_kernel(void *data) health_code_update(); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -870,7 +866,7 @@ static void *thread_manage_consumer(void *data) * Pass 2 as size here for the thread quit pipe and kconsumerd_err_sock. * Nothing more will be added to this poll set. */ - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error_poll; } @@ -917,7 +913,7 @@ restart: health_code_update(); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -1011,7 +1007,7 @@ restart_poll: health_code_update(); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -1093,7 +1089,6 @@ static void *thread_manage_apps(void *data) { int i, ret, pollfd, err = -1; uint32_t revents, nb_fd; - struct ust_command ust_cmd; struct lttng_poll_event events; DBG("[thread] Manage application started"); @@ -1109,7 +1104,7 @@ static void *thread_manage_apps(void *data) health_code_update(); - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error_poll_create; } @@ -1153,7 +1148,7 @@ static void *thread_manage_apps(void *data) health_code_update(); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -1165,11 +1160,13 @@ static void *thread_manage_apps(void *data) ERR("Apps command pipe error"); goto error; } else if (revents & LPOLLIN) { + int sock; + /* Empty pipe */ do { - ret = read(apps_cmd_pipe[0], &ust_cmd, sizeof(ust_cmd)); + ret = read(apps_cmd_pipe[0], &sock, sizeof(sock)); } while (ret < 0 && errno == EINTR); - if (ret < 0 || ret < sizeof(ust_cmd)) { + if (ret < 0 || ret < sizeof(sock)) { PERROR("read apps cmd pipe"); goto error; } @@ -1177,70 +1174,23 @@ static void *thread_manage_apps(void *data) health_code_update(); /* - * @session_lock - * Lock the global session list so from the register up to - * the registration done message, no thread can see the - * application and change its state. + * We only monitor the error events of the socket. This + * thread does not handle any incoming data from UST + * (POLLIN). */ - session_lock_list(); - - /* Register applicaton to the session daemon */ - ret = ust_app_register(&ust_cmd.reg_msg, - ust_cmd.sock); - if (ret == -ENOMEM) { - session_unlock_list(); + ret = lttng_poll_add(&events, sock, + LPOLLERR | LPOLLHUP | LPOLLRDHUP); + if (ret < 0) { goto error; - } else if (ret < 0) { - session_unlock_list(); - break; - } - - health_code_update(); - - /* - * Validate UST version compatibility. - */ - ret = ust_app_validate_version(ust_cmd.sock); - if (ret >= 0) { - /* - * Add channel(s) and event(s) to newly registered apps - * from lttng global UST domain. - */ - update_ust_app(ust_cmd.sock); } - health_code_update(); - - ret = ust_app_register_done(ust_cmd.sock); - if (ret < 0) { - /* - * If the registration is not possible, we simply - * unregister the apps and continue - */ - ust_app_unregister(ust_cmd.sock); - } else { - /* - * We only monitor the error events of the socket. This - * thread does not handle any incoming data from UST - * (POLLIN). - */ - ret = lttng_poll_add(&events, ust_cmd.sock, - LPOLLERR & LPOLLHUP & LPOLLRDHUP); - if (ret < 0) { - session_unlock_list(); - goto error; - } - - /* Set socket timeout for both receiving and ending */ - (void) lttcomm_setsockopt_rcv_timeout(ust_cmd.sock, - app_socket_timeout); - (void) lttcomm_setsockopt_snd_timeout(ust_cmd.sock, - app_socket_timeout); + /* Set socket timeout for both receiving and ending */ + (void) lttcomm_setsockopt_rcv_timeout(sock, + app_socket_timeout); + (void) lttcomm_setsockopt_snd_timeout(sock, + app_socket_timeout); - DBG("Apps with sock %d added to poll set", - ust_cmd.sock); - } - session_unlock_list(); + DBG("Apps with sock %d added to poll set", sock); health_code_update(); @@ -1293,6 +1243,54 @@ error_testpoint: return NULL; } +/* + * Send the application sockets (cmd and notify) to the respective threads. + * This is called from the dispatch UST registration thread once all sockets + * are set for the application. + * + * On success, return 0 else a negative value being the errno message of the + * write(). + */ +static int send_app_sockets_to_threads(struct ust_app *app) +{ + int ret; + + assert(app); + /* Sockets MUST be set or else this should not have been called. */ + assert(app->sock >= 0); + assert(app->notify_sock >= 0); + assert(apps_cmd_pipe[1] >= 0); + assert(apps_cmd_notify_pipe[1] >= 0); + + do { + ret = write(apps_cmd_pipe[1], &app->sock, sizeof(app->sock)); + } while (ret < 0 && errno == EINTR); + if (ret < 0 || ret != sizeof(app->sock)) { + PERROR("write apps cmd pipe %d", apps_cmd_pipe[1]); + if (ret < 0) { + ret = -errno; + } + goto error; + } + + do { + ret = write(apps_cmd_notify_pipe[1], &app->notify_sock, + sizeof(app->notify_sock)); + } while (ret < 0 && errno == EINTR); + if (ret < 0 || ret != sizeof(app->notify_sock)) { + PERROR("write apps notify cmd pipe %d", apps_cmd_notify_pipe[1]); + if (ret < 0) { + ret = -errno; + } + goto error; + } + + /* All good. Don't send back the write positive ret value. */ + ret = 0; +error: + return ret; +} + /* * Dispatch request from the registration threads to the application * communication thread. @@ -1302,6 +1300,12 @@ static void *thread_dispatch_ust_registration(void *data) int ret; struct cds_wfq_node *node; struct ust_command *ust_cmd = NULL; + struct { + struct ust_app *app; + struct cds_list_head head; + } *wait_node = NULL; + + CDS_LIST_HEAD(wait_queue); DBG("[thread] Dispatch UST command started"); @@ -1310,6 +1314,8 @@ static void *thread_dispatch_ust_registration(void *data) futex_nto1_prepare(&ust_cmd_queue.futex); do { + struct ust_app *app = NULL; + /* Dequeue command for registration */ node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); if (node == NULL) { @@ -1326,30 +1332,95 @@ static void *thread_dispatch_ust_registration(void *data) ust_cmd->reg_msg.uid, ust_cmd->reg_msg.gid, ust_cmd->sock, ust_cmd->reg_msg.name, ust_cmd->reg_msg.major, ust_cmd->reg_msg.minor); - /* - * Inform apps thread of the new application registration. This - * call is blocking so we can be assured that the data will be read - * at some point in time or wait to the end of the world :) - */ - if (apps_cmd_pipe[1] >= 0) { - do { - ret = write(apps_cmd_pipe[1], ust_cmd, - sizeof(struct ust_command)); - } while (ret < 0 && errno == EINTR); - if (ret < 0 || ret != sizeof(struct ust_command)) { - PERROR("write apps cmd pipe"); - if (errno == EBADF) { - /* - * We can't inform the application thread to process - * registration. We will exit or else application - * registration will not occur and tracing will never - * start. - */ - goto error; + + if (ust_cmd->reg_msg.type == USTCTL_SOCKET_CMD) { + wait_node = zmalloc(sizeof(*wait_node)); + if (!wait_node) { + PERROR("zmalloc wait_node dispatch"); + goto error; + } + CDS_INIT_LIST_HEAD(&wait_node->head); + + /* Create application object if socket is CMD. */ + wait_node->app = ust_app_create(&ust_cmd->reg_msg, + ust_cmd->sock); + if (!wait_node->app) { + ret = close(ust_cmd->sock); + if (ret < 0) { + PERROR("close ust sock dispatch %d", ust_cmd->sock); } + continue; } + /* + * Add application to the wait queue so we can set the notify + * socket before putting this object in the global ht. + */ + cds_list_add(&wait_node->head, &wait_queue); + + /* + * We have to continue here since we don't have the notify + * socket and the application MUST be added to the hash table + * only at that moment. + */ + continue; } else { - /* Application manager thread is not available. */ + /* + * Look for the application in the local wait queue and set the + * notify socket if found. + */ + cds_list_for_each_entry(wait_node, &wait_queue, head) { + if (wait_node->app->pid == ust_cmd->reg_msg.pid) { + wait_node->app->notify_sock = ust_cmd->sock; + cds_list_del(&wait_node->head); + app = wait_node->app; + free(wait_node); + DBG3("UST app notify socket %d is set", ust_cmd->sock); + break; + } + } + } + + if (app) { + ret = send_app_sockets_to_threads(app); + if (ret < 0) { + goto error; + } + /* + * @session_lock_list + * + * Lock the global session list so from the register up to the + * registration done message, no thread can see the application + * and change its state. + */ + session_lock_list(); + rcu_read_lock(); + /* + * Add application to the global hash table. This needs to be + * done before the update to the UST registry can locate the + * application. + */ + ust_app_add(app); + /* + * Get app version. + */ + ret = ust_app_version(app); + if (ret) { + ERR("Unable to get app version"); + } + /* + * Update newly registered application with the tracing + * registry info already enabled information. + */ + update_ust_app(app->sock); + ret = ust_app_register_done(app->sock); + if (ret < 0) { + /* Remove application from the registry. */ + ust_app_unregister(app->sock); + } + rcu_read_unlock(); + session_unlock_list(); + } else { + /* Application manager threads are not available. */ ret = close(ust_cmd->sock); if (ret < 0) { PERROR("close ust_cmd sock"); @@ -1398,7 +1469,7 @@ static void *thread_registration_apps(void *data) * Pass 2 as size here for the thread quit pipe and apps socket. Nothing * more will be added to this poll set. */ - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error_create_poll; } @@ -1445,7 +1516,7 @@ static void *thread_registration_apps(void *data) pollfd = LTTNG_POLL_GETFD(&events, i); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -1491,15 +1562,10 @@ static void *thread_registration_apps(void *data) continue; } health_code_update(); - ret = lttcomm_recv_unix_sock(sock, &ust_cmd->reg_msg, - sizeof(struct ust_register_msg)); - if (ret < 0 || ret < sizeof(struct ust_register_msg)) { - if (ret < 0) { - PERROR("lttcomm_recv_unix_sock register apps"); - } else { - ERR("Wrong size received on apps register"); - } + ret = ust_app_recv_registration(sock, &ust_cmd->reg_msg); + if (ret < 0) { free(ust_cmd); + /* Close socket of the application. */ ret = close(sock); if (ret) { PERROR("close"); @@ -2966,7 +3032,7 @@ static void *thread_manage_health(void *data) * Pass 2 as size here for the thread quit pipe and client_sock. Nothing * more will be added to this poll set. */ - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error; } @@ -3001,7 +3067,7 @@ restart: pollfd = LTTNG_POLL_GETFD(&events, i); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -3154,7 +3220,7 @@ static void *thread_manage_clients(void *data) * Pass 2 as size here for the thread quit pipe and client_sock. Nothing * more will be added to this poll set. */ - ret = create_thread_poll_set(&events, 2); + ret = sessiond_set_thread_pollset(&events, 2); if (ret < 0) { goto error_create_poll; } @@ -3206,7 +3272,7 @@ static void *thread_manage_clients(void *data) health_code_update(); /* Thread quit pipe has been closed. Killing thread. */ - ret = check_thread_quit_pipe(pollfd, revents); + ret = sessiond_check_thread_quit_pipe(pollfd, revents); if (ret) { err = 0; goto exit; @@ -4006,7 +4072,7 @@ int main(int argc, char **argv) /* Set global SHM for ust */ if (strlen(wait_shm_path) == 0) { snprintf(wait_shm_path, PATH_MAX, - DEFAULT_HOME_APPS_WAIT_SHM_PATH, geteuid()); + DEFAULT_HOME_APPS_WAIT_SHM_PATH, getuid()); } /* Set health check Unix path */ @@ -4022,6 +4088,7 @@ int main(int argc, char **argv) DBG("Client socket path %s", client_unix_sock_path); DBG("Application socket path %s", apps_unix_sock_path); + DBG("Application wait path %s", wait_shm_path); DBG("LTTng run directory path: %s", rundir); /* 32 bits consumerd path setup */ @@ -4130,6 +4197,11 @@ int main(int argc, char **argv) goto exit; } + /* Setup the thread apps notify communication pipe. */ + if (utils_create_pipe_cloexec(apps_cmd_notify_pipe) < 0) { + goto exit; + } + /* Init UST command queue. */ cds_wfq_init(&ust_cmd_queue.queue); @@ -4194,6 +4266,14 @@ int main(int argc, char **argv) goto exit_apps; } + /* Create thread to manage application notify socket */ + ret = pthread_create(&apps_notify_thread, NULL, + ust_thread_manage_notify, (void *) NULL); + if (ret != 0) { + PERROR("pthread_create apps"); + goto exit_apps; + } + /* Don't start this thread if kernel tracing is not requested nor root */ if (is_root && !opt_no_kernel) { /* Create kernel thread to manage kernel event */ diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c index aa188931d..371f1600b 100644 --- a/src/bin/lttng-sessiond/ust-app.c +++ b/src/bin/lttng-sessiond/ust-app.c @@ -142,16 +142,18 @@ no_match: * Unique add of an ust app event in the given ht. This uses the custom * ht_match_ust_app_event match function and the event name as hash. */ -static void add_unique_ust_app_event(struct lttng_ht *ht, +static void add_unique_ust_app_event(struct ust_app_channel *ua_chan, struct ust_app_event *event) { struct cds_lfht_node *node_ptr; struct ust_app_ht_key key; + struct lttng_ht *ht; - assert(ht); - assert(ht->ht); + assert(ua_chan); + assert(ua_chan->events); assert(event); + ht = ua_chan->events; key.name = event->attr.name; key.filter = event->filter; key.loglevel = event->attr.loglevel; @@ -175,9 +177,9 @@ void delete_ust_app_ctx(int sock, struct ust_app_ctx *ua_ctx) if (ua_ctx->obj) { ret = ustctl_release_object(sock, ua_ctx->obj); - if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app sock %d release context obj failed with ret %d", - sock, ret); + if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app sock %d release ctx obj handle %d failed with ret %d", + sock, ua_ctx->obj->handle, ret); } free(ua_ctx->obj); } @@ -236,7 +238,8 @@ void delete_ust_app_stream(int sock, struct ust_app_stream *stream) * this function. */ static -void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan) +void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan, + struct ust_app *app) { int ret; struct lttng_ht_iter iter; @@ -271,7 +274,13 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan) } lttng_ht_destroy(ua_chan->events); + /* Wipe and free registry. */ + ust_registry_channel_destroy(&ua_chan->session->registry, &ua_chan->registry); + if (ua_chan->obj != NULL) { + /* Remove channel from application UST object descriptor. */ + iter.iter.node = &ua_chan->ust_objd_node.node; + lttng_ht_del(app->ust_objd, &iter); ret = ustctl_release_object(sock, ua_chan->obj); if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { ERR("UST app sock %d release channel obj failed with ret %d", @@ -288,24 +297,27 @@ void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan) * this function. */ static -void delete_ust_app_session(int sock, struct ust_app_session *ua_sess) +void delete_ust_app_session(int sock, struct ust_app_session *ua_sess, + struct ust_app *app) { int ret; struct lttng_ht_iter iter; struct ust_app_channel *ua_chan; if (ua_sess->metadata) { - delete_ust_app_channel(sock, ua_sess->metadata); + delete_ust_app_channel(sock, ua_sess->metadata, app); } cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, node.node) { ret = lttng_ht_del(ua_sess->channels, &iter); assert(!ret); - delete_ust_app_channel(sock, ua_chan); + delete_ust_app_channel(sock, ua_chan, app); } lttng_ht_destroy(ua_sess->channels); + ust_registry_session_destroy(&ua_sess->registry); + if (ua_sess->handle != -1) { ret = ustctl_release_handle(sock, ua_sess->handle); if (ret < 0 && ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { @@ -338,7 +350,7 @@ void delete_ust_app(struct ust_app *app) cds_list_for_each_entry_safe(ua_sess, tmp_ua_sess, &app->teardown_head, teardown_node) { /* Free every object in the session and the session. */ - delete_ust_app_session(sock, ua_sess); + delete_ust_app_session(sock, ua_sess, app); } /* @@ -385,7 +397,7 @@ void delete_ust_app_rcu(struct rcu_head *head) * Delete the session from the application ht and delete the data structure by * freeing every object inside and releasing them. */ -static void destroy_session(struct ust_app *app, +static void destroy_app_session(struct ust_app *app, struct ust_app_session *ua_sess) { int ret; @@ -402,7 +414,7 @@ static void destroy_session(struct ust_app *app, } /* Once deleted, free the data structure. */ - delete_ust_app_session(app->sock, ua_sess); + delete_ust_app_session(app->sock, ua_sess, app); end: return; @@ -412,7 +424,7 @@ end: * Alloc new UST app session. */ static -struct ust_app_session *alloc_ust_app_session(void) +struct ust_app_session *alloc_ust_app_session(struct ust_app *app) { struct ust_app_session *ua_sess; @@ -425,9 +437,15 @@ struct ust_app_session *alloc_ust_app_session(void) ua_sess->handle = -1; ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); - - if ((lttng_uuid_generate(ua_sess->uuid))) { - ERR("Failed to generate UST uuid"); + pthread_mutex_init(&ua_sess->lock, NULL); + if (ust_registry_session_init(&ua_sess->registry, app, + app->bits_per_long, + app->uint8_t_alignment, + app->uint16_t_alignment, + app->uint32_t_alignment, + app->uint64_t_alignment, + app->long_alignment, + app->byte_order)) { goto error; } @@ -444,6 +462,7 @@ error_free: */ static struct ust_app_channel *alloc_ust_app_channel(char *name, + struct ust_app_session *ua_sess, struct lttng_ust_channel_attr *attr) { struct ust_app_channel *ua_chan; @@ -468,6 +487,9 @@ struct ust_app_channel *alloc_ust_app_channel(char *name, CDS_INIT_LIST_HEAD(&ua_chan->streams.head); + /* Initialize UST registry. */ + ust_registry_channel_init(&ua_sess->registry, &ua_chan->registry); + /* Copy attributes */ if (attr) { /* Translate from lttng_ust_channel to ustctl_consumer_channel_attr. */ @@ -614,6 +636,29 @@ error: return NULL; } +/* + * Find an ust_app using the notify sock and return it. RCU read side lock must + * be held before calling this helper function. + */ +static struct ust_app *find_app_by_notify_sock(int sock) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + + lttng_ht_lookup(ust_app_ht_by_notify_sock, (void *)((unsigned long) sock), + &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app find by notify sock %d not found", sock); + goto error; + } + + return caa_container_of(node, struct ust_app, notify_sock_n); + +error: + return NULL; +} + /* * Lookup for an ust app event based on event name, filter bytecode and the * event loglevel. @@ -652,6 +697,8 @@ end: /* * Create the channel context on the tracer. + * + * Called with UST app session lock held. */ static int create_ust_channel_context(struct ust_app_channel *ua_chan, @@ -675,7 +722,8 @@ int create_ust_channel_context(struct ust_app_channel *ua_chan, ua_ctx->handle = ua_ctx->obj->handle; - DBG2("UST app context created successfully for channel %s", ua_chan->name); + DBG2("UST app context handle %d created successfully for channel %s", + ua_ctx->handle, ua_chan->name); error: health_code_update(); @@ -841,6 +889,7 @@ error: /* * Create the specified channel onto the UST tracer for a UST session. + * Called with UST app session lock held. * * Return 0 on success. On error, a negative value is returned. */ @@ -921,6 +970,11 @@ static int create_ust_channel(struct ust_app *app, /* Flag the channel that it is sent to the application. */ ua_chan->is_sent = 1; + /* Assign session to channel. */ + ua_chan->session = ua_sess; + /* Initialize ust objd object using the received handle and add it. */ + lttng_ht_node_init_ulong(&ua_chan->ust_objd_node, ua_chan->handle); + lttng_ht_add_unique_ulong(app->ust_objd, &ua_chan->ust_objd_node); health_code_update(); @@ -951,6 +1005,8 @@ error: /* * Create the specified event onto the UST tracer for a UST session. + * + * Should be called with session mutex held. */ static int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, @@ -1091,7 +1147,7 @@ static void shadow_copy_channel(struct ust_app_channel *ua_chan, continue; } shadow_copy_event(ua_event, uevent); - add_unique_ust_app_event(ua_chan->events, ua_event); + add_unique_ust_app_event(ua_chan, ua_event); } } @@ -1148,7 +1204,7 @@ static void shadow_copy_session(struct ust_app_session *ua_sess, DBG2("Channel %s not found on shadow session copy, creating it", uchan->name); - ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); + ua_chan = alloc_ust_app_channel(uchan->name, ua_sess, &uchan->attr); if (ua_chan == NULL) { /* malloc failed FIXME: Might want to do handle ENOMEM .. */ continue; @@ -1226,7 +1282,7 @@ static int create_ust_app_session(struct ltt_ust_session *usess, if (ua_sess == NULL) { DBG2("UST app pid: %d session id %d not found, creating it", app->pid, usess->id); - ua_sess = alloc_ust_app_session(); + ua_sess = alloc_ust_app_session(app); if (ua_sess == NULL) { /* Only malloc can failed so something is really wrong */ ret = -ENOMEM; @@ -1247,7 +1303,7 @@ static int create_ust_app_session(struct ltt_ust_session *usess, } else { DBG("UST app creating session failed. Application is dead"); } - delete_ust_app_session(-1, ua_sess); + delete_ust_app_session(-1, ua_sess, app); if (ret != -ENOMEM) { /* * Tracer is probably gone or got an internal error so let's @@ -1281,6 +1337,8 @@ error: /* * Create a context for the channel on the tracer. + * + * Called with UST app session lock held. */ static int create_ust_app_channel_context(struct ust_app_session *ua_sess, @@ -1322,6 +1380,8 @@ error: /* * Enable on the tracer side a ust app event for the session and channel. + * + * Called with UST app session lock held. */ static int enable_ust_app_event(struct ust_app_session *ua_sess, @@ -1412,6 +1472,8 @@ error: /* * Create UST app channel and create it on the tracer. Set ua_chanp of the * newly created channel if not NULL. + * + * Called with UST app session lock held. */ static int create_ust_app_channel(struct ust_app_session *ua_sess, struct ltt_ust_channel *uchan, struct ust_app *app, @@ -1431,7 +1493,7 @@ static int create_ust_app_channel(struct ust_app_session *ua_sess, goto end; } - ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); + ua_chan = alloc_ust_app_channel(uchan->name, ua_sess, &uchan->attr); if (ua_chan == NULL) { /* Only malloc can fail here */ ret = -ENOMEM; @@ -1447,12 +1509,12 @@ static int create_ust_app_channel(struct ust_app_session *ua_sess, goto error; } - /* Only add the channel if successful on the tracer side. */ - lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); - DBG2("UST app create channel %s for PID %d completed", ua_chan->name, app->pid); + /* Only add the channel if successful on the tracer side. */ + lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); + end: if (ua_chanp) { *ua_chanp = ua_chan; @@ -1462,12 +1524,14 @@ end: return 0; error: - delete_ust_app_channel(ua_chan->is_sent ? app->sock : -1, ua_chan); + delete_ust_app_channel(ua_chan->is_sent ? app->sock : -1, ua_chan, app); return ret; } /* * Create UST app event and create it on the tracer side. + * + * Called with ust app session mutex held. */ static int create_ust_app_event(struct ust_app_session *ua_sess, @@ -1502,7 +1566,7 @@ int create_ust_app_event(struct ust_app_session *ua_sess, goto error; } - add_unique_ust_app_event(ua_chan->events, ua_event); + add_unique_ust_app_event(ua_chan, ua_event); DBG2("UST app create event %s for PID %d completed", ua_event->name, app->pid); @@ -1518,6 +1582,8 @@ error: /* * Create UST metadata and open it on the tracer side. + * + * Called with UST app session lock held. */ static int create_ust_app_metadata(struct ust_app_session *ua_sess, struct ust_app *app, struct consumer_output *consumer) @@ -1534,7 +1600,7 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, } /* Allocate UST metadata */ - metadata = alloc_ust_app_channel(DEFAULT_METADATA_NAME, NULL); + metadata = alloc_ust_app_channel(DEFAULT_METADATA_NAME, ua_sess, NULL); if (!metadata) { /* malloc() failed */ ret = -ENOMEM; @@ -1562,7 +1628,7 @@ static int create_ust_app_metadata(struct ust_app_session *ua_sess, end: return 0; error_create: - delete_ust_app_channel(metadata->is_sent ? app->sock : -1, metadata); + delete_ust_app_channel(metadata->is_sent ? app->sock : -1, metadata, app); error: return ret; } @@ -1583,14 +1649,12 @@ struct ust_app *ust_app_find_by_pid(pid_t pid) struct lttng_ht_node_ulong *node; struct lttng_ht_iter iter; - rcu_read_lock(); lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) pid), &iter); node = lttng_ht_iter_get_node_ulong(&iter); if (node == NULL) { DBG2("UST app no found with pid %d", pid); goto error; } - rcu_read_unlock(); DBG2("Found UST app by pid %d", pid); @@ -1601,89 +1665,99 @@ error: return NULL; } -/* - * Using pid and uid (of the app), allocate a new ust_app struct and - * add it to the global traceable app list. - * - * On success, return 0, else return malloc -ENOMEM, or -EINVAL if app - * bitness is not supported. - */ -int ust_app_register(struct ust_register_msg *msg, int sock) +struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) { - struct ust_app *lta; - int ret; + struct ust_app *lta = NULL; + + assert(msg); + assert(sock >= 0); + + DBG3("UST app creating application for socket %d", sock); if ((msg->bits_per_long == 64 && (uatomic_read(&ust_consumerd64_fd) == -EINVAL)) || (msg->bits_per_long == 32 && (uatomic_read(&ust_consumerd32_fd) == -EINVAL))) { ERR("Registration failed: application \"%s\" (pid: %d) has " - "%d-bit long, but no consumerd for this long size is available.\n", - msg->name, msg->pid, msg->bits_per_long); - ret = close(sock); - if (ret) { - PERROR("close"); - } - lttng_fd_put(LTTNG_FD_APPS, 1); - return -EINVAL; - } - if (msg->major != LTTNG_UST_COMM_MAJOR) { - ERR("Registration failed: application \"%s\" (pid: %d) has " - "communication protocol version %u.%u, but sessiond supports 2.x.\n", - msg->name, msg->pid, msg->major, msg->minor); - ret = close(sock); - if (ret) { - PERROR("close"); - } - lttng_fd_put(LTTNG_FD_APPS, 1); - return -EINVAL; + "%d-bit long, but no consumerd for this size is available.\n", + msg->name, msg->pid, msg->bits_per_long); + goto error; } + lta = zmalloc(sizeof(struct ust_app)); if (lta == NULL) { PERROR("malloc"); - return -ENOMEM; + goto error; } lta->ppid = msg->ppid; lta->uid = msg->uid; lta->gid = msg->gid; lta->compatible = 0; /* Not compatible until proven */ + lta->bits_per_long = msg->bits_per_long; + lta->uint8_t_alignment = msg->uint8_t_alignment; + lta->uint16_t_alignment = msg->uint16_t_alignment; + lta->uint32_t_alignment = msg->uint32_t_alignment; + lta->uint64_t_alignment = msg->uint64_t_alignment; + lta->long_alignment = msg->long_alignment; + lta->byte_order = msg->byte_order; + lta->v_major = msg->major; lta->v_minor = msg->minor; strncpy(lta->name, msg->name, sizeof(lta->name)); - lta->name[16] = '\0'; + lta->name[LTTNG_UST_ABI_PROCNAME_LEN] = '\0'; lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lta->notify_sock = -1; + lta->compatible = 1; lta->pid = msg->pid; - lttng_ht_node_init_ulong(<a->pid_n, (unsigned long)lta->pid); + lttng_ht_node_init_ulong(<a->pid_n, (unsigned long) lta->pid); lta->sock = sock; - lttng_ht_node_init_ulong(<a->sock_n, (unsigned long)lta->sock); + lttng_ht_node_init_ulong(<a->sock_n, (unsigned long) lta->sock); CDS_INIT_LIST_HEAD(<a->teardown_head); +error: + return lta; +} + +void ust_app_add(struct ust_app *app) +{ + assert(app); + assert(app->notify_sock >= 0); + rcu_read_lock(); /* * On a re-registration, we want to kick out the previous registration of * that pid */ - lttng_ht_add_replace_ulong(ust_app_ht, <a->pid_n); + lttng_ht_add_replace_ulong(ust_app_ht, &app->pid_n); /* * The socket _should_ be unique until _we_ call close. So, a add_unique * for the ust_app_ht_by_sock is used which asserts fail if the entry was * already in the table. */ - lttng_ht_add_unique_ulong(ust_app_ht_by_sock, <a->sock_n); + lttng_ht_add_unique_ulong(ust_app_ht_by_sock, &app->sock_n); - rcu_read_unlock(); + /* Add application to the notify socket hash table. */ + lttng_ht_node_init_ulong(&app->notify_sock_n, app->notify_sock); + lttng_ht_add_unique_ulong(ust_app_ht_by_notify_sock, &app->notify_sock_n); - DBG("App registered with pid:%d ppid:%d uid:%d gid:%d sock:%d name:%s" - " (version %d.%d)", lta->pid, lta->ppid, lta->uid, lta->gid, - lta->sock, lta->name, lta->v_major, lta->v_minor); + DBG("App registered with pid:%d ppid:%d uid:%d gid:%d sock:%d name:%s " + "(version %d.%d)", app->pid, app->ppid, app->uid, app->gid, + app->sock, app->name, app->v_major, app->v_minor); - return 0; + rcu_read_unlock(); +} + +int ust_app_version(struct ust_app *app) +{ + assert(app); + return ustctl_tracer_version(app->sock, &app->version); } /* @@ -1705,27 +1779,25 @@ void ust_app_unregister(int sock) /* Get the node reference for a call_rcu */ lttng_ht_lookup(ust_app_ht_by_sock, (void *)((unsigned long) sock), &iter); node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - ERR("Unable to find app by sock %d", sock); - goto error; - } + assert(node); lta = caa_container_of(node, struct ust_app, sock_n); - DBG("PID %d unregistering with sock %d", lta->pid, sock); /* Remove application from PID hash table */ ret = lttng_ht_del(ust_app_ht_by_sock, &iter); assert(!ret); - /* Assign second node for deletion */ - iter.iter.node = <a->pid_n.node; + /* Remove application from notify hash table */ + iter.iter.node = <a->notify_sock_n.node; + ret = lttng_ht_del(ust_app_ht_by_notify_sock, &iter); /* * Ignore return value since the node might have been removed before by an * add replace during app registration because the PID can be reassigned by * the OS. */ + iter.iter.node = <a->pid_n.node; ret = lttng_ht_del(ust_app_ht, &iter); if (ret) { DBG3("Unregister app by PID %d failed. This can happen on pid reuse", @@ -1751,7 +1823,6 @@ void ust_app_unregister(int sock) /* Free memory */ call_rcu(<a->pid_n.head, delete_ust_app_rcu); -error: rcu_read_unlock(); return; } @@ -2003,6 +2074,7 @@ void ust_app_ht_alloc(void) { ust_app_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); ust_app_ht_by_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + ust_app_ht_by_notify_sock = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); } /* @@ -2293,9 +2365,11 @@ int ust_app_create_channel_glb(struct ltt_ust_session *usess, } assert(ua_sess); + pthread_mutex_lock(&ua_sess->lock); /* Create channel onto application. We don't need the chan ref. */ ret = create_ust_app_channel(ua_sess, uchan, app, usess->consumer, LTTNG_UST_CHAN_PER_CPU, NULL); + pthread_mutex_unlock(&ua_sess->lock); if (ret < 0) { if (ret == -ENOMEM) { /* No more memory is a fatal error. Stop right now. */ @@ -2303,7 +2377,7 @@ int ust_app_create_channel_glb(struct ltt_ust_session *usess, } /* Cleanup the created session if it's the case. */ if (created) { - destroy_session(app, ua_sess); + destroy_app_session(app, ua_sess); } } } @@ -2353,6 +2427,8 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess, continue; } + pthread_mutex_lock(&ua_sess->lock); + /* Lookup channel in the ust app session */ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); ua_chan_node = lttng_ht_iter_get_node_str(&uiter); @@ -2367,13 +2443,16 @@ int ust_app_enable_event_glb(struct ltt_ust_session *usess, if (ua_event == NULL) { DBG3("UST app enable event %s not found for app PID %d." "Skipping app", uevent->attr.name, app->pid); - continue; + goto next_app; } ret = enable_ust_app_event(ua_sess, ua_event, app); if (ret < 0) { + pthread_mutex_unlock(&ua_sess->lock); goto error; } + next_app: + pthread_mutex_unlock(&ua_sess->lock); } error: @@ -2415,6 +2494,7 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess, continue; } + pthread_mutex_lock(&ua_sess->lock); /* Lookup channel in the ust app session */ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); ua_chan_node = lttng_ht_iter_get_node_str(&uiter); @@ -2424,6 +2504,7 @@ int ust_app_create_event_glb(struct ltt_ust_session *usess, ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + pthread_mutex_unlock(&ua_sess->lock); if (ret < 0) { if (ret != -LTTNG_UST_ERR_EXIST) { /* Possible value at this point: -ENOMEM. If so, we stop! */ @@ -2462,6 +2543,8 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) goto end; } + pthread_mutex_lock(&ua_sess->lock); + /* Upon restart, we skip the setup, already done */ if (ua_sess->started) { goto skip_setup; @@ -2475,7 +2558,7 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) if (ret < 0) { if (ret != -EEXIST) { ERR("Trace directory creation error"); - goto error_rcu_unlock; + goto error_unlock; } } } @@ -2483,7 +2566,7 @@ int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) /* Create the metadata for the application. */ ret = create_ust_app_metadata(ua_sess, app, usess->consumer); if (ret < 0) { - goto error_rcu_unlock; + goto error_unlock; } health_code_update(); @@ -2498,12 +2581,14 @@ skip_setup: } else { DBG("UST app start session failed. Application is dead."); } - goto error_rcu_unlock; + goto error_unlock; } /* Indicate that the session has been started once */ ua_sess->started = 1; + pthread_mutex_unlock(&ua_sess->lock); + health_code_update(); /* Quiescent wait after starting trace */ @@ -2518,7 +2603,8 @@ end: health_code_update(); return 0; -error_rcu_unlock: +error_unlock: + pthread_mutex_unlock(&ua_sess->lock); rcu_read_unlock(); health_code_update(); return -1; @@ -2656,7 +2742,7 @@ static int destroy_trace(struct ltt_ust_session *usess, struct ust_app *app) ua_sess = caa_container_of(node, struct ust_app_session, node); health_code_update(); - destroy_session(app, ua_sess); + destroy_app_session(app, ua_sess); health_code_update(); @@ -2789,8 +2875,10 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock) } assert(ua_sess); + pthread_mutex_lock(&ua_sess->lock); + /* - * We can iterate safely here over all UST app session sicne the create ust + * We can iterate safely here over all UST app session since the create ust * app session above made a shadow copy of the UST global domain from the * ltt ust session. */ @@ -2803,14 +2891,14 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock) * descriptor are available or ENOMEM so stopping here is the only * thing we can do for now. */ - goto error; + goto error_unlock; } cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter_ctx.iter, ua_ctx, node.node) { ret = create_ust_channel_context(ua_chan, ua_ctx, app); if (ret < 0) { - goto error; + goto error_unlock; } } @@ -2820,11 +2908,13 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock) node.node) { ret = create_ust_event(app, ua_sess, ua_chan, ua_event); if (ret < 0) { - goto error; + goto error_unlock; } } } + pthread_mutex_unlock(&ua_sess->lock); + if (usess->start_trace) { ret = ust_app_start_trace(usess, app); if (ret < 0) { @@ -2838,9 +2928,11 @@ void ust_app_global_update(struct ltt_ust_session *usess, int sock) rcu_read_unlock(); return; +error_unlock: + pthread_mutex_unlock(&ua_sess->lock); error: if (ua_sess) { - destroy_session(app, ua_sess); + destroy_app_session(app, ua_sess); } rcu_read_unlock(); return; @@ -2874,19 +2966,21 @@ int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, continue; } + pthread_mutex_lock(&ua_sess->lock); /* Lookup channel in the ust app session */ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); ua_chan_node = lttng_ht_iter_get_node_str(&uiter); if (ua_chan_node == NULL) { - continue; + goto next_app; } ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - ret = create_ust_app_channel_context(ua_sess, ua_chan, &uctx->ctx, app); if (ret < 0) { - continue; + goto next_app; } + next_app: + pthread_mutex_unlock(&ua_sess->lock); } rcu_read_unlock(); @@ -2915,20 +3009,22 @@ int ust_app_enable_event_pid(struct ltt_ust_session *usess, if (app == NULL) { ERR("UST app enable event per PID %d not found", pid); ret = -1; - goto error; + goto end; } if (!app->compatible) { ret = 0; - goto error; + goto end; } ua_sess = lookup_session_by_app(usess, app); if (!ua_sess) { /* The application has problem or is probably dead. */ - goto error; + ret = 0; + goto end; } + pthread_mutex_lock(&ua_sess->lock); /* Lookup channel in the ust app session */ lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); ua_chan_node = lttng_ht_iter_get_node_str(&iter); @@ -2942,16 +3038,18 @@ int ust_app_enable_event_pid(struct ltt_ust_session *usess, if (ua_event == NULL) { ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); if (ret < 0) { - goto error; + goto end_unlock; } } else { ret = enable_ust_app_event(ua_sess, ua_event, app); if (ret < 0) { - goto error; + goto end_unlock; } } -error: +end_unlock: + pthread_mutex_unlock(&ua_sess->lock); +end: rcu_read_unlock(); return ret; } @@ -3019,52 +3117,6 @@ error: return ret; } -/* - * Validate version of UST apps and set the compatible bit. - */ -int ust_app_validate_version(int sock) -{ - int ret; - struct ust_app *app; - - rcu_read_lock(); - - app = find_app_by_sock(sock); - assert(app); - - health_code_update(); - - ret = ustctl_tracer_version(sock, &app->version); - if (ret < 0) { - if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { - ERR("UST app tracer version failed for app pid %d", app->pid); - } - goto error; - } - - /* Validate version */ - if (app->version.major != UST_APP_MAJOR_VERSION) { - goto error; - } - - DBG2("UST app PID %d is compatible with internal major version %d " - "(supporting == %d)", app->pid, app->version.major, - UST_APP_MAJOR_VERSION); - app->compatible = 1; - rcu_read_unlock(); - health_code_update(); - return 0; - -error: - DBG2("UST app PID %d is not compatible with internal major version %d " - "(supporting == %d)", app->pid, app->version.major, - UST_APP_MAJOR_VERSION); - app->compatible = 0; - rcu_read_unlock(); - health_code_update(); - return -1; -} - /* * Calibrate registered applications. */ @@ -3110,3 +3162,290 @@ int ust_app_calibrate_glb(struct lttng_ust_calibrate *calibrate) return ret; } + +/* + * Receive registration and populate the given msg structure. + * + * On success return 0 else a negative value returned by the ustctl call. + */ +int ust_app_recv_registration(int sock, struct ust_register_msg *msg) +{ + int ret; + uint32_t pid, ppid, uid, gid; + + assert(msg); + + ret = ustctl_recv_reg_msg(sock, &msg->type, &msg->major, &msg->minor, + &pid, &ppid, &uid, &gid, + &msg->bits_per_long, + &msg->uint8_t_alignment, + &msg->uint16_t_alignment, + &msg->uint32_t_alignment, + &msg->uint64_t_alignment, + &msg->long_alignment, + &msg->byte_order, + msg->name); + if (ret < 0) { + switch (-ret) { + case EPIPE: + case ECONNRESET: + case LTTNG_UST_ERR_EXITING: + DBG3("UST app recv reg message failed. Application died"); + break; + case LTTNG_UST_ERR_UNSUP_MAJOR: + ERR("UST app recv reg unsupported version %d.%d. Supporting %d.%d", + msg->major, msg->minor, LTTNG_UST_ABI_MAJOR_VERSION, + LTTNG_UST_ABI_MINOR_VERSION); + break; + default: + ERR("UST app recv reg message failed with ret %d", ret); + break; + } + goto error; + } + msg->pid = (pid_t) pid; + msg->ppid = (pid_t) ppid; + msg->uid = (uid_t) uid; + msg->gid = (gid_t) gid; + +error: + return ret; +} + +static struct ust_app_channel *find_channel_by_objd(struct ust_app *app, + int objd) +{ + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + struct ust_app_channel *ua_chan = NULL; + + assert(app); + + lttng_ht_lookup(app->ust_objd, (void *)((unsigned long) objd), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app channel find by objd %d not found", objd); + goto error; + } + + ua_chan = caa_container_of(node, struct ust_app_channel, ust_objd_node); + +error: + return ua_chan; +} + +static int reply_ust_register_channel(int sock, int sobjd, int cobjd, + size_t nr_fields, struct ustctl_field *fields) +{ + int ret, ret_code = 0; + uint32_t chan_id, reg_count; + enum ustctl_channel_header type; + struct ust_app *app; + struct ust_app_channel *ua_chan; + struct ust_app_session *ua_sess; + + rcu_read_lock(); + + /* Lookup application. If not found, there is a code flow error. */ + app = find_app_by_notify_sock(sock); + assert(app); + + /* Lookup channel by UST object descriptor. Should always be found. */ + ua_chan = find_channel_by_objd(app, cobjd); + assert(ua_chan); + assert(ua_chan->session); + ua_sess = ua_chan->session; + assert(ua_sess); + + pthread_mutex_lock(&ua_sess->registry.lock); + + if (ust_registry_is_max_id(ua_chan->session->registry.used_channel_id)) { + ret_code = -1; + chan_id = -1U; + type = -1; + goto reply; + } + + /* Don't assign ID to metadata. */ + if (ua_chan->attr.type == LTTNG_UST_CHAN_METADATA) { + chan_id = -1U; + } else { + chan_id = ust_registry_get_next_chan_id(&ua_chan->session->registry); + } + + reg_count = ust_registry_get_event_count(&ua_chan->registry); + if (reg_count < 31) { + type = USTCTL_CHANNEL_HEADER_COMPACT; + } else { + type = USTCTL_CHANNEL_HEADER_LARGE; + } + + ua_chan->registry.nr_ctx_fields = nr_fields; + ua_chan->registry.ctx_fields = fields; + ua_chan->registry.chan_id = chan_id; + ua_chan->registry.header_type = type; + + /* Append to metadata */ + if (!ret_code) { + ret_code = ust_metadata_channel_statedump(&ua_chan->session->registry, + &ua_chan->registry); + if (ret_code) { + ERR("Error appending channel metadata (errno = %d)", ret_code); + goto reply; + } + } + +reply: + DBG3("UST app replying to register channel with id %u, type: %d, ret: %d", + chan_id, type, ret_code); + + ret = ustctl_reply_register_channel(sock, chan_id, type, ret_code); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app reply channel failed with ret %d", ret); + } else { + DBG3("UST app reply channel failed. Application died"); + } + goto error; + } + +error: + pthread_mutex_unlock(&ua_sess->registry.lock); + rcu_read_unlock(); + return ret; +} + +static int add_event_ust_registry(int sock, int sobjd, int cobjd, char *name, + char *sig, size_t nr_fields, struct ustctl_field *fields, int loglevel, + char *model_emf_uri) +{ + int ret, ret_code; + uint32_t event_id = 0; + struct ust_app *app; + struct ust_app_channel *ua_chan; + struct ust_app_session *ua_sess; + + rcu_read_lock(); + + /* Lookup application. If not found, there is a code flow error. */ + app = find_app_by_notify_sock(sock); + assert(app); + + /* Lookup channel by UST object descriptor. Should always be found. */ + ua_chan = find_channel_by_objd(app, cobjd); + assert(ua_chan); + assert(ua_chan->session); + ua_sess = ua_chan->session; + + pthread_mutex_lock(&ua_sess->registry.lock); + + ret_code = ust_registry_create_event(&ua_sess->registry, &ua_chan->registry, sobjd, cobjd, + name, sig, nr_fields, fields, loglevel, model_emf_uri, &event_id); + + /* + * The return value is returned to ustctl so in case of an error, the + * application can be notified. In case of an error, it's important not to + * return a negative error or else the application will get closed. + */ + ret = ustctl_reply_register_event(sock, event_id, ret_code); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app reply event failed with ret %d", ret); + } else { + DBG3("UST app reply event failed. Application died"); + } + /* + * No need to wipe the create event since the application socket will + * get close on error hence cleaning up everything by itself. + */ + goto error; + } + +error: + pthread_mutex_unlock(&ua_sess->registry.lock); + rcu_read_unlock(); + return ret; +} + +int ust_app_recv_notify(int sock) +{ + int ret; + enum ustctl_notify_cmd cmd; + + DBG3("UST app receiving notify from sock %d", sock); + + ret = ustctl_recv_notify(sock, &cmd); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app recv notify failed with ret %d", ret); + } else { + DBG3("UST app recv notify failed. Application died"); + } + goto error; + } + + switch (cmd) { + case USTCTL_NOTIFY_CMD_EVENT: + { + int sobjd, cobjd, loglevel; + char name[LTTNG_UST_SYM_NAME_LEN], *sig, *model_emf_uri; + size_t nr_fields; + struct ustctl_field *fields; + + DBG2("UST app ustctl register event received"); + + ret = ustctl_recv_register_event(sock, &sobjd, &cobjd, name, &loglevel, + &sig, &nr_fields, &fields, &model_emf_uri); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app recv event failed with ret %d", ret); + } else { + DBG3("UST app recv event failed. Application died"); + } + goto error; + } + + /* Add event to the UST registry coming from the notify socket. */ + ret = add_event_ust_registry(sock, sobjd, cobjd, name, sig, nr_fields, + fields, loglevel, model_emf_uri); + if (ret < 0) { + goto error; + } + + break; + } + case USTCTL_NOTIFY_CMD_CHANNEL: + { + int sobjd, cobjd; + size_t nr_fields; + struct ustctl_field *fields; + + DBG2("UST app ustctl register channel received"); + + ret = ustctl_recv_register_channel(sock, &sobjd, &cobjd, &nr_fields, + &fields); + if (ret < 0) { + if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) { + ERR("UST app recv channel failed with ret %d", ret); + } else { + DBG3("UST app recv channel failed. Application died"); + } + goto error; + } + + ret = reply_ust_register_channel(sock, sobjd, cobjd, nr_fields, + fields); + if (ret < 0) { + goto error; + } + + break; + } + default: + /* Should NEVER happen. */ + assert(0); + } + +error: + return ret; +} diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h index e8bb9a980..00d2d5ae4 100644 --- a/src/bin/lttng-sessiond/ust-app.h +++ b/src/bin/lttng-sessiond/ust-app.h @@ -22,13 +22,17 @@ #include #include "trace-ust.h" +#include "ust-registry.h" /* lttng-ust supported version. */ -#define LTTNG_UST_COMM_MAJOR 2 /* comm protocol major version */ -#define UST_APP_MAJOR_VERSION 3 /* Internal UST version supported */ +//#define LTTNG_UST_COMM_MAJOR 2 /* comm protocol major version */ +//#define UST_APP_MAJOR_VERSION 3 /* Internal UST version supported */ #define UST_APP_EVENT_LIST_SIZE 32 +/* Process name (short). Extra for the NULL byte. */ +#define UST_APP_PROCNAME_LEN 17 + struct lttng_filter_bytecode; struct lttng_ust_filter_bytecode; @@ -44,14 +48,23 @@ struct ust_app_ht_key { * Application registration data structure. */ struct ust_register_msg { + enum ustctl_socket_type type; uint32_t major; uint32_t minor; + uint32_t abi_major; + uint32_t abi_minor; pid_t pid; pid_t ppid; uid_t uid; gid_t gid; uint32_t bits_per_long; - char name[16]; + uint32_t uint8_t_alignment; + uint32_t uint16_t_alignment; + uint32_t uint32_t_alignment; + uint32_t uint64_t_alignment; + uint32_t long_alignment; + int byte_order; /* BIG_ENDIAN or LITTLE_ENDIAN */ + char name[LTTNG_UST_ABI_PROCNAME_LEN]; }; /* @@ -66,6 +79,12 @@ struct lttng_ht *ust_app_ht; */ struct lttng_ht *ust_app_ht_by_sock; +/* + * Global applications HT used by the session daemon. This table is indexed by + * socket using the notify_sock_n node and notify_sock value of an ust_app. + */ +struct lttng_ht *ust_app_ht_by_notify_sock; + /* Stream list containing ust_app_stream. */ struct ust_app_stream_list { unsigned int count; @@ -97,7 +116,6 @@ struct ust_app_stream { struct lttng_ust_object_data *obj; /* Using a list of streams to keep order. */ struct cds_list_head list; - struct ustctl_consumer_stream *ustream; }; struct ust_app_channel { @@ -112,20 +130,41 @@ struct ust_app_channel { char name[LTTNG_UST_SYM_NAME_LEN]; struct lttng_ust_object_data *obj; struct ustctl_consumer_channel_attr attr; - struct ustctl_consumer_channel *channel; struct ust_app_stream_list streams; + /* Session pointer that owns this object. */ + struct ust_app_session *session; struct lttng_ht *ctx; struct lttng_ht *events; + /* + * UST event registry. The ONLY writer is the application thread. + */ + struct ust_registry_channel registry; + /* + * Node indexed by channel name in the channels' hash table of a session. + */ struct lttng_ht_node_str node; + /* + * Node indexed by UST channel object descriptor (handle). Stored in the + * ust_objd hash table in the ust_app object. + */ + struct lttng_ht_node_ulong ust_objd_node; }; struct ust_app_session { + /* + * Lock protecting this session's ust app interaction. Held + * across command send/recv to/from app. Never nests within the + * session registry lock. + */ + pthread_mutex_t lock; + int enabled; /* started: has the session been in started state at any time ? */ int started; /* allows detection of start vs restart. */ int handle; /* used has unique identifier for app session */ int id; /* session unique identifier */ struct ust_app_channel *metadata; + struct ust_registry_session registry; struct lttng_ht *channels; /* Registered channels */ struct lttng_ht_node_ulong node; char path[PATH_MAX]; @@ -133,8 +172,6 @@ struct ust_app_session { uid_t uid; gid_t gid; struct cds_list_head teardown_node; - /* Universal unique identifier used by the tracer. */ - unsigned char uuid[UUID_STR_LEN]; }; /* @@ -143,21 +180,32 @@ struct ust_app_session { */ struct ust_app { int sock; + int notify_sock; pid_t pid; pid_t ppid; uid_t uid; /* User ID that owns the apps */ gid_t gid; /* Group ID that owns the apps */ - int bits_per_long; + + /* App ABI */ + uint32_t bits_per_long; + uint32_t uint8_t_alignment; + uint32_t uint16_t_alignment; + uint32_t uint32_t_alignment; + uint32_t uint64_t_alignment; + uint32_t long_alignment; + int byte_order; /* BIG_ENDIAN or LITTLE_ENDIAN */ + int compatible; /* If the lttng-ust tracer version does not match the supported version of the session daemon, this flag is set to 0 (NOT compatible) else 1. */ struct lttng_ust_tracer_version version; - uint32_t v_major; /* Verion major number */ - uint32_t v_minor; /* Verion minor number */ - char name[17]; /* Process name (short) */ + uint32_t v_major; /* Version major number */ + uint32_t v_minor; /* Version minor number */ + char name[UST_APP_PROCNAME_LEN]; struct lttng_ht *sessions; struct lttng_ht_node_ulong pid_n; struct lttng_ht_node_ulong sock_n; + struct lttng_ht_node_ulong notify_sock_n; /* * This is a list of ust app session that, once the app is going into * teardown mode, in the RCU call, each node in this list is removed and @@ -168,6 +216,10 @@ struct ust_app { * when a session is destroyed. */ struct cds_list_head teardown_head; + /* + * Hash table containing ust_app_channel indexed by channel objd. + */ + struct lttng_ht *ust_objd; }; #ifdef HAVE_LIBLTTNG_UST_CTL @@ -178,6 +230,7 @@ int ust_app_register_done(int sock) { return ustctl_register_done(sock); } +int ust_app_version(struct ust_app *app); void ust_app_unregister(int sock); unsigned long ust_app_list_count(void); int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app); @@ -217,9 +270,12 @@ void ust_app_clean_list(void); void ust_app_ht_alloc(void); struct lttng_ht *ust_app_get_ht(void); struct ust_app *ust_app_find_by_pid(pid_t pid); -int ust_app_validate_version(int sock); int ust_app_calibrate_glb(struct lttng_ust_calibrate *calibrate); struct ust_app_stream *ust_app_alloc_stream(void); +int ust_app_recv_registration(int sock, struct ust_register_msg *msg); +int ust_app_recv_notify(int sock); +void ust_app_add(struct ust_app *app); +struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock); #else /* HAVE_LIBLTTNG_UST_CTL */ @@ -264,6 +320,11 @@ int ust_app_register_done(int sock) return -ENOSYS; } static inline +int ust_app_version(struct ust_app *app) +{ + return -ENOSYS; +} +static inline void ust_app_unregister(int sock) { } @@ -374,15 +435,29 @@ int ust_app_disable_event_pid(struct ltt_ust_session *usess, return 0; } static inline -int ust_app_validate_version(int sock) +int ust_app_calibrate_glb(struct lttng_ust_calibrate *calibrate) { return 0; } static inline -int ust_app_calibrate_glb(struct lttng_ust_calibrate *calibrate) +int ust_app_recv_registration(int sock, struct ust_register_msg *msg) +{ + return 0; +} +static inline +int ust_app_recv_notify(int sock) { return 0; } +static inline +struct ust_app *ust_app_create(struct ust_register_msg *msg, int sock) +{ + return NULL; +} +static inline +void ust_app_add(struct ust_app *app) +{ +} #endif /* HAVE_LIBLTTNG_UST_CTL */ diff --git a/src/bin/lttng-sessiond/ust-clock.h b/src/bin/lttng-sessiond/ust-clock.h new file mode 100644 index 000000000..7d9c99a66 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-clock.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Pierre-Marc Fournier + * Copyright (C) 2011 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 as published by the Free Software Foundation; version 2.1 of + * the License. + * + * 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 _UST_CLOCK_H +#define _UST_CLOCK_H + +#include +#include +#include +#include +#include +#include + +#include + +/* TRACE CLOCK */ + +/* + * Currently using the kernel MONOTONIC clock, waiting for kernel-side + * LTTng to implement mmap'd trace clock. + */ + +/* Choosing correct trace clock */ + +static __inline__ +uint64_t trace_clock_read64(void) +{ + struct timespec ts; + + clock_gettime(CLOCK_MONOTONIC, &ts); + return ((uint64_t) ts.tv_sec * 1000000000ULL) + ts.tv_nsec; +} + +static __inline__ +uint64_t trace_clock_freq(void) +{ + return 1000000000ULL; +} + +static __inline__ +int trace_clock_uuid(char *uuid) +{ + int ret = 0; + size_t len; + FILE *fp; + + /* + * boot_id needs to be read once before being used concurrently + * to deal with a Linux kernel race. A fix is proposed for + * upstream, but the work-around is needed for older kernels. + */ + fp = fopen("/proc/sys/kernel/random/boot_id", "r"); + if (!fp) { + return -ENOENT; + } + len = fread(uuid, 1, UUID_STR_LEN - 1, fp); + if (len < UUID_STR_LEN - 1) { + ret = -EINVAL; + goto end; + } + uuid[UUID_STR_LEN - 1] = '\0'; +end: + fclose(fp); + return ret; +} + +#endif /* _UST_CLOCK_H */ diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c index 7c1ae402c..60ec5c0b2 100644 --- a/src/bin/lttng-sessiond/ust-consumer.c +++ b/src/bin/lttng-sessiond/ust-consumer.c @@ -132,7 +132,7 @@ static int ask_channel_creation(struct ust_app_session *ua_sess, ua_sess->gid, consumer->net_seq_index, ua_chan->key, - ua_sess->uuid); + ua_sess->registry.uuid); health_code_update(); @@ -228,7 +228,6 @@ int ust_consumer_get_channel(struct consumer_socket *socket, } goto error; } - ua_chan->handle = ua_chan->obj->handle; /* Next, get all streams. */ while (1) { @@ -345,6 +344,7 @@ int ust_consumer_send_stream_to_ust(struct ust_app *app, } goto error; } + channel->handle = channel->obj->handle; error: return ret; diff --git a/src/bin/lttng-sessiond/ust-metadata.c b/src/bin/lttng-sessiond/ust-metadata.c new file mode 100644 index 000000000..f8871ecaa --- /dev/null +++ b/src/bin/lttng-sessiond/ust-metadata.c @@ -0,0 +1,676 @@ +/* + * ust-metadata.c + * + * LTTng-UST metadata generation + * + * Copyright (C) 2010-2013 Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 only, + * as published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ust-registry.h" +#include "ust-clock.h" +#include "ust-app.h" + +#ifndef max_t +#define max_t(type, a, b) ((type) ((a) > (b) ? (a) : (b))) +#endif + +static inline +int fls(unsigned int x) +{ + int r = 32; + + if (!x) + return 0; + if (!(x & 0xFFFF0000U)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF000000U)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF0000000U)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC0000000U)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000U)) { + x <<= 1; + r -= 1; + } + return r; +} + +static inline +int get_count_order(unsigned int count) +{ + int order; + + order = fls(count) - 1; + if (count & (count - 1)) + order++; + return order; +} + +/* + * Returns offset where to write in metadata array, or negative error value on error. + */ +static +ssize_t metadata_reserve(struct ust_registry_session *session, size_t len) +{ + size_t new_len = session->metadata_len + len; + size_t new_alloc_len = new_len; + size_t old_alloc_len = session->metadata_alloc_len; + ssize_t ret; + + if (new_alloc_len > (UINT32_MAX >> 1)) + return -EINVAL; + if ((old_alloc_len << 1) > (UINT32_MAX >> 1)) + return -EINVAL; + + if (new_alloc_len > old_alloc_len) { + char *newptr; + + new_alloc_len = + max_t(size_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); + newptr = realloc(session->metadata, new_alloc_len); + if (!newptr) + return -ENOMEM; + session->metadata = newptr; + /* We zero directly the memory from start of allocation. */ + memset(&session->metadata[old_alloc_len], 0, new_alloc_len - old_alloc_len); + session->metadata_alloc_len = new_alloc_len; + } + ret = session->metadata_len; + session->metadata_len += len; + return ret; +} + +/* + * We have exclusive access to our metadata buffer (protected by the + * ust_lock), so we can do racy operations such as looking for + * remaining space left in packet and write, since mutual exclusion + * protects us from concurrent writes. + */ +static +int lttng_metadata_printf(struct ust_registry_session *session, + const char *fmt, ...) +{ + char *str = NULL; + size_t len; + va_list ap; + ssize_t offset; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0) + return -ENOMEM; + + len = strlen(str); + offset = metadata_reserve(session, len); + if (offset < 0) { + ret = offset; + goto end; + } + memcpy(&session->metadata[offset], str, len); + DBG3("Append to metadata: \"%s\"", str); + ret = 0; + +end: + free(str); + return ret; +} + +static +int _lttng_field_statedump(struct ust_registry_session *session, + const struct ustctl_field *field) +{ + int ret = 0; + const char *bo_be = " byte_order = be;"; + const char *bo_le = " byte_order = le;"; + const char *bo_native = ""; + const char *bo_reverse; + + if (session->byte_order == BIG_ENDIAN) + bo_reverse = bo_le; + else + bo_reverse = bo_be; + + switch (field->type.atype) { + case ustctl_atype_integer: + ret = lttng_metadata_printf(session, + " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s;\n", + field->type.u.basic.integer.size, + field->type.u.basic.integer.alignment, + field->type.u.basic.integer.signedness, + (field->type.u.basic.integer.encoding == ustctl_encode_none) + ? "none" + : (field->type.u.basic.integer.encoding == ustctl_encode_UTF8) + ? "UTF8" + : "ASCII", + field->type.u.basic.integer.base, + field->type.u.basic.integer.reverse_byte_order ? bo_reverse : bo_native, + field->name); + break; + case ustctl_atype_float: + ret = lttng_metadata_printf(session, + " floating_point { exp_dig = %u; mant_dig = %u; align = %u;%s } _%s;\n", + field->type.u.basic._float.exp_dig, + field->type.u.basic._float.mant_dig, + field->type.u.basic._float.alignment, + field->type.u.basic.integer.reverse_byte_order ? bo_reverse : bo_native, + field->name); + break; + case ustctl_atype_enum: + return -EINVAL; + case ustctl_atype_array: + { + const struct ustctl_basic_type *elem_type; + + elem_type = &field->type.u.array.elem_type; + ret = lttng_metadata_printf(session, + " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n", + elem_type->u.basic.integer.size, + elem_type->u.basic.integer.alignment, + elem_type->u.basic.integer.signedness, + (elem_type->u.basic.integer.encoding == ustctl_encode_none) + ? "none" + : (elem_type->u.basic.integer.encoding == ustctl_encode_UTF8) + ? "UTF8" + : "ASCII", + elem_type->u.basic.integer.base, + elem_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native, + field->name, field->type.u.array.length); + break; + } + case ustctl_atype_sequence: + { + const struct ustctl_basic_type *elem_type; + const struct ustctl_basic_type *length_type; + + elem_type = &field->type.u.sequence.elem_type; + length_type = &field->type.u.sequence.length_type; + ret = lttng_metadata_printf(session, + " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } __%s_length;\n", + length_type->u.basic.integer.size, + (unsigned int) length_type->u.basic.integer.alignment, + length_type->u.basic.integer.signedness, + (length_type->u.basic.integer.encoding == ustctl_encode_none) + ? "none" + : ((length_type->u.basic.integer.encoding == ustctl_encode_UTF8) + ? "UTF8" + : "ASCII"), + length_type->u.basic.integer.base, + length_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native, + field->name); + if (ret) + return ret; + + ret = lttng_metadata_printf(session, + " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[ __%s_length ];\n", + elem_type->u.basic.integer.size, + (unsigned int) elem_type->u.basic.integer.alignment, + elem_type->u.basic.integer.signedness, + (elem_type->u.basic.integer.encoding == ustctl_encode_none) + ? "none" + : ((elem_type->u.basic.integer.encoding == ustctl_encode_UTF8) + ? "UTF8" + : "ASCII"), + elem_type->u.basic.integer.base, + elem_type->u.basic.integer.reverse_byte_order ? bo_reverse : bo_native, + field->name, + field->name); + break; + } + + case ustctl_atype_string: + /* Default encoding is UTF8 */ + ret = lttng_metadata_printf(session, + " string%s _%s;\n", + field->type.u.basic.string.encoding == ustctl_encode_ASCII ? + " { encoding = ASCII; }" : "", + field->name); + break; + default: + return -EINVAL; + } + return ret; +} + +static +int _lttng_context_metadata_statedump(struct ust_registry_session *session, + size_t nr_ctx_fields, + struct ustctl_field *ctx) +{ + int ret = 0; + int i; + + if (!ctx) + return 0; + for (i = 0; i < nr_ctx_fields; i++) { + const struct ustctl_field *field = &ctx[i]; + + ret = _lttng_field_statedump(session, field); + if (ret) + return ret; + } + return ret; +} + +static +int _lttng_fields_metadata_statedump(struct ust_registry_session *session, + struct ust_registry_event *event) +{ + int ret = 0; + int i; + + for (i = 0; i < event->nr_fields; i++) { + const struct ustctl_field *field = &event->fields[i]; + + ret = _lttng_field_statedump(session, field); + if (ret) + return ret; + } + return ret; +} + +/* + * Should be called with session registry mutex held. + */ +int ust_metadata_event_statedump(struct ust_registry_session *session, + struct ust_registry_channel *chan, + struct ust_registry_event *event) +{ + int ret = 0; + + /* Don't dump metadata events */ + if (chan->chan_id == -1U) + return 0; + + ret = lttng_metadata_printf(session, + "event {\n" + " name = \"%s\";\n" + " id = %u;\n" + " stream_id = %u;\n", + event->name, + event->id, + chan->chan_id); + if (ret) + goto end; + + ret = lttng_metadata_printf(session, + " loglevel = %d;\n", + event->loglevel); + if (ret) + goto end; + + if (event->model_emf_uri) { + ret = lttng_metadata_printf(session, + " model.emf.uri = \"%s\";\n", + event->model_emf_uri); + if (ret) + goto end; + } + +#if 0 /* context for events not supported */ + if (event->ctx) { + ret = lttng_metadata_printf(session, + " context := struct {\n"); + if (ret) + goto end; + } + ret = _lttng_context_metadata_statedump(session, event->ctx); + if (ret) + goto end; + if (event->ctx) { + ret = lttng_metadata_printf(session, + " };\n"); + if (ret) + goto end; + } +#endif + ret = lttng_metadata_printf(session, + " fields := struct {\n" + ); + if (ret) + goto end; + + ret = _lttng_fields_metadata_statedump(session, event); + if (ret) + goto end; + + ret = lttng_metadata_printf(session, + " };\n" + "};\n\n"); + if (ret) + goto end; + +end: + return ret; +} + +/* + * Should be called with session registry mutex held. + */ +int ust_metadata_channel_statedump(struct ust_registry_session *session, + struct ust_registry_channel *chan) +{ + int ret = 0; + + /* Don't dump metadata events */ + if (chan->chan_id == -1U) + return 0; + + if (!chan->header_type) + return -EINVAL; + + ret = lttng_metadata_printf(session, + "stream {\n" + " id = %u;\n" + " event.header := %s;\n" + " packet.context := struct packet_context;\n", + chan->chan_id, + chan->header_type == USTCTL_CHANNEL_HEADER_COMPACT ? + "struct event_header_compact" : + "struct event_header_large"); + if (ret) + goto end; + + if (chan->ctx_fields) { + ret = lttng_metadata_printf(session, + " event.context := struct {\n"); + if (ret) + goto end; + } + ret = _lttng_context_metadata_statedump(session, + chan->nr_ctx_fields, + chan->ctx_fields); + if (ret) + goto end; + if (chan->ctx_fields) { + ret = lttng_metadata_printf(session, + " };\n"); + if (ret) + goto end; + } + + ret = lttng_metadata_printf(session, + "};\n\n"); + +end: + return ret; +} + +static +int _lttng_stream_packet_context_declare(struct ust_registry_session *session) +{ + return lttng_metadata_printf(session, + "struct packet_context {\n" + " uint64_clock_monotonic_t timestamp_begin;\n" + " uint64_clock_monotonic_t timestamp_end;\n" + " uint64_t content_size;\n" + " uint64_t packet_size;\n" + " unsigned long events_discarded;\n" + " uint32_t cpu_id;\n" + "};\n\n" + ); +} + +/* + * Compact header: + * id: range: 0 - 30. + * id 31 is reserved to indicate an extended header. + * + * Large header: + * id: range: 0 - 65534. + * id 65535 is reserved to indicate an extended header. + */ +static +int _lttng_event_header_declare(struct ust_registry_session *session) +{ + return lttng_metadata_printf(session, + "struct event_header_compact {\n" + " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n" + " variant {\n" + " struct {\n" + " uint27_clock_monotonic_t timestamp;\n" + " } compact;\n" + " struct {\n" + " uint32_t id;\n" + " uint64_clock_monotonic_t timestamp;\n" + " } extended;\n" + " } v;\n" + "} align(%u);\n" + "\n" + "struct event_header_large {\n" + " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n" + " variant {\n" + " struct {\n" + " uint32_clock_monotonic_t timestamp;\n" + " } compact;\n" + " struct {\n" + " uint32_t id;\n" + " uint64_clock_monotonic_t timestamp;\n" + " } extended;\n" + " } v;\n" + "} align(%u);\n\n", + session->uint32_t_alignment, + session->uint16_t_alignment + ); +} + +/* + * Approximation of NTP time of day to clock monotonic correlation, + * taken at start of trace. + * Yes, this is only an approximation. Yes, we can (and will) do better + * in future versions. + */ +static +uint64_t measure_clock_offset(void) +{ + uint64_t offset, monotonic[2], realtime; + struct timespec rts = { 0, 0 }; + int ret; + + monotonic[0] = trace_clock_read64(); + ret = clock_gettime(CLOCK_REALTIME, &rts); + if (ret < 0) + return 0; + monotonic[1] = trace_clock_read64(); + offset = (monotonic[0] + monotonic[1]) >> 1; + realtime = (uint64_t) rts.tv_sec * 1000000000ULL; + realtime += rts.tv_nsec; + offset = realtime - offset; + return offset; +} + + +/* + * Should be called with session registry mutex held. + */ +int ust_metadata_session_statedump(struct ust_registry_session *session, + struct ust_app *app) +{ + unsigned char *uuid_c; + char uuid_s[UUID_STR_LEN], + clock_uuid_s[UUID_STR_LEN]; + int ret = 0; + char hostname[HOST_NAME_MAX]; + + uuid_c = session->uuid; + + snprintf(uuid_s, sizeof(uuid_s), + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uuid_c[0], uuid_c[1], uuid_c[2], uuid_c[3], + uuid_c[4], uuid_c[5], uuid_c[6], uuid_c[7], + uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11], + uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]); + + ret = lttng_metadata_printf(session, + "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n" + "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n" + "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n" + "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n" + "typealias integer { size = %u; align = %u; signed = false; } := unsigned long;\n" + "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n" + "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n" + "\n" + "trace {\n" + " major = %u;\n" + " minor = %u;\n" + " uuid = \"%s\";\n" + " byte_order = %s;\n" + " packet.header := struct {\n" + " uint32_t magic;\n" + " uint8_t uuid[16];\n" + " uint32_t stream_id;\n" + " };\n" + "};\n\n", + session->uint8_t_alignment, + session->uint16_t_alignment, + session->uint32_t_alignment, + session->uint64_t_alignment, + session->bits_per_long, + session->long_alignment, + CTF_SPEC_MAJOR, + CTF_SPEC_MINOR, + uuid_s, + session->byte_order == BIG_ENDIAN ? "be" : "le" + ); + if (ret) + goto end; + + /* ignore error, just use empty string if error. */ + hostname[0] = '\0'; + ret = gethostname(hostname, sizeof(hostname)); + if (ret && errno == ENAMETOOLONG) + hostname[HOST_NAME_MAX - 1] = '\0'; + ret = lttng_metadata_printf(session, + "env {\n" + " hostname = \"%s\";\n" + " domain = \"ust\";\n" + " tracer_name = \"lttng-ust\";\n" + " tracer_major = %u;\n" + " tracer_minor = %u;\n" + " tracer_patchlevel = %u;\n" + "};\n\n", + hostname, + app->version.major, + app->version.minor, + app->version.patchlevel + ); + if (ret) + goto end; + + /* + * If per-application registry, we can output extra information + * about the application. + */ + if (app) { + ret = lttng_metadata_printf(session, + " vpid = %d;\n" + " procname = \"%s\";\n" + "};\n\n", + (int) app->pid, + app->name + ); + if (ret) + goto end; + } + + ret = lttng_metadata_printf(session, + "};\n\n" + ); + if (ret) + goto end; + + + ret = lttng_metadata_printf(session, + "clock {\n" + " name = %s;\n", + "monotonic" + ); + if (ret) + goto end; + + if (!trace_clock_uuid(clock_uuid_s)) { + ret = lttng_metadata_printf(session, + " uuid = \"%s\";\n", + clock_uuid_s + ); + if (ret) + goto end; + } + + ret = lttng_metadata_printf(session, + " description = \"Monotonic Clock\";\n" + " freq = %" PRIu64 "; /* Frequency, in Hz */\n" + " /* clock value offset from Epoch is: offset * (1/freq) */\n" + " offset = %" PRIu64 ";\n" + "};\n\n", + trace_clock_freq(), + measure_clock_offset() + ); + if (ret) + goto end; + + ret = lttng_metadata_printf(session, + "typealias integer {\n" + " size = 27; align = 1; signed = false;\n" + " map = clock.monotonic.value;\n" + "} := uint27_clock_monotonic_t;\n" + "\n" + "typealias integer {\n" + " size = 32; align = %u; signed = false;\n" + " map = clock.monotonic.value;\n" + "} := uint32_clock_monotonic_t;\n" + "\n" + "typealias integer {\n" + " size = 64; align = %u; signed = false;\n" + " map = clock.monotonic.value;\n" + "} := uint64_clock_monotonic_t;\n\n", + session->uint32_t_alignment, + session->uint64_t_alignment + ); + if (ret) + goto end; + + ret = _lttng_stream_packet_context_declare(session); + if (ret) + goto end; + + ret = _lttng_event_header_declare(session); + if (ret) + goto end; + +end: + return ret; +} diff --git a/src/bin/lttng-sessiond/ust-registry.c b/src/bin/lttng-sessiond/ust-registry.c new file mode 100644 index 000000000..31d33df4c --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry.c @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#define _GNU_SOURCE +#include + +#include +#include "ust-registry.h" + +/* + * Hash table match function for event in the registry. + */ +static int ht_match_event(struct cds_lfht_node *node, const void *_key) +{ + struct ust_registry_event *event; + const struct ust_registry_event *key; + + assert(node); + assert(_key); + + event = caa_container_of(node, struct ust_registry_event, node.node); + assert(event); + key = _key; + + /* It has to be a perfect match. */ + if (strncmp(event->name, key->name, sizeof(event->name)) != 0) { + goto no_match; + } + + /* It has to be a perfect match. */ + if (strncmp(event->signature, key->signature, + strlen(event->signature) != 0)) { + goto no_match; + } + + /* Match */ + return 1; + +no_match: + return 0; +} + +/* + * Allocate event and initialize it. This does NOT set a valid event id from a + * registry. + */ +static struct ust_registry_event *alloc_event(int session_objd, + int channel_objd, char *name, char *sig, size_t nr_fields, + struct ustctl_field *fields, int loglevel, char *model_emf_uri) +{ + struct ust_registry_event *event = NULL; + + event = zmalloc(sizeof(*event)); + if (!event) { + PERROR("zmalloc ust registry event"); + goto error; + } + + event->session_objd = session_objd; + event->channel_objd = channel_objd; + /* Allocated by ustctl. */ + event->signature = sig; + event->nr_fields = nr_fields; + event->fields = fields; + event->loglevel = loglevel; + event->model_emf_uri = model_emf_uri; + if (name) { + /* Copy event name and force NULL byte. */ + strncpy(event->name, name, sizeof(event->name)); + event->name[sizeof(event->name) - 1] = '\0'; + } + lttng_ht_node_init_str(&event->node, event->name); + +error: + return event; +} + +/* + * Free event data structure. This does NOT delete it from any hash table. It's + * safe to pass a NULL pointer. This shoudl be called inside a call RCU if the + * event is previously deleted from a rcu hash table. + */ +static void destroy_event(struct ust_registry_event *event) +{ + if (!event) { + return; + } + + free(event->fields); + free(event->model_emf_uri); + free(event->signature); + free(event); +} + +/* + * Destroy event function call of the call RCU. + */ +static void destroy_event_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ust_registry_event *event = + caa_container_of(node, struct ust_registry_event, node); + + destroy_event(event); +} + +/* + * Find an event using the name and signature in the given registry. RCU read + * side lock MUST be acquired before calling this function and as long as the + * event reference is kept by the caller. + * + * On success, the event pointer is returned else NULL. + */ +struct ust_registry_event *ust_registry_find_event( + struct ust_registry_channel *chan, char *name, char *sig) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + struct ust_registry_event *event = NULL; + struct ust_registry_event key; + + assert(chan); + assert(name); + assert(sig); + + /* Setup key for the match function. */ + strncpy(key.name, name, sizeof(key.name)); + key.name[sizeof(key.name) - 1] = '\0'; + key.signature = sig; + + cds_lfht_lookup(chan->ht->ht, chan->ht->hash_fct(name, lttng_ht_seed), + chan->ht->match_fct, &key, &iter.iter); + node = lttng_ht_iter_get_node_str(&iter); + if (!node) { + goto end; + } + event = caa_container_of(node, struct ust_registry_event, node); + +end: + return event; +} + +/* + * Create a ust_registry_event from the given parameters and add it to the + * registry hash table. If event_id is valid, it is set with the newly created + * event id. + * + * On success, return 0 else a negative value. The created event MUST be unique + * so on duplicate entry -EINVAL is returned. On error, event_id is untouched. + * + * Should be called with session registry mutex held. + */ +int ust_registry_create_event(struct ust_registry_session *session, + struct ust_registry_channel *chan, + int session_objd, int channel_objd, char *name, char *sig, + size_t nr_fields, struct ustctl_field *fields, int loglevel, + char *model_emf_uri, uint32_t *event_id) +{ + int ret; + struct cds_lfht_node *nptr; + struct ust_registry_event *event = NULL; + + assert(session); + assert(chan); + assert(name); + assert(sig); + + /* + * This should not happen but since it comes from the UST tracer, an + * external party, don't assert and simply validate values. + */ + if (session_objd < 0 || channel_objd < 0) { + ret = -EINVAL; + goto error; + } + + /* Check if we've reached the maximum possible id. */ + if (ust_registry_is_max_id(chan->used_event_id)) { + ret = -ENOENT; + goto error; + } + + event = alloc_event(session_objd, channel_objd, name, sig, nr_fields, + fields, loglevel, model_emf_uri); + if (!event) { + ret = -ENOMEM; + goto error; + } + + event->id = ust_registry_get_next_event_id(chan); + + DBG3("UST registry creating event with event: %s, sig: %s, id: %u, " + "chan_objd: %u, sess_objd: %u", event->name, event->signature, + event->id, event->channel_objd, event->session_objd); + + rcu_read_lock(); + /* + * This is an add unique with a custom match function for event. The node + * are matched using the event name and signature. + */ + nptr = cds_lfht_add_unique(chan->ht->ht, chan->ht->hash_fct(event->node.key, + lttng_ht_seed), chan->ht->match_fct, event, &event->node.node); + if (nptr != &event->node.node) { + ERR("UST registry create event add unique failed for event: %s, " + "sig: %s, id: %u, chan_objd: %u, sess_objd: %u", event->name, + event->signature, event->id, event->channel_objd, + event->session_objd); + ret = -EINVAL; + goto error_unlock; + } + + /* Set event id if user wants it. */ + if (event_id) { + *event_id = event->id; + } + rcu_read_unlock(); + + /* Append to metadata */ + ret = ust_metadata_event_statedump(session, chan, event); + if (ret) { + ERR("Error appending event metadata (errno = %d)", ret); + return ret; + } + + return 0; + +error_unlock: + rcu_read_unlock(); +error: + destroy_event(event); + return ret; +} + +/* + * For a given event in a registry, delete the entry and destroy the event. + * This MUST be called within a RCU read side lock section. + */ +void ust_registry_destroy_event(struct ust_registry_channel *chan, + struct ust_registry_event *event) +{ + int ret; + struct lttng_ht_iter iter; + + assert(chan); + assert(event); + + /* Delete the node first. */ + iter.iter.node = &event->node.node; + ret = lttng_ht_del(chan->ht, &iter); + assert(!ret); + + call_rcu(&event->node.head, destroy_event_rcu); + + return; +} + +/* + * Initialize registry with default values. + */ +void ust_registry_channel_init(struct ust_registry_session *session, + struct ust_registry_channel *chan) +{ + assert(chan); + + memset(chan, 0, sizeof(struct ust_registry_channel)); + + chan->ht = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + assert(chan->ht); + + /* Set custom match function. */ + chan->ht->match_fct = ht_match_event; +} + +/* + * Destroy every element of the registry and free the memory. This does NOT + * free the registry pointer since it might not have been allocated before so + * it's the caller responsability. + * + * This MUST be called within a RCU read side lock section. + */ +void ust_registry_channel_destroy(struct ust_registry_session *session, + struct ust_registry_channel *chan) +{ + struct lttng_ht_iter iter; + struct ust_registry_event *event; + + assert(chan); + + /* Destroy all event associated with this registry. */ + cds_lfht_for_each_entry(chan->ht->ht, &iter.iter, event, node.node) { + /* Delete the node from the ht and free it. */ + ust_registry_destroy_event(chan, event); + } + lttng_ht_destroy(chan->ht); +} + +/* + * Initialize registry with default values. + */ +int ust_registry_session_init(struct ust_registry_session *session, + struct ust_app *app, + uint32_t bits_per_long, + uint32_t uint8_t_alignment, + uint32_t uint16_t_alignment, + uint32_t uint32_t_alignment, + uint32_t uint64_t_alignment, + uint32_t long_alignment, + int byte_order) +{ + int ret; + + assert(session); + + memset(session, 0, sizeof(struct ust_registry_session)); + + pthread_mutex_init(&session->lock, NULL); + session->bits_per_long = bits_per_long; + session->uint8_t_alignment = uint8_t_alignment; + session->uint16_t_alignment = uint16_t_alignment; + session->uint32_t_alignment = uint32_t_alignment; + session->uint64_t_alignment = uint64_t_alignment; + session->long_alignment = long_alignment; + session->byte_order = byte_order; + + ret = lttng_uuid_generate(session->uuid); + if (ret) { + ERR("Failed to generate UST uuid (errno = %d)", ret); + goto error; + } + + pthread_mutex_lock(&session->lock); + ret = ust_metadata_session_statedump(session, app); + pthread_mutex_unlock(&session->lock); + if (ret) { + ERR("Failed to generate session metadata (errno = %d)", ret); + goto error; + } + + return 0; + +error: + return -1; +} + +/* + * Destroy session registry. This does NOT free the given pointer since it + * might get passed as a reference. The registry lock should NOT be acquired. + */ +void ust_registry_session_destroy(struct ust_registry_session *reg) +{ + int ret; + + /* On error, EBUSY can be returned if lock. Code flow error. */ + ret = pthread_mutex_destroy(®->lock); + assert(!ret); + + free(reg->metadata); +} diff --git a/src/bin/lttng-sessiond/ust-registry.h b/src/bin/lttng-sessiond/ust-registry.h new file mode 100644 index 000000000..ae7ad3ae3 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-registry.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LTTNG_UST_REGISTRY_H +#define LTTNG_UST_REGISTRY_H + +#include +#include +#include + +#include +#include + +#define CTF_SPEC_MAJOR 1 +#define CTF_SPEC_MINOR 8 + +struct ust_app; + +struct ust_registry_session { + /* + * With multiple writers and readers, use this lock to access + * the registry. Use defined macros above to lock it. + * Can nest within the ust app session lock. + */ + pthread_mutex_t lock; + /* Next channel ID available for a newly registered channel. */ + uint32_t next_channel_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_channel_id; + /* Universal unique identifier used by the tracer. */ + unsigned char uuid[UUID_LEN]; + + /* session ABI description */ + + /* Size of long, in bits */ + unsigned int bits_per_long; + /* Alignment, in bits */ + unsigned int uint8_t_alignment, + uint16_t_alignment, + uint32_t_alignment, + uint64_t_alignment, + long_alignment; + /* endianness */ + int byte_order; /* BIG_ENDIAN or LITTLE_ENDIAN */ + + /* Generated metadata. */ + char *metadata; /* NOT null-terminated ! Use memcpy. */ + size_t metadata_len, metadata_alloc_len; +}; + +struct ust_registry_channel { + /* Id set when replying to a register channel. */ + uint32_t chan_id; + enum ustctl_channel_header header_type; + + /* + * Hash table containing events sent by the UST tracer. MUST be accessed + * with a RCU read side lock acquired. + */ + struct lttng_ht *ht; + /* Next event ID available for a newly registered event. */ + uint32_t next_event_id; + /* Once this value reaches UINT32_MAX, no more id can be allocated. */ + uint32_t used_event_id; + /* + * Context fields of the registry. Context are per channel. Allocated by a + * register channel notification from the UST tracer. + */ + size_t nr_ctx_fields; + struct ustctl_field *ctx_fields; +}; + +/* + * Event registered from a UST tracer sent to the session daemon. This is + * indexed and matched by . + */ +struct ust_registry_event { + int id; + /* Both objd are set by the tracer. */ + int session_objd; + int channel_objd; + /* Name of the event returned by the tracer. */ + char name[LTTNG_UST_SYM_NAME_LEN]; + char *signature; + int loglevel; + size_t nr_fields; + struct ustctl_field *fields; + char *model_emf_uri; + /* + * Node in the ust-registry hash table. The event name is used to + * initialize the node and the event_name/signature for the match function. + */ + struct lttng_ht_node_str node; +}; + +/* + * Validate that the id has reached the maximum allowed or not. + * + * Return 0 if NOT else 1. + */ +static inline int ust_registry_is_max_id(uint32_t id) +{ + return (id == UINT32_MAX) ? 1 : 0; +} + +/* + * Return next available event id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique channel ID. If max is reached, the used_event_id counter is + * returned. + */ +static inline uint32_t ust_registry_get_next_event_id( + struct ust_registry_channel *r) +{ + if (ust_registry_is_max_id(r->used_event_id)) { + return r->used_event_id; + } + + r->used_event_id++; + return r->next_event_id++; +} + +/* + * Return next available channel id and increment the used counter. The + * ust_registry_is_max_id function MUST be called before in order to validate + * if the maximum number of IDs have been reached. If not, it is safe to call + * this function. + * + * Return a unique channel ID. If max is reached, the used_channel_id counter + * is returned. + */ +static inline uint32_t ust_registry_get_next_chan_id( + struct ust_registry_session *r) +{ + if (ust_registry_is_max_id(r->used_channel_id)) { + return r->used_channel_id; + } + + r->used_channel_id++; + return r->next_channel_id++; +} + +/* + * Return registry event count. This is read atomically. + */ +static inline uint32_t ust_registry_get_event_count( + struct ust_registry_channel *r) +{ + return (uint32_t) uatomic_read(&r->used_event_id); +} + +void ust_registry_channel_init(struct ust_registry_session *session, + struct ust_registry_channel *chan); +void ust_registry_channel_destroy(struct ust_registry_session *session, + struct ust_registry_channel *chan); + +int ust_registry_session_init(struct ust_registry_session *session, + struct ust_app *app, + uint32_t bits_per_long, + uint32_t uint8_t_alignment, + uint32_t uint16_t_alignment, + uint32_t uint32_t_alignment, + uint32_t uint64_t_alignment, + uint32_t long_alignment, + int byte_order); + +void ust_registry_session_destroy(struct ust_registry_session *session); + +int ust_registry_create_event(struct ust_registry_session *session, + struct ust_registry_channel *channel, + int session_objd, int channel_objd, char *name, char *sig, + size_t nr_fields, struct ustctl_field *fields, int loglevel, + char *model_emf_uri, uint32_t *event_id); +struct ust_registry_event *ust_registry_find_event( + struct ust_registry_channel *chan, char *name, char *sig); +void ust_registry_destroy_event(struct ust_registry_channel *chan, + struct ust_registry_event *event); + +/* app can be NULL for registry shared across applications. */ +int ust_metadata_session_statedump(struct ust_registry_session *session, + struct ust_app *app); +int ust_metadata_channel_statedump(struct ust_registry_session *session, + struct ust_registry_channel *chan); +int ust_metadata_event_statedump(struct ust_registry_session *session, + struct ust_registry_channel *chan, + struct ust_registry_event *event); + +#endif /* LTTNG_UST_REGISTRY_H */ diff --git a/src/bin/lttng-sessiond/ust-thread.c b/src/bin/lttng-sessiond/ust-thread.c new file mode 100644 index 000000000..76d6ef99d --- /dev/null +++ b/src/bin/lttng-sessiond/ust-thread.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#define _GNU_SOURCE +#include + +#include +#include + +#include "fd-limit.h" +#include "lttng-sessiond.h" +#include "ust-thread.h" + +/* + * This thread manage application notify communication. + */ +void *ust_thread_manage_notify(void *data) +{ + int i, ret, pollfd; + uint32_t revents, nb_fd; + struct lttng_poll_event events; + + DBG("[ust-thread] Manage application notify command"); + + rcu_register_thread(); + rcu_thread_online(); + + ret = sessiond_set_thread_pollset(&events, 2); + if (ret < 0) { + goto error_poll_create; + } + + /* Add notify pipe to the pollset. */ + ret = lttng_poll_add(&events, apps_cmd_notify_pipe[0], LPOLLIN | LPOLLERR); + if (ret < 0) { + goto error; + } + + while (1) { + DBG3("[ust-thread] Manage notify polling on %d fds", + LTTNG_POLL_GETNB(&events)); + + /* Inifinite blocking call, waiting for transmission */ +restart: + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + /* + * Restart interrupted system call. + */ + if (errno == EINTR) { + goto restart; + } + goto error; + } + + nb_fd = ret; + + for (i = 0; i < nb_fd; i++) { + /* Fetch once the poll data */ + revents = LTTNG_POLL_GETEV(&events, i); + pollfd = LTTNG_POLL_GETFD(&events, i); + + /* Thread quit pipe has been closed. Killing thread. */ + ret = sessiond_check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto exit; + } + + /* Inspect the apps cmd pipe */ + if (pollfd == apps_cmd_notify_pipe[0]) { + int sock; + + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("Apps notify command pipe error"); + goto error; + } else if (!(revents & LPOLLIN)) { + /* No POLLIN and not a catched error, stop the thread. */ + ERR("Notify command pipe failed. revent: %u", revents); + goto error; + } + + do { + /* Get socket from dispatch thread. */ + ret = read(apps_cmd_notify_pipe[0], &sock, sizeof(sock)); + } while (ret < 0 && errno == EINTR); + if (ret < 0 || ret < sizeof(sock)) { + PERROR("read apps notify pipe"); + goto error; + } + + ret = lttng_poll_add(&events, sock, + LPOLLIN | LPOLLERR | LPOLLHUP | LPOLLRDHUP); + if (ret < 0) { + /* + * It's possible we've reached the max poll fd allowed. + * Let's close the socket but continue normal execution. + */ + ret = close(sock); + if (ret) { + PERROR("close notify socket %d", sock); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + continue; + } + DBG3("UST thread notify added sock %d to pollset", sock); + } else { + /* + * At this point, we know that a registered application + * triggered the event. + */ + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + /* Removing from the poll set */ + ret = lttng_poll_del(&events, pollfd); + if (ret < 0) { + goto error; + } + + ret = close(pollfd); + if (ret < 0) { + PERROR("close sock %d", pollfd); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + } else if (revents & (LPOLLIN | LPOLLPRI)) { + ret = ust_app_recv_notify(pollfd); + if (ret < 0) { + ret = lttng_poll_del(&events, pollfd); + if (ret < 0) { + goto error; + } + + ret = close(pollfd); + if (ret < 0) { + PERROR("close sock %d", pollfd); + } + lttng_fd_put(LTTNG_FD_APPS, 1); + } + } else { + ERR("Unknown poll events %u for sock %d", revents, pollfd); + continue; + } + } + } + } + +exit: +error: + lttng_poll_clean(&events); +error_poll_create: + utils_close_pipe(apps_cmd_notify_pipe); + apps_cmd_notify_pipe[0] = apps_cmd_notify_pipe[1] = -1; + DBG("Application notify communication apps thread cleanup complete"); + rcu_thread_offline(); + rcu_unregister_thread(); + return NULL; +} diff --git a/src/bin/lttng-sessiond/ust-thread.h b/src/bin/lttng-sessiond/ust-thread.h new file mode 100644 index 000000000..0292df983 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-thread.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2013 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, version 2 only, as + * published by the Free Software Foundation. + * + * This program 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef UST_THREAD_H +#define UST_THREAD_H + +#ifdef HAVE_LIBLTTNG_UST_CTL + +void *ust_thread_manage_notify(void *data); + +#else /* HAVE_LIBLTTNG_UST_CTL */ + +void *ust_thread_manage_notify(void *data) +{ + return NULL; +} + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +#endif /* UST_THREAD_H */ diff --git a/src/common/compat/uuid.h b/src/common/compat/uuid.h index 5bf8beb40..35faf5388 100644 --- a/src/common/compat/uuid.h +++ b/src/common/compat/uuid.h @@ -28,10 +28,14 @@ * Includes final \0. */ #define UUID_STR_LEN 37 +#define UUID_LEN 16 #ifdef LTTNG_HAVE_LIBUUID #include +/* + * uuid_out is of len UUID_LEN. + */ static inline int lttng_uuid_generate(unsigned char *uuid_out) { @@ -43,6 +47,9 @@ int lttng_uuid_generate(unsigned char *uuid_out) #include #include +/* + * uuid_out is of len UUID_LEN. + */ static inline int lttng_uuid_generate(unsigned char *uuid_out) { diff --git a/src/common/consumer.c b/src/common/consumer.c index 09b3bee33..6272e19b7 100644 --- a/src/common/consumer.c +++ b/src/common/consumer.c @@ -1634,7 +1634,6 @@ int lttng_consumer_take_snapshot(struct lttng_consumer_stream *stream) assert(0); return -ENOSYS; } - } /* diff --git a/src/common/defaults.h b/src/common/defaults.h index 93eb176a0..83159d7d7 100644 --- a/src/common/defaults.h +++ b/src/common/defaults.h @@ -44,9 +44,6 @@ #define DEFAULT_GLOBAL_APPS_PIPE DEFAULT_UST_SOCK_DIR "/global" #define DEFAULT_TRACE_OUTPUT DEFAULT_HOME_DIR "/lttng" -#define DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait" -#define DEFAULT_HOME_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait-%u" - /* Default directory where the trace are written in per domain */ #define DEFAULT_KERNEL_TRACE_DIR "/kernel" #define DEFAULT_UST_TRACE_DIR "/ust" @@ -83,12 +80,25 @@ /* Default unix socket path */ #define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/client-lttng-sessiond" -#define DEFAULT_GLOBAL_APPS_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/apps-lttng-sessiond" -#define DEFAULT_HOME_APPS_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/apps-lttng-sessiond" #define DEFAULT_HOME_CLIENT_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/client-lttng-sessiond" #define DEFAULT_GLOBAL_HEALTH_UNIX_SOCK DEFAULT_LTTNG_RUNDIR "/health.sock" #define DEFAULT_HOME_HEALTH_UNIX_SOCK DEFAULT_LTTNG_HOME_RUNDIR "/health.sock" +#ifdef HAVE_LIBLTTNG_UST_CTL +#define DEFAULT_GLOBAL_APPS_UNIX_SOCK \ + DEFAULT_LTTNG_RUNDIR "/" LTTNG_UST_SOCK_FILENAME +#define DEFAULT_HOME_APPS_UNIX_SOCK \ + DEFAULT_LTTNG_HOME_RUNDIR "/" LTTNG_UST_SOCK_FILENAME +#define DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH \ + "/" LTTNG_UST_WAIT_FILENAME +#define DEFAULT_HOME_APPS_WAIT_SHM_PATH \ + DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH "-%d" + +#else +#define DEFAULT_GLOBAL_APPS_UNIX_SOCK +#define DEFAULT_HOME_APPS_UNIX_SOCK +#endif /* HAVE_LIBLTTNG_UST_CTL */ + /* * Value taken from the hard limit allowed by the kernel when using setrlimit * with RLIMIT_NOFILE on an Intel i7 CPU and Linux 3.0.3. diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h index c390a9f52..0293482fb 100644 --- a/src/common/sessiond-comm/sessiond-comm.h +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -172,7 +172,7 @@ struct lttcomm_proto_ops { * Data structure received from lttng client to session daemon. */ struct lttcomm_session_msg { - uint32_t cmd_type; /* enum lttcomm_sessiond_command */ + uint32_t cmd_type; /* enum lttcomm_sessiond_command */ struct lttng_session session; struct lttng_domain domain; union { @@ -233,9 +233,9 @@ struct lttng_filter_bytecode { * Data structure for the response from sessiond to the lttng client. */ struct lttcomm_lttng_msg { - uint32_t cmd_type; /* enum lttcomm_sessiond_command */ - uint32_t ret_code; /* enum lttcomm_return_code */ - uint32_t pid; /* pid_t */ + uint32_t cmd_type; /* enum lttcomm_sessiond_command */ + uint32_t ret_code; /* enum lttcomm_return_code */ + uint32_t pid; /* pid_t */ uint32_t data_size; /* Contains: trace_name + data */ char payload[]; @@ -292,19 +292,19 @@ struct lttcomm_consumer_msg { uint64_t session_id; } LTTNG_PACKED data_pending; struct { - uint64_t subbuf_size; /* bytes */ - uint64_t num_subbuf; /* power of 2 */ + uint64_t subbuf_size; /* bytes */ + uint64_t num_subbuf; /* power of 2 */ int overwrite; /* 1: overwrite, 0: discard */ unsigned int switch_timer_interval; /* usec */ unsigned int read_timer_interval; /* usec */ - int output; /* splice, mmap */ - int type; /* metadata or per_cpu */ - uint64_t session_id; /* Tracing session id */ - char pathname[PATH_MAX]; /* Channel file path. */ + int output; /* splice, mmap */ + int type; /* metadata or per_cpu */ + uint64_t session_id; /* Tracing session id */ + char pathname[PATH_MAX]; /* Channel file path. */ char name[LTTNG_SYMBOL_NAME_LEN]; /* Channel name. */ - uid_t uid; /* User ID of the session */ - gid_t gid; /* Group ID ot the session */ - int relayd_id; /* Relayd id if apply. */ + uid_t uid; /* User ID of the session */ + gid_t gid; /* Group ID ot the session */ + int relayd_id; /* Relayd id if apply. */ unsigned long key; /* Unique channel key. */ unsigned char uuid[UUID_STR_LEN]; /* uuid for ust tracer. */ } LTTNG_PACKED ask_channel; diff --git a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c b/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c index 39f74591f..898072227 100644 --- a/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c +++ b/src/lib/lttng-ctl/filter/filter-visitor-generate-bytecode.c @@ -107,11 +107,14 @@ int32_t bytecode_reserve(struct lttng_filter_bytecode_alloc **fb, uint32_t align return -EINVAL; if (new_alloc_len > old_alloc_len) { + struct lttng_filter_bytecode_alloc *newptr; + new_alloc_len = max_t(uint32_t, 1U << get_count_order(new_alloc_len), old_alloc_len << 1); - *fb = realloc(*fb, new_alloc_len); - if (!*fb) + newptr = realloc(*fb, new_alloc_len); + if (!newptr) return -ENOMEM; + *fb = newptr; /* We zero directly the memory from start of allocation. */ memset(&((char *) *fb)[old_alloc_len], 0, new_alloc_len - old_alloc_len); (*fb)->alloc_len = new_alloc_len; diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c index 205744ef7..8f63bf3e8 100644 --- a/src/lib/lttng-ctl/lttng-ctl.c +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -1589,7 +1589,7 @@ int _lttng_create_session_ext(const char *name, const char *url, struct lttcomm_session_msg lsm; struct lttng_uri *uris = NULL; - if (name == NULL || datetime == NULL) { + if (name == NULL || datetime == NULL || url == NULL) { return -LTTNG_ERR_INVALID; } -- 2.34.1