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 \
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
#include <urcu/wfqueue.h>
#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/compat/poll.h>
#include <common/compat/socket.h>
#include "session.h"
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 */
#include <config.h>
#include <common/common.h>
-#include <common/compat/poll.h>
#include <common/compat/socket.h>
#include <common/defaults.h>
#include <common/kernel-consumer/kernel-consumer.h>
#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;
*/
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;
/*
* 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) {
}
/* 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;
}
*
* 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;
/* 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;
}
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;
* 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;
}
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;
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;
{
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");
health_code_update();
- ret = create_thread_poll_set(&events, 2);
+ ret = sessiond_set_thread_pollset(&events, 2);
if (ret < 0) {
goto error_poll_create;
}
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;
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;
}
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();
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.
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");
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) {
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");
* 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;
}
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;
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");
* 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;
}
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;
* 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;
}
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;
/* 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 */
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 */
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);
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 */
* 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;
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);
}
* 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;
}
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",
* 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) {
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);
}
/*
* 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;
}
/* 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;
* 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;
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;
}
*/
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;
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. */
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.
/*
* 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,
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();
/*
* 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.
*/
/* 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();
/*
* 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,
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);
}
}
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;
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;
} 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
/*
* 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,
/*
* 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,
/*
* 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,
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;
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;
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,
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);
/*
* 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)
}
/* 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;
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;
}
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);
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);
}
/*
/* 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",
/* Free memory */
call_rcu(<a->pid_n.head, delete_ust_app_rcu);
-error:
rcu_read_unlock();
return;
}
{
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);
}
/*
}
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. */
}
/* Cleanup the created session if it's the case. */
if (created) {
- destroy_session(app, ua_sess);
+ destroy_app_session(app, ua_sess);
}
}
}
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_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:
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);
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! */
goto end;
}
+ pthread_mutex_lock(&ua_sess->lock);
+
/* Upon restart, we skip the setup, already done */
if (ua_sess->started) {
goto skip_setup;
if (ret < 0) {
if (ret != -EEXIST) {
ERR("Trace directory creation error");
- goto error_rcu_unlock;
+ goto error_unlock;
}
}
}
/* 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();
} 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 */
health_code_update();
return 0;
-error_rcu_unlock:
+error_unlock:
+ pthread_mutex_unlock(&ua_sess->lock);
rcu_read_unlock();
health_code_update();
return -1;
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();
}
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.
*/
* 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;
}
}
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) {
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;
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();
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);
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;
}
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.
*/
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;
+}
#include <common/compat/uuid.h>
#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;
* 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];
};
/*
*/
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;
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 {
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];
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];
};
/*
*/
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
* 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
{
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);
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 */
return -ENOSYS;
}
static inline
+int ust_app_version(struct ust_app *app)
+{
+ return -ENOSYS;
+}
+static inline
void ust_app_unregister(int sock)
{
}
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 */
--- /dev/null
+/*
+ * Copyright (C) 2010 Pierre-Marc Fournier
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <time.h>
+#include <sys/time.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <common/compat/uuid.h>
+
+/* 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 */
ua_sess->gid,
consumer->net_seq_index,
ua_chan->key,
- ua_sess->uuid);
+ ua_sess->registry.uuid);
health_code_update();
}
goto error;
}
- ua_chan->handle = ua_chan->obj->handle;
/* Next, get all streams. */
while (1) {
}
goto error;
}
+ channel->handle = channel->obj->handle;
error:
return ret;
--- /dev/null
+/*
+ * ust-metadata.c
+ *
+ * LTTng-UST metadata generation
+ *
+ * Copyright (C) 2010-2013 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <limits.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <common/common.h>
+
+#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 <id> {\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 <id> {\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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 <assert.h>
+
+#include <common/common.h>
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 <pthread.h>
+#include <stdint.h>
+#include <lttng/ust-ctl.h>
+
+#include <common/hashtable/hashtable.h>
+#include <common/compat/uuid.h>
+
+#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 <event_name/signature>.
+ */
+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 */
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 <assert.h>
+
+#include <common/common.h>
+#include <common/utils.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2013 - David Goulet <dgoulet@efficios.com>
+ *
+ * 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 */
* Includes final \0.
*/
#define UUID_STR_LEN 37
+#define UUID_LEN 16
#ifdef LTTNG_HAVE_LIBUUID
#include <uuid/uuid.h>
+/*
+ * uuid_out is of len UUID_LEN.
+ */
static inline
int lttng_uuid_generate(unsigned char *uuid_out)
{
#include <uuid.h>
#include <stdint.h>
+/*
+ * uuid_out is of len UUID_LEN.
+ */
static inline
int lttng_uuid_generate(unsigned char *uuid_out)
{
assert(0);
return -ENOSYS;
}
-
}
/*
#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"
/* 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.
* 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 {
* 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[];
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;
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;
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;
}