AM_CFLAGS = -I$(PYTHON_INCLUDE) -I$(top_srcdir)/lib/lttng-ctl -I../common \
$(BUDDY_CFLAGS)
-EXTRA_DIST = lttng.i
-python_PYTHON = lttng.py
+EXTRA_DIST = lttng.i.in
+nodist_python_PYTHON = lttng.py
pyexec_LTLIBRARIES = _lttng.la
MAINTAINERCLEANFILES = lttng_wrap.c lttng.py
-_lttng_la_SOURCES = lttng_wrap.c
-
+nodist__lttng_la_SOURCES = lttng_wrap.c
_lttng_la_LDFLAGS = -module
-
_lttng_la_LIBADD = $(top_srcdir)/src/lib/lttng-ctl/liblttng-ctl.la \
$(top_srcdir)/src/common/sessiond-comm/libsessiond-comm.la
/* TODO : support UST (all direct kernel-ctl accesses). */
-/* the two threads (receive fd, poll and metadata) */
-static pthread_t data_thread, metadata_thread, sessiond_thread;
+/* threads (channel handling, poll, metadata, sessiond) */
+static pthread_t channel_thread, data_thread, metadata_thread, sessiond_thread;
/* to count the number of times the user pressed ctrl+c */
static int sigintcount = 0;
}
lttng_consumer_set_error_sock(ctx, ret);
+ /* Create thread to manage channels */
+ ret = pthread_create(&channel_thread, NULL, consumer_thread_channel_poll,
+ (void *) ctx);
+ if (ret != 0) {
+ perror("pthread_create");
+ goto error;
+ }
+
/* Create thread to manage the polling/writing of trace metadata */
ret = pthread_create(&metadata_thread, NULL, consumer_thread_metadata_poll,
(void *) ctx);
if (ret != 0) {
perror("pthread_create");
- goto error;
+ goto metadata_error;
}
/* Create thread to manage the polling/writing of trace data */
goto error;
}
+metadata_error:
+ ret = pthread_join(channel_thread, &status);
+ if (ret != 0) {
+ perror("pthread_join");
+ goto error;
+ }
+
if (!ret) {
ret = EXIT_SUCCESS;
lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_EXIT_SUCCESS);
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
}
chan->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE;
- chan->attr.switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER;
- chan->attr.read_timer_interval = DEFAULT_CHANNEL_READ_TIMER;
switch (dom) {
case LTTNG_DOMAIN_KERNEL:
default_get_kernel_channel_subbuf_size();
chan->attr.num_subbuf = DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM;
chan->attr.output = DEFAULT_KERNEL_CHANNEL_OUTPUT;
+ chan->attr.switch_timer_interval = DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER;
+ chan->attr.read_timer_interval = DEFAULT_KERNEL_CHANNEL_READ_TIMER;
break;
case LTTNG_DOMAIN_UST:
#if 0
chan->attr.subbuf_size = default_get_ust_channel_subbuf_size();
chan->attr.num_subbuf = DEFAULT_UST_CHANNEL_SUBBUF_NUM;
chan->attr.output = DEFAULT_UST_CHANNEL_OUTPUT;
+ chan->attr.switch_timer_interval = DEFAULT_UST_CHANNEL_SWITCH_TIMER;
+ chan->attr.read_timer_interval = DEFAULT_UST_CHANNEL_READ_TIMER;
break;
default:
goto error; /* Not implemented */
/*
* Used to keep a unique index for each relayd socket created where this value
* is associated with streams on the consumer so it can match the right relayd
- * to send to.
- *
- * This value should be incremented atomically for safety purposes and future
- * possible concurrent access.
+ * to send to. It must be accessed with the relayd_net_seq_idx_lock
+ * held.
*/
-static unsigned int relayd_net_seq_idx;
+static pthread_mutex_t relayd_net_seq_idx_lock = PTHREAD_MUTEX_INITIALIZER;
+static uint64_t relayd_net_seq_idx;
/*
* Create a session path used by list_lttng_sessions for the case that the
}
/* Set the network sequence index if not set. */
- if (consumer->net_seq_index == -1) {
+ if (consumer->net_seq_index == (uint64_t) -1ULL) {
+ pthread_mutex_lock(&relayd_net_seq_idx_lock);
/*
* Increment net_seq_idx because we are about to transfer the
* new relayd socket to the consumer.
+ * Assign unique key so the consumer can match streams.
*/
- uatomic_inc(&relayd_net_seq_idx);
- /* Assign unique key so the consumer can match streams */
- uatomic_set(&consumer->net_seq_index,
- uatomic_read(&relayd_net_seq_idx));
+ consumer->net_seq_index = ++relayd_net_seq_idx;
+ pthread_mutex_unlock(&relayd_net_seq_idx_lock);
}
/* Send relayd socket to consumer. */
void cmd_init(void)
{
/*
- * Set network sequence index to 1 for streams to match a relayd socket on
- * the consumer side.
+ * Set network sequence index to 1 for streams to match a relayd
+ * socket on the consumer side.
*/
- uatomic_set(&relayd_net_seq_idx, 1);
+ pthread_mutex_lock(&relayd_net_seq_idx_lock);
+ relayd_net_seq_idx = 1;
+ pthread_mutex_unlock(&relayd_net_seq_idx_lock);
DBG("Command subsystem initialized");
}
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <inttypes.h>
#include <common/common.h>
#include <common/defaults.h>
* negative value is sent back and both parameters are untouched.
*/
int consumer_recv_status_channel(struct consumer_socket *sock,
- unsigned long *key, unsigned int *stream_count)
+ uint64_t *key, unsigned int *stream_count)
{
int ret;
struct lttcomm_consumer_status_channel reply;
/* By default, consumer output is enabled */
output->enabled = 1;
output->type = type;
- output->net_seq_index = -1;
+ output->net_seq_index = (uint64_t) -1ULL;
output->socks = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
const char *name,
uid_t uid,
gid_t gid,
- int relayd_id,
- unsigned long key,
+ uint64_t relayd_id,
+ uint64_t key,
unsigned char *uuid)
{
assert(msg);
*/
void consumer_init_channel_comm_msg(struct lttcomm_consumer_msg *msg,
enum lttng_consumer_command cmd,
- int channel_key,
+ uint64_t channel_key,
uint64_t session_id,
const char *pathname,
uid_t uid,
gid_t gid,
- int relayd_id,
+ uint64_t relayd_id,
const char *name,
unsigned int nb_init_streams,
enum lttng_event_output output,
*/
void consumer_init_stream_comm_msg(struct lttcomm_consumer_msg *msg,
enum lttng_consumer_command cmd,
- int channel_key,
- int stream_key,
+ uint64_t channel_key,
+ uint64_t stream_key,
int cpu)
{
assert(msg);
*/
int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
struct lttcomm_sock *sock, struct consumer_output *consumer,
- enum lttng_stream_type type, unsigned int session_id)
+ enum lttng_stream_type type, uint64_t session_id)
{
int ret;
struct lttcomm_consumer_msg msg;
* This function has a different behavior with the consumer i.e. that it waits
* for a reply from the consumer if yes or no the data is pending.
*/
-int consumer_is_data_pending(unsigned int id,
+int consumer_is_data_pending(uint64_t session_id,
struct consumer_output *consumer)
{
int ret;
msg.cmd_type = LTTNG_CONSUMER_DATA_PENDING;
- msg.u.data_pending.session_id = (uint64_t) id;
+ msg.u.data_pending.session_id = session_id;
- DBG3("Consumer data pending for id %u", id);
+ DBG3("Consumer data pending for id %" PRIu64, session_id);
/* Send command for each consumer */
rcu_read_lock();
}
rcu_read_unlock();
- DBG("Consumer data is %s pending for session id %u",
- ret_code == 1 ? "" : "NOT", id);
+ DBG("Consumer data is %s pending for session id %" PRIu64,
+ ret_code == 1 ? "" : "NOT", session_id);
return ret_code;
error_unlock:
* side. It tells the consumer which streams goes to which relayd with this
* index. The relayd sockets are index with it on the consumer side.
*/
- int net_seq_index;
+ uint64_t net_seq_index;
/*
* Subdirectory path name used for both local and network consumer.
struct lttcomm_consumer_msg *msg);
int consumer_send_relayd_socket(struct consumer_socket *consumer_sock,
struct lttcomm_sock *sock, struct consumer_output *consumer,
- enum lttng_stream_type type, unsigned int session_id);
+ enum lttng_stream_type type, uint64_t session_id);
int consumer_send_destroy_relayd(struct consumer_socket *sock,
struct consumer_output *consumer);
int consumer_recv_status_reply(struct consumer_socket *sock);
int consumer_recv_status_channel(struct consumer_socket *sock,
- unsigned long *key, unsigned int *stream_count);
+ uint64_t *key, unsigned int *stream_count);
void consumer_output_send_destroy_relayd(struct consumer_output *consumer);
int consumer_create_socket(struct consumer_data *data,
struct consumer_output *output);
const char *name,
uid_t uid,
gid_t gid,
- int relayd_id,
- unsigned long key,
+ uint64_t relayd_id,
+ uint64_t key,
unsigned char *uuid);
void consumer_init_stream_comm_msg(struct lttcomm_consumer_msg *msg,
enum lttng_consumer_command cmd,
- int channel_key,
- int stream_key,
+ uint64_t channel_key,
+ uint64_t stream_key,
int cpu);
void consumer_init_channel_comm_msg(struct lttcomm_consumer_msg *msg,
enum lttng_consumer_command cmd,
- int channel_key,
+ uint64_t channel_key,
uint64_t session_id,
const char *pathname,
uid_t uid,
gid_t gid,
- int relayd_id,
+ uint64_t relayd_id,
const char *name,
unsigned int nb_init_streams,
enum lttng_event_output output,
int type);
-int consumer_is_data_pending(unsigned int id,
+int consumer_is_data_pending(uint64_t session_id,
struct consumer_output *consumer);
#endif /* _CONSUMER_H */
#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 */
int ustctl_send_channel_to_sessiond(int sock,
struct ustctl_consumer_channel *channel);
+int ustctl_channel_close_wait_fd(struct ustctl_consumer_channel *consumer_chan);
+int ustctl_channel_close_wakeup_fd(struct ustctl_consumer_channel *consumer_chan);
+int ustctl_channel_get_wait_fd(struct ustctl_consumer_channel *consumer_chan);
+int ustctl_channel_get_wakeup_fd(struct ustctl_consumer_channel *consumer_chan);
+
/*
* Send a NULL stream to finish iteration over all streams of a given
* channel.
*/
int ustctl_send_stream_to_sessiond(int sock,
struct ustctl_consumer_stream *stream);
+int ustctl_stream_close_wait_fd(struct ustctl_consumer_stream *stream);
int ustctl_stream_close_wakeup_fd(struct ustctl_consumer_stream *stream);
+int ustctl_stream_get_wait_fd(struct ustctl_consumer_stream *stream);
+int ustctl_stream_get_wakeup_fd(struct ustctl_consumer_stream *stream);
/* Create/destroy stream buffers for read */
struct ustctl_consumer_stream *
int cpu);
void ustctl_destroy_stream(struct ustctl_consumer_stream *stream);
-int ustctl_get_wait_fd(struct ustctl_consumer_stream *stream);
-int ustctl_get_wakeup_fd(struct ustctl_consumer_stream *stream);
-
/* For mmap mode, readable without "get" operation */
int ustctl_get_mmap_len(struct ustctl_consumer_stream *stream,
unsigned long *len);
#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();
+ /* 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);
- /*
- * 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);
-
- 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 a socket to a thread 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_socket_to_thread(int fd, int sock)
+{
+ int ret;
+
+ /* Sockets MUST be set or else this should not have been called. */
+ assert(fd >= 0);
+ assert(sock >= 0);
+
+ do {
+ ret = write(fd, &sock, sizeof(sock));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret != sizeof(sock)) {
+ PERROR("write apps pipe %d", fd);
+ 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, *tmp_wait_node;
+
+ 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);
}
+ lttng_fd_put(1, LTTNG_FD_APPS);
+ free(wait_node);
+ 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_safe(wait_node, tmp_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) {
+ /*
+ * @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);
+
+ /* Set app version. This call will print an error if needed. */
+ (void) ust_app_version(app);
+
+ /* Send notify socket through the notify pipe. */
+ ret = send_socket_to_thread(apps_cmd_notify_pipe[1],
+ app->notify_sock);
+ if (ret < 0) {
+ rcu_read_unlock();
+ session_unlock_list();
+ /* No notify thread, stop the UST tracing. */
+ goto error;
+ }
+
+ /*
+ * Update newly registered application with the tracing
+ * registry info already enabled information.
+ */
+ update_ust_app(app->sock);
+
+ /*
+ * Don't care about return value. Let the manage apps threads
+ * handle app unregistration upon socket close.
+ */
+ (void) ust_app_register_done(app->sock);
+
+ /*
+ * Even if the application socket has been closed, send the app
+ * to the thread and unregistration will take place at that
+ * place.
+ */
+ ret = send_socket_to_thread(apps_cmd_pipe[1], app->sock);
+ if (ret < 0) {
+ rcu_read_unlock();
+ session_unlock_list();
+ /* No apps. thread, stop the UST tracing. */
+ goto error;
+ }
+
+ 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");
}
+ lttng_fd_put(1, LTTNG_FD_APPS);
}
free(ust_cmd);
} while (node != NULL);
}
error:
+ /* Clean up wait queue. */
+ cds_list_for_each_entry_safe(wait_node, tmp_wait_node,
+ &wait_queue, head) {
+ cds_list_del(&wait_node->head);
+ free(wait_node);
+ }
+
DBG("Dispatch thread dying");
return NULL;
}
* 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;
sock = -1;
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 */
chan->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE;
chan->attr.subbuf_size = default_get_metadata_subbuf_size();
chan->attr.num_subbuf = DEFAULT_METADATA_SUBBUF_NUM;
- chan->attr.switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER;
- chan->attr.read_timer_interval = DEFAULT_CHANNEL_READ_TIMER;
+ chan->attr.switch_timer_interval = DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER;
+ chan->attr.read_timer_interval = DEFAULT_KERNEL_CHANNEL_READ_TIMER;
chan->attr.output = DEFAULT_KERNEL_CHANNEL_OUTPUT;
/* Init metadata */
lum->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE;
lum->attr.subbuf_size = default_get_metadata_subbuf_size();
lum->attr.num_subbuf = DEFAULT_METADATA_SUBBUF_NUM;
- lum->attr.switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER;
- lum->attr.read_timer_interval = DEFAULT_CHANNEL_READ_TIMER;
+ lum->attr.switch_timer_interval = DEFAULT_UST_CHANNEL_SWITCH_TIMER;
+ lum->attr.read_timer_interval = DEFAULT_UST_CHANNEL_READ_TIMER;
lum->attr.output = LTTNG_UST_MMAP;
lum->handle = -1;
}
socket = consumer_find_socket(consumer_fd, consumer);
+ if (!socket) {
+ ERR("Consumer socket fd %d not found in consumer obj %p",
+ consumer_fd, consumer);
+ }
end:
return socket;
* 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;
assert(node_ptr == &event->node.node);
}
+/*
+ * Close the notify socket from the given RCU head object. This MUST be called
+ * through a call_rcu().
+ */
+static void close_notify_sock_rcu(struct rcu_head *head)
+{
+ int ret;
+ struct ust_app_notify_sock_obj *obj =
+ caa_container_of(head, struct ust_app_notify_sock_obj, head);
+
+ /* Must have a valid fd here. */
+ assert(obj->fd >= 0);
+
+ ret = close(obj->fd);
+ if (ret) {
+ ERR("close notify sock %d RCU", obj->fd);
+ }
+ lttng_fd_put(LTTNG_FD_APPS, 1);
+
+ free(obj);
+}
+
/*
* Delete ust context safely. RCU read lock must be held before calling
* this function.
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",
free(ua_chan);
}
+/*
+ * For a given application and session, push metadata to consumer. The session
+ * lock MUST be acquired here before calling this.
+ *
+ * Return 0 on success else a negative error.
+ */
+static int push_metadata(struct ust_app *app, struct ust_app_session *ua_sess)
+{
+ int ret;
+ char *metadata_str = NULL;
+ size_t len, offset;
+ struct consumer_socket *socket;
+
+ assert(app);
+ assert(ua_sess);
+
+ if (!ua_sess->consumer || !ua_sess->metadata) {
+ /* No consumer means no stream associated so just return gracefully. */
+ ret = 0;
+ goto end;
+ }
+
+ rcu_read_lock();
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = find_consumer_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+ if (!socket) {
+ ret = -1;
+ goto error_rcu_unlock;
+ }
+
+ /*
+ * TODO: Currently, we hold the socket lock around sampling of the next
+ * metadata segment to ensure we send metadata over the consumer socket in
+ * the correct order. This makes the registry lock nest inside the socket
+ * lock.
+ *
+ * Please note that this is a temporary measure: we should move this lock
+ * back into ust_consumer_push_metadata() when the consumer gets the
+ * ability to reorder the metadata it receives.
+ */
+ pthread_mutex_lock(socket->lock);
+ pthread_mutex_lock(&ua_sess->registry.lock);
+
+ offset = ua_sess->registry.metadata_len_sent;
+ len = ua_sess->registry.metadata_len - ua_sess->registry.metadata_len_sent;
+ if (len == 0) {
+ DBG3("No metadata to push for session id %d", ua_sess->id);
+ ret = 0;
+ goto error_reg_unlock;
+ }
+ assert(len > 0);
+
+ /* Allocate only what we have to send. */
+ metadata_str = zmalloc(len);
+ if (!metadata_str) {
+ PERROR("zmalloc ust app metadata string");
+ ret = -ENOMEM;
+ goto error_reg_unlock;
+ }
+ /* Copy what we haven't send out. */
+ memcpy(metadata_str, ua_sess->registry.metadata + offset, len);
+
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+
+ ret = ust_consumer_push_metadata(socket, ua_sess, metadata_str, len,
+ offset);
+ if (ret < 0) {
+ pthread_mutex_unlock(socket->lock);
+ goto error_rcu_unlock;
+ }
+
+ /* Update len sent of the registry. */
+ pthread_mutex_lock(&ua_sess->registry.lock);
+ ua_sess->registry.metadata_len_sent += len;
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+ pthread_mutex_unlock(socket->lock);
+
+ rcu_read_unlock();
+ free(metadata_str);
+ return 0;
+
+error_reg_unlock:
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+ pthread_mutex_unlock(socket->lock);
+error_rcu_unlock:
+ rcu_read_unlock();
+ free(metadata_str);
+end:
+ return ret;
+}
+
+/*
+ * Send to the consumer a close metadata command for the given session. Once
+ * done, the metadata channel is deleted and the session metadata pointer is
+ * nullified. The session lock MUST be acquired here unless the application is
+ * in the destroy path.
+ *
+ * Return 0 on success else a negative value.
+ */
+static int close_metadata(struct ust_app *app, struct ust_app_session *ua_sess)
+{
+ int ret;
+ struct consumer_socket *socket;
+
+ assert(app);
+ assert(ua_sess);
+
+ /* Ignore if no metadata. Valid since it can be called on unregister. */
+ if (!ua_sess->metadata) {
+ ret = 0;
+ goto error;
+ }
+
+ rcu_read_lock();
+
+ /* Get consumer socket to use to push the metadata.*/
+ socket = find_consumer_socket_by_bitness(app->bits_per_long,
+ ua_sess->consumer);
+ if (!socket) {
+ ret = -1;
+ goto error_rcu_unlock;
+ }
+
+ ret = ust_consumer_close_metadata(socket, ua_sess->metadata);
+ if (ret < 0) {
+ goto error_rcu_unlock;
+ }
+
+error_rcu_unlock:
+ /* Destroy metadata on our side since we must not use it anymore. */
+ delete_ust_app_channel(-1, ua_sess->metadata, app);
+ ua_sess->metadata = NULL;
+
+ rcu_read_unlock();
+error:
+ return ret;
+}
+
/*
* Delete ust app session safely. RCU read lock must be held before calling
* 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;
+ assert(ua_sess);
+
if (ua_sess->metadata) {
- delete_ust_app_channel(sock, ua_sess->metadata);
+ /* Push metadata for application before freeing the application. */
+ (void) push_metadata(app, ua_sess);
+
+ /* And ask to close it for this session. */
+ (void) close_metadata(app, ua_sess);
}
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.
+ * Create the specified channel onto the UST tracer for a UST session. This
+ * MUST be called with UST app session lock held.
*
* Return 0 on success. On error, a negative value is returned.
*/
assert(ua_chan);
assert(consumer);
+ rcu_read_lock();
health_code_update();
/* Get the right consumer socket for the application. */
/*
* Compute the number of fd needed before receiving them. It must be 2 per
- * stream.
+ * stream (2 being the default value here).
*/
nb_fd = DEFAULT_UST_STREAM_FD_NUM * ua_chan->expected_stream_count;
goto error;
}
+ health_code_update();
+
/* Send all streams to application. */
cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) {
ret = ust_consumer_send_stream_to_ust(app, ua_chan, stream);
/* 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();
}
}
+ rcu_read_unlock();
return 0;
error_destroy:
(void) ust_consumer_destroy_channel(socket, ua_chan);
error:
health_code_update();
+ rcu_read_unlock();
return ret;
}
/*
* 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
DBG2("UST app session created successfully with handle %d", ret);
}
+ /*
+ * Assign consumer if not already set. For one application, there is only
+ * one possible consumer has of now.
+ */
+ if (!ua_sess->consumer) {
+ ua_sess->consumer = usess->consumer;
+ }
+
*ua_sess_ptr = ua_sess;
if (is_created) {
*is_created = created;
}
+
/* Everything went well. */
ret = 0;
/*
* 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)
{
int ret = 0;
struct ust_app_channel *metadata;
+ struct consumer_socket *socket;
assert(ua_sess);
assert(app);
+ assert(consumer);
if (ua_sess->metadata) {
/* Already exist. Return success. */
}
/* 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;
metadata->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE;
metadata->attr.subbuf_size = default_get_metadata_subbuf_size();
metadata->attr.num_subbuf = DEFAULT_METADATA_SUBBUF_NUM;
- metadata->attr.switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER;
- metadata->attr.read_timer_interval = DEFAULT_CHANNEL_READ_TIMER;
+ metadata->attr.switch_timer_interval = DEFAULT_UST_CHANNEL_SWITCH_TIMER;
+ metadata->attr.read_timer_interval = DEFAULT_UST_CHANNEL_READ_TIMER;
metadata->attr.output = LTTNG_UST_MMAP;
metadata->attr.type = LTTNG_UST_CHAN_METADATA;
- ret = create_ust_channel(app, ua_sess, metadata, consumer);
+ /* Get the right consumer socket for the application. */
+ socket = find_consumer_socket_by_bitness(app->bits_per_long, consumer);
+ if (!socket) {
+ ret = -EINVAL;
+ goto error_consumer;
+ }
+
+ /*
+ * Ask the metadata channel creation to the consumer. The metadata object
+ * will be created by the consumer and kept their. However, the stream is
+ * never added or monitored until we do a first push metadata to the
+ * consumer.
+ */
+ ret = ust_consumer_ask_channel(ua_sess, metadata, consumer, socket);
+ if (ret < 0) {
+ goto error_consumer;
+ }
+
+ /*
+ * The setup command will make the metadata stream be sent to the relayd,
+ * if applicable, and the thread managing the metadatas. This is important
+ * because after this point, if an error occurs, the only way the stream
+ * can be deleted is to be monitored in the consumer.
+ */
+ ret = ust_consumer_setup_metadata(socket, metadata);
if (ret < 0) {
- goto error_create;
+ goto error_consumer;
}
ua_sess->metadata = metadata;
- DBG2("UST metadata opened for app pid %d", app->pid);
+ DBG2("UST metadata created for app pid %d", app->pid);
end:
return 0;
-error_create:
- delete_ust_app_channel(metadata->is_sent ? app->sock : -1, metadata);
+error_consumer:
+ delete_ust_app_channel(-1, metadata, app);
error:
return ret;
}
}
/*
- * Return ust app pointer or NULL if not found.
+ * Return ust app pointer or NULL if not found. RCU read side lock MUST be
+ * acquired before calling this function.
*/
struct ust_app *ust_app_find_by_pid(pid_t pid)
{
+ struct ust_app *app = NULL;
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 caa_container_of(node, struct ust_app, pid_n);
+ app = caa_container_of(node, struct ust_app, pid_n);
error:
- rcu_read_unlock();
- return NULL;
+ return app;
}
/*
- * Using pid and uid (of the app), allocate a new ust_app struct and
- * add it to the global traceable app list.
+ * Allocate and init an UST app object using the registration information and
+ * the command socket. This is called when the command socket connects to the
+ * session daemon.
*
- * On success, return 0, else return malloc -ENOMEM, or -EINVAL if app
- * bitness is not supported.
+ * The object is returned on success or else NULL.
*/
-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->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ lta->ust_objd = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ lta->notify_sock = -1;
+
+ /* Copy name and make sure it's NULL terminated. */
+ strncpy(lta->name, msg->name, sizeof(lta->name));
+ lta->name[UST_APP_PROCNAME_LEN] = '\0';
+
+ /*
+ * Before this can be called, when receiving the registration information,
+ * the application compatibility is checked. So, at this point, the
+ * application can work with this session daemon.
+ */
+ 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;
+}
+
+/*
+ * For a given application object, add it to every hash table.
+ */
+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);
+
+ /* 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 "
+ "notify_sock:%d (version %d.%d)", app->pid, app->ppid, app->uid,
+ app->gid, app->sock, app->name, app->notify_sock, app->v_major,
+ app->v_minor);
rcu_read_unlock();
+}
- 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);
+/*
+ * Set the application version into the object.
+ *
+ * Return 0 on success else a negative value either an errno code or a
+ * LTTng-UST error code.
+ */
+int ust_app_version(struct ust_app *app)
+{
+ int ret;
- return 0;
+ assert(app);
+
+ ret = ustctl_tracer_version(app->sock, &app->version);
+ if (ret < 0) {
+ if (ret != -LTTNG_UST_ERR_EXITING && ret != -EPIPE) {
+ ERR("UST app %d verson failed with ret %d", app->sock, ret);
+ } else {
+ DBG3("UST app %d verion failed. Application is dead", app->sock);
+ }
+ }
+
+ return ret;
}
/*
/* 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. The thread handling the
+ * notify socket could have deleted the node so ignore on error because
+ * either way it's valid. The close of that socket is handled by the other
+ * thread.
+ */
+ iter.iter.node = <a->notify_sock_n.node;
+ (void) 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",
* Add session to list for teardown. This is safe since at this point we
* are the only one using this list.
*/
+ pthread_mutex_lock(&ua_sess->lock);
+
+ /*
+ * Normally, this is done in the delete session process which is
+ * executed in the call rcu below. However, upon registration we can't
+ * afford to wait for the grace period before pushing data or else the
+ * data pending feature can race between the unregistration and stop
+ * command where the data pending command is sent *before* the grace
+ * period ended.
+ *
+ * The close metadata below nullifies the metadata pointer in the
+ * session so the delete session will NOT push/close a second time.
+ */
+ (void) push_metadata(lta, ua_sess);
+ (void) close_metadata(lta, ua_sess);
+
cds_list_add(&ua_sess->teardown_node, <a->teardown_head);
+ pthread_mutex_unlock(&ua_sess->lock);
}
/* Free memory */
call_rcu(<a->pid_n.head, delete_ust_app_rcu);
-error:
rcu_read_unlock();
return;
}
assert(!ret);
}
+ /* Cleanup notify socket hash table */
+ cds_lfht_for_each_entry(ust_app_ht_by_notify_sock->ht, &iter.iter, app,
+ notify_sock_n.node) {
+ ret = lttng_ht_del(ust_app_ht_by_notify_sock, &iter);
+ assert(!ret);
+ }
+
/* Destroy is done only when the ht is empty */
lttng_ht_destroy(ust_app_ht);
lttng_ht_destroy(ust_app_ht_by_sock);
+ lttng_ht_destroy(ust_app_ht_by_notify_sock);
rcu_read_unlock();
}
{
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;
rcu_read_lock();
if (!app->compatible) {
- goto end;
+ goto end_no_session;
}
ua_sess = lookup_session_by_app(usess, app);
if (ua_sess == NULL) {
- goto end;
+ goto end_no_session;
}
+ pthread_mutex_lock(&ua_sess->lock);
+
/*
* If started = 0, it means that stop trace has been called for a session
* that was never started. It's possible since we can have a fail start
DBG3("UST app failed to flush %s. Application is dead.",
ua_chan->name);
/* No need to continue. */
- goto end;
+ break;
}
/* Continuing flushing all buffers */
continue;
health_code_update();
- assert(ua_sess->metadata->is_sent);
- /* Flush all buffers before stopping */
- ret = ustctl_sock_flush_buffer(app->sock, ua_sess->metadata->obj);
+ ret = push_metadata(app, ua_sess);
if (ret < 0) {
- if (ret != -EPIPE && ret != -LTTNG_UST_ERR_EXITING) {
- ERR("UST app PID %d metadata flush failed with ret %d", app->pid,
- ret);
- goto error_rcu_unlock;
- } else {
- DBG3("UST app failed to flush metadata. Application is dead.");
- }
+ goto error_rcu_unlock;
}
-end:
+ pthread_mutex_unlock(&ua_sess->lock);
+end_no_session:
rcu_read_unlock();
health_code_update();
return 0;
error_rcu_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();
ERR("UST app wait quiescent failed for app pid %d ret %d",
app->pid, ret);
}
-
end:
rcu_read_unlock();
health_code_update();
cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, pid_n.node) {
ret = ust_app_stop_trace(usess, app);
if (ret < 0) {
+ ERR("UST app stop trace failed with ret %d", ret);
/* Continue to next apps even on error */
continue;
}
app = find_app_by_sock(sock);
if (app == NULL) {
- ERR("Failed to find app sock %d", sock);
+ /*
+ * Application can be unregistered before so this is possible hence
+ * simply stopping the update.
+ */
+ DBG3("UST app update failed to find app sock %d", sock);
goto error;
}
}
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;
+}
+
+/*
+ * Return a ust app channel object using the application object and the channel
+ * object descriptor has a key. If not found, NULL is returned. A RCU read side
+ * lock MUST be acquired before calling this function.
+ */
+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;
+}
+
+/*
+ * Reply to a register channel notification from an application on the notify
+ * socket. The channel metadata is also created.
+ *
+ * The session UST registry lock is acquired in this function.
+ *
+ * On success 0 is returned else a negative value.
+ */
+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);
+ if (!app) {
+ DBG("Application socket %d is being teardown. Abort event notify",
+ sock);
+ ret = 0;
+ goto error_rcu_unlock;
+ }
+
+ /* 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);
+error_rcu_unlock:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Add event to the UST channel registry. When the event is added to the
+ * registry, the metadata is also created. Once done, this replies to the
+ * application with the appropriate error code.
+ *
+ * The session UST registry lock is acquired in the function.
+ *
+ * On success 0 is returned else a negative value.
+ */
+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);
+ if (!app) {
+ DBG("Application socket %d is being teardown. Abort event notify",
+ sock);
+ ret = 0;
+ goto error_rcu_unlock;
+ }
+
+ /* 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;
+ }
+
+ DBG3("UST registry event %s has been added successfully", name);
+
+error:
+ pthread_mutex_unlock(&ua_sess->registry.lock);
+error_rcu_unlock:
+ rcu_read_unlock();
+ return ret;
+}
+
+/*
+ * Handle application notification through the given notify socket.
+ *
+ * Return 0 on success or else a negative value.
+ */
+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;
+}
+
+/*
+ * Once the notify socket hangs up, this is called. First, it tries to find the
+ * corresponding application. On failure, the call_rcu to close the socket is
+ * executed. If an application is found, it tries to delete it from the notify
+ * socket hash table. Whathever the result, it proceeds to the call_rcu.
+ *
+ * Note that an object needs to be allocated here so on ENOMEM failure, the
+ * call RCU is not done but the rest of the cleanup is.
+ */
+void ust_app_notify_sock_unregister(int sock)
+{
+ int err_enomem = 0;
+ struct lttng_ht_iter iter;
+ struct ust_app *app;
+ struct ust_app_notify_sock_obj *obj;
+
+ assert(sock >= 0);
+
+ rcu_read_lock();
+
+ obj = zmalloc(sizeof(*obj));
+ if (!obj) {
+ /*
+ * An ENOMEM is kind of uncool. If this strikes we continue the
+ * procedure but the call_rcu will not be called. In this case, we
+ * accept the fd leak rather than possibly creating an unsynchronized
+ * state between threads.
+ *
+ * TODO: The notify object should be created once the notify socket is
+ * registered and stored independantely from the ust app object. The
+ * tricky part is to synchronize the teardown of the application and
+ * this notify object. Let's keep that in mind so we can avoid this
+ * kind of shenanigans with ENOMEM in the teardown path.
+ */
+ err_enomem = 1;
+ } else {
+ obj->fd = sock;
+ }
+
+ DBG("UST app notify socket unregister %d", sock);
+
+ /*
+ * Lookup application by notify socket. If this fails, this means that the
+ * hash table delete has already been done by the application
+ * unregistration process so we can safely close the notify socket in a
+ * call RCU.
+ */
+ app = find_app_by_notify_sock(sock);
+ if (!app) {
+ goto close_socket;
+ }
+
+ iter.iter.node = &app->notify_sock_n.node;
+
+ /*
+ * Whatever happens here either we fail or succeed, in both cases we have
+ * to close the socket after a grace period to continue to the call RCU
+ * here. If the deletion is successful, the application is not visible
+ * anymore by other threads and is it fails it means that it was already
+ * deleted from the hash table so either way we just have to close the
+ * socket.
+ */
+ (void) lttng_ht_del(ust_app_ht_by_notify_sock, &iter);
+
+close_socket:
+ rcu_read_unlock();
+
+ /*
+ * Close socket after a grace period to avoid for the socket to be reused
+ * before the application object is freed creating potential race between
+ * threads trying to add unique in the global hash table.
+ */
+ if (!err_enomem) {
+ call_rcu(&obj->head, close_notify_sock_rcu);
+ }
+}
#include <common/compat/uuid.h>
#include "trace-ust.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 */
+#include "ust-registry.h"
#define UST_APP_EVENT_LIST_SIZE 32
+/* Process name (short). */
+#define UST_APP_PROCNAME_LEN 16
+
struct lttng_filter_bytecode;
struct lttng_ust_filter_bytecode;
extern int ust_consumerd64_fd, ust_consumerd32_fd;
+/*
+ * Object used to close the notify socket in a call_rcu(). Since the
+ * application might not be found, we need an independant object containing the
+ * notify socket fd.
+ */
+struct ust_app_notify_sock_obj {
+ int fd;
+ struct rcu_head head;
+};
+
struct ust_app_ht_key {
const char *name;
const struct lttng_ust_filter_bytecode *filter;
* 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 {
/* Channel and streams were sent to the UST tracer. */
int is_sent;
/* Unique key used to identify the channel on the consumer side. */
- unsigned long key;
+ uint64_t key;
/* Number of stream that this channel is expected to receive. */
unsigned int expected_stream_count;
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];
+ /*
+ * Once at least *one* session is created onto the application, the
+ * corresponding consumer is set so we can use it on unregistration.
+ */
+ struct consumer_output *consumer;
};
/*
*/
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 */
+ /* Extra for the NULL byte. */
+ char name[UST_APP_PROCNAME_LEN + 1];
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);
+void ust_app_notify_sock_unregister(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)
+{
+}
+static inline
+void ust_app_notify_sock_unregister(int sock)
+{
+}
#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 */
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <inttypes.h>
#include <common/common.h>
#include <common/consumer.h>
struct consumer_socket *socket)
{
int ret;
- unsigned long key;
+ uint64_t key;
char *pathname = NULL;
struct lttcomm_consumer_msg msg;
ua_sess->gid,
consumer->net_seq_index,
ua_chan->key,
- ua_sess->uuid);
+ ua_sess->registry.uuid);
health_code_update();
/* We need at least one where 1 stream for 1 cpu. */
assert(ua_chan->expected_stream_count > 0);
- DBG2("UST ask channel %lu successfully done with %u stream(s)", key,
+ DBG2("UST ask channel %" PRIu64 " successfully done with %u stream(s)", key,
ua_chan->expected_stream_count);
error:
}
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;
error:
return ret;
}
+
+/*
+ * Send metadata string to consumer.
+ *
+ * Return 0 on success else a negative value.
+ */
+int ust_consumer_push_metadata(struct consumer_socket *socket,
+ struct ust_app_session *ua_sess, char *metadata_str,
+ size_t len, size_t target_offset)
+{
+ int ret;
+ struct lttcomm_consumer_msg msg;
+
+ assert(socket);
+ assert(socket->fd >= 0);
+ assert(ua_sess);
+ assert(ua_sess->metadata);
+
+ DBG2("UST consumer push metadata to consumer socket %d", socket->fd);
+
+ msg.cmd_type = LTTNG_CONSUMER_PUSH_METADATA;
+ msg.u.push_metadata.key = ua_sess->metadata->key;
+ msg.u.push_metadata.target_offset = target_offset;
+ msg.u.push_metadata.len = len;
+
+ /*
+ * TODO: reenable these locks when the consumerd gets the ability to
+ * reorder the metadata it receives. This fits with locking in
+ * src/bin/lttng-sessiond/ust-app.c:push_metadata()
+ *
+ * pthread_mutex_lock(socket->lock);
+ */
+
+ health_code_update();
+ ret = consumer_send_msg(socket, &msg);
+ if (ret < 0) {
+ goto error;
+ }
+
+ DBG3("UST consumer push metadata on sock %d of len %lu", socket->fd, len);
+
+ ret = lttcomm_send_unix_sock(socket->fd, metadata_str, len);
+ if (ret < 0) {
+ fprintf(stderr, "send error: %d\n", ret);
+ goto error;
+ }
+
+ health_code_update();
+ ret = consumer_recv_status_reply(socket);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ health_code_update();
+ /*
+ * pthread_mutex_unlock(socket->lock);
+ */
+ return ret;
+}
+
+/*
+ * Send a close metdata command to consumer using the given channel key.
+ *
+ * Return 0 on success else a negative value.
+ */
+int ust_consumer_close_metadata(struct consumer_socket *socket,
+ struct ust_app_channel *ua_chan)
+{
+ int ret;
+ struct lttcomm_consumer_msg msg;
+
+ assert(ua_chan);
+ assert(socket);
+ assert(socket->fd >= 0);
+
+ DBG2("UST consumer close metadata channel key %lu", ua_chan->key);
+
+ msg.cmd_type = LTTNG_CONSUMER_CLOSE_METADATA;
+ msg.u.close_metadata.key = ua_chan->key;
+
+ pthread_mutex_lock(socket->lock);
+ health_code_update();
+
+ ret = consumer_send_msg(socket, &msg);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ health_code_update();
+ pthread_mutex_unlock(socket->lock);
+ return ret;
+}
+
+/*
+ * Send a setup metdata command to consumer using the given channel key.
+ *
+ * Return 0 on success else a negative value.
+ */
+int ust_consumer_setup_metadata(struct consumer_socket *socket,
+ struct ust_app_channel *ua_chan)
+{
+ int ret;
+ struct lttcomm_consumer_msg msg;
+
+ assert(ua_chan);
+ assert(socket);
+ assert(socket->fd >= 0);
+
+ DBG2("UST consumer setup metadata channel key %lu", ua_chan->key);
+
+ msg.cmd_type = LTTNG_CONSUMER_SETUP_METADATA;
+ msg.u.setup_metadata.key = ua_chan->key;
+
+ pthread_mutex_lock(socket->lock);
+ health_code_update();
+
+ ret = consumer_send_msg(socket, &msg);
+ if (ret < 0) {
+ goto error;
+ }
+
+error:
+ health_code_update();
+ pthread_mutex_unlock(socket->lock);
+ return ret;
+}
int ust_consumer_send_channel_to_ust(struct ust_app *app,
struct ust_app_session *ua_sess, struct ust_app_channel *channel);
+int ust_consumer_push_metadata(struct consumer_socket *socket,
+ struct ust_app_session *ua_sess, char *metadata_str,
+ size_t len, size_t target_offset);
+
+int ust_consumer_close_metadata(struct consumer_socket *socket,
+ struct ust_app_channel *ua_chan);
+
+int ust_consumer_setup_metadata(struct consumer_socket *socket,
+ struct ust_app_channel *ua_chan);
+
#endif /* _UST_CONSUMER_H */
--- /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",
+ 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",
+ (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;
+ /* Length of bytes sent to the consumer. */
+ size_t metadata_len_sent;
+};
+
+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;
+ }
+
+ /* The socket is closed after a grace period here. */
+ ust_app_notify_sock_unregister(pollfd);
+ } else if (revents & (LPOLLIN | LPOLLPRI)) {
+ ret = ust_app_recv_notify(pollfd);
+ if (ret < 0) {
+ /*
+ * If the notification failed either the application is
+ * dead or an internal error happened. In both cases,
+ * we can only continue here. If the application is
+ * dead, an unregistration will follow or else the
+ * application will notice that we are not responding
+ * on that socket and will close it.
+ */
+ continue;
+ }
+ } 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)
{
.type = LTTNG_CONSUMER_UNKNOWN,
};
+enum consumer_channel_action {
+ CONSUMER_CHANNEL_ADD,
+ CONSUMER_CHANNEL_QUIT,
+};
+
+struct consumer_channel_msg {
+ enum consumer_channel_action action;
+ struct lttng_consumer_channel *chan;
+};
+
/*
* Flag to inform the polling thread to quit when all fd hung up. Updated by
* the consumer_thread_receive_fds when it notices that all fds has hung up.
} while (ret < 0 && errno == EINTR);
}
+static void notify_channel_pipe(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_channel *chan,
+ enum consumer_channel_action action)
+{
+ struct consumer_channel_msg msg;
+ int ret;
+
+ msg.action = action;
+ msg.chan = chan;
+ do {
+ ret = write(ctx->consumer_channel_pipe[1], &msg, sizeof(msg));
+ } while (ret < 0 && errno == EINTR);
+}
+
+static int read_channel_pipe(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_channel **chan,
+ enum consumer_channel_action *action)
+{
+ struct consumer_channel_msg msg;
+ int ret;
+
+ do {
+ ret = read(ctx->consumer_channel_pipe[0], &msg, sizeof(msg));
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0) {
+ *action = msg.action;
+ *chan = msg.chan;
+ }
+ return ret;
+}
+
/*
* Find a stream. The consumer_data.lock must be locked during this
* call.
*/
-static struct lttng_consumer_stream *find_stream(int key,
+static struct lttng_consumer_stream *find_stream(uint64_t key,
struct lttng_ht *ht)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_consumer_stream *stream = NULL;
assert(ht);
- /* Negative keys are lookup failures */
- if (key < 0) {
+ /* -1ULL keys are lookup failures */
+ if (key == (uint64_t) -1ULL) {
return NULL;
}
rcu_read_lock();
- lttng_ht_lookup(ht, (void *)((unsigned long) key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(ht, &key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
stream = caa_container_of(node, struct lttng_consumer_stream, node);
}
rcu_read_lock();
stream = find_stream(key, ht);
if (stream) {
- stream->key = -1;
+ stream->key = -1ULL;
/*
* We don't want the lookup to match, but we still need
* to iterate on this stream when iterating over the hash table. Just
* change the node key.
*/
- stream->node.key = -1;
+ stream->node.key = -1ULL;
}
rcu_read_unlock();
}
* RCU read side lock MUST be acquired before calling this function and
* protects the channel ptr.
*/
-struct lttng_consumer_channel *consumer_find_channel(unsigned long key)
+struct lttng_consumer_channel *consumer_find_channel(uint64_t key)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_consumer_channel *channel = NULL;
- /* Negative keys are lookup failures */
- if (key < 0) {
+ /* -1ULL keys are lookup failures */
+ if (key == (uint64_t) -1ULL) {
return NULL;
}
- lttng_ht_lookup(consumer_data.channel_ht, (void *) key, &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(consumer_data.channel_ht, &key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
channel = caa_container_of(node, struct lttng_consumer_channel, node);
}
static void free_stream_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct lttng_consumer_stream *stream =
caa_container_of(node, struct lttng_consumer_stream, node);
static void free_channel_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct lttng_consumer_channel *channel =
caa_container_of(node, struct lttng_consumer_channel, node);
*/
static void free_relayd_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct consumer_relayd_sock_pair *relayd =
caa_container_of(node, struct consumer_relayd_sock_pair, node);
int ret;
struct lttng_ht_iter iter;
- DBG("Consumer delete channel key %d", channel->key);
+ DBG("Consumer delete channel key %" PRIu64, channel->key);
pthread_mutex_lock(&consumer_data.lock);
ret = lttng_ht_del(ht, &iter);
assert(!ret);
- /* Remove node session id from the consumer_data stream ht */
+ iter.iter.node = &stream->node_channel_id.node;
+ ret = lttng_ht_del(consumer_data.stream_per_chan_id_ht, &iter);
+ assert(!ret);
+
iter.iter.node = &stream->node_session_id.node;
ret = lttng_ht_del(consumer_data.stream_list_ht, &iter);
assert(!ret);
call_rcu(&stream->node.head, free_stream_rcu);
}
-struct lttng_consumer_stream *consumer_allocate_stream(int channel_key,
- int stream_key,
+struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
+ uint64_t stream_key,
enum lttng_consumer_stream_state state,
const char *channel_name,
uid_t uid,
}
/* Key is always the wait_fd for streams. */
- lttng_ht_node_init_ulong(&stream->node, stream->key);
+ lttng_ht_node_init_u64(&stream->node, stream->key);
+
+ /* Init node per channel id key */
+ lttng_ht_node_init_u64(&stream->node_channel_id, channel_key);
/* Init session id node with the stream session id */
- lttng_ht_node_init_ulong(&stream->node_session_id, stream->session_id);
+ lttng_ht_node_init_u64(&stream->node_session_id, stream->session_id);
- DBG3("Allocated stream %s (key %d, relayd_id %d, session_id %" PRIu64,
- stream->name, stream->key, stream->net_seq_idx, stream->session_id);
+ DBG3("Allocated stream %s (key %" PRIu64 ", chan_key %" PRIu64 " relayd_id %" PRIu64 ", session_id %" PRIu64,
+ stream->name, stream->key, channel_key, stream->net_seq_idx, stream->session_id);
rcu_read_unlock();
return stream;
assert(stream);
assert(ht);
- DBG3("Adding consumer stream %d", stream->key);
+ DBG3("Adding consumer stream %" PRIu64, stream->key);
pthread_mutex_lock(&consumer_data.lock);
pthread_mutex_lock(&stream->lock);
/* Steal stream identifier to avoid having streams with the same key */
steal_stream_key(stream->key, ht);
- lttng_ht_add_unique_ulong(ht, &stream->node);
+ lttng_ht_add_unique_u64(ht, &stream->node);
+
+ lttng_ht_add_u64(consumer_data.stream_per_chan_id_ht,
+ &stream->node_channel_id);
/*
* Add stream to the stream_list_ht of the consumer data. No need to steal
* the key since the HT does not use it and we allow to add redundant keys
* into this table.
*/
- lttng_ht_add_ulong(consumer_data.stream_list_ht, &stream->node_session_id);
+ lttng_ht_add_u64(consumer_data.stream_list_ht, &stream->node_session_id);
/* Check and cleanup relayd */
relayd = consumer_find_relayd(stream->net_seq_idx);
static int add_relayd(struct consumer_relayd_sock_pair *relayd)
{
int ret = 0;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_ht_iter iter;
assert(relayd);
lttng_ht_lookup(consumer_data.relayd_ht,
- (void *)((unsigned long) relayd->net_seq_idx), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ &relayd->net_seq_idx, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
goto end;
}
- lttng_ht_add_unique_ulong(consumer_data.relayd_ht, &relayd->node);
+ lttng_ht_add_unique_u64(consumer_data.relayd_ht, &relayd->node);
end:
return ret;
obj->net_seq_idx = net_seq_idx;
obj->refcount = 0;
obj->destroy_flag = 0;
- lttng_ht_node_init_ulong(&obj->node, obj->net_seq_idx);
+ lttng_ht_node_init_u64(&obj->node, obj->net_seq_idx);
pthread_mutex_init(&obj->ctrl_sock_mutex, NULL);
error:
* RCU read-side lock must be held across this call and while using the
* returned object.
*/
-struct consumer_relayd_sock_pair *consumer_find_relayd(int key)
+struct consumer_relayd_sock_pair *consumer_find_relayd(uint64_t key)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct consumer_relayd_sock_pair *relayd = NULL;
/* Negative keys are lookup failures */
- if (key < 0) {
+ if (key == (uint64_t) -1ULL) {
goto error;
}
- lttng_ht_lookup(consumer_data.relayd_ht, (void *)((unsigned long) key),
+ lttng_ht_lookup(consumer_data.relayd_ht, &key,
&iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
relayd = caa_container_of(node, struct consumer_relayd_sock_pair, node);
}
strncpy(channel->name, name, sizeof(channel->name));
channel->name[sizeof(channel->name) - 1] = '\0';
- lttng_ht_node_init_ulong(&channel->node, channel->key);
+ lttng_ht_node_init_u64(&channel->node, channel->key);
+
+ channel->wait_fd = -1;
+
CDS_INIT_LIST_HEAD(&channel->streams.head);
- DBG("Allocated channel (key %d)", channel->key)
+ DBG("Allocated channel (key %" PRIu64 ")", channel->key)
end:
return channel;
/*
* Add a channel to the global list protected by a mutex.
*/
-int consumer_add_channel(struct lttng_consumer_channel *channel)
+int consumer_add_channel(struct lttng_consumer_channel *channel,
+ struct lttng_consumer_local_data *ctx)
{
int ret = 0;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_ht_iter iter;
pthread_mutex_lock(&consumer_data.lock);
rcu_read_lock();
lttng_ht_lookup(consumer_data.channel_ht,
- (void *)((unsigned long) channel->key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ &channel->key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
/* Channel already exist. Ignore the insertion */
- ERR("Consumer add channel key %d already exists!", channel->key);
+ ERR("Consumer add channel key %" PRIu64 " already exists!",
+ channel->key);
ret = -1;
goto end;
}
- lttng_ht_add_unique_ulong(consumer_data.channel_ht, &channel->node);
+ lttng_ht_add_unique_u64(consumer_data.channel_ht, &channel->node);
end:
rcu_read_unlock();
pthread_mutex_unlock(&consumer_data.lock);
+ if (!ret && channel->wait_fd != -1 &&
+ channel->metadata_stream == NULL) {
+ notify_channel_pipe(ctx, channel, CONSUMER_CHANNEL_ADD);
+ }
return ret;
}
cleanup_relayd_ht();
+ lttng_ht_destroy(consumer_data.stream_per_chan_id_ht);
+
/*
* This HT contains streams that are freed by either the metadata thread or
* the data thread so we do *nothing* on the hash table and simply destroy
int (*recv_stream)(struct lttng_consumer_stream *stream),
int (*update_stream)(int stream_key, uint32_t state))
{
- int ret, i;
+ int ret;
struct lttng_consumer_local_data *ctx;
assert(consumer_data.type == LTTNG_CONSUMER_UNKNOWN ||
goto error_thread_pipe;
}
+ ret = pipe(ctx->consumer_channel_pipe);
+ if (ret < 0) {
+ PERROR("Error creating channel pipe");
+ goto error_channel_pipe;
+ }
+
ret = utils_create_pipe(ctx->consumer_metadata_pipe);
if (ret < 0) {
goto error_metadata_pipe;
error_splice_pipe:
utils_close_pipe(ctx->consumer_metadata_pipe);
error_metadata_pipe:
+ utils_close_pipe(ctx->consumer_channel_pipe);
+error_channel_pipe:
utils_close_pipe(ctx->consumer_thread_pipe);
error_thread_pipe:
- for (i = 0; i < 2; i++) {
- int err;
-
- err = close(ctx->consumer_should_quit[i]);
- if (err) {
- PERROR("close");
- }
- }
+ utils_close_pipe(ctx->consumer_should_quit);
error_poll_fcntl:
error_quit_pipe:
- for (i = 0; i < 2; i++) {
- int err;
-
- err = close(ctx->consumer_data_pipe[i]);
- if (err) {
- PERROR("close");
- }
- }
+ utils_close_pipe(ctx->consumer_data_pipe);
error_poll_pipe:
free(ctx);
error:
if (ret) {
PERROR("close");
}
- ret = close(ctx->consumer_thread_pipe[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_thread_pipe[1]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_data_pipe[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_data_pipe[1]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_should_quit[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_should_quit[1]);
- if (ret) {
- PERROR("close");
- }
+ utils_close_pipe(ctx->consumer_thread_pipe);
+ utils_close_pipe(ctx->consumer_channel_pipe);
+ utils_close_pipe(ctx->consumer_data_pipe);
+ utils_close_pipe(ctx->consumer_should_quit);
utils_close_pipe(ctx->consumer_splice_metadata_pipe);
unlink(ctx->consumer_command_sock_path);
assert(0);
return -ENOSYS;
}
-
}
/*
lttng_ht_destroy(ht);
}
+void lttng_consumer_close_metadata(void)
+{
+ switch (consumer_data.type) {
+ case LTTNG_CONSUMER_KERNEL:
+ /*
+ * The Kernel consumer has a different metadata scheme so we don't
+ * close anything because the stream will be closed by the session
+ * daemon.
+ */
+ break;
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ /*
+ * Close all metadata streams. The metadata hash table is passed and
+ * this call iterates over it by closing all wakeup fd. This is safe
+ * because at this point we are sure that the metadata producer is
+ * either dead or blocked.
+ */
+ lttng_ustconsumer_close_metadata(metadata_ht);
+ break;
+ default:
+ ERR("Unknown consumer_data type");
+ assert(0);
+ }
+}
+
/*
* Clean up a metadata stream and free its memory.
*/
ret = lttng_ht_del(ht, &iter);
assert(!ret);
- /* Remove node session id from the consumer_data stream ht */
+ iter.iter.node = &stream->node_channel_id.node;
+ ret = lttng_ht_del(consumer_data.stream_per_chan_id_ht, &iter);
+ assert(!ret);
+
iter.iter.node = &stream->node_session_id.node;
ret = lttng_ht_del(consumer_data.stream_list_ht, &iter);
assert(!ret);
int ret = 0;
struct consumer_relayd_sock_pair *relayd;
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
assert(stream);
assert(ht);
- DBG3("Adding metadata stream %d to hash table", stream->key);
+ DBG3("Adding metadata stream %" PRIu64 " to hash table", stream->key);
pthread_mutex_lock(&consumer_data.lock);
pthread_mutex_lock(&stream->lock);
* Lookup the stream just to make sure it does not exist in our internal
* state. This should NEVER happen.
*/
- lttng_ht_lookup(ht, (void *)((unsigned long) stream->key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(ht, &stream->key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
assert(!node);
/* Find relayd and, if one is found, increment refcount. */
uatomic_dec(&stream->chan->nb_init_stream_left);
}
- lttng_ht_add_unique_ulong(ht, &stream->node);
+ lttng_ht_add_unique_u64(ht, &stream->node);
+
+ lttng_ht_add_unique_u64(consumer_data.stream_per_chan_id_ht,
+ &stream->node_channel_id);
/*
* Add stream to the stream_list_ht of the consumer data. No need to steal
* the key since the HT does not use it and we allow to add redundant keys
* into this table.
*/
- lttng_ht_add_ulong(consumer_data.stream_list_ht, &stream->node_session_id);
+ lttng_ht_add_u64(consumer_data.stream_list_ht, &stream->node_session_id);
rcu_read_unlock();
uint32_t revents, nb_fd;
struct lttng_consumer_stream *stream = NULL;
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_poll_event events;
struct lttng_consumer_local_data *ctx = data;
ssize_t len;
rcu_register_thread();
- metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (!metadata_ht) {
/* ENOMEM at this point. Better to bail out. */
- goto error;
+ goto end_ht;
}
DBG("Thread metadata poll started");
ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
if (ret < 0) {
ERR("Poll set creation failed");
- goto end;
+ goto end_poll;
}
ret = lttng_poll_add(&events, ctx->consumer_metadata_pipe[0], LPOLLIN);
}
rcu_read_lock();
- lttng_ht_lookup(metadata_ht, (void *)((unsigned long) pollfd),
- &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ {
+ uint64_t tmp_id = (uint64_t) pollfd;
+
+ lttng_ht_lookup(metadata_ht, &tmp_id, &iter);
+ }
+ node = lttng_ht_iter_get_node_u64(&iter);
assert(node);
stream = caa_container_of(node, struct lttng_consumer_stream,
error:
end:
DBG("Metadata poll thread exiting");
- lttng_poll_clean(&events);
+ lttng_poll_clean(&events);
+end_poll:
destroy_stream_ht(metadata_ht);
-
+end_ht:
rcu_unregister_thread();
return NULL;
}
rcu_register_thread();
- data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (data_ht == NULL) {
/* ENOMEM at this point. Better to bail out. */
goto end;
ret = add_stream(new_stream, data_ht);
if (ret) {
- ERR("Consumer add stream %d failed. Continuing",
+ ERR("Consumer add stream %" PRIu64 " failed. Continuing",
new_stream->key);
/*
* At this point, if the add_stream fails, it is not in the
return NULL;
}
+/*
+ * Close wake-up end of each stream belonging to the channel. This will
+ * allow the poll() on the stream read-side to detect when the
+ * write-side (application) finally closes them.
+ */
+static
+void consumer_close_channel_streams(struct lttng_consumer_channel *channel)
+{
+ struct lttng_ht *ht;
+ struct lttng_consumer_stream *stream;
+ struct lttng_ht_iter iter;
+
+ ht = consumer_data.stream_per_chan_id_ht;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct(&channel->key, lttng_ht_seed),
+ ht->match_fct, &channel->key,
+ &iter.iter, stream, node_channel_id.node) {
+ switch (consumer_data.type) {
+ case LTTNG_CONSUMER_KERNEL:
+ break;
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ /*
+ * Note: a mutex is taken internally within
+ * liblttng-ust-ctl to protect timer wakeup_fd
+ * use from concurrent close.
+ */
+ lttng_ustconsumer_close_stream_wakeup(stream);
+ break;
+ default:
+ ERR("Unknown consumer_data type");
+ assert(0);
+ }
+ }
+ rcu_read_unlock();
+}
+
+static void destroy_channel_ht(struct lttng_ht *ht)
+{
+ struct lttng_ht_iter iter;
+ struct lttng_consumer_channel *channel;
+ int ret;
+
+ if (ht == NULL) {
+ return;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ht->ht, &iter.iter, channel, wait_fd_node.node) {
+ ret = lttng_ht_del(ht, &iter);
+ assert(ret != 0);
+ }
+ rcu_read_unlock();
+
+ lttng_ht_destroy(ht);
+}
+
+/*
+ * This thread polls the channel fds to detect when they are being
+ * closed. It closes all related streams if the channel is detected as
+ * closed. It is currently only used as a shim layer for UST because the
+ * consumerd needs to keep the per-stream wakeup end of pipes open for
+ * periodical flush.
+ */
+void *consumer_thread_channel_poll(void *data)
+{
+ int ret, i, pollfd;
+ uint32_t revents, nb_fd;
+ struct lttng_consumer_channel *chan = NULL;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_poll_event events;
+ struct lttng_consumer_local_data *ctx = data;
+ struct lttng_ht *channel_ht;
+
+ rcu_register_thread();
+
+ channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ if (!channel_ht) {
+ /* ENOMEM at this point. Better to bail out. */
+ goto end_ht;
+ }
+
+ DBG("Thread channel poll started");
+
+ /* Size is set to 1 for the consumer_channel pipe */
+ ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
+ if (ret < 0) {
+ ERR("Poll set creation failed");
+ goto end_poll;
+ }
+
+ ret = lttng_poll_add(&events, ctx->consumer_channel_pipe[0], LPOLLIN);
+ if (ret < 0) {
+ goto end;
+ }
+
+ /* Main loop */
+ DBG("Channel main loop started");
+
+ while (1) {
+ /* Only the channel pipe is set */
+ if (LTTNG_POLL_GETNB(&events) == 0 && consumer_quit == 1) {
+ goto end;
+ }
+
+restart:
+ DBG("Channel poll wait with %d fd(s)", LTTNG_POLL_GETNB(&events));
+ ret = lttng_poll_wait(&events, -1);
+ DBG("Channel event catched in thread");
+ if (ret < 0) {
+ if (errno == EINTR) {
+ ERR("Poll EINTR catched");
+ goto restart;
+ }
+ goto end;
+ }
+
+ nb_fd = ret;
+
+ /* From here, the event is a channel wait fd */
+ for (i = 0; i < nb_fd; i++) {
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Just don't waste time if no returned events for the fd */
+ if (!revents) {
+ continue;
+ }
+ if (pollfd == ctx->consumer_channel_pipe[0]) {
+ if (revents & (LPOLLERR | LPOLLHUP)) {
+ DBG("Channel thread pipe hung up");
+ /*
+ * Remove the pipe from the poll set and continue the loop
+ * since their might be data to consume.
+ */
+ lttng_poll_del(&events, ctx->consumer_channel_pipe[0]);
+ continue;
+ } else if (revents & LPOLLIN) {
+ enum consumer_channel_action action;
+
+ ret = read_channel_pipe(ctx, &chan, &action);
+ if (ret <= 0) {
+ ERR("Error reading channel pipe");
+ continue;
+ }
+
+ switch (action) {
+ case CONSUMER_CHANNEL_ADD:
+ DBG("Adding channel %d to poll set",
+ chan->wait_fd);
+
+ lttng_ht_node_init_u64(&chan->wait_fd_node,
+ chan->wait_fd);
+ lttng_ht_add_unique_u64(channel_ht,
+ &chan->wait_fd_node);
+ /* Add channel to the global poll events list */
+ lttng_poll_add(&events, chan->wait_fd,
+ LPOLLIN | LPOLLPRI);
+ break;
+ case CONSUMER_CHANNEL_QUIT:
+ /*
+ * Remove the pipe from the poll set and continue the loop
+ * since their might be data to consume.
+ */
+ lttng_poll_del(&events, ctx->consumer_channel_pipe[0]);
+ continue;
+ default:
+ ERR("Unknown action");
+ break;
+ }
+ }
+
+ /* Handle other stream */
+ continue;
+ }
+
+ rcu_read_lock();
+ {
+ uint64_t tmp_id = (uint64_t) pollfd;
+
+ lttng_ht_lookup(channel_ht, &tmp_id, &iter);
+ }
+ node = lttng_ht_iter_get_node_u64(&iter);
+ assert(node);
+
+ chan = caa_container_of(node, struct lttng_consumer_channel,
+ wait_fd_node);
+
+ /* Check for error event */
+ if (revents & (LPOLLERR | LPOLLHUP)) {
+ DBG("Channel fd %d is hup|err.", pollfd);
+
+ lttng_poll_del(&events, chan->wait_fd);
+ ret = lttng_ht_del(channel_ht, &iter);
+ assert(ret == 0);
+ consumer_close_channel_streams(chan);
+ }
+
+ /* Release RCU lock for the channel looked up */
+ rcu_read_unlock();
+ }
+ }
+
+end:
+ lttng_poll_clean(&events);
+end_poll:
+ destroy_channel_ht(channel_ht);
+end_ht:
+ DBG("Channel poll thread exiting");
+ rcu_unregister_thread();
+ return NULL;
+}
+
/*
* This thread listens on the consumerd socket and receives the file
* descriptors from the session daemon.
end:
DBG("Consumer thread sessiond poll exiting");
+ /*
+ * Close metadata streams since the producer is the session daemon which
+ * just died.
+ *
+ * NOTE: for now, this only applies to the UST tracer.
+ */
+ lttng_consumer_close_metadata();
+
/*
* when all fds have hung up, the polling thread
* can exit cleanly
*/
notify_thread_pipe(ctx->consumer_data_pipe[1]);
+ notify_channel_pipe(ctx, NULL, CONSUMER_CHANNEL_QUIT);
+
/* Cleaning up possibly open sockets. */
if (sock >= 0) {
ret = close(sock);
*/
void lttng_consumer_init(void)
{
- consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
- consumer_data.relayd_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
- consumer_data.stream_list_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.relayd_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.stream_list_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.stream_per_chan_id_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
}
/*
goto error;
}
- DBG("Consumer %s socket created successfully with net idx %d (fd: %d)",
+ DBG("Consumer %s socket created successfully with net idx %" PRIu64 " (fd: %d)",
sock_type == LTTNG_STREAM_CONTROL ? "control" : "data",
relayd->net_seq_idx, fd);
}
cds_lfht_for_each_entry_duplicate(ht->ht,
- ht->hash_fct((void *)((unsigned long) id), lttng_ht_seed),
- ht->match_fct, (void *)((unsigned long) id),
+ ht->hash_fct(&id, lttng_ht_seed),
+ ht->match_fct, &id,
&iter.iter, stream, node_session_id.node) {
/* If this call fails, the stream is being used hence data pending. */
ret = stream_try_lock(stream);
LTTNG_CONSUMER_ASK_CHANNEL_CREATION,
LTTNG_CONSUMER_GET_CHANNEL,
LTTNG_CONSUMER_DESTROY_CHANNEL,
+ LTTNG_CONSUMER_PUSH_METADATA,
+ LTTNG_CONSUMER_CLOSE_METADATA,
+ LTTNG_CONSUMER_SETUP_METADATA,
};
/* State of each fd in consumer */
enum consumer_channel_type {
CONSUMER_CHANNEL_TYPE_METADATA = 0,
- CONSUMER_CHANNEL_TYPE_DATA = 1,
+ CONSUMER_CHANNEL_TYPE_DATA = 1,
};
struct stream_list {
struct lttng_consumer_channel {
/* HT node used for consumer_data.channel_ht */
- struct lttng_ht_node_ulong node;
+ struct lttng_ht_node_u64 node;
/* Indexed key. Incremented value in the consumer. */
- int key;
+ uint64_t key;
/* Number of streams referencing this channel */
int refcount;
/* Tracing session id on the session daemon side. */
uid_t uid;
gid_t gid;
/* Relayd id of the channel. -1 if it does not apply. */
- int relayd_id;
+ int64_t relayd_id;
/*
* Number of streams NOT initialized yet. This is used in order to not
* delete this channel if streams are getting initialized.
* LTTNG_CONSUMER_GET_CHANNEL.
*/
struct stream_list streams;
+ /*
+ * Set if the channel is metadata. We keep a reference to the stream
+ * because we have to flush data once pushed by the session daemon. For a
+ * regular channel, this is always set to NULL.
+ */
+ struct lttng_consumer_stream *metadata_stream;
+ /*
+ * Metadata written so far. Helps keeping track of
+ * contiguousness and order.
+ */
+ uint64_t contig_metadata_written;
+
+ /* for UST */
+ int wait_fd;
+ /* Node within channel thread ht */
+ struct lttng_ht_node_u64 wait_fd_node;
};
/*
*/
struct lttng_consumer_stream {
/* HT node used by the data_ht and metadata_ht */
- struct lttng_ht_node_ulong node;
+ struct lttng_ht_node_u64 node;
+ /* stream indexed per channel key node */
+ struct lttng_ht_node_u64 node_channel_id;
/* HT node used in consumer_data.stream_list_ht */
- struct lttng_ht_node_ulong node_session_id;
+ struct lttng_ht_node_u64 node_session_id;
/* Pointer to associated channel. */
struct lttng_consumer_channel *chan;
/* Key by which the stream is indexed for 'node'. */
- int key;
+ uint64_t key;
/*
* File descriptor of the data output file. This can be either a file or a
* socket fd for relayd streaming.
uid_t uid;
gid_t gid;
/* Network sequence number. Indicating on which relayd socket it goes. */
- int net_seq_idx;
+ uint64_t net_seq_idx;
/* Identify if the stream is the metadata */
unsigned int metadata_flag;
/* Used when the stream is set for network streaming */
*/
struct consumer_relayd_sock_pair {
/* Network sequence number. */
- int net_seq_idx;
+ int64_t net_seq_idx;
/* Number of stream associated with this relayd */
unsigned int refcount;
* this socket is for now only used in a single thread.
*/
struct lttcomm_sock data_sock;
- struct lttng_ht_node_ulong node;
+ struct lttng_ht_node_u64 node;
/* Session id on both sides for the sockets. */
uint64_t relayd_session_id;
char *consumer_command_sock_path;
/* communication with splice */
int consumer_thread_pipe[2];
+ int consumer_channel_pipe[2];
int consumer_splice_metadata_pipe[2];
/* Data stream poll thread pipe. To transfer data stream to the thread */
int consumer_data_pipe[2];
* This HT uses the "node_session_id" of the consumer stream.
*/
struct lttng_ht *stream_list_ht;
+
+ /*
+ * This HT uses the "node_channel_id" of the consumer stream.
+ */
+ struct lttng_ht *stream_per_chan_id_ht;
};
/*
*/
int lttng_consumer_poll_socket(struct pollfd *kconsumer_sockpoll);
-struct lttng_consumer_stream *consumer_allocate_stream(int channel_key,
- int stream_key,
+struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
+ uint64_t stream_key,
enum lttng_consumer_stream_state state,
const char *channel_name,
uid_t uid,
int cpu,
int *alloc_ret,
enum consumer_channel_type type);
-struct lttng_consumer_channel *consumer_allocate_channel(unsigned long key,
+struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key,
uint64_t session_id,
const char *pathname,
const char *name,
struct lttng_ht *ht);
void consumer_del_metadata_stream(struct lttng_consumer_stream *stream,
struct lttng_ht *ht);
-int consumer_add_channel(struct lttng_consumer_channel *channel);
+int consumer_add_channel(struct lttng_consumer_channel *channel,
+ struct lttng_consumer_local_data *ctx);
void consumer_del_channel(struct lttng_consumer_channel *channel);
/* lttng-relayd consumer command */
struct consumer_relayd_sock_pair *consumer_allocate_relayd_sock_pair(
int net_seq_idx);
-struct consumer_relayd_sock_pair *consumer_find_relayd(int key);
-struct lttng_consumer_channel *consumer_find_channel(unsigned long key);
+struct consumer_relayd_sock_pair *consumer_find_relayd(uint64_t key);
+struct lttng_consumer_channel *consumer_find_channel(uint64_t key);
int consumer_handle_stream_before_relayd(struct lttng_consumer_stream *stream,
size_t data_size);
void consumer_steal_stream_key(int key, struct lttng_ht *ht);
void *consumer_thread_metadata_poll(void *data);
void *consumer_thread_data_poll(void *data);
void *consumer_thread_sessiond_poll(void *data);
+void *consumer_thread_channel_poll(void *data);
int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx,
int sock, struct pollfd *consumer_sockpoll);
#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.
/* DEFAULT_CHANNEL_SUBBUF_NUM must always be a power of 2 */
#define DEFAULT_CHANNEL_SUBBUF_NUM 4
#define DEFAULT_CHANNEL_SWITCH_TIMER 0 /* usec */
-#define DEFAULT_CHANNEL_READ_TIMER 200 /* usec */
+#define DEFAULT_CHANNEL_READ_TIMER 200000 /* usec */
#define DEFAULT_CHANNEL_OUTPUT LTTNG_EVENT_MMAP
#define DEFAULT_METADATA_SUBBUF_SIZE 4096
/* See lttng-kernel.h enum lttng_kernel_output for channel output */
#define DEFAULT_KERNEL_CHANNEL_OUTPUT LTTNG_EVENT_SPLICE
+#define DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER \
+ DEFAULT_CHANNEL_SWITCH_TIMER
+#define DEFAULT_KERNEL_CHANNEL_READ_TIMER 200000 /* usec */
+
/* User space defaults */
/* Must be a power of 2 */
/* See lttng-ust.h enum lttng_ust_output */
#define DEFAULT_UST_CHANNEL_OUTPUT LTTNG_EVENT_MMAP
+#define DEFAULT_UST_CHANNEL_SWITCH_TIMER \
+ DEFAULT_CHANNEL_SWITCH_TIMER
+#define DEFAULT_UST_CHANNEL_READ_TIMER 0 /* usec */
+
/*
* Default timeout value for the sem_timedwait() call. Blocking forever is not
* wanted so a timeout is used to control the data flow and not freeze the
return hash_match_key_ulong((void *) match_node->key, (void *) key);
}
+/*
+ * Match function for u64 node.
+ */
+static int match_u64(struct cds_lfht_node *node, const void *key)
+{
+ struct lttng_ht_node_u64 *match_node =
+ caa_container_of(node, struct lttng_ht_node_u64, node);
+
+ return hash_match_key_u64(&match_node->key, (void *) key);
+}
+
/*
* Return an allocated lttng hashtable.
*/
ht->match_fct = match_ulong;
ht->hash_fct = hash_key_ulong;
break;
+ case LTTNG_HT_TYPE_U64:
+ ht->match_fct = match_u64;
+ ht->hash_fct = hash_key_u64;
+ break;
default:
ERR("Unknown lttng hashtable type %d", type);
goto error;
cds_lfht_node_init(&node->node);
}
+/*
+ * Init lttng ht node uint64_t.
+ */
+void lttng_ht_node_init_u64(struct lttng_ht_node_u64 *node,
+ uint64_t key)
+{
+ assert(node);
+
+ node->key = key;
+ cds_lfht_node_init(&node->node);
+}
+
/*
* Free lttng ht node string.
*/
free(node);
}
+/*
+ * Free lttng ht node uint64_t.
+ */
+void lttng_ht_node_free_u64(struct lttng_ht_node_ulong *node)
+{
+ assert(node);
+ free(node);
+}
+
/*
* Lookup function in hashtable.
*/
&node->node);
}
+/*
+ * Add uint64_t node to hashtable.
+
+ */
+void lttng_ht_add_u64(struct lttng_ht *ht, struct lttng_ht_node_u64 *node)
+{
+ assert(ht);
+ assert(ht->ht);
+ assert(node);
+
+ cds_lfht_add(ht->ht, ht->hash_fct(&node->key, lttng_ht_seed),
+ &node->node);
+}
+
/*
* Add unique unsigned long node to hashtable.
*/
assert(node_ptr == &node->node);
}
+/*
+ * Add unique uint64_t node to hashtable.
+ */
+void lttng_ht_add_unique_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_u64 *node)
+{
+ struct cds_lfht_node *node_ptr;
+ assert(ht);
+ assert(ht->ht);
+ assert(node);
+
+ node_ptr = cds_lfht_add_unique(ht->ht,
+ ht->hash_fct(&node->key, lttng_ht_seed), ht->match_fct,
+ &node->key, &node->node);
+ assert(node_ptr == &node->node);
+}
+
/*
* Add replace unsigned long node to hashtable.
*/
assert(node_ptr == &node->node);
}
+/*
+ * Add replace unsigned long node to hashtable.
+ */
+struct lttng_ht_node_u64 *lttng_ht_add_replace_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_u64 *node)
+{
+ struct cds_lfht_node *node_ptr;
+ assert(ht);
+ assert(ht->ht);
+ assert(node);
+
+ node_ptr = cds_lfht_add_replace(ht->ht,
+ ht->hash_fct(&node->key, lttng_ht_seed), ht->match_fct,
+ &node->key, &node->node);
+ if (!node_ptr) {
+ return NULL;
+ } else {
+ return caa_container_of(node_ptr, struct lttng_ht_node_u64, node);
+ }
+ assert(node_ptr == &node->node);
+}
+
/*
* Delete node from hashtable.
*/
return caa_container_of(node, struct lttng_ht_node_ulong, node);
}
+/*
+ * Return lttng ht unsigned long node from iterator.
+ */
+struct lttng_ht_node_u64 *lttng_ht_iter_get_node_u64(
+ struct lttng_ht_iter *iter)
+{
+ struct cds_lfht_node *node;
+
+ assert(iter);
+ node = cds_lfht_iter_get_node(&iter->iter);
+ if (!node) {
+ return NULL;
+ }
+ return caa_container_of(node, struct lttng_ht_node_u64, node);
+}
+
/*
* lib constructor
*/
-static void __attribute__((constructor)) _init()
+static void __attribute__((constructor)) _init(void)
{
/* Init hash table seed */
lttng_ht_seed = (unsigned long) time(NULL);
#define _LTT_HT_H
#include <urcu.h>
+#include <stdint.h>
#include "rculfhash.h"
#include "rculfhash-internal.h"
enum lttng_ht_type {
LTTNG_HT_TYPE_STRING,
LTTNG_HT_TYPE_ULONG,
+ LTTNG_HT_TYPE_U64,
};
struct lttng_ht {
struct rcu_head head;
};
+struct lttng_ht_node_u64 {
+ uint64_t key;
+ struct cds_lfht_node node;
+ struct rcu_head head;
+};
+
/* Hashtable new and destroy */
extern struct lttng_ht *lttng_ht_new(unsigned long size, int type);
extern void lttng_ht_destroy(struct lttng_ht *ht);
extern void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key);
extern void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node,
unsigned long key);
+extern void lttng_ht_node_init_u64(struct lttng_ht_node_u64 *node,
+ uint64_t key);
extern void lttng_ht_node_free_str(struct lttng_ht_node_str *node);
extern void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node);
+extern void lttng_ht_node_free_u64(struct lttng_ht_node_ulong *node);
extern void lttng_ht_lookup(struct lttng_ht *ht, void *key,
struct lttng_ht_iter *iter);
struct lttng_ht_node_str *node);
extern void lttng_ht_add_unique_ulong(struct lttng_ht *ht,
struct lttng_ht_node_ulong *node);
+extern void lttng_ht_add_unique_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_u64 *node);
extern struct lttng_ht_node_ulong *lttng_ht_add_replace_ulong(
struct lttng_ht *ht, struct lttng_ht_node_ulong *node);
+extern struct lttng_ht_node_u64 *lttng_ht_add_replace_u64(
+ struct lttng_ht *ht, struct lttng_ht_node_u64 *node);
extern void lttng_ht_add_ulong(struct lttng_ht *ht,
struct lttng_ht_node_ulong *node);
+extern void lttng_ht_add_u64(struct lttng_ht *ht,
+ struct lttng_ht_node_u64 *node);
extern int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter);
struct lttng_ht_iter *iter);
extern struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong(
struct lttng_ht_iter *iter);
+extern struct lttng_ht_node_u64 *lttng_ht_iter_get_node_u64(
+ struct lttng_ht_iter *iter);
#endif /* _LTT_HT_H */
return c;
}
-#if (CAA_BITS_PER_LONG == 64)
-/*
- * Hash function for number value.
- */
LTTNG_HIDDEN
-unsigned long hash_key_ulong(void *_key, unsigned long seed)
+unsigned long hash_key_u64(void *_key, unsigned long seed)
{
union {
uint64_t v64;
} key;
v.v64 = (uint64_t) seed;
- key.v64 = (uint64_t) _key;
+ key.v64 = *(uint64_t *) _key;
hashword2(key.v32, 2, &v.v32[0], &v.v32[1]);
return v.v64;
}
+
+#if (CAA_BITS_PER_LONG == 64)
+/*
+ * Hash function for number value.
+ */
+LTTNG_HIDDEN
+unsigned long hash_key_ulong(void *_key, unsigned long seed)
+{
+ uint64_t __key = (uint64_t) _key;
+ return (unsigned long) hash_key_u64(&__key, seed);
+}
#else
/*
* Hash function for number value.
return 0;
}
+/*
+ * Hash function compare for number value.
+ */
+LTTNG_HIDDEN
+int hash_match_key_u64(void *key1, void *key2)
+{
+ if (*(uint64_t *) key1 == *(uint64_t *) key2) {
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Hash compare function for string.
*/
#include <stdint.h>
unsigned long hash_key_ulong(void *_key, unsigned long seed);
+unsigned long hash_key_u64(void *_key, unsigned long seed);
unsigned long hash_key_str(void *key, unsigned long seed);
int hash_match_key_ulong(void *key1, void *key2);
+int hash_match_key_u64(void *key1, void *key2);
int hash_match_key_str(void *key1, void *key2);
#endif /* _LTT_HT_UTILS_H */
goto end_nosignal;
}
- DBG("consumer_add_channel %d", msg.u.channel.channel_key);
+ DBG("consumer_add_channel %" PRIu64, msg.u.channel.channel_key);
new_channel = consumer_allocate_channel(msg.u.channel.channel_key,
msg.u.channel.session_id, msg.u.channel.pathname,
msg.u.channel.name, msg.u.channel.uid, msg.u.channel.gid,
if (ctx->on_recv_channel != NULL) {
ret = ctx->on_recv_channel(new_channel);
if (ret == 0) {
- consumer_add_channel(new_channel);
+ consumer_add_channel(new_channel, ctx);
} else if (ret < 0) {
goto end_nosignal;
}
} else {
- consumer_add_channel(new_channel);
+ consumer_add_channel(new_channel, ctx);
}
goto end_nosignal;
}
* We could not find the channel. Can happen if cpu hotplug
* happens while tearing down.
*/
- ERR("Unable to find channel key %d", msg.u.stream.channel_key);
+ ERR("Unable to find channel key %" PRIu64, msg.u.stream.channel_key);
ret_code = LTTNG_ERR_KERN_CHAN_NOT_FOUND;
}
consumer_del_stream(new_stream, NULL);
goto end_nosignal;
}
- } else if (new_stream->net_seq_idx != -1) {
- ERR("Network sequence index %d unknown. Not adding stream.",
+ } else if (new_stream->net_seq_idx != (uint64_t) -1ULL) {
+ ERR("Network sequence index %" PRIu64 " unknown. Not adding stream.",
new_stream->net_seq_idx);
consumer_del_stream(new_stream, NULL);
goto end_nosignal;
* network streaming or the full padding (len) size when we are _not_
* streaming.
*/
- if ((ret != subbuf_size && stream->net_seq_idx != -1) ||
- (ret != len && stream->net_seq_idx == -1)) {
+ if ((ret != subbuf_size && stream->net_seq_idx != (uint64_t) -1ULL) ||
+ (ret != len && stream->net_seq_idx == (uint64_t) -1ULL)) {
/*
* Display the error but continue processing to try to release the
* subbuffer
}
/* Opening the tracefile in write mode */
- if (stream->net_seq_idx == -1) {
+ if (stream->net_seq_idx == (uint64_t) -1ULL) {
ret = run_as_open(full_path, O_WRONLY | O_CREAT | O_TRUNC,
S_IRWXU|S_IRWXG|S_IRWXO, stream->uid, stream->gid);
if (ret < 0) {
LTTCOMM_CONSUMERD_SPLICE_ENOMEM, /* ENOMEM from splice(2) */
LTTCOMM_CONSUMERD_SPLICE_ESPIPE, /* ESPIPE from splice(2) */
LTTCOMM_CONSUMERD_ENOMEM, /* Consumer is out of memory */
+ LTTCOMM_CONSUMERD_ERROR_METADATA, /* Error with metadata. */
+ LTTCOMM_CONSUMERD_FATAL, /* Fatal error. */
/* MUST be last element */
LTTCOMM_NR, /* Last element */
} LTTNG_PACKED;
struct lttcomm_sock {
- int fd;
+ int32_t fd;
enum lttcomm_sock_proto proto;
struct lttcomm_sockaddr sockaddr;
const struct lttcomm_proto_ops *ops;
* Data structure received from lttng client to session daemon.
*/
struct lttcomm_session_msg {
- uint32_t cmd_type; /* enum lttcomm_sessiond_command */
+ uint32_t cmd_type; /* enum lttcomm_sessiond_command */
struct lttng_session session;
struct lttng_domain domain;
union {
* 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[];
uint32_t cmd_type; /* enum consumerd_command */
union {
struct {
- int channel_key;
+ uint64_t channel_key;
uint64_t session_id;
char pathname[PATH_MAX];
- uid_t uid;
- gid_t gid;
- int relayd_id;
+ uint32_t uid;
+ uint32_t gid;
+ uint64_t relayd_id;
/* nb_init_streams is the number of streams open initially. */
- unsigned int nb_init_streams;
+ uint32_t nb_init_streams;
char name[LTTNG_SYMBOL_NAME_LEN];
/* Use splice or mmap to consume this fd */
enum lttng_event_output output;
int type; /* Per cpu or metadata. */
} LTTNG_PACKED channel; /* Only used by Kernel. */
struct {
- int stream_key;
- int channel_key;
- int cpu; /* On which CPU this stream is assigned. */
+ uint64_t stream_key;
+ uint64_t channel_key;
+ int32_t cpu; /* On which CPU this stream is assigned. */
} LTTNG_PACKED stream; /* Only used by Kernel. */
struct {
- int net_index;
+ uint64_t net_index;
enum lttng_stream_type type;
/* Open socket to the relayd */
struct lttcomm_sock sock;
uint64_t session_id;
} LTTNG_PACKED data_pending;
struct {
- 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. */
+ uint64_t subbuf_size; /* bytes */
+ uint64_t num_subbuf; /* power of 2 */
+ int32_t overwrite; /* 1: overwrite, 0: discard */
+ uint32_t switch_timer_interval; /* usec */
+ uint32_t read_timer_interval; /* usec */
+ int32_t output; /* splice, mmap */
+ int32_t 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. */
- unsigned long key; /* Unique channel key. */
+ uint32_t uid; /* User ID of the session */
+ uint32_t gid; /* Group ID ot the session */
+ uint64_t relayd_id; /* Relayd id if apply. */
+ uint64_t key; /* Unique channel key. */
unsigned char uuid[UUID_STR_LEN]; /* uuid for ust tracer. */
} LTTNG_PACKED ask_channel;
struct {
- unsigned long key;
+ uint64_t key;
} LTTNG_PACKED get_channel;
struct {
- unsigned long key;
+ uint64_t key;
} LTTNG_PACKED destroy_channel;
+ struct {
+ uint64_t key; /* Metadata channel key. */
+ uint64_t target_offset; /* Offset in the consumer */
+ uint64_t len; /* Length of metadata to be received. */
+ } LTTNG_PACKED push_metadata;
+ struct {
+ uint64_t key; /* Metadata channel key. */
+ } LTTNG_PACKED close_metadata;
+ struct {
+ uint64_t key; /* Metadata channel key. */
+ } LTTNG_PACKED setup_metadata;
} u;
} LTTNG_PACKED;
struct lttcomm_consumer_status_channel {
enum lttng_error_code ret_code;
- unsigned long key;
+ uint64_t key;
unsigned int stream_count;
} LTTNG_PACKED;
if (ctx->on_recv_channel != NULL) {
ret = ctx->on_recv_channel(channel);
if (ret == 0) {
- ret = consumer_add_channel(channel);
+ ret = consumer_add_channel(channel, ctx);
} else if (ret < 0) {
/* Most likely an ENOMEM. */
lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_OUTFD_ERROR);
goto error;
}
} else {
- ret = consumer_add_channel(channel);
+ ret = consumer_add_channel(channel, ctx);
}
- DBG("UST consumer channel added (key: %u)", channel->key);
+ DBG("UST consumer channel added (key: %" PRIu64 ")", channel->key);
error:
return ret;
*/
static struct lttng_consumer_channel *allocate_channel(uint64_t session_id,
const char *pathname, const char *name, uid_t uid, gid_t gid,
- int relayd_id, unsigned long key, enum lttng_event_output output)
+ int relayd_id, uint64_t key, enum lttng_event_output output)
{
assert(pathname);
assert(name);
if (ret < 0) {
goto error;
}
- } else if (stream->net_seq_idx != -1) {
- ERR("Network sequence index %d unknown. Not adding stream.",
+ } else if (stream->net_seq_idx != (uint64_t) -1ULL) {
+ ERR("Network sequence index %" PRIu64 " unknown. Not adding stream.",
stream->net_seq_idx);
ret = -1;
goto error;
return ret;
}
+/*
+ * Create streams for the given channel using liblttng-ust-ctl.
+ *
+ * Return 0 on success else a negative value.
+ */
static int create_ust_streams(struct lttng_consumer_channel *channel,
struct lttng_consumer_local_data *ctx)
{
while ((ustream = ustctl_create_stream(channel->uchan, cpu))) {
int wait_fd;
- wait_fd = ustctl_get_wait_fd(ustream);
+ wait_fd = ustctl_stream_get_wait_fd(ustream);
/* Allocate consumer stream object. */
stream = allocate_stream(cpu, wait_fd, channel, ctx, &ret);
}
}
- DBG("UST consumer add stream %s (key: %d) with relayd id %" PRIu64,
+ DBG("UST consumer add stream %s (key: %" PRIu64 ") with relayd id %" PRIu64,
stream->name, stream->key, stream->relayd_stream_id);
/* Set next CPU stream. */
channel->streams.count = ++cpu;
+
+ /* Keep stream reference when creating metadata. */
+ if (channel->type == CONSUMER_CHANNEL_TYPE_METADATA) {
+ channel->metadata_stream = stream;
+ }
}
return 0;
return ret;
}
+/*
+ * Send a single given stream to the session daemon using the sock.
+ *
+ * Return 0 on success else a negative value.
+ */
static int send_sessiond_stream(int sock, struct lttng_consumer_stream *stream)
{
int ret;
assert(stream);
assert(sock >= 0);
- DBG2("UST consumer sending stream %d to sessiond", stream->key);
+ DBG2("UST consumer sending stream %" PRIu64 " to sessiond", stream->key);
/* Send stream to session daemon. */
ret = ustctl_send_stream_to_sessiond(sock, stream->ustream);
goto error;
}
- ret = ustctl_stream_close_wakeup_fd(stream->ustream);
- if (ret < 0) {
- goto error;
- }
-
error:
return ret;
}
/*
* Send channel to sessiond.
*
- * Return 0 on success or else a negative value. On error, the channel is
- * destroy using ustctl.
+ * Return 0 on success or else a negative value.
*/
static int send_sessiond_channel(int sock,
struct lttng_consumer_channel *channel,
goto error;
}
+ ret = ustctl_channel_close_wakeup_fd(channel->uchan);
+ if (ret < 0) {
+ goto error;
+ }
+
/* The channel was sent successfully to the sessiond at this point. */
cds_list_for_each_entry(stream, &channel->streams.head, send_node) {
/* Try to send the stream to the relayd if one is available. */
goto error;
}
+ channel->wait_fd = ustctl_channel_get_wait_fd(channel->uchan);
+
+ if (ret < 0) {
+ goto error;
+ }
+
/* Open all streams for this channel. */
ret = create_ust_streams(channel, ctx);
if (ret < 0) {
return ret;
}
+/*
+ * Send all stream of a channel to the right thread handling it.
+ *
+ * On error, return a negative value else 0 on success.
+ */
+static int send_streams_to_thread(struct lttng_consumer_channel *channel,
+ struct lttng_consumer_local_data *ctx)
+{
+ int ret = 0;
+ struct lttng_consumer_stream *stream, *stmp;
+
+ assert(channel);
+ assert(ctx);
+
+ /* Send streams to the corresponding thread. */
+ cds_list_for_each_entry_safe(stream, stmp, &channel->streams.head,
+ send_node) {
+ /* Sending the stream to the thread. */
+ ret = send_stream_to_thread(stream, ctx);
+ if (ret < 0) {
+ /*
+ * If we are unable to send the stream to the thread, there is
+ * a big problem so just stop everything.
+ */
+ goto error;
+ }
+
+ /* Remove node from the channel stream list. */
+ cds_list_del(&stream->send_node);
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * Write metadata to the given channel using ustctl to convert the string to
+ * the ringbuffer.
+ *
+ * Return 0 on success else a negative value.
+ */
+static int push_metadata(struct lttng_consumer_channel *metadata,
+ const char *metadata_str, uint64_t target_offset, uint64_t len)
+{
+ int ret;
+
+ assert(metadata);
+ assert(metadata_str);
+
+ DBG("UST consumer writing metadata to channel %s", metadata->name);
+
+ assert(target_offset == metadata->contig_metadata_written);
+ ret = ustctl_write_metadata_to_channel(metadata->uchan, metadata_str, len);
+ if (ret < 0) {
+ ERR("ustctl write metadata fail with ret %d, len %ld", ret, len);
+ goto error;
+ }
+ metadata->contig_metadata_written += len;
+
+ ustctl_flush_buffer(metadata->metadata_stream->ustream, 1);
+
+error:
+ return ret;
+}
+
+/*
+ * Close metadata stream wakeup_fd using the given key to retrieve the channel.
+ *
+ * Return 0 on success else an LTTng error code.
+ */
+static int close_metadata(uint64_t chan_key)
+{
+ int ret;
+ struct lttng_consumer_channel *channel;
+
+ DBG("UST consumer close metadata key %lu", chan_key);
+
+ channel = consumer_find_channel(chan_key);
+ if (!channel) {
+ ERR("UST consumer close metadata %lu not found", chan_key);
+ ret = LTTNG_ERR_UST_CHAN_NOT_FOUND;
+ goto error;
+ }
+
+ ret = ustctl_stream_close_wakeup_fd(channel->metadata_stream->ustream);
+ if (ret < 0) {
+ ERR("UST consumer unable to close fd of metadata (ret: %d)", ret);
+ ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+/*
+ * RCU read side lock MUST be acquired before calling this function.
+ *
+ * Return 0 on success else an LTTng error code.
+ */
+static int setup_metadata(struct lttng_consumer_local_data *ctx, uint64_t key)
+{
+ int ret;
+ struct lttng_consumer_channel *metadata;
+
+ DBG("UST consumer setup metadata key %lu", key);
+
+ metadata = consumer_find_channel(key);
+ if (!metadata) {
+ ERR("UST consumer push metadata %" PRIu64 " not found", key);
+ ret = LTTNG_ERR_UST_CHAN_NOT_FOUND;
+ goto error;
+ }
+
+ /*
+ * Send metadata stream to relayd if one available. Availability is
+ * known if the stream is still in the list of the channel.
+ */
+ if (cds_list_empty(&metadata->streams.head)) {
+ ERR("Metadata channel key %" PRIu64 ", no stream available.", key);
+ ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ goto error;
+ }
+
+ /* Send metadata stream to relayd if needed. */
+ ret = send_stream_to_relayd(metadata->metadata_stream);
+ if (ret < 0) {
+ ret = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ goto error;
+ }
+
+ ret = send_streams_to_thread(metadata, ctx);
+ if (ret < 0) {
+ /*
+ * If we are unable to send the stream to the thread, there is
+ * a big problem so just stop everything.
+ */
+ ret = LTTCOMM_CONSUMERD_FATAL;
+ goto error;
+ }
+ /* List MUST be empty after or else it could be reused. */
+ assert(cds_list_empty(&metadata->streams.head));
+
+ ret = 0;
+
+error:
+ return ret;
+}
+
/*
* Receive command from session daemon and process it.
*
consumer_flag_relayd_for_destroy(relayd);
}
- ret = consumer_send_status_msg(sock, ret_code);
- if (ret < 0) {
- /* Somehow, the session daemon is not responding anymore. */
- goto end_nosignal;
- }
-
- goto end_nosignal;
+ goto end_msg_sessiond;
}
case LTTNG_CONSUMER_UPDATE_STREAM:
{
case LTTNG_CONSUMER_GET_CHANNEL:
{
int ret, relayd_err = 0;
- unsigned long key = msg.u.get_channel.key;
+ uint64_t key = msg.u.get_channel.key;
struct lttng_consumer_channel *channel;
- struct lttng_consumer_stream *stream, *stmp;
channel = consumer_find_channel(key);
if (!channel) {
goto error_fatal;
}
- /* Send streams to the corresponding thread. */
- cds_list_for_each_entry_safe(stream, stmp, &channel->streams.head,
- send_node) {
- /* Sending the stream to the thread. */
- ret = send_stream_to_thread(stream, ctx);
- if (ret < 0) {
- /*
- * If we are unable to send the stream to the thread, there is
- * a big problem so just stop everything.
- */
- goto error_fatal;
- }
-
- /* Remove node from the channel stream list. */
- cds_list_del(&stream->send_node);
+ ret = send_streams_to_thread(channel, ctx);
+ if (ret < 0) {
+ /*
+ * If we are unable to send the stream to the thread, there is
+ * a big problem so just stop everything.
+ */
+ goto error_fatal;
}
-
/* List MUST be empty after or else it could be reused. */
assert(cds_list_empty(&channel->streams.head));
- /* Inform sessiond that everything is done and OK on our side. */
- ret = consumer_send_status_msg(sock, LTTNG_OK);
- if (ret < 0) {
- /* Somehow, the session daemon is not responding anymore. */
- goto end_nosignal;
+ goto end_msg_sessiond;
+ }
+ case LTTNG_CONSUMER_DESTROY_CHANNEL:
+ {
+ uint64_t key = msg.u.destroy_channel.key;
+ struct lttng_consumer_channel *channel;
+
+ channel = consumer_find_channel(key);
+ if (!channel) {
+ ERR("UST consumer get channel key %lu not found", key);
+ ret_code = LTTNG_ERR_UST_CHAN_NOT_FOUND;
+ goto end_msg_sessiond;
}
- break;
+ destroy_channel(channel);
+
+ goto end_msg_sessiond;
}
- case LTTNG_CONSUMER_DESTROY_CHANNEL:
+ case LTTNG_CONSUMER_CLOSE_METADATA:
{
int ret;
- unsigned long key = msg.u.destroy_channel.key;
+
+ ret = close_metadata(msg.u.close_metadata.key);
+ if (ret != 0) {
+ ret_code = ret;
+ }
+
+ goto end_msg_sessiond;
+ }
+ case LTTNG_CONSUMER_PUSH_METADATA:
+ {
+ int ret;
+ uint64_t len = msg.u.push_metadata.len;
+ uint64_t target_offset = msg.u.push_metadata.target_offset;
+ uint64_t key = msg.u.push_metadata.key;
struct lttng_consumer_channel *channel;
+ char *metadata_str;
- DBG("UST consumer destroy channel key %lu", key);
+ DBG("UST consumer push metadata key %lu of len %lu", key, len);
channel = consumer_find_channel(key);
if (!channel) {
- ERR("UST consumer destroy channel %lu not found", key);
+ ERR("UST consumer push metadata %lu not found", key);
ret_code = LTTNG_ERR_UST_CHAN_NOT_FOUND;
- } else {
- /* Protocol error if the stream list is NOT empty. */
- assert(!cds_list_empty(&channel->streams.head));
- consumer_del_channel(channel);
}
+ metadata_str = zmalloc(len * sizeof(char));
+ if (!metadata_str) {
+ PERROR("zmalloc metadata string");
+ ret_code = LTTCOMM_CONSUMERD_ENOMEM;
+ goto end_msg_sessiond;
+ }
+
+ /* Tell session daemon we are ready to receive the metadata. */
ret = consumer_send_status_msg(sock, LTTNG_OK);
if (ret < 0) {
/* Somehow, the session daemon is not responding anymore. */
+ goto error_fatal;
+ }
+
+ /* Wait for more data. */
+ if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) {
goto end_nosignal;
}
+
+ /* Receive metadata string. */
+ ret = lttcomm_recv_unix_sock(sock, metadata_str, len);
+ if (ret < 0) {
+ /* Session daemon is dead so return gracefully. */
+ goto end_nosignal;
+ }
+
+ ret = push_metadata(channel, metadata_str, target_offset, len);
+ free(metadata_str);
+ if (ret < 0) {
+ /* Unable to handle metadata. Notify session daemon. */
+ ret_code = LTTCOMM_CONSUMERD_ERROR_METADATA;
+ goto end_msg_sessiond;
+ }
+
+ goto end_msg_sessiond;
+ }
+ case LTTNG_CONSUMER_SETUP_METADATA:
+ {
+ int ret;
+
+ ret = setup_metadata(ctx, msg.u.setup_metadata.key);
+ if (ret) {
+ ret_code = ret;
+ }
+ goto end_msg_sessiond;
}
default:
break;
* The mmap operation should write subbuf_size amount of data when network
* streaming or the full padding (len) size when we are _not_ streaming.
*/
- if ((ret != subbuf_size && stream->net_seq_idx != -1) ||
- (ret != len && stream->net_seq_idx == -1)) {
+ if ((ret != subbuf_size && stream->net_seq_idx != (uint64_t) -1ULL) ||
+ (ret != len && stream->net_seq_idx == (uint64_t) -1ULL)) {
/*
* Display the error but continue processing to try to release the
* subbuffer. This is a DBG statement since any unexpected kill or
char full_path[PATH_MAX];
/* Opening the tracefile in write mode */
- if (stream->net_seq_idx != -1) {
+ if (stream->net_seq_idx != (uint64_t) -1ULL) {
goto end;
}
end:
return ret;
}
+
+/*
+ * Close every metadata stream wait fd of the metadata hash table. This
+ * function MUST be used very carefully so not to run into a race between the
+ * metadata thread handling streams and this function closing their wait fd.
+ *
+ * For UST, this is used when the session daemon hangs up. Its the metadata
+ * producer so calling this is safe because we are assured that no state change
+ * can occur in the metadata thread for the streams in the hash table.
+ */
+void lttng_ustconsumer_close_metadata(struct lttng_ht *metadata_ht)
+{
+ int ret;
+ struct lttng_ht_iter iter;
+ struct lttng_consumer_stream *stream;
+
+ assert(metadata_ht);
+ assert(metadata_ht->ht);
+
+ DBG("UST consumer closing all metadata streams");
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(metadata_ht->ht, &iter.iter, stream,
+ node.node) {
+ int fd = stream->wait_fd;
+
+ /*
+ * Whatever happens here we have to continue to try to close every
+ * streams. Let's report at least the error on failure.
+ */
+ ret = ustctl_stream_close_wakeup_fd(stream->ustream);
+ if (ret) {
+ ERR("Unable to close metadata stream fd %d ret %d", fd, ret);
+ }
+ DBG("Metadata wait fd %d closed", fd);
+ }
+ rcu_read_unlock();
+}
+
+void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream)
+{
+ int ret;
+
+ ret = ustctl_stream_close_wakeup_fd(stream->ustream);
+ if (ret < 0) {
+ ERR("Unable to close wakeup fd");
+ }
+}
unsigned long *off);
void *lttng_ustctl_get_mmap_base(struct lttng_consumer_stream *stream);
int lttng_ustconsumer_data_pending(struct lttng_consumer_stream *stream);
+void lttng_ustconsumer_close_metadata(struct lttng_ht *ht);
+void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream);
#else /* HAVE_LIBLTTNG_UST_CTL */
{
return NULL;
}
+static inline
+void lttng_ustconsumer_close_metadata(struct lttng_ht *ht)
+{
+}
+static inline
+void lttng_ustconsumer_close_stream_wakeup(struct lttng_consumer_stream *stream)
+{
+}
#endif /* HAVE_LIBLTTNG_UST_CTL */
#endif /* _LTTNG_USTCONSUMER_H */
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;
}
ok $? "Create session with uri $uri"
}
-function enable_lttng_consumer
-{
- uri=$1
- # Create session with custom URI
- $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-consumer -u $uri >/dev/null 2>&1
- ok $? "Enable consumer with uri $uri"
-}
-
function run_apps
{
for i in `seq 1 $NR_APP_ITER`; do
{
NETWORK_URI="net://localhost"
create_lttng_session_with_uri $SESSION_NAME $NETWORK_URI
- enable_lttng_consumer $NETWORK_URI
enable_ust_lttng_event $SESSION_NAME $EVENT_NAME
start_lttng_tracing $SESSION_NAME
run_apps
kern->metadata->conf->attr.num_subbuf
== DEFAULT_METADATA_SUBBUF_NUM &&
kern->metadata->conf->attr.switch_timer_interval
- == DEFAULT_CHANNEL_SWITCH_TIMER &&
+ == DEFAULT_KERNEL_CHANNEL_SWITCH_TIMER &&
kern->metadata->conf->attr.read_timer_interval
- == DEFAULT_CHANNEL_READ_TIMER &&
+ == DEFAULT_KERNEL_CHANNEL_READ_TIMER &&
kern->metadata->conf->attr.output
== DEFAULT_KERNEL_CHANNEL_OUTPUT,
"Validate kernel session metadata");
metadata->attr.num_subbuf
== DEFAULT_METADATA_SUBBUF_NUM &&
metadata->attr.switch_timer_interval
- == DEFAULT_CHANNEL_SWITCH_TIMER &&
+ == DEFAULT_UST_CHANNEL_SWITCH_TIMER &&
metadata->attr.read_timer_interval
- == DEFAULT_CHANNEL_READ_TIMER &&
+ == DEFAULT_UST_CHANNEL_READ_TIMER &&
metadata->attr.output == LTTNG_UST_MMAP,
"Validate UST session metadata");