From: David Goulet Date: Thu, 3 Nov 2011 15:34:59 +0000 (-0400) Subject: Rename ltt-sessiond to lttng-sessiond X-Git-Tag: v2.0-pre15~169 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=322585731ced1adba36cddcb8bdd5d997d1b2e3e;p=lttng-tools.git Rename ltt-sessiond to lttng-sessiond Signed-off-by: David Goulet --- diff --git a/.gitignore b/.gitignore index cf4970371..c2b6ff596 100644 --- a/.gitignore +++ b/.gitignore @@ -30,9 +30,9 @@ config/ !config/epoll.m4 !config/config_feature.m4 -ltt-sessiond/ltt-sessiond +lttng-sessiond/lttng-sessiond lttng/lttng -ltt-kconsumerd/ltt-kconsumerd +lttng-kconsumerd/lttng-kconsumerd lttng-consumerd/lttng-consumerd tests/test_sessions diff --git a/Makefile.am b/Makefile.am index e43da8d75..44f17148b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,7 +8,7 @@ SUBDIRS = liblttng-sessiond-comm \ lttng-consumerd \ liblttngctl \ lttng \ - ltt-sessiond \ + lttng-sessiond \ tests \ include \ doc diff --git a/README b/README index 04d840889..03bc1f591 100644 --- a/README +++ b/README @@ -50,7 +50,7 @@ PACKAGE CONTENTS: The LTTng tracing control library. - liblttng-sessiond-comm - The ltt-sessiond communication library. In order to talk with ltt-sessiond, + The lttng-sessiond communication library. In order to talk with lttng-sessiond, this library must be used. - libkernelctl @@ -62,7 +62,7 @@ PACKAGE CONTENTS: - lttng-consumerd The consumer daemon which uses liblttng-consumer. - - ltt-sessiond + - lttng-sessiond The LTTng session daemon binary. - lttng diff --git a/configure.ac b/configure.ac index ff950cd79..3ead48e7b 100644 --- a/configure.ac +++ b/configure.ac @@ -95,7 +95,7 @@ AC_CONFIG_FILES([ liblttngctl/Makefile liblttng-sessiond-comm/Makefile lttng-consumerd/Makefile - ltt-sessiond/Makefile + lttng-sessiond/Makefile lttng/Makefile tests/Makefile doc/Makefile diff --git a/doc/quickstart.txt b/doc/quickstart.txt index be43d49df..7078eecc9 100644 --- a/doc/quickstart.txt +++ b/doc/quickstart.txt @@ -18,7 +18,7 @@ installed. See http://lttng.org/lttng2.0 for more instructions for that part. For user-space tracing, you'll need an instrumented application, please see http://lttng.org/ust. -lttng-tools provide a session daemon (ltt-sessiond) that acts as a tracing +lttng-tools provide a session daemon (lttng-sessiond) that acts as a tracing registry. To trace any instrumented applications or the kernel, a registered tracing session is needed beforehand. To interact with the session daemon and a tracing session, you should use the lttng command line UI (lttng). @@ -36,7 +36,7 @@ The next sections explain how to do tracing :) Kernel Tracing -------------- -You can start the session daemon by invoking the command "ltt-sessiond", +You can start the session daemon by invoking the command "lttng-sessiond", or let the lttng command line tool do it for you. The session daemon loads the LTTng tracer modules for you if those modules can be found on your system. If they are not found, the kernel tracing feature will be diff --git a/doc/session-daemon-model.txt b/doc/session-daemon-model.txt index 6b9f4dc17..da42e276d 100644 --- a/doc/session-daemon-model.txt +++ b/doc/session-daemon-model.txt @@ -47,6 +47,7 @@ Terminology ----------------- ltt-sessiond - Main daemon for trace session registry for UST and LTTng +NOTE: Changed to lttng-sessiond in the git tree ust-consumerd - Daemon that consume UST buffers for a specific application diff --git a/include/lttng-sessiond-comm.h b/include/lttng-sessiond-comm.h index f30bf6673..ae978c278 100644 --- a/include/lttng-sessiond-comm.h +++ b/include/lttng-sessiond-comm.h @@ -32,10 +32,10 @@ #define LTTNG_RUNDIR "/var/run/lttng" /* Default unix socket path */ -#define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK LTTNG_RUNDIR "/client-ltt-sessiond" -#define DEFAULT_GLOBAL_APPS_UNIX_SOCK LTTNG_RUNDIR "/apps-ltt-sessiond" -#define DEFAULT_HOME_APPS_UNIX_SOCK "%s/.apps-ltt-sessiond" -#define DEFAULT_HOME_CLIENT_UNIX_SOCK "%s/.client-ltt-sessiond" +#define DEFAULT_GLOBAL_CLIENT_UNIX_SOCK LTTNG_RUNDIR "/client-lttng-sessiond" +#define DEFAULT_GLOBAL_APPS_UNIX_SOCK LTTNG_RUNDIR "/apps-lttng-sessiond" +#define DEFAULT_HOME_APPS_UNIX_SOCK "%s/.apps-lttng-sessiond" +#define DEFAULT_HOME_CLIENT_UNIX_SOCK "%s/.client-lttng-sessiond" /* Queue size of listen(2) */ #define LTTNG_SESSIOND_COMM_MAX_LISTEN 64 diff --git a/include/lttng/lttng-consumer.h b/include/lttng/lttng-consumer.h index 2587b3b18..a0277cf04 100644 --- a/include/lttng/lttng-consumer.h +++ b/include/lttng/lttng-consumer.h @@ -28,8 +28,8 @@ /* * When the receiving thread dies, we need to have a way to make the polling * thread exit eventually. If all FDs hang up (normal case when the - * ltt-sessiond stops), we can exit cleanly, but if there is a problem and for - * whatever reason some FDs remain open, the consumer should still exit + * lttng-sessiond stops), we can exit cleanly, but if there is a problem and + * for whatever reason some FDs remain open, the consumer should still exit * eventually. * * If the timeout is reached, it means that during this period no events diff --git a/liblttng-consumer/lttng-consumer.c b/liblttng-consumer/lttng-consumer.c index f031d5a67..081d6142b 100644 --- a/liblttng-consumer/lttng-consumer.c +++ b/liblttng-consumer/lttng-consumer.c @@ -916,11 +916,11 @@ void *lttng_consumer_thread_receive_fds(void *data) goto end; } - DBG("Sending ready command to ltt-sessiond"); + DBG("Sending ready command to lttng-sessiond"); ret = lttng_consumer_send_error(ctx, CONSUMERD_COMMAND_SOCK_READY); /* return < 0 on error, but == 0 is not fatal */ if (ret < 0) { - ERR("Error sending ready command to ltt-sessiond"); + ERR("Error sending ready command to lttng-sessiond"); goto end; } diff --git a/ltt-sessiond/Makefile.am b/ltt-sessiond/Makefile.am deleted file mode 100644 index 4c23849cf..000000000 --- a/ltt-sessiond/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include \ - -DINSTALL_BIN_PATH=\""$(bindir)"\" - -AM_CFLAGS = -fno-strict-aliasing - -bin_PROGRAMS = ltt-sessiond - -if COMPAT_EPOLL -COMPAT=compat/compat-epoll.c -else -COMPAT=compat/compat-poll.c -endif - -ltt_sessiond_SOURCES = utils.c utils.h \ - hashtable.c hashtable.h \ - compat/poll.h $(COMPAT) \ - trace-kernel.c trace-kernel.h \ - kernel-ctl.c kernel-ctl.h \ - ust-ctl.h ust-app.h trace-ust.h \ - context.c context.h \ - channel.c channel.h \ - event.c event.h \ - futex.c futex.h \ - shm.c shm.h \ - session.c session.h \ - ../hashtable/rculfhash.c \ - ../hashtable/rculfhash.h \ - ../hashtable/hash.c ../hashtable/hash.h - -if LTTNG_TOOLS_HAVE_UST -ltt_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h -endif - -# Add main.c at the end for compile order -ltt_sessiond_SOURCES += ltt-sessiond.h main.c - -# link on liblttngctl for check if sessiond is already alive. -ltt_sessiond_LDADD = -lrt -lurcu-cds -lurcu \ - $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ - $(top_builddir)/libkernelctl/libkernelctl.la \ - $(top_builddir)/liblttngctl/liblttngctl.la - -if LTTNG_TOOLS_HAVE_UST -ltt_sessiond_LDADD += -llttng-ust-comm -lustctl -endif diff --git a/ltt-sessiond/channel.c b/ltt-sessiond/channel.c deleted file mode 100644 index fda679c78..000000000 --- a/ltt-sessiond/channel.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include - -#include -#include -#include -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST -#include -#include -#else -#include "lttng-ust-ctl.h" -#include "lttng-ust-abi.h" -#endif - -#include "channel.h" -#include "hashtable.h" -#include "kernel-ctl.h" -#include "ust-ctl.h" -#include "utils.h" - -/* - * Return allocated channel attributes. - */ -struct lttng_channel *channel_new_default_attr(int dom) -{ - struct lttng_channel *chan; - - chan = zmalloc(sizeof(struct lttng_channel)); - if (chan == NULL) { - perror("malloc channel init"); - goto error_alloc; - } - - if (snprintf(chan->name, sizeof(chan->name), "%s", - DEFAULT_CHANNEL_NAME) < 0) { - perror("snprintf default channel name"); - goto error; - } - - 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: - chan->attr.subbuf_size = DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE; - chan->attr.num_subbuf = DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM; - chan->attr.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; - break; - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_UST_PID: - chan->attr.subbuf_size = DEFAULT_UST_CHANNEL_SUBBUF_SIZE; - chan->attr.num_subbuf = DEFAULT_UST_CHANNEL_SUBBUF_NUM; - chan->attr.output = DEFAULT_UST_CHANNEL_OUTPUT; - break; - default: - goto error; /* Not implemented */ - } - - return chan; - -error: - free(chan); -error_alloc: - return NULL; -} - -/* - * Copy two ltt ust channel. Dst and src must be already allocated. - */ -int channel_ust_copy(struct ltt_ust_channel *dst, - struct ltt_ust_channel *src) -{ - //struct ltt_ust_event *uevent, *new_uevent; - - memcpy(dst, src, sizeof(struct ltt_ust_channel)); - dst->events = hashtable_new_str(0); - - /* - cds_list_for_each_entry(uevent, &src->events.head, list) { - new_uevent = malloc(sizeof(struct ltt_ust_event)); - if (new_uevent == NULL) { - perror("malloc ltt_ust_event"); - goto error; - } - - memcpy(new_uevent, uevent, sizeof(struct ltt_ust_event)); - cds_list_add(&new_uevent->list, &dst->events.head); - dst->events.count++; - } - */ - - return 0; - -//error: -// return -1; -} - -/* - * Disable kernel channel of the kernel session. - */ -int channel_kernel_disable(struct ltt_kernel_session *ksession, - char *channel_name) -{ - int ret; - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, ksession); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } else if (kchan->enabled == 1) { - ret = kernel_disable_channel(kchan); - if (ret < 0) { - if (ret != EEXIST) { - ret = LTTCOMM_KERN_CHAN_DISABLE_FAIL; - } - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Enable kernel channel of the kernel session. - */ -int channel_kernel_enable(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan) -{ - int ret; - - if (kchan->enabled == 0) { - ret = kernel_enable_channel(kchan); - if (ret < 0) { - ret = LTTCOMM_KERN_CHAN_ENABLE_FAIL; - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Create kernel channel of the kernel session and notify kernel thread. - */ -int channel_kernel_create(struct ltt_kernel_session *ksession, - struct lttng_channel *chan, int kernel_pipe) -{ - int ret; - struct lttng_channel *attr = chan; - - /* Creating channel attributes if needed */ - if (attr == NULL) { - /* FIXME: this appears to be a memory leak */ - attr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - } - - /* Channel not found, creating it */ - ret = kernel_create_channel(ksession, attr, ksession->trace_path); - if (ret < 0) { - ret = LTTCOMM_KERN_CHAN_FAIL; - goto error; - } - - /* Notify kernel thread that there is a new channel */ - ret = notify_thread_pipe(kernel_pipe); - if (ret < 0) { - ret = LTTCOMM_FATAL; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Create UST channel and enable it on the tracer. - */ -int channel_ust_create(struct ltt_ust_session *usess, - struct lttng_channel *attr) -{ - int ret; - struct ltt_ust_channel *uchan; - //struct lttng_ust_channel_attr uattr; - //struct object_data *obj; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - attr->name); - if (uchan == NULL) { - uchan = trace_ust_create_channel(attr, usess->pathname); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_FAIL; - goto error; - } - rcu_read_lock(); - hashtable_add_unique(usess->domain_global.channels, &uchan->node); - rcu_read_unlock(); - } else { - ret = LTTCOMM_UST_CHAN_EXIST; - goto error; - } - - /* TODO: NOTIFY ust application to update */ - /* - ret = ustctl_create_channel(sock, usession->handle, &uattr, &obj); - if (ret < 0) { - ret = LTTCOMM_UST_CHAN_FAIL; - goto error; - } - */ - - /* - uchan->attr.overwrite = uattr.overwrite; - uchan->attr.subbuf_size = uattr.subbuf_size; - uchan->attr.num_subbuf = uattr.num_subbuf; - uchan->attr.switch_timer_interval = uattr.switch_timer_interval; - uchan->attr.read_timer_interval = uattr.read_timer_interval; - uchan->attr.output = uattr.output; - uchan->handle = obj->handle; - uchan->attr.shm_fd = obj->shm_fd; - uchan->attr.wait_fd = obj->wait_fd; - uchan->attr.memory_map_size = obj->memory_map_size; - uchan->obj = obj; - */ - - /* Add channel to session */ - //rcu_read_lock(); - //cds_list_add(&uchan->list, &usession->channels.head); - //usession->channels.count++; - //rcu_read_unlock(); - - //DBG2("Channel %s UST create successfully for sock:%d", uchan->name, sock); - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Enable UST channel on the tracer. - */ -int channel_ust_enable(struct ltt_ust_session *usession, - struct ltt_ust_channel *uchan, int sock) -{ - int ret = LTTCOMM_OK; -#ifdef DISABLE - struct object_data obj; - - obj.shm_fd = uchan->attr.shm_fd; - obj.wait_fd = uchan->attr.wait_fd; - obj.memory_map_size = uchan->attr.memory_map_size; - ret = ustctl_enable(sock, &obj); - if (ret < 0) { - ret = LTTCOMM_UST_CHAN_FAIL; - goto end; - } - ret = LTTCOMM_OK; -end: -#endif - return ret; -} - -/* - * Disable UST channel on the tracer. - */ -int channel_ust_disable(struct ltt_ust_session *usession, - struct ltt_ust_channel *uchan, int sock) -{ - int ret = LTTCOMM_OK; -#ifdef DISABLE - struct object_data obj; - - obj.shm_fd = uchan->attr.shm_fd; - obj.wait_fd = uchan->attr.wait_fd; - obj.memory_map_size = uchan->attr.memory_map_size; - ret = ustctl_disable(sock, &obj); - if (ret < 0) { - ret = LTTCOMM_UST_CHAN_FAIL; - goto end; - } - ret = LTTCOMM_OK; -end: -#endif - return ret; -} diff --git a/ltt-sessiond/channel.h b/ltt-sessiond/channel.h deleted file mode 100644 index 37c1b8c0a..000000000 --- a/ltt-sessiond/channel.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_CHANNEL_H -#define _LTT_CHANNEL_H - -#include - -#include "trace-kernel.h" -#include "trace-ust.h" - -int channel_kernel_disable(struct ltt_kernel_session *ksession, - char *channel_name); -int channel_kernel_enable(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan); -int channel_kernel_create(struct ltt_kernel_session *ksession, - struct lttng_channel *chan, int kernel_pipe); - -int channel_ust_create(struct ltt_ust_session *usession, - struct lttng_channel *chan); -int channel_ust_copy(struct ltt_ust_channel *dst, - struct ltt_ust_channel *src); -int channel_ust_disable(struct ltt_ust_session *usession, - struct ltt_ust_channel *uchan, int sock); -int channel_ust_enable(struct ltt_ust_session *usession, - struct ltt_ust_channel *uchan, int sock); - -struct lttng_channel *channel_new_default_attr(int domain); - -#endif /* _LTT_CHANNEL_H */ diff --git a/ltt-sessiond/compat/compat-epoll.c b/ltt-sessiond/compat/compat-epoll.c deleted file mode 100644 index e909b603c..000000000 --- a/ltt-sessiond/compat/compat-epoll.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "poll.h" - -unsigned int poll_max_size; - -/* - * Create epoll set and allocate returned events structure. - */ -int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) -{ - int ret; - - if (events == NULL || size <= 0) { - goto error; - } - - /* Don't bust the limit here */ - if (size > poll_max_size) { - size = poll_max_size; - } - - ret = epoll_create1(flags); - if (ret < 0) { - /* At this point, every error is fatal */ - perror("epoll_create1"); - goto error; - } - - events->epfd = ret; - - /* This *must* be freed by using lttng_poll_free() */ - events->events = zmalloc(size * sizeof(struct epoll_event)); - if (events->events == NULL) { - perror("malloc epoll set"); - goto error_close; - } - - events->events_size = size; - events->nb_fd = 0; - - return 0; - -error_close: - close(events->epfd); -error: - return -1; -} - -/* - * Add a fd to the epoll set with requesting events. - */ -int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events) -{ - int ret, new_size; - struct epoll_event ev, *ptr; - - if (events == NULL || events->events == NULL || fd < 0) { - ERR("Bad compat epoll add arguments"); - goto error; - } - - ev.events = req_events; - ev.data.fd = fd; - - ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev); - if (ret < 0) { - switch (errno) { - case EEXIST: - case ENOSPC: - case EPERM: - /* Print perror and goto end not failing. Show must go on. */ - perror("epoll_ctl ADD"); - goto end; - default: - perror("epoll_ctl ADD fatal"); - goto error; - } - } - - events->nb_fd++; - - if (events->nb_fd >= events->events_size) { - new_size = 2 * events->events_size; - ptr = realloc(events->events, new_size * sizeof(struct epoll_event)); - if (ptr == NULL) { - perror("realloc epoll add"); - goto error; - } - events->events = ptr; - events->events_size = new_size; - } - -end: - return 0; - -error: - return -1; -} - -/* - * Remove a fd from the epoll set. - */ -int compat_epoll_del(struct lttng_poll_event *events, int fd) -{ - int ret; - - if (events == NULL || fd < 0) { - goto error; - } - - ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL); - if (ret < 0) { - switch (errno) { - case ENOENT: - case EPERM: - /* Print perror and goto end not failing. Show must go on. */ - perror("epoll_ctl DEL"); - goto end; - default: - perror("epoll_ctl DEL fatal"); - goto error; - } - perror("epoll_ctl del"); - goto error; - } - - events->nb_fd--; - -end: - return 0; - -error: - return -1; -} - -/* - * Wait on epoll set. This is a blocking call of timeout value. - */ -int compat_epoll_wait(struct lttng_poll_event *events, int timeout) -{ - int ret; - - if (events == NULL || events->events == NULL || - events->events_size < events->nb_fd) { - ERR("Wrong arguments in compat_epoll_wait"); - goto error; - } - - do { - ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout); - } while (ret == -1 && errno == EINTR); - if (ret < 0) { - /* At this point, every error is fatal */ - perror("epoll_wait"); - goto error; - } - - return ret; - -error: - return -1; -} - -/* - * Setup poll set maximum size. - */ -void compat_epoll_set_max_size(void) -{ - int ret, fd; - char buf[64]; - - poll_max_size = LTTNG_POLL_DEFAULT_SIZE; - - fd = open(LTTNG_EPOLL_PROC_PATH, O_RDONLY); - if (fd < 0) { - return; - } - - ret = read(fd, buf, sizeof(buf)); - if (ret < 0) { - perror("read set max size"); - goto error; - } - - poll_max_size = atoi(buf); - if (poll_max_size <= 0) { - /* Extra precaution */ - poll_max_size = LTTNG_POLL_DEFAULT_SIZE; - } - - DBG("epoll set max size is %d", poll_max_size); - -error: - close(fd); -} diff --git a/ltt-sessiond/compat/compat-poll.c b/ltt-sessiond/compat/compat-poll.c deleted file mode 100644 index cc4bb0f97..000000000 --- a/ltt-sessiond/compat/compat-poll.c +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include - -#include "poll.h" - -unsigned int poll_max_size; - -/* - * Create pollfd data structure. - */ -int compat_poll_create(struct lttng_poll_event *events, int size) -{ - if (events == NULL || size <= 0) { - ERR("Wrong arguments for poll create"); - goto error; - } - - /* Don't bust the limit here */ - if (size > poll_max_size) { - size = poll_max_size; - } - - /* This *must* be freed by using lttng_poll_free() */ - events->events = zmalloc(size * sizeof(struct pollfd)); - if (events->events == NULL) { - perror("malloc struct pollfd"); - goto error; - } - - events->events_size = size; - events->nb_fd = 0; - - return 0; - -error: - return -1; -} - -/* - * Add fd to pollfd data structure with requested events. - */ -int compat_poll_add(struct lttng_poll_event *events, int fd, - uint32_t req_events) -{ - int new_size; - struct pollfd *ptr; - - if (events == NULL || events->events == NULL || fd < 0) { - ERR("Bad compat poll add arguments"); - goto error; - } - - /* Reallocate pollfd structure by a factor of 2 if needed. */ - if (events->nb_fd >= events->events_size) { - new_size = 2 * events->events_size; - ptr = realloc(events->events, new_size * sizeof(struct pollfd)); - if (ptr == NULL) { - perror("realloc poll add"); - goto error; - } - events->events = ptr; - events->events_size = new_size; - } - - events->events[events->nb_fd].fd = fd; - events->events[events->nb_fd].events = req_events; - events->nb_fd++; - - DBG("fd %d of %d added to pollfd", fd, events->nb_fd); - - return 0; - -error: - return -1; -} - -/* - * Remove a fd from the pollfd structure. - */ -int compat_poll_del(struct lttng_poll_event *events, int fd) -{ - int new_size, i, count = 0; - struct pollfd *old = NULL, *new = NULL; - - if (events == NULL || events->events == NULL || fd < 0) { - ERR("Wrong arguments for poll del"); - goto error; - } - - old = events->events; - new_size = events->events_size - 1; - - /* Safety check on size */ - if (new_size > poll_max_size) { - new_size = poll_max_size; - } - - new = zmalloc(new_size * sizeof(struct pollfd)); - if (new == NULL) { - perror("malloc poll del"); - goto error; - } - - for (i = 0; i < events->events_size; i++) { - /* Don't put back the fd we want to delete */ - if (old[i].fd != fd) { - new[count].fd = old[i].fd; - new[count].events = old[i].events; - count++; - } - } - - events->events_size = new_size; - events->events = new; - events->nb_fd--; - - free(old); - - return 0; - -error: - return -1; -} - -/* - * Wait on poll() with timeout. Blocking call. - */ -int compat_poll_wait(struct lttng_poll_event *events, int timeout) -{ - int ret; - - if (events == NULL || events->events == NULL || - events->events_size < events->nb_fd) { - ERR("poll wait arguments error"); - goto error; - } - - ret = poll(events->events, events->nb_fd, timeout); - if (ret < 0) { - /* At this point, every error is fatal */ - perror("poll wait"); - goto error; - } - - return ret; - -error: - return -1; -} - -/* - * Setup poll set maximum size. - */ -void compat_poll_set_max_size(void) -{ - int ret; - struct rlimit lim; - - /* Default value */ - poll_max_size = LTTNG_POLL_DEFAULT_SIZE; - - ret = getrlimit(RLIMIT_NOFILE, &lim); - if (ret < 0) { - perror("getrlimit poll RLIMIT_NOFILE"); - return; - } - - poll_max_size = lim.rlim_cur; - if (poll_max_size <= 0) { - /* Extra precaution */ - poll_max_size = LTTNG_POLL_DEFAULT_SIZE; - } - - DBG("poll set max size set to %u", poll_max_size); -} diff --git a/ltt-sessiond/compat/poll.h b/ltt-sessiond/compat/poll.h deleted file mode 100644 index 3e804305f..000000000 --- a/ltt-sessiond/compat/poll.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_POLL_H -#define _LTT_POLL_H - -#include -#include - -#include - -/* - * 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. - */ -#define LTTNG_POLL_DEFAULT_SIZE 65535 - -/* - * Maximum number of fd we can monitor. - * - * For epoll(7), /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28) will - * be used for the maximum size of the poll set. If this interface is not - * available, according to the manpage, the max_user_watches value is 1/25 (4%) - * of the available low memory divided by the registration cost in bytes which - * is 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. - * - * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by - * getrlimit(2). - */ -extern unsigned int poll_max_size; - -/* - * Used by lttng_poll_clean to free the events structure in a lttng_poll_event. - */ -static inline void __lttng_poll_free(void *events) -{ - free(events); -} - -/* - * epoll(7) implementation. - */ -#ifdef HAVE_EPOLL -#include - -/* See man epoll(7) for this define path */ -#define LTTNG_EPOLL_PROC_PATH "/proc/sys/fs/epoll/max_user_watches" - -enum { - /* Polling variables compatibility for epoll */ - LPOLLIN = EPOLLIN, - LPOLLPRI = EPOLLPRI, - LPOLLOUT = EPOLLOUT, - LPOLLRDNORM = EPOLLRDNORM, - LPOLLRDBAND = EPOLLRDBAND, - LPOLLWRNORM = EPOLLWRNORM, - LPOLLWRBAND = EPOLLWRBAND, - LPOLLMSG = EPOLLMSG, - LPOLLERR = EPOLLERR, - LPOLLHUP = EPOLLHUP, - LPOLLNVAL = EPOLLHUP, - LPOLLRDHUP = EPOLLRDHUP, - /* Close on exec feature of epoll */ - LTTNG_CLOEXEC = EPOLL_CLOEXEC, -}; - -struct compat_epoll_event { - int epfd; - uint32_t nb_fd; /* Current number of fd in events */ - uint32_t events_size; /* Size of events array */ - struct epoll_event *events; -}; -#define lttng_poll_event compat_epoll_event - -/* - * For the following calls, consider 'e' to be a lttng_poll_event pointer and i - * being the index of the events array. - */ -#define LTTNG_POLL_GETFD(e, i) LTTNG_REF(e)->events[i].data.fd -#define LTTNG_POLL_GETEV(e, i) LTTNG_REF(e)->events[i].events -#define LTTNG_POLL_GETNB(e) LTTNG_REF(e)->nb_fd -#define LTTNG_POLL_GETSZ(e) LTTNG_REF(e)->events_size - -/* - * Create the epoll set. No memory allocation is done here. - */ -extern int compat_epoll_create(struct lttng_poll_event *events, - int size, int flags); -#define lttng_poll_create(events, size, flags) \ - compat_epoll_create(events, size, flags); - -/* - * Wait on epoll set with the number of fd registered to the lttng_poll_event - * data structure (events). - */ -extern int compat_epoll_wait(struct lttng_poll_event *events, int timeout); -#define lttng_poll_wait(events, timeout) \ - compat_epoll_wait(events, timeout); - -/* - * Add a fd to the epoll set and resize the epoll_event structure if needed. - */ -extern int compat_epoll_add(struct lttng_poll_event *events, - int fd, uint32_t req_events); -#define lttng_poll_add(events, fd, req_events) \ - compat_epoll_add(events, fd, req_events); - -/* - * Remove a fd from the epoll set. - */ -extern int compat_epoll_del(struct lttng_poll_event *events, int fd); -#define lttng_poll_del(events, fd) \ - compat_epoll_del(events, fd); - -/* - * Set up the poll set limits variable poll_max_size - */ -extern void compat_epoll_set_max_size(void); -#define lttng_poll_set_max_size(void) \ - compat_epoll_set_max_size(void); - -/* - * This function memset with zero the structure since it can be reused at each - * round of a main loop. Being in a loop and using a non static number of fds, - * this function must be called to insure coherent events with associted fds. - */ -static inline void lttng_poll_reset(struct lttng_poll_event *events) -{ - if (events && events->events) { - memset(events->events, 0, - events->nb_fd * sizeof(struct epoll_event)); - } -} - -/* - * Clean the events structure of a lttng_poll_event. It's the caller - * responsability to free the lttng_poll_event memory. - */ -static inline void lttng_poll_clean(struct lttng_poll_event *events) -{ - if (events) { - close(events->epfd); - __lttng_poll_free((void *) events->events); - } -} - -#else /* HAVE_EPOLL */ -/* - * Fallback on poll(2) API - */ - -/* Needed for some poll event values */ -#ifndef __USE_XOPEN -#define __USE_XOPEN -#endif - -/* Needed for some poll event values */ -#ifndef __USE_GNU -#define __USE_GNU -#endif - -#include -#include - -enum { - /* Polling variables compatibility for poll */ - LPOLLIN = POLLIN, - LPOLLPRI = POLLPRI, - LPOLLOUT = POLLOUT, - LPOLLRDNORM = POLLRDNORM, - LPOLLRDBAND = POLLRDBAND, - LPOLLWRNORM = POLLWRNORM, - LPOLLWRBAND = POLLWRBAND, - LPOLLMSG = POLLMSG, - LPOLLERR = POLLERR, - LPOLLHUP = POLLHUP | POLLNVAL, - LPOLLRDHUP = POLLRDHUP, - /* Close on exec feature does not exist for poll(2) */ - LTTNG_CLOEXEC = 0xdead, -}; - -struct compat_poll_event { - uint32_t nb_fd; /* Current number of fd in events */ - uint32_t events_size; /* Size of events array */ - struct pollfd *events; -}; -#define lttng_poll_event compat_poll_event - -/* - * For the following calls, consider 'e' to be a lttng_poll_event pointer and i - * being the index of the events array. - */ -#define LTTNG_POLL_GETFD(e, i) LTTNG_REF(e)->events[i].fd -#define LTTNG_POLL_GETEV(e, i) LTTNG_REF(e)->events[i].revents -#define LTTNG_POLL_GETNB(e) LTTNG_REF(e)->nb_fd -#define LTTNG_POLL_GETSZ(e) LTTNG_REF(e)->events_size - -/* - * Create a pollfd structure of size 'size'. - */ -extern int compat_poll_create(struct lttng_poll_event *events, int size); -#define lttng_poll_create(events, size, flags) \ - compat_poll_create(events, size); - -/* - * Wait on poll(2) event with nb_fd registered to the lttng_poll_event data - * structure. - */ -extern int compat_poll_wait(struct lttng_poll_event *events, int timeout); -#define lttng_poll_wait(events, timeout) \ - compat_poll_wait(events, timeout); - -/* - * Add the fd to the pollfd structure. Resize if needed. - */ -extern int compat_poll_add(struct lttng_poll_event *events, - int fd, uint32_t req_events); -#define lttng_poll_add(events, fd, req_events) \ - compat_poll_add(events, fd, req_events); - -/* - * Remove the fd from the pollfd. Memory allocation is done to recreate a new - * pollfd, data is copied from the old pollfd to the new and, finally, the old - * one is freed(). - */ -extern int compat_poll_del(struct lttng_poll_event *events, int fd); -#define lttng_poll_del(events, fd) \ - compat_poll_del(events, fd); - -/* - * Set up the poll set limits variable poll_max_size - */ -extern void compat_poll_set_max_size(void); -#define lttng_poll_set_max_size(void) \ - compat_poll_set_max_size(void); - -/* - * No need to reset a pollfd structure for poll(2) - */ -static inline void lttng_poll_reset(struct lttng_poll_event *events) -{} - -/* - * Clean the events structure of a lttng_poll_event. It's the caller - * responsability to free the lttng_poll_event memory. - */ -static inline void lttng_poll_clean(struct lttng_poll_event *events) -{ - if (events) { - __lttng_poll_free((void *) events->events); - } -} - -#endif /* HAVE_EPOLL */ - -#endif /* _LTT_POLL_H */ diff --git a/ltt-sessiond/context.c b/ltt-sessiond/context.c deleted file mode 100644 index 41e33ec27..000000000 --- a/ltt-sessiond/context.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#include -#include - -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST -#include -#include -#else -#include "lttng-ust-ctl.h" -#include "lttng-ust-abi.h" -#endif - -#include "context.h" -#include "hashtable.h" -#include "kernel-ctl.h" - -/* - * Add kernel context to an event of a specific channel. - */ -static int add_kctx_to_event(struct lttng_kernel_context *kctx, - struct ltt_kernel_channel *kchan, char *event_name) -{ - int ret, found = 0; - struct ltt_kernel_event *kevent; - - DBG("Add kernel context to event %s", event_name); - - kevent = trace_kernel_get_event_by_name(event_name, kchan); - if (kevent != NULL) { - ret = kernel_add_event_context(kevent, kctx); - if (ret < 0) { - goto error; - } - found = 1; - } - - ret = found; - -error: - return ret; -} - -/* - * Add kernel context to all channel. - * - * If event_name is specified, add context to event instead. - */ -static int add_kctx_all_channels(struct ltt_kernel_session *ksession, - struct lttng_kernel_context *kctx, char *event_name) -{ - int ret, no_event = 0, found = 0; - struct ltt_kernel_channel *kchan; - - if (strlen(event_name) == 0) { - no_event = 1; - } - - DBG("Adding kernel context to all channels (event: %s)", event_name); - - /* Go over all channels */ - cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { - if (no_event) { - ret = kernel_add_channel_context(kchan, kctx); - if (ret < 0) { - ret = LTTCOMM_KERN_CONTEXT_FAIL; - goto error; - } - } else { - ret = add_kctx_to_event(kctx, kchan, event_name); - if (ret < 0) { - ret = LTTCOMM_KERN_CONTEXT_FAIL; - goto error; - } else if (ret == 1) { - /* Event found and context added */ - found = 1; - break; - } - } - } - - if (!found && !no_event) { - ret = LTTCOMM_NO_EVENT; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Add kernel context to a specific channel. - * - * If event_name is specified, add context to that event. - */ -static int add_kctx_to_channel(struct lttng_kernel_context *kctx, - struct ltt_kernel_channel *kchan, char *event_name) -{ - int ret, no_event = 0, found = 0; - - if (strlen(event_name) == 0) { - no_event = 1; - } - - DBG("Add kernel context to channel '%s', event '%s'", - kchan->channel->name, event_name); - - if (no_event) { - ret = kernel_add_channel_context(kchan, kctx); - if (ret < 0) { - ret = LTTCOMM_KERN_CONTEXT_FAIL; - goto error; - } - } else { - ret = add_kctx_to_event(kctx, kchan, event_name); - if (ret < 0) { - ret = LTTCOMM_KERN_CONTEXT_FAIL; - goto error; - } else if (ret == 1) { - /* Event found and context added */ - found = 1; - } - } - - if (!found && !no_event) { - ret = LTTCOMM_NO_EVENT; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Add kernel context to tracer. - */ -int context_kernel_add(struct ltt_kernel_session *ksession, - struct lttng_event_context *ctx, char *event_name, - char *channel_name) -{ - int ret; - struct ltt_kernel_channel *kchan; - struct lttng_kernel_context kctx; - - /* Setup kernel context structure */ - kctx.ctx = ctx->ctx; - kctx.u.perf_counter.type = ctx->u.perf_counter.type; - kctx.u.perf_counter.config = ctx->u.perf_counter.config; - strncpy(kctx.u.perf_counter.name, ctx->u.perf_counter.name, - LTTNG_SYMBOL_NAME_LEN); - kctx.u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - - if (strlen(channel_name) == 0) { - ret = add_kctx_all_channels(ksession, &kctx, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - } else { - /* Get kernel channel */ - kchan = trace_kernel_get_channel_by_name(channel_name, ksession); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = add_kctx_to_channel(&kctx, kchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * UST support. - */ - -/* - * Add UST context to an event of a specific channel. - */ -#ifdef DISABLE -static int add_ustctx_to_event(struct ltt_ust_session *ustsession, - struct lttng_ust_context *ustctx, - struct ltt_ust_channel *ustchan, char *event_name) -{ - int ret, found = 0; - struct ltt_ust_event *ustevent; - struct object_data *context_data; /* FIXME: currently a memleak */ - - DBG("Add UST context to event %s", event_name); - - ustevent = trace_ust_find_event_by_name(ustchan->events, event_name); - if (ustevent != NULL) { - ret = ustctl_add_context(ustsession->sock, ustctx, - ustevent->obj, &context_data); - if (ret < 0) { - goto error; - } - found = 1; - } - - ret = found; - -error: - return ret; -} -#endif - -/* - * Add UST context to all channel. - * - * If event_name is specified, add context to event instead. - */ -static int add_ustctx_all_channels(struct ltt_ust_session *ustsession, - struct lttng_ust_context *ustctx, char *event_name, - struct cds_lfht *channels) -{ -#ifdef DISABLE - int ret, no_event = 0, found = 0; - struct ltt_ust_channel *ustchan; - struct object_data *context_data; /* FIXME: currently a memleak */ - - if (strlen(event_name) == 0) { - no_event = 1; - } - - DBG("Adding ust context to all channels (event: %s)", event_name); - - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - hashtable_get_first(channels, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ustchan = caa_container_of(node, struct ltt_ust_channel, node); - if (no_event) { - //ret = ustctl_add_context(ustsession->sock, - // ustctx, ustchan->obj, &context_data); - if (ret < 0) { - ret = LTTCOMM_UST_CONTEXT_FAIL; - goto error; - } - } else { - ret = add_ustctx_to_event(ustsession, ustctx, ustchan, event_name); - if (ret < 0) { - ret = LTTCOMM_UST_CONTEXT_FAIL; - goto error; - } else if (ret == 1) { - /* Event found and context added */ - found = 1; - break; - } - } - hashtable_get_next(channels, &iter); - } - rcu_read_unlock(); - - if (!found && !no_event) { - ret = LTTCOMM_NO_EVENT; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -#endif - return 0; -} - -/* - * Add UST context to a specific channel. - * - * If event_name is specified, add context to that event. - */ -static int add_ustctx_to_channel(struct ltt_ust_session *ustsession, - struct lttng_ust_context *ustctx, - struct ltt_ust_channel *ustchan, char *event_name) -{ -#ifdef DISABLE - int ret, no_event = 0, found = 0; - struct object_data *context_data; /* FIXME: currently a memleak */ - - if (strlen(event_name) == 0) { - no_event = 1; - } - - DBG("Add UST context to channel '%s', event '%s'", - ustchan->name, event_name); - - if (no_event) { - //ret = ustctl_add_context(ustsession->sock, ustctx, - // ustchan->obj, &context_data); - if (ret < 0) { - ret = LTTCOMM_UST_CONTEXT_FAIL; - goto error; - } - } else { - ret = add_ustctx_to_event(ustsession, ustctx, ustchan, event_name); - if (ret < 0) { - ret = LTTCOMM_UST_CONTEXT_FAIL; - goto error; - } else if (ret == 1) { - /* Event found and context added */ - found = 1; - } - } - - if (!found && !no_event) { - ret = LTTCOMM_NO_EVENT; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -#endif - return 0; -} - -/* - * Add UST context to tracer. - */ -int context_ust_add(struct ltt_ust_session *ustsession, - struct lttng_event_context *ctx, char *event_name, - char *channel_name, int domain) -{ - int ret; - struct cds_lfht *chan_ht = NULL; - struct ltt_ust_channel *ustchan; - struct lttng_ust_context ustctx; - - /* Setup UST context structure */ - ustctx.ctx = ctx->ctx; - - switch (domain) { - case LTTNG_DOMAIN_UST: - chan_ht = ustsession->domain_global.channels; - break; - } - - if (strlen(channel_name) == 0) { - ret = add_ustctx_all_channels(ustsession, &ustctx, event_name, chan_ht); - if (ret != LTTCOMM_OK) { - goto error; - } - } else { - /* Get UST channel */ - ustchan = trace_ust_find_channel_by_name(chan_ht, channel_name); - if (ustchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = add_ustctx_to_channel(ustsession, &ustctx, ustchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} diff --git a/ltt-sessiond/context.h b/ltt-sessiond/context.h deleted file mode 100644 index e97e9489a..000000000 --- a/ltt-sessiond/context.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_CONTEXT_H -#define _LTT_CONTEXT_H - -#include -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST -#include -#else -#include "lttng-ust-abi.h" -#endif - - -#include "trace-kernel.h" -#include "trace-ust.h" - -int context_kernel_add(struct ltt_kernel_session *ksession, - struct lttng_event_context *ctx, char *event_name, char *channel_name); -int context_ust_add(struct ltt_ust_session *ustsession, - struct lttng_event_context *ctx, char *event_name, - char *channel_name, int domain); - -#endif /* _LTT_CONTEXT_H */ diff --git a/ltt-sessiond/event.c b/ltt-sessiond/event.c deleted file mode 100644 index 1f90c1c73..000000000 --- a/ltt-sessiond/event.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include -#include -#include - -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST -#include -#else -#include "lttng-ust-ctl.h" -#endif - -#include "channel.h" -#include "event.h" -#include "hashtable.h" -#include "kernel-ctl.h" - -/* - * Setup a lttng_event used to enable *all* syscall tracing. - */ -static void init_syscalls_kernel_event(struct lttng_event *event) -{ - event->name[0] = '\0'; - /* - * We use LTTNG_EVENT* here since the trace kernel creation will make the - * right changes for the kernel. - */ - event->type = LTTNG_EVENT_SYSCALL; -} - -/* - * Disable kernel tracepoint event for a channel from the kernel session. - */ -int event_kernel_disable_tracepoint(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, char *event_name) -{ - int ret; - struct ltt_kernel_event *kevent; - - kevent = trace_kernel_get_event_by_name(event_name, kchan); - if (kevent == NULL) { - ret = LTTCOMM_NO_EVENT; - goto error; - } - - ret = kernel_disable_event(kevent); - if (ret < 0) { - ret = LTTCOMM_KERN_DISABLE_FAIL; - goto error; - } - - DBG("Kernel event %s disable for channel %s.", - kevent->event->name, kchan->channel->name); - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Disable kernel tracepoint events for a channel from the kernel session. - */ -int event_kernel_disable_all_tracepoints(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan) -{ - int ret; - struct ltt_kernel_event *kevent; - - /* For each event in the kernel session */ - cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { - ret = kernel_disable_event(kevent); - if (ret < 0) { - /* We continue disabling the rest */ - continue; - } - } - ret = LTTCOMM_OK; - return ret; -} - -/* - * Disable kernel syscall events for a channel from the kernel session. - */ -int event_kernel_disable_all_syscalls(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan) -{ - ERR("Cannot disable syscall tracing for existing session. Please destroy session instead."); - return LTTCOMM_OK; /* Return OK so disable all succeeds */ -} - -/* - * Disable all kernel event for a channel from the kernel session. - */ -int event_kernel_disable_all(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan) -{ - int ret; - - ret = event_kernel_disable_all_tracepoints(ksession, kchan); - if (ret != LTTCOMM_OK) - return ret; - ret = event_kernel_disable_all_syscalls(ksession, kchan); - return ret; -} - -/* - * Enable kernel tracepoint event for a channel from the kernel session. - */ -int event_kernel_enable_tracepoint(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, struct lttng_event *event) -{ - int ret; - struct ltt_kernel_event *kevent; - - kevent = trace_kernel_get_event_by_name(event->name, kchan); - if (kevent == NULL) { - ret = kernel_create_event(event, kchan); - if (ret < 0) { - if (ret == -EEXIST) { - ret = LTTCOMM_KERN_EVENT_EXIST; - } else { - ret = LTTCOMM_KERN_ENABLE_FAIL; - } - goto end; - } - } else if (kevent->enabled == 0) { - ret = kernel_enable_event(kevent); - if (ret < 0) { - ret = LTTCOMM_KERN_ENABLE_FAIL; - goto end; - } - } - ret = LTTCOMM_OK; -end: - return ret; -} - -/* - * Enable all kernel tracepoint events of a channel of the kernel session. - */ -int event_kernel_enable_all_tracepoints(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd) -{ - int size, i, ret; - struct ltt_kernel_event *kevent; - struct lttng_event *event_list; - - /* For each event in the kernel session */ - cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { - ret = kernel_enable_event(kevent); - if (ret < 0) { - /* Enable failed but still continue */ - continue; - } - } - - size = kernel_list_events(kernel_tracer_fd, &event_list); - if (size < 0) { - ret = LTTCOMM_KERN_LIST_FAIL; - goto end; - } - - for (i = 0; i < size; i++) { - kevent = trace_kernel_get_event_by_name(event_list[i].name, kchan); - if (kevent == NULL) { - /* Default event type for enable all */ - event_list[i].type = LTTNG_EVENT_TRACEPOINT; - /* Enable each single tracepoint event */ - ret = kernel_create_event(&event_list[i], kchan); - if (ret < 0) { - /* Ignore error here and continue */ - } - } - } - free(event_list); - ret = LTTCOMM_OK; -end: - return ret; - -} - -/* - * Enable all kernel tracepoint events of a channel of the kernel session. - */ -int event_kernel_enable_all_syscalls(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd) -{ - int ret; - struct lttng_event event; - - init_syscalls_kernel_event(&event); - - DBG("Enabling all syscall tracing"); - - ret = kernel_create_event(&event, kchan); - if (ret < 0) { - goto end; - } - ret = LTTCOMM_OK; -end: - return ret; -} - -/* - * Enable all kernel events of a channel of the kernel session. - */ -int event_kernel_enable_all(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd) -{ - int ret; - - ret = event_kernel_enable_all_tracepoints(ksession, kchan, kernel_tracer_fd); - if (ret != LTTCOMM_OK) { - goto end; - } - ret = event_kernel_enable_all_syscalls(ksession, kchan, kernel_tracer_fd); -end: - return ret; -} - -/* - * Enable UST tracepoint event for a channel from a UST session. - */ -#ifdef DISABLE -int event_ust_enable_tracepoint(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - int ret; - struct lttng_ust_event ltt_uevent; - struct object_data *obj_event; - - strncpy(ltt_uevent.name, uevent->attr.name, sizeof(ltt_uevent.name)); - ltt_uevent.name[sizeof(ltt_uevent.name) - 1] = '\0'; - /* TODO: adjust to other instrumentation types */ - ltt_uevent.instrumentation = LTTNG_UST_TRACEPOINT; - - ret = ustctl_create_event(app->key.sock, <t_uevent, - uchan->obj, &obj_event); - if (ret < 0) { - DBG("Error ustctl create event %s for app pid: %d, sock: %d ret %d", - uevent->attr.name, app->key.pid, app->key.sock, ret); - goto next; - } - - uevent->obj = obj_event; - uevent->handle = obj_event->handle; - uevent->enabled = 1; - ret = LTTCOMM_OK; -end: - return ret; -} -#endif - -#ifdef DISABLE -int event_ust_disable_tracepoint(struct ltt_ust_session *ustsession, - struct ltt_ust_channel *ustchan, char *event_name) -{ - int ret; - struct ltt_ust_event *ustevent; - - ustevent = trace_ust_find_event_by_name(ustchan->events, event_name); - if (ustevent == NULL) { - ret = LTTCOMM_NO_EVENT; - goto end; - } - //ret = ustctl_disable(ustsession->sock, ustevent->obj); - if (ret < 0) { - ret = LTTCOMM_UST_ENABLE_FAIL; - goto end; - } - ustevent->enabled = 0; - ret = LTTCOMM_OK; -end: - return ret; -} -#endif diff --git a/ltt-sessiond/event.h b/ltt-sessiond/event.h deleted file mode 100644 index 2852b3535..000000000 --- a/ltt-sessiond/event.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_EVENT_H -#define _LTT_EVENT_H - -#include - -#include "trace-kernel.h" - -int event_kernel_disable_tracepoint(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, char *event_name); -int event_kernel_disable_all_syscalls(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan); -int event_kernel_disable_all_tracepoints(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan); -int event_kernel_disable_all(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan); - -int event_kernel_enable_tracepoint(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, struct lttng_event *event); -int event_kernel_enable_all_tracepoints(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd); -int event_kernel_enable_all_syscalls(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd); -int event_kernel_enable_all(struct ltt_kernel_session *ksession, - struct ltt_kernel_channel *kchan, int kernel_tracer_fd); - -int event_ust_enable_tracepoint(struct ltt_ust_session *ustsession, - struct ltt_ust_channel *ustchan, struct ltt_ust_event *uevent); -int event_ust_disable_tracepoint(struct ltt_ust_session *ustsession, - struct ltt_ust_channel *ustchan, char *event_name); - -#endif /* _LTT_EVENT_H */ diff --git a/ltt-sessiond/futex.c b/ltt-sessiond/futex.c deleted file mode 100644 index de3f94e6e..000000000 --- a/ltt-sessiond/futex.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#include - -#include "futex.h" - -/* - * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the - * "nto1" added to all function signature. - * - * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu - * git tree for a detail example of this scheme being used. futex_async() is - * the urcu wrapper over the futex() sycall. - * - * There is also a formal verification available in the git tree. - * - * branch: formal-model - * commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a - * - * Ref: git://git.lttng.org/userspace-rcu.git - */ - -/* - * Update futex according to active or not. This scheme is used to wake every - * libust waiting on the shared memory map futex hence the INT_MAX used in the - * futex() call. If active, we set the value and wake everyone else we indicate - * that we are gone (cleanup() case). - */ -void futex_wait_update(int32_t *futex, int active) -{ - if (active) { - uatomic_set(futex, 1); - futex_async(futex, FUTEX_WAKE, - INT_MAX, NULL, NULL, 0); - } else { - uatomic_set(futex, 0); - } - - DBG("Futex wait update active %d", active); -} - -/* - * Prepare futex. - */ -void futex_nto1_prepare(int32_t *futex) -{ - uatomic_set(futex, -1); - cmm_smp_mb(); - - DBG("Futex n to 1 prepare done"); -} - -/* - * Wait futex. - */ -void futex_nto1_wait(int32_t *futex) -{ - cmm_smp_mb(); - - if (uatomic_read(futex) == -1) { - futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0); - } - - DBG("Futex n to 1 wait done"); -} - -/* - * Wake 1 futex. - */ -void futex_nto1_wake(int32_t *futex) -{ - if (unlikely(uatomic_read(futex) == -1)) { - uatomic_set(futex, 0); - futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0); - } - - DBG("Futex n to 1 wake done"); -} diff --git a/ltt-sessiond/futex.h b/ltt-sessiond/futex.h deleted file mode 100644 index a056ec22b..000000000 --- a/ltt-sessiond/futex.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_FUTEX_H -#define _LTT_FUTEX_H - -void futex_wait_update(int32_t *futex, int active); -void futex_nto1_prepare(int32_t *futex); -void futex_nto1_wait(int32_t *futex); -void futex_nto1_wake(int32_t *futex); - -#endif /* _LTT_FUTEX_H */ diff --git a/ltt-sessiond/hashtable.c b/ltt-sessiond/hashtable.c deleted file mode 100644 index 6bca1dac0..000000000 --- a/ltt-sessiond/hashtable.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include - -#include - -#include "hashtable.h" -#include "../hashtable/rculfhash.h" -#include "../hashtable/hash.h" - -struct cds_lfht *hashtable_new(unsigned long size) -{ - if (size == 0) { - size = DEFAULT_HT_SIZE; - } - - return cds_lfht_new(hash_key, hash_compare_key, 0x42UL, - size, size, CDS_LFHT_AUTO_RESIZE, NULL); -} - -struct cds_lfht *hashtable_new_str(unsigned long size) -{ - if (size == 0) { - size = DEFAULT_HT_SIZE; - } - - return cds_lfht_new(hash_key_str, hash_compare_key_str, 0x42UL, - size, size, CDS_LFHT_AUTO_RESIZE, NULL); -} - -struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter) -{ - /* Safety net */ - if (iter == NULL) { - return NULL; - } - - return cds_lfht_iter_get_node(iter); -} - -struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key, - size_t key_len, struct cds_lfht_iter *iter) -{ - /* Safety net */ - if (ht == NULL || iter == NULL || key == NULL) { - return NULL; - } - - cds_lfht_lookup(ht, key, key_len, iter); - - return hashtable_iter_get_node(iter); -} - -void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter) -{ - cds_lfht_first(ht, iter); -} - -void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter) -{ - cds_lfht_next(ht, iter); -} - -void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node) -{ - cds_lfht_add_unique(ht, node); -} - -void hashtable_node_init(struct cds_lfht_node *node, void *key, - size_t key_len) -{ - cds_lfht_node_init(node, key, key_len); -} - -int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter) -{ - /* Safety net */ - if (ht == NULL || iter == NULL) { - return -1; - } - - return cds_lfht_del(ht, iter); -} - -unsigned long hashtable_get_count(struct cds_lfht *ht) -{ - long ab, aa; - unsigned long count, removed; - - cds_lfht_count_nodes(ht, &ab, &count, &removed, &aa); - - return count; -} diff --git a/ltt-sessiond/hashtable.h b/ltt-sessiond/hashtable.h deleted file mode 100644 index 5174eed75..000000000 --- a/ltt-sessiond/hashtable.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_HASHTABLE_H -#define _LTT_HASHTABLE_H - -#include -#include "../hashtable/rculfhash.h" - -struct cds_lfht *hashtable_new(unsigned long size); -struct cds_lfht *hashtable_new_str(unsigned long size); - -struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter); -struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key, - size_t key_len, struct cds_lfht_iter *iter); - -void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter); -void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter); -void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node); -void hashtable_node_init(struct cds_lfht_node *node, - void *key, size_t key_len); - -int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter); -unsigned long hashtable_get_count(struct cds_lfht *ht); - -#endif /* _LTT_HASHTABLE_H */ diff --git a/ltt-sessiond/kernel-ctl.c b/ltt-sessiond/kernel-ctl.c deleted file mode 100644 index 203c01050..000000000 --- a/ltt-sessiond/kernel-ctl.c +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "kernel-ctl.h" - -/* - * Add context on a kernel channel. - */ -int kernel_add_channel_context(struct ltt_kernel_channel *chan, - struct lttng_kernel_context *ctx) -{ - int ret; - - DBG("Adding context to channel %s", chan->channel->name); - ret = kernctl_add_context(chan->fd, ctx); - if (ret < 0) { - if (errno != EEXIST) { - perror("add context ioctl"); - } else { - /* If EEXIST, we just ignore the error */ - ret = 0; - } - goto error; - } - - chan->ctx = malloc(sizeof(struct lttng_kernel_context)); - if (chan->ctx == NULL) { - perror("malloc event context"); - goto error; - } - - memcpy(chan->ctx, ctx, sizeof(struct lttng_kernel_context)); - - return 0; - -error: - return ret; -} - -/* - * Add context on a kernel event. - */ -int kernel_add_event_context(struct ltt_kernel_event *event, - struct lttng_kernel_context *ctx) -{ - int ret; - - DBG("Adding context to event %s", event->event->name); - ret = kernctl_add_context(event->fd, ctx); - if (ret < 0) { - perror("add context ioctl"); - goto error; - } - - event->ctx = malloc(sizeof(struct lttng_kernel_context)); - if (event->ctx == NULL) { - perror("malloc event context"); - goto error; - } - - memcpy(event->ctx, ctx, sizeof(struct lttng_kernel_context)); - - return 0; - -error: - return ret; -} - -/* - * Create a new kernel session, register it to the kernel tracer and add it to - * the session daemon session. - */ -int kernel_create_session(struct ltt_session *session, int tracer_fd) -{ - int ret; - struct ltt_kernel_session *lks; - - /* Allocate data structure */ - lks = trace_kernel_create_session(session->path); - if (lks == NULL) { - ret = -1; - goto error; - } - - /* Kernel tracer session creation */ - ret = kernctl_create_session(tracer_fd); - if (ret < 0) { - perror("ioctl kernel create session"); - goto error; - } - - lks->fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(lks->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - lks->consumer_fds_sent = 0; - session->kernel_session = lks; - - DBG("Kernel session created (fd: %d)", lks->fd); - - return 0; - -error: - return ret; -} - -/* - * Create a kernel channel, register it to the kernel tracer and add it to the - * kernel session. - */ -int kernel_create_channel(struct ltt_kernel_session *session, - struct lttng_channel *chan, char *path) -{ - int ret; - struct ltt_kernel_channel *lkc; - - /* Allocate kernel channel */ - lkc = trace_kernel_create_channel(chan, path); - if (lkc == NULL) { - goto error; - } - - /* Kernel tracer channel creation */ - ret = kernctl_create_channel(session->fd, &lkc->channel->attr); - if (ret < 0) { - perror("ioctl kernel create channel"); - goto error; - } - - /* Setup the channel fd */ - lkc->fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(lkc->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - /* Add channel to session */ - cds_list_add(&lkc->list, &session->channel_list.head); - session->channel_count++; - - DBG("Kernel channel %s created (fd: %d and path: %s)", - lkc->channel->name, lkc->fd, lkc->pathname); - - return 0; - -error: - return -1; -} - -/* - * Create a kernel event, enable it to the kernel tracer and add it to the - * channel event list of the kernel session. - */ -int kernel_create_event(struct lttng_event *ev, - struct ltt_kernel_channel *channel) -{ - int ret; - struct ltt_kernel_event *event; - - event = trace_kernel_create_event(ev); - if (event == NULL) { - ret = -1; - goto error; - } - - ret = kernctl_create_event(channel->fd, event->event); - if (ret < 0) { - if (errno != EEXIST) { - PERROR("create event ioctl"); - } - ret = -errno; - goto free_event; - } - - /* - * LTTNG_KERNEL_SYSCALL event creation will return 0 on success. However - * this FD must not be added to the event list. - */ - if (ret == 0 && event->event->instrumentation == LTTNG_KERNEL_SYSCALL) { - DBG2("Kernel event syscall creation success"); - goto end; - } - - event->fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(event->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - /* Add event to event list */ - cds_list_add(&event->list, &channel->events_list.head); - channel->event_count++; - - DBG("Event %s created (fd: %d)", ev->name, event->fd); - -end: - return 0; - -free_event: - free(event); -error: - return ret; -} - -/* - * Disable a kernel channel. - */ -int kernel_disable_channel(struct ltt_kernel_channel *chan) -{ - int ret; - - ret = kernctl_disable(chan->fd); - if (ret < 0) { - perror("disable chan ioctl"); - ret = errno; - goto error; - } - - chan->enabled = 0; - DBG("Kernel channel %s disabled (fd: %d)", chan->channel->name, chan->fd); - - return 0; - -error: - return ret; -} - -/* - * Enable a kernel channel. - */ -int kernel_enable_channel(struct ltt_kernel_channel *chan) -{ - int ret; - - ret = kernctl_enable(chan->fd); - if (ret < 0 && errno != EEXIST) { - perror("Enable kernel chan"); - goto error; - } - - chan->enabled = 1; - DBG("Kernel channel %s enabled (fd: %d)", chan->channel->name, chan->fd); - - return 0; - -error: - return ret; -} - -/* - * Enable a kernel event. - */ -int kernel_enable_event(struct ltt_kernel_event *event) -{ - int ret; - - ret = kernctl_enable(event->fd); - if (ret < 0 && errno != EEXIST) { - perror("enable kernel event"); - goto error; - } - - event->enabled = 1; - DBG("Kernel event %s enabled (fd: %d)", event->event->name, event->fd); - - return 0; - -error: - return ret; -} - -/* - * Disable a kernel event. - */ -int kernel_disable_event(struct ltt_kernel_event *event) -{ - int ret; - - ret = kernctl_disable(event->fd); - if (ret < 0 && errno != EEXIST) { - perror("disable kernel event"); - goto error; - } - - event->enabled = 0; - DBG("Kernel event %s disabled (fd: %d)", event->event->name, event->fd); - - return 0; - -error: - return ret; -} - -/* - * Create kernel metadata, open from the kernel tracer and add it to the - * kernel session. - */ -int kernel_open_metadata(struct ltt_kernel_session *session, char *path) -{ - int ret; - struct ltt_kernel_metadata *lkm; - - /* Allocate kernel metadata */ - lkm = trace_kernel_create_metadata(path); - if (lkm == NULL) { - goto error; - } - - /* Kernel tracer metadata creation */ - ret = kernctl_open_metadata(session->fd, &lkm->conf->attr); - if (ret < 0) { - goto error; - } - - lkm->fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(lkm->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - session->metadata = lkm; - - DBG("Kernel metadata opened (fd: %d and path: %s)", lkm->fd, lkm->pathname); - - return 0; - -error: - return -1; -} - -/* - * Start tracing session. - */ -int kernel_start_session(struct ltt_kernel_session *session) -{ - int ret; - - ret = kernctl_start_session(session->fd); - if (ret < 0) { - perror("ioctl start session"); - goto error; - } - - DBG("Kernel session started"); - - return 0; - -error: - return ret; -} - -/* - * Make a kernel wait to make sure in-flight probe have completed. - */ -void kernel_wait_quiescent(int fd) -{ - int ret; - - DBG("Kernel quiescent wait on %d", fd); - - ret = kernctl_wait_quiescent(fd); - if (ret < 0) { - perror("wait quiescent ioctl"); - ERR("Kernel quiescent wait failed"); - } -} - -/* - * Kernel calibrate - */ -int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate) -{ - int ret; - - ret = kernctl_calibrate(fd, calibrate); - if (ret < 0) { - perror("calibrate ioctl"); - return -1; - } - - return 0; -} - - -/* - * Force flush buffer of metadata. - */ -int kernel_metadata_flush_buffer(int fd) -{ - int ret; - - ret = kernctl_buffer_flush(fd); - if (ret < 0) { - ERR("Fail to flush metadata buffers %d (ret: %d", fd, ret); - } - - return 0; -} - -/* - * Force flush buffer for channel. - */ -int kernel_flush_buffer(struct ltt_kernel_channel *channel) -{ - int ret; - struct ltt_kernel_stream *stream; - - DBG("Flush buffer for channel %s", channel->channel->name); - - cds_list_for_each_entry(stream, &channel->stream_list.head, list) { - DBG("Flushing channel stream %d", stream->fd); - ret = kernctl_buffer_flush(stream->fd); - if (ret < 0) { - perror("ioctl"); - ERR("Fail to flush buffer for stream %d (ret: %d)", - stream->fd, ret); - } - } - - return 0; -} - -/* - * Stop tracing session. - */ -int kernel_stop_session(struct ltt_kernel_session *session) -{ - int ret; - - ret = kernctl_stop_session(session->fd); - if (ret < 0) { - goto error; - } - - DBG("Kernel session stopped"); - - return 0; - -error: - return ret; -} - -/* - * Open stream of channel, register it to the kernel tracer and add it - * to the stream list of the channel. - * - * Return the number of created stream. Else, a negative value. - */ -int kernel_open_channel_stream(struct ltt_kernel_channel *channel) -{ - int ret; - struct ltt_kernel_stream *lks; - - while ((ret = kernctl_create_stream(channel->fd)) > 0) { - lks = trace_kernel_create_stream(); - if (lks == NULL) { - close(ret); - goto error; - } - - lks->fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(lks->fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - ret = asprintf(&lks->pathname, "%s/%s_%d", - channel->pathname, channel->channel->name, channel->stream_count); - if (ret < 0) { - perror("asprintf kernel create stream"); - goto error; - } - - /* Add stream to channe stream list */ - cds_list_add(&lks->list, &channel->stream_list.head); - channel->stream_count++; - - DBG("Kernel stream %d created (fd: %d, state: %d, path: %s)", - channel->stream_count, lks->fd, lks->state, lks->pathname); - } - - return channel->stream_count; - -error: - return -1; -} - -/* - * Open the metadata stream and set it to the kernel session. - */ -int kernel_open_metadata_stream(struct ltt_kernel_session *session) -{ - int ret; - - ret = kernctl_create_stream(session->metadata->fd); - if (ret < 0) { - perror("kernel create metadata stream"); - goto error; - } - - DBG("Kernel metadata stream created (fd: %d)", ret); - session->metadata_stream_fd = ret; - /* Prevent fd duplication after execlp() */ - ret = fcntl(session->metadata_stream_fd, F_SETFD, FD_CLOEXEC); - if (ret < 0) { - perror("fcntl session fd"); - } - - return 0; - -error: - return -1; -} - -/* - * Get the event list from the kernel tracer and return the number of elements. - */ -ssize_t kernel_list_events(int tracer_fd, struct lttng_event **events) -{ - int fd, pos; - char *event; - size_t nbmem, count = 0; - ssize_t size; - FILE *fp; - struct lttng_event *elist; - - fd = kernctl_tracepoint_list(tracer_fd); - if (fd < 0) { - perror("kernel tracepoint list"); - goto error; - } - - fp = fdopen(fd, "r"); - if (fp == NULL) { - perror("kernel tracepoint list fdopen"); - goto error_fp; - } - - /* - * Init memory size counter - * See kernel-ctl.h for explanation of this value - */ - nbmem = KERNEL_EVENT_LIST_SIZE; - elist = malloc(sizeof(struct lttng_event) * nbmem); - - while ((size = fscanf(fp, "event { name = %m[^;]; };%n\n", &event, &pos)) == 1) { - if (count > nbmem) { - DBG("Reallocating event list from %zu to %zu bytes", nbmem, - nbmem + KERNEL_EVENT_LIST_SIZE); - /* Adding the default size again */ - nbmem += KERNEL_EVENT_LIST_SIZE; - elist = realloc(elist, nbmem); - if (elist == NULL) { - perror("realloc list events"); - count = -ENOMEM; - goto end; - } - } - strncpy(elist[count].name, event, LTTNG_SYMBOL_NAME_LEN); - elist[count].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - count++; - } - - *events = elist; - DBG("Kernel list events done (%zu events)", count); -end: - fclose(fp); /* closes both fp and fd */ - return count; - -error_fp: - close(fd); -error: - return -1; -} diff --git a/ltt-sessiond/kernel-ctl.h b/ltt-sessiond/kernel-ctl.h deleted file mode 100644 index 2fbaca91e..000000000 --- a/ltt-sessiond/kernel-ctl.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_KERNEL_CTL_H -#define _LTT_KERNEL_CTL_H - -#include "session.h" -#include "trace-kernel.h" - -/* - * Default size for the event list when kernel_list_events is called. This size - * value is based on the initial LTTng 2.0 version set of tracepoints. - * - * This is NOT an upper bound because if the real event list size is bigger, - * dynamic reallocation is performed. - */ -#define KERNEL_EVENT_LIST_SIZE 80 - -int kernel_add_channel_context(struct ltt_kernel_channel *chan, - struct lttng_kernel_context *ctx); -int kernel_add_event_context(struct ltt_kernel_event *event, - struct lttng_kernel_context *ctx); -int kernel_create_session(struct ltt_session *session, int tracer_fd); -int kernel_create_channel(struct ltt_kernel_session *session, - struct lttng_channel *chan, char *path); -int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel); -int kernel_disable_channel(struct ltt_kernel_channel *chan); -int kernel_disable_event(struct ltt_kernel_event *event); -int kernel_enable_event(struct ltt_kernel_event *event); -int kernel_enable_channel(struct ltt_kernel_channel *chan); -int kernel_open_metadata(struct ltt_kernel_session *session, char *path); -int kernel_open_metadata_stream(struct ltt_kernel_session *session); -int kernel_open_channel_stream(struct ltt_kernel_channel *channel); -int kernel_flush_buffer(struct ltt_kernel_channel *channel); -int kernel_metadata_flush_buffer(int fd); -int kernel_start_session(struct ltt_kernel_session *session); -int kernel_stop_session(struct ltt_kernel_session *session); -ssize_t kernel_list_events(int tracer_fd, struct lttng_event **event_list); -void kernel_wait_quiescent(int fd); -int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate); - -#endif /* _LTT_KERNEL_CTL_H */ diff --git a/ltt-sessiond/ltt-sessiond.h b/ltt-sessiond/ltt-sessiond.h deleted file mode 100644 index 63de2e120..000000000 --- a/ltt-sessiond/ltt-sessiond.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_SESSIOND_H -#define _LTT_SESSIOND_H - -#define _LGPL_SOURCE -#include -#include - -#include "ust-app.h" - -#define DEFAULT_HOME_DIR "/tmp" -#define DEFAULT_UST_SOCK_DIR DEFAULT_HOME_DIR "/ust-app-socks" -#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" - -struct module_param { - const char *name; - int required; -}; - -/* LTTng kernel tracer modules list */ -const struct module_param kernel_modules_list[] = { - { "lttng-ftrace", 0 }, - { "lttng-kprobes", 0 }, - { "lttng-kretprobes", 0 }, - { "lib-ring-buffer", 1 }, - { "ltt-relay", 1 }, - { "ltt-ring-buffer-client-discard", 1 }, - { "ltt-ring-buffer-client-overwrite", 1 }, - { "ltt-ring-buffer-metadata-client", 1 }, - { "ltt-ring-buffer-client-mmap-discard", 1 }, - { "ltt-ring-buffer-client-mmap-overwrite", 1 }, - { "ltt-ring-buffer-metadata-mmap-client", 1 }, - { "lttng-probe-lttng", 1 }, - { "lttng-types", 0 }, - { "lttng-probe-block", 0 }, - { "lttng-probe-irq", 0 }, - { "lttng-probe-kvm", 0 }, - { "lttng-probe-sched", 0 }, -}; - -extern const char default_home_dir[], - default_tracing_group[], - default_ust_sock_dir[], - default_global_apps_pipe[]; - -/* - * This contains extra data needed for processing a command received by the - * session daemon from the lttng client. - */ -struct command_ctx { - int ust_sock; - unsigned int lttng_msg_size; - struct ltt_session *session; - struct lttcomm_lttng_msg *llm; - struct lttcomm_session_msg *lsm; -}; - -struct ust_command { - int sock; - struct ust_register_msg reg_msg; - struct cds_wfq_node node; -}; - -/* - * Queue used to enqueue UST registration request (ust_commant) and protected - * by a futex with a scheme N wakers / 1 waiters. See futex.c/.h - */ -struct ust_cmd_queue { - int32_t futex; - struct cds_wfq_queue queue; -}; - -#endif /* _LTT_SESSIOND_H */ diff --git a/ltt-sessiond/lttng-ust-abi.h b/ltt-sessiond/lttng-ust-abi.h deleted file mode 100644 index 5b025aeac..000000000 --- a/ltt-sessiond/lttng-ust-abi.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef _LTTNG_UST_ABI_H -#define _LTTNG_UST_ABI_H - -/* - * lttng-ust-abi.h - * - * Copyright 2010-2011 (c) - Mathieu Desnoyers - * - * LTTng-UST ABI header - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include - -#define LTTNG_UST_SYM_NAME_LEN 128 - -#define LTTNG_UST_COMM_VERSION_MAJOR 0 -#define LTTNG_UST_COMM_VERSION_MINOR 1 - -enum lttng_ust_instrumentation { - LTTNG_UST_TRACEPOINT = 0, - LTTNG_UST_PROBE = 1, - LTTNG_UST_FUNCTION = 2, -}; - -enum lttng_ust_output { - LTTNG_UST_MMAP = 0, -}; - -struct lttng_ust_tracer_version { - uint32_t version; - uint32_t patchlevel; - uint32_t sublevel; -}; - -struct lttng_ust_channel { - int overwrite; /* 1: overwrite, 0: discard */ - uint64_t subbuf_size; /* in bytes */ - uint64_t num_subbuf; - unsigned int switch_timer_interval; /* usecs */ - unsigned int read_timer_interval; /* usecs */ - enum lttng_ust_output output; /* output mode */ - /* The following fields are used internally within UST. */ - int shm_fd; - int wait_fd; - uint64_t memory_map_size; -}; - -/* - * This structure is only used internally within UST. It is not per-se - * part of the communication between sessiond and UST. - */ -struct lttng_ust_stream { - int shm_fd; - int wait_fd; - uint64_t memory_map_size; -}; - -struct lttng_ust_event { - char name[LTTNG_UST_SYM_NAME_LEN]; /* event name */ - enum lttng_ust_instrumentation instrumentation; - /* Per instrumentation type configuration */ - union { - } u; -}; - -enum lttng_ust_context_type { - LTTNG_UST_CONTEXT_VTID = 0, - LTTNG_UST_CONTEXT_VPID = 1, - LTTNG_UST_CONTEXT_PTHREAD_ID = 2, - LTTNG_UST_CONTEXT_PROCNAME = 3, -}; - -struct lttng_ust_context { - enum lttng_ust_context_type ctx; - union { - } u; -}; - -/* - * Tracer channel attributes. - */ -struct lttng_ust_channel_attr { - int overwrite; /* 1: overwrite, 0: discard */ - uint64_t subbuf_size; /* bytes */ - uint64_t num_subbuf; /* power of 2 */ - unsigned int switch_timer_interval; /* usec */ - unsigned int read_timer_interval; /* usec */ - enum lttng_ust_output output; /* splice, mmap */ -}; - -struct lttng_ust_object_data { - int handle; - int shm_fd; - int wait_fd; - uint64_t memory_map_size; -}; - -#define _UST_CMD(minor) (minor) -#define _UST_CMDR(minor, type) (minor) -#define _UST_CMDW(minor, type) (minor) - -/* Handled by object descriptor */ -#define LTTNG_UST_RELEASE _UST_CMD(0x1) - -/* Handled by object cmd */ - -/* LTTng-UST commands */ -#define LTTNG_UST_SESSION _UST_CMD(0x40) -#define LTTNG_UST_TRACER_VERSION \ - _UST_CMDR(0x41, struct lttng_ust_tracer_version) -#define LTTNG_UST_TRACEPOINT_LIST _UST_CMD(0x42) -#define LTTNG_UST_WAIT_QUIESCENT _UST_CMD(0x43) -#define LTTNG_UST_REGISTER_DONE _UST_CMD(0x44) - -/* Session FD commands */ -#define LTTNG_UST_METADATA \ - _UST_CMDW(0x50, struct lttng_ust_channel) -#define LTTNG_UST_CHANNEL \ - _UST_CMDW(0x51, struct lttng_ust_channel) -#define LTTNG_UST_SESSION_START _UST_CMD(0x52) -#define LTTNG_UST_SESSION_STOP _UST_CMD(0x53) - -/* Channel FD commands */ -#define LTTNG_UST_STREAM _UST_CMD(0x60) -#define LTTNG_UST_EVENT \ - _UST_CMDW(0x61, struct lttng_ust_event) - -/* Event and Channel FD commands */ -#define LTTNG_UST_CONTEXT \ - _UST_CMDW(0x70, struct lttng_ust_context) -#define LTTNG_UST_FLUSH_BUFFER \ - _UST_CMD(0x71) - -/* Event, Channel and Session commands */ -#define LTTNG_UST_ENABLE _UST_CMD(0x80) -#define LTTNG_UST_DISABLE _UST_CMD(0x81) - -#define LTTNG_UST_ROOT_HANDLE 0 - -struct obj; - -struct objd_ops { - long (*cmd)(int objd, unsigned int cmd, unsigned long arg); - int (*release)(int objd); -}; - -/* Create root handle. Always ID 0. */ -int lttng_abi_create_root_handle(void); - -const struct objd_ops *objd_ops(int id); -int objd_unref(int id); - -void lttng_ust_abi_exit(void); -void ltt_events_exit(void); - -#endif /* _LTTNG_UST_ABI_H */ diff --git a/ltt-sessiond/lttng-ust-ctl.h b/ltt-sessiond/lttng-ust-ctl.h deleted file mode 100644 index de5eeafee..000000000 --- a/ltt-sessiond/lttng-ust-ctl.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTTNG_UST_CTL_H -#define _LTTNG_UST_CTL_H - -#include - -int ustctl_register_done(int sock); -int ustctl_create_session(int sock); -int ustctl_open_metadata(int sock, int session_handle, - struct lttng_ust_channel_attr *chops, - struct lttng_ust_object_data **metadata_data); -int ustctl_create_channel(int sock, int session_handle, - struct lttng_ust_channel_attr *chops, - struct lttng_ust_object_data **channel_data); -int ustctl_create_stream(int sock, struct lttng_ust_object_data *channel_data, - struct lttng_ust_object_data **stream_data); -int ustctl_create_event(int sock, struct lttng_ust_event *ev, - struct lttng_ust_object_data *channel_data, - struct lttng_ust_object_data **event_data); -int ustctl_add_context(int sock, struct lttng_ust_context *ctx, - struct lttng_ust_object_data *obj_data, - struct lttng_ust_object_data **context_data); - -int ustctl_enable(int sock, struct lttng_ust_object_data *object); -int ustctl_disable(int sock, struct lttng_ust_object_data *object); -int ustctl_start_session(int sock, int handle); -int ustctl_stop_session(int sock, int handle); - -int ustctl_tracepoint_list(int sock); /* not implemented yet */ -int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v); -int ustctl_wait_quiescent(int sock); - -/* Flush each buffers in this channel */ -int ustctl_flush_buffer(int sock, struct lttng_ust_object_data *channel_data); - -/* not implemented yet */ -struct lttng_ust_calibrate; -int ustctl_calibrate(int sock, struct lttng_ust_calibrate *calibrate); - -/* - * Map channel lttng_ust_shm_handle and add streams. Typically performed by the - * consumer to map the objects into its memory space. - */ -struct lttng_ust_shm_handle *ustctl_map_channel(struct lttng_ust_object_data *chan_data); -int ustctl_add_stream(struct lttng_ust_shm_handle *lttng_ust_shm_handle, - struct lttng_ust_object_data *stream_data); -/* - * Note: the lttng_ust_object_data from which the lttng_ust_shm_handle is derived can only - * be released after unmapping the handle. - */ -void ustctl_unmap_channel(struct lttng_ust_shm_handle *lttng_ust_shm_handle); - -/* Buffer operations */ - -struct lttng_ust_shm_handle; -struct lttng_ust_lib_ring_buffer; - -/* Open/close stream buffers for read */ -struct lttng_ust_lib_ring_buffer *ustctl_open_stream_read(struct lttng_ust_shm_handle *handle, - int cpu); -void ustctl_close_stream_read(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); - -/* For mmap mode, readable without "get" operation */ -int ustctl_get_mmap_len(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, - unsigned long *len); -int ustctl_get_max_subbuf_size(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, - unsigned long *len); - -/* - * For mmap mode, operate on the current packet (between get/put or - * get_next/put_next). - */ -void *ustctl_get_mmap_base(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); -int ustctl_get_mmap_read_offset(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *off); -int ustctl_get_subbuf_size(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *len); -int ustctl_get_padded_subbuf_size(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *len); -int ustctl_get_next_subbuf(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); -int ustctl_put_next_subbuf(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); - -/* snapshot */ - -int ustctl_snapshot(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); -int ustctl_snapshot_get_consumed(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); -int ustctl_snapshot_get_produced(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); -int ustctl_get_subbuf(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); -int ustctl_put_subbuf(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf); - -/* Release object created by members of this API */ -void release_object(int sock, struct lttng_ust_object_data *data); - -#endif /* _LTTNG_UST_CTL_H */ diff --git a/ltt-sessiond/main.c b/ltt-sessiond/main.c deleted file mode 100644 index 2c2fe44bf..000000000 --- a/ltt-sessiond/main.c +++ /dev/null @@ -1,3913 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "channel.h" -#include "compat/poll.h" -#include "context.h" -#include "event.h" -#include "futex.h" -#include "hashtable.h" -#include "kernel-ctl.h" -#include "ltt-sessiond.h" -#include "shm.h" -#include "ust-app.h" -#include "ust-ctl.h" -#include "utils.h" - -struct consumer_data { - enum lttng_consumer_type type; - - pthread_t thread; /* Worker thread interacting with the consumer */ - sem_t sem; - - /* Mutex to control consumerd pid assignation */ - pthread_mutex_t pid_mutex; - pid_t pid; - - int err_sock; - int cmd_sock; - - /* consumer error and command Unix socket path */ - char err_unix_sock_path[PATH_MAX]; - char cmd_unix_sock_path[PATH_MAX]; -}; - -/* Const values */ -const char default_home_dir[] = DEFAULT_HOME_DIR; -const char default_tracing_group[] = LTTNG_DEFAULT_TRACING_GROUP; -const char default_ust_sock_dir[] = DEFAULT_UST_SOCK_DIR; -const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE; - -/* Variables */ -int opt_verbose; /* Not static for lttngerr.h */ -int opt_verbose_consumer; /* Not static for lttngerr.h */ -int opt_quiet; /* Not static for lttngerr.h */ - -const char *progname; -const char *opt_tracing_group; -static int opt_sig_parent; -static int opt_daemon; -static int is_root; /* Set to 1 if the daemon is running as root */ -static pid_t ppid; /* Parent PID for --sig-parent option */ - -/* Consumer daemon specific control data */ -static struct consumer_data kconsumer_data = { - .type = LTTNG_CONSUMER_KERNEL, -}; -static struct consumer_data ustconsumer_data = { - .type = LTTNG_CONSUMER_UST, -}; - -static int dispatch_thread_exit; - -/* Global application Unix socket path */ -static char apps_unix_sock_path[PATH_MAX]; -/* Global client Unix socket path */ -static char client_unix_sock_path[PATH_MAX]; -/* global wait shm path for UST */ -static char wait_shm_path[PATH_MAX]; - -/* Sockets and FDs */ -static int client_sock; -static int apps_sock; -static int kernel_tracer_fd; -static int kernel_poll_pipe[2]; - -/* - * Quit pipe for all threads. This permits a single cancellation point - * for all threads when receiving an event on the pipe. - */ -static int thread_quit_pipe[2]; - -/* - * This pipe is used to inform the thread managing application communication - * that a command is queued and ready to be processed. - */ -static int apps_cmd_pipe[2]; - -/* Pthread, Mutexes and Semaphores */ -static pthread_t apps_thread; -static pthread_t reg_apps_thread; -static pthread_t client_thread; -static pthread_t kernel_thread; -static pthread_t dispatch_thread; - - -/* - * UST registration command queue. This queue is tied with a futex and uses a N - * wakers / 1 waiter implemented and detailed in futex.c/.h - * - * The thread_manage_apps and thread_dispatch_ust_registration interact with - * this queue and the wait/wake scheme. - */ -static struct ust_cmd_queue ust_cmd_queue; - -/* - * Pointer initialized before thread creation. - * - * This points to the tracing session list containing the session count and a - * mutex lock. The lock MUST be taken if you iterate over the list. The lock - * MUST NOT be taken if you call a public function in session.c. - * - * The lock is nested inside the structure: session_list_ptr->lock. Please use - * session_lock_list and session_unlock_list for lock acquisition. - */ -static struct ltt_session_list *session_list_ptr; - -/* - * 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 ret; - - if (events == NULL || size == 0) { - ret = -1; - goto error; - } - - ret = lttng_poll_create(events, size, LTTNG_CLOEXEC); - if (ret < 0) { - goto error; - } - - /* Add quit pipe */ - ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN); - if (ret < 0) { - goto error; - } - - return 0; - -error: - return ret; -} - -/* - * Check if the thread quit pipe was triggered. - * - * Return 1 if it was triggered else 0; - */ -static int check_thread_quit_pipe(int fd, uint32_t events) -{ - if (fd == thread_quit_pipe[0] && (events & LPOLLIN)) { - return 1; - } - - return 0; -} - -/* - * Remove modules in reverse load order. - */ -static int modprobe_remove_kernel_modules(void) -{ - int ret = 0, i; - char modprobe[256]; - - for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) { - ret = snprintf(modprobe, sizeof(modprobe), - "/sbin/modprobe -r -q %s", - kernel_modules_list[i].name); - if (ret < 0) { - perror("snprintf modprobe -r"); - goto error; - } - modprobe[sizeof(modprobe) - 1] = '\0'; - ret = system(modprobe); - if (ret == -1) { - ERR("Unable to launch modprobe -r for module %s", - kernel_modules_list[i].name); - } else if (kernel_modules_list[i].required - && WEXITSTATUS(ret) != 0) { - ERR("Unable to remove module %s", - kernel_modules_list[i].name); - } else { - DBG("Modprobe removal successful %s", - kernel_modules_list[i].name); - } - } - -error: - return ret; -} - -/* - * Return group ID of the tracing group or -1 if not found. - */ -static gid_t allowed_group(void) -{ - struct group *grp; - - if (opt_tracing_group) { - grp = getgrnam(opt_tracing_group); - } else { - grp = getgrnam(default_tracing_group); - } - if (!grp) { - return -1; - } else { - return grp->gr_gid; - } -} - -/* - * Init thread quit pipe. - * - * Return -1 on error or 0 if all pipes are created. - */ -static int init_thread_quit_pipe(void) -{ - int ret; - - ret = pipe2(thread_quit_pipe, O_CLOEXEC); - if (ret < 0) { - perror("thread quit pipe"); - goto error; - } - -error: - return ret; -} - -/* - * Complete teardown of a kernel session. This free all data structure related - * to a kernel session and update counter. - */ -static void teardown_kernel_session(struct ltt_session *session) -{ - if (session->kernel_session != NULL) { - DBG("Tearing down kernel session"); - - /* - * If a custom kernel consumer was registered, close the socket before - * tearing down the complete kernel session structure - */ - if (session->kernel_session->consumer_fd != kconsumer_data.cmd_sock) { - lttcomm_close_unix_sock(session->kernel_session->consumer_fd); - } - - trace_kernel_destroy_session(session->kernel_session); - /* Extra precaution */ - session->kernel_session = NULL; - } -} - -/* - * Complete teardown of all UST sessions. This will free everything on his path - * and destroy the core essence of all ust sessions :) - */ -static void teardown_ust_session(struct ltt_session *session) -{ - DBG("Tearing down UST session(s)"); - - trace_ust_destroy_session(session->ust_session); -} - -/* - * Stop all threads by closing the thread quit pipe. - */ -static void stop_threads(void) -{ - int ret; - - /* Stopping all threads */ - DBG("Terminating all threads"); - ret = notify_thread_pipe(thread_quit_pipe[1]); - if (ret < 0) { - ERR("write error on thread quit pipe"); - } - - /* Dispatch thread */ - dispatch_thread_exit = 1; - futex_nto1_wake(&ust_cmd_queue.futex); -} - -/* - * Cleanup the daemon - */ -static void cleanup(void) -{ - int ret; - char *cmd; - struct ltt_session *sess, *stmp; - - DBG("Cleaning up"); - - /* */ - MSG("%c[%d;%dm*** assert failed *** ==> %c[%dm%c[%d;%dm" - "Matthew, BEET driven development works!%c[%dm", - 27, 1, 31, 27, 0, 27, 1, 33, 27, 0); - /* */ - - if (is_root) { - DBG("Removing %s directory", LTTNG_RUNDIR); - ret = asprintf(&cmd, "rm -rf " LTTNG_RUNDIR); - if (ret < 0) { - ERR("asprintf failed. Something is really wrong!"); - } - - /* Remove lttng run directory */ - ret = system(cmd); - if (ret < 0) { - ERR("Unable to clean " LTTNG_RUNDIR); - } - } - - DBG("Cleaning up all session"); - - /* Destroy session list mutex */ - if (session_list_ptr != NULL) { - pthread_mutex_destroy(&session_list_ptr->lock); - - /* Cleanup ALL session */ - cds_list_for_each_entry_safe(sess, stmp, - &session_list_ptr->head, list) { - teardown_kernel_session(sess); - teardown_ust_session(sess); - free(sess); - } - } - - DBG("Closing all UST sockets"); - ust_app_clean_list(); - - pthread_mutex_destroy(&kconsumer_data.pid_mutex); - - DBG("Closing kernel fd"); - close(kernel_tracer_fd); - - if (is_root) { - DBG("Unloading kernel modules"); - modprobe_remove_kernel_modules(); - } - - close(thread_quit_pipe[0]); - close(thread_quit_pipe[1]); -} - -/* - * Send data on a unix socket using the liblttsessiondcomm API. - * - * Return lttcomm error code. - */ -static int send_unix_sock(int sock, void *buf, size_t len) -{ - /* Check valid length */ - if (len <= 0) { - return -1; - } - - return lttcomm_send_unix_sock(sock, buf, len); -} - -/* - * Free memory of a command context structure. - */ -static void clean_command_ctx(struct command_ctx **cmd_ctx) -{ - DBG("Clean command context structure"); - if (*cmd_ctx) { - if ((*cmd_ctx)->llm) { - free((*cmd_ctx)->llm); - } - if ((*cmd_ctx)->lsm) { - free((*cmd_ctx)->lsm); - } - free(*cmd_ctx); - *cmd_ctx = NULL; - } -} - -/* - * Send all stream fds of kernel channel to the consumer. - */ -static int send_kconsumer_channel_streams(struct consumer_data *consumer_data, - int sock, struct ltt_kernel_channel *channel) -{ - int ret; - struct ltt_kernel_stream *stream; - struct lttcomm_consumer_msg lkm; - - DBG("Sending streams of channel %s to kernel consumer", - channel->channel->name); - - /* Send channel */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - lkm.u.channel.channel_key = channel->fd; - lkm.u.channel.max_sb_size = channel->channel->attr.subbuf_size; - lkm.u.channel.mmap_len = 0; /* for kernel */ - DBG("Sending channel %d to consumer", lkm.u.channel.channel_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - - /* Send streams */ - cds_list_for_each_entry(stream, &channel->stream_list.head, list) { - if (!stream->fd) { - continue; - } - lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lkm.u.stream.channel_key = channel->fd; - lkm.u.stream.stream_key = stream->fd; - lkm.u.stream.state = stream->state; - lkm.u.stream.output = channel->channel->attr.output; - lkm.u.stream.mmap_len = 0; /* for kernel */ - strncpy(lkm.u.stream.path_name, stream->pathname, PATH_MAX - 1); - lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending stream %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - ret = lttcomm_send_fds_unix_sock(sock, &stream->fd, 1); - if (ret < 0) { - perror("send consumer stream ancillary data"); - goto error; - } - } - - DBG("consumer channel streams sent"); - - return 0; - -error: - return ret; -} - -/* - * Send all stream fds of the kernel session to the consumer. - */ -static int send_kconsumer_session_streams(struct consumer_data *consumer_data, - struct ltt_kernel_session *session) -{ - int ret; - struct ltt_kernel_channel *chan; - struct lttcomm_consumer_msg lkm; - int sock = session->consumer_fd; - - DBG("Sending metadata stream fd"); - - /* Extra protection. It's NOT supposed to be set to 0 at this point */ - if (session->consumer_fd == 0) { - session->consumer_fd = consumer_data->cmd_sock; - } - - if (session->metadata_stream_fd != 0) { - /* Send metadata channel fd */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - lkm.u.channel.channel_key = session->metadata->fd; - lkm.u.channel.max_sb_size = session->metadata->conf->attr.subbuf_size; - lkm.u.channel.mmap_len = 0; /* for kernel */ - DBG("Sending metadata channel %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - - /* Send metadata stream fd */ - lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lkm.u.stream.channel_key = session->metadata->fd; - lkm.u.stream.stream_key = session->metadata_stream_fd; - lkm.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; - lkm.u.stream.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; - lkm.u.stream.mmap_len = 0; /* for kernel */ - strncpy(lkm.u.stream.path_name, session->metadata->pathname, PATH_MAX - 1); - lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending metadata stream %d to consumer", lkm.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - ret = lttcomm_send_fds_unix_sock(sock, &session->metadata_stream_fd, 1); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - } - - cds_list_for_each_entry(chan, &session->channel_list.head, list) { - ret = send_kconsumer_channel_streams(consumer_data, sock, chan); - if (ret < 0) { - goto error; - } - } - - DBG("consumer fds (metadata and channel streams) sent"); - - return 0; - -error: - return ret; -} - -/* - * Notify UST applications using the shm mmap futex. - */ -static int notify_ust_apps(int active) -{ - char *wait_shm_mmap; - - DBG("Notifying applications of session daemon state: %d", active); - - /* See shm.c for this call implying mmap, shm and futex calls */ - wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root); - if (wait_shm_mmap == NULL) { - goto error; - } - - /* Wake waiting process */ - futex_wait_update((int32_t *) wait_shm_mmap, active); - - /* Apps notified successfully */ - return 0; - -error: - return -1; -} - -/* - * Setup the outgoing data buffer for the response (llm) by allocating the - * right amount of memory and copying the original information from the lsm - * structure. - * - * Return total size of the buffer pointed by buf. - */ -static int setup_lttng_msg(struct command_ctx *cmd_ctx, size_t size) -{ - int ret, buf_size; - - buf_size = size; - - cmd_ctx->llm = malloc(sizeof(struct lttcomm_lttng_msg) + buf_size); - if (cmd_ctx->llm == NULL) { - perror("malloc"); - ret = -ENOMEM; - goto error; - } - - /* Copy common data */ - cmd_ctx->llm->cmd_type = cmd_ctx->lsm->cmd_type; - cmd_ctx->llm->pid = cmd_ctx->lsm->domain.attr.pid; - - cmd_ctx->llm->data_size = size; - cmd_ctx->lttng_msg_size = sizeof(struct lttcomm_lttng_msg) + buf_size; - - return buf_size; - -error: - return ret; -} - -/* - * Update the kernel poll set of all channel fd available over all tracing - * session. Add the wakeup pipe at the end of the set. - */ -static int update_kernel_poll(struct lttng_poll_event *events) -{ - int ret; - struct ltt_session *session; - struct ltt_kernel_channel *channel; - - DBG("Updating kernel poll set"); - - session_lock_list(); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - session_lock(session); - if (session->kernel_session == NULL) { - session_unlock(session); - continue; - } - - cds_list_for_each_entry(channel, - &session->kernel_session->channel_list.head, list) { - /* Add channel fd to the kernel poll set */ - ret = lttng_poll_add(events, channel->fd, LPOLLIN | LPOLLRDNORM); - if (ret < 0) { - session_unlock(session); - goto error; - } - DBG("Channel fd %d added to kernel set", channel->fd); - } - session_unlock(session); - } - session_unlock_list(); - - return 0; - -error: - session_unlock_list(); - return -1; -} - -/* - * Find the channel fd from 'fd' over all tracing session. When found, check - * for new channel stream and send those stream fds to the kernel consumer. - * - * Useful for CPU hotplug feature. - */ -static int update_kernel_stream(struct consumer_data *consumer_data, int fd) -{ - int ret = 0; - struct ltt_session *session; - struct ltt_kernel_channel *channel; - - DBG("Updating kernel streams for channel fd %d", fd); - - session_lock_list(); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - session_lock(session); - if (session->kernel_session == NULL) { - session_unlock(session); - continue; - } - - /* This is not suppose to be 0 but this is an extra security check */ - if (session->kernel_session->consumer_fd == 0) { - session->kernel_session->consumer_fd = consumer_data->cmd_sock; - } - - cds_list_for_each_entry(channel, - &session->kernel_session->channel_list.head, list) { - if (channel->fd == fd) { - DBG("Channel found, updating kernel streams"); - ret = kernel_open_channel_stream(channel); - if (ret < 0) { - goto error; - } - - /* - * Have we already sent fds to the consumer? If yes, it means - * that tracing is started so it is safe to send our updated - * stream fds. - */ - if (session->kernel_session->consumer_fds_sent == 1) { - ret = send_kconsumer_channel_streams(consumer_data, - session->kernel_session->consumer_fd, channel); - if (ret < 0) { - goto error; - } - } - goto error; - } - } - session_unlock(session); - } - session_unlock_list(); - return ret; - -error: - session_unlock(session); - session_unlock_list(); - return ret; -} - -/* - * This thread manage event coming from the kernel. - * - * Features supported in this thread: - * -) CPU Hotplug - */ -static void *thread_manage_kernel(void *data) -{ - int ret, i, pollfd, update_poll_flag = 1; - uint32_t revents, nb_fd; - char tmp; - struct lttng_poll_event events; - - DBG("Thread manage kernel started"); - - ret = create_thread_poll_set(&events, 2); - if (ret < 0) { - goto error; - } - - ret = lttng_poll_add(&events, kernel_poll_pipe[0], LPOLLIN); - if (ret < 0) { - goto error; - } - - while (1) { - if (update_poll_flag == 1) { - /* - * Reset number of fd in the poll set. Always 2 since there is the thread - * quit pipe and the kernel pipe. - */ - events.nb_fd = 2; - - ret = update_kernel_poll(&events); - if (ret < 0) { - goto error; - } - update_poll_flag = 0; - } - - nb_fd = LTTNG_POLL_GETNB(&events); - - DBG("Thread kernel polling on %d fds", nb_fd); - - /* Zeroed the poll events */ - lttng_poll_reset(&events); - - /* Poll infinite value of time */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } else if (ret == 0) { - /* Should not happen since timeout is infinite */ - ERR("Return value of poll is 0 with an infinite timeout.\n" - "This should not have happened! Continuing..."); - continue; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Check for data on kernel pipe */ - if (pollfd == kernel_poll_pipe[0] && (revents & LPOLLIN)) { - ret = read(kernel_poll_pipe[0], &tmp, 1); - update_poll_flag = 1; - continue; - } else { - /* - * New CPU detected by the kernel. Adding kernel stream to - * kernel session and updating the kernel consumer - */ - if (revents & LPOLLIN) { - ret = update_kernel_stream(&kconsumer_data, pollfd); - if (ret < 0) { - continue; - } - break; - /* - * TODO: We might want to handle the LPOLLERR | LPOLLHUP - * and unregister kernel stream at this point. - */ - } - } - } - } - -error: - DBG("Kernel thread dying"); - close(kernel_poll_pipe[0]); - close(kernel_poll_pipe[1]); - - lttng_poll_clean(&events); - - return NULL; -} - -/* - * This thread manage the consumer error sent back to the session daemon. - */ -static void *thread_manage_consumer(void *data) -{ - int sock = 0, i, ret, pollfd; - uint32_t revents, nb_fd; - enum lttcomm_return_code code; - struct lttng_poll_event events; - struct consumer_data *consumer_data = data; - - DBG("[thread] Manage consumer started"); - - ret = lttcomm_listen_unix_sock(consumer_data->err_sock); - if (ret < 0) { - goto error; - } - - /* - * 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); - if (ret < 0) { - goto error; - } - - ret = lttng_poll_add(&events, consumer_data->err_sock, LPOLLIN | LPOLLRDHUP); - if (ret < 0) { - goto error; - } - - nb_fd = LTTNG_POLL_GETNB(&events); - - /* Inifinite blocking call, waiting for transmission */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Event on the registration socket */ - if (pollfd == consumer_data->err_sock) { - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("consumer err socket poll error"); - goto error; - } - } - } - - sock = lttcomm_accept_unix_sock(consumer_data->err_sock); - if (sock < 0) { - goto error; - } - - DBG2("Receiving code from consumer err_sock"); - - /* Getting status code from kconsumerd */ - ret = lttcomm_recv_unix_sock(sock, &code, - sizeof(enum lttcomm_return_code)); - if (ret <= 0) { - goto error; - } - - if (code == CONSUMERD_COMMAND_SOCK_READY) { - consumer_data->cmd_sock = - lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); - if (consumer_data->cmd_sock < 0) { - sem_post(&consumer_data->sem); - PERROR("consumer connect"); - goto error; - } - /* Signal condition to tell that the kconsumerd is ready */ - sem_post(&consumer_data->sem); - DBG("consumer command socket ready"); - } else { - ERR("consumer error when waiting for SOCK_READY : %s", - lttcomm_get_readable_code(-code)); - goto error; - } - - /* Remove the kconsumerd error sock since we've established a connexion */ - ret = lttng_poll_del(&events, consumer_data->err_sock); - if (ret < 0) { - goto error; - } - - ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLRDHUP); - if (ret < 0) { - goto error; - } - - /* Update number of fd */ - nb_fd = LTTNG_POLL_GETNB(&events); - - /* Inifinite blocking call, waiting for transmission */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Event on the kconsumerd socket */ - if (pollfd == sock) { - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("consumer err socket second poll error"); - goto error; - } - } - } - - /* Wait for any kconsumerd error */ - ret = lttcomm_recv_unix_sock(sock, &code, - sizeof(enum lttcomm_return_code)); - if (ret <= 0) { - ERR("consumer closed the command socket"); - goto error; - } - - ERR("consumer return code : %s", lttcomm_get_readable_code(-code)); - -error: - DBG("consumer thread dying"); - close(consumer_data->err_sock); - close(consumer_data->cmd_sock); - close(sock); - - unlink(consumer_data->err_unix_sock_path); - unlink(consumer_data->cmd_unix_sock_path); - consumer_data->pid = 0; - - lttng_poll_clean(&events); - - return NULL; -} - -/* - * This thread manage application communication. - */ -static void *thread_manage_apps(void *data) -{ - int i, ret, pollfd; - uint32_t revents, nb_fd; - struct ust_command ust_cmd; - struct lttng_poll_event events; - - DBG("[thread] Manage application started"); - - rcu_register_thread(); - rcu_thread_online(); - - ret = create_thread_poll_set(&events, 2); - if (ret < 0) { - goto error; - } - - ret = lttng_poll_add(&events, apps_cmd_pipe[0], LPOLLIN | LPOLLRDHUP); - if (ret < 0) { - goto error; - } - - while (1) { - /* Zeroed the events structure */ - lttng_poll_reset(&events); - - nb_fd = LTTNG_POLL_GETNB(&events); - - DBG("Apps thread polling on %d fds", nb_fd); - - /* Inifinite blocking call, waiting for transmission */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Inspect the apps cmd pipe */ - if (pollfd == apps_cmd_pipe[0]) { - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("Apps command pipe error"); - goto error; - } else if (revents & LPOLLIN) { - /* Empty pipe */ - ret = read(apps_cmd_pipe[0], &ust_cmd, sizeof(ust_cmd)); - if (ret < 0 || ret < sizeof(ust_cmd)) { - perror("read apps cmd pipe"); - goto error; - } - - /* Register applicaton to the session daemon */ - ret = ust_app_register(&ust_cmd.reg_msg, - ust_cmd.sock); - if (ret < 0) { - /* Only critical ENOMEM error can be returned here */ - goto error; - } - - ret = ustctl_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 just need here to monitor the close of the UST - * socket and poll set monitor those by default. - */ - ret = lttng_poll_add(&events, ust_cmd.sock, 0); - if (ret < 0) { - goto error; - } - - DBG("Apps with sock %d added to poll set", - ust_cmd.sock); - } - break; - } - } else { - /* - * At this point, we know that a registered application made - * the event at poll_wait. - */ - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - /* Removing from the poll set */ - ret = lttng_poll_del(&events, pollfd); - if (ret < 0) { - goto error; - } - - /* Socket closed */ - ust_app_unregister(pollfd); - break; - } - } - } - } - -error: - DBG("Application communication apps dying"); - close(apps_cmd_pipe[0]); - close(apps_cmd_pipe[1]); - - lttng_poll_clean(&events); - - rcu_thread_offline(); - rcu_unregister_thread(); - return NULL; -} - -/* - * Dispatch request from the registration threads to the application - * communication thread. - */ -static void *thread_dispatch_ust_registration(void *data) -{ - int ret; - struct cds_wfq_node *node; - struct ust_command *ust_cmd = NULL; - - DBG("[thread] Dispatch UST command started"); - - while (!dispatch_thread_exit) { - /* Atomically prepare the queue futex */ - futex_nto1_prepare(&ust_cmd_queue.futex); - - do { - /* Dequeue command for registration */ - node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); - if (node == NULL) { - DBG("Woken up but nothing in the UST command queue"); - /* Continue thread execution */ - break; - } - - ust_cmd = caa_container_of(node, struct ust_command, node); - - DBG("Dispatching UST registration pid:%d ppid:%d uid:%d" - " gid:%d sock:%d name:%s (version %d.%d)", - ust_cmd->reg_msg.pid, ust_cmd->reg_msg.ppid, - 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 :) - */ - ret = write(apps_cmd_pipe[1], ust_cmd, - sizeof(struct ust_command)); - if (ret < 0) { - 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; - } - } - free(ust_cmd); - } while (node != NULL); - - /* Futex wait on queue. Blocking call on futex() */ - futex_nto1_wait(&ust_cmd_queue.futex); - } - -error: - DBG("Dispatch thread dying"); - return NULL; -} - -/* - * This thread manage application registration. - */ -static void *thread_registration_apps(void *data) -{ - int sock = 0, i, ret, pollfd; - uint32_t revents, nb_fd; - struct lttng_poll_event events; - /* - * Get allocated in this thread, enqueued to a global queue, dequeued and - * freed in the manage apps thread. - */ - struct ust_command *ust_cmd = NULL; - - DBG("[thread] Manage application registration started"); - - ret = lttcomm_listen_unix_sock(apps_sock); - if (ret < 0) { - goto error; - } - - /* - * 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); - if (ret < 0) { - goto error; - } - - /* Add the application registration socket */ - ret = lttng_poll_add(&events, apps_sock, LPOLLIN | LPOLLRDHUP); - if (ret < 0) { - goto error; - } - - /* Notify all applications to register */ - ret = notify_ust_apps(1); - if (ret < 0) { - ERR("Failed to notify applications or create the wait shared memory.\n" - "Execution continues but there might be problem for already\n" - "running applications that wishes to register."); - } - - while (1) { - DBG("Accepting application registration"); - - nb_fd = LTTNG_POLL_GETNB(&events); - - /* Inifinite blocking call, waiting for transmission */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Event on the registration socket */ - if (pollfd == apps_sock) { - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("Register apps socket poll error"); - goto error; - } else if (revents & LPOLLIN) { - sock = lttcomm_accept_unix_sock(apps_sock); - if (sock < 0) { - goto error; - } - - /* Create UST registration command for enqueuing */ - ust_cmd = malloc(sizeof(struct ust_command)); - if (ust_cmd == NULL) { - perror("ust command malloc"); - goto error; - } - - /* - * Using message-based transmissions to ensure we don't - * have to deal with partially received messages. - */ - 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"); - } - free(ust_cmd); - close(sock); - continue; - } - - ust_cmd->sock = sock; - - DBG("UST registration received with pid:%d ppid:%d uid:%d" - " gid:%d sock:%d name:%s (version %d.%d)", - ust_cmd->reg_msg.pid, ust_cmd->reg_msg.ppid, - 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); - - /* - * Lock free enqueue the registration request. The red pill - * has been taken! This apps will be part of the *system*. - */ - cds_wfq_enqueue(&ust_cmd_queue.queue, &ust_cmd->node); - - /* - * Wake the registration queue futex. Implicit memory - * barrier with the exchange in cds_wfq_enqueue. - */ - futex_nto1_wake(&ust_cmd_queue.futex); - } - } - } - } - -error: - DBG("UST Registration thread dying"); - - /* Notify that the registration thread is gone */ - notify_ust_apps(0); - - close(apps_sock); - close(sock); - unlink(apps_unix_sock_path); - - lttng_poll_clean(&events); - - return NULL; -} - -/* - * Start the thread_manage_consumer. This must be done after a lttng-consumerd - * exec or it will fails. - */ -static int spawn_consumer_thread(struct consumer_data *consumer_data) -{ - int ret; - struct timespec timeout; - - timeout.tv_sec = DEFAULT_SEM_WAIT_TIMEOUT; - timeout.tv_nsec = 0; - - /* Setup semaphore */ - ret = sem_init(&consumer_data->sem, 0, 0); - if (ret < 0) { - PERROR("sem_init consumer semaphore"); - goto error; - } - - ret = pthread_create(&consumer_data->thread, NULL, - thread_manage_consumer, consumer_data); - if (ret != 0) { - PERROR("pthread_create consumer"); - ret = -1; - goto error; - } - - /* Get time for sem_timedwait absolute timeout */ - ret = clock_gettime(CLOCK_REALTIME, &timeout); - if (ret < 0) { - PERROR("clock_gettime spawn consumer"); - /* Infinite wait for the kconsumerd thread to be ready */ - ret = sem_wait(&consumer_data->sem); - } else { - /* Normal timeout if the gettime was successful */ - timeout.tv_sec += DEFAULT_SEM_WAIT_TIMEOUT; - ret = sem_timedwait(&consumer_data->sem, &timeout); - } - - if (ret < 0) { - if (errno == ETIMEDOUT) { - /* - * Call has timed out so we kill the kconsumerd_thread and return - * an error. - */ - ERR("The consumer thread was never ready. Killing it"); - ret = pthread_cancel(consumer_data->thread); - if (ret < 0) { - PERROR("pthread_cancel consumer thread"); - } - } else { - PERROR("semaphore wait failed consumer thread"); - } - goto error; - } - - pthread_mutex_lock(&consumer_data->pid_mutex); - if (consumer_data->pid == 0) { - ERR("Kconsumerd did not start"); - pthread_mutex_unlock(&consumer_data->pid_mutex); - goto error; - } - pthread_mutex_unlock(&consumer_data->pid_mutex); - - return 0; - -error: - return ret; -} - -/* - * Join consumer thread - */ -static int join_consumer_thread(struct consumer_data *consumer_data) -{ - void *status; - int ret; - - if (consumer_data->pid != 0) { - ret = kill(consumer_data->pid, SIGTERM); - if (ret) { - ERR("Error killing consumer daemon"); - return ret; - } - return pthread_join(consumer_data->thread, &status); - } else { - return 0; - } -} - -/* - * Fork and exec a consumer daemon (consumerd). - * - * Return pid if successful else -1. - */ -static pid_t spawn_consumerd(struct consumer_data *consumer_data) -{ - int ret; - pid_t pid; - const char *verbosity; - - DBG("Spawning consumerd"); - - pid = fork(); - if (pid == 0) { - /* - * Exec consumerd. - */ - if (opt_verbose > 1 || opt_verbose_consumer) { - verbosity = "--verbose"; - } else { - verbosity = "--quiet"; - } - switch (consumer_data->type) { - case LTTNG_CONSUMER_KERNEL: - execl(INSTALL_BIN_PATH "/lttng-consumerd", - "lttng-consumerd", verbosity, "-k", NULL); - break; - case LTTNG_CONSUMER_UST: - execl(INSTALL_BIN_PATH "/lttng-consumerd", - "lttng-consumerd", verbosity, "-u", NULL); - break; - default: - perror("unknown consumer type"); - exit(EXIT_FAILURE); - } - if (errno != 0) { - perror("kernel start consumer exec"); - } - exit(EXIT_FAILURE); - } else if (pid > 0) { - ret = pid; - } else { - perror("start consumer fork"); - ret = -errno; - } - return ret; -} - -/* - * Spawn the consumerd daemon and session daemon thread. - */ -static int start_consumerd(struct consumer_data *consumer_data) -{ - int ret; - - pthread_mutex_lock(&consumer_data->pid_mutex); - if (consumer_data->pid != 0) { - pthread_mutex_unlock(&consumer_data->pid_mutex); - goto end; - } - - ret = spawn_consumerd(consumer_data); - if (ret < 0) { - ERR("Spawning consumerd failed"); - pthread_mutex_unlock(&consumer_data->pid_mutex); - goto error; - } - - /* Setting up the consumer_data pid */ - consumer_data->pid = ret; - DBG2("Consumer pid %d", consumer_data->pid); - pthread_mutex_unlock(&consumer_data->pid_mutex); - - DBG2("Spawning consumer control thread"); - ret = spawn_consumer_thread(consumer_data); - if (ret < 0) { - ERR("Fatal error spawning consumer control thread"); - goto error; - } - -end: - return 0; - -error: - return ret; -} - -/* - * modprobe_kernel_modules - */ -static int modprobe_kernel_modules(void) -{ - int ret = 0, i; - char modprobe[256]; - - for (i = 0; i < ARRAY_SIZE(kernel_modules_list); i++) { - ret = snprintf(modprobe, sizeof(modprobe), - "/sbin/modprobe %s%s", - kernel_modules_list[i].required ? "" : "-q ", - kernel_modules_list[i].name); - if (ret < 0) { - perror("snprintf modprobe"); - goto error; - } - modprobe[sizeof(modprobe) - 1] = '\0'; - ret = system(modprobe); - if (ret == -1) { - ERR("Unable to launch modprobe for module %s", - kernel_modules_list[i].name); - } else if (kernel_modules_list[i].required - && WEXITSTATUS(ret) != 0) { - ERR("Unable to load module %s", - kernel_modules_list[i].name); - } else { - DBG("Modprobe successfully %s", - kernel_modules_list[i].name); - } - } - -error: - return ret; -} - -/* - * mount_debugfs - */ -static int mount_debugfs(char *path) -{ - int ret; - char *type = "debugfs"; - - ret = mkdir_recursive(path, S_IRWXU | S_IRWXG, geteuid(), getegid()); - if (ret < 0) { - PERROR("Cannot create debugfs path"); - goto error; - } - - ret = mount(type, path, type, 0, NULL); - if (ret < 0) { - PERROR("Cannot mount debugfs"); - goto error; - } - - DBG("Mounted debugfs successfully at %s", path); - -error: - return ret; -} - -/* - * Setup necessary data for kernel tracer action. - */ -static void init_kernel_tracer(void) -{ - int ret; - char *proc_mounts = "/proc/mounts"; - char line[256]; - char *debugfs_path = NULL, *lttng_path = NULL; - FILE *fp; - - /* Detect debugfs */ - fp = fopen(proc_mounts, "r"); - if (fp == NULL) { - ERR("Unable to probe %s", proc_mounts); - goto error; - } - - while (fgets(line, sizeof(line), fp) != NULL) { - if (strstr(line, "debugfs") != NULL) { - /* Remove first string */ - strtok(line, " "); - /* Dup string here so we can reuse line later on */ - debugfs_path = strdup(strtok(NULL, " ")); - DBG("Got debugfs path : %s", debugfs_path); - break; - } - } - - fclose(fp); - - /* Mount debugfs if needded */ - if (debugfs_path == NULL) { - ret = asprintf(&debugfs_path, "/mnt/debugfs"); - if (ret < 0) { - perror("asprintf debugfs path"); - goto error; - } - ret = mount_debugfs(debugfs_path); - if (ret < 0) { - perror("Cannot mount debugfs"); - goto error; - } - } - - /* Modprobe lttng kernel modules */ - ret = modprobe_kernel_modules(); - if (ret < 0) { - goto error; - } - - /* Setup lttng kernel path */ - ret = asprintf(<tng_path, "%s/lttng", debugfs_path); - if (ret < 0) { - perror("asprintf lttng path"); - goto error; - } - - /* Open debugfs lttng */ - kernel_tracer_fd = open(lttng_path, O_RDWR); - if (kernel_tracer_fd < 0) { - DBG("Failed to open %s", lttng_path); - goto error; - } - - free(lttng_path); - free(debugfs_path); - DBG("Kernel tracer fd %d", kernel_tracer_fd); - return; - -error: - if (lttng_path) { - free(lttng_path); - } - if (debugfs_path) { - free(debugfs_path); - } - WARN("No kernel tracer available"); - kernel_tracer_fd = 0; - return; -} - -/* - * Init tracing by creating trace directory and sending fds kernel consumer. - */ -static int init_kernel_tracing(struct ltt_kernel_session *session) -{ - int ret = 0; - - if (session->consumer_fds_sent == 0) { - /* - * Assign default kernel consumer socket if no consumer assigned to the - * kernel session. At this point, it's NOT suppose to be 0 but this is - * an extra security check. - */ - if (session->consumer_fd == 0) { - session->consumer_fd = kconsumer_data.cmd_sock; - } - - ret = send_kconsumer_session_streams(&kconsumer_data, session); - if (ret < 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - - session->consumer_fds_sent = 1; - } - -error: - return ret; -} - -/* - * Create an UST session and add it to the session ust list. - */ -static int create_ust_session(struct ltt_session *session, - struct lttng_domain *domain) -{ - int ret; - unsigned int uid; - struct ltt_ust_session *lus = NULL; - - switch (domain->type) { - case LTTNG_DOMAIN_UST: - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - DBG("Creating UST session"); - - session_lock_list(); - uid = session_list_ptr->count; - session_unlock_list(); - - lus = trace_ust_create_session(session->path, uid, domain); - if (lus == NULL) { - ret = LTTCOMM_UST_SESS_FAIL; - goto error; - } - - ret = mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, - geteuid(), allowed_group()); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - ret = LTTCOMM_UST_SESS_FAIL; - goto error; - } - } - - /* The domain type dictate different actions on session creation */ - switch (domain->type) { - case LTTNG_DOMAIN_UST: - /* No ustctl for the global UST domain */ - break; - default: - goto error; - } - session->ust_session = lus; - - return LTTCOMM_OK; - -error: - free(lus); - return ret; -} - -/* - * Create a kernel tracer session then create the default channel. - */ -static int create_kernel_session(struct ltt_session *session) -{ - int ret; - - DBG("Creating kernel session"); - - ret = kernel_create_session(session, kernel_tracer_fd); - if (ret < 0) { - ret = LTTCOMM_KERN_SESS_FAIL; - goto error; - } - - /* Set kernel consumer socket fd */ - if (kconsumer_data.cmd_sock) { - session->kernel_session->consumer_fd = kconsumer_data.cmd_sock; - } - - ret = mkdir_recursive(session->kernel_session->trace_path, - S_IRWXU | S_IRWXG, geteuid(), allowed_group()); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - goto error; - } - } - -error: - return ret; -} - -/* - * Using the session list, filled a lttng_session array to send back to the - * client for session listing. - * - * The session list lock MUST be acquired before calling this function. Use - * session_lock_list() and session_unlock_list(). - */ -static void list_lttng_sessions(struct lttng_session *sessions) -{ - int i = 0; - struct ltt_session *session; - - DBG("Getting all available session"); - /* - * Iterate over session list and append data after the control struct in - * the buffer. - */ - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - strncpy(sessions[i].path, session->path, PATH_MAX); - sessions[i].path[PATH_MAX - 1] = '\0'; - strncpy(sessions[i].name, session->name, NAME_MAX); - sessions[i].name[NAME_MAX - 1] = '\0'; - i++; - } -} - -/* - * Fill lttng_channel array of all channels. - */ -static void list_lttng_channels(struct ltt_session *session, - struct lttng_channel *channels) -{ - int i = 0; - struct ltt_kernel_channel *kchan; - - DBG("Listing channels for session %s", session->name); - - /* Kernel channels */ - if (session->kernel_session != NULL) { - cds_list_for_each_entry(kchan, - &session->kernel_session->channel_list.head, list) { - /* Copy lttng_channel struct to array */ - memcpy(&channels[i], kchan->channel, sizeof(struct lttng_channel)); - channels[i].enabled = kchan->enabled; - i++; - } - } - - /* TODO: Missing UST listing */ -} - -/* - * Fill lttng_event array of all events in the channel. - */ -static void list_lttng_events(struct ltt_kernel_channel *kchan, - struct lttng_event *events) -{ - /* - * TODO: This is ONLY kernel. Need UST support. - */ - int i = 0; - struct ltt_kernel_event *event; - - DBG("Listing events for channel %s", kchan->channel->name); - - /* Kernel channels */ - cds_list_for_each_entry(event, &kchan->events_list.head , list) { - strncpy(events[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); - events[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - events[i].enabled = event->enabled; - switch (event->event->instrumentation) { - case LTTNG_KERNEL_TRACEPOINT: - events[i].type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_KERNEL_KPROBE: - case LTTNG_KERNEL_KRETPROBE: - events[i].type = LTTNG_EVENT_PROBE; - memcpy(&events[i].attr.probe, &event->event->u.kprobe, - sizeof(struct lttng_kernel_kprobe)); - break; - case LTTNG_KERNEL_FUNCTION: - events[i].type = LTTNG_EVENT_FUNCTION; - memcpy(&events[i].attr.ftrace, &event->event->u.ftrace, - sizeof(struct lttng_kernel_function)); - break; - case LTTNG_KERNEL_NOOP: - events[i].type = LTTNG_EVENT_NOOP; - break; - case LTTNG_KERNEL_SYSCALL: - events[i].type = LTTNG_EVENT_SYSCALL; - break; - case LTTNG_KERNEL_ALL: - assert(0); - break; - } - i++; - } -} - -/* - * Command LTTNG_DISABLE_CHANNEL processed by the client thread. - */ -static int cmd_disable_channel(struct ltt_session *session, - int domain, char *channel_name) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - ret = channel_kernel_disable(session->kernel_session, - channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - case LTTNG_DOMAIN_UST_PID: - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Copy channel from attributes and set it in the application channel list. - */ -/* -static int copy_ust_channel_to_app(struct ltt_ust_session *usess, - struct lttng_channel *attr, struct ust_app *app) -{ - int ret; - struct ltt_ust_channel *uchan, *new_chan; - - uchan = trace_ust_get_channel_by_key(usess->channels, attr->name); - if (uchan == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - new_chan = trace_ust_create_channel(attr, usess->path); - if (new_chan == NULL) { - PERROR("malloc ltt_ust_channel"); - ret = LTTCOMM_FATAL; - goto error; - } - - ret = channel_ust_copy(new_chan, uchan); - if (ret < 0) { - ret = LTTCOMM_FATAL; - goto error; - } - -error: - return ret; -} -*/ - -/* - * Command LTTNG_ENABLE_CHANNEL processed by the client thread. - */ -static int cmd_enable_channel(struct ltt_session *session, - struct lttng_domain *domain, struct lttng_channel *attr) -{ - int ret; - struct ltt_ust_session *usess = session->ust_session; - - DBG("Enabling channel %s for session %s", session->name, attr->name); - - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(attr->name, - session->kernel_session); - if (kchan == NULL) { - ret = channel_kernel_create(session->kernel_session, - attr, kernel_poll_pipe[1]); - } else { - ret = channel_kernel_enable(session->kernel_session, kchan); - } - - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - - DBG2("Enabling channel for LTTNG_DOMAIN_UST"); - - /* Get channel in global UST domain HT */ - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - attr->name); - if (uchan == NULL) { - uchan = trace_ust_create_channel(attr, usess->pathname); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_FAIL; - goto error; - } - rcu_read_lock(); - hashtable_add_unique(usess->domain_global.channels, &uchan->node); - rcu_read_unlock(); - DBG2("UST channel %s added to global domain HT", attr->name); - } else { - ret = LTTCOMM_UST_CHAN_EXIST; - goto error; - } - - ret = ust_app_add_channel(usess, uchan); - if (ret != LTTCOMM_OK) { - goto error; - } - - break; - } - case LTTNG_DOMAIN_UST_PID: - { - /* - int sock; - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess; - struct ust_app *app; - - usess = trace_ust_get_session_by_pid(&session->ust_session_list, - domain->attr.pid); - if (usess == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - app = ust_app_get_by_pid(domain->attr.pid); - if (app == NULL) { - ret = LTTCOMM_APP_NOT_FOUND; - goto error; - } - sock = app->sock; - - uchan = trace_ust_get_channel_by_name(attr->name, usess); - if (uchan == NULL) { - ret = channel_ust_create(usess, attr, sock); - } else { - ret = channel_ust_enable(usess, uchan, sock); - } - - if (ret != LTTCOMM_OK) { - goto error; - } - - ret = copy_ust_channel_to_app(usess, attr, app); - if (ret != LTTCOMM_OK) { - goto error; - } - - DBG("UST channel %s created for app sock %d with pid %d", - attr->name, app->sock, domain->attr.pid); - */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_EVENT processed by the client thread. - */ -static int cmd_disable_event(struct ltt_session *session, int domain, - char *channel_name, char *event_name) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_tracepoint(session->kernel_session, kchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - default: - /* TODO: Other UST domains */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_DISABLE_ALL_EVENT processed by the client thread. - */ -static int cmd_disable_event_all(struct ltt_session *session, int domain, - char *channel_name) -{ - int ret; - struct ltt_kernel_channel *kchan; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_all(session->kernel_session, kchan); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - default: - /* TODO: Userspace tracing */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ADD_CONTEXT processed by the client thread. - */ -static int cmd_add_context(struct ltt_session *session, int domain, - char *channel_name, char *event_name, struct lttng_event_context *ctx) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* Add kernel context to kernel tracer */ - ret = context_kernel_add(session->kernel_session, ctx, - event_name, channel_name); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - case LTTNG_DOMAIN_UST: - { - /* - struct ltt_ust_session *usess; - - cds_list_for_each_entry(usess, &session->ust_session_list.head, list) { - ret = context_ust_add(usess, ctx, - event_name, channel_name, domain); - if (ret != LTTCOMM_OK) { - goto error; - } - } - break; - */ - } - default: - /* TODO: UST other domains */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ENABLE_EVENT processed by the client thread. - */ -static int cmd_enable_event(struct ltt_session *session, int domain, - char *channel_name, struct lttng_event *event) -{ - int ret; - struct lttng_channel *attr; - struct ltt_ust_session *usess = session->ust_session; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - attr = channel_new_default_attr(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - - /* This call will notify the kernel thread */ - ret = channel_kernel_create(session->kernel_session, - attr, kernel_poll_pipe[1]); - if (ret != LTTCOMM_OK) { - goto error; - } - } - - /* Get the newly created kernel channel pointer */ - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - /* This sould not happen... */ - ret = LTTCOMM_FATAL; - goto error; - } - - ret = event_kernel_enable_tracepoint(session->kernel_session, kchan, - event); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_channel *uchan; - struct ltt_ust_event *uevent; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - /* TODO: Create default channel */ - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - uevent = trace_ust_find_event_by_name(uchan->events, event->name); - if (uevent == NULL) { - uevent = trace_ust_create_event(event); - if (uevent == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - } - - ret = ust_app_add_event(usess, uchan, uevent); - if (ret < 0) { - ret = LTTCOMM_UST_ENABLE_FAIL; - goto error; - } - break; - } - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - default: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ENABLE_ALL_EVENT processed by the client thread. - */ -static int cmd_enable_event_all(struct ltt_session *session, int domain, - char *channel_name, int event_type) -{ - int ret; - struct ltt_kernel_channel *kchan; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - /* This call will notify the kernel thread */ - ret = channel_kernel_create(session->kernel_session, NULL, - kernel_poll_pipe[1]); - if (ret != LTTCOMM_OK) { - goto error; - } - } - - /* Get the newly created kernel channel pointer */ - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - /* This sould not happen... */ - ret = LTTCOMM_FATAL; - goto error; - } - - switch (event_type) { - case LTTNG_KERNEL_SYSCALL: - ret = event_kernel_enable_all_syscalls(session->kernel_session, - kchan, kernel_tracer_fd); - break; - case LTTNG_KERNEL_TRACEPOINT: - /* - * This call enables all LTTNG_KERNEL_TRACEPOINTS and - * events already registered to the channel. - */ - ret = event_kernel_enable_all_tracepoints(session->kernel_session, - kchan, kernel_tracer_fd); - break; - case LTTNG_KERNEL_ALL: - /* Enable syscalls and tracepoints */ - ret = event_kernel_enable_all(session->kernel_session, - kchan, kernel_tracer_fd); - break; - default: - ret = LTTCOMM_KERN_ENABLE_FAIL; - goto error; - } - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - default: - /* TODO: Userspace tracing */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. - */ -static ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) -{ - int ret; - ssize_t nb_events = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - nb_events = kernel_list_events(kernel_tracer_fd, events); - if (nb_events < 0) { - ret = LTTCOMM_KERN_LIST_FAIL; - goto error; - } - break; - default: - /* TODO: Userspace listing */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - return nb_events; - -error: - /* Return negative value to differentiate return code */ - return -ret; -} - -/* - * Command LTTNG_START_TRACE processed by the client thread. - */ -static int cmd_start_trace(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_session *ksession; - struct ltt_ust_session *usess = session->ust_session; - - /* Short cut */ - ksession = session->kernel_session; - - /* Kernel tracing */ - if (ksession != NULL) { - struct ltt_kernel_channel *kchan; - - /* Open kernel metadata */ - if (ksession->metadata == NULL) { - ret = kernel_open_metadata(ksession, ksession->trace_path); - if (ret < 0) { - ret = LTTCOMM_KERN_META_FAIL; - goto error; - } - } - - /* Open kernel metadata stream */ - if (ksession->metadata_stream_fd == 0) { - ret = kernel_open_metadata_stream(ksession); - if (ret < 0) { - ERR("Kernel create metadata stream failed"); - ret = LTTCOMM_KERN_STREAM_FAIL; - goto error; - } - } - - /* For each channel */ - cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { - if (kchan->stream_count == 0) { - ret = kernel_open_channel_stream(kchan); - if (ret < 0) { - ret = LTTCOMM_KERN_STREAM_FAIL; - goto error; - } - /* Update the stream global counter */ - ksession->stream_count_global += ret; - } - } - - /* Setup kernel consumer socket and send fds to it */ - ret = init_kernel_tracing(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_START_FAIL; - goto error; - } - - /* This start the kernel tracing */ - ret = kernel_start_session(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_START_FAIL; - goto error; - } - - /* Quiescent wait after starting trace */ - kernel_wait_quiescent(kernel_tracer_fd); - } - - ret = ust_app_start_trace(usess); - if (ret < 0) { - ret = LTTCOMM_UST_START_FAIL; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_STOP_TRACE processed by the client thread. - */ -static int cmd_stop_trace(struct ltt_session *session) -{ - int ret; - struct ltt_kernel_channel *kchan; - struct ltt_kernel_session *ksession; - //struct ltt_ust_session *usess; - //struct ltt_ust_channel *ustchan; - - /* Short cut */ - ksession = session->kernel_session; - - /* Kernel tracer */ - if (ksession != NULL) { - DBG("Stop kernel tracing"); - - /* Flush all buffers before stopping */ - ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); - if (ret < 0) { - ERR("Kernel metadata flush failed"); - } - - cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { - ret = kernel_flush_buffer(kchan); - if (ret < 0) { - ERR("Kernel flush buffer error"); - } - } - - ret = kernel_stop_session(ksession); - if (ret < 0) { - ret = LTTCOMM_KERN_STOP_FAIL; - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - } - -#ifdef DISABLE - /* Stop each UST session */ - DBG("Stop UST tracing"); - cds_list_for_each_entry(usess, &session->ust_session_list.head, list) { - /* Flush all buffers before stopping */ - ret = ustctl_flush_buffer(usess->sock, usess->metadata->obj); - if (ret < 0) { - ERR("UST metadata flush failed"); - } - - cds_list_for_each_entry(ustchan, &usess->channels.head, list) { - ret = ustctl_flush_buffer(usess->sock, ustchan->obj); - if (ret < 0) { - ERR("UST flush buffer error"); - } - } - - ret = ustctl_stop_session(usess->sock, usess->handle); - if (ret < 0) { - ret = LTTCOMM_KERN_STOP_FAIL; - goto error; - } - - ustctl_wait_quiescent(usess->sock); - } -#endif - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_CREATE_SESSION processed by the client thread. - */ -static int cmd_create_session(char *name, char *path) -{ - int ret; - - ret = session_create(name, path); - if (ret != LTTCOMM_OK) { - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_DESTROY_SESSION processed by the client thread. - */ -static int cmd_destroy_session(struct ltt_session *session, char *name) -{ - int ret; - - /* Clean kernel session teardown */ - teardown_kernel_session(session); - - /* - * Must notify the kernel thread here to update it's poll setin order - * to remove the channel(s)' fd just destroyed. - */ - ret = notify_thread_pipe(kernel_poll_pipe[1]); - if (ret < 0) { - perror("write kernel poll pipe"); - } - - ret = session_destroy(session); - - return ret; -} - -/* - * Command LTTNG_CALIBRATE processed by the client thread. - */ -static int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) -{ - int ret; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct lttng_kernel_calibrate kcalibrate; - - kcalibrate.type = calibrate->type; - ret = kernel_calibrate(kernel_tracer_fd, &kcalibrate); - if (ret < 0) { - ret = LTTCOMM_KERN_ENABLE_FAIL; - goto error; - } - break; - } - default: - /* TODO: Userspace tracing */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_REGISTER_CONSUMER processed by the client thread. - */ -static int cmd_register_consumer(struct ltt_session *session, int domain, - char *sock_path) -{ - int ret, sock; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* Can't register a consumer if there is already one */ - if (session->kernel_session->consumer_fd != 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - - sock = lttcomm_connect_unix_sock(sock_path); - if (sock < 0) { - ret = LTTCOMM_CONNECT_FAIL; - goto error; - } - - session->kernel_session->consumer_fd = sock; - break; - default: - /* TODO: Userspace tracing */ - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_DOMAINS processed by the client thread. - */ -static ssize_t cmd_list_domains(struct ltt_session *session, - struct lttng_domain **domains) -{ - int ret; - ssize_t nb_dom = 0; - - if (session->kernel_session != NULL) { - nb_dom++; - } - - /* TODO: User-space tracer domain support */ - - *domains = malloc(nb_dom * sizeof(struct lttng_domain)); - if (*domains == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - (*domains)[0].type = LTTNG_DOMAIN_KERNEL; - - return nb_dom; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_CHANNELS processed by the client thread. - */ -static ssize_t cmd_list_channels(struct ltt_session *session, - struct lttng_channel **channels) -{ - int ret; - ssize_t nb_chan = 0; - - if (session->kernel_session != NULL) { - nb_chan += session->kernel_session->channel_count; - } - - *channels = malloc(nb_chan * sizeof(struct lttng_channel)); - if (*channels == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - list_lttng_channels(session, *channels); - - /* TODO UST support */ - - return nb_chan; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_EVENTS processed by the client thread. - */ -static ssize_t cmd_list_events(struct ltt_session *session, - char *channel_name, struct lttng_event **events) -{ - int ret; - ssize_t nb_event = 0; - struct ltt_kernel_channel *kchan = NULL; - - if (session->kernel_session != NULL) { - kchan = trace_kernel_get_channel_by_name(channel_name, - session->kernel_session); - if (kchan == NULL) { - ret = -LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - nb_event += kchan->event_count; - } - - *events = malloc(nb_event * sizeof(struct lttng_event)); - if (*events == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - list_lttng_events(kchan, *events); - - /* TODO: User-space tracer support */ - - return nb_event; - -error: - return ret; -} - -/* - * Process the command requested by the lttng client within the command - * context structure. This function make sure that the return structure (llm) - * is set and ready for transmission before returning. - * - * Return any error encountered or 0 for success. - */ -static int process_client_msg(struct command_ctx *cmd_ctx) -{ - int ret = LTTCOMM_OK; - int need_tracing_session = 1; - - DBG("Processing client command %d", cmd_ctx->lsm->cmd_type); - - /* - * Check for command that don't needs to allocate a returned payload. We do - * this here so we don't have to make the call for no payload at each - * command. - */ - switch(cmd_ctx->lsm->cmd_type) { - case LTTNG_LIST_SESSIONS: - case LTTNG_LIST_TRACEPOINTS: - case LTTNG_LIST_DOMAINS: - case LTTNG_LIST_CHANNELS: - case LTTNG_LIST_EVENTS: - break; - default: - /* Setup lttng message with no payload */ - ret = setup_lttng_msg(cmd_ctx, 0); - if (ret < 0) { - /* This label does not try to unlock the session */ - goto init_setup_error; - } - } - - /* Commands that DO NOT need a session. */ - switch (cmd_ctx->lsm->cmd_type) { - case LTTNG_CALIBRATE: - case LTTNG_CREATE_SESSION: - case LTTNG_LIST_SESSIONS: - case LTTNG_LIST_TRACEPOINTS: - need_tracing_session = 0; - break; - default: - DBG("Getting session %s by name", cmd_ctx->lsm->session.name); - session_lock_list(); - cmd_ctx->session = session_find_by_name(cmd_ctx->lsm->session.name); - session_unlock_list(); - if (cmd_ctx->session == NULL) { - if (cmd_ctx->lsm->session.name != NULL) { - ret = LTTCOMM_SESS_NOT_FOUND; - } else { - /* If no session name specified */ - ret = LTTCOMM_SELECT_SESS; - } - goto error; - } else { - /* Acquire lock for the session */ - session_lock(cmd_ctx->session); - } - break; - } - - /* - * Check domain type for specific "pre-action". - */ - switch (cmd_ctx->lsm->domain.type) { - case LTTNG_DOMAIN_KERNEL: - /* Kernel tracer check */ - if (kernel_tracer_fd == 0) { - /* Basically, load kernel tracer modules */ - init_kernel_tracer(); - if (kernel_tracer_fd == 0) { - ret = LTTCOMM_KERN_NA; - goto error; - } - } - - /* Need a session for kernel command */ - if (need_tracing_session) { - if (cmd_ctx->session->kernel_session == NULL) { - ret = create_kernel_session(cmd_ctx->session); - if (ret < 0) { - ret = LTTCOMM_KERN_SESS_FAIL; - goto error; - } - } - - /* Start the kernel consumer daemon */ - pthread_mutex_lock(&kconsumer_data.pid_mutex); - if (kconsumer_data.pid == 0 && - cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { - pthread_mutex_unlock(&kconsumer_data.pid_mutex); - ret = start_consumerd(&kconsumer_data); - if (ret < 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - } - pthread_mutex_unlock(&kconsumer_data.pid_mutex); - } - break; - case LTTNG_DOMAIN_UST: - { - if (need_tracing_session) { - if (cmd_ctx->session->ust_session == NULL) { - ret = create_ust_session(cmd_ctx->session, - &cmd_ctx->lsm->domain); - if (ret != LTTCOMM_OK) { - goto error; - } - } - /* Start the kernel consumer daemon */ - pthread_mutex_lock(&ustconsumer_data.pid_mutex); - if (ustconsumer_data.pid == 0 && - cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { - pthread_mutex_unlock(&ustconsumer_data.pid_mutex); - ret = start_consumerd(&ustconsumer_data); - if (ret < 0) { - ret = LTTCOMM_KERN_CONSUMER_FAIL; - goto error; - } - - cmd_ctx->session->ust_session->consumer_fd = - ustconsumer_data.cmd_sock; - } - pthread_mutex_unlock(&ustconsumer_data.pid_mutex); - } - break; - } - default: - break; - } - - /* Process by command type */ - switch (cmd_ctx->lsm->cmd_type) { - case LTTNG_ADD_CONTEXT: - { - ret = cmd_add_context(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.context.channel_name, - cmd_ctx->lsm->u.context.event_name, - &cmd_ctx->lsm->u.context.ctx); - break; - } - case LTTNG_DISABLE_CHANNEL: - { - ret = cmd_disable_channel(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.disable.channel_name); - break; - } - case LTTNG_DISABLE_EVENT: - { - ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.disable.channel_name, - cmd_ctx->lsm->u.disable.name); - ret = LTTCOMM_OK; - break; - } - case LTTNG_DISABLE_ALL_EVENT: - { - DBG("Disabling all events"); - - ret = cmd_disable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.disable.channel_name); - break; - } - case LTTNG_ENABLE_CHANNEL: - { - ret = cmd_enable_channel(cmd_ctx->session, &cmd_ctx->lsm->domain, - &cmd_ctx->lsm->u.channel.chan); - break; - } - case LTTNG_ENABLE_EVENT: - { - ret = cmd_enable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.enable.channel_name, - &cmd_ctx->lsm->u.enable.event); - break; - } - case LTTNG_ENABLE_ALL_EVENT: - { - DBG("Enabling all events"); - - ret = cmd_enable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.enable.channel_name, - cmd_ctx->lsm->u.enable.event.type); - break; - } - case LTTNG_LIST_TRACEPOINTS: - { - struct lttng_event *events; - ssize_t nb_events; - - nb_events = cmd_list_tracepoints(cmd_ctx->lsm->domain.type, &events); - if (nb_events < 0) { - ret = -nb_events; - goto error; - } - - /* - * Setup lttng message with payload size set to the event list size in - * bytes and then copy list into the llm payload. - */ - ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_event) * nb_events); - if (ret < 0) { - free(events); - goto setup_error; - } - - /* Copy event list into message payload */ - memcpy(cmd_ctx->llm->payload, events, - sizeof(struct lttng_event) * nb_events); - - free(events); - - ret = LTTCOMM_OK; - break; - } - case LTTNG_START_TRACE: - { - ret = cmd_start_trace(cmd_ctx->session); - break; - } - case LTTNG_STOP_TRACE: - { - ret = cmd_stop_trace(cmd_ctx->session); - break; - } - case LTTNG_CREATE_SESSION: - { - ret = cmd_create_session(cmd_ctx->lsm->session.name, - cmd_ctx->lsm->session.path); - break; - } - case LTTNG_DESTROY_SESSION: - { - ret = cmd_destroy_session(cmd_ctx->session, - cmd_ctx->lsm->session.name); - break; - } - case LTTNG_LIST_DOMAINS: - { - ssize_t nb_dom; - struct lttng_domain *domains; - - nb_dom = cmd_list_domains(cmd_ctx->session, &domains); - if (nb_dom < 0) { - ret = -nb_dom; - goto error; - } - - ret = setup_lttng_msg(cmd_ctx, nb_dom * sizeof(struct lttng_domain)); - if (ret < 0) { - goto setup_error; - } - - /* Copy event list into message payload */ - memcpy(cmd_ctx->llm->payload, domains, - nb_dom * sizeof(struct lttng_domain)); - - free(domains); - - ret = LTTCOMM_OK; - break; - } - case LTTNG_LIST_CHANNELS: - { - size_t nb_chan; - struct lttng_channel *channels; - - nb_chan = cmd_list_channels(cmd_ctx->session, &channels); - if (nb_chan < 0) { - ret = -nb_chan; - goto error; - } - - ret = setup_lttng_msg(cmd_ctx, nb_chan * sizeof(struct lttng_channel)); - if (ret < 0) { - goto setup_error; - } - - /* Copy event list into message payload */ - memcpy(cmd_ctx->llm->payload, channels, - nb_chan * sizeof(struct lttng_channel)); - - free(channels); - - ret = LTTCOMM_OK; - break; - } - case LTTNG_LIST_EVENTS: - { - size_t nb_event; - struct lttng_event *events = NULL; - - nb_event = cmd_list_events(cmd_ctx->session, - cmd_ctx->lsm->u.list.channel_name, &events); - if (nb_event < 0) { - ret = -nb_event; - goto error; - } - - ret = setup_lttng_msg(cmd_ctx, nb_event * sizeof(struct lttng_event)); - if (ret < 0) { - goto setup_error; - } - - /* Copy event list into message payload */ - memcpy(cmd_ctx->llm->payload, events, - nb_event * sizeof(struct lttng_event)); - - free(events); - - ret = LTTCOMM_OK; - break; - } - case LTTNG_LIST_SESSIONS: - { - session_lock_list(); - - if (session_list_ptr->count == 0) { - ret = LTTCOMM_NO_SESSION; - session_unlock_list(); - goto error; - } - - ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * - session_list_ptr->count); - if (ret < 0) { - session_unlock_list(); - goto setup_error; - } - - /* Filled the session array */ - list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload)); - - session_unlock_list(); - - ret = LTTCOMM_OK; - break; - } - case LTTNG_CALIBRATE: - { - ret = cmd_calibrate(cmd_ctx->lsm->domain.type, - &cmd_ctx->lsm->u.calibrate); - break; - } - case LTTNG_REGISTER_CONSUMER: - { - ret = cmd_register_consumer(cmd_ctx->session, cmd_ctx->lsm->domain.type, - cmd_ctx->lsm->u.reg.path); - break; - } - default: - ret = LTTCOMM_UND; - break; - } - -error: - if (cmd_ctx->llm == NULL) { - DBG("Missing llm structure. Allocating one."); - if (setup_lttng_msg(cmd_ctx, 0) < 0) { - goto setup_error; - } - } - /* Set return code */ - cmd_ctx->llm->ret_code = ret; -setup_error: - if (cmd_ctx->session) { - session_unlock(cmd_ctx->session); - } -init_setup_error: - return ret; -} - -/* - * This thread manage all clients request using the unix client socket for - * communication. - */ -static void *thread_manage_clients(void *data) -{ - int sock = 0, ret, i, pollfd; - uint32_t revents, nb_fd; - struct command_ctx *cmd_ctx = NULL; - struct lttng_poll_event events; - - DBG("[thread] Manage client started"); - - rcu_register_thread(); - - ret = lttcomm_listen_unix_sock(client_sock); - if (ret < 0) { - goto error; - } - - /* - * 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); - if (ret < 0) { - goto error; - } - - /* Add the application registration socket */ - ret = lttng_poll_add(&events, client_sock, LPOLLIN | LPOLLPRI); - if (ret < 0) { - goto error; - } - - /* - * Notify parent pid that we are ready to accept command for client side. - */ - if (opt_sig_parent) { - kill(ppid, SIGCHLD); - } - - while (1) { - DBG("Accepting client command ..."); - - nb_fd = LTTNG_POLL_GETNB(&events); - - /* Inifinite blocking call, waiting for transmission */ - ret = lttng_poll_wait(&events, -1); - if (ret < 0) { - goto error; - } - - 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 = check_thread_quit_pipe(pollfd, revents); - if (ret) { - goto error; - } - - /* Event on the registration socket */ - if (pollfd == client_sock) { - if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { - ERR("Client socket poll error"); - goto error; - } - } - } - - DBG("Wait for client response"); - - sock = lttcomm_accept_unix_sock(client_sock); - if (sock < 0) { - goto error; - } - - /* Allocate context command to process the client request */ - cmd_ctx = malloc(sizeof(struct command_ctx)); - if (cmd_ctx == NULL) { - perror("malloc cmd_ctx"); - goto error; - } - - /* Allocate data buffer for reception */ - cmd_ctx->lsm = malloc(sizeof(struct lttcomm_session_msg)); - if (cmd_ctx->lsm == NULL) { - perror("malloc cmd_ctx->lsm"); - goto error; - } - - cmd_ctx->llm = NULL; - cmd_ctx->session = NULL; - - /* - * Data is received from the lttng client. The struct - * lttcomm_session_msg (lsm) contains the command and data request of - * the client. - */ - DBG("Receiving data from client ..."); - ret = lttcomm_recv_unix_sock(sock, cmd_ctx->lsm, - sizeof(struct lttcomm_session_msg)); - if (ret <= 0) { - DBG("Nothing recv() from client... continuing"); - close(sock); - free(cmd_ctx); - continue; - } - - // TODO: Validate cmd_ctx including sanity check for - // security purpose. - - rcu_thread_online(); - /* - * This function dispatch the work to the kernel or userspace tracer - * libs and fill the lttcomm_lttng_msg data structure of all the needed - * informations for the client. The command context struct contains - * everything this function may needs. - */ - ret = process_client_msg(cmd_ctx); - rcu_thread_offline(); - if (ret < 0) { - /* - * TODO: Inform client somehow of the fatal error. At - * this point, ret < 0 means that a malloc failed - * (ENOMEM). Error detected but still accept command. - */ - clean_command_ctx(&cmd_ctx); - continue; - } - - DBG("Sending response (size: %d, retcode: %s)", - cmd_ctx->lttng_msg_size, - lttng_strerror(-cmd_ctx->llm->ret_code)); - ret = send_unix_sock(sock, cmd_ctx->llm, cmd_ctx->lttng_msg_size); - if (ret < 0) { - ERR("Failed to send data back to client"); - } - - clean_command_ctx(&cmd_ctx); - - /* End of transmission */ - close(sock); - } - -error: - DBG("Client thread dying"); - unlink(client_unix_sock_path); - close(client_sock); - close(sock); - - lttng_poll_clean(&events); - clean_command_ctx(&cmd_ctx); - - rcu_unregister_thread(); - return NULL; -} - - -/* - * usage function on stderr - */ -static void usage(void) -{ - fprintf(stderr, "Usage: %s OPTIONS\n\nOptions:\n", progname); - fprintf(stderr, " -h, --help Display this usage.\n"); - fprintf(stderr, " -c, --client-sock PATH Specify path for the client unix socket\n"); - fprintf(stderr, " -a, --apps-sock PATH Specify path for apps unix socket\n"); - fprintf(stderr, " --kconsumerd-err-sock PATH Specify path for the kernel consumer error socket\n"); - fprintf(stderr, " --kconsumerd-cmd-sock PATH Specify path for the kernel consumer command socket\n"); - fprintf(stderr, " --ustconsumerd-err-sock PATH Specify path for the UST consumer error socket\n"); - fprintf(stderr, " --ustconsumerd-cmd-sock PATH Specify path for the UST consumer command socket\n"); - fprintf(stderr, " -d, --daemonize Start as a daemon.\n"); - fprintf(stderr, " -g, --group NAME Specify the tracing group name. (default: tracing)\n"); - fprintf(stderr, " -V, --version Show version number.\n"); - fprintf(stderr, " -S, --sig-parent Send SIGCHLD to parent pid to notify readiness.\n"); - fprintf(stderr, " -q, --quiet No output at all.\n"); - fprintf(stderr, " -v, --verbose Verbose mode. Activate DBG() macro.\n"); - fprintf(stderr, " --verbose-consumer Verbose mode for consumer. Activate DBG() macro.\n"); -} - -/* - * daemon argument parsing - */ -static int parse_args(int argc, char **argv) -{ - int c; - - static struct option long_options[] = { - { "client-sock", 1, 0, 'c' }, - { "apps-sock", 1, 0, 'a' }, - { "kconsumerd-cmd-sock", 1, 0, 'C' }, - { "kconsumerd-err-sock", 1, 0, 'E' }, - { "ustconsumerd-cmd-sock", 1, 0, 'D' }, - { "ustconsumerd-err-sock", 1, 0, 'F' }, - { "daemonize", 0, 0, 'd' }, - { "sig-parent", 0, 0, 'S' }, - { "help", 0, 0, 'h' }, - { "group", 1, 0, 'g' }, - { "version", 0, 0, 'V' }, - { "quiet", 0, 0, 'q' }, - { "verbose", 0, 0, 'v' }, - { "verbose-consumer", 0, 0, 'Z' }, - { NULL, 0, 0, 0 } - }; - - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "dhqvVS" "a:c:g:s:C:E:D:F:Z", - long_options, &option_index); - if (c == -1) { - break; - } - - switch (c) { - case 0: - fprintf(stderr, "option %s", long_options[option_index].name); - if (optarg) { - fprintf(stderr, " with arg %s\n", optarg); - } - break; - case 'c': - snprintf(client_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'a': - snprintf(apps_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'd': - opt_daemon = 1; - break; - case 'g': - opt_tracing_group = strdup(optarg); - break; - case 'h': - usage(); - exit(EXIT_FAILURE); - case 'V': - fprintf(stdout, "%s\n", VERSION); - exit(EXIT_SUCCESS); - case 'S': - opt_sig_parent = 1; - break; - case 'E': - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'C': - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'F': - snprintf(ustconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'D': - snprintf(ustconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'q': - opt_quiet = 1; - break; - case 'v': - /* Verbose level can increase using multiple -v */ - opt_verbose += 1; - break; - case 'Z': - opt_verbose_consumer += 1; - break; - default: - /* Unknown option or other error. - * Error is printed by getopt, just return */ - return -1; - } - } - - return 0; -} - -/* - * Creates the two needed socket by the daemon. - * apps_sock - The communication socket for all UST apps. - * client_sock - The communication of the cli tool (lttng). - */ -static int init_daemon_socket(void) -{ - int ret = 0; - mode_t old_umask; - - old_umask = umask(0); - - /* Create client tool unix socket */ - client_sock = lttcomm_create_unix_sock(client_unix_sock_path); - if (client_sock < 0) { - ERR("Create unix sock failed: %s", client_unix_sock_path); - ret = -1; - goto end; - } - - /* File permission MUST be 660 */ - ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (ret < 0) { - ERR("Set file permissions failed: %s", client_unix_sock_path); - perror("chmod"); - goto end; - } - - /* Create the application unix socket */ - apps_sock = lttcomm_create_unix_sock(apps_unix_sock_path); - if (apps_sock < 0) { - ERR("Create unix sock failed: %s", apps_unix_sock_path); - ret = -1; - goto end; - } - - /* File permission MUST be 666 */ - ret = chmod(apps_unix_sock_path, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if (ret < 0) { - ERR("Set file permissions failed: %s", apps_unix_sock_path); - perror("chmod"); - goto end; - } - -end: - umask(old_umask); - return ret; -} - -/* - * Check if the global socket is available, and if a daemon is answering at the - * other side. If yes, error is returned. - */ -static int check_existing_daemon(void) -{ - if (access(client_unix_sock_path, F_OK) < 0 && - access(apps_unix_sock_path, F_OK) < 0) { - return 0; - } - - /* Is there anybody out there ? */ - if (lttng_session_daemon_alive()) { - return -EEXIST; - } else { - return 0; - } -} - -/* - * Set the tracing group gid onto the client socket. - * - * Race window between mkdir and chown is OK because we are going from more - * permissive (root.root) to les permissive (root.tracing). - */ -static int set_permissions(void) -{ - int ret; - gid_t gid; - - gid = allowed_group(); - if (gid < 0) { - if (is_root) { - WARN("No tracing group detected"); - ret = 0; - } else { - ERR("Missing tracing group. Aborting execution."); - ret = -1; - } - goto end; - } - - /* Set lttng run dir */ - ret = chown(LTTNG_RUNDIR, 0, gid); - if (ret < 0) { - ERR("Unable to set group on " LTTNG_RUNDIR); - perror("chown"); - } - - /* lttng client socket path */ - ret = chown(client_unix_sock_path, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", client_unix_sock_path); - perror("chown"); - } - - /* kconsumer error socket path */ - ret = chown(kconsumer_data.err_unix_sock_path, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", kconsumer_data.err_unix_sock_path); - perror("chown"); - } - - /* ustconsumer error socket path */ - ret = chown(ustconsumer_data.err_unix_sock_path, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", ustconsumer_data.err_unix_sock_path); - perror("chown"); - } - - DBG("All permissions are set"); - -end: - return ret; -} - -/* - * Create the pipe used to wake up the kernel thread. - */ -static int create_kernel_poll_pipe(void) -{ - return pipe2(kernel_poll_pipe, O_CLOEXEC); -} - -/* - * Create the application command pipe to wake thread_manage_apps. - */ -static int create_apps_cmd_pipe(void) -{ - return pipe2(apps_cmd_pipe, O_CLOEXEC); -} - -/* - * Create the lttng run directory needed for all global sockets and pipe. - */ -static int create_lttng_rundir(void) -{ - int ret; - - ret = mkdir(LTTNG_RUNDIR, S_IRWXU | S_IRWXG ); - if (ret < 0) { - if (errno != EEXIST) { - ERR("Unable to create " LTTNG_RUNDIR); - goto error; - } else { - ret = 0; - } - } - -error: - return ret; -} - -/* - * Setup sockets and directory needed by the kconsumerd communication with the - * session daemon. - */ -static int set_consumer_sockets(struct consumer_data *consumer_data) -{ - int ret; - const char *path = consumer_data->type == LTTNG_CONSUMER_KERNEL ? - KCONSUMERD_PATH : USTCONSUMERD_PATH; - - if (strlen(consumer_data->err_unix_sock_path) == 0) { - snprintf(consumer_data->err_unix_sock_path, PATH_MAX, - consumer_data->type == LTTNG_CONSUMER_KERNEL ? - KCONSUMERD_ERR_SOCK_PATH : - USTCONSUMERD_ERR_SOCK_PATH); - } - - if (strlen(consumer_data->cmd_unix_sock_path) == 0) { - snprintf(consumer_data->cmd_unix_sock_path, PATH_MAX, - consumer_data->type == LTTNG_CONSUMER_KERNEL ? - KCONSUMERD_CMD_SOCK_PATH : - USTCONSUMERD_CMD_SOCK_PATH); - } - - ret = mkdir(path, S_IRWXU | S_IRWXG); - if (ret < 0) { - if (errno != EEXIST) { - ERR("Failed to create %s", path); - goto error; - } - ret = 0; - } - - /* Create the kconsumerd error unix socket */ - consumer_data->err_sock = - lttcomm_create_unix_sock(consumer_data->err_unix_sock_path); - if (consumer_data->err_sock < 0) { - ERR("Create unix sock failed: %s", consumer_data->err_unix_sock_path); - ret = -1; - goto error; - } - - /* File permission MUST be 660 */ - ret = chmod(consumer_data->err_unix_sock_path, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (ret < 0) { - ERR("Set file permissions failed: %s", consumer_data->err_unix_sock_path); - perror("chmod"); - goto error; - } - -error: - return ret; -} - -/* - * Signal handler for the daemon - * - * Simply stop all worker threads, leaving main() return gracefully after - * joining all threads and calling cleanup(). - */ -static void sighandler(int sig) -{ - switch (sig) { - case SIGPIPE: - DBG("SIGPIPE catched"); - return; - case SIGINT: - DBG("SIGINT catched"); - stop_threads(); - break; - case SIGTERM: - DBG("SIGTERM catched"); - stop_threads(); - break; - default: - break; - } -} - -/* - * Setup signal handler for : - * SIGINT, SIGTERM, SIGPIPE - */ -static int set_signal_handler(void) -{ - int ret = 0; - struct sigaction sa; - sigset_t sigset; - - if ((ret = sigemptyset(&sigset)) < 0) { - perror("sigemptyset"); - return ret; - } - - sa.sa_handler = sighandler; - sa.sa_mask = sigset; - sa.sa_flags = 0; - if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - perror("sigaction"); - return ret; - } - - if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { - perror("sigaction"); - return ret; - } - - if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { - perror("sigaction"); - return ret; - } - - DBG("Signal handler set for SIGTERM, SIGPIPE and SIGINT"); - - return ret; -} - -/* - * Set open files limit to unlimited. This daemon can open a large number of - * file descriptors in order to consumer multiple kernel traces. - */ -static void set_ulimit(void) -{ - int ret; - struct rlimit lim; - - /* The kernel does not allowed an infinite limit for open files */ - lim.rlim_cur = 65535; - lim.rlim_max = 65535; - - ret = setrlimit(RLIMIT_NOFILE, &lim); - if (ret < 0) { - perror("failed to set open files limit"); - } -} - -/* - * main - */ -int main(int argc, char **argv) -{ - int ret = 0; - void *status; - const char *home_path; - - rcu_register_thread(); - - /* Create thread quit pipe */ - if ((ret = init_thread_quit_pipe()) < 0) { - goto error; - } - - /* Parse arguments */ - progname = argv[0]; - if ((ret = parse_args(argc, argv) < 0)) { - goto error; - } - - /* Daemonize */ - if (opt_daemon) { - ret = daemon(0, 0); - if (ret < 0) { - perror("daemon"); - goto error; - } - } - - /* Check if daemon is UID = 0 */ - is_root = !getuid(); - - if (is_root) { - ret = create_lttng_rundir(); - if (ret < 0) { - goto error; - } - - if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_UNIX_SOCK); - } - - if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); - } - - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, - DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); - } - } else { - home_path = get_home_dir(); - if (home_path == NULL) { - /* TODO: Add --socket PATH option */ - ERR("Can't get HOME directory for sockets creation."); - ret = -EPERM; - goto error; - } - - if (strlen(apps_unix_sock_path) == 0) { - snprintf(apps_unix_sock_path, PATH_MAX, - DEFAULT_HOME_APPS_UNIX_SOCK, home_path); - } - - /* Set the cli tool unix socket path */ - if (strlen(client_unix_sock_path) == 0) { - snprintf(client_unix_sock_path, PATH_MAX, - DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path); - } - - /* Set global SHM for ust */ - if (strlen(wait_shm_path) == 0) { - snprintf(wait_shm_path, PATH_MAX, - DEFAULT_HOME_APPS_WAIT_SHM_PATH, geteuid()); - } - } - - DBG("Client socket path %s", client_unix_sock_path); - DBG("Application socket path %s", apps_unix_sock_path); - - /* - * See if daemon already exist. - */ - if ((ret = check_existing_daemon()) < 0) { - ERR("Already running daemon.\n"); - /* - * We do not goto exit because we must not cleanup() - * because a daemon is already running. - */ - goto error; - } - - /* After this point, we can safely call cleanup() with "goto exit" */ - - /* - * These actions must be executed as root. We do that *after* setting up - * the sockets path because we MUST make the check for another daemon using - * those paths *before* trying to set the kernel consumer sockets and init - * kernel tracer. - */ - if (is_root) { - ret = set_consumer_sockets(&kconsumer_data); - if (ret < 0) { - goto exit; - } - - ret = set_consumer_sockets(&ustconsumer_data); - if (ret < 0) { - goto exit; - } - /* Setup kernel tracer */ - init_kernel_tracer(); - - /* Set ulimit for open files */ - set_ulimit(); - } - - if ((ret = set_signal_handler()) < 0) { - goto exit; - } - - /* Setup the needed unix socket */ - if ((ret = init_daemon_socket()) < 0) { - goto exit; - } - - /* Set credentials to socket */ - if (is_root && ((ret = set_permissions()) < 0)) { - goto exit; - } - - /* Get parent pid if -S, --sig-parent is specified. */ - if (opt_sig_parent) { - ppid = getppid(); - } - - /* Setup the kernel pipe for waking up the kernel thread */ - if ((ret = create_kernel_poll_pipe()) < 0) { - goto exit; - } - - /* Setup the thread apps communication pipe. */ - if ((ret = create_apps_cmd_pipe()) < 0) { - goto exit; - } - - /* Init UST command queue. */ - cds_wfq_init(&ust_cmd_queue.queue); - - /* Init UST app hash table */ - ust_app_ht_alloc(); - - /* - * Get session list pointer. This pointer MUST NOT be free(). This list is - * statically declared in session.c - */ - session_list_ptr = session_get_list(); - - /* Set up max poll set size */ - lttng_poll_set_max_size(); - - /* Create thread to manage the client socket */ - ret = pthread_create(&client_thread, NULL, - thread_manage_clients, (void *) NULL); - if (ret != 0) { - perror("pthread_create clients"); - goto exit_client; - } - - /* Create thread to dispatch registration */ - ret = pthread_create(&dispatch_thread, NULL, - thread_dispatch_ust_registration, (void *) NULL); - if (ret != 0) { - perror("pthread_create dispatch"); - goto exit_dispatch; - } - - /* Create thread to manage application registration. */ - ret = pthread_create(®_apps_thread, NULL, - thread_registration_apps, (void *) NULL); - if (ret != 0) { - perror("pthread_create registration"); - goto exit_reg_apps; - } - - /* Create thread to manage application socket */ - ret = pthread_create(&apps_thread, NULL, - thread_manage_apps, (void *) NULL); - if (ret != 0) { - perror("pthread_create apps"); - goto exit_apps; - } - - /* Create kernel thread to manage kernel event */ - ret = pthread_create(&kernel_thread, NULL, - thread_manage_kernel, (void *) NULL); - if (ret != 0) { - perror("pthread_create kernel"); - goto exit_kernel; - } - - ret = pthread_join(kernel_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - -exit_kernel: - ret = pthread_join(apps_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - -exit_apps: - ret = pthread_join(reg_apps_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - -exit_reg_apps: - ret = pthread_join(dispatch_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - -exit_dispatch: - ret = pthread_join(client_thread, &status); - if (ret != 0) { - perror("pthread_join"); - goto error; /* join error, exit without cleanup */ - } - - ret = join_consumer_thread(&kconsumer_data); - if (ret != 0) { - perror("join_consumer"); - goto error; /* join error, exit without cleanup */ - } - -exit_client: -exit: - /* - * cleanup() is called when no other thread is running. - */ - rcu_thread_online(); - cleanup(); - rcu_thread_offline(); - rcu_unregister_thread(); - if (!ret) - exit(EXIT_SUCCESS); -error: - exit(EXIT_FAILURE); -} diff --git a/ltt-sessiond/session.c b/ltt-sessiond/session.c deleted file mode 100644 index c7ce843d7..000000000 --- a/ltt-sessiond/session.c +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#include -#include - -#include "hashtable.h" -#include "session.h" - -#include "../hashtable/hash.h" - -/* - * NOTES: - * - * No ltt_session.lock is taken here because those data structure are widely - * spread across the lttng-tools code base so before caling functions below - * that can read/write a session, the caller MUST acquire the session lock - * using session_lock() and session_unlock(). - */ - -/* - * Init tracing session list. - * - * Please see session.h for more explanation and correct usage of the list. - */ -static struct ltt_session_list ltt_session_list = { - .head = CDS_LIST_HEAD_INIT(ltt_session_list.head), - .lock = PTHREAD_MUTEX_INITIALIZER, - .count = 0, -}; - -/* - * Add a ltt_session structure to the global list. - * - * The caller MUST acquire the session list lock before. - */ -static void add_session_list(struct ltt_session *ls) -{ - cds_list_add(&ls->list, <t_session_list.head); - ltt_session_list.count++; -} - -/* - * Delete a ltt_session structure to the global list. - * - * The caller MUST acquire the session list lock before. - */ -static void del_session_list(struct ltt_session *ls) -{ - cds_list_del(&ls->list); - /* Sanity check */ - if (ltt_session_list.count > 0) { - ltt_session_list.count--; - } -} - -/* - * Return a pointer to the session list. - */ -struct ltt_session_list *session_get_list(void) -{ - return <t_session_list; -} - -/* - * Acquire session list lock - */ -void session_lock_list(void) -{ - pthread_mutex_lock(<t_session_list.lock); -} - -/* - * Release session list lock - */ -void session_unlock_list(void) -{ - pthread_mutex_unlock(<t_session_list.lock); -} - -/* - * Acquire session lock - */ -void session_lock(struct ltt_session *session) -{ - pthread_mutex_lock(&session->lock); -} - -/* - * Release session lock - */ -void session_unlock(struct ltt_session *session) -{ - pthread_mutex_unlock(&session->lock); -} - -/* - * Return a ltt_session structure ptr that matches name. If no session found, - * NULL is returned. This must be called with the session lock held using - * session_lock_list and session_unlock_list. - */ -struct ltt_session *session_find_by_name(char *name) -{ - struct ltt_session *iter; - - DBG2("Trying to find session by name %s", name); - - cds_list_for_each_entry(iter, <t_session_list.head, list) { - if (strncmp(iter->name, name, NAME_MAX) == 0) { - goto found; - } - } - - iter = NULL; - -found: - return iter; -} - -/* - * Delete session from the session list and free the memory. - * - * Return -1 if no session is found. On success, return 1; - */ -int session_destroy(struct ltt_session *session) -{ - /* Safety check */ - if (session == NULL) { - ERR("Session pointer was null on session destroy"); - return LTTCOMM_OK; - } - - DBG("Destroying session %s", session->name); - del_session_list(session); - pthread_mutex_destroy(&session->lock); - free(session); - - return LTTCOMM_OK; -} - -/* - * Create a brand new session and add it to the session list. - */ -int session_create(char *name, char *path) -{ - int ret; - struct ltt_session *new_session; - - new_session = session_find_by_name(name); - if (new_session != NULL) { - ret = LTTCOMM_EXIST_SESS; - goto error_exist; - } - - /* Allocate session data structure */ - new_session = malloc(sizeof(struct ltt_session)); - if (new_session == NULL) { - perror("malloc"); - ret = LTTCOMM_FATAL; - goto error_malloc; - } - - /* Define session name */ - if (name != NULL) { - if (snprintf(new_session->name, NAME_MAX, "%s", name) < 0) { - ret = LTTCOMM_FATAL; - goto error_asprintf; - } - } else { - ERR("No session name given"); - ret = LTTCOMM_FATAL; - goto error; - } - - /* Define session system path */ - if (path != NULL) { - if (snprintf(new_session->path, PATH_MAX, "%s", path) < 0) { - ret = LTTCOMM_FATAL; - goto error_asprintf; - } - } else { - ERR("No session path given"); - ret = LTTCOMM_FATAL; - goto error; - } - - /* Init kernel session */ - new_session->kernel_session = NULL; - new_session->ust_session = NULL; - - /* Init lock */ - pthread_mutex_init(&new_session->lock, NULL); - - /* Add new session to the session list */ - session_lock_list(); - add_session_list(new_session); - session_unlock_list(); - - DBG("Tracing session %s created in %s", name, path); - - return LTTCOMM_OK; - -error: -error_asprintf: - if (new_session != NULL) { - free(new_session); - } - -error_exist: -error_malloc: - return ret; -} diff --git a/ltt-sessiond/session.h b/ltt-sessiond/session.h deleted file mode 100644 index f6a9fff4d..000000000 --- a/ltt-sessiond/session.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_SESSION_H -#define _LTT_SESSION_H - -#include -#include - -#include "trace-kernel.h" -#include "trace-ust.h" - -/* - * Tracing session list - * - * Statically declared in session.c and can be accessed by using - * session_get_list() function that returns the pointer to the list. - */ -struct ltt_session_list { - /* - * This lock protects any read/write access to the list and count (which is - * basically the list size). All public functions in session.c acquire this - * lock and release it before returning. If none of those functions are - * used, the lock MUST be acquired in order to iterate or/and do any - * actions on that list. - */ - pthread_mutex_t lock; - - /* - * Number of element in the list. The session list lock MUST be acquired if - * this counter is used when iterating over the session list. - */ - unsigned int count; - - /* Linked list head */ - struct cds_list_head head; -}; - -/* - * This data structure contains information needed to identify a tracing - * session for both LTTng and UST. - */ -struct ltt_session { - char name[NAME_MAX]; - char path[PATH_MAX]; - struct ltt_kernel_session *kernel_session; - struct ltt_ust_session *ust_session; - /* - * Protect any read/write on this session data structure. This lock must be - * acquired *before* using any public functions declared below. Use - * session_lock() and session_unlock() for that. - */ - pthread_mutex_t lock; - struct cds_list_head list; -}; - -/* Prototypes */ -int session_create(char *name, char *path); -int session_destroy(struct ltt_session *session); - -void session_lock(struct ltt_session *session); -void session_lock_list(void); -void session_unlock(struct ltt_session *session); -void session_unlock_list(void); - -struct ltt_session *session_find_by_name(char *name); -struct ltt_session_list *session_get_list(void); - -#endif /* _LTT_SESSION_H */ diff --git a/ltt-sessiond/shm.c b/ltt-sessiond/shm.c deleted file mode 100644 index 7dac1659f..000000000 --- a/ltt-sessiond/shm.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "shm.h" - -/* - * Using fork to set umask in the child process (not multi-thread safe). We - * deal with the shm_open vs ftruncate race (happening when the sessiond owns - * the shm and does not let everybody modify it, to ensure safety against - * shm_unlink) by simply letting the mmap fail and retrying after a few - * seconds. For global shm, everybody has rw access to it until the sessiond - * starts. - */ -static int get_wait_shm(char *shm_path, size_t mmap_size, int global) -{ - int wait_shm_fd, ret; - mode_t mode; - - /* Default permissions */ - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; - - /* Change owner of the shm path */ - if (global) { - ret = chown(shm_path, 0, 0); - if (ret < 0) { - if (errno != ENOENT) { - perror("chown wait shm"); - goto error; - } - } - - /* - * If global session daemon, any application can register so the shm - * needs to be set in read-only mode for others. - */ - mode |= S_IROTH; - } else { - ret = chown(shm_path, getuid(), getgid()); - if (ret < 0) { - if (errno != ENOENT) { - perror("chown wait shm"); - goto error; - } - } - } - - /* - * Set permissions to the shm even if we did not create the shm. - */ - ret = chmod(shm_path, mode); - if (ret < 0) { - if (errno != ENOENT) { - perror("chmod wait shm"); - goto error; - } - } - - /* - * We're alone in a child process, so we can modify the process-wide - * umask. - */ - umask(~mode); - - /* - * Try creating shm (or get rw access). We don't do an exclusive open, - * because we allow other processes to create+ftruncate it concurrently. - */ - wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); - if (wait_shm_fd < 0) { - perror("shm_open wait shm"); - goto error; - } - - ret = ftruncate(wait_shm_fd, mmap_size); - if (ret < 0) { - perror("ftruncate wait shm"); - exit(EXIT_FAILURE); - } - - ret = fchmod(wait_shm_fd, mode); - if (ret < 0) { - perror("fchmod"); - exit(EXIT_FAILURE); - } - - DBG("Got the wait shm fd %d", wait_shm_fd); - - return wait_shm_fd; - -error: - DBG("Failing to get the wait shm fd"); - - return -1; -} - -/* - * Return the wait shm mmap for UST application notification. The global - * variable is used to indicate if the the session daemon is global - * (root:tracing) or running with an unprivileged user. - * - * This returned value is used by futex_wait_update() in futex.c to WAKE all - * waiters which are UST application waiting for a session daemon. - */ -char *shm_ust_get_mmap(char *shm_path, int global) -{ - size_t mmap_size = sysconf(_SC_PAGE_SIZE); - int wait_shm_fd, ret; - char *wait_shm_mmap; - - wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); - if (wait_shm_fd < 0) { - goto error; - } - - wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, - MAP_SHARED, wait_shm_fd, 0); - - /* close shm fd immediately after taking the mmap reference */ - ret = close(wait_shm_fd); - if (ret) { - perror("Error closing fd"); - } - - if (wait_shm_mmap == MAP_FAILED) { - DBG("mmap error (can be caused by race with ust)."); - goto error; - } - - return wait_shm_mmap; - -error: - return NULL; -} diff --git a/ltt-sessiond/shm.h b/ltt-sessiond/shm.h deleted file mode 100644 index 2d301bbac..000000000 --- a/ltt-sessiond/shm.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_SHM_H -#define _LTT_SHM_H - -char *shm_ust_get_mmap(char *shm_path, int global); - -#endif /* _LTT_SHM_H */ diff --git a/ltt-sessiond/trace-kernel.c b/ltt-sessiond/trace-kernel.c deleted file mode 100644 index fa3ba692c..000000000 --- a/ltt-sessiond/trace-kernel.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include - -#include "trace-kernel.h" - -/* - * Find the channel name for the given kernel session. - */ -struct ltt_kernel_channel *trace_kernel_get_channel_by_name( - char *name, struct ltt_kernel_session *session) -{ - struct ltt_kernel_channel *chan; - - if (session == NULL) { - ERR("Undefine session"); - goto error; - } - - DBG("Trying to find channel %s", name); - - cds_list_for_each_entry(chan, &session->channel_list.head, list) { - if (strcmp(name, chan->channel->name) == 0) { - DBG("Found channel by name %s", name); - return chan; - } - } - -error: - return NULL; -} - -/* - * Find the event name for the given channel. - */ -struct ltt_kernel_event *trace_kernel_get_event_by_name( - char *name, struct ltt_kernel_channel *channel) -{ - struct ltt_kernel_event *ev; - - if (channel == NULL) { - ERR("Undefine channel"); - goto error; - } - - cds_list_for_each_entry(ev, &channel->events_list.head, list) { - if (strcmp(name, ev->event->name) == 0) { - DBG("Found event by name %s for channel %s", name, - channel->channel->name); - return ev; - } - } - -error: - return NULL; -} - -/* - * Allocate and initialize a kernel session data structure. - * - * Return pointer to structure or NULL. - */ -struct ltt_kernel_session *trace_kernel_create_session(char *path) -{ - int ret; - struct ltt_kernel_session *lks; - - /* Allocate a new ltt kernel session */ - lks = malloc(sizeof(struct ltt_kernel_session)); - if (lks == NULL) { - perror("create kernel session malloc"); - goto error; - } - - /* Init data structure */ - lks->fd = 0; - lks->metadata_stream_fd = 0; - lks->channel_count = 0; - lks->stream_count_global = 0; - lks->metadata = NULL; - lks->consumer_fd = 0; - CDS_INIT_LIST_HEAD(&lks->channel_list.head); - - /* Set session path */ - ret = asprintf(&lks->trace_path, "%s/kernel", path); - if (ret < 0) { - perror("asprintf kernel traces path"); - goto error; - } - - return lks; - -error: - return NULL; -} - -/* - * Allocate and initialize a kernel channel data structure. - * - * Return pointer to structure or NULL. - */ -struct ltt_kernel_channel *trace_kernel_create_channel(struct lttng_channel *chan, char *path) -{ - int ret; - struct ltt_kernel_channel *lkc; - - lkc = malloc(sizeof(struct ltt_kernel_channel)); - if (lkc == NULL) { - perror("ltt_kernel_channel malloc"); - goto error; - } - - lkc->channel = malloc(sizeof(struct lttng_channel)); - if (lkc->channel == NULL) { - perror("lttng_channel malloc"); - goto error; - } - memcpy(lkc->channel, chan, sizeof(struct lttng_channel)); - - lkc->fd = 0; - lkc->stream_count = 0; - lkc->event_count = 0; - lkc->enabled = 1; - lkc->ctx = NULL; - /* Init linked list */ - CDS_INIT_LIST_HEAD(&lkc->events_list.head); - CDS_INIT_LIST_HEAD(&lkc->stream_list.head); - /* Set default trace output path */ - ret = asprintf(&lkc->pathname, "%s", path); - if (ret < 0) { - perror("asprintf kernel create channel"); - goto error; - } - - return lkc; - -error: - return NULL; -} - -/* - * Allocate and initialize a kernel event. Set name and event type. - * - * Return pointer to structure or NULL. - */ -struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev) -{ - struct ltt_kernel_event *lke; - struct lttng_kernel_event *attr; - - lke = malloc(sizeof(struct ltt_kernel_event)); - attr = malloc(sizeof(struct lttng_kernel_event)); - if (lke == NULL || attr == NULL) { - perror("kernel event malloc"); - goto error; - } - - switch (ev->type) { - case LTTNG_EVENT_PROBE: - attr->instrumentation = LTTNG_KERNEL_KPROBE; - attr->u.kprobe.addr = ev->attr.probe.addr; - attr->u.kprobe.offset = ev->attr.probe.offset; - strncpy(attr->u.kprobe.symbol_name, - ev->attr.probe.symbol_name, LTTNG_SYM_NAME_LEN); - attr->u.kprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_FUNCTION: - attr->instrumentation = LTTNG_KERNEL_KRETPROBE; - attr->u.kretprobe.addr = ev->attr.probe.addr; - attr->u.kretprobe.offset = ev->attr.probe.offset; - attr->u.kretprobe.offset = ev->attr.probe.offset; - strncpy(attr->u.kretprobe.symbol_name, - ev->attr.probe.symbol_name, LTTNG_SYM_NAME_LEN); - attr->u.kretprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - attr->instrumentation = LTTNG_KERNEL_FUNCTION; - strncpy(attr->u.ftrace.symbol_name, - ev->attr.ftrace.symbol_name, LTTNG_SYM_NAME_LEN); - attr->u.ftrace.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_TRACEPOINT: - attr->instrumentation = LTTNG_KERNEL_TRACEPOINT; - break; - case LTTNG_EVENT_SYSCALL: - attr->instrumentation = LTTNG_KERNEL_SYSCALL; - break; - case LTTNG_EVENT_ALL: - attr->instrumentation = LTTNG_KERNEL_ALL; - break; - default: - ERR("Unknown kernel instrumentation type (%d)", ev->type); - goto error; - } - - /* Copy event name */ - strncpy(attr->name, ev->name, LTTNG_SYM_NAME_LEN); - attr->name[LTTNG_SYM_NAME_LEN - 1] = '\0'; - - /* Setting up a kernel event */ - lke->fd = 0; - lke->event = attr; - lke->enabled = 1; - lke->ctx = NULL; - - return lke; - -error: - return NULL; -} - -/* - * Allocate and initialize a kernel metadata. - * - * Return pointer to structure or NULL. - */ -struct ltt_kernel_metadata *trace_kernel_create_metadata(char *path) -{ - int ret; - struct ltt_kernel_metadata *lkm; - struct lttng_channel *chan; - - lkm = malloc(sizeof(struct ltt_kernel_metadata)); - chan = malloc(sizeof(struct lttng_channel)); - if (lkm == NULL || chan == NULL) { - perror("kernel metadata malloc"); - goto error; - } - - /* Set default attributes */ - chan->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE; - chan->attr.subbuf_size = DEFAULT_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.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; - - /* Init metadata */ - lkm->fd = 0; - lkm->conf = chan; - /* Set default metadata path */ - ret = asprintf(&lkm->pathname, "%s/metadata", path); - if (ret < 0) { - perror("asprintf kernel metadata"); - goto error; - } - - return lkm; - -error: - return NULL; -} - -/* - * Allocate and initialize a kernel stream. The stream is set to ACTIVE_FD by - * default. - * - * Return pointer to structure or NULL. - */ -struct ltt_kernel_stream *trace_kernel_create_stream(void) -{ - struct ltt_kernel_stream *lks; - - lks = malloc(sizeof(struct ltt_kernel_stream)); - if (lks == NULL) { - perror("kernel stream malloc"); - goto error; - } - - /* Init stream */ - lks->fd = 0; - lks->pathname = NULL; - lks->state = 0; - - return lks; - -error: - return NULL; -} - -/* - * Cleanup kernel stream structure. - */ -void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream) -{ - DBG("[trace] Closing stream fd %d", stream->fd); - /* Close kernel fd */ - close(stream->fd); - /* Remove from stream list */ - cds_list_del(&stream->list); - - free(stream->pathname); - free(stream); -} - -/* - * Cleanup kernel event structure. - */ -void trace_kernel_destroy_event(struct ltt_kernel_event *event) -{ - DBG("[trace] Closing event fd %d", event->fd); - /* Close kernel fd */ - close(event->fd); - - /* Remove from event list */ - cds_list_del(&event->list); - - free(event->event); - free(event->ctx); - free(event); -} - -/* - * Cleanup kernel channel structure. - */ -void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel) -{ - struct ltt_kernel_stream *stream, *stmp; - struct ltt_kernel_event *event, *etmp; - - DBG("[trace] Closing channel fd %d", channel->fd); - /* Close kernel fd */ - close(channel->fd); - - /* For each stream in the channel list */ - cds_list_for_each_entry_safe(stream, stmp, &channel->stream_list.head, list) { - trace_kernel_destroy_stream(stream); - } - - /* For each event in the channel list */ - cds_list_for_each_entry_safe(event, etmp, &channel->events_list.head, list) { - trace_kernel_destroy_event(event); - } - - /* Remove from channel list */ - cds_list_del(&channel->list); - - free(channel->pathname); - free(channel->channel); - free(channel->ctx); - free(channel); -} - -/* - * Cleanup kernel metadata structure. - */ -void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata) -{ - DBG("[trace] Closing metadata fd %d", metadata->fd); - /* Close kernel fd */ - close(metadata->fd); - - free(metadata->conf); - free(metadata->pathname); - free(metadata); -} - -/* - * Cleanup kernel session structure - */ -void trace_kernel_destroy_session(struct ltt_kernel_session *session) -{ - struct ltt_kernel_channel *channel, *ctmp; - - DBG("[trace] Closing session fd %d", session->fd); - /* Close kernel fds */ - close(session->fd); - - if (session->metadata_stream_fd != 0) { - DBG("[trace] Closing metadata stream fd %d", session->metadata_stream_fd); - close(session->metadata_stream_fd); - } - - if (session->metadata != NULL) { - trace_kernel_destroy_metadata(session->metadata); - } - - cds_list_for_each_entry_safe(channel, ctmp, &session->channel_list.head, list) { - trace_kernel_destroy_channel(channel); - } - - free(session->trace_path); - free(session); -} diff --git a/ltt-sessiond/trace-kernel.h b/ltt-sessiond/trace-kernel.h deleted file mode 100644 index bcea65148..000000000 --- a/ltt-sessiond/trace-kernel.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_TRACE_KERNEL_H -#define _LTT_TRACE_KERNEL_H - -#include -#include - -#include -#include - -/* Kernel event list */ -struct ltt_kernel_event_list { - struct cds_list_head head; -}; - -/* Channel stream list */ -struct ltt_kernel_stream_list { - struct cds_list_head head; -}; - -/* Channel list */ -struct ltt_kernel_channel_list { - struct cds_list_head head; -}; - -/* Kernel event */ -struct ltt_kernel_event { - int fd; - int enabled; - /* - * TODO: need internal representation to support more than a - * single context. - */ - struct lttng_kernel_context *ctx; - struct lttng_kernel_event *event; - struct cds_list_head list; -}; - -/* Kernel channel */ -struct ltt_kernel_channel { - int fd; - int enabled; - char *pathname; - unsigned int stream_count; - unsigned int event_count; - /* - * TODO: need internal representation to support more than a - * single context. - */ - struct lttng_kernel_context *ctx; - struct lttng_channel *channel; - struct ltt_kernel_event_list events_list; - struct ltt_kernel_stream_list stream_list; - struct cds_list_head list; -}; - -/* Metadata */ -struct ltt_kernel_metadata { - int fd; - char *pathname; - struct lttng_channel *conf; -}; - -/* Channel stream */ -struct ltt_kernel_stream { - int fd; - char *pathname; - int state; - struct cds_list_head list; -}; - -/* Kernel session */ -struct ltt_kernel_session { - int fd; - int metadata_stream_fd; - int consumer_fds_sent; - int consumer_fd; - unsigned int channel_count; - unsigned int stream_count_global; - char *trace_path; - struct ltt_kernel_metadata *metadata; - struct ltt_kernel_channel_list channel_list; -}; - -/* - * Lookup functions. NULL is returned if not found. - */ -struct ltt_kernel_event *trace_kernel_get_event_by_name( - char *name, struct ltt_kernel_channel *channel); -struct ltt_kernel_channel *trace_kernel_get_channel_by_name( - char *name, struct ltt_kernel_session *session); - -/* - * Create functions malloc() the data structure. - */ -struct ltt_kernel_session *trace_kernel_create_session(char *path); -struct ltt_kernel_channel *trace_kernel_create_channel(struct lttng_channel *chan, char *path); -struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev); -struct ltt_kernel_metadata *trace_kernel_create_metadata(char *path); -struct ltt_kernel_stream *trace_kernel_create_stream(void); - -/* - * Destroy functions free() the data structure and remove from linked list if - * it's applies. - */ -void trace_kernel_destroy_session(struct ltt_kernel_session *session); -void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata); -void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); -void trace_kernel_destroy_event(struct ltt_kernel_event *event); -void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); - -#endif /* _LTT_TRACE_KERNEL_H */ diff --git a/ltt-sessiond/trace-ust.c b/ltt-sessiond/trace-ust.c deleted file mode 100644 index e912c53c7..000000000 --- a/ltt-sessiond/trace-ust.c +++ /dev/null @@ -1,471 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include -#include - -#include "hashtable.h" -#include "trace-ust.h" - -/* - * Find the channel in the hashtable. - */ -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, - char *name) -{ - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - node = hashtable_lookup(ht, (void *) name, strlen(name), &iter); - if (node == NULL) { - rcu_read_unlock(); - goto error; - } - rcu_read_unlock(); - - DBG2("Trace UST channel %s found by name", name); - - return caa_container_of(node, struct ltt_ust_channel, node); - -error: - DBG2("Trace UST channel %s not found by name", name); - return NULL; -} - -/* - * Find the event in the hashtable. - */ -struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, - char *name) -{ - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - node = hashtable_lookup(ht, (void *) name, strlen(name), &iter); - if (node == NULL) { - rcu_read_unlock(); - goto error; - } - rcu_read_unlock(); - - DBG2("Found UST event by name %s", name); - - return caa_container_of(node, struct ltt_ust_event, node); - -error: - return NULL; -} - -/* - * Allocate and initialize a ust session data structure. - * - * Return pointer to structure or NULL. - */ -struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid, - struct lttng_domain *domain) -{ - int ret; - struct ltt_ust_session *lus; - - /* Allocate a new ltt ust session */ - lus = malloc(sizeof(struct ltt_ust_session)); - if (lus == NULL) { - perror("create ust session malloc"); - goto error; - } - - /* Init data structure */ - lus->consumer_fds_sent = 0; - lus->uid = uid; - - /* Alloc UST domain hash tables */ - lus->domain_pid = hashtable_new(0); - lus->domain_exec = hashtable_new_str(0); - - /* Alloc UST global domain channels' HT */ - lus->domain_global.channels = hashtable_new_str(0); - - /* Set session path */ - ret = snprintf(lus->pathname, PATH_MAX, "%s/ust", path); - if (ret < 0) { - PERROR("snprintf kernel traces path"); - goto error; - } - - DBG2("UST trace session create successful"); - - return lus; - -error: - return NULL; -} - -/* - * Allocate and initialize a ust channel data structure. - * - * Return pointer to structure or NULL. - */ -struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *chan, - char *path) -{ - int ret; - struct ltt_ust_channel *luc; - - luc = malloc(sizeof(struct ltt_ust_channel)); - if (luc == NULL) { - perror("ltt_ust_channel malloc"); - goto error; - } - - /* Copy UST channel attributes */ - luc->attr.overwrite = chan->attr.overwrite; - luc->attr.subbuf_size = chan->attr.subbuf_size; - luc->attr.num_subbuf = chan->attr.num_subbuf; - luc->attr.switch_timer_interval = chan->attr.switch_timer_interval; - luc->attr.read_timer_interval = chan->attr.read_timer_interval; - luc->attr.output = chan->attr.output; - - /* Translate to UST output enum */ - switch (luc->attr.output) { - default: - luc->attr.output = LTTNG_UST_MMAP; - break; - } - - /* Copy channel name */ - strncpy(luc->name, chan->name, sizeof(&luc->name)); - luc->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - - /* Init node */ - hashtable_node_init(&luc->node, (void *) luc->name, strlen(luc->name)); - /* Alloc hash tables */ - luc->events = hashtable_new_str(0); - luc->ctx = hashtable_new_str(0); - - /* Set trace output path */ - ret = snprintf(luc->pathname, PATH_MAX, "%s", path); - if (ret < 0) { - perror("asprintf ust create channel"); - goto error; - } - - DBG2("Trace UST channel %s created", luc->name); - - return luc; - -error: - return NULL; -} - -/* - * Allocate and initialize a ust event. Set name and event type. - * - * Return pointer to structure or NULL. - */ -struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev) -{ - struct ltt_ust_event *lue; - - lue = malloc(sizeof(struct ltt_ust_event)); - if (lue == NULL) { - PERROR("ust event malloc"); - goto error; - } - - switch (ev->type) { - case LTTNG_EVENT_PROBE: - lue->attr.instrumentation = LTTNG_UST_PROBE; - break; - case LTTNG_EVENT_FUNCTION: - lue->attr.instrumentation = LTTNG_UST_FUNCTION; - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - lue->attr.instrumentation = LTTNG_UST_FUNCTION; - break; - case LTTNG_EVENT_TRACEPOINT: - lue->attr.instrumentation = LTTNG_UST_TRACEPOINT; - break; - default: - ERR("Unknown ust instrumentation type (%d)", ev->type); - goto error; - } - - /* Copy event name */ - strncpy(lue->attr.name, ev->name, LTTNG_UST_SYM_NAME_LEN); - lue->attr.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; - - /* Init node */ - hashtable_node_init(&lue->node, (void *) lue->attr.name, - strlen(lue->attr.name)); - /* Alloc context hash tables */ - lue->ctx = hashtable_new_str(5); - - return lue; - -error: - return NULL; -} - -/* - * Allocate and initialize a ust metadata. - * - * Return pointer to structure or NULL. - */ -struct ltt_ust_metadata *trace_ust_create_metadata(char *path) -{ - int ret; - struct ltt_ust_metadata *lum; - - lum = malloc(sizeof(struct ltt_ust_metadata)); - if (lum == NULL) { - perror("ust metadata malloc"); - goto error; - } - - /* Set default attributes */ - lum->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE; - lum->attr.subbuf_size = DEFAULT_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.output = DEFAULT_UST_CHANNEL_OUTPUT; - - lum->handle = -1; - /* Set metadata trace path */ - ret = snprintf(lum->pathname, PATH_MAX, "%s/metadata", path); - if (ret < 0) { - perror("asprintf ust metadata"); - goto error; - } - - return lum; - -error: - return NULL; -} - -/* - * RCU safe free context structure. - */ -static void destroy_context_rcu(struct rcu_head *head) -{ - struct cds_lfht_node *node = - caa_container_of(head, struct cds_lfht_node, head); - struct ltt_ust_context *ctx = - caa_container_of(node, struct ltt_ust_context, node); - - free(ctx); -} - -/* - * Cleanup UST context hash table. - */ -static void destroy_context(struct cds_lfht *ht) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - hashtable_get_first(ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(ht, &iter); - if (!ret) { - call_rcu(&node->head, destroy_context_rcu); - } - hashtable_get_next(ht, &iter); - } -} - -/* - * Cleanup ust event structure. - */ -void trace_ust_destroy_event(struct ltt_ust_event *event) -{ - DBG2("Trace destroy UST event %s", event->attr.name); - destroy_context(event->ctx); - free(event); -} - -/* - * URCU intermediate call to complete destroy event. - */ -static void destroy_event_rcu(struct rcu_head *head) -{ - struct cds_lfht_node *node = - caa_container_of(head, struct cds_lfht_node, head); - struct ltt_ust_event *event = - caa_container_of(node, struct ltt_ust_event, node); - - trace_ust_destroy_event(event); -} - -/* - * Cleanup ust channel structure. - */ -void trace_ust_destroy_channel(struct ltt_ust_channel *channel) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - DBG2("Trace destroy UST channel %s", channel->name); - - rcu_read_lock(); - - hashtable_get_first(channel->events, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(channel->events, &iter); - if (!ret) { - call_rcu(&node->head, destroy_event_rcu); - } - hashtable_get_next(channel->events, &iter); - } - - free(channel->events); - destroy_context(channel->ctx); - free(channel); - - rcu_read_unlock(); -} - -/* - * URCU intermediate call to complete destroy channel. - */ -static void destroy_channel_rcu(struct rcu_head *head) -{ - struct cds_lfht_node *node = - caa_container_of(head, struct cds_lfht_node, head); - struct ltt_ust_channel *channel = - caa_container_of(node, struct ltt_ust_channel, node); - - trace_ust_destroy_channel(channel); -} - -/* - * Cleanup ust metadata structure. - */ -void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata) -{ - DBG2("Trace UST destroy metadata %d", metadata->handle); - - free(metadata); -} - -/* - * Iterate over a hash table containing channels and cleanup safely. - */ -static void destroy_channels(struct cds_lfht *channels) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - hashtable_get_first(channels, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(channels, &iter); - if (!ret) { - call_rcu(&node->head, destroy_channel_rcu); - } - hashtable_get_next(channels, &iter); - } -} - -/* - * Cleanup UST pid domain. - */ -static void destroy_domain_pid(struct cds_lfht *ht) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - struct ltt_ust_domain_pid *d; - - hashtable_get_first(ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(ht , &iter); - if (!ret) { - d = caa_container_of(node, struct ltt_ust_domain_pid, node); - destroy_channels(d->channels); - } - hashtable_get_next(ht, &iter); - } -} - -/* - * Cleanup UST exec name domain. - */ -static void destroy_domain_exec(struct cds_lfht *ht) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - struct ltt_ust_domain_exec *d; - - hashtable_get_first(ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(ht , &iter); - if (!ret) { - d = caa_container_of(node, struct ltt_ust_domain_exec, node); - destroy_channels(d->channels); - } - hashtable_get_next(ht, &iter); - } -} - -/* - * Cleanup UST global domain. - */ -static void destroy_domain_global(struct ltt_ust_domain_global *dom) -{ - destroy_channels(dom->channels); -} - -/* - * Cleanup ust session structure - */ -void trace_ust_destroy_session(struct ltt_ust_session *session) -{ - /* Extra safety */ - if (session == NULL) { - return; - } - - rcu_read_lock(); - - DBG2("Trace UST destroy session %d", session->uid); - - //if (session->metadata != NULL) { - // trace_ust_destroy_metadata(session->metadata); - //} - - /* Cleaning up UST domain */ - destroy_domain_global(&session->domain_global); - destroy_domain_pid(session->domain_pid); - destroy_domain_exec(session->domain_exec); - - free(session); - - rcu_read_unlock(); -} diff --git a/ltt-sessiond/trace-ust.h b/ltt-sessiond/trace-ust.h deleted file mode 100644 index aea7e714f..000000000 --- a/ltt-sessiond/trace-ust.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_TRACE_UST_H -#define _LTT_TRACE_UST_H - -#include -#include -#include -#include -#include - -#include "ust-ctl.h" - -#include "../hashtable/rculfhash.h" - -/* UST Stream list */ -struct ltt_ust_stream_list { - unsigned int count; - struct cds_list_head head; -}; - -/* Context hash table nodes */ -struct ltt_ust_context { - struct lttng_ust_context ctx; - struct cds_lfht_node node; -}; - -/* UST event */ -struct ltt_ust_event { - struct lttng_ust_event attr; - struct cds_lfht *ctx; - struct cds_lfht_node node; -}; - -/* UST stream */ -struct ltt_ust_stream { - int handle; - char pathname[PATH_MAX]; - struct lttng_ust_object_data *obj; - struct cds_lfht_node node; -}; - -/* UST channel */ -struct ltt_ust_channel { - char name[LTTNG_UST_SYM_NAME_LEN]; - char pathname[PATH_MAX]; - struct lttng_ust_channel attr; - struct cds_lfht *ctx; - struct cds_lfht *events; - struct cds_lfht_node node; -}; - -/* UST Metadata */ -struct ltt_ust_metadata { - int handle; - struct lttng_ust_object_data *obj; - char pathname[PATH_MAX]; /* Trace file path name */ - struct lttng_ust_channel attr; - struct lttng_ust_object_data *stream_obj; -}; - -/* UST domain global (LTTNG_DOMAIN_UST) */ -struct ltt_ust_domain_global { - struct cds_lfht *channels; -}; - -/* UST domain pid (LTTNG_DOMAIN_UST_PID) */ -struct ltt_ust_domain_pid { - pid_t pid; - struct cds_lfht *channels; - struct cds_lfht_node node; -}; - -/* UST domain exec name (LTTNG_DOMAIN_UST_EXEC_NAME) */ -struct ltt_ust_domain_exec { - char exec_name[LTTNG_UST_SYM_NAME_LEN]; - struct cds_lfht *channels; - struct cds_lfht_node node; -}; - -/* UST session */ -struct ltt_ust_session { - int uid; /* Unique identifier of session */ - int consumer_fds_sent; - int consumer_fd; - char pathname[PATH_MAX]; - struct ltt_ust_domain_global domain_global; - /* - * Those two hash tables contains data for a specific UST domain and each - * contains a HT of channels. See ltt_ust_domain_exec and - * ltt_ust_domain_pid data structures. - */ - struct cds_lfht *domain_pid; - struct cds_lfht *domain_exec; -}; - -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST - -/* - * Lookup functions. NULL is returned if not found. - */ -struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, - char *name); -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, - char *name); - -/* - * Create functions malloc() the data structure. - */ -struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid, - struct lttng_domain *domain); -struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, - char *path); -struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev); -struct ltt_ust_metadata *trace_ust_create_metadata(char *path); - -/* - * Destroy functions free() the data structure and remove from linked list if - * it's applies. - */ -void trace_ust_destroy_session(struct ltt_ust_session *session); -void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata); -void trace_ust_destroy_channel(struct ltt_ust_channel *channel); -void trace_ust_destroy_event(struct ltt_ust_event *event); - -#else - -static inline -struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, - char *name) -{ - return NULL; -} - -static inline -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, - char *name) -{ - return NULL; -} - -static inline -struct ltt_ust_session *trace_ust_create_session(char *path, pid_t pid, - struct lttng_domain *domain) -{ - return NULL; -} -static inline -struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, - char *path) -{ - return NULL; -} -static inline -struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev) -{ - return NULL; -} -static inline -struct ltt_ust_metadata *trace_ust_create_metadata(char *path) -{ - return NULL; -} - -static inline -void trace_ust_destroy_session(struct ltt_ust_session *session) -{ -} - -static inline -void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata) -{ -} - -static inline -void trace_ust_destroy_channel(struct ltt_ust_channel *channel) -{ -} - -static inline -void trace_ust_destroy_event(struct ltt_ust_event *event) -{ -} - -#endif /* CONFIG_CONFIG_LTTNG_TOOLS_HAVE_UST */ - -#endif /* _LTT_TRACE_UST_H */ diff --git a/ltt-sessiond/ust-app.c b/ltt-sessiond/ust-app.c deleted file mode 100644 index 489bd8b2f..000000000 --- a/ltt-sessiond/ust-app.c +++ /dev/null @@ -1,831 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "hashtable.h" -#include "ust-app.h" -#include "../hashtable/hash.h" -#include "ust-ctl.h" -#include "ust-consumer.h" - -/* - * Delete a traceable application structure from the global list. - */ -static void delete_ust_app(struct ust_app *lta) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - - free(lta->sessions); - close(lta->key.sock); - - /* Remove from apps hash table */ - node = hashtable_lookup(ust_app_ht, - (void *) ((unsigned long) lta->key.pid), sizeof(void *), &iter); - if (node == NULL) { - ERR("UST app pid %d not found in hash table", lta->key.pid); - } else { - ret = hashtable_del(ust_app_ht, &iter); - if (ret) { - ERR("UST app unable to delete app %d from hash table", - lta->key.pid); - } else { - DBG2("UST app pid %d deleted", lta->key.pid); - } - } - - /* Remove from key hash table */ - node = hashtable_lookup(ust_app_sock_key_map, - (void *) ((unsigned long) lta->key.sock), sizeof(void *), &iter); - if (node == NULL) { - ERR("UST app key %d not found in key hash table", lta->key.sock); - } else { - ret = hashtable_del(ust_app_sock_key_map, &iter); - if (ret) { - ERR("UST app unable to delete app sock %d from key hash table", - lta->key.sock); - } else { - DBG2("UST app pair sock %d key %d deleted", - lta->key.sock, lta->key.pid); - } - } - - free(lta); - - rcu_read_unlock(); -} - -/* - * URCU intermediate call to delete an UST app. - */ -static void delete_ust_app_rcu(struct rcu_head *head) -{ - struct cds_lfht_node *node = - caa_container_of(head, struct cds_lfht_node, head); - struct ust_app *app = - caa_container_of(node, struct ust_app, node); - - delete_ust_app(app); -} - -/* - * Find an ust_app using the sock and return it. - */ -static struct ust_app *find_app_by_sock(int sock) -{ - struct cds_lfht_node *node; - struct ust_app_key *key; - struct cds_lfht_iter iter; - - rcu_read_lock(); - - node = hashtable_lookup(ust_app_sock_key_map, - (void *)((unsigned long) sock), sizeof(void *), &iter); - if (node == NULL) { - DBG2("UST app find by sock %d key not found", sock); - rcu_read_unlock(); - goto error; - } - - key = caa_container_of(node, struct ust_app_key, node); - - node = hashtable_lookup(ust_app_ht, - (void *)((unsigned long) key->pid), sizeof(void *), &iter); - if (node == NULL) { - DBG2("UST app find by sock %d not found", sock); - rcu_read_unlock(); - goto error; - } - rcu_read_unlock(); - - return caa_container_of(node, struct ust_app, node); - -error: - return NULL; -} - -/* - * Return pointer to traceable apps list. - */ -struct cds_lfht *ust_app_get_ht(void) -{ - return ust_app_ht; -} - -/* - * Return ust app pointer or NULL if not found. - */ -struct ust_app *ust_app_find_by_pid(pid_t pid) -{ - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - rcu_read_lock(); - node = hashtable_lookup(ust_app_ht, - (void *)((unsigned long) pid), sizeof(void *), &iter); - if (node == NULL) { - rcu_read_unlock(); - 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, node); - -error: - return NULL; -} - -/* - * Using pid and uid (of the app), allocate a new ust_app struct and - * add it to the global traceable app list. - * - * On success, return 0, else return malloc ENOMEM. - */ -int ust_app_register(struct ust_register_msg *msg, int sock) -{ - struct ust_app *lta; - - lta = malloc(sizeof(struct ust_app)); - if (lta == NULL) { - PERROR("malloc"); - return -ENOMEM; - } - - lta->uid = msg->uid; - lta->gid = msg->gid; - lta->key.pid = msg->pid; - lta->ppid = msg->ppid; - lta->v_major = msg->major; - lta->v_minor = msg->minor; - lta->key.sock = sock; - strncpy(lta->name, msg->name, sizeof(lta->name)); - lta->name[16] = '\0'; - hashtable_node_init(<a->node, (void *)((unsigned long)lta->key.pid), - sizeof(void *)); - - /* Session hashtable */ - lta->sessions = hashtable_new(0); - - /* Set sock key map */ - hashtable_node_init(<a->key.node, (void *)((unsigned long)lta->key.sock), - sizeof(void *)); - - rcu_read_lock(); - hashtable_add_unique(ust_app_ht, <a->node); - hashtable_add_unique(ust_app_sock_key_map, <a->key.node); - rcu_read_unlock(); - - DBG("App registered with pid:%d ppid:%d uid:%d gid:%d sock:%d name:%s" - " (version %d.%d)", lta->key.pid, lta->ppid, lta->uid, lta->gid, - lta->key.sock, lta->name, lta->v_major, lta->v_minor); - - return 0; -} - -/* - * Unregister app by removing it from the global traceable app list and freeing - * the data struct. - * - * The socket is already closed at this point so no close to sock. - */ -void ust_app_unregister(int sock) -{ - struct ust_app *lta; - - DBG2("UST app unregistering sock %d", sock); - - lta = find_app_by_sock(sock); - if (lta) { - DBG("PID %d unregistering with sock %d", lta->key.pid, sock); - /* FIXME: Better use a call_rcu here ? */ - delete_ust_app(lta); - } -} - -/* - * Return traceable_app_count - */ -unsigned long ust_app_list_count(void) -{ - unsigned long count; - - rcu_read_lock(); - count = hashtable_get_count(ust_app_ht); - rcu_read_unlock(); - - return count; -} - -/* - * Free and clean all traceable apps of the global list. - */ -void ust_app_clean_list(void) -{ - int ret; - struct cds_lfht_node *node; - struct cds_lfht_iter iter; - - DBG2("UST app clean hash table"); - - rcu_read_lock(); - - hashtable_get_first(ust_app_ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - ret = hashtable_del(ust_app_ht, &iter); - if (!ret) { - call_rcu(&node->head, delete_ust_app_rcu); - } - hashtable_get_next(ust_app_ht, &iter); - } - - rcu_read_unlock(); -} - -/* - * Init UST app hash table. - */ -void ust_app_ht_alloc(void) -{ - ust_app_ht = hashtable_new(0); - ust_app_sock_key_map = hashtable_new(0); -} - -/* - * Alloc new UST app session. - */ -static struct ust_app_session *alloc_app_session(void) -{ - struct ust_app_session *ua_sess; - - ua_sess = zmalloc(sizeof(struct ust_app_session)); - if (ua_sess == NULL) { - PERROR("malloc"); - goto error; - } - - ua_sess->enabled = 0; - ua_sess->handle = -1; - ua_sess->channels = hashtable_new_str(0); - ua_sess->metadata = NULL; - ua_sess->obj = NULL; - - return ua_sess; - -error: - return NULL; -} - -static struct ust_app_channel *alloc_app_channel(char *name) -{ - struct ust_app_channel *ua_chan; - - ua_chan = zmalloc(sizeof(struct ust_app_channel)); - if (ua_chan == NULL) { - PERROR("malloc"); - goto error; - } - - strncpy(ua_chan->name, name, sizeof(ua_chan->name)); - ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; - ua_chan->enabled = 0; - ua_chan->handle = -1; - ua_chan->obj = NULL; - ua_chan->ctx = hashtable_new(0); - ua_chan->streams = hashtable_new(0); - ua_chan->events = hashtable_new_str(0); - hashtable_node_init(&ua_chan->node, (void *) ua_chan->name, - strlen(ua_chan->name)); - - DBG3("UST app channel %s allocated", ua_chan->name); - - return ua_chan; - -error: - return NULL; -} - -static struct ust_app_event *alloc_app_event(char *name) -{ - struct ust_app_event *ua_event; - - ua_event = zmalloc(sizeof(struct ust_app_event)); - if (ua_event == NULL) { - PERROR("malloc"); - goto error; - } - - strncpy(ua_event->name, name, sizeof(ua_event->name)); - ua_event->name[sizeof(ua_event->name) - 1] = '\0'; - ua_event->ctx = hashtable_new(0); - hashtable_node_init(&ua_event->node, (void *) ua_event->name, - strlen(ua_event->name)); - - DBG3("UST app event %s allocated", ua_event->name); - - return ua_event; - -error: - return NULL; -} - -static void shallow_copy_event(struct ust_app_event *ua_event, - struct ltt_ust_event *uevent) -{ - strncpy(ua_event->name, uevent->attr.name, sizeof(ua_event->name)); - ua_event->name[sizeof(ua_event->name) - 1] = '\0'; - - /* TODO: support copy context */ -} - -static void shallow_copy_channel(struct ust_app_channel *ua_chan, - struct ltt_ust_channel *uchan) -{ - struct cds_lfht_iter iter; - struct cds_lfht_node *node, *ua_event_node; - struct ltt_ust_event *uevent; - struct ust_app_event *ua_event; - - DBG2("Shallow copy of UST app channel %s", ua_chan->name); - - strncpy(ua_chan->name, uchan->name, sizeof(ua_chan->name)); - ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; - - /* TODO: support copy context */ - - hashtable_get_first(uchan->events, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - uevent = caa_container_of(node, struct ltt_ust_event, node); - - ua_event_node = hashtable_lookup(ua_chan->events, - (void *) uevent->attr.name, strlen(uevent->attr.name), &iter); - if (ua_event_node == NULL) { - DBG2("UST event %s not found on shallow copy channel", - uevent->attr.name); - ua_event = alloc_app_event(uevent->attr.name); - if (ua_event == NULL) { - continue; - } - hashtable_add_unique(ua_chan->events, &ua_event->node); - } else { - ua_event = caa_container_of(node, struct ust_app_event, node); - } - - shallow_copy_event(ua_event, uevent); - - /* Get next UST events */ - hashtable_get_next(uchan->events, &iter); - } - - DBG3("Shallow copy channel done"); -} - -static void shallow_copy_session(struct ust_app_session *ua_sess, - struct ltt_ust_session *usess) -{ - struct cds_lfht_node *node, *ua_chan_node; - struct cds_lfht_iter iter; - struct ltt_ust_channel *uchan; - struct ust_app_channel *ua_chan; - - DBG2("Shallow copy of session handle"); - - ua_sess->uid = usess->uid; - - /* TODO: support all UST domain */ - - /* Iterate over all channels in global domain. */ - hashtable_get_first(usess->domain_global.channels, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - uchan = caa_container_of(node, struct ltt_ust_channel, node); - - ua_chan_node = hashtable_lookup(ua_sess->channels, - (void *) uchan->name, strlen(uchan->name), &iter); - if (ua_chan_node == NULL) { - DBG2("Channel %s not found on shallow session copy, creating it", - uchan->name); - ua_chan = alloc_app_channel(uchan->name); - if (ua_chan == NULL) { - /* malloc failed... continuing */ - continue; - } - hashtable_add_unique(ua_sess->channels, &ua_chan->node); - } else { - ua_chan = caa_container_of(node, struct ust_app_channel, node); - } - - shallow_copy_channel(ua_chan, uchan); - - /* Next item in hash table */ - hashtable_get_next(usess->domain_global.channels, &iter); - } -} - -static struct ust_app_session *lookup_session_by_app( - struct ltt_ust_session *usess, struct ust_app *app) -{ - struct cds_lfht_iter iter; - struct cds_lfht_node *node; - - /* Get right UST app session from app */ - node = hashtable_lookup(app->sessions, - (void *) ((unsigned long) usess->uid), - sizeof(void *), &iter); - if (node == NULL) { - goto error; - } - - return caa_container_of(node, struct ust_app_session, node); - -error: - return NULL; -} - -int ust_app_add_channel(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - int ret = 0; - struct cds_lfht_iter iter; - struct cds_lfht_node *node, *ua_chan_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - - DBG2("UST app adding channel %s to global domain for session uid %d", - uchan->name, usess->uid); - - rcu_read_lock(); - hashtable_get_first(ust_app_ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - app = caa_container_of(node, struct ust_app, node); - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - DBG2("UST app pid: %d session uid %d not found, creating one", - app->key.pid, usess->uid); - ua_sess = alloc_app_session(); - if (ua_sess == NULL) { - /* Only malloc can failed so something is really wrong */ - goto next; - } - shallow_copy_session(ua_sess, usess); - } - - if (ua_sess->handle == -1) { - ret = ustctl_create_session(app->key.sock); - if (ret < 0) { - DBG("Error creating session for app pid %d, sock %d", - app->key.pid, app->key.sock); - /* TODO: free() ua_sess */ - goto next; - } - - DBG2("UST app ustctl create session handle %d", ret); - ua_sess->handle = ret; - - /* Add ust app session to app's HT */ - hashtable_node_init(&ua_sess->node, - (void *)((unsigned long) ua_sess->uid), sizeof(void *)); - hashtable_add_unique(app->sessions, &ua_sess->node); - } - - /* Lookup channel in the ust app session */ - ua_chan_node = hashtable_lookup(ua_sess->channels, - (void *) uchan->name, strlen(uchan->name), &iter); - if (ua_chan_node == NULL) { - ERR("Channel suppose to be present with the above shallow " - "session copy. Continuing..."); - goto next; - } - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - /* TODO: remove cast and use lttng-ust-abi.h */ - ret = ustctl_create_channel(app->key.sock, ua_sess->handle, - (struct lttng_ust_channel_attr *)&uchan->attr, &ua_chan->obj); - if (ret < 0) { - DBG("Error creating channel %s for app (pid: %d, sock: %d) " - "and session handle %d with ret %d", - uchan->name, app->key.pid, app->key.sock, - ua_sess->handle, ret); - goto next; - } - - ua_chan->handle = ua_chan->obj->handle; - ua_chan->attr.shm_fd = ua_chan->obj->shm_fd; - ua_chan->attr.wait_fd = ua_chan->obj->wait_fd; - ua_chan->attr.memory_map_size = ua_chan->obj->memory_map_size; - - DBG2("Channel %s UST create successfully for pid:%d and sock:%d", - uchan->name, app->key.pid, app->key.sock); - -next: - /* Next applications */ - hashtable_get_next(ust_app_ht, &iter); - } - rcu_read_unlock(); - - return ret; -} - -int ust_app_add_event(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - int ret = 0; - struct cds_lfht_iter iter; - struct cds_lfht_node *node, *ua_chan_node, *ua_event_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - struct ust_app_event *ua_event; - struct lttng_ust_event ltt_uevent; - struct lttng_ust_object_data *obj_event; - - DBG2("UST app adding event %s to global domain for session uid %d", - uevent->attr.name, usess->uid); - - rcu_read_lock(); - hashtable_get_first(ust_app_ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - app = caa_container_of(node, struct ust_app, node); - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - DBG2("UST app (pid: %d, sock: %d) session not found, creating one", - app->key.pid, app->key.sock); - ua_sess = alloc_app_session(); - if (ua_sess == NULL) { - /* Only malloc can failed so something is really wrong */ - goto next; - } - shallow_copy_session(ua_sess, usess); - } - - if (ua_sess->handle == -1) { - ret = ustctl_create_session(app->key.sock); - if (ret < 0) { - DBG("Error creating session for app pid %d, sock %d", - app->key.pid, app->key.sock); - /* TODO: free() ua_sess */ - goto next; - } - - DBG2("UST app ustctl create session handle %d", ret); - ua_sess->handle = ret; - /* Add ust app session to app's HT */ - hashtable_node_init(&ua_sess->node, - (void *)((unsigned long) ua_sess->uid), sizeof(void *)); - hashtable_add_unique(app->sessions, &ua_sess->node); - } - - /* Lookup channel in the ust app session */ - ua_chan_node = hashtable_lookup(ua_sess->channels, - (void *) uchan->name, strlen(uchan->name), &iter); - if (ua_chan_node == NULL) { - ERR("Channel suppose to be present with the above shallow " - "session copy. Continuing..."); - goto next; - } - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - /* Prepare lttng ust event */ - strncpy(ltt_uevent.name, uevent->attr.name, sizeof(ltt_uevent.name)); - ltt_uevent.name[sizeof(ltt_uevent.name) - 1] = '\0'; - /* TODO: adjust to other instrumentation types */ - ltt_uevent.instrumentation = LTTNG_UST_TRACEPOINT; - - /* Get event node */ - ua_event_node = hashtable_lookup(ua_chan->events, - (void *) uevent->attr.name, strlen(uevent->attr.name), &iter); - if (ua_event_node == NULL) { - DBG2("UST app event %s not found, creating one", uevent->attr.name); - /* Does not exist so create one */ - ua_event = alloc_app_event(uevent->attr.name); - if (ua_event == NULL) { - /* Only malloc can failed so something is really wrong */ - goto next; - } - - shallow_copy_event(ua_event, uevent); - - /* Create UST event on tracer */ - ret = ustctl_create_event(app->key.sock, <t_uevent, ua_chan->obj, - &obj_event); - if (ret < 0) { - ERR("Error ustctl create event %s for app pid: %d with ret %d", - uevent->attr.name, app->key.pid, ret); - /* TODO: free() ua_event and obj_event */ - goto next; - } - ua_event->obj = obj_event; - ua_event->handle = obj_event->handle; - ua_event->enabled = 1; - } else { - ua_event = caa_container_of(ua_event_node, - struct ust_app_event, node); - - if (ua_event->enabled == 0) { - ret = ustctl_enable(app->key.sock, ua_event->obj); - if (ret < 0) { - ERR("Error ustctl enable event %s for app " - "pid: %d with ret %d", uevent->attr.name, - app->key.pid, ret); - goto next; - } - ua_event->enabled = 1; - } - } - - hashtable_add_unique(ua_chan->events, &ua_event->node); - - DBG2("Event %s UST create successfully for pid:%d", uevent->attr.name, - app->key.pid); - -next: - /* Next applications */ - hashtable_get_next(ust_app_ht, &iter); - } - rcu_read_unlock(); - - return ret; -} - -int ust_app_start_trace(struct ltt_ust_session *usess) -{ - int ret = 0; - struct cds_lfht_iter iter; - struct cds_lfht_node *node, *ua_chan_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - struct lttng_ust_channel_attr uattr; - struct ltt_ust_channel *uchan; - - rcu_read_lock(); - hashtable_get_first(ust_app_ht, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - app = caa_container_of(node, struct ust_app, node); - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - /* Only malloc can failed so something is really wrong */ - goto next; - } - - if (ua_sess->metadata == NULL) { - /* Allocate UST metadata */ - ua_sess->metadata = trace_ust_create_metadata(usess->pathname); - if (ua_sess->metadata == NULL) { - ERR("UST app session %d creating metadata failed", - ua_sess->handle); - goto next; - } - - uattr.overwrite = ua_sess->metadata->attr.overwrite; - uattr.subbuf_size = ua_sess->metadata->attr.subbuf_size; - uattr.num_subbuf = ua_sess->metadata->attr.num_subbuf; - uattr.switch_timer_interval = - ua_sess->metadata->attr.switch_timer_interval; - uattr.read_timer_interval = - ua_sess->metadata->attr.read_timer_interval; - uattr.output = ua_sess->metadata->attr.output; - - /* UST tracer metadata creation */ - ret = ustctl_open_metadata(app->key.sock, ua_sess->handle, &uattr, - &ua_sess->metadata->obj); - if (ret < 0) { - ERR("UST app open metadata failed for app pid:%d", - app->key.pid); - goto next; - } - - DBG2("UST metadata opened for app pid %d", app->key.pid); - } - - /* Open UST metadata stream */ - if (ua_sess->metadata->stream_obj == NULL) { - ret = ustctl_create_stream(app->key.sock, ua_sess->metadata->obj, - &ua_sess->metadata->stream_obj); - if (ret < 0) { - ERR("UST create metadata stream failed"); - goto next; - } - - ret = snprintf(ua_sess->metadata->pathname, PATH_MAX, "%s/%s", - usess->pathname, "metadata"); - if (ret < 0) { - PERROR("asprintf UST create stream"); - goto next; - } - - DBG2("UST metadata stream object created for app pid %d", - app->key.pid); - } - - /* For each channel */ - hashtable_get_first(usess->domain_global.channels, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - uchan = caa_container_of(node, struct ltt_ust_channel, node); - - /* Lookup channel in the ust app session */ - ua_chan_node = hashtable_lookup(ua_sess->channels, - (void *) uchan->name, strlen(uchan->name), &iter); - if (ua_chan_node == NULL) { - ERR("Channel suppose to be present with the above shallow " - "session copy. Continuing..."); - goto next; - } - - ua_chan = caa_container_of(ua_chan_node, - struct ust_app_channel, node); - - struct ltt_ust_stream *ustream; - - ustream = malloc(sizeof(*ustream)); - if (ustream == NULL) { - goto next_chan; - } - - memset(ustream, 0, sizeof(struct ltt_ust_stream)); - - ret = ustctl_create_stream(app->key.sock, ua_chan->obj, - &ustream->obj); - if (ret < 0) { - ERR("Creating channel stream failed"); - goto next_chan; - } - - ustream->handle = ustream->obj->handle; - - hashtable_node_init(&ustream->node, - (void *)((unsigned long) ustream->handle), sizeof(void *)); - hashtable_add_unique(ua_chan->streams, &ustream->node); - - ret = snprintf(ustream->pathname, PATH_MAX, "%s/%s_%lu", - uchan->pathname, uchan->name, - hashtable_get_count(ua_chan->streams)); - if (ret < 0) { - PERROR("asprintf UST create stream"); - goto next_chan; - } - -next_chan: - /* Next applications */ - hashtable_get_next(ua_sess->channels, &iter); - } - - /* Setup UST consumer socket and send fds to it */ - printf("WTF HERE: sock: %d\n", usess->consumer_fd); - ret = ust_consumer_send_session(usess->consumer_fd, ua_sess); - if (ret < 0) { - goto next; - } - - /* This start the UST tracing */ - ret = ustctl_start_session(app->key.sock, ua_sess->handle); - if (ret < 0) { - ERR("Error starting tracing for app pid: %d", app->key.pid); - goto next; - } - - /* Quiescent wait after starting trace */ - ustctl_wait_quiescent(app->key.sock); -next: - /* Next applications */ - hashtable_get_next(ust_app_ht, &iter); - } - rcu_read_unlock(); - - return 0; -} diff --git a/ltt-sessiond/ust-app.h b/ltt-sessiond/ust-app.h deleted file mode 100644 index 46fb65427..000000000 --- a/ltt-sessiond/ust-app.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_UST_APP_H -#define _LTT_UST_APP_H - -#include -#include - -#include "trace-ust.h" - -/* - * Application registration data structure. - */ -struct ust_register_msg { - uint32_t major; - uint32_t minor; - pid_t pid; - pid_t ppid; - uid_t uid; - gid_t gid; - char name[16]; -}; - -/* - * Global applications HT used by the session daemon. - */ -struct cds_lfht *ust_app_ht; - -struct cds_lfht *ust_app_sock_key_map; - -struct ust_app_key { - pid_t pid; - int sock; - struct cds_lfht_node node; -}; - -struct ust_app_event { - int enabled; - int handle; - struct lttng_ust_object_data *obj; - char name[LTTNG_UST_SYM_NAME_LEN]; - struct cds_lfht *ctx; - struct cds_lfht_node node; -}; - -struct ust_app_channel { - int enabled; - int handle; - char name[LTTNG_UST_SYM_NAME_LEN]; - struct lttng_ust_channel attr; - struct lttng_ust_object_data *obj; - struct cds_lfht *streams; - struct cds_lfht *ctx; - struct cds_lfht *events; - struct cds_lfht_node node; -}; - -struct ust_app_session { - int enabled; - int handle; /* Used has unique identifier */ - unsigned int uid; - struct ltt_ust_metadata *metadata; - struct lttng_ust_object_data *obj; - struct cds_lfht *channels; /* Registered channels */ - struct cds_lfht_node node; -}; - -/* - * Registered traceable applications. Libust registers to the session daemon - * and a linked list is kept of all running traceable app. - */ -struct ust_app { - pid_t ppid; - uid_t uid; /* User ID that owns the apps */ - gid_t gid; /* Group ID that owns the apps */ - uint32_t v_major; /* Verion major number */ - uint32_t v_minor; /* Verion minor number */ - char name[17]; /* Process name (short) */ - struct cds_lfht *sessions; - struct cds_lfht_node node; - struct ust_app_key key; -}; - -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST - -int ust_app_register(struct ust_register_msg *msg, int sock); -void ust_app_unregister(int sock); -int ust_app_add_channel(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_add_event(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -unsigned long ust_app_list_count(void); -int ust_app_start_trace(struct ltt_ust_session *usess); - -void ust_app_clean_list(void); -void ust_app_ht_alloc(void); -struct cds_lfht *ust_app_get_ht(void); -struct ust_app *ust_app_find_by_pid(pid_t pid); - -#else - -static inline -int ust_app_register(struct ust_register_msg *msg, int sock) -{ - return -ENOSYS; -} -static inline -void ust_app_unregister(int sock) -{ -} -static inline -unsigned int ust_app_list_count(void) -{ - return 0; -} - -static inline -void ust_app_lock_list(void) -{ -} -static inline -void ust_app_unlock_list(void) -{ -} -static inline -void ust_app_clean_list(void) -{ -} -static inline -struct ust_app_list *ust_app_get_list(void) -{ - return NULL; -} -static inline -struct ust_app *ust_app_get_by_pid(pid_t pid) -{ - return NULL; -} - -static inline -int ust_app_add_channel(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} - -#endif /* CONFIG_LTTNG_TOOLS_HAVE_UST */ - -#endif /* _LTT_UST_APP_H */ diff --git a/ltt-sessiond/ust-consumer.c b/ltt-sessiond/ust-consumer.c deleted file mode 100644 index b3b97552e..000000000 --- a/ltt-sessiond/ust-consumer.c +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "hashtable.h" -#include "ust-consumer.h" - -/* - * Send all stream fds of UST channel to the consumer. - */ -static int send_channel_streams(int sock, - struct ust_app_channel *uchan) -{ - int ret, fds[2]; - struct ltt_ust_stream *stream; - struct lttcomm_consumer_msg lum; - struct cds_lfht_iter iter; - struct cds_lfht_node *node; - - DBG("Sending streams of channel %s to UST consumer", uchan->name); - - /* Send channel */ - lum.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - - /* - * We need to keep shm_fd open to make sure this key stays unique within - * the session daemon. - */ - lum.u.channel.channel_key = uchan->obj->shm_fd; - lum.u.channel.max_sb_size = uchan->attr.subbuf_size; - lum.u.channel.mmap_len = uchan->obj->memory_map_size; - DBG("Sending channel %d to consumer", lum.u.channel.channel_key); - ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - fds[0] = uchan->obj->shm_fd; - fds[1] = uchan->obj->wait_fd; - ret = lttcomm_send_fds_unix_sock(sock, fds, 2); - if (ret < 0) { - perror("send consumer channel ancillary data"); - goto error; - } - - - rcu_read_lock(); - hashtable_get_first(uchan->streams, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - stream = caa_container_of(node, struct ltt_ust_stream, node); - - int fds[2]; - - if (!stream->obj->shm_fd) { - goto next; - } - - lum.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lum.u.stream.channel_key = uchan->obj->shm_fd; - lum.u.stream.stream_key = stream->obj->shm_fd; - lum.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; - lum.u.stream.output = uchan->attr.output; - lum.u.stream.mmap_len = stream->obj->memory_map_size; - strncpy(lum.u.stream.path_name, stream->pathname, PATH_MAX - 1); - lum.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending stream %d to consumer", lum.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - - fds[0] = stream->obj->shm_fd; - fds[1] = stream->obj->wait_fd; - ret = lttcomm_send_fds_unix_sock(sock, fds, 2); - if (ret < 0) { - perror("send consumer stream ancillary data"); - goto error; - } - -next: - hashtable_get_next(uchan->streams, &iter); - } - rcu_read_unlock(); - - DBG("consumer channel streams sent"); - - return 0; - -error: - return ret; -} - -/* - * Send all stream fds of the UST session to the consumer. - */ -int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess) -{ - int ret = 0; - int sock = consumer_fd; - struct cds_lfht_iter iter; - struct cds_lfht_node *node; - struct lttcomm_consumer_msg lum; - struct ust_app_channel *uchan; - - DBG("Sending metadata stream fd"); - - if (usess->metadata->obj->shm_fd != 0) { - int fds[2]; - - /* Send metadata channel fd */ - lum.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; - lum.u.channel.channel_key = usess->metadata->obj->shm_fd; - lum.u.channel.max_sb_size = usess->metadata->attr.subbuf_size; - lum.u.channel.mmap_len = 0; /* for kernel */ - DBG("Sending metadata channel %d to consumer", lum.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); - if (ret < 0) { - perror("send consumer channel"); - goto error; - } - fds[0] = usess->metadata->obj->shm_fd; - fds[1] = usess->metadata->obj->wait_fd; - ret = lttcomm_send_fds_unix_sock(sock, fds, 2); - if (ret < 0) { - perror("send consumer metadata channel"); - goto error; - } - - /* Send metadata stream fd */ - lum.cmd_type = LTTNG_CONSUMER_ADD_STREAM; - lum.u.stream.channel_key = usess->metadata->obj->shm_fd; - lum.u.stream.stream_key = usess->metadata->stream_obj->shm_fd; - lum.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; - lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT; - lum.u.stream.mmap_len = usess->metadata->stream_obj->memory_map_size; - strncpy(lum.u.stream.path_name, usess->metadata->pathname, PATH_MAX - 1); - lum.u.stream.path_name[PATH_MAX - 1] = '\0'; - DBG("Sending metadata stream %d to consumer", lum.u.stream.stream_key); - ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); - if (ret < 0) { - perror("send consumer metadata stream"); - goto error; - } - fds[0] = usess->metadata->stream_obj->shm_fd; - fds[1] = usess->metadata->stream_obj->wait_fd; - ret = lttcomm_send_fds_unix_sock(sock, fds, 2); - if (ret < 0) { - perror("send consumer stream"); - goto error; - } - } - - /* Send each channel fd streams of session */ - rcu_read_lock(); - hashtable_get_first(usess->channels, &iter); - while ((node = hashtable_iter_get_node(&iter)) != NULL) { - uchan = caa_container_of(node, struct ust_app_channel, node); - - ret = send_channel_streams(sock, uchan); - if (ret < 0) { - goto error; - } - hashtable_get_next(usess->channels, &iter); - } - rcu_read_unlock(); - - DBG("consumer fds (metadata and channel streams) sent"); - - return 0; - -error: - return ret; -} diff --git a/ltt-sessiond/ust-consumer.h b/ltt-sessiond/ust-consumer.h deleted file mode 100644 index 85169e6b8..000000000 --- a/ltt-sessiond/ust-consumer.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _UST_CONSUMER_H -#define _UST_CONSUMER_H - -#include "ust-app.h" - -int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess); - -#endif /* _UST_CONSUMER_H */ diff --git a/ltt-sessiond/ust-ctl.c b/ltt-sessiond/ust-ctl.c deleted file mode 100644 index 6f73bfc66..000000000 --- a/ltt-sessiond/ust-ctl.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; only version 2 - * of the License. - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include - -#include -#include - -#include "ust-comm.h" -#include "ust-ctl.h" -#include "../hashtable/hash.h" - -/* - * Init command for tracer with cmd type and correct handle. - */ -static void init_command(int cmd, int handle, struct lttcomm_ust_msg *command) -{ - memset(command, 0, sizeof(struct lttcomm_ust_msg)); - - command->cmd = cmd; - command->handle = handle; -} - -/* - * Generic send command to ust tracer. Caller must free reply. - */ -static struct lttcomm_ust_reply *send_command(int sock, - struct lttcomm_ust_msg *command) -{ - struct lttcomm_ust_reply *reply; - - reply = ustcomm_send_command(sock, command); - if (reply == NULL) { - goto error; - } - - if (reply->ret_code != LTTCOMM_OK) { - ERR("Return code (%d): %s", reply->ret_code, - lttcomm_get_readable_code(reply->ret_code)); - goto error; - } - - return reply; - -error: - return NULL; -} - -/* - * Send registration done packet to the application. - */ -int ustctl_register_done(int sock) -{ - struct lttcomm_ust_msg command; - struct lttcomm_ust_reply *reply; - - DBG("Sending register done command to %d", sock); - - command.cmd = LTTNG_UST_REGISTER_DONE; - command.handle = LTTNG_UST_ROOT_HANDLE; - - reply = ustcomm_send_command(sock, &command); - if (reply == NULL) { - goto error; - } - - if (reply->ret_code != LTTCOMM_OK) { - DBG("Return code: %s", lttcomm_get_readable_code(reply->ret_code)); - goto error; - } - - return 0; - -error: - return -1; -} - -/* - * Create an UST session on the tracer. - * - * Return handle if success else negative value. - */ -int ustctl_create_session(int sock, struct ltt_ust_session *session) -{ - int ret; - struct lttcomm_ust_msg command; - struct lttcomm_ust_reply *reply = NULL; - - command.cmd = LTTNG_UST_SESSION; - command.handle = LTTNG_UST_ROOT_HANDLE; - - reply = ustcomm_send_command(sock, &command); - if (reply == NULL) { - goto error; - } - - /* Save session handle */ - ret = reply->ret_val; - free(reply); - - DBG2("ustctl create session command successful with handle %d", ret); - - return ret; - -error: - free(reply); - return -1; -} - -/* - * Create UST channel to the tracer. - * - * Return handle if success else negative value. - */ -int ustctl_create_channel(int sock, struct ltt_ust_session *session, - struct lttng_ust_channel *channel) -{ - int ret; - struct lttcomm_ust_msg command; - struct lttcomm_ust_reply *reply = NULL; - - init_command(LTTNG_UST_CHANNEL, session->handle, &command); - /* Copy channel attributes to command */ - memcpy(&command.u.channel, channel, sizeof(command.u.channel)); - - /* Send command to tracer */ - reply = send_command(sock, &command); - if (reply == NULL) { - goto error; - } - - ret = reply->ret_val; - free(reply); - - return ret; - -error: - free(reply); - return -1; -} - -/* - * Enable UST channel. - */ -int ustctl_enable_channel(int sock, struct ltt_ust_session *session, - struct ltt_ust_channel *chan) -{ - struct lttcomm_ust_msg command; - struct lttcomm_ust_reply *reply = NULL; - - init_command(LTTNG_UST_ENABLE, chan->handle, &command); - - reply = ustcomm_send_command(sock, &command); - if (reply == NULL) { - goto error; - } - - if (reply->handle != chan->handle) { - ERR("Receive wrong handle from UST reply on enable channel"); - goto error; - } - - chan->enabled = 1; - free(reply); - - DBG2("ustctl enable channel successful for sock %d", sock); - return 0; - -error: - free(reply); - return -1; -} - -/* - * Disable UST channel. - */ -int ustctl_disable_channel(int sock, struct ltt_ust_session *session, - struct ltt_ust_channel *chan) -{ - struct lttcomm_ust_msg command; - struct lttcomm_ust_reply *reply = NULL; - - memset(&command, 0, sizeof(command)); - - command.cmd = LTTNG_UST_DISABLE; - command.handle = chan->handle; - - reply = ustcomm_send_command(sock, &command); - if (reply == NULL) { - goto error; - } - - if (reply->handle != chan->handle) { - ERR("Receive wrong handle from UST reply on enable channel"); - goto error; - } - - chan->enabled = 1; - free(reply); - - DBG2("ustctl disable channel successful for sock %d", sock); - return 0; - -error: - free(reply); - return -1; -} diff --git a/ltt-sessiond/ust-ctl.h b/ltt-sessiond/ust-ctl.h deleted file mode 100644 index 1fe29c877..000000000 --- a/ltt-sessiond/ust-ctl.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * ust-ctl.h - * - * Meta header used to include all relevant file from the lttng ust ABI. - * - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_UST_CTL_H -#define _LTT_UST_CTL_H - -#include - -/* - * FIXME: temporary workaround: we use a lttng-tools local version of - * lttng-ust-abi.h if UST is not found. Eventually, we should use our - * own internal structures within lttng-tools instead of relying on the - * UST ABI. - */ -#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST -#include -#include -#else -#include "lttng-ust-ctl.h" -#include "lttng-ust-abi.h" -#endif - -#endif /* _LTT_UST_CTL_H */ diff --git a/ltt-sessiond/utils.c b/ltt-sessiond/utils.c deleted file mode 100644 index 0da564210..000000000 --- a/ltt-sessiond/utils.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Mathieu Desnoyers - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "utils.h" - -/* - * Write to writable pipe used to notify a thread. - */ -int notify_thread_pipe(int wpipe) -{ - int ret; - - ret = write(wpipe, "!", 1); - if (ret < 0) { - PERROR("write poll pipe"); - } - - return ret; -} - -/* - * Return pointer to home directory path using the env variable HOME. - * - * No home, NULL is returned. - */ -const char *get_home_dir(void) -{ - return ((const char *) getenv("HOME")); -} - -/* - * Create recursively directory using the FULL path. - */ -int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - int ret; - char *p, tmp[PATH_MAX]; - size_t len; - mode_t old_umask; - - ret = snprintf(tmp, sizeof(tmp), "%s", path); - if (ret < 0) { - PERROR("snprintf mkdir"); - goto error; - } - - len = ret; - if (tmp[len - 1] == '/') { - tmp[len - 1] = 0; - } - - old_umask = umask(0); - for (p = tmp + 1; *p; p++) { - if (*p == '/') { - *p = 0; - ret = mkdir(tmp, mode); - if (ret < 0) { - if (!(errno == EEXIST)) { - PERROR("mkdir recursive"); - ret = -errno; - goto umask_error; - } - } else if (ret == 0) { - /* - * We created the directory. Set its ownership to the - * user/group specified. - */ - ret = chown(tmp, uid, gid); - if (ret < 0) { - PERROR("chown in mkdir recursive"); - ret = -errno; - goto umask_error; - } - } - *p = '/'; - } - } - - ret = mkdir(tmp, mode); - if (ret < 0) { - if (!(errno == EEXIST)) { - PERROR("mkdir recursive last piece"); - ret = -errno; - } else { - ret = 0; - } - } else if (ret == 0) { - /* - * We created the directory. Set its ownership to the user/group - * specified. - */ - ret = chown(tmp, uid, gid); - if (ret < 0) { - PERROR("chown in mkdir recursive"); - ret = -errno; - goto umask_error; - } - } - -umask_error: - umask(old_umask); -error: - return ret; -} diff --git a/ltt-sessiond/utils.h b/ltt-sessiond/utils.h deleted file mode 100644 index 449c3ef6b..000000000 --- a/ltt-sessiond/utils.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; only version 2 of the License. - * - * 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., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef _LTT_UTILS_H -#define _LTT_UTILS_H - -#include - -int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid); -const char *get_home_dir(void); -int notify_thread_pipe(int wpipe); - -#endif /* _LTT_UTILS_H */ diff --git a/lttng-consumerd/lttng-consumerd.c b/lttng-consumerd/lttng-consumerd.c index cac71cace..946f0aba0 100644 --- a/lttng-consumerd/lttng-consumerd.c +++ b/lttng-consumerd/lttng-consumerd.c @@ -401,12 +401,12 @@ int main(int argc, char **argv) goto error; } - /* Connect to the socket created by ltt-sessiond to report errors */ + /* Connect to the socket created by lttng-sessiond to report errors */ DBG("Connecting to error socket %s", error_sock_path); ret = lttcomm_connect_unix_sock(error_sock_path); - /* not a fatal error, but all communication with ltt-sessiond will fail */ + /* not a fatal error, but all communication with lttng-sessiond will fail */ if (ret < 0) { - WARN("Cannot connect to error socket, is ltt-sessiond started ?"); + WARN("Cannot connect to error socket, is lttng-sessiond started ?"); } lttng_consumer_set_error_sock(ctx, ret); diff --git a/lttng-sessiond/Makefile.am b/lttng-sessiond/Makefile.am new file mode 100644 index 000000000..78179bd7a --- /dev/null +++ b/lttng-sessiond/Makefile.am @@ -0,0 +1,45 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include \ + -DINSTALL_BIN_PATH=\""$(bindir)"\" + +AM_CFLAGS = -fno-strict-aliasing + +bin_PROGRAMS = lttng-sessiond + +if COMPAT_EPOLL +COMPAT=compat/compat-epoll.c +else +COMPAT=compat/compat-poll.c +endif + +lttng_sessiond_SOURCES = utils.c utils.h \ + hashtable.c hashtable.h \ + compat/poll.h $(COMPAT) \ + trace-kernel.c trace-kernel.h \ + kernel-ctl.c kernel-ctl.h \ + ust-ctl.h ust-app.h trace-ust.h \ + context.c context.h \ + channel.c channel.h \ + event.c event.h \ + futex.c futex.h \ + shm.c shm.h \ + session.c session.h \ + ../hashtable/rculfhash.c \ + ../hashtable/rculfhash.h \ + ../hashtable/hash.c ../hashtable/hash.h + +if LTTNG_TOOLS_HAVE_UST +lttng_sessiond_SOURCES += trace-ust.c ust-app.c ust-consumer.c ust-consumer.h +endif + +# Add main.c at the end for compile order +lttng_sessiond_SOURCES += lttng-sessiond.h main.c + +# link on liblttngctl for check if sessiond is already alive. +lttng_sessiond_LDADD = -lrt -lurcu-cds -lurcu \ + $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ + $(top_builddir)/libkernelctl/libkernelctl.la \ + $(top_builddir)/liblttngctl/liblttngctl.la + +if LTTNG_TOOLS_HAVE_UST +lttng_sessiond_LDADD += -llttng-ust-comm -lustctl +endif diff --git a/lttng-sessiond/channel.c b/lttng-sessiond/channel.c new file mode 100644 index 000000000..fda679c78 --- /dev/null +++ b/lttng-sessiond/channel.c @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include +#include +#include +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST +#include +#include +#else +#include "lttng-ust-ctl.h" +#include "lttng-ust-abi.h" +#endif + +#include "channel.h" +#include "hashtable.h" +#include "kernel-ctl.h" +#include "ust-ctl.h" +#include "utils.h" + +/* + * Return allocated channel attributes. + */ +struct lttng_channel *channel_new_default_attr(int dom) +{ + struct lttng_channel *chan; + + chan = zmalloc(sizeof(struct lttng_channel)); + if (chan == NULL) { + perror("malloc channel init"); + goto error_alloc; + } + + if (snprintf(chan->name, sizeof(chan->name), "%s", + DEFAULT_CHANNEL_NAME) < 0) { + perror("snprintf default channel name"); + goto error; + } + + 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: + chan->attr.subbuf_size = DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE; + chan->attr.num_subbuf = DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM; + chan->attr.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_UST_PID: + chan->attr.subbuf_size = DEFAULT_UST_CHANNEL_SUBBUF_SIZE; + chan->attr.num_subbuf = DEFAULT_UST_CHANNEL_SUBBUF_NUM; + chan->attr.output = DEFAULT_UST_CHANNEL_OUTPUT; + break; + default: + goto error; /* Not implemented */ + } + + return chan; + +error: + free(chan); +error_alloc: + return NULL; +} + +/* + * Copy two ltt ust channel. Dst and src must be already allocated. + */ +int channel_ust_copy(struct ltt_ust_channel *dst, + struct ltt_ust_channel *src) +{ + //struct ltt_ust_event *uevent, *new_uevent; + + memcpy(dst, src, sizeof(struct ltt_ust_channel)); + dst->events = hashtable_new_str(0); + + /* + cds_list_for_each_entry(uevent, &src->events.head, list) { + new_uevent = malloc(sizeof(struct ltt_ust_event)); + if (new_uevent == NULL) { + perror("malloc ltt_ust_event"); + goto error; + } + + memcpy(new_uevent, uevent, sizeof(struct ltt_ust_event)); + cds_list_add(&new_uevent->list, &dst->events.head); + dst->events.count++; + } + */ + + return 0; + +//error: +// return -1; +} + +/* + * Disable kernel channel of the kernel session. + */ +int channel_kernel_disable(struct ltt_kernel_session *ksession, + char *channel_name) +{ + int ret; + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, ksession); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } else if (kchan->enabled == 1) { + ret = kernel_disable_channel(kchan); + if (ret < 0) { + if (ret != EEXIST) { + ret = LTTCOMM_KERN_CHAN_DISABLE_FAIL; + } + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Enable kernel channel of the kernel session. + */ +int channel_kernel_enable(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan) +{ + int ret; + + if (kchan->enabled == 0) { + ret = kernel_enable_channel(kchan); + if (ret < 0) { + ret = LTTCOMM_KERN_CHAN_ENABLE_FAIL; + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Create kernel channel of the kernel session and notify kernel thread. + */ +int channel_kernel_create(struct ltt_kernel_session *ksession, + struct lttng_channel *chan, int kernel_pipe) +{ + int ret; + struct lttng_channel *attr = chan; + + /* Creating channel attributes if needed */ + if (attr == NULL) { + /* FIXME: this appears to be a memory leak */ + attr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + } + + /* Channel not found, creating it */ + ret = kernel_create_channel(ksession, attr, ksession->trace_path); + if (ret < 0) { + ret = LTTCOMM_KERN_CHAN_FAIL; + goto error; + } + + /* Notify kernel thread that there is a new channel */ + ret = notify_thread_pipe(kernel_pipe); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Create UST channel and enable it on the tracer. + */ +int channel_ust_create(struct ltt_ust_session *usess, + struct lttng_channel *attr) +{ + int ret; + struct ltt_ust_channel *uchan; + //struct lttng_ust_channel_attr uattr; + //struct object_data *obj; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + attr->name); + if (uchan == NULL) { + uchan = trace_ust_create_channel(attr, usess->pathname); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_FAIL; + goto error; + } + rcu_read_lock(); + hashtable_add_unique(usess->domain_global.channels, &uchan->node); + rcu_read_unlock(); + } else { + ret = LTTCOMM_UST_CHAN_EXIST; + goto error; + } + + /* TODO: NOTIFY ust application to update */ + /* + ret = ustctl_create_channel(sock, usession->handle, &uattr, &obj); + if (ret < 0) { + ret = LTTCOMM_UST_CHAN_FAIL; + goto error; + } + */ + + /* + uchan->attr.overwrite = uattr.overwrite; + uchan->attr.subbuf_size = uattr.subbuf_size; + uchan->attr.num_subbuf = uattr.num_subbuf; + uchan->attr.switch_timer_interval = uattr.switch_timer_interval; + uchan->attr.read_timer_interval = uattr.read_timer_interval; + uchan->attr.output = uattr.output; + uchan->handle = obj->handle; + uchan->attr.shm_fd = obj->shm_fd; + uchan->attr.wait_fd = obj->wait_fd; + uchan->attr.memory_map_size = obj->memory_map_size; + uchan->obj = obj; + */ + + /* Add channel to session */ + //rcu_read_lock(); + //cds_list_add(&uchan->list, &usession->channels.head); + //usession->channels.count++; + //rcu_read_unlock(); + + //DBG2("Channel %s UST create successfully for sock:%d", uchan->name, sock); + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Enable UST channel on the tracer. + */ +int channel_ust_enable(struct ltt_ust_session *usession, + struct ltt_ust_channel *uchan, int sock) +{ + int ret = LTTCOMM_OK; +#ifdef DISABLE + struct object_data obj; + + obj.shm_fd = uchan->attr.shm_fd; + obj.wait_fd = uchan->attr.wait_fd; + obj.memory_map_size = uchan->attr.memory_map_size; + ret = ustctl_enable(sock, &obj); + if (ret < 0) { + ret = LTTCOMM_UST_CHAN_FAIL; + goto end; + } + ret = LTTCOMM_OK; +end: +#endif + return ret; +} + +/* + * Disable UST channel on the tracer. + */ +int channel_ust_disable(struct ltt_ust_session *usession, + struct ltt_ust_channel *uchan, int sock) +{ + int ret = LTTCOMM_OK; +#ifdef DISABLE + struct object_data obj; + + obj.shm_fd = uchan->attr.shm_fd; + obj.wait_fd = uchan->attr.wait_fd; + obj.memory_map_size = uchan->attr.memory_map_size; + ret = ustctl_disable(sock, &obj); + if (ret < 0) { + ret = LTTCOMM_UST_CHAN_FAIL; + goto end; + } + ret = LTTCOMM_OK; +end: +#endif + return ret; +} diff --git a/lttng-sessiond/channel.h b/lttng-sessiond/channel.h new file mode 100644 index 000000000..37c1b8c0a --- /dev/null +++ b/lttng-sessiond/channel.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_CHANNEL_H +#define _LTT_CHANNEL_H + +#include + +#include "trace-kernel.h" +#include "trace-ust.h" + +int channel_kernel_disable(struct ltt_kernel_session *ksession, + char *channel_name); +int channel_kernel_enable(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan); +int channel_kernel_create(struct ltt_kernel_session *ksession, + struct lttng_channel *chan, int kernel_pipe); + +int channel_ust_create(struct ltt_ust_session *usession, + struct lttng_channel *chan); +int channel_ust_copy(struct ltt_ust_channel *dst, + struct ltt_ust_channel *src); +int channel_ust_disable(struct ltt_ust_session *usession, + struct ltt_ust_channel *uchan, int sock); +int channel_ust_enable(struct ltt_ust_session *usession, + struct ltt_ust_channel *uchan, int sock); + +struct lttng_channel *channel_new_default_attr(int domain); + +#endif /* _LTT_CHANNEL_H */ diff --git a/lttng-sessiond/compat/compat-epoll.c b/lttng-sessiond/compat/compat-epoll.c new file mode 100644 index 000000000..e909b603c --- /dev/null +++ b/lttng-sessiond/compat/compat-epoll.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "poll.h" + +unsigned int poll_max_size; + +/* + * Create epoll set and allocate returned events structure. + */ +int compat_epoll_create(struct lttng_poll_event *events, int size, int flags) +{ + int ret; + + if (events == NULL || size <= 0) { + goto error; + } + + /* Don't bust the limit here */ + if (size > poll_max_size) { + size = poll_max_size; + } + + ret = epoll_create1(flags); + if (ret < 0) { + /* At this point, every error is fatal */ + perror("epoll_create1"); + goto error; + } + + events->epfd = ret; + + /* This *must* be freed by using lttng_poll_free() */ + events->events = zmalloc(size * sizeof(struct epoll_event)); + if (events->events == NULL) { + perror("malloc epoll set"); + goto error_close; + } + + events->events_size = size; + events->nb_fd = 0; + + return 0; + +error_close: + close(events->epfd); +error: + return -1; +} + +/* + * Add a fd to the epoll set with requesting events. + */ +int compat_epoll_add(struct lttng_poll_event *events, int fd, uint32_t req_events) +{ + int ret, new_size; + struct epoll_event ev, *ptr; + + if (events == NULL || events->events == NULL || fd < 0) { + ERR("Bad compat epoll add arguments"); + goto error; + } + + ev.events = req_events; + ev.data.fd = fd; + + ret = epoll_ctl(events->epfd, EPOLL_CTL_ADD, fd, &ev); + if (ret < 0) { + switch (errno) { + case EEXIST: + case ENOSPC: + case EPERM: + /* Print perror and goto end not failing. Show must go on. */ + perror("epoll_ctl ADD"); + goto end; + default: + perror("epoll_ctl ADD fatal"); + goto error; + } + } + + events->nb_fd++; + + if (events->nb_fd >= events->events_size) { + new_size = 2 * events->events_size; + ptr = realloc(events->events, new_size * sizeof(struct epoll_event)); + if (ptr == NULL) { + perror("realloc epoll add"); + goto error; + } + events->events = ptr; + events->events_size = new_size; + } + +end: + return 0; + +error: + return -1; +} + +/* + * Remove a fd from the epoll set. + */ +int compat_epoll_del(struct lttng_poll_event *events, int fd) +{ + int ret; + + if (events == NULL || fd < 0) { + goto error; + } + + ret = epoll_ctl(events->epfd, EPOLL_CTL_DEL, fd, NULL); + if (ret < 0) { + switch (errno) { + case ENOENT: + case EPERM: + /* Print perror and goto end not failing. Show must go on. */ + perror("epoll_ctl DEL"); + goto end; + default: + perror("epoll_ctl DEL fatal"); + goto error; + } + perror("epoll_ctl del"); + goto error; + } + + events->nb_fd--; + +end: + return 0; + +error: + return -1; +} + +/* + * Wait on epoll set. This is a blocking call of timeout value. + */ +int compat_epoll_wait(struct lttng_poll_event *events, int timeout) +{ + int ret; + + if (events == NULL || events->events == NULL || + events->events_size < events->nb_fd) { + ERR("Wrong arguments in compat_epoll_wait"); + goto error; + } + + do { + ret = epoll_wait(events->epfd, events->events, events->nb_fd, timeout); + } while (ret == -1 && errno == EINTR); + if (ret < 0) { + /* At this point, every error is fatal */ + perror("epoll_wait"); + goto error; + } + + return ret; + +error: + return -1; +} + +/* + * Setup poll set maximum size. + */ +void compat_epoll_set_max_size(void) +{ + int ret, fd; + char buf[64]; + + poll_max_size = LTTNG_POLL_DEFAULT_SIZE; + + fd = open(LTTNG_EPOLL_PROC_PATH, O_RDONLY); + if (fd < 0) { + return; + } + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + perror("read set max size"); + goto error; + } + + poll_max_size = atoi(buf); + if (poll_max_size <= 0) { + /* Extra precaution */ + poll_max_size = LTTNG_POLL_DEFAULT_SIZE; + } + + DBG("epoll set max size is %d", poll_max_size); + +error: + close(fd); +} diff --git a/lttng-sessiond/compat/compat-poll.c b/lttng-sessiond/compat/compat-poll.c new file mode 100644 index 000000000..cc4bb0f97 --- /dev/null +++ b/lttng-sessiond/compat/compat-poll.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include + +#include "poll.h" + +unsigned int poll_max_size; + +/* + * Create pollfd data structure. + */ +int compat_poll_create(struct lttng_poll_event *events, int size) +{ + if (events == NULL || size <= 0) { + ERR("Wrong arguments for poll create"); + goto error; + } + + /* Don't bust the limit here */ + if (size > poll_max_size) { + size = poll_max_size; + } + + /* This *must* be freed by using lttng_poll_free() */ + events->events = zmalloc(size * sizeof(struct pollfd)); + if (events->events == NULL) { + perror("malloc struct pollfd"); + goto error; + } + + events->events_size = size; + events->nb_fd = 0; + + return 0; + +error: + return -1; +} + +/* + * Add fd to pollfd data structure with requested events. + */ +int compat_poll_add(struct lttng_poll_event *events, int fd, + uint32_t req_events) +{ + int new_size; + struct pollfd *ptr; + + if (events == NULL || events->events == NULL || fd < 0) { + ERR("Bad compat poll add arguments"); + goto error; + } + + /* Reallocate pollfd structure by a factor of 2 if needed. */ + if (events->nb_fd >= events->events_size) { + new_size = 2 * events->events_size; + ptr = realloc(events->events, new_size * sizeof(struct pollfd)); + if (ptr == NULL) { + perror("realloc poll add"); + goto error; + } + events->events = ptr; + events->events_size = new_size; + } + + events->events[events->nb_fd].fd = fd; + events->events[events->nb_fd].events = req_events; + events->nb_fd++; + + DBG("fd %d of %d added to pollfd", fd, events->nb_fd); + + return 0; + +error: + return -1; +} + +/* + * Remove a fd from the pollfd structure. + */ +int compat_poll_del(struct lttng_poll_event *events, int fd) +{ + int new_size, i, count = 0; + struct pollfd *old = NULL, *new = NULL; + + if (events == NULL || events->events == NULL || fd < 0) { + ERR("Wrong arguments for poll del"); + goto error; + } + + old = events->events; + new_size = events->events_size - 1; + + /* Safety check on size */ + if (new_size > poll_max_size) { + new_size = poll_max_size; + } + + new = zmalloc(new_size * sizeof(struct pollfd)); + if (new == NULL) { + perror("malloc poll del"); + goto error; + } + + for (i = 0; i < events->events_size; i++) { + /* Don't put back the fd we want to delete */ + if (old[i].fd != fd) { + new[count].fd = old[i].fd; + new[count].events = old[i].events; + count++; + } + } + + events->events_size = new_size; + events->events = new; + events->nb_fd--; + + free(old); + + return 0; + +error: + return -1; +} + +/* + * Wait on poll() with timeout. Blocking call. + */ +int compat_poll_wait(struct lttng_poll_event *events, int timeout) +{ + int ret; + + if (events == NULL || events->events == NULL || + events->events_size < events->nb_fd) { + ERR("poll wait arguments error"); + goto error; + } + + ret = poll(events->events, events->nb_fd, timeout); + if (ret < 0) { + /* At this point, every error is fatal */ + perror("poll wait"); + goto error; + } + + return ret; + +error: + return -1; +} + +/* + * Setup poll set maximum size. + */ +void compat_poll_set_max_size(void) +{ + int ret; + struct rlimit lim; + + /* Default value */ + poll_max_size = LTTNG_POLL_DEFAULT_SIZE; + + ret = getrlimit(RLIMIT_NOFILE, &lim); + if (ret < 0) { + perror("getrlimit poll RLIMIT_NOFILE"); + return; + } + + poll_max_size = lim.rlim_cur; + if (poll_max_size <= 0) { + /* Extra precaution */ + poll_max_size = LTTNG_POLL_DEFAULT_SIZE; + } + + DBG("poll set max size set to %u", poll_max_size); +} diff --git a/lttng-sessiond/compat/poll.h b/lttng-sessiond/compat/poll.h new file mode 100644 index 000000000..3e804305f --- /dev/null +++ b/lttng-sessiond/compat/poll.h @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_POLL_H +#define _LTT_POLL_H + +#include +#include + +#include + +/* + * 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. + */ +#define LTTNG_POLL_DEFAULT_SIZE 65535 + +/* + * Maximum number of fd we can monitor. + * + * For epoll(7), /proc/sys/fs/epoll/max_user_watches (since Linux 2.6.28) will + * be used for the maximum size of the poll set. If this interface is not + * available, according to the manpage, the max_user_watches value is 1/25 (4%) + * of the available low memory divided by the registration cost in bytes which + * is 90 bytes on a 32-bit kernel and 160 bytes on a 64-bit kernel. + * + * For poll(2), the max fds must not exceed RLIMIT_NOFILE given by + * getrlimit(2). + */ +extern unsigned int poll_max_size; + +/* + * Used by lttng_poll_clean to free the events structure in a lttng_poll_event. + */ +static inline void __lttng_poll_free(void *events) +{ + free(events); +} + +/* + * epoll(7) implementation. + */ +#ifdef HAVE_EPOLL +#include + +/* See man epoll(7) for this define path */ +#define LTTNG_EPOLL_PROC_PATH "/proc/sys/fs/epoll/max_user_watches" + +enum { + /* Polling variables compatibility for epoll */ + LPOLLIN = EPOLLIN, + LPOLLPRI = EPOLLPRI, + LPOLLOUT = EPOLLOUT, + LPOLLRDNORM = EPOLLRDNORM, + LPOLLRDBAND = EPOLLRDBAND, + LPOLLWRNORM = EPOLLWRNORM, + LPOLLWRBAND = EPOLLWRBAND, + LPOLLMSG = EPOLLMSG, + LPOLLERR = EPOLLERR, + LPOLLHUP = EPOLLHUP, + LPOLLNVAL = EPOLLHUP, + LPOLLRDHUP = EPOLLRDHUP, + /* Close on exec feature of epoll */ + LTTNG_CLOEXEC = EPOLL_CLOEXEC, +}; + +struct compat_epoll_event { + int epfd; + uint32_t nb_fd; /* Current number of fd in events */ + uint32_t events_size; /* Size of events array */ + struct epoll_event *events; +}; +#define lttng_poll_event compat_epoll_event + +/* + * For the following calls, consider 'e' to be a lttng_poll_event pointer and i + * being the index of the events array. + */ +#define LTTNG_POLL_GETFD(e, i) LTTNG_REF(e)->events[i].data.fd +#define LTTNG_POLL_GETEV(e, i) LTTNG_REF(e)->events[i].events +#define LTTNG_POLL_GETNB(e) LTTNG_REF(e)->nb_fd +#define LTTNG_POLL_GETSZ(e) LTTNG_REF(e)->events_size + +/* + * Create the epoll set. No memory allocation is done here. + */ +extern int compat_epoll_create(struct lttng_poll_event *events, + int size, int flags); +#define lttng_poll_create(events, size, flags) \ + compat_epoll_create(events, size, flags); + +/* + * Wait on epoll set with the number of fd registered to the lttng_poll_event + * data structure (events). + */ +extern int compat_epoll_wait(struct lttng_poll_event *events, int timeout); +#define lttng_poll_wait(events, timeout) \ + compat_epoll_wait(events, timeout); + +/* + * Add a fd to the epoll set and resize the epoll_event structure if needed. + */ +extern int compat_epoll_add(struct lttng_poll_event *events, + int fd, uint32_t req_events); +#define lttng_poll_add(events, fd, req_events) \ + compat_epoll_add(events, fd, req_events); + +/* + * Remove a fd from the epoll set. + */ +extern int compat_epoll_del(struct lttng_poll_event *events, int fd); +#define lttng_poll_del(events, fd) \ + compat_epoll_del(events, fd); + +/* + * Set up the poll set limits variable poll_max_size + */ +extern void compat_epoll_set_max_size(void); +#define lttng_poll_set_max_size(void) \ + compat_epoll_set_max_size(void); + +/* + * This function memset with zero the structure since it can be reused at each + * round of a main loop. Being in a loop and using a non static number of fds, + * this function must be called to insure coherent events with associted fds. + */ +static inline void lttng_poll_reset(struct lttng_poll_event *events) +{ + if (events && events->events) { + memset(events->events, 0, + events->nb_fd * sizeof(struct epoll_event)); + } +} + +/* + * Clean the events structure of a lttng_poll_event. It's the caller + * responsability to free the lttng_poll_event memory. + */ +static inline void lttng_poll_clean(struct lttng_poll_event *events) +{ + if (events) { + close(events->epfd); + __lttng_poll_free((void *) events->events); + } +} + +#else /* HAVE_EPOLL */ +/* + * Fallback on poll(2) API + */ + +/* Needed for some poll event values */ +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +/* Needed for some poll event values */ +#ifndef __USE_GNU +#define __USE_GNU +#endif + +#include +#include + +enum { + /* Polling variables compatibility for poll */ + LPOLLIN = POLLIN, + LPOLLPRI = POLLPRI, + LPOLLOUT = POLLOUT, + LPOLLRDNORM = POLLRDNORM, + LPOLLRDBAND = POLLRDBAND, + LPOLLWRNORM = POLLWRNORM, + LPOLLWRBAND = POLLWRBAND, + LPOLLMSG = POLLMSG, + LPOLLERR = POLLERR, + LPOLLHUP = POLLHUP | POLLNVAL, + LPOLLRDHUP = POLLRDHUP, + /* Close on exec feature does not exist for poll(2) */ + LTTNG_CLOEXEC = 0xdead, +}; + +struct compat_poll_event { + uint32_t nb_fd; /* Current number of fd in events */ + uint32_t events_size; /* Size of events array */ + struct pollfd *events; +}; +#define lttng_poll_event compat_poll_event + +/* + * For the following calls, consider 'e' to be a lttng_poll_event pointer and i + * being the index of the events array. + */ +#define LTTNG_POLL_GETFD(e, i) LTTNG_REF(e)->events[i].fd +#define LTTNG_POLL_GETEV(e, i) LTTNG_REF(e)->events[i].revents +#define LTTNG_POLL_GETNB(e) LTTNG_REF(e)->nb_fd +#define LTTNG_POLL_GETSZ(e) LTTNG_REF(e)->events_size + +/* + * Create a pollfd structure of size 'size'. + */ +extern int compat_poll_create(struct lttng_poll_event *events, int size); +#define lttng_poll_create(events, size, flags) \ + compat_poll_create(events, size); + +/* + * Wait on poll(2) event with nb_fd registered to the lttng_poll_event data + * structure. + */ +extern int compat_poll_wait(struct lttng_poll_event *events, int timeout); +#define lttng_poll_wait(events, timeout) \ + compat_poll_wait(events, timeout); + +/* + * Add the fd to the pollfd structure. Resize if needed. + */ +extern int compat_poll_add(struct lttng_poll_event *events, + int fd, uint32_t req_events); +#define lttng_poll_add(events, fd, req_events) \ + compat_poll_add(events, fd, req_events); + +/* + * Remove the fd from the pollfd. Memory allocation is done to recreate a new + * pollfd, data is copied from the old pollfd to the new and, finally, the old + * one is freed(). + */ +extern int compat_poll_del(struct lttng_poll_event *events, int fd); +#define lttng_poll_del(events, fd) \ + compat_poll_del(events, fd); + +/* + * Set up the poll set limits variable poll_max_size + */ +extern void compat_poll_set_max_size(void); +#define lttng_poll_set_max_size(void) \ + compat_poll_set_max_size(void); + +/* + * No need to reset a pollfd structure for poll(2) + */ +static inline void lttng_poll_reset(struct lttng_poll_event *events) +{} + +/* + * Clean the events structure of a lttng_poll_event. It's the caller + * responsability to free the lttng_poll_event memory. + */ +static inline void lttng_poll_clean(struct lttng_poll_event *events) +{ + if (events) { + __lttng_poll_free((void *) events->events); + } +} + +#endif /* HAVE_EPOLL */ + +#endif /* _LTT_POLL_H */ diff --git a/lttng-sessiond/context.c b/lttng-sessiond/context.c new file mode 100644 index 000000000..41e33ec27 --- /dev/null +++ b/lttng-sessiond/context.c @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST +#include +#include +#else +#include "lttng-ust-ctl.h" +#include "lttng-ust-abi.h" +#endif + +#include "context.h" +#include "hashtable.h" +#include "kernel-ctl.h" + +/* + * Add kernel context to an event of a specific channel. + */ +static int add_kctx_to_event(struct lttng_kernel_context *kctx, + struct ltt_kernel_channel *kchan, char *event_name) +{ + int ret, found = 0; + struct ltt_kernel_event *kevent; + + DBG("Add kernel context to event %s", event_name); + + kevent = trace_kernel_get_event_by_name(event_name, kchan); + if (kevent != NULL) { + ret = kernel_add_event_context(kevent, kctx); + if (ret < 0) { + goto error; + } + found = 1; + } + + ret = found; + +error: + return ret; +} + +/* + * Add kernel context to all channel. + * + * If event_name is specified, add context to event instead. + */ +static int add_kctx_all_channels(struct ltt_kernel_session *ksession, + struct lttng_kernel_context *kctx, char *event_name) +{ + int ret, no_event = 0, found = 0; + struct ltt_kernel_channel *kchan; + + if (strlen(event_name) == 0) { + no_event = 1; + } + + DBG("Adding kernel context to all channels (event: %s)", event_name); + + /* Go over all channels */ + cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { + if (no_event) { + ret = kernel_add_channel_context(kchan, kctx); + if (ret < 0) { + ret = LTTCOMM_KERN_CONTEXT_FAIL; + goto error; + } + } else { + ret = add_kctx_to_event(kctx, kchan, event_name); + if (ret < 0) { + ret = LTTCOMM_KERN_CONTEXT_FAIL; + goto error; + } else if (ret == 1) { + /* Event found and context added */ + found = 1; + break; + } + } + } + + if (!found && !no_event) { + ret = LTTCOMM_NO_EVENT; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Add kernel context to a specific channel. + * + * If event_name is specified, add context to that event. + */ +static int add_kctx_to_channel(struct lttng_kernel_context *kctx, + struct ltt_kernel_channel *kchan, char *event_name) +{ + int ret, no_event = 0, found = 0; + + if (strlen(event_name) == 0) { + no_event = 1; + } + + DBG("Add kernel context to channel '%s', event '%s'", + kchan->channel->name, event_name); + + if (no_event) { + ret = kernel_add_channel_context(kchan, kctx); + if (ret < 0) { + ret = LTTCOMM_KERN_CONTEXT_FAIL; + goto error; + } + } else { + ret = add_kctx_to_event(kctx, kchan, event_name); + if (ret < 0) { + ret = LTTCOMM_KERN_CONTEXT_FAIL; + goto error; + } else if (ret == 1) { + /* Event found and context added */ + found = 1; + } + } + + if (!found && !no_event) { + ret = LTTCOMM_NO_EVENT; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Add kernel context to tracer. + */ +int context_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_event_context *ctx, char *event_name, + char *channel_name) +{ + int ret; + struct ltt_kernel_channel *kchan; + struct lttng_kernel_context kctx; + + /* Setup kernel context structure */ + kctx.ctx = ctx->ctx; + kctx.u.perf_counter.type = ctx->u.perf_counter.type; + kctx.u.perf_counter.config = ctx->u.perf_counter.config; + strncpy(kctx.u.perf_counter.name, ctx->u.perf_counter.name, + LTTNG_SYMBOL_NAME_LEN); + kctx.u.perf_counter.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + + if (strlen(channel_name) == 0) { + ret = add_kctx_all_channels(ksession, &kctx, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + } else { + /* Get kernel channel */ + kchan = trace_kernel_get_channel_by_name(channel_name, ksession); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = add_kctx_to_channel(&kctx, kchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * UST support. + */ + +/* + * Add UST context to an event of a specific channel. + */ +#ifdef DISABLE +static int add_ustctx_to_event(struct ltt_ust_session *ustsession, + struct lttng_ust_context *ustctx, + struct ltt_ust_channel *ustchan, char *event_name) +{ + int ret, found = 0; + struct ltt_ust_event *ustevent; + struct object_data *context_data; /* FIXME: currently a memleak */ + + DBG("Add UST context to event %s", event_name); + + ustevent = trace_ust_find_event_by_name(ustchan->events, event_name); + if (ustevent != NULL) { + ret = ustctl_add_context(ustsession->sock, ustctx, + ustevent->obj, &context_data); + if (ret < 0) { + goto error; + } + found = 1; + } + + ret = found; + +error: + return ret; +} +#endif + +/* + * Add UST context to all channel. + * + * If event_name is specified, add context to event instead. + */ +static int add_ustctx_all_channels(struct ltt_ust_session *ustsession, + struct lttng_ust_context *ustctx, char *event_name, + struct cds_lfht *channels) +{ +#ifdef DISABLE + int ret, no_event = 0, found = 0; + struct ltt_ust_channel *ustchan; + struct object_data *context_data; /* FIXME: currently a memleak */ + + if (strlen(event_name) == 0) { + no_event = 1; + } + + DBG("Adding ust context to all channels (event: %s)", event_name); + + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + hashtable_get_first(channels, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ustchan = caa_container_of(node, struct ltt_ust_channel, node); + if (no_event) { + //ret = ustctl_add_context(ustsession->sock, + // ustctx, ustchan->obj, &context_data); + if (ret < 0) { + ret = LTTCOMM_UST_CONTEXT_FAIL; + goto error; + } + } else { + ret = add_ustctx_to_event(ustsession, ustctx, ustchan, event_name); + if (ret < 0) { + ret = LTTCOMM_UST_CONTEXT_FAIL; + goto error; + } else if (ret == 1) { + /* Event found and context added */ + found = 1; + break; + } + } + hashtable_get_next(channels, &iter); + } + rcu_read_unlock(); + + if (!found && !no_event) { + ret = LTTCOMM_NO_EVENT; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +#endif + return 0; +} + +/* + * Add UST context to a specific channel. + * + * If event_name is specified, add context to that event. + */ +static int add_ustctx_to_channel(struct ltt_ust_session *ustsession, + struct lttng_ust_context *ustctx, + struct ltt_ust_channel *ustchan, char *event_name) +{ +#ifdef DISABLE + int ret, no_event = 0, found = 0; + struct object_data *context_data; /* FIXME: currently a memleak */ + + if (strlen(event_name) == 0) { + no_event = 1; + } + + DBG("Add UST context to channel '%s', event '%s'", + ustchan->name, event_name); + + if (no_event) { + //ret = ustctl_add_context(ustsession->sock, ustctx, + // ustchan->obj, &context_data); + if (ret < 0) { + ret = LTTCOMM_UST_CONTEXT_FAIL; + goto error; + } + } else { + ret = add_ustctx_to_event(ustsession, ustctx, ustchan, event_name); + if (ret < 0) { + ret = LTTCOMM_UST_CONTEXT_FAIL; + goto error; + } else if (ret == 1) { + /* Event found and context added */ + found = 1; + } + } + + if (!found && !no_event) { + ret = LTTCOMM_NO_EVENT; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +#endif + return 0; +} + +/* + * Add UST context to tracer. + */ +int context_ust_add(struct ltt_ust_session *ustsession, + struct lttng_event_context *ctx, char *event_name, + char *channel_name, int domain) +{ + int ret; + struct cds_lfht *chan_ht = NULL; + struct ltt_ust_channel *ustchan; + struct lttng_ust_context ustctx; + + /* Setup UST context structure */ + ustctx.ctx = ctx->ctx; + + switch (domain) { + case LTTNG_DOMAIN_UST: + chan_ht = ustsession->domain_global.channels; + break; + } + + if (strlen(channel_name) == 0) { + ret = add_ustctx_all_channels(ustsession, &ustctx, event_name, chan_ht); + if (ret != LTTCOMM_OK) { + goto error; + } + } else { + /* Get UST channel */ + ustchan = trace_ust_find_channel_by_name(chan_ht, channel_name); + if (ustchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = add_ustctx_to_channel(ustsession, &ustctx, ustchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} diff --git a/lttng-sessiond/context.h b/lttng-sessiond/context.h new file mode 100644 index 000000000..e97e9489a --- /dev/null +++ b/lttng-sessiond/context.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_CONTEXT_H +#define _LTT_CONTEXT_H + +#include +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST +#include +#else +#include "lttng-ust-abi.h" +#endif + + +#include "trace-kernel.h" +#include "trace-ust.h" + +int context_kernel_add(struct ltt_kernel_session *ksession, + struct lttng_event_context *ctx, char *event_name, char *channel_name); +int context_ust_add(struct ltt_ust_session *ustsession, + struct lttng_event_context *ctx, char *event_name, + char *channel_name, int domain); + +#endif /* _LTT_CONTEXT_H */ diff --git a/lttng-sessiond/event.c b/lttng-sessiond/event.c new file mode 100644 index 000000000..1f90c1c73 --- /dev/null +++ b/lttng-sessiond/event.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST +#include +#else +#include "lttng-ust-ctl.h" +#endif + +#include "channel.h" +#include "event.h" +#include "hashtable.h" +#include "kernel-ctl.h" + +/* + * Setup a lttng_event used to enable *all* syscall tracing. + */ +static void init_syscalls_kernel_event(struct lttng_event *event) +{ + event->name[0] = '\0'; + /* + * We use LTTNG_EVENT* here since the trace kernel creation will make the + * right changes for the kernel. + */ + event->type = LTTNG_EVENT_SYSCALL; +} + +/* + * Disable kernel tracepoint event for a channel from the kernel session. + */ +int event_kernel_disable_tracepoint(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, char *event_name) +{ + int ret; + struct ltt_kernel_event *kevent; + + kevent = trace_kernel_get_event_by_name(event_name, kchan); + if (kevent == NULL) { + ret = LTTCOMM_NO_EVENT; + goto error; + } + + ret = kernel_disable_event(kevent); + if (ret < 0) { + ret = LTTCOMM_KERN_DISABLE_FAIL; + goto error; + } + + DBG("Kernel event %s disable for channel %s.", + kevent->event->name, kchan->channel->name); + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Disable kernel tracepoint events for a channel from the kernel session. + */ +int event_kernel_disable_all_tracepoints(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan) +{ + int ret; + struct ltt_kernel_event *kevent; + + /* For each event in the kernel session */ + cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { + ret = kernel_disable_event(kevent); + if (ret < 0) { + /* We continue disabling the rest */ + continue; + } + } + ret = LTTCOMM_OK; + return ret; +} + +/* + * Disable kernel syscall events for a channel from the kernel session. + */ +int event_kernel_disable_all_syscalls(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan) +{ + ERR("Cannot disable syscall tracing for existing session. Please destroy session instead."); + return LTTCOMM_OK; /* Return OK so disable all succeeds */ +} + +/* + * Disable all kernel event for a channel from the kernel session. + */ +int event_kernel_disable_all(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan) +{ + int ret; + + ret = event_kernel_disable_all_tracepoints(ksession, kchan); + if (ret != LTTCOMM_OK) + return ret; + ret = event_kernel_disable_all_syscalls(ksession, kchan); + return ret; +} + +/* + * Enable kernel tracepoint event for a channel from the kernel session. + */ +int event_kernel_enable_tracepoint(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, struct lttng_event *event) +{ + int ret; + struct ltt_kernel_event *kevent; + + kevent = trace_kernel_get_event_by_name(event->name, kchan); + if (kevent == NULL) { + ret = kernel_create_event(event, kchan); + if (ret < 0) { + if (ret == -EEXIST) { + ret = LTTCOMM_KERN_EVENT_EXIST; + } else { + ret = LTTCOMM_KERN_ENABLE_FAIL; + } + goto end; + } + } else if (kevent->enabled == 0) { + ret = kernel_enable_event(kevent); + if (ret < 0) { + ret = LTTCOMM_KERN_ENABLE_FAIL; + goto end; + } + } + ret = LTTCOMM_OK; +end: + return ret; +} + +/* + * Enable all kernel tracepoint events of a channel of the kernel session. + */ +int event_kernel_enable_all_tracepoints(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd) +{ + int size, i, ret; + struct ltt_kernel_event *kevent; + struct lttng_event *event_list; + + /* For each event in the kernel session */ + cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { + ret = kernel_enable_event(kevent); + if (ret < 0) { + /* Enable failed but still continue */ + continue; + } + } + + size = kernel_list_events(kernel_tracer_fd, &event_list); + if (size < 0) { + ret = LTTCOMM_KERN_LIST_FAIL; + goto end; + } + + for (i = 0; i < size; i++) { + kevent = trace_kernel_get_event_by_name(event_list[i].name, kchan); + if (kevent == NULL) { + /* Default event type for enable all */ + event_list[i].type = LTTNG_EVENT_TRACEPOINT; + /* Enable each single tracepoint event */ + ret = kernel_create_event(&event_list[i], kchan); + if (ret < 0) { + /* Ignore error here and continue */ + } + } + } + free(event_list); + ret = LTTCOMM_OK; +end: + return ret; + +} + +/* + * Enable all kernel tracepoint events of a channel of the kernel session. + */ +int event_kernel_enable_all_syscalls(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd) +{ + int ret; + struct lttng_event event; + + init_syscalls_kernel_event(&event); + + DBG("Enabling all syscall tracing"); + + ret = kernel_create_event(&event, kchan); + if (ret < 0) { + goto end; + } + ret = LTTCOMM_OK; +end: + return ret; +} + +/* + * Enable all kernel events of a channel of the kernel session. + */ +int event_kernel_enable_all(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd) +{ + int ret; + + ret = event_kernel_enable_all_tracepoints(ksession, kchan, kernel_tracer_fd); + if (ret != LTTCOMM_OK) { + goto end; + } + ret = event_kernel_enable_all_syscalls(ksession, kchan, kernel_tracer_fd); +end: + return ret; +} + +/* + * Enable UST tracepoint event for a channel from a UST session. + */ +#ifdef DISABLE +int event_ust_enable_tracepoint(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret; + struct lttng_ust_event ltt_uevent; + struct object_data *obj_event; + + strncpy(ltt_uevent.name, uevent->attr.name, sizeof(ltt_uevent.name)); + ltt_uevent.name[sizeof(ltt_uevent.name) - 1] = '\0'; + /* TODO: adjust to other instrumentation types */ + ltt_uevent.instrumentation = LTTNG_UST_TRACEPOINT; + + ret = ustctl_create_event(app->key.sock, <t_uevent, + uchan->obj, &obj_event); + if (ret < 0) { + DBG("Error ustctl create event %s for app pid: %d, sock: %d ret %d", + uevent->attr.name, app->key.pid, app->key.sock, ret); + goto next; + } + + uevent->obj = obj_event; + uevent->handle = obj_event->handle; + uevent->enabled = 1; + ret = LTTCOMM_OK; +end: + return ret; +} +#endif + +#ifdef DISABLE +int event_ust_disable_tracepoint(struct ltt_ust_session *ustsession, + struct ltt_ust_channel *ustchan, char *event_name) +{ + int ret; + struct ltt_ust_event *ustevent; + + ustevent = trace_ust_find_event_by_name(ustchan->events, event_name); + if (ustevent == NULL) { + ret = LTTCOMM_NO_EVENT; + goto end; + } + //ret = ustctl_disable(ustsession->sock, ustevent->obj); + if (ret < 0) { + ret = LTTCOMM_UST_ENABLE_FAIL; + goto end; + } + ustevent->enabled = 0; + ret = LTTCOMM_OK; +end: + return ret; +} +#endif diff --git a/lttng-sessiond/event.h b/lttng-sessiond/event.h new file mode 100644 index 000000000..2852b3535 --- /dev/null +++ b/lttng-sessiond/event.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_EVENT_H +#define _LTT_EVENT_H + +#include + +#include "trace-kernel.h" + +int event_kernel_disable_tracepoint(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, char *event_name); +int event_kernel_disable_all_syscalls(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan); +int event_kernel_disable_all_tracepoints(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan); +int event_kernel_disable_all(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan); + +int event_kernel_enable_tracepoint(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, struct lttng_event *event); +int event_kernel_enable_all_tracepoints(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd); +int event_kernel_enable_all_syscalls(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd); +int event_kernel_enable_all(struct ltt_kernel_session *ksession, + struct ltt_kernel_channel *kchan, int kernel_tracer_fd); + +int event_ust_enable_tracepoint(struct ltt_ust_session *ustsession, + struct ltt_ust_channel *ustchan, struct ltt_ust_event *uevent); +int event_ust_disable_tracepoint(struct ltt_ust_session *ustsession, + struct ltt_ust_channel *ustchan, char *event_name); + +#endif /* _LTT_EVENT_H */ diff --git a/lttng-sessiond/futex.c b/lttng-sessiond/futex.c new file mode 100644 index 000000000..de3f94e6e --- /dev/null +++ b/lttng-sessiond/futex.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include + +#include "futex.h" + +/* + * This futex wait/wake scheme only works for N wakers / 1 waiters. Hence the + * "nto1" added to all function signature. + * + * Please see wait_gp()/update_counter_and_wait() calls in urcu.c in the urcu + * git tree for a detail example of this scheme being used. futex_async() is + * the urcu wrapper over the futex() sycall. + * + * There is also a formal verification available in the git tree. + * + * branch: formal-model + * commit id: 2a8044f3493046fcc8c67016902dc7beec6f026a + * + * Ref: git://git.lttng.org/userspace-rcu.git + */ + +/* + * Update futex according to active or not. This scheme is used to wake every + * libust waiting on the shared memory map futex hence the INT_MAX used in the + * futex() call. If active, we set the value and wake everyone else we indicate + * that we are gone (cleanup() case). + */ +void futex_wait_update(int32_t *futex, int active) +{ + if (active) { + uatomic_set(futex, 1); + futex_async(futex, FUTEX_WAKE, + INT_MAX, NULL, NULL, 0); + } else { + uatomic_set(futex, 0); + } + + DBG("Futex wait update active %d", active); +} + +/* + * Prepare futex. + */ +void futex_nto1_prepare(int32_t *futex) +{ + uatomic_set(futex, -1); + cmm_smp_mb(); + + DBG("Futex n to 1 prepare done"); +} + +/* + * Wait futex. + */ +void futex_nto1_wait(int32_t *futex) +{ + cmm_smp_mb(); + + if (uatomic_read(futex) == -1) { + futex_async(futex, FUTEX_WAIT, -1, NULL, NULL, 0); + } + + DBG("Futex n to 1 wait done"); +} + +/* + * Wake 1 futex. + */ +void futex_nto1_wake(int32_t *futex) +{ + if (unlikely(uatomic_read(futex) == -1)) { + uatomic_set(futex, 0); + futex_async(futex, FUTEX_WAKE, 1, NULL, NULL, 0); + } + + DBG("Futex n to 1 wake done"); +} diff --git a/lttng-sessiond/futex.h b/lttng-sessiond/futex.h new file mode 100644 index 000000000..a056ec22b --- /dev/null +++ b/lttng-sessiond/futex.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_FUTEX_H +#define _LTT_FUTEX_H + +void futex_wait_update(int32_t *futex, int active); +void futex_nto1_prepare(int32_t *futex); +void futex_nto1_wait(int32_t *futex); +void futex_nto1_wake(int32_t *futex); + +#endif /* _LTT_FUTEX_H */ diff --git a/lttng-sessiond/hashtable.c b/lttng-sessiond/hashtable.c new file mode 100644 index 000000000..6bca1dac0 --- /dev/null +++ b/lttng-sessiond/hashtable.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include + +#include + +#include "hashtable.h" +#include "../hashtable/rculfhash.h" +#include "../hashtable/hash.h" + +struct cds_lfht *hashtable_new(unsigned long size) +{ + if (size == 0) { + size = DEFAULT_HT_SIZE; + } + + return cds_lfht_new(hash_key, hash_compare_key, 0x42UL, + size, size, CDS_LFHT_AUTO_RESIZE, NULL); +} + +struct cds_lfht *hashtable_new_str(unsigned long size) +{ + if (size == 0) { + size = DEFAULT_HT_SIZE; + } + + return cds_lfht_new(hash_key_str, hash_compare_key_str, 0x42UL, + size, size, CDS_LFHT_AUTO_RESIZE, NULL); +} + +struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter) +{ + /* Safety net */ + if (iter == NULL) { + return NULL; + } + + return cds_lfht_iter_get_node(iter); +} + +struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key, + size_t key_len, struct cds_lfht_iter *iter) +{ + /* Safety net */ + if (ht == NULL || iter == NULL || key == NULL) { + return NULL; + } + + cds_lfht_lookup(ht, key, key_len, iter); + + return hashtable_iter_get_node(iter); +} + +void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter) +{ + cds_lfht_first(ht, iter); +} + +void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter) +{ + cds_lfht_next(ht, iter); +} + +void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node) +{ + cds_lfht_add_unique(ht, node); +} + +void hashtable_node_init(struct cds_lfht_node *node, void *key, + size_t key_len) +{ + cds_lfht_node_init(node, key, key_len); +} + +int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter) +{ + /* Safety net */ + if (ht == NULL || iter == NULL) { + return -1; + } + + return cds_lfht_del(ht, iter); +} + +unsigned long hashtable_get_count(struct cds_lfht *ht) +{ + long ab, aa; + unsigned long count, removed; + + cds_lfht_count_nodes(ht, &ab, &count, &removed, &aa); + + return count; +} diff --git a/lttng-sessiond/hashtable.h b/lttng-sessiond/hashtable.h new file mode 100644 index 000000000..5174eed75 --- /dev/null +++ b/lttng-sessiond/hashtable.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_HASHTABLE_H +#define _LTT_HASHTABLE_H + +#include +#include "../hashtable/rculfhash.h" + +struct cds_lfht *hashtable_new(unsigned long size); +struct cds_lfht *hashtable_new_str(unsigned long size); + +struct cds_lfht_node *hashtable_iter_get_node(struct cds_lfht_iter *iter); +struct cds_lfht_node *hashtable_lookup(struct cds_lfht *ht, void *key, + size_t key_len, struct cds_lfht_iter *iter); + +void hashtable_get_first(struct cds_lfht *ht, struct cds_lfht_iter *iter); +void hashtable_get_next(struct cds_lfht *ht, struct cds_lfht_iter *iter); +void hashtable_add_unique(struct cds_lfht *ht, struct cds_lfht_node *node); +void hashtable_node_init(struct cds_lfht_node *node, + void *key, size_t key_len); + +int hashtable_del(struct cds_lfht *ht, struct cds_lfht_iter *iter); +unsigned long hashtable_get_count(struct cds_lfht *ht); + +#endif /* _LTT_HASHTABLE_H */ diff --git a/lttng-sessiond/kernel-ctl.c b/lttng-sessiond/kernel-ctl.c new file mode 100644 index 000000000..203c01050 --- /dev/null +++ b/lttng-sessiond/kernel-ctl.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kernel-ctl.h" + +/* + * Add context on a kernel channel. + */ +int kernel_add_channel_context(struct ltt_kernel_channel *chan, + struct lttng_kernel_context *ctx) +{ + int ret; + + DBG("Adding context to channel %s", chan->channel->name); + ret = kernctl_add_context(chan->fd, ctx); + if (ret < 0) { + if (errno != EEXIST) { + perror("add context ioctl"); + } else { + /* If EEXIST, we just ignore the error */ + ret = 0; + } + goto error; + } + + chan->ctx = malloc(sizeof(struct lttng_kernel_context)); + if (chan->ctx == NULL) { + perror("malloc event context"); + goto error; + } + + memcpy(chan->ctx, ctx, sizeof(struct lttng_kernel_context)); + + return 0; + +error: + return ret; +} + +/* + * Add context on a kernel event. + */ +int kernel_add_event_context(struct ltt_kernel_event *event, + struct lttng_kernel_context *ctx) +{ + int ret; + + DBG("Adding context to event %s", event->event->name); + ret = kernctl_add_context(event->fd, ctx); + if (ret < 0) { + perror("add context ioctl"); + goto error; + } + + event->ctx = malloc(sizeof(struct lttng_kernel_context)); + if (event->ctx == NULL) { + perror("malloc event context"); + goto error; + } + + memcpy(event->ctx, ctx, sizeof(struct lttng_kernel_context)); + + return 0; + +error: + return ret; +} + +/* + * Create a new kernel session, register it to the kernel tracer and add it to + * the session daemon session. + */ +int kernel_create_session(struct ltt_session *session, int tracer_fd) +{ + int ret; + struct ltt_kernel_session *lks; + + /* Allocate data structure */ + lks = trace_kernel_create_session(session->path); + if (lks == NULL) { + ret = -1; + goto error; + } + + /* Kernel tracer session creation */ + ret = kernctl_create_session(tracer_fd); + if (ret < 0) { + perror("ioctl kernel create session"); + goto error; + } + + lks->fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(lks->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + lks->consumer_fds_sent = 0; + session->kernel_session = lks; + + DBG("Kernel session created (fd: %d)", lks->fd); + + return 0; + +error: + return ret; +} + +/* + * Create a kernel channel, register it to the kernel tracer and add it to the + * kernel session. + */ +int kernel_create_channel(struct ltt_kernel_session *session, + struct lttng_channel *chan, char *path) +{ + int ret; + struct ltt_kernel_channel *lkc; + + /* Allocate kernel channel */ + lkc = trace_kernel_create_channel(chan, path); + if (lkc == NULL) { + goto error; + } + + /* Kernel tracer channel creation */ + ret = kernctl_create_channel(session->fd, &lkc->channel->attr); + if (ret < 0) { + perror("ioctl kernel create channel"); + goto error; + } + + /* Setup the channel fd */ + lkc->fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(lkc->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + /* Add channel to session */ + cds_list_add(&lkc->list, &session->channel_list.head); + session->channel_count++; + + DBG("Kernel channel %s created (fd: %d and path: %s)", + lkc->channel->name, lkc->fd, lkc->pathname); + + return 0; + +error: + return -1; +} + +/* + * Create a kernel event, enable it to the kernel tracer and add it to the + * channel event list of the kernel session. + */ +int kernel_create_event(struct lttng_event *ev, + struct ltt_kernel_channel *channel) +{ + int ret; + struct ltt_kernel_event *event; + + event = trace_kernel_create_event(ev); + if (event == NULL) { + ret = -1; + goto error; + } + + ret = kernctl_create_event(channel->fd, event->event); + if (ret < 0) { + if (errno != EEXIST) { + PERROR("create event ioctl"); + } + ret = -errno; + goto free_event; + } + + /* + * LTTNG_KERNEL_SYSCALL event creation will return 0 on success. However + * this FD must not be added to the event list. + */ + if (ret == 0 && event->event->instrumentation == LTTNG_KERNEL_SYSCALL) { + DBG2("Kernel event syscall creation success"); + goto end; + } + + event->fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(event->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + /* Add event to event list */ + cds_list_add(&event->list, &channel->events_list.head); + channel->event_count++; + + DBG("Event %s created (fd: %d)", ev->name, event->fd); + +end: + return 0; + +free_event: + free(event); +error: + return ret; +} + +/* + * Disable a kernel channel. + */ +int kernel_disable_channel(struct ltt_kernel_channel *chan) +{ + int ret; + + ret = kernctl_disable(chan->fd); + if (ret < 0) { + perror("disable chan ioctl"); + ret = errno; + goto error; + } + + chan->enabled = 0; + DBG("Kernel channel %s disabled (fd: %d)", chan->channel->name, chan->fd); + + return 0; + +error: + return ret; +} + +/* + * Enable a kernel channel. + */ +int kernel_enable_channel(struct ltt_kernel_channel *chan) +{ + int ret; + + ret = kernctl_enable(chan->fd); + if (ret < 0 && errno != EEXIST) { + perror("Enable kernel chan"); + goto error; + } + + chan->enabled = 1; + DBG("Kernel channel %s enabled (fd: %d)", chan->channel->name, chan->fd); + + return 0; + +error: + return ret; +} + +/* + * Enable a kernel event. + */ +int kernel_enable_event(struct ltt_kernel_event *event) +{ + int ret; + + ret = kernctl_enable(event->fd); + if (ret < 0 && errno != EEXIST) { + perror("enable kernel event"); + goto error; + } + + event->enabled = 1; + DBG("Kernel event %s enabled (fd: %d)", event->event->name, event->fd); + + return 0; + +error: + return ret; +} + +/* + * Disable a kernel event. + */ +int kernel_disable_event(struct ltt_kernel_event *event) +{ + int ret; + + ret = kernctl_disable(event->fd); + if (ret < 0 && errno != EEXIST) { + perror("disable kernel event"); + goto error; + } + + event->enabled = 0; + DBG("Kernel event %s disabled (fd: %d)", event->event->name, event->fd); + + return 0; + +error: + return ret; +} + +/* + * Create kernel metadata, open from the kernel tracer and add it to the + * kernel session. + */ +int kernel_open_metadata(struct ltt_kernel_session *session, char *path) +{ + int ret; + struct ltt_kernel_metadata *lkm; + + /* Allocate kernel metadata */ + lkm = trace_kernel_create_metadata(path); + if (lkm == NULL) { + goto error; + } + + /* Kernel tracer metadata creation */ + ret = kernctl_open_metadata(session->fd, &lkm->conf->attr); + if (ret < 0) { + goto error; + } + + lkm->fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(lkm->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + session->metadata = lkm; + + DBG("Kernel metadata opened (fd: %d and path: %s)", lkm->fd, lkm->pathname); + + return 0; + +error: + return -1; +} + +/* + * Start tracing session. + */ +int kernel_start_session(struct ltt_kernel_session *session) +{ + int ret; + + ret = kernctl_start_session(session->fd); + if (ret < 0) { + perror("ioctl start session"); + goto error; + } + + DBG("Kernel session started"); + + return 0; + +error: + return ret; +} + +/* + * Make a kernel wait to make sure in-flight probe have completed. + */ +void kernel_wait_quiescent(int fd) +{ + int ret; + + DBG("Kernel quiescent wait on %d", fd); + + ret = kernctl_wait_quiescent(fd); + if (ret < 0) { + perror("wait quiescent ioctl"); + ERR("Kernel quiescent wait failed"); + } +} + +/* + * Kernel calibrate + */ +int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate) +{ + int ret; + + ret = kernctl_calibrate(fd, calibrate); + if (ret < 0) { + perror("calibrate ioctl"); + return -1; + } + + return 0; +} + + +/* + * Force flush buffer of metadata. + */ +int kernel_metadata_flush_buffer(int fd) +{ + int ret; + + ret = kernctl_buffer_flush(fd); + if (ret < 0) { + ERR("Fail to flush metadata buffers %d (ret: %d", fd, ret); + } + + return 0; +} + +/* + * Force flush buffer for channel. + */ +int kernel_flush_buffer(struct ltt_kernel_channel *channel) +{ + int ret; + struct ltt_kernel_stream *stream; + + DBG("Flush buffer for channel %s", channel->channel->name); + + cds_list_for_each_entry(stream, &channel->stream_list.head, list) { + DBG("Flushing channel stream %d", stream->fd); + ret = kernctl_buffer_flush(stream->fd); + if (ret < 0) { + perror("ioctl"); + ERR("Fail to flush buffer for stream %d (ret: %d)", + stream->fd, ret); + } + } + + return 0; +} + +/* + * Stop tracing session. + */ +int kernel_stop_session(struct ltt_kernel_session *session) +{ + int ret; + + ret = kernctl_stop_session(session->fd); + if (ret < 0) { + goto error; + } + + DBG("Kernel session stopped"); + + return 0; + +error: + return ret; +} + +/* + * Open stream of channel, register it to the kernel tracer and add it + * to the stream list of the channel. + * + * Return the number of created stream. Else, a negative value. + */ +int kernel_open_channel_stream(struct ltt_kernel_channel *channel) +{ + int ret; + struct ltt_kernel_stream *lks; + + while ((ret = kernctl_create_stream(channel->fd)) > 0) { + lks = trace_kernel_create_stream(); + if (lks == NULL) { + close(ret); + goto error; + } + + lks->fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(lks->fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + ret = asprintf(&lks->pathname, "%s/%s_%d", + channel->pathname, channel->channel->name, channel->stream_count); + if (ret < 0) { + perror("asprintf kernel create stream"); + goto error; + } + + /* Add stream to channe stream list */ + cds_list_add(&lks->list, &channel->stream_list.head); + channel->stream_count++; + + DBG("Kernel stream %d created (fd: %d, state: %d, path: %s)", + channel->stream_count, lks->fd, lks->state, lks->pathname); + } + + return channel->stream_count; + +error: + return -1; +} + +/* + * Open the metadata stream and set it to the kernel session. + */ +int kernel_open_metadata_stream(struct ltt_kernel_session *session) +{ + int ret; + + ret = kernctl_create_stream(session->metadata->fd); + if (ret < 0) { + perror("kernel create metadata stream"); + goto error; + } + + DBG("Kernel metadata stream created (fd: %d)", ret); + session->metadata_stream_fd = ret; + /* Prevent fd duplication after execlp() */ + ret = fcntl(session->metadata_stream_fd, F_SETFD, FD_CLOEXEC); + if (ret < 0) { + perror("fcntl session fd"); + } + + return 0; + +error: + return -1; +} + +/* + * Get the event list from the kernel tracer and return the number of elements. + */ +ssize_t kernel_list_events(int tracer_fd, struct lttng_event **events) +{ + int fd, pos; + char *event; + size_t nbmem, count = 0; + ssize_t size; + FILE *fp; + struct lttng_event *elist; + + fd = kernctl_tracepoint_list(tracer_fd); + if (fd < 0) { + perror("kernel tracepoint list"); + goto error; + } + + fp = fdopen(fd, "r"); + if (fp == NULL) { + perror("kernel tracepoint list fdopen"); + goto error_fp; + } + + /* + * Init memory size counter + * See kernel-ctl.h for explanation of this value + */ + nbmem = KERNEL_EVENT_LIST_SIZE; + elist = malloc(sizeof(struct lttng_event) * nbmem); + + while ((size = fscanf(fp, "event { name = %m[^;]; };%n\n", &event, &pos)) == 1) { + if (count > nbmem) { + DBG("Reallocating event list from %zu to %zu bytes", nbmem, + nbmem + KERNEL_EVENT_LIST_SIZE); + /* Adding the default size again */ + nbmem += KERNEL_EVENT_LIST_SIZE; + elist = realloc(elist, nbmem); + if (elist == NULL) { + perror("realloc list events"); + count = -ENOMEM; + goto end; + } + } + strncpy(elist[count].name, event, LTTNG_SYMBOL_NAME_LEN); + elist[count].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + count++; + } + + *events = elist; + DBG("Kernel list events done (%zu events)", count); +end: + fclose(fp); /* closes both fp and fd */ + return count; + +error_fp: + close(fd); +error: + return -1; +} diff --git a/lttng-sessiond/kernel-ctl.h b/lttng-sessiond/kernel-ctl.h new file mode 100644 index 000000000..2fbaca91e --- /dev/null +++ b/lttng-sessiond/kernel-ctl.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_KERNEL_CTL_H +#define _LTT_KERNEL_CTL_H + +#include "session.h" +#include "trace-kernel.h" + +/* + * Default size for the event list when kernel_list_events is called. This size + * value is based on the initial LTTng 2.0 version set of tracepoints. + * + * This is NOT an upper bound because if the real event list size is bigger, + * dynamic reallocation is performed. + */ +#define KERNEL_EVENT_LIST_SIZE 80 + +int kernel_add_channel_context(struct ltt_kernel_channel *chan, + struct lttng_kernel_context *ctx); +int kernel_add_event_context(struct ltt_kernel_event *event, + struct lttng_kernel_context *ctx); +int kernel_create_session(struct ltt_session *session, int tracer_fd); +int kernel_create_channel(struct ltt_kernel_session *session, + struct lttng_channel *chan, char *path); +int kernel_create_event(struct lttng_event *ev, struct ltt_kernel_channel *channel); +int kernel_disable_channel(struct ltt_kernel_channel *chan); +int kernel_disable_event(struct ltt_kernel_event *event); +int kernel_enable_event(struct ltt_kernel_event *event); +int kernel_enable_channel(struct ltt_kernel_channel *chan); +int kernel_open_metadata(struct ltt_kernel_session *session, char *path); +int kernel_open_metadata_stream(struct ltt_kernel_session *session); +int kernel_open_channel_stream(struct ltt_kernel_channel *channel); +int kernel_flush_buffer(struct ltt_kernel_channel *channel); +int kernel_metadata_flush_buffer(int fd); +int kernel_start_session(struct ltt_kernel_session *session); +int kernel_stop_session(struct ltt_kernel_session *session); +ssize_t kernel_list_events(int tracer_fd, struct lttng_event **event_list); +void kernel_wait_quiescent(int fd); +int kernel_calibrate(int fd, struct lttng_kernel_calibrate *calibrate); + +#endif /* _LTT_KERNEL_CTL_H */ diff --git a/lttng-sessiond/lttng-sessiond.h b/lttng-sessiond/lttng-sessiond.h new file mode 100644 index 000000000..63de2e120 --- /dev/null +++ b/lttng-sessiond/lttng-sessiond.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_SESSIOND_H +#define _LTT_SESSIOND_H + +#define _LGPL_SOURCE +#include +#include + +#include "ust-app.h" + +#define DEFAULT_HOME_DIR "/tmp" +#define DEFAULT_UST_SOCK_DIR DEFAULT_HOME_DIR "/ust-app-socks" +#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" + +struct module_param { + const char *name; + int required; +}; + +/* LTTng kernel tracer modules list */ +const struct module_param kernel_modules_list[] = { + { "lttng-ftrace", 0 }, + { "lttng-kprobes", 0 }, + { "lttng-kretprobes", 0 }, + { "lib-ring-buffer", 1 }, + { "ltt-relay", 1 }, + { "ltt-ring-buffer-client-discard", 1 }, + { "ltt-ring-buffer-client-overwrite", 1 }, + { "ltt-ring-buffer-metadata-client", 1 }, + { "ltt-ring-buffer-client-mmap-discard", 1 }, + { "ltt-ring-buffer-client-mmap-overwrite", 1 }, + { "ltt-ring-buffer-metadata-mmap-client", 1 }, + { "lttng-probe-lttng", 1 }, + { "lttng-types", 0 }, + { "lttng-probe-block", 0 }, + { "lttng-probe-irq", 0 }, + { "lttng-probe-kvm", 0 }, + { "lttng-probe-sched", 0 }, +}; + +extern const char default_home_dir[], + default_tracing_group[], + default_ust_sock_dir[], + default_global_apps_pipe[]; + +/* + * This contains extra data needed for processing a command received by the + * session daemon from the lttng client. + */ +struct command_ctx { + int ust_sock; + unsigned int lttng_msg_size; + struct ltt_session *session; + struct lttcomm_lttng_msg *llm; + struct lttcomm_session_msg *lsm; +}; + +struct ust_command { + int sock; + struct ust_register_msg reg_msg; + struct cds_wfq_node node; +}; + +/* + * Queue used to enqueue UST registration request (ust_commant) and protected + * by a futex with a scheme N wakers / 1 waiters. See futex.c/.h + */ +struct ust_cmd_queue { + int32_t futex; + struct cds_wfq_queue queue; +}; + +#endif /* _LTT_SESSIOND_H */ diff --git a/lttng-sessiond/lttng-ust-abi.h b/lttng-sessiond/lttng-ust-abi.h new file mode 100644 index 000000000..5b025aeac --- /dev/null +++ b/lttng-sessiond/lttng-ust-abi.h @@ -0,0 +1,158 @@ +#ifndef _LTTNG_UST_ABI_H +#define _LTTNG_UST_ABI_H + +/* + * lttng-ust-abi.h + * + * Copyright 2010-2011 (c) - Mathieu Desnoyers + * + * LTTng-UST ABI header + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include + +#define LTTNG_UST_SYM_NAME_LEN 128 + +#define LTTNG_UST_COMM_VERSION_MAJOR 0 +#define LTTNG_UST_COMM_VERSION_MINOR 1 + +enum lttng_ust_instrumentation { + LTTNG_UST_TRACEPOINT = 0, + LTTNG_UST_PROBE = 1, + LTTNG_UST_FUNCTION = 2, +}; + +enum lttng_ust_output { + LTTNG_UST_MMAP = 0, +}; + +struct lttng_ust_tracer_version { + uint32_t version; + uint32_t patchlevel; + uint32_t sublevel; +}; + +struct lttng_ust_channel { + int overwrite; /* 1: overwrite, 0: discard */ + uint64_t subbuf_size; /* in bytes */ + uint64_t num_subbuf; + unsigned int switch_timer_interval; /* usecs */ + unsigned int read_timer_interval; /* usecs */ + enum lttng_ust_output output; /* output mode */ + /* The following fields are used internally within UST. */ + int shm_fd; + int wait_fd; + uint64_t memory_map_size; +}; + +/* + * This structure is only used internally within UST. It is not per-se + * part of the communication between sessiond and UST. + */ +struct lttng_ust_stream { + int shm_fd; + int wait_fd; + uint64_t memory_map_size; +}; + +struct lttng_ust_event { + char name[LTTNG_UST_SYM_NAME_LEN]; /* event name */ + enum lttng_ust_instrumentation instrumentation; + /* Per instrumentation type configuration */ + union { + } u; +}; + +enum lttng_ust_context_type { + LTTNG_UST_CONTEXT_VTID = 0, + LTTNG_UST_CONTEXT_VPID = 1, + LTTNG_UST_CONTEXT_PTHREAD_ID = 2, + LTTNG_UST_CONTEXT_PROCNAME = 3, +}; + +struct lttng_ust_context { + enum lttng_ust_context_type ctx; + union { + } u; +}; + +/* + * Tracer channel attributes. + */ +struct lttng_ust_channel_attr { + int overwrite; /* 1: overwrite, 0: discard */ + uint64_t subbuf_size; /* bytes */ + uint64_t num_subbuf; /* power of 2 */ + unsigned int switch_timer_interval; /* usec */ + unsigned int read_timer_interval; /* usec */ + enum lttng_ust_output output; /* splice, mmap */ +}; + +struct lttng_ust_object_data { + int handle; + int shm_fd; + int wait_fd; + uint64_t memory_map_size; +}; + +#define _UST_CMD(minor) (minor) +#define _UST_CMDR(minor, type) (minor) +#define _UST_CMDW(minor, type) (minor) + +/* Handled by object descriptor */ +#define LTTNG_UST_RELEASE _UST_CMD(0x1) + +/* Handled by object cmd */ + +/* LTTng-UST commands */ +#define LTTNG_UST_SESSION _UST_CMD(0x40) +#define LTTNG_UST_TRACER_VERSION \ + _UST_CMDR(0x41, struct lttng_ust_tracer_version) +#define LTTNG_UST_TRACEPOINT_LIST _UST_CMD(0x42) +#define LTTNG_UST_WAIT_QUIESCENT _UST_CMD(0x43) +#define LTTNG_UST_REGISTER_DONE _UST_CMD(0x44) + +/* Session FD commands */ +#define LTTNG_UST_METADATA \ + _UST_CMDW(0x50, struct lttng_ust_channel) +#define LTTNG_UST_CHANNEL \ + _UST_CMDW(0x51, struct lttng_ust_channel) +#define LTTNG_UST_SESSION_START _UST_CMD(0x52) +#define LTTNG_UST_SESSION_STOP _UST_CMD(0x53) + +/* Channel FD commands */ +#define LTTNG_UST_STREAM _UST_CMD(0x60) +#define LTTNG_UST_EVENT \ + _UST_CMDW(0x61, struct lttng_ust_event) + +/* Event and Channel FD commands */ +#define LTTNG_UST_CONTEXT \ + _UST_CMDW(0x70, struct lttng_ust_context) +#define LTTNG_UST_FLUSH_BUFFER \ + _UST_CMD(0x71) + +/* Event, Channel and Session commands */ +#define LTTNG_UST_ENABLE _UST_CMD(0x80) +#define LTTNG_UST_DISABLE _UST_CMD(0x81) + +#define LTTNG_UST_ROOT_HANDLE 0 + +struct obj; + +struct objd_ops { + long (*cmd)(int objd, unsigned int cmd, unsigned long arg); + int (*release)(int objd); +}; + +/* Create root handle. Always ID 0. */ +int lttng_abi_create_root_handle(void); + +const struct objd_ops *objd_ops(int id); +int objd_unref(int id); + +void lttng_ust_abi_exit(void); +void ltt_events_exit(void); + +#endif /* _LTTNG_UST_ABI_H */ diff --git a/lttng-sessiond/lttng-ust-ctl.h b/lttng-sessiond/lttng-ust-ctl.h new file mode 100644 index 000000000..de5eeafee --- /dev/null +++ b/lttng-sessiond/lttng-ust-ctl.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTTNG_UST_CTL_H +#define _LTTNG_UST_CTL_H + +#include + +int ustctl_register_done(int sock); +int ustctl_create_session(int sock); +int ustctl_open_metadata(int sock, int session_handle, + struct lttng_ust_channel_attr *chops, + struct lttng_ust_object_data **metadata_data); +int ustctl_create_channel(int sock, int session_handle, + struct lttng_ust_channel_attr *chops, + struct lttng_ust_object_data **channel_data); +int ustctl_create_stream(int sock, struct lttng_ust_object_data *channel_data, + struct lttng_ust_object_data **stream_data); +int ustctl_create_event(int sock, struct lttng_ust_event *ev, + struct lttng_ust_object_data *channel_data, + struct lttng_ust_object_data **event_data); +int ustctl_add_context(int sock, struct lttng_ust_context *ctx, + struct lttng_ust_object_data *obj_data, + struct lttng_ust_object_data **context_data); + +int ustctl_enable(int sock, struct lttng_ust_object_data *object); +int ustctl_disable(int sock, struct lttng_ust_object_data *object); +int ustctl_start_session(int sock, int handle); +int ustctl_stop_session(int sock, int handle); + +int ustctl_tracepoint_list(int sock); /* not implemented yet */ +int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v); +int ustctl_wait_quiescent(int sock); + +/* Flush each buffers in this channel */ +int ustctl_flush_buffer(int sock, struct lttng_ust_object_data *channel_data); + +/* not implemented yet */ +struct lttng_ust_calibrate; +int ustctl_calibrate(int sock, struct lttng_ust_calibrate *calibrate); + +/* + * Map channel lttng_ust_shm_handle and add streams. Typically performed by the + * consumer to map the objects into its memory space. + */ +struct lttng_ust_shm_handle *ustctl_map_channel(struct lttng_ust_object_data *chan_data); +int ustctl_add_stream(struct lttng_ust_shm_handle *lttng_ust_shm_handle, + struct lttng_ust_object_data *stream_data); +/* + * Note: the lttng_ust_object_data from which the lttng_ust_shm_handle is derived can only + * be released after unmapping the handle. + */ +void ustctl_unmap_channel(struct lttng_ust_shm_handle *lttng_ust_shm_handle); + +/* Buffer operations */ + +struct lttng_ust_shm_handle; +struct lttng_ust_lib_ring_buffer; + +/* Open/close stream buffers for read */ +struct lttng_ust_lib_ring_buffer *ustctl_open_stream_read(struct lttng_ust_shm_handle *handle, + int cpu); +void ustctl_close_stream_read(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); + +/* For mmap mode, readable without "get" operation */ +int ustctl_get_mmap_len(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, + unsigned long *len); +int ustctl_get_max_subbuf_size(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, + unsigned long *len); + +/* + * For mmap mode, operate on the current packet (between get/put or + * get_next/put_next). + */ +void *ustctl_get_mmap_base(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); +int ustctl_get_mmap_read_offset(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *off); +int ustctl_get_subbuf_size(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *len); +int ustctl_get_padded_subbuf_size(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *len); +int ustctl_get_next_subbuf(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); +int ustctl_put_next_subbuf(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); + +/* snapshot */ + +int ustctl_snapshot(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); +int ustctl_snapshot_get_consumed(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); +int ustctl_snapshot_get_produced(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); +int ustctl_get_subbuf(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos); +int ustctl_put_subbuf(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf); + +/* Release object created by members of this API */ +void release_object(int sock, struct lttng_ust_object_data *data); + +#endif /* _LTTNG_UST_CTL_H */ diff --git a/lttng-sessiond/main.c b/lttng-sessiond/main.c new file mode 100644 index 000000000..ea3853c1a --- /dev/null +++ b/lttng-sessiond/main.c @@ -0,0 +1,3913 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "channel.h" +#include "compat/poll.h" +#include "context.h" +#include "event.h" +#include "futex.h" +#include "hashtable.h" +#include "kernel-ctl.h" +#include "lttng-sessiond.h" +#include "shm.h" +#include "ust-app.h" +#include "ust-ctl.h" +#include "utils.h" + +struct consumer_data { + enum lttng_consumer_type type; + + pthread_t thread; /* Worker thread interacting with the consumer */ + sem_t sem; + + /* Mutex to control consumerd pid assignation */ + pthread_mutex_t pid_mutex; + pid_t pid; + + int err_sock; + int cmd_sock; + + /* consumer error and command Unix socket path */ + char err_unix_sock_path[PATH_MAX]; + char cmd_unix_sock_path[PATH_MAX]; +}; + +/* Const values */ +const char default_home_dir[] = DEFAULT_HOME_DIR; +const char default_tracing_group[] = LTTNG_DEFAULT_TRACING_GROUP; +const char default_ust_sock_dir[] = DEFAULT_UST_SOCK_DIR; +const char default_global_apps_pipe[] = DEFAULT_GLOBAL_APPS_PIPE; + +/* Variables */ +int opt_verbose; /* Not static for lttngerr.h */ +int opt_verbose_consumer; /* Not static for lttngerr.h */ +int opt_quiet; /* Not static for lttngerr.h */ + +const char *progname; +const char *opt_tracing_group; +static int opt_sig_parent; +static int opt_daemon; +static int is_root; /* Set to 1 if the daemon is running as root */ +static pid_t ppid; /* Parent PID for --sig-parent option */ + +/* Consumer daemon specific control data */ +static struct consumer_data kconsumer_data = { + .type = LTTNG_CONSUMER_KERNEL, +}; +static struct consumer_data ustconsumer_data = { + .type = LTTNG_CONSUMER_UST, +}; + +static int dispatch_thread_exit; + +/* Global application Unix socket path */ +static char apps_unix_sock_path[PATH_MAX]; +/* Global client Unix socket path */ +static char client_unix_sock_path[PATH_MAX]; +/* global wait shm path for UST */ +static char wait_shm_path[PATH_MAX]; + +/* Sockets and FDs */ +static int client_sock; +static int apps_sock; +static int kernel_tracer_fd; +static int kernel_poll_pipe[2]; + +/* + * Quit pipe for all threads. This permits a single cancellation point + * for all threads when receiving an event on the pipe. + */ +static int thread_quit_pipe[2]; + +/* + * This pipe is used to inform the thread managing application communication + * that a command is queued and ready to be processed. + */ +static int apps_cmd_pipe[2]; + +/* Pthread, Mutexes and Semaphores */ +static pthread_t apps_thread; +static pthread_t reg_apps_thread; +static pthread_t client_thread; +static pthread_t kernel_thread; +static pthread_t dispatch_thread; + + +/* + * UST registration command queue. This queue is tied with a futex and uses a N + * wakers / 1 waiter implemented and detailed in futex.c/.h + * + * The thread_manage_apps and thread_dispatch_ust_registration interact with + * this queue and the wait/wake scheme. + */ +static struct ust_cmd_queue ust_cmd_queue; + +/* + * Pointer initialized before thread creation. + * + * This points to the tracing session list containing the session count and a + * mutex lock. The lock MUST be taken if you iterate over the list. The lock + * MUST NOT be taken if you call a public function in session.c. + * + * The lock is nested inside the structure: session_list_ptr->lock. Please use + * session_lock_list and session_unlock_list for lock acquisition. + */ +static struct ltt_session_list *session_list_ptr; + +/* + * 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 ret; + + if (events == NULL || size == 0) { + ret = -1; + goto error; + } + + ret = lttng_poll_create(events, size, LTTNG_CLOEXEC); + if (ret < 0) { + goto error; + } + + /* Add quit pipe */ + ret = lttng_poll_add(events, thread_quit_pipe[0], LPOLLIN); + if (ret < 0) { + goto error; + } + + return 0; + +error: + return ret; +} + +/* + * Check if the thread quit pipe was triggered. + * + * Return 1 if it was triggered else 0; + */ +static int check_thread_quit_pipe(int fd, uint32_t events) +{ + if (fd == thread_quit_pipe[0] && (events & LPOLLIN)) { + return 1; + } + + return 0; +} + +/* + * Remove modules in reverse load order. + */ +static int modprobe_remove_kernel_modules(void) +{ + int ret = 0, i; + char modprobe[256]; + + for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) { + ret = snprintf(modprobe, sizeof(modprobe), + "/sbin/modprobe -r -q %s", + kernel_modules_list[i].name); + if (ret < 0) { + perror("snprintf modprobe -r"); + goto error; + } + modprobe[sizeof(modprobe) - 1] = '\0'; + ret = system(modprobe); + if (ret == -1) { + ERR("Unable to launch modprobe -r for module %s", + kernel_modules_list[i].name); + } else if (kernel_modules_list[i].required + && WEXITSTATUS(ret) != 0) { + ERR("Unable to remove module %s", + kernel_modules_list[i].name); + } else { + DBG("Modprobe removal successful %s", + kernel_modules_list[i].name); + } + } + +error: + return ret; +} + +/* + * Return group ID of the tracing group or -1 if not found. + */ +static gid_t allowed_group(void) +{ + struct group *grp; + + if (opt_tracing_group) { + grp = getgrnam(opt_tracing_group); + } else { + grp = getgrnam(default_tracing_group); + } + if (!grp) { + return -1; + } else { + return grp->gr_gid; + } +} + +/* + * Init thread quit pipe. + * + * Return -1 on error or 0 if all pipes are created. + */ +static int init_thread_quit_pipe(void) +{ + int ret; + + ret = pipe2(thread_quit_pipe, O_CLOEXEC); + if (ret < 0) { + perror("thread quit pipe"); + goto error; + } + +error: + return ret; +} + +/* + * Complete teardown of a kernel session. This free all data structure related + * to a kernel session and update counter. + */ +static void teardown_kernel_session(struct ltt_session *session) +{ + if (session->kernel_session != NULL) { + DBG("Tearing down kernel session"); + + /* + * If a custom kernel consumer was registered, close the socket before + * tearing down the complete kernel session structure + */ + if (session->kernel_session->consumer_fd != kconsumer_data.cmd_sock) { + lttcomm_close_unix_sock(session->kernel_session->consumer_fd); + } + + trace_kernel_destroy_session(session->kernel_session); + /* Extra precaution */ + session->kernel_session = NULL; + } +} + +/* + * Complete teardown of all UST sessions. This will free everything on his path + * and destroy the core essence of all ust sessions :) + */ +static void teardown_ust_session(struct ltt_session *session) +{ + DBG("Tearing down UST session(s)"); + + trace_ust_destroy_session(session->ust_session); +} + +/* + * Stop all threads by closing the thread quit pipe. + */ +static void stop_threads(void) +{ + int ret; + + /* Stopping all threads */ + DBG("Terminating all threads"); + ret = notify_thread_pipe(thread_quit_pipe[1]); + if (ret < 0) { + ERR("write error on thread quit pipe"); + } + + /* Dispatch thread */ + dispatch_thread_exit = 1; + futex_nto1_wake(&ust_cmd_queue.futex); +} + +/* + * Cleanup the daemon + */ +static void cleanup(void) +{ + int ret; + char *cmd; + struct ltt_session *sess, *stmp; + + DBG("Cleaning up"); + + /* */ + MSG("%c[%d;%dm*** assert failed *** ==> %c[%dm%c[%d;%dm" + "Matthew, BEET driven development works!%c[%dm", + 27, 1, 31, 27, 0, 27, 1, 33, 27, 0); + /* */ + + if (is_root) { + DBG("Removing %s directory", LTTNG_RUNDIR); + ret = asprintf(&cmd, "rm -rf " LTTNG_RUNDIR); + if (ret < 0) { + ERR("asprintf failed. Something is really wrong!"); + } + + /* Remove lttng run directory */ + ret = system(cmd); + if (ret < 0) { + ERR("Unable to clean " LTTNG_RUNDIR); + } + } + + DBG("Cleaning up all session"); + + /* Destroy session list mutex */ + if (session_list_ptr != NULL) { + pthread_mutex_destroy(&session_list_ptr->lock); + + /* Cleanup ALL session */ + cds_list_for_each_entry_safe(sess, stmp, + &session_list_ptr->head, list) { + teardown_kernel_session(sess); + teardown_ust_session(sess); + free(sess); + } + } + + DBG("Closing all UST sockets"); + ust_app_clean_list(); + + pthread_mutex_destroy(&kconsumer_data.pid_mutex); + + DBG("Closing kernel fd"); + close(kernel_tracer_fd); + + if (is_root) { + DBG("Unloading kernel modules"); + modprobe_remove_kernel_modules(); + } + + close(thread_quit_pipe[0]); + close(thread_quit_pipe[1]); +} + +/* + * Send data on a unix socket using the liblttsessiondcomm API. + * + * Return lttcomm error code. + */ +static int send_unix_sock(int sock, void *buf, size_t len) +{ + /* Check valid length */ + if (len <= 0) { + return -1; + } + + return lttcomm_send_unix_sock(sock, buf, len); +} + +/* + * Free memory of a command context structure. + */ +static void clean_command_ctx(struct command_ctx **cmd_ctx) +{ + DBG("Clean command context structure"); + if (*cmd_ctx) { + if ((*cmd_ctx)->llm) { + free((*cmd_ctx)->llm); + } + if ((*cmd_ctx)->lsm) { + free((*cmd_ctx)->lsm); + } + free(*cmd_ctx); + *cmd_ctx = NULL; + } +} + +/* + * Send all stream fds of kernel channel to the consumer. + */ +static int send_kconsumer_channel_streams(struct consumer_data *consumer_data, + int sock, struct ltt_kernel_channel *channel) +{ + int ret; + struct ltt_kernel_stream *stream; + struct lttcomm_consumer_msg lkm; + + DBG("Sending streams of channel %s to kernel consumer", + channel->channel->name); + + /* Send channel */ + lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; + lkm.u.channel.channel_key = channel->fd; + lkm.u.channel.max_sb_size = channel->channel->attr.subbuf_size; + lkm.u.channel.mmap_len = 0; /* for kernel */ + DBG("Sending channel %d to consumer", lkm.u.channel.channel_key); + ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); + if (ret < 0) { + perror("send consumer channel"); + goto error; + } + + /* Send streams */ + cds_list_for_each_entry(stream, &channel->stream_list.head, list) { + if (!stream->fd) { + continue; + } + lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; + lkm.u.stream.channel_key = channel->fd; + lkm.u.stream.stream_key = stream->fd; + lkm.u.stream.state = stream->state; + lkm.u.stream.output = channel->channel->attr.output; + lkm.u.stream.mmap_len = 0; /* for kernel */ + strncpy(lkm.u.stream.path_name, stream->pathname, PATH_MAX - 1); + lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; + DBG("Sending stream %d to consumer", lkm.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); + if (ret < 0) { + perror("send consumer stream"); + goto error; + } + ret = lttcomm_send_fds_unix_sock(sock, &stream->fd, 1); + if (ret < 0) { + perror("send consumer stream ancillary data"); + goto error; + } + } + + DBG("consumer channel streams sent"); + + return 0; + +error: + return ret; +} + +/* + * Send all stream fds of the kernel session to the consumer. + */ +static int send_kconsumer_session_streams(struct consumer_data *consumer_data, + struct ltt_kernel_session *session) +{ + int ret; + struct ltt_kernel_channel *chan; + struct lttcomm_consumer_msg lkm; + int sock = session->consumer_fd; + + DBG("Sending metadata stream fd"); + + /* Extra protection. It's NOT supposed to be set to 0 at this point */ + if (session->consumer_fd == 0) { + session->consumer_fd = consumer_data->cmd_sock; + } + + if (session->metadata_stream_fd != 0) { + /* Send metadata channel fd */ + lkm.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; + lkm.u.channel.channel_key = session->metadata->fd; + lkm.u.channel.max_sb_size = session->metadata->conf->attr.subbuf_size; + lkm.u.channel.mmap_len = 0; /* for kernel */ + DBG("Sending metadata channel %d to consumer", lkm.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); + if (ret < 0) { + perror("send consumer channel"); + goto error; + } + + /* Send metadata stream fd */ + lkm.cmd_type = LTTNG_CONSUMER_ADD_STREAM; + lkm.u.stream.channel_key = session->metadata->fd; + lkm.u.stream.stream_key = session->metadata_stream_fd; + lkm.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; + lkm.u.stream.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; + lkm.u.stream.mmap_len = 0; /* for kernel */ + strncpy(lkm.u.stream.path_name, session->metadata->pathname, PATH_MAX - 1); + lkm.u.stream.path_name[PATH_MAX - 1] = '\0'; + DBG("Sending metadata stream %d to consumer", lkm.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lkm, sizeof(lkm)); + if (ret < 0) { + perror("send consumer stream"); + goto error; + } + ret = lttcomm_send_fds_unix_sock(sock, &session->metadata_stream_fd, 1); + if (ret < 0) { + perror("send consumer stream"); + goto error; + } + } + + cds_list_for_each_entry(chan, &session->channel_list.head, list) { + ret = send_kconsumer_channel_streams(consumer_data, sock, chan); + if (ret < 0) { + goto error; + } + } + + DBG("consumer fds (metadata and channel streams) sent"); + + return 0; + +error: + return ret; +} + +/* + * Notify UST applications using the shm mmap futex. + */ +static int notify_ust_apps(int active) +{ + char *wait_shm_mmap; + + DBG("Notifying applications of session daemon state: %d", active); + + /* See shm.c for this call implying mmap, shm and futex calls */ + wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root); + if (wait_shm_mmap == NULL) { + goto error; + } + + /* Wake waiting process */ + futex_wait_update((int32_t *) wait_shm_mmap, active); + + /* Apps notified successfully */ + return 0; + +error: + return -1; +} + +/* + * Setup the outgoing data buffer for the response (llm) by allocating the + * right amount of memory and copying the original information from the lsm + * structure. + * + * Return total size of the buffer pointed by buf. + */ +static int setup_lttng_msg(struct command_ctx *cmd_ctx, size_t size) +{ + int ret, buf_size; + + buf_size = size; + + cmd_ctx->llm = malloc(sizeof(struct lttcomm_lttng_msg) + buf_size); + if (cmd_ctx->llm == NULL) { + perror("malloc"); + ret = -ENOMEM; + goto error; + } + + /* Copy common data */ + cmd_ctx->llm->cmd_type = cmd_ctx->lsm->cmd_type; + cmd_ctx->llm->pid = cmd_ctx->lsm->domain.attr.pid; + + cmd_ctx->llm->data_size = size; + cmd_ctx->lttng_msg_size = sizeof(struct lttcomm_lttng_msg) + buf_size; + + return buf_size; + +error: + return ret; +} + +/* + * Update the kernel poll set of all channel fd available over all tracing + * session. Add the wakeup pipe at the end of the set. + */ +static int update_kernel_poll(struct lttng_poll_event *events) +{ + int ret; + struct ltt_session *session; + struct ltt_kernel_channel *channel; + + DBG("Updating kernel poll set"); + + session_lock_list(); + cds_list_for_each_entry(session, &session_list_ptr->head, list) { + session_lock(session); + if (session->kernel_session == NULL) { + session_unlock(session); + continue; + } + + cds_list_for_each_entry(channel, + &session->kernel_session->channel_list.head, list) { + /* Add channel fd to the kernel poll set */ + ret = lttng_poll_add(events, channel->fd, LPOLLIN | LPOLLRDNORM); + if (ret < 0) { + session_unlock(session); + goto error; + } + DBG("Channel fd %d added to kernel set", channel->fd); + } + session_unlock(session); + } + session_unlock_list(); + + return 0; + +error: + session_unlock_list(); + return -1; +} + +/* + * Find the channel fd from 'fd' over all tracing session. When found, check + * for new channel stream and send those stream fds to the kernel consumer. + * + * Useful for CPU hotplug feature. + */ +static int update_kernel_stream(struct consumer_data *consumer_data, int fd) +{ + int ret = 0; + struct ltt_session *session; + struct ltt_kernel_channel *channel; + + DBG("Updating kernel streams for channel fd %d", fd); + + session_lock_list(); + cds_list_for_each_entry(session, &session_list_ptr->head, list) { + session_lock(session); + if (session->kernel_session == NULL) { + session_unlock(session); + continue; + } + + /* This is not suppose to be 0 but this is an extra security check */ + if (session->kernel_session->consumer_fd == 0) { + session->kernel_session->consumer_fd = consumer_data->cmd_sock; + } + + cds_list_for_each_entry(channel, + &session->kernel_session->channel_list.head, list) { + if (channel->fd == fd) { + DBG("Channel found, updating kernel streams"); + ret = kernel_open_channel_stream(channel); + if (ret < 0) { + goto error; + } + + /* + * Have we already sent fds to the consumer? If yes, it means + * that tracing is started so it is safe to send our updated + * stream fds. + */ + if (session->kernel_session->consumer_fds_sent == 1) { + ret = send_kconsumer_channel_streams(consumer_data, + session->kernel_session->consumer_fd, channel); + if (ret < 0) { + goto error; + } + } + goto error; + } + } + session_unlock(session); + } + session_unlock_list(); + return ret; + +error: + session_unlock(session); + session_unlock_list(); + return ret; +} + +/* + * This thread manage event coming from the kernel. + * + * Features supported in this thread: + * -) CPU Hotplug + */ +static void *thread_manage_kernel(void *data) +{ + int ret, i, pollfd, update_poll_flag = 1; + uint32_t revents, nb_fd; + char tmp; + struct lttng_poll_event events; + + DBG("Thread manage kernel started"); + + ret = create_thread_poll_set(&events, 2); + if (ret < 0) { + goto error; + } + + ret = lttng_poll_add(&events, kernel_poll_pipe[0], LPOLLIN); + if (ret < 0) { + goto error; + } + + while (1) { + if (update_poll_flag == 1) { + /* + * Reset number of fd in the poll set. Always 2 since there is the thread + * quit pipe and the kernel pipe. + */ + events.nb_fd = 2; + + ret = update_kernel_poll(&events); + if (ret < 0) { + goto error; + } + update_poll_flag = 0; + } + + nb_fd = LTTNG_POLL_GETNB(&events); + + DBG("Thread kernel polling on %d fds", nb_fd); + + /* Zeroed the poll events */ + lttng_poll_reset(&events); + + /* Poll infinite value of time */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } else if (ret == 0) { + /* Should not happen since timeout is infinite */ + ERR("Return value of poll is 0 with an infinite timeout.\n" + "This should not have happened! Continuing..."); + continue; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Check for data on kernel pipe */ + if (pollfd == kernel_poll_pipe[0] && (revents & LPOLLIN)) { + ret = read(kernel_poll_pipe[0], &tmp, 1); + update_poll_flag = 1; + continue; + } else { + /* + * New CPU detected by the kernel. Adding kernel stream to + * kernel session and updating the kernel consumer + */ + if (revents & LPOLLIN) { + ret = update_kernel_stream(&kconsumer_data, pollfd); + if (ret < 0) { + continue; + } + break; + /* + * TODO: We might want to handle the LPOLLERR | LPOLLHUP + * and unregister kernel stream at this point. + */ + } + } + } + } + +error: + DBG("Kernel thread dying"); + close(kernel_poll_pipe[0]); + close(kernel_poll_pipe[1]); + + lttng_poll_clean(&events); + + return NULL; +} + +/* + * This thread manage the consumer error sent back to the session daemon. + */ +static void *thread_manage_consumer(void *data) +{ + int sock = 0, i, ret, pollfd; + uint32_t revents, nb_fd; + enum lttcomm_return_code code; + struct lttng_poll_event events; + struct consumer_data *consumer_data = data; + + DBG("[thread] Manage consumer started"); + + ret = lttcomm_listen_unix_sock(consumer_data->err_sock); + if (ret < 0) { + goto error; + } + + /* + * 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); + if (ret < 0) { + goto error; + } + + ret = lttng_poll_add(&events, consumer_data->err_sock, LPOLLIN | LPOLLRDHUP); + if (ret < 0) { + goto error; + } + + nb_fd = LTTNG_POLL_GETNB(&events); + + /* Inifinite blocking call, waiting for transmission */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Event on the registration socket */ + if (pollfd == consumer_data->err_sock) { + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("consumer err socket poll error"); + goto error; + } + } + } + + sock = lttcomm_accept_unix_sock(consumer_data->err_sock); + if (sock < 0) { + goto error; + } + + DBG2("Receiving code from consumer err_sock"); + + /* Getting status code from kconsumerd */ + ret = lttcomm_recv_unix_sock(sock, &code, + sizeof(enum lttcomm_return_code)); + if (ret <= 0) { + goto error; + } + + if (code == CONSUMERD_COMMAND_SOCK_READY) { + consumer_data->cmd_sock = + lttcomm_connect_unix_sock(consumer_data->cmd_unix_sock_path); + if (consumer_data->cmd_sock < 0) { + sem_post(&consumer_data->sem); + PERROR("consumer connect"); + goto error; + } + /* Signal condition to tell that the kconsumerd is ready */ + sem_post(&consumer_data->sem); + DBG("consumer command socket ready"); + } else { + ERR("consumer error when waiting for SOCK_READY : %s", + lttcomm_get_readable_code(-code)); + goto error; + } + + /* Remove the kconsumerd error sock since we've established a connexion */ + ret = lttng_poll_del(&events, consumer_data->err_sock); + if (ret < 0) { + goto error; + } + + ret = lttng_poll_add(&events, sock, LPOLLIN | LPOLLRDHUP); + if (ret < 0) { + goto error; + } + + /* Update number of fd */ + nb_fd = LTTNG_POLL_GETNB(&events); + + /* Inifinite blocking call, waiting for transmission */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Event on the kconsumerd socket */ + if (pollfd == sock) { + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("consumer err socket second poll error"); + goto error; + } + } + } + + /* Wait for any kconsumerd error */ + ret = lttcomm_recv_unix_sock(sock, &code, + sizeof(enum lttcomm_return_code)); + if (ret <= 0) { + ERR("consumer closed the command socket"); + goto error; + } + + ERR("consumer return code : %s", lttcomm_get_readable_code(-code)); + +error: + DBG("consumer thread dying"); + close(consumer_data->err_sock); + close(consumer_data->cmd_sock); + close(sock); + + unlink(consumer_data->err_unix_sock_path); + unlink(consumer_data->cmd_unix_sock_path); + consumer_data->pid = 0; + + lttng_poll_clean(&events); + + return NULL; +} + +/* + * This thread manage application communication. + */ +static void *thread_manage_apps(void *data) +{ + int i, ret, pollfd; + uint32_t revents, nb_fd; + struct ust_command ust_cmd; + struct lttng_poll_event events; + + DBG("[thread] Manage application started"); + + rcu_register_thread(); + rcu_thread_online(); + + ret = create_thread_poll_set(&events, 2); + if (ret < 0) { + goto error; + } + + ret = lttng_poll_add(&events, apps_cmd_pipe[0], LPOLLIN | LPOLLRDHUP); + if (ret < 0) { + goto error; + } + + while (1) { + /* Zeroed the events structure */ + lttng_poll_reset(&events); + + nb_fd = LTTNG_POLL_GETNB(&events); + + DBG("Apps thread polling on %d fds", nb_fd); + + /* Inifinite blocking call, waiting for transmission */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Inspect the apps cmd pipe */ + if (pollfd == apps_cmd_pipe[0]) { + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("Apps command pipe error"); + goto error; + } else if (revents & LPOLLIN) { + /* Empty pipe */ + ret = read(apps_cmd_pipe[0], &ust_cmd, sizeof(ust_cmd)); + if (ret < 0 || ret < sizeof(ust_cmd)) { + perror("read apps cmd pipe"); + goto error; + } + + /* Register applicaton to the session daemon */ + ret = ust_app_register(&ust_cmd.reg_msg, + ust_cmd.sock); + if (ret < 0) { + /* Only critical ENOMEM error can be returned here */ + goto error; + } + + ret = ustctl_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 just need here to monitor the close of the UST + * socket and poll set monitor those by default. + */ + ret = lttng_poll_add(&events, ust_cmd.sock, 0); + if (ret < 0) { + goto error; + } + + DBG("Apps with sock %d added to poll set", + ust_cmd.sock); + } + break; + } + } else { + /* + * At this point, we know that a registered application made + * the event at poll_wait. + */ + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + /* Removing from the poll set */ + ret = lttng_poll_del(&events, pollfd); + if (ret < 0) { + goto error; + } + + /* Socket closed */ + ust_app_unregister(pollfd); + break; + } + } + } + } + +error: + DBG("Application communication apps dying"); + close(apps_cmd_pipe[0]); + close(apps_cmd_pipe[1]); + + lttng_poll_clean(&events); + + rcu_thread_offline(); + rcu_unregister_thread(); + return NULL; +} + +/* + * Dispatch request from the registration threads to the application + * communication thread. + */ +static void *thread_dispatch_ust_registration(void *data) +{ + int ret; + struct cds_wfq_node *node; + struct ust_command *ust_cmd = NULL; + + DBG("[thread] Dispatch UST command started"); + + while (!dispatch_thread_exit) { + /* Atomically prepare the queue futex */ + futex_nto1_prepare(&ust_cmd_queue.futex); + + do { + /* Dequeue command for registration */ + node = cds_wfq_dequeue_blocking(&ust_cmd_queue.queue); + if (node == NULL) { + DBG("Woken up but nothing in the UST command queue"); + /* Continue thread execution */ + break; + } + + ust_cmd = caa_container_of(node, struct ust_command, node); + + DBG("Dispatching UST registration pid:%d ppid:%d uid:%d" + " gid:%d sock:%d name:%s (version %d.%d)", + ust_cmd->reg_msg.pid, ust_cmd->reg_msg.ppid, + 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 :) + */ + ret = write(apps_cmd_pipe[1], ust_cmd, + sizeof(struct ust_command)); + if (ret < 0) { + 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; + } + } + free(ust_cmd); + } while (node != NULL); + + /* Futex wait on queue. Blocking call on futex() */ + futex_nto1_wait(&ust_cmd_queue.futex); + } + +error: + DBG("Dispatch thread dying"); + return NULL; +} + +/* + * This thread manage application registration. + */ +static void *thread_registration_apps(void *data) +{ + int sock = 0, i, ret, pollfd; + uint32_t revents, nb_fd; + struct lttng_poll_event events; + /* + * Get allocated in this thread, enqueued to a global queue, dequeued and + * freed in the manage apps thread. + */ + struct ust_command *ust_cmd = NULL; + + DBG("[thread] Manage application registration started"); + + ret = lttcomm_listen_unix_sock(apps_sock); + if (ret < 0) { + goto error; + } + + /* + * 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); + if (ret < 0) { + goto error; + } + + /* Add the application registration socket */ + ret = lttng_poll_add(&events, apps_sock, LPOLLIN | LPOLLRDHUP); + if (ret < 0) { + goto error; + } + + /* Notify all applications to register */ + ret = notify_ust_apps(1); + if (ret < 0) { + ERR("Failed to notify applications or create the wait shared memory.\n" + "Execution continues but there might be problem for already\n" + "running applications that wishes to register."); + } + + while (1) { + DBG("Accepting application registration"); + + nb_fd = LTTNG_POLL_GETNB(&events); + + /* Inifinite blocking call, waiting for transmission */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Event on the registration socket */ + if (pollfd == apps_sock) { + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("Register apps socket poll error"); + goto error; + } else if (revents & LPOLLIN) { + sock = lttcomm_accept_unix_sock(apps_sock); + if (sock < 0) { + goto error; + } + + /* Create UST registration command for enqueuing */ + ust_cmd = malloc(sizeof(struct ust_command)); + if (ust_cmd == NULL) { + perror("ust command malloc"); + goto error; + } + + /* + * Using message-based transmissions to ensure we don't + * have to deal with partially received messages. + */ + 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"); + } + free(ust_cmd); + close(sock); + continue; + } + + ust_cmd->sock = sock; + + DBG("UST registration received with pid:%d ppid:%d uid:%d" + " gid:%d sock:%d name:%s (version %d.%d)", + ust_cmd->reg_msg.pid, ust_cmd->reg_msg.ppid, + 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); + + /* + * Lock free enqueue the registration request. The red pill + * has been taken! This apps will be part of the *system*. + */ + cds_wfq_enqueue(&ust_cmd_queue.queue, &ust_cmd->node); + + /* + * Wake the registration queue futex. Implicit memory + * barrier with the exchange in cds_wfq_enqueue. + */ + futex_nto1_wake(&ust_cmd_queue.futex); + } + } + } + } + +error: + DBG("UST Registration thread dying"); + + /* Notify that the registration thread is gone */ + notify_ust_apps(0); + + close(apps_sock); + close(sock); + unlink(apps_unix_sock_path); + + lttng_poll_clean(&events); + + return NULL; +} + +/* + * Start the thread_manage_consumer. This must be done after a lttng-consumerd + * exec or it will fails. + */ +static int spawn_consumer_thread(struct consumer_data *consumer_data) +{ + int ret; + struct timespec timeout; + + timeout.tv_sec = DEFAULT_SEM_WAIT_TIMEOUT; + timeout.tv_nsec = 0; + + /* Setup semaphore */ + ret = sem_init(&consumer_data->sem, 0, 0); + if (ret < 0) { + PERROR("sem_init consumer semaphore"); + goto error; + } + + ret = pthread_create(&consumer_data->thread, NULL, + thread_manage_consumer, consumer_data); + if (ret != 0) { + PERROR("pthread_create consumer"); + ret = -1; + goto error; + } + + /* Get time for sem_timedwait absolute timeout */ + ret = clock_gettime(CLOCK_REALTIME, &timeout); + if (ret < 0) { + PERROR("clock_gettime spawn consumer"); + /* Infinite wait for the kconsumerd thread to be ready */ + ret = sem_wait(&consumer_data->sem); + } else { + /* Normal timeout if the gettime was successful */ + timeout.tv_sec += DEFAULT_SEM_WAIT_TIMEOUT; + ret = sem_timedwait(&consumer_data->sem, &timeout); + } + + if (ret < 0) { + if (errno == ETIMEDOUT) { + /* + * Call has timed out so we kill the kconsumerd_thread and return + * an error. + */ + ERR("The consumer thread was never ready. Killing it"); + ret = pthread_cancel(consumer_data->thread); + if (ret < 0) { + PERROR("pthread_cancel consumer thread"); + } + } else { + PERROR("semaphore wait failed consumer thread"); + } + goto error; + } + + pthread_mutex_lock(&consumer_data->pid_mutex); + if (consumer_data->pid == 0) { + ERR("Kconsumerd did not start"); + pthread_mutex_unlock(&consumer_data->pid_mutex); + goto error; + } + pthread_mutex_unlock(&consumer_data->pid_mutex); + + return 0; + +error: + return ret; +} + +/* + * Join consumer thread + */ +static int join_consumer_thread(struct consumer_data *consumer_data) +{ + void *status; + int ret; + + if (consumer_data->pid != 0) { + ret = kill(consumer_data->pid, SIGTERM); + if (ret) { + ERR("Error killing consumer daemon"); + return ret; + } + return pthread_join(consumer_data->thread, &status); + } else { + return 0; + } +} + +/* + * Fork and exec a consumer daemon (consumerd). + * + * Return pid if successful else -1. + */ +static pid_t spawn_consumerd(struct consumer_data *consumer_data) +{ + int ret; + pid_t pid; + const char *verbosity; + + DBG("Spawning consumerd"); + + pid = fork(); + if (pid == 0) { + /* + * Exec consumerd. + */ + if (opt_verbose > 1 || opt_verbose_consumer) { + verbosity = "--verbose"; + } else { + verbosity = "--quiet"; + } + switch (consumer_data->type) { + case LTTNG_CONSUMER_KERNEL: + execl(INSTALL_BIN_PATH "/lttng-consumerd", + "lttng-consumerd", verbosity, "-k", NULL); + break; + case LTTNG_CONSUMER_UST: + execl(INSTALL_BIN_PATH "/lttng-consumerd", + "lttng-consumerd", verbosity, "-u", NULL); + break; + default: + perror("unknown consumer type"); + exit(EXIT_FAILURE); + } + if (errno != 0) { + perror("kernel start consumer exec"); + } + exit(EXIT_FAILURE); + } else if (pid > 0) { + ret = pid; + } else { + perror("start consumer fork"); + ret = -errno; + } + return ret; +} + +/* + * Spawn the consumerd daemon and session daemon thread. + */ +static int start_consumerd(struct consumer_data *consumer_data) +{ + int ret; + + pthread_mutex_lock(&consumer_data->pid_mutex); + if (consumer_data->pid != 0) { + pthread_mutex_unlock(&consumer_data->pid_mutex); + goto end; + } + + ret = spawn_consumerd(consumer_data); + if (ret < 0) { + ERR("Spawning consumerd failed"); + pthread_mutex_unlock(&consumer_data->pid_mutex); + goto error; + } + + /* Setting up the consumer_data pid */ + consumer_data->pid = ret; + DBG2("Consumer pid %d", consumer_data->pid); + pthread_mutex_unlock(&consumer_data->pid_mutex); + + DBG2("Spawning consumer control thread"); + ret = spawn_consumer_thread(consumer_data); + if (ret < 0) { + ERR("Fatal error spawning consumer control thread"); + goto error; + } + +end: + return 0; + +error: + return ret; +} + +/* + * modprobe_kernel_modules + */ +static int modprobe_kernel_modules(void) +{ + int ret = 0, i; + char modprobe[256]; + + for (i = 0; i < ARRAY_SIZE(kernel_modules_list); i++) { + ret = snprintf(modprobe, sizeof(modprobe), + "/sbin/modprobe %s%s", + kernel_modules_list[i].required ? "" : "-q ", + kernel_modules_list[i].name); + if (ret < 0) { + perror("snprintf modprobe"); + goto error; + } + modprobe[sizeof(modprobe) - 1] = '\0'; + ret = system(modprobe); + if (ret == -1) { + ERR("Unable to launch modprobe for module %s", + kernel_modules_list[i].name); + } else if (kernel_modules_list[i].required + && WEXITSTATUS(ret) != 0) { + ERR("Unable to load module %s", + kernel_modules_list[i].name); + } else { + DBG("Modprobe successfully %s", + kernel_modules_list[i].name); + } + } + +error: + return ret; +} + +/* + * mount_debugfs + */ +static int mount_debugfs(char *path) +{ + int ret; + char *type = "debugfs"; + + ret = mkdir_recursive(path, S_IRWXU | S_IRWXG, geteuid(), getegid()); + if (ret < 0) { + PERROR("Cannot create debugfs path"); + goto error; + } + + ret = mount(type, path, type, 0, NULL); + if (ret < 0) { + PERROR("Cannot mount debugfs"); + goto error; + } + + DBG("Mounted debugfs successfully at %s", path); + +error: + return ret; +} + +/* + * Setup necessary data for kernel tracer action. + */ +static void init_kernel_tracer(void) +{ + int ret; + char *proc_mounts = "/proc/mounts"; + char line[256]; + char *debugfs_path = NULL, *lttng_path = NULL; + FILE *fp; + + /* Detect debugfs */ + fp = fopen(proc_mounts, "r"); + if (fp == NULL) { + ERR("Unable to probe %s", proc_mounts); + goto error; + } + + while (fgets(line, sizeof(line), fp) != NULL) { + if (strstr(line, "debugfs") != NULL) { + /* Remove first string */ + strtok(line, " "); + /* Dup string here so we can reuse line later on */ + debugfs_path = strdup(strtok(NULL, " ")); + DBG("Got debugfs path : %s", debugfs_path); + break; + } + } + + fclose(fp); + + /* Mount debugfs if needded */ + if (debugfs_path == NULL) { + ret = asprintf(&debugfs_path, "/mnt/debugfs"); + if (ret < 0) { + perror("asprintf debugfs path"); + goto error; + } + ret = mount_debugfs(debugfs_path); + if (ret < 0) { + perror("Cannot mount debugfs"); + goto error; + } + } + + /* Modprobe lttng kernel modules */ + ret = modprobe_kernel_modules(); + if (ret < 0) { + goto error; + } + + /* Setup lttng kernel path */ + ret = asprintf(<tng_path, "%s/lttng", debugfs_path); + if (ret < 0) { + perror("asprintf lttng path"); + goto error; + } + + /* Open debugfs lttng */ + kernel_tracer_fd = open(lttng_path, O_RDWR); + if (kernel_tracer_fd < 0) { + DBG("Failed to open %s", lttng_path); + goto error; + } + + free(lttng_path); + free(debugfs_path); + DBG("Kernel tracer fd %d", kernel_tracer_fd); + return; + +error: + if (lttng_path) { + free(lttng_path); + } + if (debugfs_path) { + free(debugfs_path); + } + WARN("No kernel tracer available"); + kernel_tracer_fd = 0; + return; +} + +/* + * Init tracing by creating trace directory and sending fds kernel consumer. + */ +static int init_kernel_tracing(struct ltt_kernel_session *session) +{ + int ret = 0; + + if (session->consumer_fds_sent == 0) { + /* + * Assign default kernel consumer socket if no consumer assigned to the + * kernel session. At this point, it's NOT suppose to be 0 but this is + * an extra security check. + */ + if (session->consumer_fd == 0) { + session->consumer_fd = kconsumer_data.cmd_sock; + } + + ret = send_kconsumer_session_streams(&kconsumer_data, session); + if (ret < 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + + session->consumer_fds_sent = 1; + } + +error: + return ret; +} + +/* + * Create an UST session and add it to the session ust list. + */ +static int create_ust_session(struct ltt_session *session, + struct lttng_domain *domain) +{ + int ret; + unsigned int uid; + struct ltt_ust_session *lus = NULL; + + switch (domain->type) { + case LTTNG_DOMAIN_UST: + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + DBG("Creating UST session"); + + session_lock_list(); + uid = session_list_ptr->count; + session_unlock_list(); + + lus = trace_ust_create_session(session->path, uid, domain); + if (lus == NULL) { + ret = LTTCOMM_UST_SESS_FAIL; + goto error; + } + + ret = mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, + geteuid(), allowed_group()); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_UST_SESS_FAIL; + goto error; + } + } + + /* The domain type dictate different actions on session creation */ + switch (domain->type) { + case LTTNG_DOMAIN_UST: + /* No ustctl for the global UST domain */ + break; + default: + goto error; + } + session->ust_session = lus; + + return LTTCOMM_OK; + +error: + free(lus); + return ret; +} + +/* + * Create a kernel tracer session then create the default channel. + */ +static int create_kernel_session(struct ltt_session *session) +{ + int ret; + + DBG("Creating kernel session"); + + ret = kernel_create_session(session, kernel_tracer_fd); + if (ret < 0) { + ret = LTTCOMM_KERN_SESS_FAIL; + goto error; + } + + /* Set kernel consumer socket fd */ + if (kconsumer_data.cmd_sock) { + session->kernel_session->consumer_fd = kconsumer_data.cmd_sock; + } + + ret = mkdir_recursive(session->kernel_session->trace_path, + S_IRWXU | S_IRWXG, geteuid(), allowed_group()); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + goto error; + } + } + +error: + return ret; +} + +/* + * Using the session list, filled a lttng_session array to send back to the + * client for session listing. + * + * The session list lock MUST be acquired before calling this function. Use + * session_lock_list() and session_unlock_list(). + */ +static void list_lttng_sessions(struct lttng_session *sessions) +{ + int i = 0; + struct ltt_session *session; + + DBG("Getting all available session"); + /* + * Iterate over session list and append data after the control struct in + * the buffer. + */ + cds_list_for_each_entry(session, &session_list_ptr->head, list) { + strncpy(sessions[i].path, session->path, PATH_MAX); + sessions[i].path[PATH_MAX - 1] = '\0'; + strncpy(sessions[i].name, session->name, NAME_MAX); + sessions[i].name[NAME_MAX - 1] = '\0'; + i++; + } +} + +/* + * Fill lttng_channel array of all channels. + */ +static void list_lttng_channels(struct ltt_session *session, + struct lttng_channel *channels) +{ + int i = 0; + struct ltt_kernel_channel *kchan; + + DBG("Listing channels for session %s", session->name); + + /* Kernel channels */ + if (session->kernel_session != NULL) { + cds_list_for_each_entry(kchan, + &session->kernel_session->channel_list.head, list) { + /* Copy lttng_channel struct to array */ + memcpy(&channels[i], kchan->channel, sizeof(struct lttng_channel)); + channels[i].enabled = kchan->enabled; + i++; + } + } + + /* TODO: Missing UST listing */ +} + +/* + * Fill lttng_event array of all events in the channel. + */ +static void list_lttng_events(struct ltt_kernel_channel *kchan, + struct lttng_event *events) +{ + /* + * TODO: This is ONLY kernel. Need UST support. + */ + int i = 0; + struct ltt_kernel_event *event; + + DBG("Listing events for channel %s", kchan->channel->name); + + /* Kernel channels */ + cds_list_for_each_entry(event, &kchan->events_list.head , list) { + strncpy(events[i].name, event->event->name, LTTNG_SYMBOL_NAME_LEN); + events[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + events[i].enabled = event->enabled; + switch (event->event->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + events[i].type = LTTNG_EVENT_TRACEPOINT; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_KRETPROBE: + events[i].type = LTTNG_EVENT_PROBE; + memcpy(&events[i].attr.probe, &event->event->u.kprobe, + sizeof(struct lttng_kernel_kprobe)); + break; + case LTTNG_KERNEL_FUNCTION: + events[i].type = LTTNG_EVENT_FUNCTION; + memcpy(&events[i].attr.ftrace, &event->event->u.ftrace, + sizeof(struct lttng_kernel_function)); + break; + case LTTNG_KERNEL_NOOP: + events[i].type = LTTNG_EVENT_NOOP; + break; + case LTTNG_KERNEL_SYSCALL: + events[i].type = LTTNG_EVENT_SYSCALL; + break; + case LTTNG_KERNEL_ALL: + assert(0); + break; + } + i++; + } +} + +/* + * Command LTTNG_DISABLE_CHANNEL processed by the client thread. + */ +static int cmd_disable_channel(struct ltt_session *session, + int domain, char *channel_name) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + ret = channel_kernel_disable(session->kernel_session, + channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + case LTTNG_DOMAIN_UST_PID: + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Copy channel from attributes and set it in the application channel list. + */ +/* +static int copy_ust_channel_to_app(struct ltt_ust_session *usess, + struct lttng_channel *attr, struct ust_app *app) +{ + int ret; + struct ltt_ust_channel *uchan, *new_chan; + + uchan = trace_ust_get_channel_by_key(usess->channels, attr->name); + if (uchan == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + new_chan = trace_ust_create_channel(attr, usess->path); + if (new_chan == NULL) { + PERROR("malloc ltt_ust_channel"); + ret = LTTCOMM_FATAL; + goto error; + } + + ret = channel_ust_copy(new_chan, uchan); + if (ret < 0) { + ret = LTTCOMM_FATAL; + goto error; + } + +error: + return ret; +} +*/ + +/* + * Command LTTNG_ENABLE_CHANNEL processed by the client thread. + */ +static int cmd_enable_channel(struct ltt_session *session, + struct lttng_domain *domain, struct lttng_channel *attr) +{ + int ret; + struct ltt_ust_session *usess = session->ust_session; + + DBG("Enabling channel %s for session %s", session->name, attr->name); + + switch (domain->type) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(attr->name, + session->kernel_session); + if (kchan == NULL) { + ret = channel_kernel_create(session->kernel_session, + attr, kernel_poll_pipe[1]); + } else { + ret = channel_kernel_enable(session->kernel_session, kchan); + } + + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + + DBG2("Enabling channel for LTTNG_DOMAIN_UST"); + + /* Get channel in global UST domain HT */ + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + attr->name); + if (uchan == NULL) { + uchan = trace_ust_create_channel(attr, usess->pathname); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_FAIL; + goto error; + } + rcu_read_lock(); + hashtable_add_unique(usess->domain_global.channels, &uchan->node); + rcu_read_unlock(); + DBG2("UST channel %s added to global domain HT", attr->name); + } else { + ret = LTTCOMM_UST_CHAN_EXIST; + goto error; + } + + ret = ust_app_add_channel(usess, uchan); + if (ret != LTTCOMM_OK) { + goto error; + } + + break; + } + case LTTNG_DOMAIN_UST_PID: + { + /* + int sock; + struct ltt_ust_channel *uchan; + struct ltt_ust_session *usess; + struct ust_app *app; + + usess = trace_ust_get_session_by_pid(&session->ust_session_list, + domain->attr.pid); + if (usess == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + app = ust_app_get_by_pid(domain->attr.pid); + if (app == NULL) { + ret = LTTCOMM_APP_NOT_FOUND; + goto error; + } + sock = app->sock; + + uchan = trace_ust_get_channel_by_name(attr->name, usess); + if (uchan == NULL) { + ret = channel_ust_create(usess, attr, sock); + } else { + ret = channel_ust_enable(usess, uchan, sock); + } + + if (ret != LTTCOMM_OK) { + goto error; + } + + ret = copy_ust_channel_to_app(usess, attr, app); + if (ret != LTTCOMM_OK) { + goto error; + } + + DBG("UST channel %s created for app sock %d with pid %d", + attr->name, app->sock, domain->attr.pid); + */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_DISABLE_EVENT processed by the client thread. + */ +static int cmd_disable_event(struct ltt_session *session, int domain, + char *channel_name, char *event_name) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_tracepoint(session->kernel_session, kchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + default: + /* TODO: Other UST domains */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_DISABLE_ALL_EVENT processed by the client thread. + */ +static int cmd_disable_event_all(struct ltt_session *session, int domain, + char *channel_name) +{ + int ret; + struct ltt_kernel_channel *kchan; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_all(session->kernel_session, kchan); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + default: + /* TODO: Userspace tracing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ADD_CONTEXT processed by the client thread. + */ +static int cmd_add_context(struct ltt_session *session, int domain, + char *channel_name, char *event_name, struct lttng_event_context *ctx) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Add kernel context to kernel tracer */ + ret = context_kernel_add(session->kernel_session, ctx, + event_name, channel_name); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + case LTTNG_DOMAIN_UST: + { + /* + struct ltt_ust_session *usess; + + cds_list_for_each_entry(usess, &session->ust_session_list.head, list) { + ret = context_ust_add(usess, ctx, + event_name, channel_name, domain); + if (ret != LTTCOMM_OK) { + goto error; + } + } + break; + */ + } + default: + /* TODO: UST other domains */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_EVENT processed by the client thread. + */ +static int cmd_enable_event(struct ltt_session *session, int domain, + char *channel_name, struct lttng_event *event) +{ + int ret; + struct lttng_channel *attr; + struct ltt_ust_session *usess = session->ust_session; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + attr = channel_new_default_attr(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + snprintf(attr->name, NAME_MAX, "%s", channel_name); + + /* This call will notify the kernel thread */ + ret = channel_kernel_create(session->kernel_session, + attr, kernel_poll_pipe[1]); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + /* Get the newly created kernel channel pointer */ + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + /* This sould not happen... */ + ret = LTTCOMM_FATAL; + goto error; + } + + ret = event_kernel_enable_tracepoint(session->kernel_session, kchan, + event); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_channel *uchan; + struct ltt_ust_event *uevent; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + /* TODO: Create default channel */ + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + uevent = trace_ust_find_event_by_name(uchan->events, event->name); + if (uevent == NULL) { + uevent = trace_ust_create_event(event); + if (uevent == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + } + + ret = ust_app_add_event(usess, uchan, uevent); + if (ret < 0) { + ret = LTTCOMM_UST_ENABLE_FAIL; + goto error; + } + break; + } + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + default: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_ALL_EVENT processed by the client thread. + */ +static int cmd_enable_event_all(struct ltt_session *session, int domain, + char *channel_name, int event_type) +{ + int ret; + struct ltt_kernel_channel *kchan; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + /* This call will notify the kernel thread */ + ret = channel_kernel_create(session->kernel_session, NULL, + kernel_poll_pipe[1]); + if (ret != LTTCOMM_OK) { + goto error; + } + } + + /* Get the newly created kernel channel pointer */ + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + /* This sould not happen... */ + ret = LTTCOMM_FATAL; + goto error; + } + + switch (event_type) { + case LTTNG_KERNEL_SYSCALL: + ret = event_kernel_enable_all_syscalls(session->kernel_session, + kchan, kernel_tracer_fd); + break; + case LTTNG_KERNEL_TRACEPOINT: + /* + * This call enables all LTTNG_KERNEL_TRACEPOINTS and + * events already registered to the channel. + */ + ret = event_kernel_enable_all_tracepoints(session->kernel_session, + kchan, kernel_tracer_fd); + break; + case LTTNG_KERNEL_ALL: + /* Enable syscalls and tracepoints */ + ret = event_kernel_enable_all(session->kernel_session, + kchan, kernel_tracer_fd); + break; + default: + ret = LTTCOMM_KERN_ENABLE_FAIL; + goto error; + } + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + default: + /* TODO: Userspace tracing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_TRACEPOINTS processed by the client thread. + */ +static ssize_t cmd_list_tracepoints(int domain, struct lttng_event **events) +{ + int ret; + ssize_t nb_events = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + nb_events = kernel_list_events(kernel_tracer_fd, events); + if (nb_events < 0) { + ret = LTTCOMM_KERN_LIST_FAIL; + goto error; + } + break; + default: + /* TODO: Userspace listing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + return nb_events; + +error: + /* Return negative value to differentiate return code */ + return -ret; +} + +/* + * Command LTTNG_START_TRACE processed by the client thread. + */ +static int cmd_start_trace(struct ltt_session *session) +{ + int ret; + struct ltt_kernel_session *ksession; + struct ltt_ust_session *usess = session->ust_session; + + /* Short cut */ + ksession = session->kernel_session; + + /* Kernel tracing */ + if (ksession != NULL) { + struct ltt_kernel_channel *kchan; + + /* Open kernel metadata */ + if (ksession->metadata == NULL) { + ret = kernel_open_metadata(ksession, ksession->trace_path); + if (ret < 0) { + ret = LTTCOMM_KERN_META_FAIL; + goto error; + } + } + + /* Open kernel metadata stream */ + if (ksession->metadata_stream_fd == 0) { + ret = kernel_open_metadata_stream(ksession); + if (ret < 0) { + ERR("Kernel create metadata stream failed"); + ret = LTTCOMM_KERN_STREAM_FAIL; + goto error; + } + } + + /* For each channel */ + cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { + if (kchan->stream_count == 0) { + ret = kernel_open_channel_stream(kchan); + if (ret < 0) { + ret = LTTCOMM_KERN_STREAM_FAIL; + goto error; + } + /* Update the stream global counter */ + ksession->stream_count_global += ret; + } + } + + /* Setup kernel consumer socket and send fds to it */ + ret = init_kernel_tracing(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_START_FAIL; + goto error; + } + + /* This start the kernel tracing */ + ret = kernel_start_session(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_START_FAIL; + goto error; + } + + /* Quiescent wait after starting trace */ + kernel_wait_quiescent(kernel_tracer_fd); + } + + ret = ust_app_start_trace(usess); + if (ret < 0) { + ret = LTTCOMM_UST_START_FAIL; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_STOP_TRACE processed by the client thread. + */ +static int cmd_stop_trace(struct ltt_session *session) +{ + int ret; + struct ltt_kernel_channel *kchan; + struct ltt_kernel_session *ksession; + //struct ltt_ust_session *usess; + //struct ltt_ust_channel *ustchan; + + /* Short cut */ + ksession = session->kernel_session; + + /* Kernel tracer */ + if (ksession != NULL) { + DBG("Stop kernel tracing"); + + /* Flush all buffers before stopping */ + ret = kernel_metadata_flush_buffer(ksession->metadata_stream_fd); + if (ret < 0) { + ERR("Kernel metadata flush failed"); + } + + cds_list_for_each_entry(kchan, &ksession->channel_list.head, list) { + ret = kernel_flush_buffer(kchan); + if (ret < 0) { + ERR("Kernel flush buffer error"); + } + } + + ret = kernel_stop_session(ksession); + if (ret < 0) { + ret = LTTCOMM_KERN_STOP_FAIL; + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + } + +#ifdef DISABLE + /* Stop each UST session */ + DBG("Stop UST tracing"); + cds_list_for_each_entry(usess, &session->ust_session_list.head, list) { + /* Flush all buffers before stopping */ + ret = ustctl_flush_buffer(usess->sock, usess->metadata->obj); + if (ret < 0) { + ERR("UST metadata flush failed"); + } + + cds_list_for_each_entry(ustchan, &usess->channels.head, list) { + ret = ustctl_flush_buffer(usess->sock, ustchan->obj); + if (ret < 0) { + ERR("UST flush buffer error"); + } + } + + ret = ustctl_stop_session(usess->sock, usess->handle); + if (ret < 0) { + ret = LTTCOMM_KERN_STOP_FAIL; + goto error; + } + + ustctl_wait_quiescent(usess->sock); + } +#endif + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_CREATE_SESSION processed by the client thread. + */ +static int cmd_create_session(char *name, char *path) +{ + int ret; + + ret = session_create(name, path); + if (ret != LTTCOMM_OK) { + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_DESTROY_SESSION processed by the client thread. + */ +static int cmd_destroy_session(struct ltt_session *session, char *name) +{ + int ret; + + /* Clean kernel session teardown */ + teardown_kernel_session(session); + + /* + * Must notify the kernel thread here to update it's poll setin order + * to remove the channel(s)' fd just destroyed. + */ + ret = notify_thread_pipe(kernel_poll_pipe[1]); + if (ret < 0) { + perror("write kernel poll pipe"); + } + + ret = session_destroy(session); + + return ret; +} + +/* + * Command LTTNG_CALIBRATE processed by the client thread. + */ +static int cmd_calibrate(int domain, struct lttng_calibrate *calibrate) +{ + int ret; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct lttng_kernel_calibrate kcalibrate; + + kcalibrate.type = calibrate->type; + ret = kernel_calibrate(kernel_tracer_fd, &kcalibrate); + if (ret < 0) { + ret = LTTCOMM_KERN_ENABLE_FAIL; + goto error; + } + break; + } + default: + /* TODO: Userspace tracing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_REGISTER_CONSUMER processed by the client thread. + */ +static int cmd_register_consumer(struct ltt_session *session, int domain, + char *sock_path) +{ + int ret, sock; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* Can't register a consumer if there is already one */ + if (session->kernel_session->consumer_fd != 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + + sock = lttcomm_connect_unix_sock(sock_path); + if (sock < 0) { + ret = LTTCOMM_CONNECT_FAIL; + goto error; + } + + session->kernel_session->consumer_fd = sock; + break; + default: + /* TODO: Userspace tracing */ + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_DOMAINS processed by the client thread. + */ +static ssize_t cmd_list_domains(struct ltt_session *session, + struct lttng_domain **domains) +{ + int ret; + ssize_t nb_dom = 0; + + if (session->kernel_session != NULL) { + nb_dom++; + } + + /* TODO: User-space tracer domain support */ + + *domains = malloc(nb_dom * sizeof(struct lttng_domain)); + if (*domains == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + (*domains)[0].type = LTTNG_DOMAIN_KERNEL; + + return nb_dom; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_CHANNELS processed by the client thread. + */ +static ssize_t cmd_list_channels(struct ltt_session *session, + struct lttng_channel **channels) +{ + int ret; + ssize_t nb_chan = 0; + + if (session->kernel_session != NULL) { + nb_chan += session->kernel_session->channel_count; + } + + *channels = malloc(nb_chan * sizeof(struct lttng_channel)); + if (*channels == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + list_lttng_channels(session, *channels); + + /* TODO UST support */ + + return nb_chan; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_EVENTS processed by the client thread. + */ +static ssize_t cmd_list_events(struct ltt_session *session, + char *channel_name, struct lttng_event **events) +{ + int ret; + ssize_t nb_event = 0; + struct ltt_kernel_channel *kchan = NULL; + + if (session->kernel_session != NULL) { + kchan = trace_kernel_get_channel_by_name(channel_name, + session->kernel_session); + if (kchan == NULL) { + ret = -LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + nb_event += kchan->event_count; + } + + *events = malloc(nb_event * sizeof(struct lttng_event)); + if (*events == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + list_lttng_events(kchan, *events); + + /* TODO: User-space tracer support */ + + return nb_event; + +error: + return ret; +} + +/* + * Process the command requested by the lttng client within the command + * context structure. This function make sure that the return structure (llm) + * is set and ready for transmission before returning. + * + * Return any error encountered or 0 for success. + */ +static int process_client_msg(struct command_ctx *cmd_ctx) +{ + int ret = LTTCOMM_OK; + int need_tracing_session = 1; + + DBG("Processing client command %d", cmd_ctx->lsm->cmd_type); + + /* + * Check for command that don't needs to allocate a returned payload. We do + * this here so we don't have to make the call for no payload at each + * command. + */ + switch(cmd_ctx->lsm->cmd_type) { + case LTTNG_LIST_SESSIONS: + case LTTNG_LIST_TRACEPOINTS: + case LTTNG_LIST_DOMAINS: + case LTTNG_LIST_CHANNELS: + case LTTNG_LIST_EVENTS: + break; + default: + /* Setup lttng message with no payload */ + ret = setup_lttng_msg(cmd_ctx, 0); + if (ret < 0) { + /* This label does not try to unlock the session */ + goto init_setup_error; + } + } + + /* Commands that DO NOT need a session. */ + switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_CALIBRATE: + case LTTNG_CREATE_SESSION: + case LTTNG_LIST_SESSIONS: + case LTTNG_LIST_TRACEPOINTS: + need_tracing_session = 0; + break; + default: + DBG("Getting session %s by name", cmd_ctx->lsm->session.name); + session_lock_list(); + cmd_ctx->session = session_find_by_name(cmd_ctx->lsm->session.name); + session_unlock_list(); + if (cmd_ctx->session == NULL) { + if (cmd_ctx->lsm->session.name != NULL) { + ret = LTTCOMM_SESS_NOT_FOUND; + } else { + /* If no session name specified */ + ret = LTTCOMM_SELECT_SESS; + } + goto error; + } else { + /* Acquire lock for the session */ + session_lock(cmd_ctx->session); + } + break; + } + + /* + * Check domain type for specific "pre-action". + */ + switch (cmd_ctx->lsm->domain.type) { + case LTTNG_DOMAIN_KERNEL: + /* Kernel tracer check */ + if (kernel_tracer_fd == 0) { + /* Basically, load kernel tracer modules */ + init_kernel_tracer(); + if (kernel_tracer_fd == 0) { + ret = LTTCOMM_KERN_NA; + goto error; + } + } + + /* Need a session for kernel command */ + if (need_tracing_session) { + if (cmd_ctx->session->kernel_session == NULL) { + ret = create_kernel_session(cmd_ctx->session); + if (ret < 0) { + ret = LTTCOMM_KERN_SESS_FAIL; + goto error; + } + } + + /* Start the kernel consumer daemon */ + pthread_mutex_lock(&kconsumer_data.pid_mutex); + if (kconsumer_data.pid == 0 && + cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { + pthread_mutex_unlock(&kconsumer_data.pid_mutex); + ret = start_consumerd(&kconsumer_data); + if (ret < 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + } + pthread_mutex_unlock(&kconsumer_data.pid_mutex); + } + break; + case LTTNG_DOMAIN_UST: + { + if (need_tracing_session) { + if (cmd_ctx->session->ust_session == NULL) { + ret = create_ust_session(cmd_ctx->session, + &cmd_ctx->lsm->domain); + if (ret != LTTCOMM_OK) { + goto error; + } + } + /* Start the kernel consumer daemon */ + pthread_mutex_lock(&ustconsumer_data.pid_mutex); + if (ustconsumer_data.pid == 0 && + cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { + pthread_mutex_unlock(&ustconsumer_data.pid_mutex); + ret = start_consumerd(&ustconsumer_data); + if (ret < 0) { + ret = LTTCOMM_KERN_CONSUMER_FAIL; + goto error; + } + + cmd_ctx->session->ust_session->consumer_fd = + ustconsumer_data.cmd_sock; + } + pthread_mutex_unlock(&ustconsumer_data.pid_mutex); + } + break; + } + default: + break; + } + + /* Process by command type */ + switch (cmd_ctx->lsm->cmd_type) { + case LTTNG_ADD_CONTEXT: + { + ret = cmd_add_context(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.context.channel_name, + cmd_ctx->lsm->u.context.event_name, + &cmd_ctx->lsm->u.context.ctx); + break; + } + case LTTNG_DISABLE_CHANNEL: + { + ret = cmd_disable_channel(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.disable.channel_name); + break; + } + case LTTNG_DISABLE_EVENT: + { + ret = cmd_disable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.disable.channel_name, + cmd_ctx->lsm->u.disable.name); + ret = LTTCOMM_OK; + break; + } + case LTTNG_DISABLE_ALL_EVENT: + { + DBG("Disabling all events"); + + ret = cmd_disable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.disable.channel_name); + break; + } + case LTTNG_ENABLE_CHANNEL: + { + ret = cmd_enable_channel(cmd_ctx->session, &cmd_ctx->lsm->domain, + &cmd_ctx->lsm->u.channel.chan); + break; + } + case LTTNG_ENABLE_EVENT: + { + ret = cmd_enable_event(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.enable.channel_name, + &cmd_ctx->lsm->u.enable.event); + break; + } + case LTTNG_ENABLE_ALL_EVENT: + { + DBG("Enabling all events"); + + ret = cmd_enable_event_all(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.enable.channel_name, + cmd_ctx->lsm->u.enable.event.type); + break; + } + case LTTNG_LIST_TRACEPOINTS: + { + struct lttng_event *events; + ssize_t nb_events; + + nb_events = cmd_list_tracepoints(cmd_ctx->lsm->domain.type, &events); + if (nb_events < 0) { + ret = -nb_events; + goto error; + } + + /* + * Setup lttng message with payload size set to the event list size in + * bytes and then copy list into the llm payload. + */ + ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_event) * nb_events); + if (ret < 0) { + free(events); + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, events, + sizeof(struct lttng_event) * nb_events); + + free(events); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_START_TRACE: + { + ret = cmd_start_trace(cmd_ctx->session); + break; + } + case LTTNG_STOP_TRACE: + { + ret = cmd_stop_trace(cmd_ctx->session); + break; + } + case LTTNG_CREATE_SESSION: + { + ret = cmd_create_session(cmd_ctx->lsm->session.name, + cmd_ctx->lsm->session.path); + break; + } + case LTTNG_DESTROY_SESSION: + { + ret = cmd_destroy_session(cmd_ctx->session, + cmd_ctx->lsm->session.name); + break; + } + case LTTNG_LIST_DOMAINS: + { + ssize_t nb_dom; + struct lttng_domain *domains; + + nb_dom = cmd_list_domains(cmd_ctx->session, &domains); + if (nb_dom < 0) { + ret = -nb_dom; + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, nb_dom * sizeof(struct lttng_domain)); + if (ret < 0) { + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, domains, + nb_dom * sizeof(struct lttng_domain)); + + free(domains); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_LIST_CHANNELS: + { + size_t nb_chan; + struct lttng_channel *channels; + + nb_chan = cmd_list_channels(cmd_ctx->session, &channels); + if (nb_chan < 0) { + ret = -nb_chan; + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, nb_chan * sizeof(struct lttng_channel)); + if (ret < 0) { + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, channels, + nb_chan * sizeof(struct lttng_channel)); + + free(channels); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_LIST_EVENTS: + { + size_t nb_event; + struct lttng_event *events = NULL; + + nb_event = cmd_list_events(cmd_ctx->session, + cmd_ctx->lsm->u.list.channel_name, &events); + if (nb_event < 0) { + ret = -nb_event; + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, nb_event * sizeof(struct lttng_event)); + if (ret < 0) { + goto setup_error; + } + + /* Copy event list into message payload */ + memcpy(cmd_ctx->llm->payload, events, + nb_event * sizeof(struct lttng_event)); + + free(events); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_LIST_SESSIONS: + { + session_lock_list(); + + if (session_list_ptr->count == 0) { + ret = LTTCOMM_NO_SESSION; + session_unlock_list(); + goto error; + } + + ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * + session_list_ptr->count); + if (ret < 0) { + session_unlock_list(); + goto setup_error; + } + + /* Filled the session array */ + list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload)); + + session_unlock_list(); + + ret = LTTCOMM_OK; + break; + } + case LTTNG_CALIBRATE: + { + ret = cmd_calibrate(cmd_ctx->lsm->domain.type, + &cmd_ctx->lsm->u.calibrate); + break; + } + case LTTNG_REGISTER_CONSUMER: + { + ret = cmd_register_consumer(cmd_ctx->session, cmd_ctx->lsm->domain.type, + cmd_ctx->lsm->u.reg.path); + break; + } + default: + ret = LTTCOMM_UND; + break; + } + +error: + if (cmd_ctx->llm == NULL) { + DBG("Missing llm structure. Allocating one."); + if (setup_lttng_msg(cmd_ctx, 0) < 0) { + goto setup_error; + } + } + /* Set return code */ + cmd_ctx->llm->ret_code = ret; +setup_error: + if (cmd_ctx->session) { + session_unlock(cmd_ctx->session); + } +init_setup_error: + return ret; +} + +/* + * This thread manage all clients request using the unix client socket for + * communication. + */ +static void *thread_manage_clients(void *data) +{ + int sock = 0, ret, i, pollfd; + uint32_t revents, nb_fd; + struct command_ctx *cmd_ctx = NULL; + struct lttng_poll_event events; + + DBG("[thread] Manage client started"); + + rcu_register_thread(); + + ret = lttcomm_listen_unix_sock(client_sock); + if (ret < 0) { + goto error; + } + + /* + * 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); + if (ret < 0) { + goto error; + } + + /* Add the application registration socket */ + ret = lttng_poll_add(&events, client_sock, LPOLLIN | LPOLLPRI); + if (ret < 0) { + goto error; + } + + /* + * Notify parent pid that we are ready to accept command for client side. + */ + if (opt_sig_parent) { + kill(ppid, SIGCHLD); + } + + while (1) { + DBG("Accepting client command ..."); + + nb_fd = LTTNG_POLL_GETNB(&events); + + /* Inifinite blocking call, waiting for transmission */ + ret = lttng_poll_wait(&events, -1); + if (ret < 0) { + goto error; + } + + 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 = check_thread_quit_pipe(pollfd, revents); + if (ret) { + goto error; + } + + /* Event on the registration socket */ + if (pollfd == client_sock) { + if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) { + ERR("Client socket poll error"); + goto error; + } + } + } + + DBG("Wait for client response"); + + sock = lttcomm_accept_unix_sock(client_sock); + if (sock < 0) { + goto error; + } + + /* Allocate context command to process the client request */ + cmd_ctx = malloc(sizeof(struct command_ctx)); + if (cmd_ctx == NULL) { + perror("malloc cmd_ctx"); + goto error; + } + + /* Allocate data buffer for reception */ + cmd_ctx->lsm = malloc(sizeof(struct lttcomm_session_msg)); + if (cmd_ctx->lsm == NULL) { + perror("malloc cmd_ctx->lsm"); + goto error; + } + + cmd_ctx->llm = NULL; + cmd_ctx->session = NULL; + + /* + * Data is received from the lttng client. The struct + * lttcomm_session_msg (lsm) contains the command and data request of + * the client. + */ + DBG("Receiving data from client ..."); + ret = lttcomm_recv_unix_sock(sock, cmd_ctx->lsm, + sizeof(struct lttcomm_session_msg)); + if (ret <= 0) { + DBG("Nothing recv() from client... continuing"); + close(sock); + free(cmd_ctx); + continue; + } + + // TODO: Validate cmd_ctx including sanity check for + // security purpose. + + rcu_thread_online(); + /* + * This function dispatch the work to the kernel or userspace tracer + * libs and fill the lttcomm_lttng_msg data structure of all the needed + * informations for the client. The command context struct contains + * everything this function may needs. + */ + ret = process_client_msg(cmd_ctx); + rcu_thread_offline(); + if (ret < 0) { + /* + * TODO: Inform client somehow of the fatal error. At + * this point, ret < 0 means that a malloc failed + * (ENOMEM). Error detected but still accept command. + */ + clean_command_ctx(&cmd_ctx); + continue; + } + + DBG("Sending response (size: %d, retcode: %s)", + cmd_ctx->lttng_msg_size, + lttng_strerror(-cmd_ctx->llm->ret_code)); + ret = send_unix_sock(sock, cmd_ctx->llm, cmd_ctx->lttng_msg_size); + if (ret < 0) { + ERR("Failed to send data back to client"); + } + + clean_command_ctx(&cmd_ctx); + + /* End of transmission */ + close(sock); + } + +error: + DBG("Client thread dying"); + unlink(client_unix_sock_path); + close(client_sock); + close(sock); + + lttng_poll_clean(&events); + clean_command_ctx(&cmd_ctx); + + rcu_unregister_thread(); + return NULL; +} + + +/* + * usage function on stderr + */ +static void usage(void) +{ + fprintf(stderr, "Usage: %s OPTIONS\n\nOptions:\n", progname); + fprintf(stderr, " -h, --help Display this usage.\n"); + fprintf(stderr, " -c, --client-sock PATH Specify path for the client unix socket\n"); + fprintf(stderr, " -a, --apps-sock PATH Specify path for apps unix socket\n"); + fprintf(stderr, " --kconsumerd-err-sock PATH Specify path for the kernel consumer error socket\n"); + fprintf(stderr, " --kconsumerd-cmd-sock PATH Specify path for the kernel consumer command socket\n"); + fprintf(stderr, " --ustconsumerd-err-sock PATH Specify path for the UST consumer error socket\n"); + fprintf(stderr, " --ustconsumerd-cmd-sock PATH Specify path for the UST consumer command socket\n"); + fprintf(stderr, " -d, --daemonize Start as a daemon.\n"); + fprintf(stderr, " -g, --group NAME Specify the tracing group name. (default: tracing)\n"); + fprintf(stderr, " -V, --version Show version number.\n"); + fprintf(stderr, " -S, --sig-parent Send SIGCHLD to parent pid to notify readiness.\n"); + fprintf(stderr, " -q, --quiet No output at all.\n"); + fprintf(stderr, " -v, --verbose Verbose mode. Activate DBG() macro.\n"); + fprintf(stderr, " --verbose-consumer Verbose mode for consumer. Activate DBG() macro.\n"); +} + +/* + * daemon argument parsing + */ +static int parse_args(int argc, char **argv) +{ + int c; + + static struct option long_options[] = { + { "client-sock", 1, 0, 'c' }, + { "apps-sock", 1, 0, 'a' }, + { "kconsumerd-cmd-sock", 1, 0, 'C' }, + { "kconsumerd-err-sock", 1, 0, 'E' }, + { "ustconsumerd-cmd-sock", 1, 0, 'D' }, + { "ustconsumerd-err-sock", 1, 0, 'F' }, + { "daemonize", 0, 0, 'd' }, + { "sig-parent", 0, 0, 'S' }, + { "help", 0, 0, 'h' }, + { "group", 1, 0, 'g' }, + { "version", 0, 0, 'V' }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "verbose-consumer", 0, 0, 'Z' }, + { NULL, 0, 0, 0 } + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "dhqvVS" "a:c:g:s:C:E:D:F:Z", + long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 0: + fprintf(stderr, "option %s", long_options[option_index].name); + if (optarg) { + fprintf(stderr, " with arg %s\n", optarg); + } + break; + case 'c': + snprintf(client_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'a': + snprintf(apps_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'd': + opt_daemon = 1; + break; + case 'g': + opt_tracing_group = strdup(optarg); + break; + case 'h': + usage(); + exit(EXIT_FAILURE); + case 'V': + fprintf(stdout, "%s\n", VERSION); + exit(EXIT_SUCCESS); + case 'S': + opt_sig_parent = 1; + break; + case 'E': + snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'C': + snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'F': + snprintf(ustconsumer_data.err_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'D': + snprintf(ustconsumer_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'q': + opt_quiet = 1; + break; + case 'v': + /* Verbose level can increase using multiple -v */ + opt_verbose += 1; + break; + case 'Z': + opt_verbose_consumer += 1; + break; + default: + /* Unknown option or other error. + * Error is printed by getopt, just return */ + return -1; + } + } + + return 0; +} + +/* + * Creates the two needed socket by the daemon. + * apps_sock - The communication socket for all UST apps. + * client_sock - The communication of the cli tool (lttng). + */ +static int init_daemon_socket(void) +{ + int ret = 0; + mode_t old_umask; + + old_umask = umask(0); + + /* Create client tool unix socket */ + client_sock = lttcomm_create_unix_sock(client_unix_sock_path); + if (client_sock < 0) { + ERR("Create unix sock failed: %s", client_unix_sock_path); + ret = -1; + goto end; + } + + /* File permission MUST be 660 */ + ret = chmod(client_unix_sock_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (ret < 0) { + ERR("Set file permissions failed: %s", client_unix_sock_path); + perror("chmod"); + goto end; + } + + /* Create the application unix socket */ + apps_sock = lttcomm_create_unix_sock(apps_unix_sock_path); + if (apps_sock < 0) { + ERR("Create unix sock failed: %s", apps_unix_sock_path); + ret = -1; + goto end; + } + + /* File permission MUST be 666 */ + ret = chmod(apps_unix_sock_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (ret < 0) { + ERR("Set file permissions failed: %s", apps_unix_sock_path); + perror("chmod"); + goto end; + } + +end: + umask(old_umask); + return ret; +} + +/* + * Check if the global socket is available, and if a daemon is answering at the + * other side. If yes, error is returned. + */ +static int check_existing_daemon(void) +{ + if (access(client_unix_sock_path, F_OK) < 0 && + access(apps_unix_sock_path, F_OK) < 0) { + return 0; + } + + /* Is there anybody out there ? */ + if (lttng_session_daemon_alive()) { + return -EEXIST; + } else { + return 0; + } +} + +/* + * Set the tracing group gid onto the client socket. + * + * Race window between mkdir and chown is OK because we are going from more + * permissive (root.root) to les permissive (root.tracing). + */ +static int set_permissions(void) +{ + int ret; + gid_t gid; + + gid = allowed_group(); + if (gid < 0) { + if (is_root) { + WARN("No tracing group detected"); + ret = 0; + } else { + ERR("Missing tracing group. Aborting execution."); + ret = -1; + } + goto end; + } + + /* Set lttng run dir */ + ret = chown(LTTNG_RUNDIR, 0, gid); + if (ret < 0) { + ERR("Unable to set group on " LTTNG_RUNDIR); + perror("chown"); + } + + /* lttng client socket path */ + ret = chown(client_unix_sock_path, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", client_unix_sock_path); + perror("chown"); + } + + /* kconsumer error socket path */ + ret = chown(kconsumer_data.err_unix_sock_path, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", kconsumer_data.err_unix_sock_path); + perror("chown"); + } + + /* ustconsumer error socket path */ + ret = chown(ustconsumer_data.err_unix_sock_path, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", ustconsumer_data.err_unix_sock_path); + perror("chown"); + } + + DBG("All permissions are set"); + +end: + return ret; +} + +/* + * Create the pipe used to wake up the kernel thread. + */ +static int create_kernel_poll_pipe(void) +{ + return pipe2(kernel_poll_pipe, O_CLOEXEC); +} + +/* + * Create the application command pipe to wake thread_manage_apps. + */ +static int create_apps_cmd_pipe(void) +{ + return pipe2(apps_cmd_pipe, O_CLOEXEC); +} + +/* + * Create the lttng run directory needed for all global sockets and pipe. + */ +static int create_lttng_rundir(void) +{ + int ret; + + ret = mkdir(LTTNG_RUNDIR, S_IRWXU | S_IRWXG ); + if (ret < 0) { + if (errno != EEXIST) { + ERR("Unable to create " LTTNG_RUNDIR); + goto error; + } else { + ret = 0; + } + } + +error: + return ret; +} + +/* + * Setup sockets and directory needed by the kconsumerd communication with the + * session daemon. + */ +static int set_consumer_sockets(struct consumer_data *consumer_data) +{ + int ret; + const char *path = consumer_data->type == LTTNG_CONSUMER_KERNEL ? + KCONSUMERD_PATH : USTCONSUMERD_PATH; + + if (strlen(consumer_data->err_unix_sock_path) == 0) { + snprintf(consumer_data->err_unix_sock_path, PATH_MAX, + consumer_data->type == LTTNG_CONSUMER_KERNEL ? + KCONSUMERD_ERR_SOCK_PATH : + USTCONSUMERD_ERR_SOCK_PATH); + } + + if (strlen(consumer_data->cmd_unix_sock_path) == 0) { + snprintf(consumer_data->cmd_unix_sock_path, PATH_MAX, + consumer_data->type == LTTNG_CONSUMER_KERNEL ? + KCONSUMERD_CMD_SOCK_PATH : + USTCONSUMERD_CMD_SOCK_PATH); + } + + ret = mkdir(path, S_IRWXU | S_IRWXG); + if (ret < 0) { + if (errno != EEXIST) { + ERR("Failed to create %s", path); + goto error; + } + ret = 0; + } + + /* Create the kconsumerd error unix socket */ + consumer_data->err_sock = + lttcomm_create_unix_sock(consumer_data->err_unix_sock_path); + if (consumer_data->err_sock < 0) { + ERR("Create unix sock failed: %s", consumer_data->err_unix_sock_path); + ret = -1; + goto error; + } + + /* File permission MUST be 660 */ + ret = chmod(consumer_data->err_unix_sock_path, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if (ret < 0) { + ERR("Set file permissions failed: %s", consumer_data->err_unix_sock_path); + perror("chmod"); + goto error; + } + +error: + return ret; +} + +/* + * Signal handler for the daemon + * + * Simply stop all worker threads, leaving main() return gracefully after + * joining all threads and calling cleanup(). + */ +static void sighandler(int sig) +{ + switch (sig) { + case SIGPIPE: + DBG("SIGPIPE catched"); + return; + case SIGINT: + DBG("SIGINT catched"); + stop_threads(); + break; + case SIGTERM: + DBG("SIGTERM catched"); + stop_threads(); + break; + default: + break; + } +} + +/* + * Setup signal handler for : + * SIGINT, SIGTERM, SIGPIPE + */ +static int set_signal_handler(void) +{ + int ret = 0; + struct sigaction sa; + sigset_t sigset; + + if ((ret = sigemptyset(&sigset)) < 0) { + perror("sigemptyset"); + return ret; + } + + sa.sa_handler = sighandler; + sa.sa_mask = sigset; + sa.sa_flags = 0; + if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { + perror("sigaction"); + return ret; + } + + if ((ret = sigaction(SIGINT, &sa, NULL)) < 0) { + perror("sigaction"); + return ret; + } + + if ((ret = sigaction(SIGPIPE, &sa, NULL)) < 0) { + perror("sigaction"); + return ret; + } + + DBG("Signal handler set for SIGTERM, SIGPIPE and SIGINT"); + + return ret; +} + +/* + * Set open files limit to unlimited. This daemon can open a large number of + * file descriptors in order to consumer multiple kernel traces. + */ +static void set_ulimit(void) +{ + int ret; + struct rlimit lim; + + /* The kernel does not allowed an infinite limit for open files */ + lim.rlim_cur = 65535; + lim.rlim_max = 65535; + + ret = setrlimit(RLIMIT_NOFILE, &lim); + if (ret < 0) { + perror("failed to set open files limit"); + } +} + +/* + * main + */ +int main(int argc, char **argv) +{ + int ret = 0; + void *status; + const char *home_path; + + rcu_register_thread(); + + /* Create thread quit pipe */ + if ((ret = init_thread_quit_pipe()) < 0) { + goto error; + } + + /* Parse arguments */ + progname = argv[0]; + if ((ret = parse_args(argc, argv) < 0)) { + goto error; + } + + /* Daemonize */ + if (opt_daemon) { + ret = daemon(0, 0); + if (ret < 0) { + perror("daemon"); + goto error; + } + } + + /* Check if daemon is UID = 0 */ + is_root = !getuid(); + + if (is_root) { + ret = create_lttng_rundir(); + if (ret < 0) { + goto error; + } + + if (strlen(apps_unix_sock_path) == 0) { + snprintf(apps_unix_sock_path, PATH_MAX, + DEFAULT_GLOBAL_APPS_UNIX_SOCK); + } + + if (strlen(client_unix_sock_path) == 0) { + snprintf(client_unix_sock_path, PATH_MAX, + DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); + } + + /* Set global SHM for ust */ + if (strlen(wait_shm_path) == 0) { + snprintf(wait_shm_path, PATH_MAX, + DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); + } + } else { + home_path = get_home_dir(); + if (home_path == NULL) { + /* TODO: Add --socket PATH option */ + ERR("Can't get HOME directory for sockets creation."); + ret = -EPERM; + goto error; + } + + if (strlen(apps_unix_sock_path) == 0) { + snprintf(apps_unix_sock_path, PATH_MAX, + DEFAULT_HOME_APPS_UNIX_SOCK, home_path); + } + + /* Set the cli tool unix socket path */ + if (strlen(client_unix_sock_path) == 0) { + snprintf(client_unix_sock_path, PATH_MAX, + DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path); + } + + /* Set global SHM for ust */ + if (strlen(wait_shm_path) == 0) { + snprintf(wait_shm_path, PATH_MAX, + DEFAULT_HOME_APPS_WAIT_SHM_PATH, geteuid()); + } + } + + DBG("Client socket path %s", client_unix_sock_path); + DBG("Application socket path %s", apps_unix_sock_path); + + /* + * See if daemon already exist. + */ + if ((ret = check_existing_daemon()) < 0) { + ERR("Already running daemon.\n"); + /* + * We do not goto exit because we must not cleanup() + * because a daemon is already running. + */ + goto error; + } + + /* After this point, we can safely call cleanup() with "goto exit" */ + + /* + * These actions must be executed as root. We do that *after* setting up + * the sockets path because we MUST make the check for another daemon using + * those paths *before* trying to set the kernel consumer sockets and init + * kernel tracer. + */ + if (is_root) { + ret = set_consumer_sockets(&kconsumer_data); + if (ret < 0) { + goto exit; + } + + ret = set_consumer_sockets(&ustconsumer_data); + if (ret < 0) { + goto exit; + } + /* Setup kernel tracer */ + init_kernel_tracer(); + + /* Set ulimit for open files */ + set_ulimit(); + } + + if ((ret = set_signal_handler()) < 0) { + goto exit; + } + + /* Setup the needed unix socket */ + if ((ret = init_daemon_socket()) < 0) { + goto exit; + } + + /* Set credentials to socket */ + if (is_root && ((ret = set_permissions()) < 0)) { + goto exit; + } + + /* Get parent pid if -S, --sig-parent is specified. */ + if (opt_sig_parent) { + ppid = getppid(); + } + + /* Setup the kernel pipe for waking up the kernel thread */ + if ((ret = create_kernel_poll_pipe()) < 0) { + goto exit; + } + + /* Setup the thread apps communication pipe. */ + if ((ret = create_apps_cmd_pipe()) < 0) { + goto exit; + } + + /* Init UST command queue. */ + cds_wfq_init(&ust_cmd_queue.queue); + + /* Init UST app hash table */ + ust_app_ht_alloc(); + + /* + * Get session list pointer. This pointer MUST NOT be free(). This list is + * statically declared in session.c + */ + session_list_ptr = session_get_list(); + + /* Set up max poll set size */ + lttng_poll_set_max_size(); + + /* Create thread to manage the client socket */ + ret = pthread_create(&client_thread, NULL, + thread_manage_clients, (void *) NULL); + if (ret != 0) { + perror("pthread_create clients"); + goto exit_client; + } + + /* Create thread to dispatch registration */ + ret = pthread_create(&dispatch_thread, NULL, + thread_dispatch_ust_registration, (void *) NULL); + if (ret != 0) { + perror("pthread_create dispatch"); + goto exit_dispatch; + } + + /* Create thread to manage application registration. */ + ret = pthread_create(®_apps_thread, NULL, + thread_registration_apps, (void *) NULL); + if (ret != 0) { + perror("pthread_create registration"); + goto exit_reg_apps; + } + + /* Create thread to manage application socket */ + ret = pthread_create(&apps_thread, NULL, + thread_manage_apps, (void *) NULL); + if (ret != 0) { + perror("pthread_create apps"); + goto exit_apps; + } + + /* Create kernel thread to manage kernel event */ + ret = pthread_create(&kernel_thread, NULL, + thread_manage_kernel, (void *) NULL); + if (ret != 0) { + perror("pthread_create kernel"); + goto exit_kernel; + } + + ret = pthread_join(kernel_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + +exit_kernel: + ret = pthread_join(apps_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + +exit_apps: + ret = pthread_join(reg_apps_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + +exit_reg_apps: + ret = pthread_join(dispatch_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + +exit_dispatch: + ret = pthread_join(client_thread, &status); + if (ret != 0) { + perror("pthread_join"); + goto error; /* join error, exit without cleanup */ + } + + ret = join_consumer_thread(&kconsumer_data); + if (ret != 0) { + perror("join_consumer"); + goto error; /* join error, exit without cleanup */ + } + +exit_client: +exit: + /* + * cleanup() is called when no other thread is running. + */ + rcu_thread_online(); + cleanup(); + rcu_thread_offline(); + rcu_unregister_thread(); + if (!ret) + exit(EXIT_SUCCESS); +error: + exit(EXIT_FAILURE); +} diff --git a/lttng-sessiond/session.c b/lttng-sessiond/session.c new file mode 100644 index 000000000..c7ce843d7 --- /dev/null +++ b/lttng-sessiond/session.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include + +#include "hashtable.h" +#include "session.h" + +#include "../hashtable/hash.h" + +/* + * NOTES: + * + * No ltt_session.lock is taken here because those data structure are widely + * spread across the lttng-tools code base so before caling functions below + * that can read/write a session, the caller MUST acquire the session lock + * using session_lock() and session_unlock(). + */ + +/* + * Init tracing session list. + * + * Please see session.h for more explanation and correct usage of the list. + */ +static struct ltt_session_list ltt_session_list = { + .head = CDS_LIST_HEAD_INIT(ltt_session_list.head), + .lock = PTHREAD_MUTEX_INITIALIZER, + .count = 0, +}; + +/* + * Add a ltt_session structure to the global list. + * + * The caller MUST acquire the session list lock before. + */ +static void add_session_list(struct ltt_session *ls) +{ + cds_list_add(&ls->list, <t_session_list.head); + ltt_session_list.count++; +} + +/* + * Delete a ltt_session structure to the global list. + * + * The caller MUST acquire the session list lock before. + */ +static void del_session_list(struct ltt_session *ls) +{ + cds_list_del(&ls->list); + /* Sanity check */ + if (ltt_session_list.count > 0) { + ltt_session_list.count--; + } +} + +/* + * Return a pointer to the session list. + */ +struct ltt_session_list *session_get_list(void) +{ + return <t_session_list; +} + +/* + * Acquire session list lock + */ +void session_lock_list(void) +{ + pthread_mutex_lock(<t_session_list.lock); +} + +/* + * Release session list lock + */ +void session_unlock_list(void) +{ + pthread_mutex_unlock(<t_session_list.lock); +} + +/* + * Acquire session lock + */ +void session_lock(struct ltt_session *session) +{ + pthread_mutex_lock(&session->lock); +} + +/* + * Release session lock + */ +void session_unlock(struct ltt_session *session) +{ + pthread_mutex_unlock(&session->lock); +} + +/* + * Return a ltt_session structure ptr that matches name. If no session found, + * NULL is returned. This must be called with the session lock held using + * session_lock_list and session_unlock_list. + */ +struct ltt_session *session_find_by_name(char *name) +{ + struct ltt_session *iter; + + DBG2("Trying to find session by name %s", name); + + cds_list_for_each_entry(iter, <t_session_list.head, list) { + if (strncmp(iter->name, name, NAME_MAX) == 0) { + goto found; + } + } + + iter = NULL; + +found: + return iter; +} + +/* + * Delete session from the session list and free the memory. + * + * Return -1 if no session is found. On success, return 1; + */ +int session_destroy(struct ltt_session *session) +{ + /* Safety check */ + if (session == NULL) { + ERR("Session pointer was null on session destroy"); + return LTTCOMM_OK; + } + + DBG("Destroying session %s", session->name); + del_session_list(session); + pthread_mutex_destroy(&session->lock); + free(session); + + return LTTCOMM_OK; +} + +/* + * Create a brand new session and add it to the session list. + */ +int session_create(char *name, char *path) +{ + int ret; + struct ltt_session *new_session; + + new_session = session_find_by_name(name); + if (new_session != NULL) { + ret = LTTCOMM_EXIST_SESS; + goto error_exist; + } + + /* Allocate session data structure */ + new_session = malloc(sizeof(struct ltt_session)); + if (new_session == NULL) { + perror("malloc"); + ret = LTTCOMM_FATAL; + goto error_malloc; + } + + /* Define session name */ + if (name != NULL) { + if (snprintf(new_session->name, NAME_MAX, "%s", name) < 0) { + ret = LTTCOMM_FATAL; + goto error_asprintf; + } + } else { + ERR("No session name given"); + ret = LTTCOMM_FATAL; + goto error; + } + + /* Define session system path */ + if (path != NULL) { + if (snprintf(new_session->path, PATH_MAX, "%s", path) < 0) { + ret = LTTCOMM_FATAL; + goto error_asprintf; + } + } else { + ERR("No session path given"); + ret = LTTCOMM_FATAL; + goto error; + } + + /* Init kernel session */ + new_session->kernel_session = NULL; + new_session->ust_session = NULL; + + /* Init lock */ + pthread_mutex_init(&new_session->lock, NULL); + + /* Add new session to the session list */ + session_lock_list(); + add_session_list(new_session); + session_unlock_list(); + + DBG("Tracing session %s created in %s", name, path); + + return LTTCOMM_OK; + +error: +error_asprintf: + if (new_session != NULL) { + free(new_session); + } + +error_exist: +error_malloc: + return ret; +} diff --git a/lttng-sessiond/session.h b/lttng-sessiond/session.h new file mode 100644 index 000000000..f6a9fff4d --- /dev/null +++ b/lttng-sessiond/session.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_SESSION_H +#define _LTT_SESSION_H + +#include +#include + +#include "trace-kernel.h" +#include "trace-ust.h" + +/* + * Tracing session list + * + * Statically declared in session.c and can be accessed by using + * session_get_list() function that returns the pointer to the list. + */ +struct ltt_session_list { + /* + * This lock protects any read/write access to the list and count (which is + * basically the list size). All public functions in session.c acquire this + * lock and release it before returning. If none of those functions are + * used, the lock MUST be acquired in order to iterate or/and do any + * actions on that list. + */ + pthread_mutex_t lock; + + /* + * Number of element in the list. The session list lock MUST be acquired if + * this counter is used when iterating over the session list. + */ + unsigned int count; + + /* Linked list head */ + struct cds_list_head head; +}; + +/* + * This data structure contains information needed to identify a tracing + * session for both LTTng and UST. + */ +struct ltt_session { + char name[NAME_MAX]; + char path[PATH_MAX]; + struct ltt_kernel_session *kernel_session; + struct ltt_ust_session *ust_session; + /* + * Protect any read/write on this session data structure. This lock must be + * acquired *before* using any public functions declared below. Use + * session_lock() and session_unlock() for that. + */ + pthread_mutex_t lock; + struct cds_list_head list; +}; + +/* Prototypes */ +int session_create(char *name, char *path); +int session_destroy(struct ltt_session *session); + +void session_lock(struct ltt_session *session); +void session_lock_list(void); +void session_unlock(struct ltt_session *session); +void session_unlock_list(void); + +struct ltt_session *session_find_by_name(char *name); +struct ltt_session_list *session_get_list(void); + +#endif /* _LTT_SESSION_H */ diff --git a/lttng-sessiond/shm.c b/lttng-sessiond/shm.c new file mode 100644 index 000000000..7dac1659f --- /dev/null +++ b/lttng-sessiond/shm.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shm.h" + +/* + * Using fork to set umask in the child process (not multi-thread safe). We + * deal with the shm_open vs ftruncate race (happening when the sessiond owns + * the shm and does not let everybody modify it, to ensure safety against + * shm_unlink) by simply letting the mmap fail and retrying after a few + * seconds. For global shm, everybody has rw access to it until the sessiond + * starts. + */ +static int get_wait_shm(char *shm_path, size_t mmap_size, int global) +{ + int wait_shm_fd, ret; + mode_t mode; + + /* Default permissions */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + /* Change owner of the shm path */ + if (global) { + ret = chown(shm_path, 0, 0); + if (ret < 0) { + if (errno != ENOENT) { + perror("chown wait shm"); + goto error; + } + } + + /* + * If global session daemon, any application can register so the shm + * needs to be set in read-only mode for others. + */ + mode |= S_IROTH; + } else { + ret = chown(shm_path, getuid(), getgid()); + if (ret < 0) { + if (errno != ENOENT) { + perror("chown wait shm"); + goto error; + } + } + } + + /* + * Set permissions to the shm even if we did not create the shm. + */ + ret = chmod(shm_path, mode); + if (ret < 0) { + if (errno != ENOENT) { + perror("chmod wait shm"); + goto error; + } + } + + /* + * We're alone in a child process, so we can modify the process-wide + * umask. + */ + umask(~mode); + + /* + * Try creating shm (or get rw access). We don't do an exclusive open, + * because we allow other processes to create+ftruncate it concurrently. + */ + wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); + if (wait_shm_fd < 0) { + perror("shm_open wait shm"); + goto error; + } + + ret = ftruncate(wait_shm_fd, mmap_size); + if (ret < 0) { + perror("ftruncate wait shm"); + exit(EXIT_FAILURE); + } + + ret = fchmod(wait_shm_fd, mode); + if (ret < 0) { + perror("fchmod"); + exit(EXIT_FAILURE); + } + + DBG("Got the wait shm fd %d", wait_shm_fd); + + return wait_shm_fd; + +error: + DBG("Failing to get the wait shm fd"); + + return -1; +} + +/* + * Return the wait shm mmap for UST application notification. The global + * variable is used to indicate if the the session daemon is global + * (root:tracing) or running with an unprivileged user. + * + * This returned value is used by futex_wait_update() in futex.c to WAKE all + * waiters which are UST application waiting for a session daemon. + */ +char *shm_ust_get_mmap(char *shm_path, int global) +{ + size_t mmap_size = sysconf(_SC_PAGE_SIZE); + int wait_shm_fd, ret; + char *wait_shm_mmap; + + wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); + if (wait_shm_fd < 0) { + goto error; + } + + wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, + MAP_SHARED, wait_shm_fd, 0); + + /* close shm fd immediately after taking the mmap reference */ + ret = close(wait_shm_fd); + if (ret) { + perror("Error closing fd"); + } + + if (wait_shm_mmap == MAP_FAILED) { + DBG("mmap error (can be caused by race with ust)."); + goto error; + } + + return wait_shm_mmap; + +error: + return NULL; +} diff --git a/lttng-sessiond/shm.h b/lttng-sessiond/shm.h new file mode 100644 index 000000000..2d301bbac --- /dev/null +++ b/lttng-sessiond/shm.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_SHM_H +#define _LTT_SHM_H + +char *shm_ust_get_mmap(char *shm_path, int global); + +#endif /* _LTT_SHM_H */ diff --git a/lttng-sessiond/trace-kernel.c b/lttng-sessiond/trace-kernel.c new file mode 100644 index 000000000..fa3ba692c --- /dev/null +++ b/lttng-sessiond/trace-kernel.c @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include + +#include "trace-kernel.h" + +/* + * Find the channel name for the given kernel session. + */ +struct ltt_kernel_channel *trace_kernel_get_channel_by_name( + char *name, struct ltt_kernel_session *session) +{ + struct ltt_kernel_channel *chan; + + if (session == NULL) { + ERR("Undefine session"); + goto error; + } + + DBG("Trying to find channel %s", name); + + cds_list_for_each_entry(chan, &session->channel_list.head, list) { + if (strcmp(name, chan->channel->name) == 0) { + DBG("Found channel by name %s", name); + return chan; + } + } + +error: + return NULL; +} + +/* + * Find the event name for the given channel. + */ +struct ltt_kernel_event *trace_kernel_get_event_by_name( + char *name, struct ltt_kernel_channel *channel) +{ + struct ltt_kernel_event *ev; + + if (channel == NULL) { + ERR("Undefine channel"); + goto error; + } + + cds_list_for_each_entry(ev, &channel->events_list.head, list) { + if (strcmp(name, ev->event->name) == 0) { + DBG("Found event by name %s for channel %s", name, + channel->channel->name); + return ev; + } + } + +error: + return NULL; +} + +/* + * Allocate and initialize a kernel session data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_kernel_session *trace_kernel_create_session(char *path) +{ + int ret; + struct ltt_kernel_session *lks; + + /* Allocate a new ltt kernel session */ + lks = malloc(sizeof(struct ltt_kernel_session)); + if (lks == NULL) { + perror("create kernel session malloc"); + goto error; + } + + /* Init data structure */ + lks->fd = 0; + lks->metadata_stream_fd = 0; + lks->channel_count = 0; + lks->stream_count_global = 0; + lks->metadata = NULL; + lks->consumer_fd = 0; + CDS_INIT_LIST_HEAD(&lks->channel_list.head); + + /* Set session path */ + ret = asprintf(&lks->trace_path, "%s/kernel", path); + if (ret < 0) { + perror("asprintf kernel traces path"); + goto error; + } + + return lks; + +error: + return NULL; +} + +/* + * Allocate and initialize a kernel channel data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_kernel_channel *trace_kernel_create_channel(struct lttng_channel *chan, char *path) +{ + int ret; + struct ltt_kernel_channel *lkc; + + lkc = malloc(sizeof(struct ltt_kernel_channel)); + if (lkc == NULL) { + perror("ltt_kernel_channel malloc"); + goto error; + } + + lkc->channel = malloc(sizeof(struct lttng_channel)); + if (lkc->channel == NULL) { + perror("lttng_channel malloc"); + goto error; + } + memcpy(lkc->channel, chan, sizeof(struct lttng_channel)); + + lkc->fd = 0; + lkc->stream_count = 0; + lkc->event_count = 0; + lkc->enabled = 1; + lkc->ctx = NULL; + /* Init linked list */ + CDS_INIT_LIST_HEAD(&lkc->events_list.head); + CDS_INIT_LIST_HEAD(&lkc->stream_list.head); + /* Set default trace output path */ + ret = asprintf(&lkc->pathname, "%s", path); + if (ret < 0) { + perror("asprintf kernel create channel"); + goto error; + } + + return lkc; + +error: + return NULL; +} + +/* + * Allocate and initialize a kernel event. Set name and event type. + * + * Return pointer to structure or NULL. + */ +struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev) +{ + struct ltt_kernel_event *lke; + struct lttng_kernel_event *attr; + + lke = malloc(sizeof(struct ltt_kernel_event)); + attr = malloc(sizeof(struct lttng_kernel_event)); + if (lke == NULL || attr == NULL) { + perror("kernel event malloc"); + goto error; + } + + switch (ev->type) { + case LTTNG_EVENT_PROBE: + attr->instrumentation = LTTNG_KERNEL_KPROBE; + attr->u.kprobe.addr = ev->attr.probe.addr; + attr->u.kprobe.offset = ev->attr.probe.offset; + strncpy(attr->u.kprobe.symbol_name, + ev->attr.probe.symbol_name, LTTNG_SYM_NAME_LEN); + attr->u.kprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_FUNCTION: + attr->instrumentation = LTTNG_KERNEL_KRETPROBE; + attr->u.kretprobe.addr = ev->attr.probe.addr; + attr->u.kretprobe.offset = ev->attr.probe.offset; + attr->u.kretprobe.offset = ev->attr.probe.offset; + strncpy(attr->u.kretprobe.symbol_name, + ev->attr.probe.symbol_name, LTTNG_SYM_NAME_LEN); + attr->u.kretprobe.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + attr->instrumentation = LTTNG_KERNEL_FUNCTION; + strncpy(attr->u.ftrace.symbol_name, + ev->attr.ftrace.symbol_name, LTTNG_SYM_NAME_LEN); + attr->u.ftrace.symbol_name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_TRACEPOINT: + attr->instrumentation = LTTNG_KERNEL_TRACEPOINT; + break; + case LTTNG_EVENT_SYSCALL: + attr->instrumentation = LTTNG_KERNEL_SYSCALL; + break; + case LTTNG_EVENT_ALL: + attr->instrumentation = LTTNG_KERNEL_ALL; + break; + default: + ERR("Unknown kernel instrumentation type (%d)", ev->type); + goto error; + } + + /* Copy event name */ + strncpy(attr->name, ev->name, LTTNG_SYM_NAME_LEN); + attr->name[LTTNG_SYM_NAME_LEN - 1] = '\0'; + + /* Setting up a kernel event */ + lke->fd = 0; + lke->event = attr; + lke->enabled = 1; + lke->ctx = NULL; + + return lke; + +error: + return NULL; +} + +/* + * Allocate and initialize a kernel metadata. + * + * Return pointer to structure or NULL. + */ +struct ltt_kernel_metadata *trace_kernel_create_metadata(char *path) +{ + int ret; + struct ltt_kernel_metadata *lkm; + struct lttng_channel *chan; + + lkm = malloc(sizeof(struct ltt_kernel_metadata)); + chan = malloc(sizeof(struct lttng_channel)); + if (lkm == NULL || chan == NULL) { + perror("kernel metadata malloc"); + goto error; + } + + /* Set default attributes */ + chan->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE; + chan->attr.subbuf_size = DEFAULT_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.output = DEFAULT_KERNEL_CHANNEL_OUTPUT; + + /* Init metadata */ + lkm->fd = 0; + lkm->conf = chan; + /* Set default metadata path */ + ret = asprintf(&lkm->pathname, "%s/metadata", path); + if (ret < 0) { + perror("asprintf kernel metadata"); + goto error; + } + + return lkm; + +error: + return NULL; +} + +/* + * Allocate and initialize a kernel stream. The stream is set to ACTIVE_FD by + * default. + * + * Return pointer to structure or NULL. + */ +struct ltt_kernel_stream *trace_kernel_create_stream(void) +{ + struct ltt_kernel_stream *lks; + + lks = malloc(sizeof(struct ltt_kernel_stream)); + if (lks == NULL) { + perror("kernel stream malloc"); + goto error; + } + + /* Init stream */ + lks->fd = 0; + lks->pathname = NULL; + lks->state = 0; + + return lks; + +error: + return NULL; +} + +/* + * Cleanup kernel stream structure. + */ +void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream) +{ + DBG("[trace] Closing stream fd %d", stream->fd); + /* Close kernel fd */ + close(stream->fd); + /* Remove from stream list */ + cds_list_del(&stream->list); + + free(stream->pathname); + free(stream); +} + +/* + * Cleanup kernel event structure. + */ +void trace_kernel_destroy_event(struct ltt_kernel_event *event) +{ + DBG("[trace] Closing event fd %d", event->fd); + /* Close kernel fd */ + close(event->fd); + + /* Remove from event list */ + cds_list_del(&event->list); + + free(event->event); + free(event->ctx); + free(event); +} + +/* + * Cleanup kernel channel structure. + */ +void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel) +{ + struct ltt_kernel_stream *stream, *stmp; + struct ltt_kernel_event *event, *etmp; + + DBG("[trace] Closing channel fd %d", channel->fd); + /* Close kernel fd */ + close(channel->fd); + + /* For each stream in the channel list */ + cds_list_for_each_entry_safe(stream, stmp, &channel->stream_list.head, list) { + trace_kernel_destroy_stream(stream); + } + + /* For each event in the channel list */ + cds_list_for_each_entry_safe(event, etmp, &channel->events_list.head, list) { + trace_kernel_destroy_event(event); + } + + /* Remove from channel list */ + cds_list_del(&channel->list); + + free(channel->pathname); + free(channel->channel); + free(channel->ctx); + free(channel); +} + +/* + * Cleanup kernel metadata structure. + */ +void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata) +{ + DBG("[trace] Closing metadata fd %d", metadata->fd); + /* Close kernel fd */ + close(metadata->fd); + + free(metadata->conf); + free(metadata->pathname); + free(metadata); +} + +/* + * Cleanup kernel session structure + */ +void trace_kernel_destroy_session(struct ltt_kernel_session *session) +{ + struct ltt_kernel_channel *channel, *ctmp; + + DBG("[trace] Closing session fd %d", session->fd); + /* Close kernel fds */ + close(session->fd); + + if (session->metadata_stream_fd != 0) { + DBG("[trace] Closing metadata stream fd %d", session->metadata_stream_fd); + close(session->metadata_stream_fd); + } + + if (session->metadata != NULL) { + trace_kernel_destroy_metadata(session->metadata); + } + + cds_list_for_each_entry_safe(channel, ctmp, &session->channel_list.head, list) { + trace_kernel_destroy_channel(channel); + } + + free(session->trace_path); + free(session); +} diff --git a/lttng-sessiond/trace-kernel.h b/lttng-sessiond/trace-kernel.h new file mode 100644 index 000000000..bcea65148 --- /dev/null +++ b/lttng-sessiond/trace-kernel.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_TRACE_KERNEL_H +#define _LTT_TRACE_KERNEL_H + +#include +#include + +#include +#include + +/* Kernel event list */ +struct ltt_kernel_event_list { + struct cds_list_head head; +}; + +/* Channel stream list */ +struct ltt_kernel_stream_list { + struct cds_list_head head; +}; + +/* Channel list */ +struct ltt_kernel_channel_list { + struct cds_list_head head; +}; + +/* Kernel event */ +struct ltt_kernel_event { + int fd; + int enabled; + /* + * TODO: need internal representation to support more than a + * single context. + */ + struct lttng_kernel_context *ctx; + struct lttng_kernel_event *event; + struct cds_list_head list; +}; + +/* Kernel channel */ +struct ltt_kernel_channel { + int fd; + int enabled; + char *pathname; + unsigned int stream_count; + unsigned int event_count; + /* + * TODO: need internal representation to support more than a + * single context. + */ + struct lttng_kernel_context *ctx; + struct lttng_channel *channel; + struct ltt_kernel_event_list events_list; + struct ltt_kernel_stream_list stream_list; + struct cds_list_head list; +}; + +/* Metadata */ +struct ltt_kernel_metadata { + int fd; + char *pathname; + struct lttng_channel *conf; +}; + +/* Channel stream */ +struct ltt_kernel_stream { + int fd; + char *pathname; + int state; + struct cds_list_head list; +}; + +/* Kernel session */ +struct ltt_kernel_session { + int fd; + int metadata_stream_fd; + int consumer_fds_sent; + int consumer_fd; + unsigned int channel_count; + unsigned int stream_count_global; + char *trace_path; + struct ltt_kernel_metadata *metadata; + struct ltt_kernel_channel_list channel_list; +}; + +/* + * Lookup functions. NULL is returned if not found. + */ +struct ltt_kernel_event *trace_kernel_get_event_by_name( + char *name, struct ltt_kernel_channel *channel); +struct ltt_kernel_channel *trace_kernel_get_channel_by_name( + char *name, struct ltt_kernel_session *session); + +/* + * Create functions malloc() the data structure. + */ +struct ltt_kernel_session *trace_kernel_create_session(char *path); +struct ltt_kernel_channel *trace_kernel_create_channel(struct lttng_channel *chan, char *path); +struct ltt_kernel_event *trace_kernel_create_event(struct lttng_event *ev); +struct ltt_kernel_metadata *trace_kernel_create_metadata(char *path); +struct ltt_kernel_stream *trace_kernel_create_stream(void); + +/* + * Destroy functions free() the data structure and remove from linked list if + * it's applies. + */ +void trace_kernel_destroy_session(struct ltt_kernel_session *session); +void trace_kernel_destroy_metadata(struct ltt_kernel_metadata *metadata); +void trace_kernel_destroy_channel(struct ltt_kernel_channel *channel); +void trace_kernel_destroy_event(struct ltt_kernel_event *event); +void trace_kernel_destroy_stream(struct ltt_kernel_stream *stream); + +#endif /* _LTT_TRACE_KERNEL_H */ diff --git a/lttng-sessiond/trace-ust.c b/lttng-sessiond/trace-ust.c new file mode 100644 index 000000000..e912c53c7 --- /dev/null +++ b/lttng-sessiond/trace-ust.c @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include + +#include "hashtable.h" +#include "trace-ust.h" + +/* + * Find the channel in the hashtable. + */ +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, + char *name) +{ + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + node = hashtable_lookup(ht, (void *) name, strlen(name), &iter); + if (node == NULL) { + rcu_read_unlock(); + goto error; + } + rcu_read_unlock(); + + DBG2("Trace UST channel %s found by name", name); + + return caa_container_of(node, struct ltt_ust_channel, node); + +error: + DBG2("Trace UST channel %s not found by name", name); + return NULL; +} + +/* + * Find the event in the hashtable. + */ +struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, + char *name) +{ + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + node = hashtable_lookup(ht, (void *) name, strlen(name), &iter); + if (node == NULL) { + rcu_read_unlock(); + goto error; + } + rcu_read_unlock(); + + DBG2("Found UST event by name %s", name); + + return caa_container_of(node, struct ltt_ust_event, node); + +error: + return NULL; +} + +/* + * Allocate and initialize a ust session data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid, + struct lttng_domain *domain) +{ + int ret; + struct ltt_ust_session *lus; + + /* Allocate a new ltt ust session */ + lus = malloc(sizeof(struct ltt_ust_session)); + if (lus == NULL) { + perror("create ust session malloc"); + goto error; + } + + /* Init data structure */ + lus->consumer_fds_sent = 0; + lus->uid = uid; + + /* Alloc UST domain hash tables */ + lus->domain_pid = hashtable_new(0); + lus->domain_exec = hashtable_new_str(0); + + /* Alloc UST global domain channels' HT */ + lus->domain_global.channels = hashtable_new_str(0); + + /* Set session path */ + ret = snprintf(lus->pathname, PATH_MAX, "%s/ust", path); + if (ret < 0) { + PERROR("snprintf kernel traces path"); + goto error; + } + + DBG2("UST trace session create successful"); + + return lus; + +error: + return NULL; +} + +/* + * Allocate and initialize a ust channel data structure. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *chan, + char *path) +{ + int ret; + struct ltt_ust_channel *luc; + + luc = malloc(sizeof(struct ltt_ust_channel)); + if (luc == NULL) { + perror("ltt_ust_channel malloc"); + goto error; + } + + /* Copy UST channel attributes */ + luc->attr.overwrite = chan->attr.overwrite; + luc->attr.subbuf_size = chan->attr.subbuf_size; + luc->attr.num_subbuf = chan->attr.num_subbuf; + luc->attr.switch_timer_interval = chan->attr.switch_timer_interval; + luc->attr.read_timer_interval = chan->attr.read_timer_interval; + luc->attr.output = chan->attr.output; + + /* Translate to UST output enum */ + switch (luc->attr.output) { + default: + luc->attr.output = LTTNG_UST_MMAP; + break; + } + + /* Copy channel name */ + strncpy(luc->name, chan->name, sizeof(&luc->name)); + luc->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + + /* Init node */ + hashtable_node_init(&luc->node, (void *) luc->name, strlen(luc->name)); + /* Alloc hash tables */ + luc->events = hashtable_new_str(0); + luc->ctx = hashtable_new_str(0); + + /* Set trace output path */ + ret = snprintf(luc->pathname, PATH_MAX, "%s", path); + if (ret < 0) { + perror("asprintf ust create channel"); + goto error; + } + + DBG2("Trace UST channel %s created", luc->name); + + return luc; + +error: + return NULL; +} + +/* + * Allocate and initialize a ust event. Set name and event type. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev) +{ + struct ltt_ust_event *lue; + + lue = malloc(sizeof(struct ltt_ust_event)); + if (lue == NULL) { + PERROR("ust event malloc"); + goto error; + } + + switch (ev->type) { + case LTTNG_EVENT_PROBE: + lue->attr.instrumentation = LTTNG_UST_PROBE; + break; + case LTTNG_EVENT_FUNCTION: + lue->attr.instrumentation = LTTNG_UST_FUNCTION; + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + lue->attr.instrumentation = LTTNG_UST_FUNCTION; + break; + case LTTNG_EVENT_TRACEPOINT: + lue->attr.instrumentation = LTTNG_UST_TRACEPOINT; + break; + default: + ERR("Unknown ust instrumentation type (%d)", ev->type); + goto error; + } + + /* Copy event name */ + strncpy(lue->attr.name, ev->name, LTTNG_UST_SYM_NAME_LEN); + lue->attr.name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0'; + + /* Init node */ + hashtable_node_init(&lue->node, (void *) lue->attr.name, + strlen(lue->attr.name)); + /* Alloc context hash tables */ + lue->ctx = hashtable_new_str(5); + + return lue; + +error: + return NULL; +} + +/* + * Allocate and initialize a ust metadata. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_metadata *trace_ust_create_metadata(char *path) +{ + int ret; + struct ltt_ust_metadata *lum; + + lum = malloc(sizeof(struct ltt_ust_metadata)); + if (lum == NULL) { + perror("ust metadata malloc"); + goto error; + } + + /* Set default attributes */ + lum->attr.overwrite = DEFAULT_CHANNEL_OVERWRITE; + lum->attr.subbuf_size = DEFAULT_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.output = DEFAULT_UST_CHANNEL_OUTPUT; + + lum->handle = -1; + /* Set metadata trace path */ + ret = snprintf(lum->pathname, PATH_MAX, "%s/metadata", path); + if (ret < 0) { + perror("asprintf ust metadata"); + goto error; + } + + return lum; + +error: + return NULL; +} + +/* + * RCU safe free context structure. + */ +static void destroy_context_rcu(struct rcu_head *head) +{ + struct cds_lfht_node *node = + caa_container_of(head, struct cds_lfht_node, head); + struct ltt_ust_context *ctx = + caa_container_of(node, struct ltt_ust_context, node); + + free(ctx); +} + +/* + * Cleanup UST context hash table. + */ +static void destroy_context(struct cds_lfht *ht) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + hashtable_get_first(ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(ht, &iter); + if (!ret) { + call_rcu(&node->head, destroy_context_rcu); + } + hashtable_get_next(ht, &iter); + } +} + +/* + * Cleanup ust event structure. + */ +void trace_ust_destroy_event(struct ltt_ust_event *event) +{ + DBG2("Trace destroy UST event %s", event->attr.name); + destroy_context(event->ctx); + free(event); +} + +/* + * URCU intermediate call to complete destroy event. + */ +static void destroy_event_rcu(struct rcu_head *head) +{ + struct cds_lfht_node *node = + caa_container_of(head, struct cds_lfht_node, head); + struct ltt_ust_event *event = + caa_container_of(node, struct ltt_ust_event, node); + + trace_ust_destroy_event(event); +} + +/* + * Cleanup ust channel structure. + */ +void trace_ust_destroy_channel(struct ltt_ust_channel *channel) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + DBG2("Trace destroy UST channel %s", channel->name); + + rcu_read_lock(); + + hashtable_get_first(channel->events, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(channel->events, &iter); + if (!ret) { + call_rcu(&node->head, destroy_event_rcu); + } + hashtable_get_next(channel->events, &iter); + } + + free(channel->events); + destroy_context(channel->ctx); + free(channel); + + rcu_read_unlock(); +} + +/* + * URCU intermediate call to complete destroy channel. + */ +static void destroy_channel_rcu(struct rcu_head *head) +{ + struct cds_lfht_node *node = + caa_container_of(head, struct cds_lfht_node, head); + struct ltt_ust_channel *channel = + caa_container_of(node, struct ltt_ust_channel, node); + + trace_ust_destroy_channel(channel); +} + +/* + * Cleanup ust metadata structure. + */ +void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata) +{ + DBG2("Trace UST destroy metadata %d", metadata->handle); + + free(metadata); +} + +/* + * Iterate over a hash table containing channels and cleanup safely. + */ +static void destroy_channels(struct cds_lfht *channels) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + hashtable_get_first(channels, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(channels, &iter); + if (!ret) { + call_rcu(&node->head, destroy_channel_rcu); + } + hashtable_get_next(channels, &iter); + } +} + +/* + * Cleanup UST pid domain. + */ +static void destroy_domain_pid(struct cds_lfht *ht) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct ltt_ust_domain_pid *d; + + hashtable_get_first(ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(ht , &iter); + if (!ret) { + d = caa_container_of(node, struct ltt_ust_domain_pid, node); + destroy_channels(d->channels); + } + hashtable_get_next(ht, &iter); + } +} + +/* + * Cleanup UST exec name domain. + */ +static void destroy_domain_exec(struct cds_lfht *ht) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + struct ltt_ust_domain_exec *d; + + hashtable_get_first(ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(ht , &iter); + if (!ret) { + d = caa_container_of(node, struct ltt_ust_domain_exec, node); + destroy_channels(d->channels); + } + hashtable_get_next(ht, &iter); + } +} + +/* + * Cleanup UST global domain. + */ +static void destroy_domain_global(struct ltt_ust_domain_global *dom) +{ + destroy_channels(dom->channels); +} + +/* + * Cleanup ust session structure + */ +void trace_ust_destroy_session(struct ltt_ust_session *session) +{ + /* Extra safety */ + if (session == NULL) { + return; + } + + rcu_read_lock(); + + DBG2("Trace UST destroy session %d", session->uid); + + //if (session->metadata != NULL) { + // trace_ust_destroy_metadata(session->metadata); + //} + + /* Cleaning up UST domain */ + destroy_domain_global(&session->domain_global); + destroy_domain_pid(session->domain_pid); + destroy_domain_exec(session->domain_exec); + + free(session); + + rcu_read_unlock(); +} diff --git a/lttng-sessiond/trace-ust.h b/lttng-sessiond/trace-ust.h new file mode 100644 index 000000000..aea7e714f --- /dev/null +++ b/lttng-sessiond/trace-ust.h @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_TRACE_UST_H +#define _LTT_TRACE_UST_H + +#include +#include +#include +#include +#include + +#include "ust-ctl.h" + +#include "../hashtable/rculfhash.h" + +/* UST Stream list */ +struct ltt_ust_stream_list { + unsigned int count; + struct cds_list_head head; +}; + +/* Context hash table nodes */ +struct ltt_ust_context { + struct lttng_ust_context ctx; + struct cds_lfht_node node; +}; + +/* UST event */ +struct ltt_ust_event { + struct lttng_ust_event attr; + struct cds_lfht *ctx; + struct cds_lfht_node node; +}; + +/* UST stream */ +struct ltt_ust_stream { + int handle; + char pathname[PATH_MAX]; + struct lttng_ust_object_data *obj; + struct cds_lfht_node node; +}; + +/* UST channel */ +struct ltt_ust_channel { + char name[LTTNG_UST_SYM_NAME_LEN]; + char pathname[PATH_MAX]; + struct lttng_ust_channel attr; + struct cds_lfht *ctx; + struct cds_lfht *events; + struct cds_lfht_node node; +}; + +/* UST Metadata */ +struct ltt_ust_metadata { + int handle; + struct lttng_ust_object_data *obj; + char pathname[PATH_MAX]; /* Trace file path name */ + struct lttng_ust_channel attr; + struct lttng_ust_object_data *stream_obj; +}; + +/* UST domain global (LTTNG_DOMAIN_UST) */ +struct ltt_ust_domain_global { + struct cds_lfht *channels; +}; + +/* UST domain pid (LTTNG_DOMAIN_UST_PID) */ +struct ltt_ust_domain_pid { + pid_t pid; + struct cds_lfht *channels; + struct cds_lfht_node node; +}; + +/* UST domain exec name (LTTNG_DOMAIN_UST_EXEC_NAME) */ +struct ltt_ust_domain_exec { + char exec_name[LTTNG_UST_SYM_NAME_LEN]; + struct cds_lfht *channels; + struct cds_lfht_node node; +}; + +/* UST session */ +struct ltt_ust_session { + int uid; /* Unique identifier of session */ + int consumer_fds_sent; + int consumer_fd; + char pathname[PATH_MAX]; + struct ltt_ust_domain_global domain_global; + /* + * Those two hash tables contains data for a specific UST domain and each + * contains a HT of channels. See ltt_ust_domain_exec and + * ltt_ust_domain_pid data structures. + */ + struct cds_lfht *domain_pid; + struct cds_lfht *domain_exec; +}; + +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST + +/* + * Lookup functions. NULL is returned if not found. + */ +struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, + char *name); +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, + char *name); + +/* + * Create functions malloc() the data structure. + */ +struct ltt_ust_session *trace_ust_create_session(char *path, unsigned int uid, + struct lttng_domain *domain); +struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, + char *path); +struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev); +struct ltt_ust_metadata *trace_ust_create_metadata(char *path); + +/* + * Destroy functions free() the data structure and remove from linked list if + * it's applies. + */ +void trace_ust_destroy_session(struct ltt_ust_session *session); +void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata); +void trace_ust_destroy_channel(struct ltt_ust_channel *channel); +void trace_ust_destroy_event(struct ltt_ust_event *event); + +#else + +static inline +struct ltt_ust_event *trace_ust_find_event_by_name(struct cds_lfht *ht, + char *name) +{ + return NULL; +} + +static inline +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct cds_lfht *ht, + char *name) +{ + return NULL; +} + +static inline +struct ltt_ust_session *trace_ust_create_session(char *path, pid_t pid, + struct lttng_domain *domain) +{ + return NULL; +} +static inline +struct ltt_ust_channel *trace_ust_create_channel(struct lttng_channel *attr, + char *path) +{ + return NULL; +} +static inline +struct ltt_ust_event *trace_ust_create_event(struct lttng_event *ev) +{ + return NULL; +} +static inline +struct ltt_ust_metadata *trace_ust_create_metadata(char *path) +{ + return NULL; +} + +static inline +void trace_ust_destroy_session(struct ltt_ust_session *session) +{ +} + +static inline +void trace_ust_destroy_metadata(struct ltt_ust_metadata *metadata) +{ +} + +static inline +void trace_ust_destroy_channel(struct ltt_ust_channel *channel) +{ +} + +static inline +void trace_ust_destroy_event(struct ltt_ust_event *event) +{ +} + +#endif /* CONFIG_CONFIG_LTTNG_TOOLS_HAVE_UST */ + +#endif /* _LTT_TRACE_UST_H */ diff --git a/lttng-sessiond/ust-app.c b/lttng-sessiond/ust-app.c new file mode 100644 index 000000000..489bd8b2f --- /dev/null +++ b/lttng-sessiond/ust-app.c @@ -0,0 +1,831 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hashtable.h" +#include "ust-app.h" +#include "../hashtable/hash.h" +#include "ust-ctl.h" +#include "ust-consumer.h" + +/* + * Delete a traceable application structure from the global list. + */ +static void delete_ust_app(struct ust_app *lta) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + + free(lta->sessions); + close(lta->key.sock); + + /* Remove from apps hash table */ + node = hashtable_lookup(ust_app_ht, + (void *) ((unsigned long) lta->key.pid), sizeof(void *), &iter); + if (node == NULL) { + ERR("UST app pid %d not found in hash table", lta->key.pid); + } else { + ret = hashtable_del(ust_app_ht, &iter); + if (ret) { + ERR("UST app unable to delete app %d from hash table", + lta->key.pid); + } else { + DBG2("UST app pid %d deleted", lta->key.pid); + } + } + + /* Remove from key hash table */ + node = hashtable_lookup(ust_app_sock_key_map, + (void *) ((unsigned long) lta->key.sock), sizeof(void *), &iter); + if (node == NULL) { + ERR("UST app key %d not found in key hash table", lta->key.sock); + } else { + ret = hashtable_del(ust_app_sock_key_map, &iter); + if (ret) { + ERR("UST app unable to delete app sock %d from key hash table", + lta->key.sock); + } else { + DBG2("UST app pair sock %d key %d deleted", + lta->key.sock, lta->key.pid); + } + } + + free(lta); + + rcu_read_unlock(); +} + +/* + * URCU intermediate call to delete an UST app. + */ +static void delete_ust_app_rcu(struct rcu_head *head) +{ + struct cds_lfht_node *node = + caa_container_of(head, struct cds_lfht_node, head); + struct ust_app *app = + caa_container_of(node, struct ust_app, node); + + delete_ust_app(app); +} + +/* + * Find an ust_app using the sock and return it. + */ +static struct ust_app *find_app_by_sock(int sock) +{ + struct cds_lfht_node *node; + struct ust_app_key *key; + struct cds_lfht_iter iter; + + rcu_read_lock(); + + node = hashtable_lookup(ust_app_sock_key_map, + (void *)((unsigned long) sock), sizeof(void *), &iter); + if (node == NULL) { + DBG2("UST app find by sock %d key not found", sock); + rcu_read_unlock(); + goto error; + } + + key = caa_container_of(node, struct ust_app_key, node); + + node = hashtable_lookup(ust_app_ht, + (void *)((unsigned long) key->pid), sizeof(void *), &iter); + if (node == NULL) { + DBG2("UST app find by sock %d not found", sock); + rcu_read_unlock(); + goto error; + } + rcu_read_unlock(); + + return caa_container_of(node, struct ust_app, node); + +error: + return NULL; +} + +/* + * Return pointer to traceable apps list. + */ +struct cds_lfht *ust_app_get_ht(void) +{ + return ust_app_ht; +} + +/* + * Return ust app pointer or NULL if not found. + */ +struct ust_app *ust_app_find_by_pid(pid_t pid) +{ + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + rcu_read_lock(); + node = hashtable_lookup(ust_app_ht, + (void *)((unsigned long) pid), sizeof(void *), &iter); + if (node == NULL) { + rcu_read_unlock(); + 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, node); + +error: + return NULL; +} + +/* + * Using pid and uid (of the app), allocate a new ust_app struct and + * add it to the global traceable app list. + * + * On success, return 0, else return malloc ENOMEM. + */ +int ust_app_register(struct ust_register_msg *msg, int sock) +{ + struct ust_app *lta; + + lta = malloc(sizeof(struct ust_app)); + if (lta == NULL) { + PERROR("malloc"); + return -ENOMEM; + } + + lta->uid = msg->uid; + lta->gid = msg->gid; + lta->key.pid = msg->pid; + lta->ppid = msg->ppid; + lta->v_major = msg->major; + lta->v_minor = msg->minor; + lta->key.sock = sock; + strncpy(lta->name, msg->name, sizeof(lta->name)); + lta->name[16] = '\0'; + hashtable_node_init(<a->node, (void *)((unsigned long)lta->key.pid), + sizeof(void *)); + + /* Session hashtable */ + lta->sessions = hashtable_new(0); + + /* Set sock key map */ + hashtable_node_init(<a->key.node, (void *)((unsigned long)lta->key.sock), + sizeof(void *)); + + rcu_read_lock(); + hashtable_add_unique(ust_app_ht, <a->node); + hashtable_add_unique(ust_app_sock_key_map, <a->key.node); + rcu_read_unlock(); + + DBG("App registered with pid:%d ppid:%d uid:%d gid:%d sock:%d name:%s" + " (version %d.%d)", lta->key.pid, lta->ppid, lta->uid, lta->gid, + lta->key.sock, lta->name, lta->v_major, lta->v_minor); + + return 0; +} + +/* + * Unregister app by removing it from the global traceable app list and freeing + * the data struct. + * + * The socket is already closed at this point so no close to sock. + */ +void ust_app_unregister(int sock) +{ + struct ust_app *lta; + + DBG2("UST app unregistering sock %d", sock); + + lta = find_app_by_sock(sock); + if (lta) { + DBG("PID %d unregistering with sock %d", lta->key.pid, sock); + /* FIXME: Better use a call_rcu here ? */ + delete_ust_app(lta); + } +} + +/* + * Return traceable_app_count + */ +unsigned long ust_app_list_count(void) +{ + unsigned long count; + + rcu_read_lock(); + count = hashtable_get_count(ust_app_ht); + rcu_read_unlock(); + + return count; +} + +/* + * Free and clean all traceable apps of the global list. + */ +void ust_app_clean_list(void) +{ + int ret; + struct cds_lfht_node *node; + struct cds_lfht_iter iter; + + DBG2("UST app clean hash table"); + + rcu_read_lock(); + + hashtable_get_first(ust_app_ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + ret = hashtable_del(ust_app_ht, &iter); + if (!ret) { + call_rcu(&node->head, delete_ust_app_rcu); + } + hashtable_get_next(ust_app_ht, &iter); + } + + rcu_read_unlock(); +} + +/* + * Init UST app hash table. + */ +void ust_app_ht_alloc(void) +{ + ust_app_ht = hashtable_new(0); + ust_app_sock_key_map = hashtable_new(0); +} + +/* + * Alloc new UST app session. + */ +static struct ust_app_session *alloc_app_session(void) +{ + struct ust_app_session *ua_sess; + + ua_sess = zmalloc(sizeof(struct ust_app_session)); + if (ua_sess == NULL) { + PERROR("malloc"); + goto error; + } + + ua_sess->enabled = 0; + ua_sess->handle = -1; + ua_sess->channels = hashtable_new_str(0); + ua_sess->metadata = NULL; + ua_sess->obj = NULL; + + return ua_sess; + +error: + return NULL; +} + +static struct ust_app_channel *alloc_app_channel(char *name) +{ + struct ust_app_channel *ua_chan; + + ua_chan = zmalloc(sizeof(struct ust_app_channel)); + if (ua_chan == NULL) { + PERROR("malloc"); + goto error; + } + + strncpy(ua_chan->name, name, sizeof(ua_chan->name)); + ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; + ua_chan->enabled = 0; + ua_chan->handle = -1; + ua_chan->obj = NULL; + ua_chan->ctx = hashtable_new(0); + ua_chan->streams = hashtable_new(0); + ua_chan->events = hashtable_new_str(0); + hashtable_node_init(&ua_chan->node, (void *) ua_chan->name, + strlen(ua_chan->name)); + + DBG3("UST app channel %s allocated", ua_chan->name); + + return ua_chan; + +error: + return NULL; +} + +static struct ust_app_event *alloc_app_event(char *name) +{ + struct ust_app_event *ua_event; + + ua_event = zmalloc(sizeof(struct ust_app_event)); + if (ua_event == NULL) { + PERROR("malloc"); + goto error; + } + + strncpy(ua_event->name, name, sizeof(ua_event->name)); + ua_event->name[sizeof(ua_event->name) - 1] = '\0'; + ua_event->ctx = hashtable_new(0); + hashtable_node_init(&ua_event->node, (void *) ua_event->name, + strlen(ua_event->name)); + + DBG3("UST app event %s allocated", ua_event->name); + + return ua_event; + +error: + return NULL; +} + +static void shallow_copy_event(struct ust_app_event *ua_event, + struct ltt_ust_event *uevent) +{ + strncpy(ua_event->name, uevent->attr.name, sizeof(ua_event->name)); + ua_event->name[sizeof(ua_event->name) - 1] = '\0'; + + /* TODO: support copy context */ +} + +static void shallow_copy_channel(struct ust_app_channel *ua_chan, + struct ltt_ust_channel *uchan) +{ + struct cds_lfht_iter iter; + struct cds_lfht_node *node, *ua_event_node; + struct ltt_ust_event *uevent; + struct ust_app_event *ua_event; + + DBG2("Shallow copy of UST app channel %s", ua_chan->name); + + strncpy(ua_chan->name, uchan->name, sizeof(ua_chan->name)); + ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; + + /* TODO: support copy context */ + + hashtable_get_first(uchan->events, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + uevent = caa_container_of(node, struct ltt_ust_event, node); + + ua_event_node = hashtable_lookup(ua_chan->events, + (void *) uevent->attr.name, strlen(uevent->attr.name), &iter); + if (ua_event_node == NULL) { + DBG2("UST event %s not found on shallow copy channel", + uevent->attr.name); + ua_event = alloc_app_event(uevent->attr.name); + if (ua_event == NULL) { + continue; + } + hashtable_add_unique(ua_chan->events, &ua_event->node); + } else { + ua_event = caa_container_of(node, struct ust_app_event, node); + } + + shallow_copy_event(ua_event, uevent); + + /* Get next UST events */ + hashtable_get_next(uchan->events, &iter); + } + + DBG3("Shallow copy channel done"); +} + +static void shallow_copy_session(struct ust_app_session *ua_sess, + struct ltt_ust_session *usess) +{ + struct cds_lfht_node *node, *ua_chan_node; + struct cds_lfht_iter iter; + struct ltt_ust_channel *uchan; + struct ust_app_channel *ua_chan; + + DBG2("Shallow copy of session handle"); + + ua_sess->uid = usess->uid; + + /* TODO: support all UST domain */ + + /* Iterate over all channels in global domain. */ + hashtable_get_first(usess->domain_global.channels, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + uchan = caa_container_of(node, struct ltt_ust_channel, node); + + ua_chan_node = hashtable_lookup(ua_sess->channels, + (void *) uchan->name, strlen(uchan->name), &iter); + if (ua_chan_node == NULL) { + DBG2("Channel %s not found on shallow session copy, creating it", + uchan->name); + ua_chan = alloc_app_channel(uchan->name); + if (ua_chan == NULL) { + /* malloc failed... continuing */ + continue; + } + hashtable_add_unique(ua_sess->channels, &ua_chan->node); + } else { + ua_chan = caa_container_of(node, struct ust_app_channel, node); + } + + shallow_copy_channel(ua_chan, uchan); + + /* Next item in hash table */ + hashtable_get_next(usess->domain_global.channels, &iter); + } +} + +static struct ust_app_session *lookup_session_by_app( + struct ltt_ust_session *usess, struct ust_app *app) +{ + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + + /* Get right UST app session from app */ + node = hashtable_lookup(app->sessions, + (void *) ((unsigned long) usess->uid), + sizeof(void *), &iter); + if (node == NULL) { + goto error; + } + + return caa_container_of(node, struct ust_app_session, node); + +error: + return NULL; +} + +int ust_app_add_channel(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct cds_lfht_iter iter; + struct cds_lfht_node *node, *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + DBG2("UST app adding channel %s to global domain for session uid %d", + uchan->name, usess->uid); + + rcu_read_lock(); + hashtable_get_first(ust_app_ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + app = caa_container_of(node, struct ust_app, node); + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + DBG2("UST app pid: %d session uid %d not found, creating one", + app->key.pid, usess->uid); + ua_sess = alloc_app_session(); + if (ua_sess == NULL) { + /* Only malloc can failed so something is really wrong */ + goto next; + } + shallow_copy_session(ua_sess, usess); + } + + if (ua_sess->handle == -1) { + ret = ustctl_create_session(app->key.sock); + if (ret < 0) { + DBG("Error creating session for app pid %d, sock %d", + app->key.pid, app->key.sock); + /* TODO: free() ua_sess */ + goto next; + } + + DBG2("UST app ustctl create session handle %d", ret); + ua_sess->handle = ret; + + /* Add ust app session to app's HT */ + hashtable_node_init(&ua_sess->node, + (void *)((unsigned long) ua_sess->uid), sizeof(void *)); + hashtable_add_unique(app->sessions, &ua_sess->node); + } + + /* Lookup channel in the ust app session */ + ua_chan_node = hashtable_lookup(ua_sess->channels, + (void *) uchan->name, strlen(uchan->name), &iter); + if (ua_chan_node == NULL) { + ERR("Channel suppose to be present with the above shallow " + "session copy. Continuing..."); + goto next; + } + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + /* TODO: remove cast and use lttng-ust-abi.h */ + ret = ustctl_create_channel(app->key.sock, ua_sess->handle, + (struct lttng_ust_channel_attr *)&uchan->attr, &ua_chan->obj); + if (ret < 0) { + DBG("Error creating channel %s for app (pid: %d, sock: %d) " + "and session handle %d with ret %d", + uchan->name, app->key.pid, app->key.sock, + ua_sess->handle, ret); + goto next; + } + + ua_chan->handle = ua_chan->obj->handle; + ua_chan->attr.shm_fd = ua_chan->obj->shm_fd; + ua_chan->attr.wait_fd = ua_chan->obj->wait_fd; + ua_chan->attr.memory_map_size = ua_chan->obj->memory_map_size; + + DBG2("Channel %s UST create successfully for pid:%d and sock:%d", + uchan->name, app->key.pid, app->key.sock); + +next: + /* Next applications */ + hashtable_get_next(ust_app_ht, &iter); + } + rcu_read_unlock(); + + return ret; +} + +int ust_app_add_event(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct cds_lfht_iter iter; + struct cds_lfht_node *node, *ua_chan_node, *ua_event_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + struct ust_app_event *ua_event; + struct lttng_ust_event ltt_uevent; + struct lttng_ust_object_data *obj_event; + + DBG2("UST app adding event %s to global domain for session uid %d", + uevent->attr.name, usess->uid); + + rcu_read_lock(); + hashtable_get_first(ust_app_ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + app = caa_container_of(node, struct ust_app, node); + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + DBG2("UST app (pid: %d, sock: %d) session not found, creating one", + app->key.pid, app->key.sock); + ua_sess = alloc_app_session(); + if (ua_sess == NULL) { + /* Only malloc can failed so something is really wrong */ + goto next; + } + shallow_copy_session(ua_sess, usess); + } + + if (ua_sess->handle == -1) { + ret = ustctl_create_session(app->key.sock); + if (ret < 0) { + DBG("Error creating session for app pid %d, sock %d", + app->key.pid, app->key.sock); + /* TODO: free() ua_sess */ + goto next; + } + + DBG2("UST app ustctl create session handle %d", ret); + ua_sess->handle = ret; + /* Add ust app session to app's HT */ + hashtable_node_init(&ua_sess->node, + (void *)((unsigned long) ua_sess->uid), sizeof(void *)); + hashtable_add_unique(app->sessions, &ua_sess->node); + } + + /* Lookup channel in the ust app session */ + ua_chan_node = hashtable_lookup(ua_sess->channels, + (void *) uchan->name, strlen(uchan->name), &iter); + if (ua_chan_node == NULL) { + ERR("Channel suppose to be present with the above shallow " + "session copy. Continuing..."); + goto next; + } + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + /* Prepare lttng ust event */ + strncpy(ltt_uevent.name, uevent->attr.name, sizeof(ltt_uevent.name)); + ltt_uevent.name[sizeof(ltt_uevent.name) - 1] = '\0'; + /* TODO: adjust to other instrumentation types */ + ltt_uevent.instrumentation = LTTNG_UST_TRACEPOINT; + + /* Get event node */ + ua_event_node = hashtable_lookup(ua_chan->events, + (void *) uevent->attr.name, strlen(uevent->attr.name), &iter); + if (ua_event_node == NULL) { + DBG2("UST app event %s not found, creating one", uevent->attr.name); + /* Does not exist so create one */ + ua_event = alloc_app_event(uevent->attr.name); + if (ua_event == NULL) { + /* Only malloc can failed so something is really wrong */ + goto next; + } + + shallow_copy_event(ua_event, uevent); + + /* Create UST event on tracer */ + ret = ustctl_create_event(app->key.sock, <t_uevent, ua_chan->obj, + &obj_event); + if (ret < 0) { + ERR("Error ustctl create event %s for app pid: %d with ret %d", + uevent->attr.name, app->key.pid, ret); + /* TODO: free() ua_event and obj_event */ + goto next; + } + ua_event->obj = obj_event; + ua_event->handle = obj_event->handle; + ua_event->enabled = 1; + } else { + ua_event = caa_container_of(ua_event_node, + struct ust_app_event, node); + + if (ua_event->enabled == 0) { + ret = ustctl_enable(app->key.sock, ua_event->obj); + if (ret < 0) { + ERR("Error ustctl enable event %s for app " + "pid: %d with ret %d", uevent->attr.name, + app->key.pid, ret); + goto next; + } + ua_event->enabled = 1; + } + } + + hashtable_add_unique(ua_chan->events, &ua_event->node); + + DBG2("Event %s UST create successfully for pid:%d", uevent->attr.name, + app->key.pid); + +next: + /* Next applications */ + hashtable_get_next(ust_app_ht, &iter); + } + rcu_read_unlock(); + + return ret; +} + +int ust_app_start_trace(struct ltt_ust_session *usess) +{ + int ret = 0; + struct cds_lfht_iter iter; + struct cds_lfht_node *node, *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + struct lttng_ust_channel_attr uattr; + struct ltt_ust_channel *uchan; + + rcu_read_lock(); + hashtable_get_first(ust_app_ht, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + app = caa_container_of(node, struct ust_app, node); + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + /* Only malloc can failed so something is really wrong */ + goto next; + } + + if (ua_sess->metadata == NULL) { + /* Allocate UST metadata */ + ua_sess->metadata = trace_ust_create_metadata(usess->pathname); + if (ua_sess->metadata == NULL) { + ERR("UST app session %d creating metadata failed", + ua_sess->handle); + goto next; + } + + uattr.overwrite = ua_sess->metadata->attr.overwrite; + uattr.subbuf_size = ua_sess->metadata->attr.subbuf_size; + uattr.num_subbuf = ua_sess->metadata->attr.num_subbuf; + uattr.switch_timer_interval = + ua_sess->metadata->attr.switch_timer_interval; + uattr.read_timer_interval = + ua_sess->metadata->attr.read_timer_interval; + uattr.output = ua_sess->metadata->attr.output; + + /* UST tracer metadata creation */ + ret = ustctl_open_metadata(app->key.sock, ua_sess->handle, &uattr, + &ua_sess->metadata->obj); + if (ret < 0) { + ERR("UST app open metadata failed for app pid:%d", + app->key.pid); + goto next; + } + + DBG2("UST metadata opened for app pid %d", app->key.pid); + } + + /* Open UST metadata stream */ + if (ua_sess->metadata->stream_obj == NULL) { + ret = ustctl_create_stream(app->key.sock, ua_sess->metadata->obj, + &ua_sess->metadata->stream_obj); + if (ret < 0) { + ERR("UST create metadata stream failed"); + goto next; + } + + ret = snprintf(ua_sess->metadata->pathname, PATH_MAX, "%s/%s", + usess->pathname, "metadata"); + if (ret < 0) { + PERROR("asprintf UST create stream"); + goto next; + } + + DBG2("UST metadata stream object created for app pid %d", + app->key.pid); + } + + /* For each channel */ + hashtable_get_first(usess->domain_global.channels, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + uchan = caa_container_of(node, struct ltt_ust_channel, node); + + /* Lookup channel in the ust app session */ + ua_chan_node = hashtable_lookup(ua_sess->channels, + (void *) uchan->name, strlen(uchan->name), &iter); + if (ua_chan_node == NULL) { + ERR("Channel suppose to be present with the above shallow " + "session copy. Continuing..."); + goto next; + } + + ua_chan = caa_container_of(ua_chan_node, + struct ust_app_channel, node); + + struct ltt_ust_stream *ustream; + + ustream = malloc(sizeof(*ustream)); + if (ustream == NULL) { + goto next_chan; + } + + memset(ustream, 0, sizeof(struct ltt_ust_stream)); + + ret = ustctl_create_stream(app->key.sock, ua_chan->obj, + &ustream->obj); + if (ret < 0) { + ERR("Creating channel stream failed"); + goto next_chan; + } + + ustream->handle = ustream->obj->handle; + + hashtable_node_init(&ustream->node, + (void *)((unsigned long) ustream->handle), sizeof(void *)); + hashtable_add_unique(ua_chan->streams, &ustream->node); + + ret = snprintf(ustream->pathname, PATH_MAX, "%s/%s_%lu", + uchan->pathname, uchan->name, + hashtable_get_count(ua_chan->streams)); + if (ret < 0) { + PERROR("asprintf UST create stream"); + goto next_chan; + } + +next_chan: + /* Next applications */ + hashtable_get_next(ua_sess->channels, &iter); + } + + /* Setup UST consumer socket and send fds to it */ + printf("WTF HERE: sock: %d\n", usess->consumer_fd); + ret = ust_consumer_send_session(usess->consumer_fd, ua_sess); + if (ret < 0) { + goto next; + } + + /* This start the UST tracing */ + ret = ustctl_start_session(app->key.sock, ua_sess->handle); + if (ret < 0) { + ERR("Error starting tracing for app pid: %d", app->key.pid); + goto next; + } + + /* Quiescent wait after starting trace */ + ustctl_wait_quiescent(app->key.sock); +next: + /* Next applications */ + hashtable_get_next(ust_app_ht, &iter); + } + rcu_read_unlock(); + + return 0; +} diff --git a/lttng-sessiond/ust-app.h b/lttng-sessiond/ust-app.h new file mode 100644 index 000000000..46fb65427 --- /dev/null +++ b/lttng-sessiond/ust-app.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_UST_APP_H +#define _LTT_UST_APP_H + +#include +#include + +#include "trace-ust.h" + +/* + * Application registration data structure. + */ +struct ust_register_msg { + uint32_t major; + uint32_t minor; + pid_t pid; + pid_t ppid; + uid_t uid; + gid_t gid; + char name[16]; +}; + +/* + * Global applications HT used by the session daemon. + */ +struct cds_lfht *ust_app_ht; + +struct cds_lfht *ust_app_sock_key_map; + +struct ust_app_key { + pid_t pid; + int sock; + struct cds_lfht_node node; +}; + +struct ust_app_event { + int enabled; + int handle; + struct lttng_ust_object_data *obj; + char name[LTTNG_UST_SYM_NAME_LEN]; + struct cds_lfht *ctx; + struct cds_lfht_node node; +}; + +struct ust_app_channel { + int enabled; + int handle; + char name[LTTNG_UST_SYM_NAME_LEN]; + struct lttng_ust_channel attr; + struct lttng_ust_object_data *obj; + struct cds_lfht *streams; + struct cds_lfht *ctx; + struct cds_lfht *events; + struct cds_lfht_node node; +}; + +struct ust_app_session { + int enabled; + int handle; /* Used has unique identifier */ + unsigned int uid; + struct ltt_ust_metadata *metadata; + struct lttng_ust_object_data *obj; + struct cds_lfht *channels; /* Registered channels */ + struct cds_lfht_node node; +}; + +/* + * Registered traceable applications. Libust registers to the session daemon + * and a linked list is kept of all running traceable app. + */ +struct ust_app { + pid_t ppid; + uid_t uid; /* User ID that owns the apps */ + gid_t gid; /* Group ID that owns the apps */ + uint32_t v_major; /* Verion major number */ + uint32_t v_minor; /* Verion minor number */ + char name[17]; /* Process name (short) */ + struct cds_lfht *sessions; + struct cds_lfht_node node; + struct ust_app_key key; +}; + +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST + +int ust_app_register(struct ust_register_msg *msg, int sock); +void ust_app_unregister(int sock); +int ust_app_add_channel(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_add_event(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +unsigned long ust_app_list_count(void); +int ust_app_start_trace(struct ltt_ust_session *usess); + +void ust_app_clean_list(void); +void ust_app_ht_alloc(void); +struct cds_lfht *ust_app_get_ht(void); +struct ust_app *ust_app_find_by_pid(pid_t pid); + +#else + +static inline +int ust_app_register(struct ust_register_msg *msg, int sock) +{ + return -ENOSYS; +} +static inline +void ust_app_unregister(int sock) +{ +} +static inline +unsigned int ust_app_list_count(void) +{ + return 0; +} + +static inline +void ust_app_lock_list(void) +{ +} +static inline +void ust_app_unlock_list(void) +{ +} +static inline +void ust_app_clean_list(void) +{ +} +static inline +struct ust_app_list *ust_app_get_list(void) +{ + return NULL; +} +static inline +struct ust_app *ust_app_get_by_pid(pid_t pid) +{ + return NULL; +} + +static inline +int ust_app_add_channel(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} + +#endif /* CONFIG_LTTNG_TOOLS_HAVE_UST */ + +#endif /* _LTT_UST_APP_H */ diff --git a/lttng-sessiond/ust-consumer.c b/lttng-sessiond/ust-consumer.c new file mode 100644 index 000000000..b3b97552e --- /dev/null +++ b/lttng-sessiond/ust-consumer.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hashtable.h" +#include "ust-consumer.h" + +/* + * Send all stream fds of UST channel to the consumer. + */ +static int send_channel_streams(int sock, + struct ust_app_channel *uchan) +{ + int ret, fds[2]; + struct ltt_ust_stream *stream; + struct lttcomm_consumer_msg lum; + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + + DBG("Sending streams of channel %s to UST consumer", uchan->name); + + /* Send channel */ + lum.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; + + /* + * We need to keep shm_fd open to make sure this key stays unique within + * the session daemon. + */ + lum.u.channel.channel_key = uchan->obj->shm_fd; + lum.u.channel.max_sb_size = uchan->attr.subbuf_size; + lum.u.channel.mmap_len = uchan->obj->memory_map_size; + DBG("Sending channel %d to consumer", lum.u.channel.channel_key); + ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); + if (ret < 0) { + perror("send consumer channel"); + goto error; + } + fds[0] = uchan->obj->shm_fd; + fds[1] = uchan->obj->wait_fd; + ret = lttcomm_send_fds_unix_sock(sock, fds, 2); + if (ret < 0) { + perror("send consumer channel ancillary data"); + goto error; + } + + + rcu_read_lock(); + hashtable_get_first(uchan->streams, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + stream = caa_container_of(node, struct ltt_ust_stream, node); + + int fds[2]; + + if (!stream->obj->shm_fd) { + goto next; + } + + lum.cmd_type = LTTNG_CONSUMER_ADD_STREAM; + lum.u.stream.channel_key = uchan->obj->shm_fd; + lum.u.stream.stream_key = stream->obj->shm_fd; + lum.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; + lum.u.stream.output = uchan->attr.output; + lum.u.stream.mmap_len = stream->obj->memory_map_size; + strncpy(lum.u.stream.path_name, stream->pathname, PATH_MAX - 1); + lum.u.stream.path_name[PATH_MAX - 1] = '\0'; + DBG("Sending stream %d to consumer", lum.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); + if (ret < 0) { + perror("send consumer stream"); + goto error; + } + + fds[0] = stream->obj->shm_fd; + fds[1] = stream->obj->wait_fd; + ret = lttcomm_send_fds_unix_sock(sock, fds, 2); + if (ret < 0) { + perror("send consumer stream ancillary data"); + goto error; + } + +next: + hashtable_get_next(uchan->streams, &iter); + } + rcu_read_unlock(); + + DBG("consumer channel streams sent"); + + return 0; + +error: + return ret; +} + +/* + * Send all stream fds of the UST session to the consumer. + */ +int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess) +{ + int ret = 0; + int sock = consumer_fd; + struct cds_lfht_iter iter; + struct cds_lfht_node *node; + struct lttcomm_consumer_msg lum; + struct ust_app_channel *uchan; + + DBG("Sending metadata stream fd"); + + if (usess->metadata->obj->shm_fd != 0) { + int fds[2]; + + /* Send metadata channel fd */ + lum.cmd_type = LTTNG_CONSUMER_ADD_CHANNEL; + lum.u.channel.channel_key = usess->metadata->obj->shm_fd; + lum.u.channel.max_sb_size = usess->metadata->attr.subbuf_size; + lum.u.channel.mmap_len = 0; /* for kernel */ + DBG("Sending metadata channel %d to consumer", lum.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); + if (ret < 0) { + perror("send consumer channel"); + goto error; + } + fds[0] = usess->metadata->obj->shm_fd; + fds[1] = usess->metadata->obj->wait_fd; + ret = lttcomm_send_fds_unix_sock(sock, fds, 2); + if (ret < 0) { + perror("send consumer metadata channel"); + goto error; + } + + /* Send metadata stream fd */ + lum.cmd_type = LTTNG_CONSUMER_ADD_STREAM; + lum.u.stream.channel_key = usess->metadata->obj->shm_fd; + lum.u.stream.stream_key = usess->metadata->stream_obj->shm_fd; + lum.u.stream.state = LTTNG_CONSUMER_ACTIVE_STREAM; + lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT; + lum.u.stream.mmap_len = usess->metadata->stream_obj->memory_map_size; + strncpy(lum.u.stream.path_name, usess->metadata->pathname, PATH_MAX - 1); + lum.u.stream.path_name[PATH_MAX - 1] = '\0'; + DBG("Sending metadata stream %d to consumer", lum.u.stream.stream_key); + ret = lttcomm_send_unix_sock(sock, &lum, sizeof(lum)); + if (ret < 0) { + perror("send consumer metadata stream"); + goto error; + } + fds[0] = usess->metadata->stream_obj->shm_fd; + fds[1] = usess->metadata->stream_obj->wait_fd; + ret = lttcomm_send_fds_unix_sock(sock, fds, 2); + if (ret < 0) { + perror("send consumer stream"); + goto error; + } + } + + /* Send each channel fd streams of session */ + rcu_read_lock(); + hashtable_get_first(usess->channels, &iter); + while ((node = hashtable_iter_get_node(&iter)) != NULL) { + uchan = caa_container_of(node, struct ust_app_channel, node); + + ret = send_channel_streams(sock, uchan); + if (ret < 0) { + goto error; + } + hashtable_get_next(usess->channels, &iter); + } + rcu_read_unlock(); + + DBG("consumer fds (metadata and channel streams) sent"); + + return 0; + +error: + return ret; +} diff --git a/lttng-sessiond/ust-consumer.h b/lttng-sessiond/ust-consumer.h new file mode 100644 index 000000000..85169e6b8 --- /dev/null +++ b/lttng-sessiond/ust-consumer.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _UST_CONSUMER_H +#define _UST_CONSUMER_H + +#include "ust-app.h" + +int ust_consumer_send_session(int consumer_fd, struct ust_app_session *usess); + +#endif /* _UST_CONSUMER_H */ diff --git a/lttng-sessiond/ust-ctl.c b/lttng-sessiond/ust-ctl.c new file mode 100644 index 000000000..6f73bfc66 --- /dev/null +++ b/lttng-sessiond/ust-ctl.c @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; only version 2 + * of the License. + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include +#include + +#include "ust-comm.h" +#include "ust-ctl.h" +#include "../hashtable/hash.h" + +/* + * Init command for tracer with cmd type and correct handle. + */ +static void init_command(int cmd, int handle, struct lttcomm_ust_msg *command) +{ + memset(command, 0, sizeof(struct lttcomm_ust_msg)); + + command->cmd = cmd; + command->handle = handle; +} + +/* + * Generic send command to ust tracer. Caller must free reply. + */ +static struct lttcomm_ust_reply *send_command(int sock, + struct lttcomm_ust_msg *command) +{ + struct lttcomm_ust_reply *reply; + + reply = ustcomm_send_command(sock, command); + if (reply == NULL) { + goto error; + } + + if (reply->ret_code != LTTCOMM_OK) { + ERR("Return code (%d): %s", reply->ret_code, + lttcomm_get_readable_code(reply->ret_code)); + goto error; + } + + return reply; + +error: + return NULL; +} + +/* + * Send registration done packet to the application. + */ +int ustctl_register_done(int sock) +{ + struct lttcomm_ust_msg command; + struct lttcomm_ust_reply *reply; + + DBG("Sending register done command to %d", sock); + + command.cmd = LTTNG_UST_REGISTER_DONE; + command.handle = LTTNG_UST_ROOT_HANDLE; + + reply = ustcomm_send_command(sock, &command); + if (reply == NULL) { + goto error; + } + + if (reply->ret_code != LTTCOMM_OK) { + DBG("Return code: %s", lttcomm_get_readable_code(reply->ret_code)); + goto error; + } + + return 0; + +error: + return -1; +} + +/* + * Create an UST session on the tracer. + * + * Return handle if success else negative value. + */ +int ustctl_create_session(int sock, struct ltt_ust_session *session) +{ + int ret; + struct lttcomm_ust_msg command; + struct lttcomm_ust_reply *reply = NULL; + + command.cmd = LTTNG_UST_SESSION; + command.handle = LTTNG_UST_ROOT_HANDLE; + + reply = ustcomm_send_command(sock, &command); + if (reply == NULL) { + goto error; + } + + /* Save session handle */ + ret = reply->ret_val; + free(reply); + + DBG2("ustctl create session command successful with handle %d", ret); + + return ret; + +error: + free(reply); + return -1; +} + +/* + * Create UST channel to the tracer. + * + * Return handle if success else negative value. + */ +int ustctl_create_channel(int sock, struct ltt_ust_session *session, + struct lttng_ust_channel *channel) +{ + int ret; + struct lttcomm_ust_msg command; + struct lttcomm_ust_reply *reply = NULL; + + init_command(LTTNG_UST_CHANNEL, session->handle, &command); + /* Copy channel attributes to command */ + memcpy(&command.u.channel, channel, sizeof(command.u.channel)); + + /* Send command to tracer */ + reply = send_command(sock, &command); + if (reply == NULL) { + goto error; + } + + ret = reply->ret_val; + free(reply); + + return ret; + +error: + free(reply); + return -1; +} + +/* + * Enable UST channel. + */ +int ustctl_enable_channel(int sock, struct ltt_ust_session *session, + struct ltt_ust_channel *chan) +{ + struct lttcomm_ust_msg command; + struct lttcomm_ust_reply *reply = NULL; + + init_command(LTTNG_UST_ENABLE, chan->handle, &command); + + reply = ustcomm_send_command(sock, &command); + if (reply == NULL) { + goto error; + } + + if (reply->handle != chan->handle) { + ERR("Receive wrong handle from UST reply on enable channel"); + goto error; + } + + chan->enabled = 1; + free(reply); + + DBG2("ustctl enable channel successful for sock %d", sock); + return 0; + +error: + free(reply); + return -1; +} + +/* + * Disable UST channel. + */ +int ustctl_disable_channel(int sock, struct ltt_ust_session *session, + struct ltt_ust_channel *chan) +{ + struct lttcomm_ust_msg command; + struct lttcomm_ust_reply *reply = NULL; + + memset(&command, 0, sizeof(command)); + + command.cmd = LTTNG_UST_DISABLE; + command.handle = chan->handle; + + reply = ustcomm_send_command(sock, &command); + if (reply == NULL) { + goto error; + } + + if (reply->handle != chan->handle) { + ERR("Receive wrong handle from UST reply on enable channel"); + goto error; + } + + chan->enabled = 1; + free(reply); + + DBG2("ustctl disable channel successful for sock %d", sock); + return 0; + +error: + free(reply); + return -1; +} diff --git a/lttng-sessiond/ust-ctl.h b/lttng-sessiond/ust-ctl.h new file mode 100644 index 000000000..1fe29c877 --- /dev/null +++ b/lttng-sessiond/ust-ctl.h @@ -0,0 +1,41 @@ +/* + * ust-ctl.h + * + * Meta header used to include all relevant file from the lttng ust ABI. + * + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_UST_CTL_H +#define _LTT_UST_CTL_H + +#include + +/* + * FIXME: temporary workaround: we use a lttng-tools local version of + * lttng-ust-abi.h if UST is not found. Eventually, we should use our + * own internal structures within lttng-tools instead of relying on the + * UST ABI. + */ +#ifdef CONFIG_LTTNG_TOOLS_HAVE_UST +#include +#include +#else +#include "lttng-ust-ctl.h" +#include "lttng-ust-abi.h" +#endif + +#endif /* _LTT_UST_CTL_H */ diff --git a/lttng-sessiond/utils.c b/lttng-sessiond/utils.c new file mode 100644 index 000000000..0da564210 --- /dev/null +++ b/lttng-sessiond/utils.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "utils.h" + +/* + * Write to writable pipe used to notify a thread. + */ +int notify_thread_pipe(int wpipe) +{ + int ret; + + ret = write(wpipe, "!", 1); + if (ret < 0) { + PERROR("write poll pipe"); + } + + return ret; +} + +/* + * Return pointer to home directory path using the env variable HOME. + * + * No home, NULL is returned. + */ +const char *get_home_dir(void) +{ + return ((const char *) getenv("HOME")); +} + +/* + * Create recursively directory using the FULL path. + */ +int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + int ret; + char *p, tmp[PATH_MAX]; + size_t len; + mode_t old_umask; + + ret = snprintf(tmp, sizeof(tmp), "%s", path); + if (ret < 0) { + PERROR("snprintf mkdir"); + goto error; + } + + len = ret; + if (tmp[len - 1] == '/') { + tmp[len - 1] = 0; + } + + old_umask = umask(0); + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + ret = mkdir(tmp, mode); + if (ret < 0) { + if (!(errno == EEXIST)) { + PERROR("mkdir recursive"); + ret = -errno; + goto umask_error; + } + } else if (ret == 0) { + /* + * We created the directory. Set its ownership to the + * user/group specified. + */ + ret = chown(tmp, uid, gid); + if (ret < 0) { + PERROR("chown in mkdir recursive"); + ret = -errno; + goto umask_error; + } + } + *p = '/'; + } + } + + ret = mkdir(tmp, mode); + if (ret < 0) { + if (!(errno == EEXIST)) { + PERROR("mkdir recursive last piece"); + ret = -errno; + } else { + ret = 0; + } + } else if (ret == 0) { + /* + * We created the directory. Set its ownership to the user/group + * specified. + */ + ret = chown(tmp, uid, gid); + if (ret < 0) { + PERROR("chown in mkdir recursive"); + ret = -errno; + goto umask_error; + } + } + +umask_error: + umask(old_umask); +error: + return ret; +} diff --git a/lttng-sessiond/utils.h b/lttng-sessiond/utils.h new file mode 100644 index 000000000..449c3ef6b --- /dev/null +++ b/lttng-sessiond/utils.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 - David Goulet + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * 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., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_UTILS_H +#define _LTT_UTILS_H + +#include + +int mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid); +const char *get_home_dir(void); +int notify_thread_pipe(int wpipe); + +#endif /* _LTT_UTILS_H */ diff --git a/lttng/lttng.c b/lttng/lttng.c index e76136669..e488cc566 100644 --- a/lttng/lttng.c +++ b/lttng/lttng.c @@ -287,7 +287,7 @@ static int spawn_sessiond(char *pathname) * Spawn session daemon and tell * it to signal us when ready. */ - execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL); + execlp(pathname, "lttng-sessiond", "--sig-parent", "--quiet", NULL); /* execlp only returns if error happened */ if (errno == ENOENT) { ERR("No session daemon found. Use --sessiond-path."); @@ -297,7 +297,7 @@ static int spawn_sessiond(char *pathname) kill(getppid(), SIGTERM); /* unpause parent */ exit(EXIT_FAILURE); } else if (pid > 0) { - /* Wait for ltt-sessiond to start */ + /* Wait for lttng-sessiond to start */ pause(); goto end; } else { @@ -339,7 +339,7 @@ static int check_sessiond(void) /* Let's rock and roll */ if (pathname == NULL) { - ret = asprintf(&alloc_pathname, INSTALL_BIN_PATH "/ltt-sessiond"); + ret = asprintf(&alloc_pathname, INSTALL_BIN_PATH "/lttng-sessiond"); if (ret < 0) { perror("asprintf spawn sessiond"); goto end; diff --git a/tests/Makefile.am b/tests/Makefile.am index e0b369730..f64ca5291 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -7,9 +7,9 @@ noinst_PROGRAMS = test_sessions test_kernel_data_trace kernel_all_events_basic \ kernel_event_basic UTILS=utils.h -SESSIONS=$(top_srcdir)/ltt-sessiond/session.c $(top_srcdir)/ltt-sessiond/hashtable.c \ +SESSIONS=$(top_srcdir)/lttng-sessiond/session.c $(top_srcdir)/lttng-sessiond/hashtable.c \ $(top_srcdir)/hashtable/rculfhash.c $(top_srcdir)/hashtable/hash.c -KERN_DATA_TRACE=$(top_srcdir)/ltt-sessiond/trace-kernel.c +KERN_DATA_TRACE=$(top_srcdir)/lttng-sessiond/trace-kernel.c LIBLTTNG=$(top_srcdir)/liblttngctl/lttngctl.c \ $(top_srcdir)/liblttng-sessiond-comm/lttng-sessiond-comm.c diff --git a/tests/lttng/run-kernel-tests.sh b/tests/lttng/run-kernel-tests.sh index 28ad6281b..04814fc9c 100755 --- a/tests/lttng/run-kernel-tests.sh +++ b/tests/lttng/run-kernel-tests.sh @@ -1,6 +1,6 @@ #!/bin/bash -SESSIOND_BIN="ltt-sessiond" +SESSIOND_BIN="lttng-sessiond" tmpdir=`mktemp -d` tests=( kernel_event_basic kernel_all_events_basic ) @@ -42,7 +42,7 @@ check_lttng_modules if [ -z $(pidof $SESSIOND_BIN) ]; then echo -n "Starting session daemon... " - ../ltt-sessiond/$SESSIOND_BIN --daemonize --quiet + ../lttng-sessiond/$SESSIOND_BIN --daemonize --quiet if [ $? -eq 1 ]; then echo -e '\e[1;31mFAILED\e[0m' rm -rf $tmpdir diff --git a/tests/test_kernel_data_trace.c b/tests/test_kernel_data_trace.c index 548714e94..05fac0949 100644 --- a/tests/test_kernel_data_trace.c +++ b/tests/test_kernel_data_trace.c @@ -25,7 +25,7 @@ #include #include -#include "ltt-sessiond/trace-kernel.h" +#include "lttng-sessiond/trace-kernel.h" #include "utils.h" /* This path will NEVER be created in this test */ diff --git a/tests/test_sessions.c b/tests/test_sessions.c index 461a33c1e..3e45c21b6 100644 --- a/tests/test_sessions.c +++ b/tests/test_sessions.c @@ -27,7 +27,7 @@ #include -#include +#include #include "utils.h" #define SESSION1 "test1"