From: David Goulet Date: Fri, 20 Jan 2012 18:17:16 +0000 (-0500) Subject: Complete change of the source directory tree X-Git-Tag: v2.0-pre18~19 X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=commitdiff_plain;h=10a8a2237343699e3923d87e24dbf2d7fe225377 Complete change of the source directory tree For the sake of clearness and awesomeness :P, this change was needed for better semantic across the code and better organization of source files. src/ --> Contains source files of the project bin/ --> Contains every binaries this git tree builds (lttng, lttng-sessiond and lttng-consumerd). lib/ --> Contains exported libraries (lttng-ctl). common/ --> Contains shared code for the entire project. (internal libraries mostly) Every internal headers are moved from include/ to src/common. The include directory only contains exported public headers now (lttng/lttng.h). The -I$(top_srcdir)/src is also added for all Makefiles. The consumerd APIs are not exported anymore. The reason is that the consumer will get completely re-engineered in the next months. It's our safety net for the APIs breakage of the coming year concerning the consumerd library. Renaming is done also on some internal libraries to follow the naming convention. The public library liblttngctl is renamed to liblttng-ctl making it linkable now with "-llttng-ctl" deprecating the old "-llttngctl". No code is changed with this commit except #include statements added/removed/renamed. This is why there is yet files and internal API calls that does not follow the naming convention (Ex: lttngerr.h, lttng-share.h in src/common). Signed-off-by: David Goulet --- diff --git a/.gitignore b/.gitignore index afa850578..0eacb03bb 100644 --- a/.gitignore +++ b/.gitignore @@ -30,10 +30,9 @@ config/ !config/epoll.m4 !config/config_feature.m4 -lttng-sessiond/lttng-sessiond -lttng/lttng -lttng-kconsumerd/lttng-kconsumerd -lttng-consumerd/lttng-consumerd +src/bin/lttng-sessiond/lttng-sessiond +src/bin/lttng/lttng +src/bin/lttng-consumerd/lttng-consumerd tests/test_sessions tests/test_kernel_data_trace diff --git a/Makefile.am b/Makefile.am index 3b6344fa6..7184340a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,20 +1,6 @@ ACLOCAL_AMFLAGS = -I config -SUBDIRS = common \ - liblttng-sessiond-comm \ - libkernelctl \ - liblttng-ht \ - liblttng-kconsumer \ - liblttng-ustconsumer \ - liblttng-consumer \ - lttng-consumerd - -if ! BUILD_CONSUMERD_ONLY -SUBDIRS += liblttngctl \ - lttng \ - lttng-sessiond -endif - -SUBDIRS += tests \ - include \ - doc +SUBDIRS = src \ + tests \ + include \ + doc diff --git a/common/Makefile.am b/common/Makefile.am deleted file mode 100644 index 1c6eb9b1d..000000000 --- a/common/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -AM_CFLAGS = -fno-strict-aliasing - -noinst_LTLIBRARIES = libcommon.la - -libcommon_la_SOURCES = runas.c runas.h diff --git a/common/runas.c b/common/runas.c deleted file mode 100644 index 8ec7dc6b3..000000000 --- a/common/runas.c +++ /dev/null @@ -1,313 +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 "runas.h" - -#define RUNAS_CHILD_STACK_SIZE 10485760 - -struct run_as_data { - int (*cmd)(void *data); - void *data; - uid_t uid; - gid_t gid; - int retval_pipe; -}; - -struct run_as_mkdir_data { - const char *path; - mode_t mode; -}; - -struct run_as_open_data { - const char *path; - int flags; - mode_t mode; -}; - -/* - * Create recursively directory using the FULL path. - */ -static -int _mkdir_recursive(void *_data) -{ - struct run_as_mkdir_data *data = _data; - const char *path; - char *p, tmp[PATH_MAX]; - struct stat statbuf; - mode_t mode; - size_t len; - int ret; - - path = data->path; - mode = data->mode; - - 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; - } - - for (p = tmp + 1; *p; p++) { - if (*p == '/') { - *p = 0; - ret = stat(tmp, &statbuf); - if (ret < 0) { - ret = mkdir(tmp, mode); - if (ret < 0) { - if (!(errno == EEXIST)) { - PERROR("mkdir recursive"); - ret = -errno; - goto error; - } - } - } - *p = '/'; - } - } - - ret = mkdir(tmp, mode); - if (ret < 0) { - if (!(errno == EEXIST)) { - PERROR("mkdir recursive last piece"); - ret = -errno; - } else { - ret = 0; - } - } - -error: - return ret; -} - -static -int _mkdir(void *_data) -{ - struct run_as_mkdir_data *data = _data; - return mkdir(data->path, data->mode); -} - -static -int _open(void *_data) -{ - struct run_as_open_data *data = _data; - return open(data->path, data->flags, data->mode); -} - -static -int child_run_as(void *_data) -{ - struct run_as_data *data = _data; - size_t writelen, writeleft, index; - union { - int i; - char c[sizeof(int)]; - } sendret; - int ret; - - /* - * Child: it is safe to drop egid and euid while sharing the - * file descriptors with the parent process, since we do not - * drop "uid": therefore, the user we are dropping egid/euid to - * cannot attach to this process with, e.g. ptrace, nor map this - * process memory. - */ - if (data->gid != getegid()) { - ret = setegid(data->gid); - if (ret < 0) { - perror("setegid"); - return EXIT_FAILURE; - } - } - if (data->uid != geteuid()) { - ret = seteuid(data->uid); - if (ret < 0) { - perror("seteuid"); - return EXIT_FAILURE; - } - } - /* - * Also set umask to 0 for mkdir executable bit. - */ - umask(0); - sendret.i = (*data->cmd)(data->data); - /* send back return value */ - writeleft = sizeof(sendret); - index = 0; - do { - writelen = write(data->retval_pipe, &sendret.c[index], - writeleft); - if (writelen < 0) { - perror("write"); - return EXIT_FAILURE; - } - writeleft -= writelen; - index += writelen; - } while (writeleft > 0); - return EXIT_SUCCESS; -} - -static -int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid) -{ - struct run_as_data run_as_data; - int ret = 0; - int status; - pid_t pid; - int retval_pipe[2]; - ssize_t readlen, readleft, index; - void *child_stack; - union { - int i; - char c[sizeof(int)]; - } retval; - - /* - * If we are non-root, we can only deal with our own uid. - */ - if (geteuid() != 0) { - if (uid != geteuid()) { - ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", - uid, geteuid()); - return -EPERM; - } - } - - ret = pipe(retval_pipe); - if (ret < 0) { - perror("pipe"); - goto end; - } - run_as_data.data = data; - run_as_data.cmd = cmd; - run_as_data.uid = uid; - run_as_data.gid = gid; - run_as_data.retval_pipe = retval_pipe[1]; /* write end */ - child_stack = mmap(NULL, RUNAS_CHILD_STACK_SIZE, - PROT_WRITE | PROT_READ, - MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS | MAP_STACK, - -1, 0); - if (child_stack == MAP_FAILED) { - perror("mmap"); - ret = -ENOMEM; - goto close_pipe; - } - /* - * Pointing to the middle of the stack to support architectures - * where the stack grows up (HPPA). - */ - pid = clone(child_run_as, child_stack + (RUNAS_CHILD_STACK_SIZE / 2), - CLONE_FILES | SIGCHLD, - &run_as_data, NULL); - if (pid < 0) { - perror("clone"); - ret = pid; - goto unmap_stack; - } - /* receive return value */ - readleft = sizeof(retval); - index = 0; - do { - readlen = read(retval_pipe[0], &retval.c[index], readleft); - if (readlen < 0) { - perror("read"); - ret = -1; - break; - } - readleft -= readlen; - index += readlen; - } while (readleft > 0); - - /* - * Parent: wait for child to return, in which case the - * shared memory map will have been created. - */ - pid = waitpid(pid, &status, 0); - if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { - perror("wait"); - ret = -1; - } -unmap_stack: - ret = munmap(child_stack, RUNAS_CHILD_STACK_SIZE); - if (ret < 0) { - perror("munmap"); - } -close_pipe: - close(retval_pipe[0]); - close(retval_pipe[1]); -end: - return retval.i; -} - -int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - struct run_as_mkdir_data data; - - DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d", - path, mode, uid, gid); - data.path = path; - data.mode = mode; - return run_as(_mkdir_recursive, &data, uid, gid); -} - -int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) -{ - struct run_as_mkdir_data data; - - DBG3("mkdir() %s with mode %d for uid %d and gid %d", - path, mode, uid, gid); - data.path = path; - data.mode = mode; - return run_as(_mkdir, &data, uid, gid); -} - -/* - * Note: open_run_as is currently not working. We'd need to pass the fd - * opened in the child to the parent. - */ -int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid) -{ - struct run_as_open_data data; - - DBG3("open() %s with flags %X mode %d for uid %d and gid %d", - path, flags, mode, uid, gid); - data.path = path; - data.flags = flags; - data.mode = mode; - return run_as(_open, &data, uid, gid); -} diff --git a/common/runas.h b/common/runas.h deleted file mode 100644 index c8c485c1f..000000000 --- a/common/runas.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef _RUNAS_H -#define _RUNAS_H - -/* - * 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 verion 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 - -int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid); -int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid); -int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid); - -#endif /* _RUNAS_H */ diff --git a/configure.ac b/configure.ac index f98c874a6..072cc8516 100644 --- a/configure.ac +++ b/configure.ac @@ -122,7 +122,7 @@ LT_INIT CFLAGS="-Wall $CFLAGS -g -fno-strict-aliasing" -DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_builddir)" +DEFAULT_INCLUDES="-I\$(top_srcdir) -I\$(top_builddir) -I\$(top_builddir)/src -I\$(top_builddir)/include" lttngincludedir="${includedir}/lttng" @@ -131,22 +131,24 @@ AC_SUBST(DEFAULT_INCLUDES) AC_CONFIG_FILES([ Makefile + doc/Makefile include/Makefile - common/Makefile - libkernelctl/Makefile - liblttng-consumer/Makefile - liblttng-kconsumer/Makefile - liblttng-ustconsumer/Makefile - liblttngctl/Makefile - liblttng-sessiond-comm/Makefile - liblttng-ht/Makefile - lttng-consumerd/Makefile - lttng-sessiond/Makefile - lttng/Makefile + src/Makefile + src/common/Makefile + src/common/kernel-ctl/Makefile + src/common/kernel-consumer/Makefile + src/common/ust-consumer/Makefile + src/common/hashtable/Makefile + src/common/sessiond-comm/Makefile + src/lib/Makefile + src/lib/lttng-ctl/Makefile + src/bin/Makefile + src/bin/lttng-consumerd/Makefile + src/bin/lttng-sessiond/Makefile + src/bin/lttng/Makefile tests/Makefile tests/ust-nevents/Makefile tests/ust-nprocesses/Makefile - doc/Makefile ]) AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am index fd212f22f..bf9187f8a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1 @@ -lttnginclude_HEADERS = lttng/lttng.h lttng/lttng-kconsumer.h \ - lttng/lttng-ustconsumer.h lttng/lttng-consumer.h - -noinst_HEADERS = lttngerr.h lttng-kernel.h lttng-consumerd.h lttng-share.h \ - lttng-sessiond-comm.h lttng-kernel-ctl.h lttng-ht.h +lttnginclude_HEADERS = lttng/lttng.h diff --git a/include/lttng-consumerd.h b/include/lttng-consumerd.h deleted file mode 100644 index e42b2fea5..000000000 --- a/include/lttng-consumerd.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * 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 verion 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_CONSUMERD_H -#define _LTTNG_CONSUMERD_H - -#define CONSUMERD_RUNDIR "%s" - -/* Kernel consumer path */ -#define KCONSUMERD_PATH CONSUMERD_RUNDIR "/kconsumerd" -#define KCONSUMERD_CMD_SOCK_PATH KCONSUMERD_PATH "/command" -#define KCONSUMERD_ERR_SOCK_PATH KCONSUMERD_PATH "/error" - -/* UST 64-bit consumer path */ -#define USTCONSUMERD64_PATH CONSUMERD_RUNDIR "/ustconsumerd64" -#define USTCONSUMERD64_CMD_SOCK_PATH USTCONSUMERD64_PATH "/command" -#define USTCONSUMERD64_ERR_SOCK_PATH USTCONSUMERD64_PATH "/error" - -/* UST 32-bit consumer path */ -#define USTCONSUMERD32_PATH CONSUMERD_RUNDIR "/ustconsumerd32" -#define USTCONSUMERD32_CMD_SOCK_PATH USTCONSUMERD32_PATH "/command" -#define USTCONSUMERD32_ERR_SOCK_PATH USTCONSUMERD32_PATH "/error" - -#endif /* _LTTNG_CONSUMERD_H */ diff --git a/include/lttng-ht.h b/include/lttng-ht.h deleted file mode 100644 index 649ffcf54..000000000 --- a/include/lttng-ht.h +++ /dev/null @@ -1,88 +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_HT_H -#define _LTT_HT_H - -#include -#include "../liblttng-ht/rculfhash.h" -#include "../liblttng-ht/rculfhash-internal.h" - -typedef unsigned long (*hash_fct)(void *_key, unsigned long seed); -typedef cds_lfht_match_fct hash_match_fct; - -enum lttng_ht_type { - LTTNG_HT_TYPE_STRING, - LTTNG_HT_TYPE_ULONG, -}; - -struct lttng_ht { - struct cds_lfht *ht; - cds_lfht_match_fct match_fct; - hash_fct hash_fct; -}; - -struct lttng_ht_iter { - struct cds_lfht_iter iter; -}; - -struct lttng_ht_node_str { - char *key; - struct cds_lfht_node node; - struct rcu_head head; -}; - -struct lttng_ht_node_ulong { - unsigned long key; - struct cds_lfht_node node; - struct rcu_head head; -}; - -/* Hashtable new and destroy */ -extern struct lttng_ht *lttng_ht_new(unsigned long size, int type); -extern void lttng_ht_destroy(struct lttng_ht *ht); - -/* Specialized node init and free functions */ -extern void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key); -extern void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node, - unsigned long key); -extern void lttng_ht_node_free_str(struct lttng_ht_node_str *node); -extern void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node); - -extern void lttng_ht_lookup(struct lttng_ht *ht, void *key, - struct lttng_ht_iter *iter); - -/* Specialized add unique functions */ -extern void lttng_ht_add_unique_str(struct lttng_ht *ht, - struct lttng_ht_node_str *node); -extern void lttng_ht_add_unique_ulong(struct lttng_ht *ht, - struct lttng_ht_node_ulong *node); - -extern int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter); - -extern void lttng_ht_get_first(struct lttng_ht *ht, - struct lttng_ht_iter *iter); -extern void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter); - -extern unsigned long lttng_ht_get_count(struct lttng_ht *ht); - -extern struct lttng_ht_node_str *lttng_ht_iter_get_node_str( - struct lttng_ht_iter *iter); -extern struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong( - struct lttng_ht_iter *iter); - -#endif /* _LTT_HT_H */ diff --git a/include/lttng-kernel-ctl.h b/include/lttng-kernel-ctl.h deleted file mode 100644 index fa307e446..000000000 --- a/include/lttng-kernel-ctl.h +++ /dev/null @@ -1,70 +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_KERNEL_CTL_H -#define _LTTNG_KERNEL_CTL_H - -#include -#include - -int kernctl_create_session(int fd); -int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops); -int kernctl_create_channel(int fd, struct lttng_channel_attr *chops); -int kernctl_create_stream(int fd); -int kernctl_create_event(int fd, struct lttng_kernel_event *ev); -int kernctl_add_context(int fd, struct lttng_kernel_context *ctx); - -int kernctl_enable(int fd); -int kernctl_disable(int fd); -int kernctl_start_session(int fd); -int kernctl_stop_session(int fd); - -int kernctl_tracepoint_list(int fd); -int kernctl_tracer_version(int fd, struct lttng_kernel_tracer_version *v); -int kernctl_wait_quiescent(int fd); -int kernctl_calibrate(int fd, struct lttng_kernel_calibrate *calibrate); - - -/* Buffer operations */ - -/* For mmap mode, readable without "get" operation */ -int kernctl_get_mmap_len(int fd, unsigned long *len); -int kernctl_get_max_subbuf_size(int fd, unsigned long *len); - -/* - * For mmap mode, operate on the current packet (between get/put or - * get_next/put_next). - */ -int kernctl_get_mmap_read_offset(int fd, unsigned long *len); -int kernctl_get_subbuf_size(int fd, unsigned long *len); -int kernctl_get_padded_subbuf_size(int fd, unsigned long *len); - -int kernctl_get_next_subbuf(int fd); -int kernctl_put_next_subbuf(int fd); - -/* snapshot */ -int kernctl_snapshot(int fd); -int kernctl_snapshot_get_consumed(int fd, unsigned long *pos); -int kernctl_snapshot_get_produced(int fd, unsigned long *pos); -int kernctl_get_subbuf(int fd, unsigned long *pos); -int kernctl_put_subbuf(int fd); - -int kernctl_buffer_flush(int fd); - -#endif /* _LTTNG_KERNEL_CTL_H */ diff --git a/include/lttng-kernel.h b/include/lttng-kernel.h deleted file mode 100644 index 455b1c460..000000000 --- a/include/lttng-kernel.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * Mathieu Desnoyers - * 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 _LTTNG_KERNEL_H -#define _LTTNG_KERNEL_H - -#include - -#include - -#define LTTNG_SYM_NAME_LEN 256 - -/* - * LTTng DebugFS ABI structures. - * - * This is the kernel ABI copied from lttng-modules tree. - */ - -enum lttng_kernel_instrumentation { - LTTNG_KERNEL_ALL = -1, /* Used within lttng-tools */ - LTTNG_KERNEL_TRACEPOINT = 0, - LTTNG_KERNEL_KPROBE = 1, - LTTNG_KERNEL_FUNCTION = 2, - LTTNG_KERNEL_KRETPROBE = 3, - LTTNG_KERNEL_NOOP = 4, /* not hooked */ - LTTNG_KERNEL_SYSCALL = 5, -}; - -enum lttng_kernel_context_type { - LTTNG_KERNEL_CONTEXT_PID = 0, - LTTNG_KERNEL_CONTEXT_PERF_COUNTER = 1, - LTTNG_KERNEL_CONTEXT_COMM = 2, - LTTNG_KERNEL_CONTEXT_PRIO = 3, - LTTNG_KERNEL_CONTEXT_NICE = 4, - LTTNG_KERNEL_CONTEXT_VPID = 5, - LTTNG_KERNEL_CONTEXT_TID = 6, - LTTNG_KERNEL_CONTEXT_VTID = 7, - LTTNG_KERNEL_CONTEXT_PPID = 8, - LTTNG_KERNEL_CONTEXT_VPPID = 9, -}; - -/* Perf counter attributes */ -struct lttng_kernel_perf_counter_ctx { - uint32_t type; - uint64_t config; - char name[LTTNG_SYM_NAME_LEN]; -}; - -/* Event/Channel context */ -struct lttng_kernel_context { - enum lttng_kernel_context_type ctx; - union { - struct lttng_kernel_perf_counter_ctx perf_counter; - } u; -}; - -struct lttng_kernel_kretprobe { - uint64_t addr; - - uint64_t offset; - char symbol_name[LTTNG_SYM_NAME_LEN]; -}; - -/* - * Either addr is used, or symbol_name and offset. - */ -struct lttng_kernel_kprobe { - uint64_t addr; - - uint64_t offset; - char symbol_name[LTTNG_SYM_NAME_LEN]; -}; - -/* Function tracer */ -struct lttng_kernel_function { - char symbol_name[LTTNG_SYM_NAME_LEN]; -}; - -struct lttng_kernel_event { - char name[LTTNG_SYM_NAME_LEN]; - enum lttng_kernel_instrumentation instrumentation; - /* Per instrumentation type configuration */ - union { - struct lttng_kernel_kretprobe kretprobe; - struct lttng_kernel_kprobe kprobe; - struct lttng_kernel_function ftrace; - } u; -}; - -struct lttng_kernel_tracer_version { - uint32_t version; - uint32_t patchlevel; - uint32_t sublevel; -}; - -enum lttng_kernel_calibrate_type { - LTTNG_KERNEL_CALIBRATE_KRETPROBE, -}; - -struct lttng_kernel_calibrate { - enum lttng_kernel_calibrate_type type; /* type (input) */ -}; - -#endif /* _LTTNG_KERNEL_H */ diff --git a/include/lttng-sessiond-comm.h b/include/lttng-sessiond-comm.h deleted file mode 100644 index 48cf93321..000000000 --- a/include/lttng-sessiond-comm.h +++ /dev/null @@ -1,303 +0,0 @@ -#ifndef _LTTNG_SESSIOND_COMM_H -#define _LTTNG_SESSIOND_COMM_H - -/* - * Copyright (C) 2011 - David Goulet - * 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. - */ - -/* - * This header is meant for liblttng and libust internal use ONLY. - * These declarations should NOT be considered stable API. - */ - -#define _GNU_SOURCE -#include -#include -#include - -#define LTTNG_RUNDIR "/var/run/lttng" -#define LTTNG_HOME_RUNDIR "%s/.lttng" - -/* Default unix socket path */ -#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 LTTNG_HOME_RUNDIR "/apps-lttng-sessiond" -#define DEFAULT_HOME_CLIENT_UNIX_SOCK LTTNG_HOME_RUNDIR "/client-lttng-sessiond" - -/* Queue size of listen(2) */ -#define LTTNG_SESSIOND_COMM_MAX_LISTEN 64 - -/* - * Get the error code index from 0 since LTTCOMM_OK start at 1000 - */ -#define LTTCOMM_ERR_INDEX(code) (code - LTTCOMM_OK) - -enum lttcomm_sessiond_command { - /* Tracer command */ - LTTNG_ADD_CONTEXT, - LTTNG_CALIBRATE, - LTTNG_DISABLE_CHANNEL, - LTTNG_DISABLE_EVENT, - LTTNG_DISABLE_ALL_EVENT, - LTTNG_ENABLE_CHANNEL, - LTTNG_ENABLE_EVENT, - LTTNG_ENABLE_ALL_EVENT, - /* Session daemon command */ - LTTNG_CREATE_SESSION, - LTTNG_DESTROY_SESSION, - LTTNG_LIST_CHANNELS, - LTTNG_LIST_DOMAINS, - LTTNG_LIST_EVENTS, - LTTNG_LIST_SESSIONS, - LTTNG_LIST_TRACEPOINTS, - LTTNG_REGISTER_CONSUMER, - LTTNG_START_TRACE, - LTTNG_STOP_TRACE, -}; - -/* - * lttcomm error code. - */ -enum lttcomm_return_code { - LTTCOMM_OK = 1000, /* Ok */ - LTTCOMM_ERR, /* Unknown Error */ - LTTCOMM_UND, /* Undefine command */ - LTTCOMM_NOT_IMPLEMENTED, /* Command not implemented */ - LTTCOMM_UNKNOWN_DOMAIN, /* Tracing domain not known */ - LTTCOMM_ALLOC_FAIL, /* Trace allocation fail */ - LTTCOMM_NO_SESSION, /* No session found */ - LTTCOMM_CREATE_FAIL, /* Create trace fail */ - LTTCOMM_SESSION_FAIL, /* Create session fail */ - LTTCOMM_START_FAIL, /* Start tracing fail */ - LTTCOMM_STOP_FAIL, /* Stop tracing fail */ - LTTCOMM_LIST_FAIL, /* Listing apps fail */ - LTTCOMM_NO_APPS, /* No traceable application */ - LTTCOMM_SESS_NOT_FOUND, /* Session name not found */ - LTTCOMM_NO_TRACE, /* No trace exist */ - LTTCOMM_FATAL, /* Session daemon had a fatal error */ - LTTCOMM_NO_TRACEABLE, /* Error for non traceable app */ - LTTCOMM_SELECT_SESS, /* Must select a session */ - LTTCOMM_EXIST_SESS, /* Session name already exist */ - LTTCOMM_NO_EVENT, /* No event found */ - LTTCOMM_CONNECT_FAIL, /* Unable to connect to unix socket */ - LTTCOMM_APP_NOT_FOUND, /* App not found in traceable app list */ - LTTCOMM_EPERM, /* Permission denied */ - LTTCOMM_KERN_NA, /* Kernel tracer unavalable */ - LTTCOMM_KERN_EVENT_EXIST, /* Kernel event already exists */ - LTTCOMM_KERN_SESS_FAIL, /* Kernel create session failed */ - LTTCOMM_KERN_CHAN_FAIL, /* Kernel create channel failed */ - LTTCOMM_KERN_CHAN_NOT_FOUND, /* Kernel channel not found */ - LTTCOMM_KERN_CHAN_DISABLE_FAIL, /* Kernel disable channel failed */ - LTTCOMM_KERN_CHAN_ENABLE_FAIL, /* Kernel enable channel failed */ - LTTCOMM_KERN_CONTEXT_FAIL, /* Kernel add context failed */ - LTTCOMM_KERN_ENABLE_FAIL, /* Kernel enable event failed */ - LTTCOMM_KERN_DISABLE_FAIL, /* Kernel disable event failed */ - LTTCOMM_KERN_META_FAIL, /* Kernel open metadata failed */ - LTTCOMM_KERN_START_FAIL, /* Kernel start trace failed */ - LTTCOMM_KERN_STOP_FAIL, /* Kernel stop trace failed */ - LTTCOMM_KERN_CONSUMER_FAIL, /* Kernel consumer start failed */ - LTTCOMM_KERN_STREAM_FAIL, /* Kernel create stream failed */ - LTTCOMM_KERN_DIR_FAIL, /* Kernel trace directory creation failed */ - LTTCOMM_KERN_DIR_EXIST, /* Kernel trace directory exist */ - LTTCOMM_KERN_NO_SESSION, /* No kernel session found */ - LTTCOMM_KERN_LIST_FAIL, /* Kernel listing events failed */ - LTTCOMM_UST_SESS_FAIL, /* UST create session failed */ - LTTCOMM_UST_CHAN_EXIST, /* UST channel already exist */ - LTTCOMM_UST_CHAN_FAIL, /* UST create channel failed */ - LTTCOMM_UST_CHAN_NOT_FOUND, /* UST channel not found */ - LTTCOMM_UST_CHAN_DISABLE_FAIL, /* UST disable channel failed */ - LTTCOMM_UST_CHAN_ENABLE_FAIL, /* UST enable channel failed */ - LTTCOMM_UST_CONTEXT_FAIL, /* UST add context failed */ - LTTCOMM_UST_ENABLE_FAIL, /* UST enable event failed */ - LTTCOMM_UST_DISABLE_FAIL, /* UST disable event failed */ - LTTCOMM_UST_META_FAIL, /* UST open metadata failed */ - LTTCOMM_UST_START_FAIL, /* UST start trace failed */ - LTTCOMM_UST_STOP_FAIL, /* UST stop trace failed */ - LTTCOMM_UST_CONSUMER64_FAIL, /* 64-bit UST consumer start failed */ - LTTCOMM_UST_CONSUMER32_FAIL, /* 32-bit UST consumer start failed */ - LTTCOMM_UST_STREAM_FAIL, /* UST create stream failed */ - LTTCOMM_UST_DIR_FAIL, /* UST trace directory creation failed */ - LTTCOMM_UST_DIR_EXIST, /* UST trace directory exist */ - LTTCOMM_UST_NO_SESSION, /* No UST session found */ - LTTCOMM_UST_LIST_FAIL, /* UST listing events failed */ - LTTCOMM_UST_EVENT_EXIST, /* UST event exist */ - LTTCOMM_UST_EVENT_NOT_FOUND, /* UST event not found */ - LTTCOMM_UST_CONTEXT_EXIST, /* UST context exist */ - - CONSUMERD_COMMAND_SOCK_READY, /* when consumerd command socket ready */ - CONSUMERD_SUCCESS_RECV_FD, /* success on receiving fds */ - CONSUMERD_ERROR_RECV_FD, /* error on receiving fds */ - CONSUMERD_ERROR_RECV_CMD, /* error on receiving command */ - CONSUMERD_POLL_ERROR, /* Error in polling thread in kconsumerd */ - CONSUMERD_POLL_NVAL, /* Poll on closed fd */ - CONSUMERD_POLL_HUP, /* All fds have hungup */ - CONSUMERD_EXIT_SUCCESS, /* kconsumerd exiting normally */ - CONSUMERD_EXIT_FAILURE, /* kconsumerd exiting on error */ - CONSUMERD_OUTFD_ERROR, /* error opening the tracefile */ - CONSUMERD_SPLICE_EBADF, /* EBADF from splice(2) */ - CONSUMERD_SPLICE_EINVAL, /* EINVAL from splice(2) */ - CONSUMERD_SPLICE_ENOMEM, /* ENOMEM from splice(2) */ - CONSUMERD_SPLICE_ESPIPE, /* ESPIPE from splice(2) */ - /* MUST be last element */ - LTTCOMM_NR, /* Last element */ -}; - -/* - * Data structure received from lttng client to session daemon. - */ -struct lttcomm_session_msg { - uint32_t cmd_type; /* enum lttcomm_sessiond_command */ - struct lttng_session session; - struct lttng_domain domain; - union { - struct { - char channel_name[NAME_MAX]; - char name[NAME_MAX]; - } disable; - /* Event data */ - struct { - char channel_name[NAME_MAX]; - struct lttng_event event; - } enable; - /* Create channel */ - struct { - struct lttng_channel chan; - } channel; - /* Context */ - struct { - char channel_name[NAME_MAX]; - char event_name[NAME_MAX]; - struct lttng_event_context ctx; - } context; - /* Use by register_consumer */ - struct { - char path[PATH_MAX]; - } reg; - /* List */ - struct { - char channel_name[NAME_MAX]; - } list; - struct lttng_calibrate calibrate; - } u; -}; - -/* - * Data structure for the response from sessiond to the lttng client. - */ -struct lttcomm_lttng_msg { - uint32_t cmd_type; /* enum lttcomm_sessiond_command */ - uint32_t ret_code; /* enum lttcomm_return_code */ - uint32_t pid; /* pid_t */ - uint32_t data_size; - /* Contains: trace_name + data */ - char payload[]; -}; - -/* - * lttcomm_consumer_msg is the message sent from sessiond to consumerd - * to either add a channel, add a stream, update a stream, or stop - * operation. - */ -struct lttcomm_consumer_msg { - uint32_t cmd_type; /* enum consumerd_command */ - union { - struct { - int channel_key; - uint64_t max_sb_size; /* the subbuffer size for this channel */ - /* shm_fd and wait_fd are sent as ancillary data */ - uint64_t mmap_len; - } channel; - struct { - int channel_key; - int stream_key; - /* shm_fd and wait_fd are sent as ancillary data */ - uint32_t state; /* enum lttcomm_consumer_fd_state */ - enum lttng_event_output output; /* use splice or mmap to consume this fd */ - uint64_t mmap_len; - uid_t uid; /* User ID owning the session */ - gid_t gid; /* Group ID owning the session */ - char path_name[PATH_MAX]; - } stream; - } u; -}; - -#ifdef HAVE_LIBLTTNG_UST_CTL - -#include - -/* - * Data structure for the commands sent from sessiond to UST. - */ -struct lttcomm_ust_msg { - uint32_t handle; - uint32_t cmd; - union { - struct lttng_ust_channel channel; - struct lttng_ust_stream stream; - struct lttng_ust_event event; - struct lttng_ust_context context; - struct lttng_ust_tracer_version version; - } u; -}; - -/* - * Data structure for the response from UST to the session daemon. - * cmd_type is sent back in the reply for validation. - */ -struct lttcomm_ust_reply { - uint32_t handle; - uint32_t cmd; - uint32_t ret_code; /* enum lttcomm_return_code */ - uint32_t ret_val; /* return value */ - union { - struct { - uint64_t memory_map_size; - } channel; - struct { - uint64_t memory_map_size; - } stream; - struct lttng_ust_tracer_version version; - } u; -}; - -#endif /* HAVE_LIBLTTNG_UST_CTL */ - -extern int lttcomm_create_unix_sock(const char *pathname); -extern int lttcomm_connect_unix_sock(const char *pathname); -extern int lttcomm_accept_unix_sock(int sock); -extern int lttcomm_listen_unix_sock(int sock); -extern int lttcomm_close_unix_sock(int sock); - -#define LTTCOMM_MAX_SEND_FDS 4 -/* Send a message accompanied by fd(s) over a unix socket. */ -extern ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd); -/* Recv a message accompanied by fd(s) from a unix socket */ -extern ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd); - -extern ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len); -extern ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len); - -extern ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len); -extern ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, - struct ucred *creds); - -extern const char *lttcomm_get_readable_code(enum lttcomm_return_code code); -extern int lttcomm_setsockopt_creds_unix_sock(int sock); - -#endif /* _LTTNG_SESSIOND_COMM_H */ diff --git a/include/lttng-share.h b/include/lttng-share.h deleted file mode 100644 index 41d659b6f..000000000 --- a/include/lttng-share.h +++ /dev/null @@ -1,100 +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 _LTTNG_SHARE_H -#define _LTTNG_SHARE_H - -#include - -#include - -/* Default size of a hash table */ -#define DEFAULT_HT_SIZE 4 - -/* Default channel attributes */ -#define DEFAULT_CHANNEL_NAME "channel0" -#define DEFAULT_CHANNEL_OVERWRITE 0 /* usec */ -/* DEFAULT_CHANNEL_SUBBUF_SIZE must always be a power of 2 */ -#define DEFAULT_CHANNEL_SUBBUF_SIZE 4096 /* bytes */ -/* DEFAULT_CHANNEL_SUBBUF_NUM must always be a power of 2 */ -#define DEFAULT_CHANNEL_SUBBUF_NUM 8 -#define DEFAULT_CHANNEL_SWITCH_TIMER 0 /* usec */ -#define DEFAULT_CHANNEL_READ_TIMER 200 /* usec */ -#define DEFAULT_CHANNEL_OUTPUT LTTNG_EVENT_MMAP - -#define DEFAULT_METADATA_SUBBUF_SIZE 4096 -#define DEFAULT_METADATA_SUBBUF_NUM 2 - -/* Kernel has different defaults */ - -/* DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE must always be a power of 2 */ -#define DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE 262144 /* bytes */ -/* DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM must always be a power of 2 */ -#define DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM 4 -/* See lttng-kernel.h enum lttng_kernel_output for channel output */ -#define DEFAULT_KERNEL_CHANNEL_OUTPUT LTTNG_EVENT_SPLICE - -/* User space defaults */ - -/* Must be a power of 2 */ -#define DEFAULT_UST_CHANNEL_SUBBUF_SIZE 4096 /* bytes */ -/* Must be a power of 2 */ -#define DEFAULT_UST_CHANNEL_SUBBUF_NUM 4 -/* See lttng-ust.h enum lttng_ust_output */ -#define DEFAULT_UST_CHANNEL_OUTPUT LTTNG_EVENT_MMAP - -/* - * Default timeout value for the sem_timedwait() call. Blocking forever is not - * wanted so a timeout is used to control the data flow and not freeze the - * session daemon. - */ -#define DEFAULT_SEM_WAIT_TIMEOUT 30 /* in seconds */ - -/* - * Takes a pointer x and transform it so we can use it to access members - * without a function call. Here an example: - * - * #define GET_SIZE(x) LTTNG_REF(x)->size - * - * struct { int size; } s; - * - * printf("size : %d\n", GET_SIZE(&s)); - * - * For this example we can't use something like this for compatibility purpose - * since this will fail: - * - * #define GET_SIZE(x) x->size; - * - * This is mostly use for the compatibility layer of lttng-tools. See - * poll/epoll for a good example. Since x can be on the stack or allocated - * memory using malloc(), we must use generic accessors for compat in order to - * *not* use a function to access members and not the variable name. - */ -#define LTTNG_REF(x) ((typeof(*x) *)(x)) - -/* - * Memory allocation zeroed - */ -#define zmalloc(x) calloc(1, x) - -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(array) (sizeof(array) / (sizeof((array)[0]))) -#endif - - -#endif /* _LTTNG_SHARE_H */ diff --git a/include/lttng/lttng-consumer.h b/include/lttng/lttng-consumer.h deleted file mode 100644 index 3d16d4973..000000000 --- a/include/lttng/lttng-consumer.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * Copyright (C) 2011 - 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_CONSUMER_H -#define _LTTNG_CONSUMER_H - -#include -#include -#include - -#include -#include - -/* - * 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 - * 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 - * occurred on the FDs so we need to force an exit. This case should not happen - * but it is a safety to ensure we won't block the consumer indefinitely. - * - * The value of 2 seconds is an arbitrary choice. - */ -#define LTTNG_CONSUMER_POLL_TIMEOUT 2000 - -/* Commands for consumer */ -enum lttng_consumer_command { - LTTNG_CONSUMER_ADD_CHANNEL, - LTTNG_CONSUMER_ADD_STREAM, - /* pause, delete, active depending on fd state */ - LTTNG_CONSUMER_UPDATE_STREAM, - /* inform the consumer to quit when all fd has hang up */ - LTTNG_CONSUMER_STOP, -}; - -/* State of each fd in consumer */ -enum lttng_consumer_stream_state { - LTTNG_CONSUMER_ACTIVE_STREAM, - LTTNG_CONSUMER_PAUSE_STREAM, - LTTNG_CONSUMER_DELETE_STREAM, -}; - -enum lttng_consumer_type { - LTTNG_CONSUMER_UNKNOWN = 0, - LTTNG_CONSUMER_KERNEL, - LTTNG_CONSUMER64_UST, - LTTNG_CONSUMER32_UST, -}; - -struct lttng_consumer_channel { - struct lttng_ht_node_ulong node; - int key; - uint64_t max_sb_size; /* the subbuffer size for this channel */ - int refcount; /* Number of streams referencing this channel */ - /* For UST */ - int shm_fd; - int wait_fd; - void *mmap_base; - size_t mmap_len; - struct lttng_ust_shm_handle *handle; - int nr_streams; - int wait_fd_is_copy; - int cpucount; -}; - -/* Forward declaration for UST. */ -struct lttng_ust_lib_ring_buffer; - -/* - * Internal representation of the streams, sessiond_key is used to identify - * uniquely a stream. - */ -struct lttng_consumer_stream { - struct lttng_ht_node_ulong node; - struct lttng_consumer_channel *chan; /* associated channel */ - /* - * key is the key used by the session daemon to refer to the - * object in the consumer daemon. - */ - int key; - int shm_fd; - int wait_fd; - int out_fd; /* output file to write the data */ - off_t out_fd_offset; /* write position in the output file descriptor */ - char path_name[PATH_MAX]; /* tracefile name */ - enum lttng_consumer_stream_state state; - size_t shm_len; - void *mmap_base; - size_t mmap_len; - enum lttng_event_output output; /* splice or mmap */ - int shm_fd_is_copy; - int wait_fd_is_copy; - /* For UST */ - struct lttng_ust_lib_ring_buffer *buf; - int cpu; - int hangup_flush_done; - /* UID/GID of the user owning the session to which stream belongs */ - uid_t uid; - gid_t gid; -}; - -/* - * UST consumer local data to the program. One or more instance per - * process. - */ -struct lttng_consumer_local_data { - /* function to call when data is available on a buffer */ - int (*on_buffer_ready)(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx); - /* - * function to call when we receive a new channel, it receives a - * newly allocated channel, depending on the return code of this - * function, the new channel will be handled by the application - * or the library. - * - * Returns: - * > 0 (success, FD is kept by application) - * == 0 (success, FD is left to library) - * < 0 (error) - */ - int (*on_recv_channel)(struct lttng_consumer_channel *channel); - /* - * function to call when we receive a new stream, it receives a - * newly allocated stream, depending on the return code of this - * function, the new stream will be handled by the application - * or the library. - * - * Returns: - * > 0 (success, FD is kept by application) - * == 0 (success, FD is left to library) - * < 0 (error) - */ - int (*on_recv_stream)(struct lttng_consumer_stream *stream); - /* - * function to call when a stream is getting updated by the session - * daemon, this function receives the sessiond key and the new - * state, depending on the return code of this function the - * update of state for the stream is handled by the application - * or the library. - * - * Returns: - * > 0 (success, FD is kept by application) - * == 0 (success, FD is left to library) - * < 0 (error) - */ - int (*on_update_stream)(int sessiond_key, uint32_t state); - /* socket to communicate errors with sessiond */ - int consumer_error_socket; - /* socket to exchange commands with sessiond */ - char *consumer_command_sock_path; - /* communication with splice */ - int consumer_thread_pipe[2]; - /* pipe to wake the poll thread when necessary */ - int consumer_poll_pipe[2]; - /* to let the signal handler wake up the fd receiver thread */ - int consumer_should_quit[2]; -}; - -/* - * Library-level data. One instance per process. - */ -struct lttng_consumer_global_data { - - /* - * At this time, this lock is used to ensure coherence between the count - * and number of element in the hash table. It's also a protection for - * concurrent read/write between threads. - * - * XXX: We need to see if this lock is still needed with the lockless RCU - * hash tables. - */ - pthread_mutex_t lock; - - /* - * Number of streams in the hash table. Protected by consumer_data.lock. - */ - int stream_count; - /* - * Hash tables of streams and channels. Protected by consumer_data.lock. - */ - struct lttng_ht *stream_ht; - struct lttng_ht *channel_ht; - /* - * Flag specifying if the local array of FDs needs update in the - * poll function. Protected by consumer_data.lock. - */ - unsigned int need_update; - enum lttng_consumer_type type; -}; - -/* - * Init consumer data structures. - */ -extern void lttng_consumer_init(void); - -/* - * Set the error socket for communication with a session daemon. - */ -extern void lttng_consumer_set_error_sock( - struct lttng_consumer_local_data *ctx, int sock); - -/* - * Set the command socket path for communication with a session daemon. - */ -extern void lttng_consumer_set_command_sock_path( - struct lttng_consumer_local_data *ctx, char *sock); - -/* - * Send return code to session daemon. - * - * Returns the return code of sendmsg : the number of bytes transmitted or -1 - * on error. - */ -extern int lttng_consumer_send_error( - struct lttng_consumer_local_data *ctx, int cmd); - -/* - * Called from signal handler to ensure a clean exit. - */ -extern void lttng_consumer_should_exit( - struct lttng_consumer_local_data *ctx); - -/* - * Cleanup the daemon's socket on exit. - */ -extern void lttng_consumer_cleanup(void); - -/* - * Flush pending writes to trace output disk file. - */ -extern void lttng_consumer_sync_trace_file( - struct lttng_consumer_stream *stream, off_t orig_offset); - -/* - * Poll on the should_quit pipe and the command socket return -1 on error and - * should exit, 0 if data is available on the command socket - */ -extern int lttng_consumer_poll_socket(struct pollfd *kconsumer_sockpoll); - -extern int consumer_update_poll_array( - struct lttng_consumer_local_data *ctx, struct pollfd **pollfd, - struct lttng_consumer_stream **local_consumer_streams); - -extern struct lttng_consumer_stream *consumer_allocate_stream( - int channel_key, int stream_key, - int shm_fd, int wait_fd, - enum lttng_consumer_stream_state state, - uint64_t mmap_len, - enum lttng_event_output output, - const char *path_name, - uid_t uid, - gid_t gid); -extern int consumer_add_stream(struct lttng_consumer_stream *stream); -extern void consumer_del_stream(struct lttng_consumer_stream *stream); -extern void consumer_change_stream_state(int stream_key, - enum lttng_consumer_stream_state state); -extern void consumer_del_channel(struct lttng_consumer_channel *channel); -extern struct lttng_consumer_channel *consumer_allocate_channel( - int channel_key, - int shm_fd, int wait_fd, - uint64_t mmap_len, - uint64_t max_sb_size); -int consumer_add_channel(struct lttng_consumer_channel *channel); - -extern struct lttng_consumer_local_data *lttng_consumer_create( - enum lttng_consumer_type type, - int (*buffer_ready)(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx), - int (*recv_channel)(struct lttng_consumer_channel *channel), - int (*recv_stream)(struct lttng_consumer_stream *stream), - int (*update_stream)(int sessiond_key, uint32_t state)); -extern void lttng_consumer_destroy(struct lttng_consumer_local_data *ctx); -extern int lttng_consumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); -extern int lttng_consumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); -extern int lttng_consumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream); -extern int lttng_consumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos); -extern void *lttng_consumer_thread_poll_fds(void *data); -extern void *lttng_consumer_thread_receive_fds(void *data); -extern int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll); - -int lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx); -int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream); - -#endif /* _LTTNG_CONSUMER_H */ diff --git a/include/lttng/lttng-kconsumer.h b/include/lttng/lttng-kconsumer.h deleted file mode 100644 index 4753b5c59..000000000 --- a/include/lttng/lttng-kconsumer.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * Copyright (C) 2011 - 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_KCONSUMER_H -#define _LTTNG_KCONSUMER_H - -#include - -/* - * Mmap the ring buffer, read it and write the data to the tracefile. - * - * Returns the number of bytes written. - */ -extern int lttng_kconsumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); - -/* - * Splice the data from the ring buffer to the tracefile. - * - * Returns the number of bytes spliced. - */ -extern int lttng_kconsumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); - -/* - * Take a snapshot for a specific fd - * - * Returns 0 on success, < 0 on error - */ -int lttng_kconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream); - -/* - * Get the produced position - * - * Returns 0 on success, < 0 on error - */ -int lttng_kconsumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos); - -int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll); - - -int lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx); -int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream); - -#endif /* _LTTNG_KCONSUMER_H */ diff --git a/include/lttng/lttng-ustconsumer.h b/include/lttng/lttng-ustconsumer.h deleted file mode 100644 index 217c592d7..000000000 --- a/include/lttng/lttng-ustconsumer.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2011 - Julien Desfossez - * Copyright (C) 2011 - 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_USTCONSUMER_H -#define _LTTNG_USTCONSUMER_H - -#include -#include -#include - -#ifdef HAVE_LIBLTTNG_UST_CTL - -/* - * Mmap the ring buffer, read it and write the data to the tracefile. - * - * Returns the number of bytes written. - */ -extern int lttng_ustconsumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); - -/* Not implemented */ -extern int lttng_ustconsumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len); - -/* - * Take a snapshot for a specific fd - * - * Returns 0 on success, < 0 on error - */ -int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream); - -/* - * Get the produced position - * - * Returns 0 on success, < 0 on error - */ -int lttng_ustconsumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos); - -int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll); - -extern int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan); -extern void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan); -extern int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream); -extern void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream); - -int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx); -int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream); - -void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream); - -#else /* HAVE_LIBLTTNG_UST_CTL */ - -static inline -int lttng_ustconsumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *uststream, unsigned long len) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan) -{ - return -ENOSYS; -} - -static inline -void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) -{ -} - -static inline -int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream) -{ - return -ENOSYS; -} - -static inline -void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream) -{ -} - -static inline -int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx) -{ - return -ENOSYS; -} - -static inline -int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream) -{ - return -ENOSYS; -} - -static inline -void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream) -{ -} - -#endif /* HAVE_LIBLTTNG_UST_CTL */ - -#endif /* _LTTNG_USTCONSUMER_H */ diff --git a/include/lttngerr.h b/include/lttngerr.h deleted file mode 100644 index b14f23218..000000000 --- a/include/lttngerr.h +++ /dev/null @@ -1,88 +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 _LTTNGERR_H -#define _LTTNGERR_H - -#include -#include - -/* Stringify the expansion of a define */ -#define XSTR(d) STR(d) -#define STR(s) #s - -extern int opt_quiet; -extern int opt_verbose; - -#define PRINT_ERR 0x1 -#define PRINT_WARN 0x2 -#define PRINT_BUG 0x3 -#define PRINT_MSG 0x4 -#define PRINT_DBG 0x10 -#define PRINT_DBG2 0x20 -#define PRINT_DBG3 0x30 - -/* - * Macro for printing message depending on command line option and verbosity. - */ -#define __lttng_print(type, fmt, args...) \ - do { \ - if (opt_quiet == 0) { \ - if (type == PRINT_MSG) { \ - fprintf(stdout, fmt, ## args); \ - } else if (((type & PRINT_DBG) && opt_verbose == 1) || \ - ((type & (PRINT_DBG | PRINT_DBG2)) && \ - opt_verbose == 2) || \ - ((type & (PRINT_DBG | PRINT_DBG2 | PRINT_DBG3)) && \ - opt_verbose == 3)) { \ - fprintf(stderr, fmt, ## args); \ - } else if (type & (PRINT_ERR | PRINT_WARN | PRINT_BUG)) { \ - fprintf(stderr, fmt, ## args); \ - } \ - } \ - } while (0); - -#define MSG(fmt, args...) \ - __lttng_print(PRINT_MSG, fmt "\n", ## args) -#define ERR(fmt, args...) \ - __lttng_print(PRINT_ERR, "Error: " fmt "\n", ## args) -#define WARN(fmt, args...) \ - __lttng_print(PRINT_WARN, "Warning: " fmt "\n", ## args) -#define BUG(fmt, args...) \ - __lttng_print(PRINT_BUG, "BUG: " fmt "\n", ## args) - -/* Three level of debug. Use -v, -vv or -vvv for the levels */ -#define DBG(fmt, args...) __lttng_print(PRINT_DBG, "DEBUG1: " fmt \ - " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) -#define DBG2(fmt, args...) __lttng_print(PRINT_DBG2, "DEBUG2: " fmt \ - " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) -#define DBG3(fmt, args...) __lttng_print(PRINT_DBG3, "DEBUG3: " fmt \ - " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) - -#define _PERROR(fmt, args...) \ - __lttng_print(PRINT_ERR, "perror " fmt "\n", ## args) - -#define PERROR(call, args...) \ - do { \ - char *buf; \ - char tmp[200]; \ - buf = strerror_r(errno, tmp, sizeof(tmp)); \ - _PERROR(call ": %s", ## args, buf); \ - } while(0); - -#endif /* _LTTNGERR_H */ diff --git a/libkernelctl/Makefile.am b/libkernelctl/Makefile.am deleted file mode 100644 index cabdeedd9..000000000 --- a/libkernelctl/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = libkernelctl.la - -libkernelctl_la_SOURCES = kernelctl.c kernel-ioctl.h diff --git a/libkernelctl/kernel-ioctl.h b/libkernelctl/kernel-ioctl.h deleted file mode 100644 index 408535d43..000000000 --- a/libkernelctl/kernel-ioctl.h +++ /dev/null @@ -1,81 +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 _LTT_KERNEL_IOCTL_H -#define _LTT_KERNEL_IOCTL_H - -/* Get a snapshot of the current ring buffer producer and consumer positions */ -#define RING_BUFFER_SNAPSHOT _IO(0xF6, 0x00) -/* Get the consumer position (iteration start) */ -#define RING_BUFFER_SNAPSHOT_GET_CONSUMED _IOR(0xF6, 0x01, unsigned long) -/* Get the producer position (iteration end) */ -#define RING_BUFFER_SNAPSHOT_GET_PRODUCED _IOR(0xF6, 0x02, unsigned long) -/* Get exclusive read access to the specified sub-buffer position */ -#define RING_BUFFER_GET_SUBBUF _IOW(0xF6, 0x03, unsigned long) -/* Release exclusive sub-buffer access */ -#define RING_BUFFER_PUT_SUBBUF _IO(0xF6, 0x04) - -/* Get exclusive read access to the next sub-buffer that can be read. */ -#define RING_BUFFER_GET_NEXT_SUBBUF _IO(0xF6, 0x05) -/* Release exclusive sub-buffer access, move consumer forward. */ -#define RING_BUFFER_PUT_NEXT_SUBBUF _IO(0xF6, 0x06) -/* returns the size of the current sub-buffer, without padding (for mmap). */ -#define RING_BUFFER_GET_SUBBUF_SIZE _IOR(0xF6, 0x07, unsigned long) -/* returns the size of the current sub-buffer, with padding (for splice). */ -#define RING_BUFFER_GET_PADDED_SUBBUF_SIZE _IOR(0xF6, 0x08, unsigned long) -/* returns the maximum size for sub-buffers. */ -#define RING_BUFFER_GET_MAX_SUBBUF_SIZE _IOR(0xF6, 0x09, unsigned long) -/* returns the length to mmap. */ -#define RING_BUFFER_GET_MMAP_LEN _IOR(0xF6, 0x0A, unsigned long) -/* returns the offset of the subbuffer belonging to the mmap reader. */ -#define RING_BUFFER_GET_MMAP_READ_OFFSET _IOR(0xF6, 0x0B, unsigned long) -/* flush the current sub-buffer */ -#define RING_BUFFER_FLUSH _IO(0xF6, 0x0C) - -/* LTTng file descriptor ioctl */ -#define LTTNG_KERNEL_SESSION _IO(0xF6, 0x40) -#define LTTNG_KERNEL_TRACER_VERSION \ - _IOR(0xF6, 0x41, struct lttng_kernel_tracer_version) -#define LTTNG_KERNEL_TRACEPOINT_LIST _IO(0xF6, 0x42) -#define LTTNG_KERNEL_WAIT_QUIESCENT _IO(0xF6, 0x43) -#define LTTNG_KERNEL_CALIBRATE \ - _IOWR(0xF6, 0x44, struct lttng_kernel_calibrate) - -/* Session FD ioctl */ -#define LTTNG_KERNEL_METADATA \ - _IOW(0xF6, 0x50, struct lttng_channel_attr) -#define LTTNG_KERNEL_CHANNEL \ - _IOW(0xF6, 0x51, struct lttng_channel_attr) -#define LTTNG_KERNEL_SESSION_START _IO(0xF6, 0x52) -#define LTTNG_KERNEL_SESSION_STOP _IO(0xF6, 0x53) - -/* Channel FD ioctl */ -#define LTTNG_KERNEL_STREAM _IO(0xF6, 0x60) -#define LTTNG_KERNEL_EVENT \ - _IOW(0xF6, 0x61, struct lttng_kernel_event) - -/* Event and Channel FD ioctl */ -#define LTTNG_KERNEL_CONTEXT \ - _IOW(0xF6, 0x70, struct lttng_kernel_context) - -/* Event, Channel and Session ioctl */ -#define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x80) -#define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x81) - -#endif /* _LTT_KERNEL_IOCTL_H */ diff --git a/libkernelctl/kernelctl.c b/libkernelctl/kernelctl.c deleted file mode 100644 index 7936b2742..000000000 --- a/libkernelctl/kernelctl.c +++ /dev/null @@ -1,190 +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. - */ - -#include - -#include - -#include "kernel-ioctl.h" - -int kernctl_create_session(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_SESSION); -} - -/* open the metadata global channel */ -int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops) -{ - return ioctl(fd, LTTNG_KERNEL_METADATA, chops); -} - -int kernctl_create_channel(int fd, struct lttng_channel_attr *chops) -{ - return ioctl(fd, LTTNG_KERNEL_CHANNEL, chops); -} - -int kernctl_create_stream(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_STREAM); -} - -int kernctl_create_event(int fd, struct lttng_kernel_event *ev) -{ - return ioctl(fd, LTTNG_KERNEL_EVENT, ev); -} - -int kernctl_add_context(int fd, struct lttng_kernel_context *ctx) -{ - return ioctl(fd, LTTNG_KERNEL_CONTEXT, ctx); -} - - -/* Enable event, channel and session ioctl */ -int kernctl_enable(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_ENABLE); -} - -/* Disable event, channel and session ioctl */ -int kernctl_disable(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_DISABLE); -} - -int kernctl_start_session(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_SESSION_START); -} - -int kernctl_stop_session(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_SESSION_STOP); -} - - -int kernctl_tracepoint_list(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_TRACEPOINT_LIST); -} - -int kernctl_tracer_version(int fd, struct lttng_kernel_tracer_version *v) -{ - return ioctl(fd, LTTNG_KERNEL_TRACER_VERSION, v); -} - -int kernctl_wait_quiescent(int fd) -{ - return ioctl(fd, LTTNG_KERNEL_WAIT_QUIESCENT); -} - -int kernctl_calibrate(int fd, struct lttng_kernel_calibrate *calibrate) -{ - return ioctl(fd, LTTNG_KERNEL_CALIBRATE, calibrate); -} - - -int kernctl_buffer_flush(int fd) -{ - return ioctl(fd, RING_BUFFER_FLUSH); -} - - -/* Buffer operations */ - -/* For mmap mode, readable without "get" operation */ - -/* returns the length to mmap. */ -int kernctl_get_mmap_len(int fd, unsigned long *len) -{ - return ioctl(fd, RING_BUFFER_GET_MMAP_LEN, len); -} - -/* returns the maximum size for sub-buffers. */ -int kernctl_get_max_subbuf_size(int fd, unsigned long *len) -{ - return ioctl(fd, RING_BUFFER_GET_MAX_SUBBUF_SIZE, len); -} - -/* - * For mmap mode, operate on the current packet (between get/put or - * get_next/put_next). - */ - -/* returns the offset of the subbuffer belonging to the mmap reader. */ -int kernctl_get_mmap_read_offset(int fd, unsigned long *off) -{ - return ioctl(fd, RING_BUFFER_GET_MMAP_READ_OFFSET, off); -} - -/* returns the size of the current sub-buffer, without padding (for mmap). */ -int kernctl_get_subbuf_size(int fd, unsigned long *len) -{ - return ioctl(fd, RING_BUFFER_GET_SUBBUF_SIZE, len); -} - -/* returns the size of the current sub-buffer, without padding (for mmap). */ -int kernctl_get_padded_subbuf_size(int fd, unsigned long *len) -{ - return ioctl(fd, RING_BUFFER_GET_PADDED_SUBBUF_SIZE, len); -} - -/* Get exclusive read access to the next sub-buffer that can be read. */ -int kernctl_get_next_subbuf(int fd) -{ - return ioctl(fd, RING_BUFFER_GET_NEXT_SUBBUF); -} - - -/* Release exclusive sub-buffer access, move consumer forward. */ -int kernctl_put_next_subbuf(int fd) -{ - return ioctl(fd, RING_BUFFER_PUT_NEXT_SUBBUF); -} - -/* snapshot */ - -/* Get a snapshot of the current ring buffer producer and consumer positions */ -int kernctl_snapshot(int fd) -{ - return ioctl(fd, RING_BUFFER_SNAPSHOT); -} - -/* Get the consumer position (iteration start) */ -int kernctl_snapshot_get_consumed(int fd, unsigned long *pos) -{ - return ioctl(fd, RING_BUFFER_SNAPSHOT_GET_CONSUMED, pos); -} - -/* Get the producer position (iteration end) */ -int kernctl_snapshot_get_produced(int fd, unsigned long *pos) -{ - return ioctl(fd, RING_BUFFER_SNAPSHOT_GET_PRODUCED, pos); -} - -/* Get exclusive read access to the specified sub-buffer position */ -int kernctl_get_subbuf(int fd, unsigned long *len) -{ - return ioctl(fd, RING_BUFFER_GET_SUBBUF, len); -} - -/* Release exclusive sub-buffer access */ -int kernctl_put_subbuf(int fd) -{ - return ioctl(fd, RING_BUFFER_PUT_SUBBUF); -} diff --git a/liblttng-consumer/Makefile.am b/liblttng-consumer/Makefile.am deleted file mode 100644 index aadba52b5..000000000 --- a/liblttng-consumer/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -lib_LTLIBRARIES = liblttng-consumer.la - -liblttng_consumer_la_SOURCES = lttng-consumer.c - -liblttng_consumer_la_LIBADD = \ - $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ - $(top_builddir)/liblttng-kconsumer/liblttng-kconsumer.la \ - $(top_builddir)/liblttng-ht/liblttng-ht.la - -if HAVE_LIBLTTNG_UST_CTL -liblttng_consumer_la_LIBADD += \ - $(top_builddir)/liblttng-ustconsumer/liblttng-ustconsumer.la -endif diff --git a/liblttng-consumer/lttng-consumer.c b/liblttng-consumer/lttng-consumer.c deleted file mode 100644 index 0263aa1d8..000000000 --- a/liblttng-consumer/lttng-consumer.c +++ /dev/null @@ -1,1166 +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. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -struct lttng_consumer_global_data consumer_data = { - .stream_count = 0, - .need_update = 1, - .type = LTTNG_CONSUMER_UNKNOWN, -}; - -/* timeout parameter, to control the polling thread grace period. */ -int consumer_poll_timeout = -1; - -/* - * Flag to inform the polling thread to quit when all fd hung up. Updated by - * the consumer_thread_receive_fds when it notices that all fds has hung up. - * Also updated by the signal handler (consumer_should_exit()). Read by the - * polling threads. - */ -volatile int consumer_quit = 0; - -/* - * Find a stream. The consumer_data.lock must be locked during this - * call. - */ -static struct lttng_consumer_stream *consumer_find_stream(int key) -{ - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - struct lttng_consumer_stream *stream = NULL; - - /* Negative keys are lookup failures */ - if (key < 0) - return NULL; - - rcu_read_lock(); - - lttng_ht_lookup(consumer_data.stream_ht, (void *)((unsigned long) key), - &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node != NULL) { - stream = caa_container_of(node, struct lttng_consumer_stream, node); - } - - rcu_read_unlock(); - - return stream; -} - -static void consumer_steal_stream_key(int key) -{ - struct lttng_consumer_stream *stream; - - stream = consumer_find_stream(key); - if (stream) - stream->key = -1; -} - -static struct lttng_consumer_channel *consumer_find_channel(int key) -{ - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - struct lttng_consumer_channel *channel = NULL; - - /* Negative keys are lookup failures */ - if (key < 0) - return NULL; - - rcu_read_lock(); - - lttng_ht_lookup(consumer_data.channel_ht, (void *)((unsigned long) key), - &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node != NULL) { - channel = caa_container_of(node, struct lttng_consumer_channel, node); - } - - rcu_read_unlock(); - - return channel; -} - -static void consumer_steal_channel_key(int key) -{ - struct lttng_consumer_channel *channel; - - channel = consumer_find_channel(key); - if (channel) - channel->key = -1; -} - -/* - * Remove a stream from the global list protected by a mutex. This - * function is also responsible for freeing its data structures. - */ -void consumer_del_stream(struct lttng_consumer_stream *stream) -{ - int ret; - struct lttng_ht_iter iter; - struct lttng_consumer_channel *free_chan = NULL; - - pthread_mutex_lock(&consumer_data.lock); - - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - if (stream->mmap_base != NULL) { - ret = munmap(stream->mmap_base, stream->mmap_len); - if (ret != 0) { - perror("munmap"); - } - } - break; - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - lttng_ustconsumer_del_stream(stream); - break; - default: - ERR("Unknown consumer_data type"); - assert(0); - goto end; - } - - rcu_read_lock(); - - /* Get stream node from hash table */ - lttng_ht_lookup(consumer_data.stream_ht, - (void *)((unsigned long) stream->key), &iter); - /* Remove stream node from hash table */ - ret = lttng_ht_del(consumer_data.stream_ht, &iter); - assert(!ret); - - rcu_read_unlock(); - - if (consumer_data.stream_count <= 0) { - goto end; - } - consumer_data.stream_count--; - if (!stream) { - goto end; - } - if (stream->out_fd >= 0) { - close(stream->out_fd); - } - if (stream->wait_fd >= 0 && !stream->wait_fd_is_copy) { - close(stream->wait_fd); - } - if (stream->shm_fd >= 0 && stream->wait_fd != stream->shm_fd) { - close(stream->shm_fd); - } - if (!--stream->chan->refcount) - free_chan = stream->chan; - free(stream); -end: - consumer_data.need_update = 1; - pthread_mutex_unlock(&consumer_data.lock); - - if (free_chan) - consumer_del_channel(free_chan); -} - -static void consumer_del_stream_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_ulong *node = - caa_container_of(head, struct lttng_ht_node_ulong, head); - struct lttng_consumer_stream *stream = - caa_container_of(node, struct lttng_consumer_stream, node); - - consumer_del_stream(stream); -} - -struct lttng_consumer_stream *consumer_allocate_stream( - int channel_key, int stream_key, - int shm_fd, int wait_fd, - enum lttng_consumer_stream_state state, - uint64_t mmap_len, - enum lttng_event_output output, - const char *path_name, - uid_t uid, - gid_t gid) -{ - struct lttng_consumer_stream *stream; - int ret; - - stream = zmalloc(sizeof(*stream)); - if (stream == NULL) { - perror("malloc struct lttng_consumer_stream"); - goto end; - } - stream->chan = consumer_find_channel(channel_key); - if (!stream->chan) { - perror("Unable to find channel key"); - goto end; - } - stream->chan->refcount++; - stream->key = stream_key; - stream->shm_fd = shm_fd; - stream->wait_fd = wait_fd; - stream->out_fd = -1; - stream->out_fd_offset = 0; - stream->state = state; - stream->mmap_len = mmap_len; - stream->mmap_base = NULL; - stream->output = output; - stream->uid = uid; - stream->gid = gid; - strncpy(stream->path_name, path_name, PATH_MAX - 1); - stream->path_name[PATH_MAX - 1] = '\0'; - lttng_ht_node_init_ulong(&stream->node, stream->key); - - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - break; - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - stream->cpu = stream->chan->cpucount++; - ret = lttng_ustconsumer_allocate_stream(stream); - if (ret) { - free(stream); - return NULL; - } - break; - default: - ERR("Unknown consumer_data type"); - assert(0); - goto end; - } - DBG("Allocated stream %s (key %d, shm_fd %d, wait_fd %d, mmap_len %llu, out_fd %d)", - stream->path_name, stream->key, - stream->shm_fd, - stream->wait_fd, - (unsigned long long) stream->mmap_len, - stream->out_fd); -end: - return stream; -} - -/* - * Add a stream to the global list protected by a mutex. - */ -int consumer_add_stream(struct lttng_consumer_stream *stream) -{ - int ret = 0; - - pthread_mutex_lock(&consumer_data.lock); - /* Steal stream identifier, for UST */ - consumer_steal_stream_key(stream->key); - rcu_read_lock(); - lttng_ht_add_unique_ulong(consumer_data.stream_ht, &stream->node); - rcu_read_unlock(); - consumer_data.stream_count++; - consumer_data.need_update = 1; - - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - break; - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - /* Streams are in CPU number order (we rely on this) */ - stream->cpu = stream->chan->nr_streams++; - break; - default: - ERR("Unknown consumer_data type"); - assert(0); - goto end; - } - -end: - pthread_mutex_unlock(&consumer_data.lock); - return ret; -} - -/* - * Update a stream according to what we just received. - */ -void consumer_change_stream_state(int stream_key, - enum lttng_consumer_stream_state state) -{ - struct lttng_consumer_stream *stream; - - pthread_mutex_lock(&consumer_data.lock); - stream = consumer_find_stream(stream_key); - if (stream) { - stream->state = state; - } - consumer_data.need_update = 1; - pthread_mutex_unlock(&consumer_data.lock); -} - -/* - * Remove a channel from the global list protected by a mutex. This - * function is also responsible for freeing its data structures. - */ -void consumer_del_channel(struct lttng_consumer_channel *channel) -{ - int ret; - struct lttng_ht_iter iter; - - pthread_mutex_lock(&consumer_data.lock); - - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - break; - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - lttng_ustconsumer_del_channel(channel); - break; - default: - ERR("Unknown consumer_data type"); - assert(0); - goto end; - } - - rcu_read_lock(); - - lttng_ht_lookup(consumer_data.channel_ht, - (void *)((unsigned long) channel->key), &iter); - ret = lttng_ht_del(consumer_data.channel_ht, &iter); - assert(!ret); - - rcu_read_unlock(); - - if (channel->mmap_base != NULL) { - ret = munmap(channel->mmap_base, channel->mmap_len); - if (ret != 0) { - perror("munmap"); - } - } - if (channel->wait_fd >= 0 && !channel->wait_fd_is_copy) { - close(channel->wait_fd); - } - if (channel->shm_fd >= 0 && channel->wait_fd != channel->shm_fd) { - close(channel->shm_fd); - } - free(channel); -end: - pthread_mutex_unlock(&consumer_data.lock); -} - -static void consumer_del_channel_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_ulong *node = - caa_container_of(head, struct lttng_ht_node_ulong, head); - struct lttng_consumer_channel *channel= - caa_container_of(node, struct lttng_consumer_channel, node); - - consumer_del_channel(channel); -} - -struct lttng_consumer_channel *consumer_allocate_channel( - int channel_key, - int shm_fd, int wait_fd, - uint64_t mmap_len, - uint64_t max_sb_size) -{ - struct lttng_consumer_channel *channel; - int ret; - - channel = zmalloc(sizeof(*channel)); - if (channel == NULL) { - perror("malloc struct lttng_consumer_channel"); - goto end; - } - channel->key = channel_key; - channel->shm_fd = shm_fd; - channel->wait_fd = wait_fd; - channel->mmap_len = mmap_len; - channel->max_sb_size = max_sb_size; - channel->refcount = 0; - channel->nr_streams = 0; - lttng_ht_node_init_ulong(&channel->node, channel->key); - - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - channel->mmap_base = NULL; - channel->mmap_len = 0; - break; - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - ret = lttng_ustconsumer_allocate_channel(channel); - if (ret) { - free(channel); - return NULL; - } - break; - default: - ERR("Unknown consumer_data type"); - assert(0); - goto end; - } - DBG("Allocated channel (key %d, shm_fd %d, wait_fd %d, mmap_len %llu, max_sb_size %llu)", - channel->key, - channel->shm_fd, - channel->wait_fd, - (unsigned long long) channel->mmap_len, - (unsigned long long) channel->max_sb_size); -end: - return channel; -} - -/* - * Add a channel to the global list protected by a mutex. - */ -int consumer_add_channel(struct lttng_consumer_channel *channel) -{ - pthread_mutex_lock(&consumer_data.lock); - /* Steal channel identifier, for UST */ - consumer_steal_channel_key(channel->key); - rcu_read_lock(); - lttng_ht_add_unique_ulong(consumer_data.channel_ht, &channel->node); - rcu_read_unlock(); - pthread_mutex_unlock(&consumer_data.lock); - return 0; -} - -/* - * Allocate the pollfd structure and the local view of the out fds to avoid - * doing a lookup in the linked list and concurrency issues when writing is - * needed. Called with consumer_data.lock held. - * - * Returns the number of fds in the structures. - */ -int consumer_update_poll_array( - struct lttng_consumer_local_data *ctx, struct pollfd **pollfd, - struct lttng_consumer_stream **local_stream) -{ - int i = 0; - struct lttng_ht_iter iter; - struct lttng_consumer_stream *stream; - - DBG("Updating poll fd array"); - cds_lfht_for_each_entry(consumer_data.stream_ht->ht, &iter.iter, stream, - node.node) { - if (stream->state != LTTNG_CONSUMER_ACTIVE_STREAM) { - continue; - } - DBG("Active FD %d", stream->wait_fd); - (*pollfd)[i].fd = stream->wait_fd; - (*pollfd)[i].events = POLLIN | POLLPRI; - local_stream[i] = stream; - i++; - } - - /* - * Insert the consumer_poll_pipe at the end of the array and don't - * increment i so nb_fd is the number of real FD. - */ - (*pollfd)[i].fd = ctx->consumer_poll_pipe[0]; - (*pollfd)[i].events = POLLIN; - return i; -} - -/* - * Poll on the should_quit pipe and the command socket return -1 on error and - * should exit, 0 if data is available on the command socket - */ -int lttng_consumer_poll_socket(struct pollfd *consumer_sockpoll) -{ - int num_rdy; - - num_rdy = poll(consumer_sockpoll, 2, -1); - if (num_rdy == -1) { - perror("Poll error"); - goto exit; - } - if (consumer_sockpoll[0].revents == POLLIN) { - DBG("consumer_should_quit wake up"); - goto exit; - } - return 0; - -exit: - return -1; -} - -/* - * Set the error socket. - */ -void lttng_consumer_set_error_sock( - struct lttng_consumer_local_data *ctx, int sock) -{ - ctx->consumer_error_socket = sock; -} - -/* - * Set the command socket path. - */ - -void lttng_consumer_set_command_sock_path( - struct lttng_consumer_local_data *ctx, char *sock) -{ - ctx->consumer_command_sock_path = sock; -} - -/* - * Send return code to the session daemon. - * If the socket is not defined, we return 0, it is not a fatal error - */ -int lttng_consumer_send_error( - struct lttng_consumer_local_data *ctx, int cmd) -{ - if (ctx->consumer_error_socket > 0) { - return lttcomm_send_unix_sock(ctx->consumer_error_socket, &cmd, - sizeof(enum lttcomm_sessiond_command)); - } - - return 0; -} - -/* - * Close all the tracefiles and stream fds, should be called when all instances - * are destroyed. - */ -void lttng_consumer_cleanup(void) -{ - int ret; - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - - rcu_read_lock(); - - /* - * close all outfd. Called when there are no more threads running (after - * joining on the threads), no need to protect list iteration with mutex. - */ - cds_lfht_for_each_entry(consumer_data.stream_ht->ht, &iter.iter, node, - node) { - ret = lttng_ht_del(consumer_data.stream_ht, &iter); - assert(!ret); - call_rcu(&node->head, consumer_del_stream_rcu); - } - - cds_lfht_for_each_entry(consumer_data.channel_ht->ht, &iter.iter, node, - node) { - ret = lttng_ht_del(consumer_data.channel_ht, &iter); - assert(!ret); - call_rcu(&node->head, consumer_del_channel_rcu); - } - - rcu_read_unlock(); -} - -/* - * Called from signal handler. - */ -void lttng_consumer_should_exit(struct lttng_consumer_local_data *ctx) -{ - int ret; - consumer_quit = 1; - ret = write(ctx->consumer_should_quit[1], "4", 1); - if (ret < 0) { - perror("write consumer quit"); - } -} - -void lttng_consumer_sync_trace_file( - struct lttng_consumer_stream *stream, off_t orig_offset) -{ - int outfd = stream->out_fd; - - /* - * This does a blocking write-and-wait on any page that belongs to the - * subbuffer prior to the one we just wrote. - * Don't care about error values, as these are just hints and ways to - * limit the amount of page cache used. - */ - if (orig_offset < stream->chan->max_sb_size) { - return; - } - sync_file_range(outfd, orig_offset - stream->chan->max_sb_size, - stream->chan->max_sb_size, - SYNC_FILE_RANGE_WAIT_BEFORE - | SYNC_FILE_RANGE_WRITE - | SYNC_FILE_RANGE_WAIT_AFTER); - /* - * Give hints to the kernel about how we access the file: - * POSIX_FADV_DONTNEED : we won't re-access data in a near future after - * we write it. - * - * We need to call fadvise again after the file grows because the - * kernel does not seem to apply fadvise to non-existing parts of the - * file. - * - * Call fadvise _after_ having waited for the page writeback to - * complete because the dirty page writeback semantic is not well - * defined. So it can be expected to lead to lower throughput in - * streaming. - */ - posix_fadvise(outfd, orig_offset - stream->chan->max_sb_size, - stream->chan->max_sb_size, POSIX_FADV_DONTNEED); -} - -/* - * Initialise the necessary environnement : - * - create a new context - * - create the poll_pipe - * - create the should_quit pipe (for signal handler) - * - create the thread pipe (for splice) - * - * Takes a function pointer as argument, this function is called when data is - * available on a buffer. This function is responsible to do the - * kernctl_get_next_subbuf, read the data with mmap or splice depending on the - * buffer configuration and then kernctl_put_next_subbuf at the end. - * - * Returns a pointer to the new context or NULL on error. - */ -struct lttng_consumer_local_data *lttng_consumer_create( - enum lttng_consumer_type type, - int (*buffer_ready)(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx), - int (*recv_channel)(struct lttng_consumer_channel *channel), - int (*recv_stream)(struct lttng_consumer_stream *stream), - int (*update_stream)(int stream_key, uint32_t state)) -{ - int ret, i; - struct lttng_consumer_local_data *ctx; - - assert(consumer_data.type == LTTNG_CONSUMER_UNKNOWN || - consumer_data.type == type); - consumer_data.type = type; - - ctx = zmalloc(sizeof(struct lttng_consumer_local_data)); - if (ctx == NULL) { - perror("allocating context"); - goto error; - } - - ctx->consumer_error_socket = -1; - /* assign the callbacks */ - ctx->on_buffer_ready = buffer_ready; - ctx->on_recv_channel = recv_channel; - ctx->on_recv_stream = recv_stream; - ctx->on_update_stream = update_stream; - - ret = pipe(ctx->consumer_poll_pipe); - if (ret < 0) { - perror("Error creating poll pipe"); - goto error_poll_pipe; - } - - ret = pipe(ctx->consumer_should_quit); - if (ret < 0) { - perror("Error creating recv pipe"); - goto error_quit_pipe; - } - - ret = pipe(ctx->consumer_thread_pipe); - if (ret < 0) { - perror("Error creating thread pipe"); - goto error_thread_pipe; - } - - return ctx; - - -error_thread_pipe: - for (i = 0; i < 2; i++) { - int err; - - err = close(ctx->consumer_should_quit[i]); - assert(!err); - } -error_quit_pipe: - for (i = 0; i < 2; i++) { - int err; - - err = close(ctx->consumer_poll_pipe[i]); - assert(!err); - } -error_poll_pipe: - free(ctx); -error: - return NULL; -} - -/* - * Close all fds associated with the instance and free the context. - */ -void lttng_consumer_destroy(struct lttng_consumer_local_data *ctx) -{ - close(ctx->consumer_error_socket); - close(ctx->consumer_thread_pipe[0]); - close(ctx->consumer_thread_pipe[1]); - close(ctx->consumer_poll_pipe[0]); - close(ctx->consumer_poll_pipe[1]); - close(ctx->consumer_should_quit[0]); - close(ctx->consumer_should_quit[1]); - unlink(ctx->consumer_command_sock_path); - free(ctx); -} - -/* - * Mmap the ring buffer, read it and write the data to the tracefile. - * - * Returns the number of bytes written - */ -int lttng_consumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_on_read_subbuffer_mmap(ctx, stream, len); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_on_read_subbuffer_mmap(ctx, stream, len); - default: - ERR("Unknown consumer_data type"); - assert(0); - } -} - -/* - * Splice the data from the ring buffer to the tracefile. - * - * Returns the number of bytes spliced. - */ -int lttng_consumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_on_read_subbuffer_splice(ctx, stream, len); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return -ENOSYS; - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } - -} - -/* - * Take a snapshot for a specific fd - * - * Returns 0 on success, < 0 on error - */ -int lttng_consumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_take_snapshot(ctx, stream); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_take_snapshot(ctx, stream); - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } - -} - -/* - * Get the produced position - * - * Returns 0 on success, < 0 on error - */ -int lttng_consumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_get_produced_snapshot(ctx, stream, pos); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_get_produced_snapshot(ctx, stream, pos); - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } -} - -int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_recv_cmd(ctx, sock, consumer_sockpoll); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_recv_cmd(ctx, sock, consumer_sockpoll); - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } -} - -/* - * This thread polls the fds in the set to consume the data and write - * it to tracefile if necessary. - */ -void *lttng_consumer_thread_poll_fds(void *data) -{ - int num_rdy, num_hup, high_prio, ret, i; - struct pollfd *pollfd = NULL; - /* local view of the streams */ - struct lttng_consumer_stream **local_stream = NULL; - /* local view of consumer_data.fds_count */ - int nb_fd = 0; - char tmp; - int tmp2; - struct lttng_consumer_local_data *ctx = data; - - rcu_register_thread(); - - local_stream = zmalloc(sizeof(struct lttng_consumer_stream)); - - while (1) { - high_prio = 0; - num_hup = 0; - - /* - * the fds set has been updated, we need to update our - * local array as well - */ - pthread_mutex_lock(&consumer_data.lock); - if (consumer_data.need_update) { - if (pollfd != NULL) { - free(pollfd); - pollfd = NULL; - } - if (local_stream != NULL) { - free(local_stream); - local_stream = NULL; - } - - /* allocate for all fds + 1 for the consumer_poll_pipe */ - pollfd = zmalloc((consumer_data.stream_count + 1) * sizeof(struct pollfd)); - if (pollfd == NULL) { - perror("pollfd malloc"); - pthread_mutex_unlock(&consumer_data.lock); - goto end; - } - - /* allocate for all fds + 1 for the consumer_poll_pipe */ - local_stream = zmalloc((consumer_data.stream_count + 1) * - sizeof(struct lttng_consumer_stream)); - if (local_stream == NULL) { - perror("local_stream malloc"); - pthread_mutex_unlock(&consumer_data.lock); - goto end; - } - ret = consumer_update_poll_array(ctx, &pollfd, local_stream); - if (ret < 0) { - ERR("Error in allocating pollfd or local_outfds"); - lttng_consumer_send_error(ctx, CONSUMERD_POLL_ERROR); - pthread_mutex_unlock(&consumer_data.lock); - goto end; - } - nb_fd = ret; - consumer_data.need_update = 0; - } - pthread_mutex_unlock(&consumer_data.lock); - - /* poll on the array of fds */ - DBG("polling on %d fd", nb_fd + 1); - num_rdy = poll(pollfd, nb_fd + 1, consumer_poll_timeout); - DBG("poll num_rdy : %d", num_rdy); - if (num_rdy == -1) { - perror("Poll error"); - lttng_consumer_send_error(ctx, CONSUMERD_POLL_ERROR); - goto end; - } else if (num_rdy == 0) { - DBG("Polling thread timed out"); - goto end; - } - - /* No FDs and consumer_quit, consumer_cleanup the thread */ - if (nb_fd == 0 && consumer_quit == 1) { - goto end; - } - - /* - * If the consumer_poll_pipe triggered poll go - * directly to the beginning of the loop to update the - * array. We want to prioritize array update over - * low-priority reads. - */ - if (pollfd[nb_fd].revents & POLLIN) { - DBG("consumer_poll_pipe wake up"); - tmp2 = read(ctx->consumer_poll_pipe[0], &tmp, 1); - if (tmp2 < 0) { - perror("read consumer poll"); - } - continue; - } - - /* Take care of high priority channels first. */ - for (i = 0; i < nb_fd; i++) { - if (pollfd[i].revents & POLLPRI) { - DBG("Urgent read on fd %d", pollfd[i].fd); - high_prio = 1; - ret = ctx->on_buffer_ready(local_stream[i], ctx); - /* it's ok to have an unavailable sub-buffer */ - if (ret == EAGAIN) { - ret = 0; - } - } else if (pollfd[i].revents & POLLERR) { - ERR("Error returned in polling fd %d.", pollfd[i].fd); - rcu_read_lock(); - consumer_del_stream_rcu(&local_stream[i]->node.head); - rcu_read_unlock(); - num_hup++; - } else if (pollfd[i].revents & POLLNVAL) { - ERR("Polling fd %d tells fd is not open.", pollfd[i].fd); - rcu_read_lock(); - consumer_del_stream_rcu(&local_stream[i]->node.head); - rcu_read_unlock(); - num_hup++; - } else if ((pollfd[i].revents & POLLHUP) && - !(pollfd[i].revents & POLLIN)) { - if (consumer_data.type == LTTNG_CONSUMER32_UST - || consumer_data.type == LTTNG_CONSUMER64_UST) { - DBG("Polling fd %d tells it has hung up. Attempting flush and read.", - pollfd[i].fd); - if (!local_stream[i]->hangup_flush_done) { - lttng_ustconsumer_on_stream_hangup(local_stream[i]); - /* read after flush */ - do { - ret = ctx->on_buffer_ready(local_stream[i], ctx); - } while (ret == EAGAIN); - } - } else { - DBG("Polling fd %d tells it has hung up.", pollfd[i].fd); - } - rcu_read_lock(); - consumer_del_stream_rcu(&local_stream[i]->node.head); - rcu_read_unlock(); - num_hup++; - } - } - - /* If every buffer FD has hung up, we end the read loop here */ - if (nb_fd > 0 && num_hup == nb_fd) { - DBG("every buffer FD has hung up\n"); - if (consumer_quit == 1) { - goto end; - } - continue; - } - - /* Take care of low priority channels. */ - if (high_prio == 0) { - for (i = 0; i < nb_fd; i++) { - if (pollfd[i].revents & POLLIN) { - DBG("Normal read on fd %d", pollfd[i].fd); - ret = ctx->on_buffer_ready(local_stream[i], ctx); - /* it's ok to have an unavailable subbuffer */ - if (ret == EAGAIN) { - ret = 0; - } - } - } - } - } -end: - DBG("polling thread exiting"); - if (pollfd != NULL) { - free(pollfd); - pollfd = NULL; - } - if (local_stream != NULL) { - free(local_stream); - local_stream = NULL; - } - rcu_unregister_thread(); - return NULL; -} - -/* - * This thread listens on the consumerd socket and receives the file - * descriptors from the session daemon. - */ -void *lttng_consumer_thread_receive_fds(void *data) -{ - int sock, client_socket, ret; - /* - * structure to poll for incoming data on communication socket avoids - * making blocking sockets. - */ - struct pollfd consumer_sockpoll[2]; - struct lttng_consumer_local_data *ctx = data; - - rcu_register_thread(); - - DBG("Creating command socket %s", ctx->consumer_command_sock_path); - unlink(ctx->consumer_command_sock_path); - client_socket = lttcomm_create_unix_sock(ctx->consumer_command_sock_path); - if (client_socket < 0) { - ERR("Cannot create command socket"); - goto end; - } - - ret = lttcomm_listen_unix_sock(client_socket); - if (ret < 0) { - goto end; - } - - 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 lttng-sessiond"); - goto end; - } - - ret = fcntl(client_socket, F_SETFL, O_NONBLOCK); - if (ret < 0) { - perror("fcntl O_NONBLOCK"); - goto end; - } - - /* prepare the FDs to poll : to client socket and the should_quit pipe */ - consumer_sockpoll[0].fd = ctx->consumer_should_quit[0]; - consumer_sockpoll[0].events = POLLIN | POLLPRI; - consumer_sockpoll[1].fd = client_socket; - consumer_sockpoll[1].events = POLLIN | POLLPRI; - - if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { - goto end; - } - DBG("Connection on client_socket"); - - /* Blocking call, waiting for transmission */ - sock = lttcomm_accept_unix_sock(client_socket); - if (sock <= 0) { - WARN("On accept"); - goto end; - } - ret = fcntl(sock, F_SETFL, O_NONBLOCK); - if (ret < 0) { - perror("fcntl O_NONBLOCK"); - goto end; - } - - /* update the polling structure to poll on the established socket */ - consumer_sockpoll[1].fd = sock; - consumer_sockpoll[1].events = POLLIN | POLLPRI; - - while (1) { - if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { - goto end; - } - DBG("Incoming command on sock"); - ret = lttng_consumer_recv_cmd(ctx, sock, consumer_sockpoll); - if (ret == -ENOENT) { - DBG("Received STOP command"); - goto end; - } - if (ret < 0) { - ERR("Communication interrupted on command socket"); - goto end; - } - if (consumer_quit) { - DBG("consumer_thread_receive_fds received quit from signal"); - goto end; - } - DBG("received fds on sock"); - } -end: - DBG("consumer_thread_receive_fds exiting"); - - /* - * when all fds have hung up, the polling thread - * can exit cleanly - */ - consumer_quit = 1; - - /* - * 2s of grace period, if no polling events occur during - * this period, the polling thread will exit even if there - * are still open FDs (should not happen, but safety mechanism). - */ - consumer_poll_timeout = LTTNG_CONSUMER_POLL_TIMEOUT; - - /* wake up the polling thread */ - ret = write(ctx->consumer_poll_pipe[1], "4", 1); - if (ret < 0) { - perror("poll pipe write"); - } - rcu_unregister_thread(); - return NULL; -} - -int lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_read_subbuffer(stream, ctx); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_read_subbuffer(stream, ctx); - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } -} - -int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream) -{ - switch (consumer_data.type) { - case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_on_recv_stream(stream); - case LTTNG_CONSUMER32_UST: - case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_on_recv_stream(stream); - default: - ERR("Unknown consumer_data type"); - assert(0); - return -ENOSYS; - } -} - -/* - * Allocate and set consumer data hash tables. - */ -void lttng_consumer_init(void) -{ - consumer_data.stream_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); -} - diff --git a/liblttng-ht/Makefile.am b/liblttng-ht/Makefile.am deleted file mode 100644 index bd85018d7..000000000 --- a/liblttng-ht/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = liblttng-ht.la - -liblttng_ht_la_SOURCES = lttng-ht.c \ - utils.c utils.h \ - rculfhash-internal.h urcu-flavor.h \ - rculfhash.h rculfhash.c \ - rculfhash-mm-chunk.c \ - rculfhash-mm-mmap.c \ - rculfhash-mm-order.c - -liblttng_ht_la_LIBADD = -lurcu-common -lurcu diff --git a/liblttng-ht/lttng-ht.c b/liblttng-ht/lttng-ht.c deleted file mode 100644 index 74e5ed4fe..000000000 --- a/liblttng-ht/lttng-ht.c +++ /dev/null @@ -1,284 +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 "utils.h" - -#define HASH_SEED 0x42UL /* The answer to life */ - -static unsigned long min_hash_alloc_size = 1; -static unsigned long max_hash_buckets_size = (1UL << 20); - -/* - * Match function for string node. - */ -static int match_str(struct cds_lfht_node *node, const void *key) -{ - struct lttng_ht_node_str *match_node = - caa_container_of(node, struct lttng_ht_node_str, node); - - return hash_match_key_str(match_node->key, (void *) key); -} - -/* - * Match function for ulong node. - */ -static int match_ulong(struct cds_lfht_node *node, const void *key) -{ - struct lttng_ht_node_ulong *match_node = - caa_container_of(node, struct lttng_ht_node_ulong, node); - - return hash_match_key_ulong((void *) match_node->key, (void *) key); -} - -/* - * Return an allocated lttng hashtable. - */ -struct lttng_ht *lttng_ht_new(unsigned long size, int type) -{ - struct lttng_ht *ht; - - /* Test size */ - if (!size) - size = DEFAULT_HT_SIZE; - - ht = zmalloc(sizeof(*ht)); - if (ht == NULL) { - PERROR("zmalloc lttng_ht"); - goto error; - } - - ht->ht = cds_lfht_new(size, min_hash_alloc_size, max_hash_buckets_size, - CDS_LFHT_AUTO_RESIZE, NULL); - /* - * There is already an assert in the RCU hashtable code so if the ht is - * NULL here there is a *huge* problem. - */ - assert(ht->ht); - - switch (type) { - case LTTNG_HT_TYPE_STRING: - ht->match_fct = match_str; - ht->hash_fct = hash_key_str; - break; - case LTTNG_HT_TYPE_ULONG: - ht->match_fct = match_ulong; - ht->hash_fct = hash_key_ulong; - break; - default: - ERR("Unknown lttng hashtable type %d", type); - goto error; - } - - DBG3("Created hashtable size %lu at %p of type %d", size, ht->ht, type); - - return ht; - -error: - return NULL; -} - -/* - * Free a lttng hashtable. - */ -void lttng_ht_destroy(struct lttng_ht *ht) -{ - int ret; - - ret = cds_lfht_destroy(ht->ht, NULL); - assert(!ret); -} - -/* - * Init lttng ht node string. - */ -void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key) -{ - assert(node); - - node->key = key; - cds_lfht_node_init(&node->node); -} - -/* - * Init lttng ht node unsigned long. - */ -void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node, - unsigned long key) -{ - assert(node); - - node->key = key; - cds_lfht_node_init(&node->node); -} - -/* - * Free lttng ht node string. - */ -void lttng_ht_node_free_str(struct lttng_ht_node_str *node) -{ - assert(node); - free(node); -} - -/* - * Free lttng ht node unsigned long. - */ -void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node) -{ - assert(node); - free(node); -} - -/* - * Lookup function in hashtable. - */ -void lttng_ht_lookup(struct lttng_ht *ht, void *key, - struct lttng_ht_iter *iter) -{ - assert(ht); - assert(ht->ht); - - cds_lfht_lookup(ht->ht, ht->hash_fct(key, HASH_SEED), - ht->match_fct, key, &iter->iter); -} - -/* - * Add unique string node to hashtable. - */ -void lttng_ht_add_unique_str(struct lttng_ht *ht, - struct lttng_ht_node_str *node) -{ - struct cds_lfht_node *node_ptr; - assert(ht); - assert(ht->ht); - assert(node); - - node_ptr = cds_lfht_add_unique(ht->ht, ht->hash_fct(node->key, HASH_SEED), - ht->match_fct, node->key, &node->node); - assert(node_ptr == &node->node); -} - -/* - * Add unique unsigned long node to hashtable. - */ -void lttng_ht_add_unique_ulong(struct lttng_ht *ht, - struct lttng_ht_node_ulong *node) -{ - struct cds_lfht_node *node_ptr; - assert(ht); - assert(ht->ht); - assert(node); - - node_ptr = cds_lfht_add_unique(ht->ht, - ht->hash_fct((void *) node->key, HASH_SEED), ht->match_fct, - (void *) node->key, &node->node); - assert(node_ptr == &node->node); -} - -/* - * Delete node from hashtable. - */ -int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter) -{ - assert(ht); - assert(ht->ht); - assert(iter); - - return cds_lfht_del(ht->ht, iter->iter.node); -} - -/* - * Get first node in the hashtable. - */ -void lttng_ht_get_first(struct lttng_ht *ht, struct lttng_ht_iter *iter) -{ - assert(ht); - assert(ht->ht); - assert(iter); - - cds_lfht_first(ht->ht, &iter->iter); -} - -/* - * Get next node in the hashtable. - */ -void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter) -{ - assert(ht); - assert(ht->ht); - assert(iter); - - cds_lfht_next(ht->ht, &iter->iter); -} - -/* - * Return the number of nodes in the hashtable. - */ -unsigned long lttng_ht_get_count(struct lttng_ht *ht) -{ - long scb, sca; - unsigned long count; - - assert(ht); - assert(ht->ht); - - cds_lfht_count_nodes(ht->ht, &scb, &count, &sca); - - return count; -} - -/* - * Return lttng ht string node from iterator. - */ -struct lttng_ht_node_str *lttng_ht_iter_get_node_str( - struct lttng_ht_iter *iter) -{ - struct cds_lfht_node *node; - - assert(iter); - node = cds_lfht_iter_get_node(&iter->iter); - if (!node) { - return NULL; - } - return caa_container_of(node, struct lttng_ht_node_str, node); -} - -/* - * Return lttng ht unsigned long node from iterator. - */ -struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong( - struct lttng_ht_iter *iter) -{ - struct cds_lfht_node *node; - - assert(iter); - node = cds_lfht_iter_get_node(&iter->iter); - if (!node) { - return NULL; - } - return caa_container_of(node, struct lttng_ht_node_ulong, node); -} diff --git a/liblttng-ht/rculfhash-internal.h b/liblttng-ht/rculfhash-internal.h deleted file mode 100644 index cb13ffa73..000000000 --- a/liblttng-ht/rculfhash-internal.h +++ /dev/null @@ -1,177 +0,0 @@ -#ifndef _URCU_RCULFHASH_INTERNAL_H -#define _URCU_RCULFHASH_INTERNAL_H - -/* - * urcu/rculfhash-internal.h - * - * Internal header for Lock-Free RCU Hash Table - * - * Copyright 2011 - Mathieu Desnoyers - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "rculfhash.h" - -#ifdef DEBUG -#define dbg_printf(fmt, args...) printf("[debug rculfhash] " fmt, ## args) -#else -#define dbg_printf(fmt, args...) -#endif - -#if (CAA_BITS_PER_LONG == 32) -#define MAX_TABLE_ORDER 32 -#else -#define MAX_TABLE_ORDER 64 -#endif - -#define MAX_CHUNK_TABLE (1UL << 10) - -#ifndef min -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef max -#define max(a, b) ((a) > (b) ? (a) : (b)) -#endif - -struct ht_items_count; - -/* - * cds_lfht: Top-level data structure representing a lock-free hash - * table. Defined in the implementation file to make it be an opaque - * cookie to users. - * - * The fields used in fast-paths are placed near the end of the - * structure, because we need to have a variable-sized union to contain - * the mm plugin fields, which are used in the fast path. - */ -struct cds_lfht { - /* Initial configuration items */ - unsigned long max_nr_buckets; - const struct cds_lfht_mm_type *mm; /* memory management plugin */ - const struct rcu_flavor_struct *flavor; /* RCU flavor */ - - long count; /* global approximate item count */ - - /* - * We need to put the work threads offline (QSBR) when taking this - * mutex, because we use synchronize_rcu within this mutex critical - * section, which waits on read-side critical sections, and could - * therefore cause grace-period deadlock if we hold off RCU G.P. - * completion. - */ - pthread_mutex_t resize_mutex; /* resize mutex: add/del mutex */ - pthread_attr_t *resize_attr; /* Resize threads attributes */ - unsigned int in_progress_resize, in_progress_destroy; - unsigned long resize_target; - int resize_initiated; - - /* - * Variables needed for add and remove fast-paths. - */ - int flags; - unsigned long min_alloc_buckets_order; - unsigned long min_nr_alloc_buckets; - struct ht_items_count *split_count; /* split item count */ - - /* - * Variables needed for the lookup, add and remove fast-paths. - */ - unsigned long size; /* always a power of 2, shared (RCU) */ - /* - * bucket_at pointer is kept here to skip the extra level of - * dereference needed to get to "mm" (this is a fast-path). - */ - struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht, - unsigned long index); - /* - * Dynamic length "tbl_chunk" needs to be at the end of - * cds_lfht. - */ - union { - /* - * Contains the per order-index-level bucket node table. - * The size of each bucket node table is half the number - * of hashes contained in this order (except for order 0). - * The minimum allocation buckets size parameter allows - * combining the bucket node arrays of the lowermost - * levels to improve cache locality for small index orders. - */ - struct cds_lfht_node *tbl_order[MAX_TABLE_ORDER]; - - /* - * Contains the bucket node chunks. The size of each - * bucket node chunk is ->min_alloc_size (we avoid to - * allocate chunks with different size). Chunks improve - * cache locality for small index orders, and are more - * friendly with environments where allocation of large - * contiguous memory areas is challenging due to memory - * fragmentation concerns or inability to use virtual - * memory addressing. - */ - struct cds_lfht_node *tbl_chunk[0]; - - /* - * Memory mapping with room for all possible buckets. - * Their memory is allocated when needed. - */ - struct cds_lfht_node *tbl_mmap; - }; - /* - * End of variables needed for the lookup, add and remove - * fast-paths. - */ -}; - -extern unsigned int cds_lfht_fls_ulong(unsigned long x); -extern int cds_lfht_get_count_order_ulong(unsigned long x); - -#ifdef POISON_FREE -#define poison_free(ptr) \ - do { \ - if (ptr) { \ - memset(ptr, 0x42, sizeof(*(ptr))); \ - free(ptr); \ - } \ - } while (0) -#else -#define poison_free(ptr) free(ptr) -#endif - -static inline -struct cds_lfht *__default_alloc_cds_lfht( - const struct cds_lfht_mm_type *mm, - unsigned long cds_lfht_size, - unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets) -{ - struct cds_lfht *ht; - - ht = calloc(1, cds_lfht_size); - assert(ht); - - ht->mm = mm; - ht->bucket_at = mm->bucket_at; - ht->min_nr_alloc_buckets = min_nr_alloc_buckets; - ht->min_alloc_buckets_order = - cds_lfht_get_count_order_ulong(min_nr_alloc_buckets); - ht->max_nr_buckets = max_nr_buckets; - - return ht; -} - -#endif /* _URCU_RCULFHASH_INTERNAL_H */ diff --git a/liblttng-ht/rculfhash-mm-chunk.c b/liblttng-ht/rculfhash-mm-chunk.c deleted file mode 100644 index 7204831e5..000000000 --- a/liblttng-ht/rculfhash-mm-chunk.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * rculfhash-mm-chunk.c - * - * Chunk based memory management for Lock-Free RCU Hash Table - * - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include "rculfhash-internal.h" - -static -void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) { - ht->tbl_chunk[0] = calloc(ht->min_nr_alloc_buckets, - sizeof(struct cds_lfht_node)); - assert(ht->tbl_chunk[0]); - } else if (order > ht->min_alloc_buckets_order) { - unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order); - - for (i = len; i < 2 * len; i++) { - ht->tbl_chunk[i] = calloc(ht->min_nr_alloc_buckets, - sizeof(struct cds_lfht_node)); - assert(ht->tbl_chunk[i]); - } - } - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -/* - * cds_lfht_free_bucket_table() should be called with decreasing order. - * When cds_lfht_free_bucket_table(0) is called, it means the whole - * lfht is destroyed. - */ -static -void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) - poison_free(ht->tbl_chunk[0]); - else if (order > ht->min_alloc_buckets_order) { - unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order); - - for (i = len; i < 2 * len; i++) - poison_free(ht->tbl_chunk[i]); - } - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -static -struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) -{ - unsigned long chunk, offset; - - chunk = index >> ht->min_alloc_buckets_order; - offset = index & (ht->min_nr_alloc_buckets - 1); - return &ht->tbl_chunk[chunk][offset]; -} - -static -struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets) -{ - unsigned long nr_chunks, cds_lfht_size; - - min_nr_alloc_buckets = max(min_nr_alloc_buckets, - max_nr_buckets / MAX_CHUNK_TABLE); - nr_chunks = max_nr_buckets / min_nr_alloc_buckets; - cds_lfht_size = offsetof(struct cds_lfht, tbl_chunk) + - sizeof(struct cds_lfht_node *) * nr_chunks; - cds_lfht_size = max(cds_lfht_size, sizeof(struct cds_lfht)); - - return __default_alloc_cds_lfht( - &cds_lfht_mm_chunk, cds_lfht_size, - min_nr_alloc_buckets, max_nr_buckets); -} - -const struct cds_lfht_mm_type cds_lfht_mm_chunk = { - .alloc_cds_lfht = alloc_cds_lfht, - .alloc_bucket_table = cds_lfht_alloc_bucket_table, - .free_bucket_table = cds_lfht_free_bucket_table, - .bucket_at = bucket_at, -}; diff --git a/liblttng-ht/rculfhash-mm-mmap.c b/liblttng-ht/rculfhash-mm-mmap.c deleted file mode 100644 index 4554ed600..000000000 --- a/liblttng-ht/rculfhash-mm-mmap.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * rculfhash-mm-mmap.c - * - * mmap/reservation based memory management for Lock-Free RCU Hash Table - * - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include "rculfhash-internal.h" - -/* reserve inaccessible memory space without allocation any memory */ -static void *memory_map(size_t length) -{ - void *ret = mmap(NULL, length, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - assert(ret != MAP_FAILED); - return ret; -} - -static void memory_unmap(void *ptr, size_t length) -{ - int ret __attribute__((unused)); - - ret = munmap(ptr, length); - - assert(ret == 0); -} - -static void memory_populate(void *ptr, size_t length) -{ - void *ret __attribute__((unused)); - - ret = mmap(ptr, length, PROT_READ | PROT_WRITE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - assert(ret == ptr); -} - -/* - * Discard garbage memory and avoid system save it when try to swap it out. - * Make it still reserved, inaccessible. - */ -static void memory_discard(void *ptr, size_t length) -{ - void *ret __attribute__((unused)); - - ret = mmap(ptr, length, PROT_NONE, - MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - assert(ret == ptr); -} - -static -void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) { - if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) { - /* small table */ - ht->tbl_mmap = calloc(ht->max_nr_buckets, - sizeof(*ht->tbl_mmap)); - assert(ht->tbl_mmap); - return; - } - /* large table */ - ht->tbl_mmap = memory_map(ht->max_nr_buckets - * sizeof(*ht->tbl_mmap)); - memory_populate(ht->tbl_mmap, - ht->min_nr_alloc_buckets * sizeof(*ht->tbl_mmap)); - } else if (order > ht->min_alloc_buckets_order) { - /* large table */ - unsigned long len = 1UL << (order - 1); - - assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets); - memory_populate(ht->tbl_mmap + len, - len * sizeof(*ht->tbl_mmap)); - } - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -/* - * cds_lfht_free_bucket_table() should be called with decreasing order. - * When cds_lfht_free_bucket_table(0) is called, it means the whole - * lfht is destroyed. - */ -static -void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) { - if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) { - /* small table */ - poison_free(ht->tbl_mmap); - return; - } - /* large table */ - memory_unmap(ht->tbl_mmap, - ht->max_nr_buckets * sizeof(*ht->tbl_mmap)); - } else if (order > ht->min_alloc_buckets_order) { - /* large table */ - unsigned long len = 1UL << (order - 1); - - assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets); - memory_discard(ht->tbl_mmap + len, len * sizeof(*ht->tbl_mmap)); - } - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -static -struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) -{ - return &ht->tbl_mmap[index]; -} - -static -struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets) -{ - unsigned long page_bucket_size; - - page_bucket_size = getpagesize() / sizeof(struct cds_lfht_node); - if (max_nr_buckets <= page_bucket_size) { - /* small table */ - min_nr_alloc_buckets = max_nr_buckets; - } else { - /* large table */ - min_nr_alloc_buckets = max(min_nr_alloc_buckets, - page_bucket_size); - } - - return __default_alloc_cds_lfht( - &cds_lfht_mm_mmap, sizeof(struct cds_lfht), - min_nr_alloc_buckets, max_nr_buckets); -} - -const struct cds_lfht_mm_type cds_lfht_mm_mmap = { - .alloc_cds_lfht = alloc_cds_lfht, - .alloc_bucket_table = cds_lfht_alloc_bucket_table, - .free_bucket_table = cds_lfht_free_bucket_table, - .bucket_at = bucket_at, -}; diff --git a/liblttng-ht/rculfhash-mm-order.c b/liblttng-ht/rculfhash-mm-order.c deleted file mode 100644 index 6e3d29bb3..000000000 --- a/liblttng-ht/rculfhash-mm-order.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * rculfhash-mm-order.c - * - * Order based memory management for Lock-Free RCU Hash Table - * - * Copyright 2011 - Mathieu Desnoyers - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "rculfhash-internal.h" - -static -void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) { - ht->tbl_order[0] = calloc(ht->min_nr_alloc_buckets, - sizeof(struct cds_lfht_node)); - assert(ht->tbl_order[0]); - } else if (order > ht->min_alloc_buckets_order) { - ht->tbl_order[order] = calloc(1UL << (order -1), - sizeof(struct cds_lfht_node)); - assert(ht->tbl_order[order]); - } - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -/* - * cds_lfht_free_bucket_table() should be called with decreasing order. - * When cds_lfht_free_bucket_table(0) is called, it means the whole - * lfht is destroyed. - */ -static -void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - if (order == 0) - poison_free(ht->tbl_order[0]); - else if (order > ht->min_alloc_buckets_order) - poison_free(ht->tbl_order[order]); - /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ -} - -static -struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) -{ - unsigned long order; - - if (index < ht->min_nr_alloc_buckets) { - dbg_printf("bucket index %lu order 0 aridx 0\n", index); - return &ht->tbl_order[0][index]; - } - /* - * equivalent to cds_lfht_get_count_order_ulong(index + 1), but - * optimizes away the non-existing 0 special-case for - * cds_lfht_get_count_order_ulong. - */ - order = cds_lfht_fls_ulong(index); - dbg_printf("bucket index %lu order %lu aridx %lu\n", - index, order, index & ((1UL << (order - 1)) - 1)); - return &ht->tbl_order[order][index & ((1UL << (order - 1)) - 1)]; -} - -static -struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets) -{ - return __default_alloc_cds_lfht( - &cds_lfht_mm_order, sizeof(struct cds_lfht), - min_nr_alloc_buckets, max_nr_buckets); -} - -const struct cds_lfht_mm_type cds_lfht_mm_order = { - .alloc_cds_lfht = alloc_cds_lfht, - .alloc_bucket_table = cds_lfht_alloc_bucket_table, - .free_bucket_table = cds_lfht_free_bucket_table, - .bucket_at = bucket_at, -}; diff --git a/liblttng-ht/rculfhash.c b/liblttng-ht/rculfhash.c deleted file mode 100644 index 840de351b..000000000 --- a/liblttng-ht/rculfhash.c +++ /dev/null @@ -1,1832 +0,0 @@ -/* - * rculfhash.c - * - * Userspace RCU library - Lock-Free Resizable RCU Hash Table - * - * Copyright 2010-2011 - Mathieu Desnoyers - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* - * Based on the following articles: - * - Ori Shalev and Nir Shavit. Split-ordered lists: Lock-free - * extensible hash tables. J. ACM 53, 3 (May 2006), 379-405. - * - Michael, M. M. High performance dynamic lock-free hash tables - * and list-based sets. In Proceedings of the fourteenth annual ACM - * symposium on Parallel algorithms and architectures, ACM Press, - * (2002), 73-82. - * - * Some specificities of this Lock-Free Resizable RCU Hash Table - * implementation: - * - * - RCU read-side critical section allows readers to perform hash - * table lookups and use the returned objects safely by delaying - * memory reclaim of a grace period. - * - Add and remove operations are lock-free, and do not need to - * allocate memory. They need to be executed within RCU read-side - * critical section to ensure the objects they read are valid and to - * deal with the cmpxchg ABA problem. - * - add and add_unique operations are supported. add_unique checks if - * the node key already exists in the hash table. It ensures no key - * duplicata exists. - * - The resize operation executes concurrently with add/remove/lookup. - * - Hash table nodes are contained within a split-ordered list. This - * list is ordered by incrementing reversed-bits-hash value. - * - An index of bucket nodes is kept. These bucket nodes are the hash - * table "buckets", and they are also chained together in the - * split-ordered list, which allows recursive expansion. - * - The resize operation for small tables only allows expanding the hash table. - * It is triggered automatically by detecting long chains in the add - * operation. - * - The resize operation for larger tables (and available through an - * API) allows both expanding and shrinking the hash table. - * - Split-counters are used to keep track of the number of - * nodes within the hash table for automatic resize triggering. - * - Resize operation initiated by long chain detection is executed by a - * call_rcu thread, which keeps lock-freedom of add and remove. - * - Resize operations are protected by a mutex. - * - The removal operation is split in two parts: first, a "removed" - * flag is set in the next pointer within the node to remove. Then, - * a "garbage collection" is performed in the bucket containing the - * removed node (from the start of the bucket up to the removed node). - * All encountered nodes with "removed" flag set in their next - * pointers are removed from the linked-list. If the cmpxchg used for - * removal fails (due to concurrent garbage-collection or concurrent - * add), we retry from the beginning of the bucket. This ensures that - * the node with "removed" flag set is removed from the hash table - * (not visible to lookups anymore) before the RCU read-side critical - * section held across removal ends. Furthermore, this ensures that - * the node with "removed" flag set is removed from the linked-list - * before its memory is reclaimed. Only the thread which removal - * successfully set the "removed" flag (with a cmpxchg) into a node's - * next pointer is considered to have succeeded its removal (and thus - * owns the node to reclaim). Because we garbage-collect starting from - * an invariant node (the start-of-bucket bucket node) up to the - * "removed" node (or find a reverse-hash that is higher), we are sure - * that a successful traversal of the chain leads to a chain that is - * present in the linked-list (the start node is never removed) and - * that is does not contain the "removed" node anymore, even if - * concurrent delete/add operations are changing the structure of the - * list concurrently. - * - The add operation performs gargage collection of buckets if it - * encounters nodes with removed flag set in the bucket where it wants - * to add its new node. This ensures lock-freedom of add operation by - * helping the remover unlink nodes from the list rather than to wait - * for it do to so. - * - A RCU "order table" indexed by log2(hash index) is copied and - * expanded by the resize operation. This order table allows finding - * the "bucket node" tables. - * - There is one bucket node table per hash index order. The size of - * each bucket node table is half the number of hashes contained in - * this order (except for order 0). - * - synchronzie_rcu is used to garbage-collect the old bucket node table. - * - The per-order bucket node tables contain a compact version of the - * hash table nodes. These tables are invariant after they are - * populated into the hash table. - * - * Bucket node tables: - * - * hash table hash table the last all bucket node tables - * order size bucket node 0 1 2 3 4 5 6(index) - * table size - * 0 1 1 1 - * 1 2 1 1 1 - * 2 4 2 1 1 2 - * 3 8 4 1 1 2 4 - * 4 16 8 1 1 2 4 8 - * 5 32 16 1 1 2 4 8 16 - * 6 64 32 1 1 2 4 8 16 32 - * - * When growing/shrinking, we only focus on the last bucket node table - * which size is (!order ? 1 : (1 << (order -1))). - * - * Example for growing/shrinking: - * grow hash table from order 5 to 6: init the index=6 bucket node table - * shrink hash table from order 6 to 5: fini the index=6 bucket node table - * - * A bit of ascii art explanation: - * - * Order index is the off-by-one compare to the actual power of 2 because - * we use index 0 to deal with the 0 special-case. - * - * This shows the nodes for a small table ordered by reversed bits: - * - * bits reverse - * 0 000 000 - * 4 100 001 - * 2 010 010 - * 6 110 011 - * 1 001 100 - * 5 101 101 - * 3 011 110 - * 7 111 111 - * - * This shows the nodes in order of non-reversed bits, linked by - * reversed-bit order. - * - * order bits reverse - * 0 0 000 000 - * 1 | 1 001 100 <- - * 2 | | 2 010 010 <- | - * | | | 3 011 110 | <- | - * 3 -> | | | 4 100 001 | | - * -> | | 5 101 101 | - * -> | 6 110 011 - * -> 7 111 111 - */ - -#define _LGPL_SOURCE -#include -#include -#include -#include -#include -#include - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include - -#include "rculfhash.h" -#include "rculfhash-internal.h" -#include "urcu-flavor.h" - -/* - * Split-counters lazily update the global counter each 1024 - * addition/removal. It automatically keeps track of resize required. - * We use the bucket length as indicator for need to expand for small - * tables and machines lacking per-cpu data suppport. - */ -#define COUNT_COMMIT_ORDER 10 -#define DEFAULT_SPLIT_COUNT_MASK 0xFUL -#define CHAIN_LEN_TARGET 1 -#define CHAIN_LEN_RESIZE_THRESHOLD 3 - -/* - * Define the minimum table size. - */ -#define MIN_TABLE_ORDER 0 -#define MIN_TABLE_SIZE (1UL << MIN_TABLE_ORDER) - -/* - * Minimum number of bucket nodes to touch per thread to parallelize grow/shrink. - */ -#define MIN_PARTITION_PER_THREAD_ORDER 12 -#define MIN_PARTITION_PER_THREAD (1UL << MIN_PARTITION_PER_THREAD_ORDER) - -/* - * The removed flag needs to be updated atomically with the pointer. - * It indicates that no node must attach to the node scheduled for - * removal, and that node garbage collection must be performed. - * The bucket flag does not require to be updated atomically with the - * pointer, but it is added as a pointer low bit flag to save space. - */ -#define REMOVED_FLAG (1UL << 0) -#define BUCKET_FLAG (1UL << 1) -#define REMOVAL_OWNER_FLAG (1UL << 2) -#define FLAGS_MASK ((1UL << 3) - 1) - -/* Value of the end pointer. Should not interact with flags. */ -#define END_VALUE NULL - -DEFINE_RCU_FLAVOR(rcu_flavor); - -/* - * ht_items_count: Split-counters counting the number of node addition - * and removal in the table. Only used if the CDS_LFHT_ACCOUNTING flag - * is set at hash table creation. - * - * These are free-running counters, never reset to zero. They count the - * number of add/remove, and trigger every (1 << COUNT_COMMIT_ORDER) - * operations to update the global counter. We choose a power-of-2 value - * for the trigger to deal with 32 or 64-bit overflow of the counter. - */ -struct ht_items_count { - unsigned long add, del; -} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); - -/* - * rcu_resize_work: Contains arguments passed to RCU worker thread - * responsible for performing lazy resize. - */ -struct rcu_resize_work { - struct rcu_head head; - struct cds_lfht *ht; -}; - -/* - * partition_resize_work: Contains arguments passed to worker threads - * executing the hash table resize on partitions of the hash table - * assigned to each processor's worker thread. - */ -struct partition_resize_work { - pthread_t thread_id; - struct cds_lfht *ht; - unsigned long i, start, len; - void (*fct)(struct cds_lfht *ht, unsigned long i, - unsigned long start, unsigned long len); -}; - -/* - * Algorithm to reverse bits in a word by lookup table, extended to - * 64-bit words. - * Source: - * http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable - * Originally from Public Domain. - */ - -static const uint8_t BitReverseTable256[256] = -{ -#define R2(n) (n), (n) + 2*64, (n) + 1*64, (n) + 3*64 -#define R4(n) R2(n), R2((n) + 2*16), R2((n) + 1*16), R2((n) + 3*16) -#define R6(n) R4(n), R4((n) + 2*4 ), R4((n) + 1*4 ), R4((n) + 3*4 ) - R6(0), R6(2), R6(1), R6(3) -}; -#undef R2 -#undef R4 -#undef R6 - -static -uint8_t bit_reverse_u8(uint8_t v) -{ - return BitReverseTable256[v]; -} - -static __attribute__((unused)) -uint32_t bit_reverse_u32(uint32_t v) -{ - return ((uint32_t) bit_reverse_u8(v) << 24) | - ((uint32_t) bit_reverse_u8(v >> 8) << 16) | - ((uint32_t) bit_reverse_u8(v >> 16) << 8) | - ((uint32_t) bit_reverse_u8(v >> 24)); -} - -static __attribute__((unused)) -uint64_t bit_reverse_u64(uint64_t v) -{ - return ((uint64_t) bit_reverse_u8(v) << 56) | - ((uint64_t) bit_reverse_u8(v >> 8) << 48) | - ((uint64_t) bit_reverse_u8(v >> 16) << 40) | - ((uint64_t) bit_reverse_u8(v >> 24) << 32) | - ((uint64_t) bit_reverse_u8(v >> 32) << 24) | - ((uint64_t) bit_reverse_u8(v >> 40) << 16) | - ((uint64_t) bit_reverse_u8(v >> 48) << 8) | - ((uint64_t) bit_reverse_u8(v >> 56)); -} - -static -unsigned long bit_reverse_ulong(unsigned long v) -{ -#if (CAA_BITS_PER_LONG == 32) - return bit_reverse_u32(v); -#else - return bit_reverse_u64(v); -#endif -} - -/* - * fls: returns the position of the most significant bit. - * Returns 0 if no bit is set, else returns the position of the most - * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). - */ -#if defined(__i386) || defined(__x86_64) -static inline -unsigned int fls_u32(uint32_t x) -{ - int r; - - asm("bsrl %1,%0\n\t" - "jnz 1f\n\t" - "movl $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U32 -#endif - -#if defined(__x86_64) -static inline -unsigned int fls_u64(uint64_t x) -{ - long r; - - asm("bsrq %1,%0\n\t" - "jnz 1f\n\t" - "movq $-1,%0\n\t" - "1:\n\t" - : "=r" (r) : "rm" (x)); - return r + 1; -} -#define HAS_FLS_U64 -#endif - -#ifndef HAS_FLS_U64 -static __attribute__((unused)) -unsigned int fls_u64(uint64_t x) -{ - unsigned int r = 64; - - if (!x) - return 0; - - if (!(x & 0xFFFFFFFF00000000ULL)) { - x <<= 32; - r -= 32; - } - if (!(x & 0xFFFF000000000000ULL)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF00000000000000ULL)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF000000000000000ULL)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC000000000000000ULL)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x8000000000000000ULL)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -#ifndef HAS_FLS_U32 -static __attribute__((unused)) -unsigned int fls_u32(uint32_t x) -{ - unsigned int r = 32; - - if (!x) - return 0; - if (!(x & 0xFFFF0000U)) { - x <<= 16; - r -= 16; - } - if (!(x & 0xFF000000U)) { - x <<= 8; - r -= 8; - } - if (!(x & 0xF0000000U)) { - x <<= 4; - r -= 4; - } - if (!(x & 0xC0000000U)) { - x <<= 2; - r -= 2; - } - if (!(x & 0x80000000U)) { - x <<= 1; - r -= 1; - } - return r; -} -#endif - -unsigned int cds_lfht_fls_ulong(unsigned long x) -{ -#if (CAA_BITS_PER_LONG == 32) - return fls_u32(x); -#else - return fls_u64(x); -#endif -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int cds_lfht_get_count_order_u32(uint32_t x) -{ - if (!x) - return -1; - - return fls_u32(x - 1); -} - -/* - * Return the minimum order for which x <= (1UL << order). - * Return -1 if x is 0. - */ -int cds_lfht_get_count_order_ulong(unsigned long x) -{ - if (!x) - return -1; - - return cds_lfht_fls_ulong(x - 1); -} - -static -void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth); - -static -void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size, - unsigned long count); - -static long nr_cpus_mask = -1; -static long split_count_mask = -1; - -#if defined(HAVE_SYSCONF) -static void ht_init_nr_cpus_mask(void) -{ - long maxcpus; - - maxcpus = sysconf(_SC_NPROCESSORS_CONF); - if (maxcpus <= 0) { - nr_cpus_mask = -2; - return; - } - /* - * round up number of CPUs to next power of two, so we - * can use & for modulo. - */ - maxcpus = 1UL << cds_lfht_get_count_order_ulong(maxcpus); - nr_cpus_mask = maxcpus - 1; -} -#else /* #if defined(HAVE_SYSCONF) */ -static void ht_init_nr_cpus_mask(void) -{ - nr_cpus_mask = -2; -} -#endif /* #else #if defined(HAVE_SYSCONF) */ - -static -void alloc_split_items_count(struct cds_lfht *ht) -{ - struct ht_items_count *count; - - if (nr_cpus_mask == -1) { - ht_init_nr_cpus_mask(); - if (nr_cpus_mask < 0) - split_count_mask = DEFAULT_SPLIT_COUNT_MASK; - else - split_count_mask = nr_cpus_mask; - } - - assert(split_count_mask >= 0); - - if (ht->flags & CDS_LFHT_ACCOUNTING) { - ht->split_count = calloc(split_count_mask + 1, sizeof(*count)); - assert(ht->split_count); - } else { - ht->split_count = NULL; - } -} - -static -void free_split_items_count(struct cds_lfht *ht) -{ - poison_free(ht->split_count); -} - -#if defined(HAVE_SCHED_GETCPU) -static -int ht_get_split_count_index(unsigned long hash) -{ - int cpu; - - assert(split_count_mask >= 0); - cpu = sched_getcpu(); - if (caa_unlikely(cpu < 0)) - return hash & split_count_mask; - else - return cpu & split_count_mask; -} -#else /* #if defined(HAVE_SCHED_GETCPU) */ -static -int ht_get_split_count_index(unsigned long hash) -{ - return hash & split_count_mask; -} -#endif /* #else #if defined(HAVE_SCHED_GETCPU) */ - -static -void ht_count_add(struct cds_lfht *ht, unsigned long size, unsigned long hash) -{ - unsigned long split_count; - int index; - long count; - - if (caa_unlikely(!ht->split_count)) - return; - index = ht_get_split_count_index(hash); - split_count = uatomic_add_return(&ht->split_count[index].add, 1); - if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1))) - return; - /* Only if number of add multiple of 1UL << COUNT_COMMIT_ORDER */ - - dbg_printf("add split count %lu\n", split_count); - count = uatomic_add_return(&ht->count, - 1UL << COUNT_COMMIT_ORDER); - if (caa_likely(count & (count - 1))) - return; - /* Only if global count is power of 2 */ - - if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) < size) - return; - dbg_printf("add set global %ld\n", count); - cds_lfht_resize_lazy_count(ht, size, - count >> (CHAIN_LEN_TARGET - 1)); -} - -static -void ht_count_del(struct cds_lfht *ht, unsigned long size, unsigned long hash) -{ - unsigned long split_count; - int index; - long count; - - if (caa_unlikely(!ht->split_count)) - return; - index = ht_get_split_count_index(hash); - split_count = uatomic_add_return(&ht->split_count[index].del, 1); - if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1))) - return; - /* Only if number of deletes multiple of 1UL << COUNT_COMMIT_ORDER */ - - dbg_printf("del split count %lu\n", split_count); - count = uatomic_add_return(&ht->count, - -(1UL << COUNT_COMMIT_ORDER)); - if (caa_likely(count & (count - 1))) - return; - /* Only if global count is power of 2 */ - - if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) >= size) - return; - dbg_printf("del set global %ld\n", count); - /* - * Don't shrink table if the number of nodes is below a - * certain threshold. - */ - if (count < (1UL << COUNT_COMMIT_ORDER) * (split_count_mask + 1)) - return; - cds_lfht_resize_lazy_count(ht, size, - count >> (CHAIN_LEN_TARGET - 1)); -} - -static -void check_resize(struct cds_lfht *ht, unsigned long size, uint32_t chain_len) -{ - unsigned long count; - - if (!(ht->flags & CDS_LFHT_AUTO_RESIZE)) - return; - count = uatomic_read(&ht->count); - /* - * Use bucket-local length for small table expand and for - * environments lacking per-cpu data support. - */ - if (count >= (1UL << COUNT_COMMIT_ORDER)) - return; - if (chain_len > 100) - dbg_printf("WARNING: large chain length: %u.\n", - chain_len); - if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD) - cds_lfht_resize_lazy_grow(ht, size, - cds_lfht_get_count_order_u32(chain_len - (CHAIN_LEN_TARGET - 1))); -} - -static -struct cds_lfht_node *clear_flag(struct cds_lfht_node *node) -{ - return (struct cds_lfht_node *) (((unsigned long) node) & ~FLAGS_MASK); -} - -static -int is_removed(struct cds_lfht_node *node) -{ - return ((unsigned long) node) & REMOVED_FLAG; -} - -static -struct cds_lfht_node *flag_removed(struct cds_lfht_node *node) -{ - return (struct cds_lfht_node *) (((unsigned long) node) | REMOVED_FLAG); -} - -static -int is_bucket(struct cds_lfht_node *node) -{ - return ((unsigned long) node) & BUCKET_FLAG; -} - -static -struct cds_lfht_node *flag_bucket(struct cds_lfht_node *node) -{ - return (struct cds_lfht_node *) (((unsigned long) node) | BUCKET_FLAG); -} - -static -int is_removal_owner(struct cds_lfht_node *node) -{ - return ((unsigned long) node) & REMOVAL_OWNER_FLAG; -} - -static -struct cds_lfht_node *flag_removal_owner(struct cds_lfht_node *node) -{ - return (struct cds_lfht_node *) (((unsigned long) node) | REMOVAL_OWNER_FLAG); -} - -static -struct cds_lfht_node *get_end(void) -{ - return (struct cds_lfht_node *) END_VALUE; -} - -static -int is_end(struct cds_lfht_node *node) -{ - return clear_flag(node) == (struct cds_lfht_node *) END_VALUE; -} - -static -unsigned long _uatomic_xchg_monotonic_increase(unsigned long *ptr, - unsigned long v) -{ - unsigned long old1, old2; - - old1 = uatomic_read(ptr); - do { - old2 = old1; - if (old2 >= v) - return old2; - } while ((old1 = uatomic_cmpxchg(ptr, old2, v)) != old2); - return old2; -} - -static -void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - return ht->mm->alloc_bucket_table(ht, order); -} - -/* - * cds_lfht_free_bucket_table() should be called with decreasing order. - * When cds_lfht_free_bucket_table(0) is called, it means the whole - * lfht is destroyed. - */ -static -void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) -{ - return ht->mm->free_bucket_table(ht, order); -} - -static inline -struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) -{ - return ht->bucket_at(ht, index); -} - -static inline -struct cds_lfht_node *lookup_bucket(struct cds_lfht *ht, unsigned long size, - unsigned long hash) -{ - assert(size > 0); - return bucket_at(ht, hash & (size - 1)); -} - -/* - * Remove all logically deleted nodes from a bucket up to a certain node key. - */ -static -void _cds_lfht_gc_bucket(struct cds_lfht_node *bucket, struct cds_lfht_node *node) -{ - struct cds_lfht_node *iter_prev, *iter, *next, *new_next; - - assert(!is_bucket(bucket)); - assert(!is_removed(bucket)); - assert(!is_bucket(node)); - assert(!is_removed(node)); - for (;;) { - iter_prev = bucket; - /* We can always skip the bucket node initially */ - iter = rcu_dereference(iter_prev->next); - assert(!is_removed(iter)); - assert(iter_prev->reverse_hash <= node->reverse_hash); - /* - * We should never be called with bucket (start of chain) - * and logically removed node (end of path compression - * marker) being the actual same node. This would be a - * bug in the algorithm implementation. - */ - assert(bucket != node); - for (;;) { - if (caa_unlikely(is_end(iter))) - return; - if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash)) - return; - next = rcu_dereference(clear_flag(iter)->next); - if (caa_likely(is_removed(next))) - break; - iter_prev = clear_flag(iter); - iter = next; - } - assert(!is_removed(iter)); - if (is_bucket(iter)) - new_next = flag_bucket(clear_flag(next)); - else - new_next = clear_flag(next); - (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next); - } -} - -static -int _cds_lfht_replace(struct cds_lfht *ht, unsigned long size, - struct cds_lfht_node *old_node, - struct cds_lfht_node *old_next, - struct cds_lfht_node *new_node) -{ - struct cds_lfht_node *bucket, *ret_next; - - if (!old_node) /* Return -ENOENT if asked to replace NULL node */ - return -ENOENT; - - assert(!is_removed(old_node)); - assert(!is_bucket(old_node)); - assert(!is_removed(new_node)); - assert(!is_bucket(new_node)); - assert(new_node != old_node); - for (;;) { - /* Insert after node to be replaced */ - if (is_removed(old_next)) { - /* - * Too late, the old node has been removed under us - * between lookup and replace. Fail. - */ - return -ENOENT; - } - assert(old_next == clear_flag(old_next)); - assert(new_node != old_next); - new_node->next = old_next; - /* - * Here is the whole trick for lock-free replace: we add - * the replacement node _after_ the node we want to - * replace by atomically setting its next pointer at the - * same time we set its removal flag. Given that - * the lookups/get next use an iterator aware of the - * next pointer, they will either skip the old node due - * to the removal flag and see the new node, or use - * the old node, but will not see the new one. - * This is a replacement of a node with another node - * that has the same value: we are therefore not - * removing a value from the hash table. - */ - ret_next = uatomic_cmpxchg(&old_node->next, - old_next, flag_removed(new_node)); - if (ret_next == old_next) - break; /* We performed the replacement. */ - old_next = ret_next; - } - - /* - * Ensure that the old node is not visible to readers anymore: - * lookup for the node, and remove it (along with any other - * logically removed node) if found. - */ - bucket = lookup_bucket(ht, size, bit_reverse_ulong(old_node->reverse_hash)); - _cds_lfht_gc_bucket(bucket, new_node); - - assert(is_removed(rcu_dereference(old_node->next))); - return 0; -} - -/* - * A non-NULL unique_ret pointer uses the "add unique" (or uniquify) add - * mode. A NULL unique_ret allows creation of duplicate keys. - */ -static -void _cds_lfht_add(struct cds_lfht *ht, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - unsigned long size, - struct cds_lfht_node *node, - struct cds_lfht_iter *unique_ret, - int bucket_flag) -{ - struct cds_lfht_node *iter_prev, *iter, *next, *new_node, *new_next, - *return_node; - struct cds_lfht_node *bucket; - - assert(!is_bucket(node)); - assert(!is_removed(node)); - bucket = lookup_bucket(ht, size, hash); - for (;;) { - uint32_t chain_len = 0; - - /* - * iter_prev points to the non-removed node prior to the - * insert location. - */ - iter_prev = bucket; - /* We can always skip the bucket node initially */ - iter = rcu_dereference(iter_prev->next); - assert(iter_prev->reverse_hash <= node->reverse_hash); - for (;;) { - if (caa_unlikely(is_end(iter))) - goto insert; - if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash)) - goto insert; - - /* bucket node is the first node of the identical-hash-value chain */ - if (bucket_flag && clear_flag(iter)->reverse_hash == node->reverse_hash) - goto insert; - - next = rcu_dereference(clear_flag(iter)->next); - if (caa_unlikely(is_removed(next))) - goto gc_node; - - /* uniquely add */ - if (unique_ret - && !is_bucket(next) - && clear_flag(iter)->reverse_hash == node->reverse_hash) { - struct cds_lfht_iter d_iter = { .node = node, .next = iter, }; - - /* - * uniquely adding inserts the node as the first - * node of the identical-hash-value node chain. - * - * This semantic ensures no duplicated keys - * should ever be observable in the table - * (including observe one node by one node - * by forward iterations) - */ - cds_lfht_next_duplicate(ht, match, key, &d_iter); - if (!d_iter.node) - goto insert; - - *unique_ret = d_iter; - return; - } - - /* Only account for identical reverse hash once */ - if (iter_prev->reverse_hash != clear_flag(iter)->reverse_hash - && !is_bucket(next)) - check_resize(ht, size, ++chain_len); - iter_prev = clear_flag(iter); - iter = next; - } - - insert: - assert(node != clear_flag(iter)); - assert(!is_removed(iter_prev)); - assert(!is_removed(iter)); - assert(iter_prev != node); - if (!bucket_flag) - node->next = clear_flag(iter); - else - node->next = flag_bucket(clear_flag(iter)); - if (is_bucket(iter)) - new_node = flag_bucket(node); - else - new_node = node; - if (uatomic_cmpxchg(&iter_prev->next, iter, - new_node) != iter) { - continue; /* retry */ - } else { - return_node = node; - goto end; - } - - gc_node: - assert(!is_removed(iter)); - if (is_bucket(iter)) - new_next = flag_bucket(clear_flag(next)); - else - new_next = clear_flag(next); - (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next); - /* retry */ - } -end: - if (unique_ret) { - unique_ret->node = return_node; - /* unique_ret->next left unset, never used. */ - } -} - -static -int _cds_lfht_del(struct cds_lfht *ht, unsigned long size, - struct cds_lfht_node *node) -{ - struct cds_lfht_node *bucket, *next; - - if (!node) /* Return -ENOENT if asked to delete NULL node */ - return -ENOENT; - - /* logically delete the node */ - assert(!is_bucket(node)); - assert(!is_removed(node)); - assert(!is_removal_owner(node)); - - /* - * We are first checking if the node had previously been - * logically removed (this check is not atomic with setting the - * logical removal flag). Return -ENOENT if the node had - * previously been removed. - */ - next = rcu_dereference(node->next); - if (caa_unlikely(is_removed(next))) - return -ENOENT; - assert(!is_bucket(next)); - /* - * We set the REMOVED_FLAG unconditionally. Note that there may - * be more than one concurrent thread setting this flag. - * Knowing which wins the race will be known after the garbage - * collection phase, stay tuned! - */ - uatomic_or(&node->next, REMOVED_FLAG); - /* We performed the (logical) deletion. */ - - /* - * Ensure that the node is not visible to readers anymore: lookup for - * the node, and remove it (along with any other logically removed node) - * if found. - */ - bucket = lookup_bucket(ht, size, bit_reverse_ulong(node->reverse_hash)); - _cds_lfht_gc_bucket(bucket, node); - - assert(is_removed(rcu_dereference(node->next))); - /* - * Last phase: atomically exchange node->next with a version - * having "REMOVAL_OWNER_FLAG" set. If the returned node->next - * pointer did _not_ have "REMOVAL_OWNER_FLAG" set, we now own - * the node and win the removal race. - * It is interesting to note that all "add" paths are forbidden - * to change the next pointer starting from the point where the - * REMOVED_FLAG is set, so here using a read, followed by a - * xchg() suffice to guarantee that the xchg() will ever only - * set the "REMOVAL_OWNER_FLAG" (or change nothing if the flag - * was already set). - */ - if (!is_removal_owner(uatomic_xchg(&node->next, - flag_removal_owner(node->next)))) - return 0; - else - return -ENOENT; -} - -static -void *partition_resize_thread(void *arg) -{ - struct partition_resize_work *work = arg; - - work->ht->flavor->register_thread(); - work->fct(work->ht, work->i, work->start, work->len); - work->ht->flavor->unregister_thread(); - return NULL; -} - -static -void partition_resize_helper(struct cds_lfht *ht, unsigned long i, - unsigned long len, - void (*fct)(struct cds_lfht *ht, unsigned long i, - unsigned long start, unsigned long len)) -{ - unsigned long partition_len; - struct partition_resize_work *work; - int thread, ret; - unsigned long nr_threads; - - /* - * Note: nr_cpus_mask + 1 is always power of 2. - * We spawn just the number of threads we need to satisfy the minimum - * partition size, up to the number of CPUs in the system. - */ - if (nr_cpus_mask > 0) { - nr_threads = min(nr_cpus_mask + 1, - len >> MIN_PARTITION_PER_THREAD_ORDER); - } else { - nr_threads = 1; - } - partition_len = len >> cds_lfht_get_count_order_ulong(nr_threads); - work = calloc(nr_threads, sizeof(*work)); - assert(work); - for (thread = 0; thread < nr_threads; thread++) { - work[thread].ht = ht; - work[thread].i = i; - work[thread].len = partition_len; - work[thread].start = thread * partition_len; - work[thread].fct = fct; - ret = pthread_create(&(work[thread].thread_id), ht->resize_attr, - partition_resize_thread, &work[thread]); - assert(!ret); - } - for (thread = 0; thread < nr_threads; thread++) { - ret = pthread_join(work[thread].thread_id, NULL); - assert(!ret); - } - free(work); -} - -/* - * Holding RCU read lock to protect _cds_lfht_add against memory - * reclaim that could be performed by other call_rcu worker threads (ABA - * problem). - * - * When we reach a certain length, we can split this population phase over - * many worker threads, based on the number of CPUs available in the system. - * This should therefore take care of not having the expand lagging behind too - * many concurrent insertion threads by using the scheduler's ability to - * schedule bucket node population fairly with insertions. - */ -static -void init_table_populate_partition(struct cds_lfht *ht, unsigned long i, - unsigned long start, unsigned long len) -{ - unsigned long j, size = 1UL << (i - 1); - - assert(i > MIN_TABLE_ORDER); - ht->flavor->read_lock(); - for (j = size + start; j < size + start + len; j++) { - struct cds_lfht_node *new_node = bucket_at(ht, j); - - assert(j >= size && j < (size << 1)); - dbg_printf("init populate: order %lu index %lu hash %lu\n", - i, j, j); - new_node->reverse_hash = bit_reverse_ulong(j); - _cds_lfht_add(ht, j, NULL, NULL, size, new_node, NULL, 1); - } - ht->flavor->read_unlock(); -} - -static -void init_table_populate(struct cds_lfht *ht, unsigned long i, - unsigned long len) -{ - assert(nr_cpus_mask != -1); - if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) { - ht->flavor->thread_online(); - init_table_populate_partition(ht, i, 0, len); - ht->flavor->thread_offline(); - return; - } - partition_resize_helper(ht, i, len, init_table_populate_partition); -} - -static -void init_table(struct cds_lfht *ht, - unsigned long first_order, unsigned long last_order) -{ - unsigned long i; - - dbg_printf("init table: first_order %lu last_order %lu\n", - first_order, last_order); - assert(first_order > MIN_TABLE_ORDER); - for (i = first_order; i <= last_order; i++) { - unsigned long len; - - len = 1UL << (i - 1); - dbg_printf("init order %lu len: %lu\n", i, len); - - /* Stop expand if the resize target changes under us */ - if (CMM_LOAD_SHARED(ht->resize_target) < (1UL << i)) - break; - - cds_lfht_alloc_bucket_table(ht, i); - - /* - * Set all bucket nodes reverse hash values for a level and - * link all bucket nodes into the table. - */ - init_table_populate(ht, i, len); - - /* - * Update table size. - */ - cmm_smp_wmb(); /* populate data before RCU size */ - CMM_STORE_SHARED(ht->size, 1UL << i); - - dbg_printf("init new size: %lu\n", 1UL << i); - if (CMM_LOAD_SHARED(ht->in_progress_destroy)) - break; - } -} - -/* - * Holding RCU read lock to protect _cds_lfht_remove against memory - * reclaim that could be performed by other call_rcu worker threads (ABA - * problem). - * For a single level, we logically remove and garbage collect each node. - * - * As a design choice, we perform logical removal and garbage collection on a - * node-per-node basis to simplify this algorithm. We also assume keeping good - * cache locality of the operation would overweight possible performance gain - * that could be achieved by batching garbage collection for multiple levels. - * However, this would have to be justified by benchmarks. - * - * Concurrent removal and add operations are helping us perform garbage - * collection of logically removed nodes. We guarantee that all logically - * removed nodes have been garbage-collected (unlinked) before call_rcu is - * invoked to free a hole level of bucket nodes (after a grace period). - * - * Logical removal and garbage collection can therefore be done in batch or on a - * node-per-node basis, as long as the guarantee above holds. - * - * When we reach a certain length, we can split this removal over many worker - * threads, based on the number of CPUs available in the system. This should - * take care of not letting resize process lag behind too many concurrent - * updater threads actively inserting into the hash table. - */ -static -void remove_table_partition(struct cds_lfht *ht, unsigned long i, - unsigned long start, unsigned long len) -{ - unsigned long j, size = 1UL << (i - 1); - - assert(i > MIN_TABLE_ORDER); - ht->flavor->read_lock(); - for (j = size + start; j < size + start + len; j++) { - struct cds_lfht_node *fini_bucket = bucket_at(ht, j); - struct cds_lfht_node *parent_bucket = bucket_at(ht, j - size); - - assert(j >= size && j < (size << 1)); - dbg_printf("remove entry: order %lu index %lu hash %lu\n", - i, j, j); - /* Set the REMOVED_FLAG to freeze the ->next for gc */ - uatomic_or(&fini_bucket->next, REMOVED_FLAG); - _cds_lfht_gc_bucket(parent_bucket, fini_bucket); - } - ht->flavor->read_unlock(); -} - -static -void remove_table(struct cds_lfht *ht, unsigned long i, unsigned long len) -{ - - assert(nr_cpus_mask != -1); - if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) { - ht->flavor->thread_online(); - remove_table_partition(ht, i, 0, len); - ht->flavor->thread_offline(); - return; - } - partition_resize_helper(ht, i, len, remove_table_partition); -} - -/* - * fini_table() is never called for first_order == 0, which is why - * free_by_rcu_order == 0 can be used as criterion to know if free must - * be called. - */ -static -void fini_table(struct cds_lfht *ht, - unsigned long first_order, unsigned long last_order) -{ - long i; - unsigned long free_by_rcu_order = 0; - - dbg_printf("fini table: first_order %lu last_order %lu\n", - first_order, last_order); - assert(first_order > MIN_TABLE_ORDER); - for (i = last_order; i >= first_order; i--) { - unsigned long len; - - len = 1UL << (i - 1); - dbg_printf("fini order %lu len: %lu\n", i, len); - - /* Stop shrink if the resize target changes under us */ - if (CMM_LOAD_SHARED(ht->resize_target) > (1UL << (i - 1))) - break; - - cmm_smp_wmb(); /* populate data before RCU size */ - CMM_STORE_SHARED(ht->size, 1UL << (i - 1)); - - /* - * We need to wait for all add operations to reach Q.S. (and - * thus use the new table for lookups) before we can start - * releasing the old bucket nodes. Otherwise their lookup will - * return a logically removed node as insert position. - */ - ht->flavor->update_synchronize_rcu(); - if (free_by_rcu_order) - cds_lfht_free_bucket_table(ht, free_by_rcu_order); - - /* - * Set "removed" flag in bucket nodes about to be removed. - * Unlink all now-logically-removed bucket node pointers. - * Concurrent add/remove operation are helping us doing - * the gc. - */ - remove_table(ht, i, len); - - free_by_rcu_order = i; - - dbg_printf("fini new size: %lu\n", 1UL << i); - if (CMM_LOAD_SHARED(ht->in_progress_destroy)) - break; - } - - if (free_by_rcu_order) { - ht->flavor->update_synchronize_rcu(); - cds_lfht_free_bucket_table(ht, free_by_rcu_order); - } -} - -static -void cds_lfht_create_bucket(struct cds_lfht *ht, unsigned long size) -{ - struct cds_lfht_node *prev, *node; - unsigned long order, len, i; - - cds_lfht_alloc_bucket_table(ht, 0); - - dbg_printf("create bucket: order 0 index 0 hash 0\n"); - node = bucket_at(ht, 0); - node->next = flag_bucket(get_end()); - node->reverse_hash = 0; - - for (order = 1; order < cds_lfht_get_count_order_ulong(size) + 1; order++) { - len = 1UL << (order - 1); - cds_lfht_alloc_bucket_table(ht, order); - - for (i = 0; i < len; i++) { - /* - * Now, we are trying to init the node with the - * hash=(len+i) (which is also a bucket with the - * index=(len+i)) and insert it into the hash table, - * so this node has to be inserted after the bucket - * with the index=(len+i)&(len-1)=i. And because there - * is no other non-bucket node nor bucket node with - * larger index/hash inserted, so the bucket node - * being inserted should be inserted directly linked - * after the bucket node with index=i. - */ - prev = bucket_at(ht, i); - node = bucket_at(ht, len + i); - - dbg_printf("create bucket: order %lu index %lu hash %lu\n", - order, len + i, len + i); - node->reverse_hash = bit_reverse_ulong(len + i); - - /* insert after prev */ - assert(is_bucket(prev->next)); - node->next = prev->next; - prev->next = flag_bucket(node); - } - } -} - -struct cds_lfht *_cds_lfht_new(unsigned long init_size, - unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets, - int flags, - const struct cds_lfht_mm_type *mm, - const struct rcu_flavor_struct *flavor, - pthread_attr_t *attr) -{ - struct cds_lfht *ht; - unsigned long order; - - /* min_nr_alloc_buckets must be power of two */ - if (!min_nr_alloc_buckets || (min_nr_alloc_buckets & (min_nr_alloc_buckets - 1))) - return NULL; - - /* init_size must be power of two */ - if (!init_size || (init_size & (init_size - 1))) - return NULL; - - /* - * Memory management plugin default. - */ - if (!mm) { - if (CAA_BITS_PER_LONG > 32 - && max_nr_buckets - && max_nr_buckets <= (1ULL << 32)) { - /* - * For 64-bit architectures, with max number of - * buckets small enough not to use the entire - * 64-bit memory mapping space (and allowing a - * fair number of hash table instances), use the - * mmap allocator, which is faster than the - * order allocator. - */ - mm = &cds_lfht_mm_mmap; - } else { - /* - * The fallback is to use the order allocator. - */ - mm = &cds_lfht_mm_order; - } - } - - /* max_nr_buckets == 0 for order based mm means infinite */ - if (mm == &cds_lfht_mm_order && !max_nr_buckets) - max_nr_buckets = 1UL << (MAX_TABLE_ORDER - 1); - - /* max_nr_buckets must be power of two */ - if (!max_nr_buckets || (max_nr_buckets & (max_nr_buckets - 1))) - return NULL; - - min_nr_alloc_buckets = max(min_nr_alloc_buckets, MIN_TABLE_SIZE); - init_size = max(init_size, MIN_TABLE_SIZE); - max_nr_buckets = max(max_nr_buckets, min_nr_alloc_buckets); - init_size = min(init_size, max_nr_buckets); - - ht = mm->alloc_cds_lfht(min_nr_alloc_buckets, max_nr_buckets); - assert(ht); - assert(ht->mm == mm); - assert(ht->bucket_at == mm->bucket_at); - - ht->flags = flags; - ht->flavor = flavor; - ht->resize_attr = attr; - alloc_split_items_count(ht); - /* this mutex should not nest in read-side C.S. */ - pthread_mutex_init(&ht->resize_mutex, NULL); - order = cds_lfht_get_count_order_ulong(init_size); - ht->resize_target = 1UL << order; - cds_lfht_create_bucket(ht, 1UL << order); - ht->size = 1UL << order; - return ht; -} - -void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash, - cds_lfht_match_fct match, const void *key, - struct cds_lfht_iter *iter) -{ - struct cds_lfht_node *node, *next, *bucket; - unsigned long reverse_hash, size; - - reverse_hash = bit_reverse_ulong(hash); - - size = rcu_dereference(ht->size); - bucket = lookup_bucket(ht, size, hash); - /* We can always skip the bucket node initially */ - node = rcu_dereference(bucket->next); - node = clear_flag(node); - for (;;) { - if (caa_unlikely(is_end(node))) { - node = next = NULL; - break; - } - if (caa_unlikely(node->reverse_hash > reverse_hash)) { - node = next = NULL; - break; - } - next = rcu_dereference(node->next); - assert(node == clear_flag(node)); - if (caa_likely(!is_removed(next)) - && !is_bucket(next) - && node->reverse_hash == reverse_hash - && caa_likely(match(node, key))) { - break; - } - node = clear_flag(next); - } - assert(!node || !is_bucket(rcu_dereference(node->next))); - iter->node = node; - iter->next = next; -} - -void cds_lfht_next_duplicate(struct cds_lfht *ht, cds_lfht_match_fct match, - const void *key, struct cds_lfht_iter *iter) -{ - struct cds_lfht_node *node, *next; - unsigned long reverse_hash; - - node = iter->node; - reverse_hash = node->reverse_hash; - next = iter->next; - node = clear_flag(next); - - for (;;) { - if (caa_unlikely(is_end(node))) { - node = next = NULL; - break; - } - if (caa_unlikely(node->reverse_hash > reverse_hash)) { - node = next = NULL; - break; - } - next = rcu_dereference(node->next); - if (caa_likely(!is_removed(next)) - && !is_bucket(next) - && caa_likely(match(node, key))) { - break; - } - node = clear_flag(next); - } - assert(!node || !is_bucket(rcu_dereference(node->next))); - iter->node = node; - iter->next = next; -} - -void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter) -{ - struct cds_lfht_node *node, *next; - - node = clear_flag(iter->next); - for (;;) { - if (caa_unlikely(is_end(node))) { - node = next = NULL; - break; - } - next = rcu_dereference(node->next); - if (caa_likely(!is_removed(next)) - && !is_bucket(next)) { - break; - } - node = clear_flag(next); - } - assert(!node || !is_bucket(rcu_dereference(node->next))); - iter->node = node; - iter->next = next; -} - -void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter) -{ - /* - * Get next after first bucket node. The first bucket node is the - * first node of the linked list. - */ - iter->next = bucket_at(ht, 0)->next; - cds_lfht_next(ht, iter); -} - -void cds_lfht_add(struct cds_lfht *ht, unsigned long hash, - struct cds_lfht_node *node) -{ - unsigned long size; - - node->reverse_hash = bit_reverse_ulong(hash); - size = rcu_dereference(ht->size); - _cds_lfht_add(ht, hash, NULL, NULL, size, node, NULL, 0); - ht_count_add(ht, size, hash); -} - -struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *node) -{ - unsigned long size; - struct cds_lfht_iter iter; - - node->reverse_hash = bit_reverse_ulong(hash); - size = rcu_dereference(ht->size); - _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0); - if (iter.node == node) - ht_count_add(ht, size, hash); - return iter.node; -} - -struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *node) -{ - unsigned long size; - struct cds_lfht_iter iter; - - node->reverse_hash = bit_reverse_ulong(hash); - size = rcu_dereference(ht->size); - for (;;) { - _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0); - if (iter.node == node) { - ht_count_add(ht, size, hash); - return NULL; - } - - if (!_cds_lfht_replace(ht, size, iter.node, iter.next, node)) - return iter.node; - } -} - -int cds_lfht_replace(struct cds_lfht *ht, - struct cds_lfht_iter *old_iter, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *new_node) -{ - unsigned long size; - - new_node->reverse_hash = bit_reverse_ulong(hash); - if (!old_iter->node) - return -ENOENT; - if (caa_unlikely(old_iter->node->reverse_hash != new_node->reverse_hash)) - return -EINVAL; - if (caa_unlikely(!match(old_iter->node, key))) - return -EINVAL; - size = rcu_dereference(ht->size); - return _cds_lfht_replace(ht, size, old_iter->node, old_iter->next, - new_node); -} - -int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node) -{ - unsigned long size, hash; - int ret; - - size = rcu_dereference(ht->size); - ret = _cds_lfht_del(ht, size, node); - if (!ret) { - hash = bit_reverse_ulong(node->reverse_hash); - ht_count_del(ht, size, hash); - } - return ret; -} - -static -int cds_lfht_delete_bucket(struct cds_lfht *ht) -{ - struct cds_lfht_node *node; - unsigned long order, i, size; - - /* Check that the table is empty */ - node = bucket_at(ht, 0); - do { - node = clear_flag(node)->next; - if (!is_bucket(node)) - return -EPERM; - assert(!is_removed(node)); - } while (!is_end(node)); - /* - * size accessed without rcu_dereference because hash table is - * being destroyed. - */ - size = ht->size; - /* Internal sanity check: all nodes left should be bucket */ - for (i = 0; i < size; i++) { - node = bucket_at(ht, i); - dbg_printf("delete bucket: index %lu expected hash %lu hash %lu\n", - i, i, bit_reverse_ulong(node->reverse_hash)); - assert(is_bucket(node->next)); - } - - for (order = cds_lfht_get_count_order_ulong(size); (long)order >= 0; order--) - cds_lfht_free_bucket_table(ht, order); - - return 0; -} - -/* - * Should only be called when no more concurrent readers nor writers can - * possibly access the table. - */ -int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr) -{ - int ret; - - /* Wait for in-flight resize operations to complete */ - _CMM_STORE_SHARED(ht->in_progress_destroy, 1); - cmm_smp_mb(); /* Store destroy before load resize */ - while (uatomic_read(&ht->in_progress_resize)) - poll(NULL, 0, 100); /* wait for 100ms */ - ret = cds_lfht_delete_bucket(ht); - if (ret) - return ret; - free_split_items_count(ht); - if (attr) - *attr = ht->resize_attr; - poison_free(ht); - return ret; -} - -void cds_lfht_count_nodes(struct cds_lfht *ht, - long *approx_before, - unsigned long *count, - long *approx_after) -{ - struct cds_lfht_node *node, *next; - unsigned long nr_bucket = 0, nr_removed = 0; - - *approx_before = 0; - if (ht->split_count) { - int i; - - for (i = 0; i < split_count_mask + 1; i++) { - *approx_before += uatomic_read(&ht->split_count[i].add); - *approx_before -= uatomic_read(&ht->split_count[i].del); - } - } - - *count = 0; - - /* Count non-bucket nodes in the table */ - node = bucket_at(ht, 0); - do { - next = rcu_dereference(node->next); - if (is_removed(next)) { - if (!is_bucket(next)) - (nr_removed)++; - else - (nr_bucket)++; - } else if (!is_bucket(next)) - (*count)++; - else - (nr_bucket)++; - node = clear_flag(next); - } while (!is_end(node)); - dbg_printf("number of logically removed nodes: %lu\n", nr_removed); - dbg_printf("number of bucket nodes: %lu\n", nr_bucket); - *approx_after = 0; - if (ht->split_count) { - int i; - - for (i = 0; i < split_count_mask + 1; i++) { - *approx_after += uatomic_read(&ht->split_count[i].add); - *approx_after -= uatomic_read(&ht->split_count[i].del); - } - } -} - -/* called with resize mutex held */ -static -void _do_cds_lfht_grow(struct cds_lfht *ht, - unsigned long old_size, unsigned long new_size) -{ - unsigned long old_order, new_order; - - old_order = cds_lfht_get_count_order_ulong(old_size); - new_order = cds_lfht_get_count_order_ulong(new_size); - dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n", - old_size, old_order, new_size, new_order); - assert(new_size > old_size); - init_table(ht, old_order + 1, new_order); -} - -/* called with resize mutex held */ -static -void _do_cds_lfht_shrink(struct cds_lfht *ht, - unsigned long old_size, unsigned long new_size) -{ - unsigned long old_order, new_order; - - new_size = max(new_size, MIN_TABLE_SIZE); - old_order = cds_lfht_get_count_order_ulong(old_size); - new_order = cds_lfht_get_count_order_ulong(new_size); - dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n", - old_size, old_order, new_size, new_order); - assert(new_size < old_size); - - /* Remove and unlink all bucket nodes to remove. */ - fini_table(ht, new_order + 1, old_order); -} - - -/* called with resize mutex held */ -static -void _do_cds_lfht_resize(struct cds_lfht *ht) -{ - unsigned long new_size, old_size; - - /* - * Resize table, re-do if the target size has changed under us. - */ - do { - assert(uatomic_read(&ht->in_progress_resize)); - if (CMM_LOAD_SHARED(ht->in_progress_destroy)) - break; - ht->resize_initiated = 1; - old_size = ht->size; - new_size = CMM_LOAD_SHARED(ht->resize_target); - if (old_size < new_size) - _do_cds_lfht_grow(ht, old_size, new_size); - else if (old_size > new_size) - _do_cds_lfht_shrink(ht, old_size, new_size); - ht->resize_initiated = 0; - /* write resize_initiated before read resize_target */ - cmm_smp_mb(); - } while (ht->size != CMM_LOAD_SHARED(ht->resize_target)); -} - -static -unsigned long resize_target_grow(struct cds_lfht *ht, unsigned long new_size) -{ - return _uatomic_xchg_monotonic_increase(&ht->resize_target, new_size); -} - -static -void resize_target_update_count(struct cds_lfht *ht, - unsigned long count) -{ - count = max(count, MIN_TABLE_SIZE); - count = min(count, ht->max_nr_buckets); - uatomic_set(&ht->resize_target, count); -} - -void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size) -{ - resize_target_update_count(ht, new_size); - CMM_STORE_SHARED(ht->resize_initiated, 1); - ht->flavor->thread_offline(); - pthread_mutex_lock(&ht->resize_mutex); - _do_cds_lfht_resize(ht); - pthread_mutex_unlock(&ht->resize_mutex); - ht->flavor->thread_online(); -} - -static -void do_resize_cb(struct rcu_head *head) -{ - struct rcu_resize_work *work = - caa_container_of(head, struct rcu_resize_work, head); - struct cds_lfht *ht = work->ht; - - ht->flavor->thread_offline(); - pthread_mutex_lock(&ht->resize_mutex); - _do_cds_lfht_resize(ht); - pthread_mutex_unlock(&ht->resize_mutex); - ht->flavor->thread_online(); - poison_free(work); - cmm_smp_mb(); /* finish resize before decrement */ - uatomic_dec(&ht->in_progress_resize); -} - -static -void __cds_lfht_resize_lazy_launch(struct cds_lfht *ht) -{ - struct rcu_resize_work *work; - - /* Store resize_target before read resize_initiated */ - cmm_smp_mb(); - if (!CMM_LOAD_SHARED(ht->resize_initiated)) { - uatomic_inc(&ht->in_progress_resize); - cmm_smp_mb(); /* increment resize count before load destroy */ - if (CMM_LOAD_SHARED(ht->in_progress_destroy)) { - uatomic_dec(&ht->in_progress_resize); - return; - } - work = malloc(sizeof(*work)); - work->ht = ht; - ht->flavor->update_call_rcu(&work->head, do_resize_cb); - CMM_STORE_SHARED(ht->resize_initiated, 1); - } -} - -static -void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth) -{ - unsigned long target_size = size << growth; - - target_size = min(target_size, ht->max_nr_buckets); - if (resize_target_grow(ht, target_size) >= target_size) - return; - - __cds_lfht_resize_lazy_launch(ht); -} - -/* - * We favor grow operations over shrink. A shrink operation never occurs - * if a grow operation is queued for lazy execution. A grow operation - * cancels any pending shrink lazy execution. - */ -static -void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size, - unsigned long count) -{ - if (!(ht->flags & CDS_LFHT_AUTO_RESIZE)) - return; - count = max(count, MIN_TABLE_SIZE); - count = min(count, ht->max_nr_buckets); - if (count == size) - return; /* Already the right size, no resize needed */ - if (count > size) { /* lazy grow */ - if (resize_target_grow(ht, count) >= count) - return; - } else { /* lazy shrink */ - for (;;) { - unsigned long s; - - s = uatomic_cmpxchg(&ht->resize_target, size, count); - if (s == size) - break; /* no resize needed */ - if (s > size) - return; /* growing is/(was just) in progress */ - if (s <= count) - return; /* some other thread do shrink */ - size = s; - } - } - __cds_lfht_resize_lazy_launch(ht); -} diff --git a/liblttng-ht/rculfhash.h b/liblttng-ht/rculfhash.h deleted file mode 100644 index 136c7259b..000000000 --- a/liblttng-ht/rculfhash.h +++ /dev/null @@ -1,434 +0,0 @@ -#ifndef _URCU_RCULFHASH_H -#define _URCU_RCULFHASH_H - -/* - * urcu/rculfhash.h - * - * Userspace RCU library - Lock-Free RCU Hash Table - * - * Copyright 2011 - Mathieu Desnoyers - * Copyright 2011 - Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Include this file _after_ including your URCU flavor. - */ - -#include -#include -#include - -#include "urcu-flavor.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * cds_lfht_node: Contains the next pointers and reverse-hash - * value required for lookup and traversal of the hash table. - * - * struct cds_lfht_node should be aligned on 8-bytes boundaries because - * the three lower bits are used as flags. It is worth noting that the - * information contained within these three bits could be represented on - * two bits by re-using the same bit for REMOVAL_OWNER_FLAG and - * BUCKET_FLAG. This can be done if we ensure that no iterator nor - * updater check the BUCKET_FLAG after it detects that the REMOVED_FLAG - * is set. Given the minimum size of struct cds_lfht_node is 8 bytes on - * 32-bit architectures, we choose to go for simplicity and reserve - * three bits. - * - * struct cds_lfht_node can be embedded into a structure (as a field). - * caa_container_of() can be used to get the structure from the struct - * cds_lfht_node after a lookup. - * - * The structure which embeds it typically holds the key (or key-value - * pair) of the object. The caller code is responsible for calculation - * of the hash value for cds_lfht APIs. - */ -struct cds_lfht_node { - struct cds_lfht_node *next; /* ptr | REMOVAL_OWNER_FLAG | BUCKET_FLAG | REMOVED_FLAG */ - unsigned long reverse_hash; -} __attribute__((aligned(8))); - -/* cds_lfht_iter: Used to track state while traversing a hash chain. */ -struct cds_lfht_iter { - struct cds_lfht_node *node, *next; -}; - -static inline -struct cds_lfht_node *cds_lfht_iter_get_node(struct cds_lfht_iter *iter) -{ - return iter->node; -} - -struct cds_lfht; - -/* - * Caution ! - * Ensure reader and writer threads are registered as urcu readers. - */ - -typedef int (*cds_lfht_match_fct)(struct cds_lfht_node *node, const void *key); - -/* - * cds_lfht_node_init - initialize a hash table node - * @node: the node to initialize. - * - * This function is kept to be eventually used for debugging purposes - * (detection of memory corruption). - */ -static inline -void cds_lfht_node_init(struct cds_lfht_node *node) -{ -} - -/* - * Hash table creation flags. - */ -enum { - CDS_LFHT_AUTO_RESIZE = (1U << 0), - CDS_LFHT_ACCOUNTING = (1U << 1), -}; - -struct cds_lfht_mm_type { - struct cds_lfht *(*alloc_cds_lfht)(unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets); - void (*alloc_bucket_table)(struct cds_lfht *ht, unsigned long order); - void (*free_bucket_table)(struct cds_lfht *ht, unsigned long order); - struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht, - unsigned long index); -}; - -extern const struct cds_lfht_mm_type cds_lfht_mm_order; -extern const struct cds_lfht_mm_type cds_lfht_mm_chunk; -extern const struct cds_lfht_mm_type cds_lfht_mm_mmap; - -/* - * _cds_lfht_new - API used by cds_lfht_new wrapper. Do not use directly. - */ -struct cds_lfht *_cds_lfht_new(unsigned long init_size, - unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets, - int flags, - const struct cds_lfht_mm_type *mm, - const struct rcu_flavor_struct *flavor, - pthread_attr_t *attr); - -/* - * cds_lfht_new - allocate a hash table. - * @init_size: number of buckets to allocate initially. Must be power of two. - * @min_nr_alloc_buckets: the minimum number of allocated buckets. - * (must be power of two) - * @max_nr_buckets: the maximum number of hash table buckets allowed. - * (must be power of two) - * @flags: hash table creation flags (can be combined with bitwise or: '|'). - * 0: no flags. - * CDS_LFHT_AUTO_RESIZE: automatically resize hash table. - * CDS_LFHT_ACCOUNTING: count the number of node addition - * and removal in the table - * @attr: optional resize worker thread attributes. NULL for default. - * - * Return NULL on error. - * Note: the RCU flavor must be already included before the hash table header. - * - * The programmer is responsible for ensuring that resize operation has a - * priority equal to hash table updater threads. It should be performed by - * specifying the appropriate priority in the pthread "attr" argument, and, - * for CDS_LFHT_AUTO_RESIZE, by ensuring that call_rcu worker threads also have - * this priority level. Having lower priority for call_rcu and resize threads - * does not pose any correctness issue, but the resize operations could be - * starved by updates, thus leading to long hash table bucket chains. - * Threads calling this API are NOT required to be registered RCU read-side - * threads. It can be called very early.(before rcu is initialized ...etc.) - */ -static inline -struct cds_lfht *cds_lfht_new(unsigned long init_size, - unsigned long min_nr_alloc_buckets, - unsigned long max_nr_buckets, - int flags, - pthread_attr_t *attr) -{ - return _cds_lfht_new(init_size, min_nr_alloc_buckets, max_nr_buckets, - flags, NULL, &rcu_flavor, attr); -} - -/* - * cds_lfht_destroy - destroy a hash table. - * @ht: the hash table to destroy. - * @attr: (output) resize worker thread attributes, as received by cds_lfht_new. - * The caller will typically want to free this pointer if dynamically - * allocated. The attr point can be NULL if the caller does not - * need to be informed of the value passed to cds_lfht_new(). - * - * Return 0 on success, negative error value on error. - * Threads calling this API need to be registered RCU read-side threads. - */ -int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr); - -/* - * cds_lfht_count_nodes - count the number of nodes in the hash table. - * @ht: the hash table. - * @split_count_before: Sample the node count split-counter before traversal. - * @count: Traverse the hash table, count the number of nodes observed. - * @split_count_after: Sample the node count split-counter after traversal. - * - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_count_nodes(struct cds_lfht *ht, - long *split_count_before, - unsigned long *count, - long *split_count_after); - -/* - * cds_lfht_lookup - lookup a node by key. - * @ht: the hash table. - * @hash: the key hash. - * @match: the key match function. - * @key: the current node key. - * @iter: Node, if found (output). *iter->node set to NULL if not found. - * - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash, - cds_lfht_match_fct match, const void *key, - struct cds_lfht_iter *iter); - -/* - * cds_lfht_next_duplicate - get the next item with same key (after a lookup). - * @ht: the hash table. - * @match: the key match function. - * @key: the current node key. - * @iter: Node, if found (output). *iter->node set to NULL if not found. - * - * Uses an iterator initialized by a lookup. - * Sets *iter-node to the following node with same key. - * Sets *iter->node to NULL if no following node exists with same key. - * RCU read-side lock must be held across cds_lfht_lookup and - * cds_lfht_next calls, and also between cds_lfht_next calls using the - * node returned by a previous cds_lfht_next. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_next_duplicate(struct cds_lfht *ht, - cds_lfht_match_fct match, const void *key, - struct cds_lfht_iter *iter); - -/* - * cds_lfht_first - get the first node in the table. - * @ht: the hash table. - * @iter: First node, if exists (output). *iter->node set to NULL if not found. - * - * Output in "*iter". *iter->node set to NULL if table is empty. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter); - -/* - * cds_lfht_next - get the next node in the table. - * @ht: the hash table. - * @iter: Next node, if exists (output). *iter->node set to NULL if not found. - * - * Input/Output in "*iter". *iter->node set to NULL if *iter was - * pointing to the last table node. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter); - -/* - * cds_lfht_add - add a node to the hash table. - * @ht: the hash table. - * @hash: the key hash. - * @node: the node to add. - * - * This function supports adding redundant keys into the table. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_add(struct cds_lfht *ht, unsigned long hash, - struct cds_lfht_node *node); - -/* - * cds_lfht_add_unique - add a node to hash table, if key is not present. - * @ht: the hash table. - * @hash: the node's hash. - * @match: the key match function. - * @key: the node's key. - * @node: the node to try adding. - * - * Return the node added upon success. - * Return the unique node already present upon failure. If - * cds_lfht_add_unique fails, the node passed as parameter should be - * freed by the caller. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - * - * The semantic of this function is that if only this function is used - * to add keys into the table, no duplicated keys should ever be - * observable in the table. The same guarantee apply for combination of - * add_unique and add_replace (see below). - */ -struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *node); - -/* - * cds_lfht_add_replace - replace or add a node within hash table. - * @ht: the hash table. - * @hash: the node's hash. - * @match: the key match function. - * @key: the node's key. - * @node: the node to add. - * - * Return the node replaced upon success. If no node matching the key - * was present, return NULL, which also means the operation succeeded. - * This replacement operation should never fail. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - * After successful replacement, a grace period must be waited for before - * freeing the memory reserved for the returned node. - * - * The semantic of replacement vs lookups is the following: if lookups - * are performed between a key unique insertion and its removal, we - * guarantee that the lookups and get next will always find exactly one - * instance of the key if it is replaced concurrently with the lookups. - * - * Providing this semantic allows us to ensure that replacement-only - * schemes will never generate duplicated keys. It also allows us to - * guarantee that a combination of add_replace and add_unique updates - * will never generate duplicated keys. - */ -struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *node); - -/* - * cds_lfht_replace - replace a node pointer to by iter within hash table. - * @ht: the hash table. - * @old_iter: the iterator position of the node to replace. - * @hash: the node's hash. - * @match: the key match function. - * @key: the node's key. - * @new_node: the new node to use as replacement. - * - * Return 0 if replacement is successful, negative value otherwise. - * Replacing a NULL old node or an already removed node will fail with - * -ENOENT. - * If the hash or value of the node to replace and the new node differ, - * this function returns -EINVAL without proceeding to the replacement. - * Old node can be looked up with cds_lfht_lookup and cds_lfht_next. - * RCU read-side lock must be held between lookup and replacement. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - * After successful replacement, a grace period must be waited for before - * freeing the memory reserved for the old node (which can be accessed - * with cds_lfht_iter_get_node). - * - * The semantic of replacement vs lookups is the following: if lookups - * are performed between a key unique insertion and its removal, we - * guarantee that the lookups and get next will always find exactly one - * instance of the key if it is replaced concurrently with the lookups. - * - * Providing this semantic allows us to ensure that replacement-only - * schemes will never generate duplicated keys. It also allows us to - * guarantee that a combination of add_replace and add_unique updates - * will never generate duplicated keys. - */ -int cds_lfht_replace(struct cds_lfht *ht, - struct cds_lfht_iter *old_iter, - unsigned long hash, - cds_lfht_match_fct match, - const void *key, - struct cds_lfht_node *new_node); - -/* - * cds_lfht_del - remove node pointed to by iterator from hash table. - * @ht: the hash table. - * @node: the node to delete. - * - * Return 0 if the node is successfully removed, negative value - * otherwise. - * Deleting a NULL node or an already removed node will fail with a - * negative value. - * Node can be looked up with cds_lfht_lookup and cds_lfht_next, - * followed by use of cds_lfht_iter_get_node. - * RCU read-side lock must be held between lookup and removal. - * Call with rcu_read_lock held. - * Threads calling this API need to be registered RCU read-side threads. - * After successful removal, a grace period must be waited for before - * freeing the memory reserved for old node (which can be accessed with - * cds_lfht_iter_get_node). - */ -int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node); - -/* - * cds_lfht_resize - Force a hash table resize - * @ht: the hash table. - * @new_size: update to this hash table size. - * - * Threads calling this API need to be registered RCU read-side threads. - */ -void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size); - -/* - * Note: cds_lfht_for_each are safe for element removal during - * iteration. - */ -#define cds_lfht_for_each(ht, iter, node) \ - for (cds_lfht_first(ht, iter), \ - node = cds_lfht_iter_get_node(iter); \ - node != NULL; \ - cds_lfht_next(ht, iter), \ - node = cds_lfht_iter_get_node(iter)) - -#define cds_lfht_for_each_duplicate(ht, hash, match, key, iter, node) \ - for (cds_lfht_lookup(ht, hash, match, key, iter), \ - node = cds_lfht_iter_get_node(iter); \ - node != NULL; \ - cds_lfht_next_duplicate(ht, match, key, iter), \ - node = cds_lfht_iter_get_node(iter)) - -#define cds_lfht_for_each_entry(ht, iter, pos, member) \ - for (cds_lfht_first(ht, iter), \ - pos = caa_container_of(cds_lfht_iter_get_node(iter), \ - typeof(*(pos)), member); \ - &(pos)->member != NULL; \ - cds_lfht_next(ht, iter), \ - pos = caa_container_of(cds_lfht_iter_get_node(iter), \ - typeof(*(pos)), member)) - -#define cds_lfht_for_each_entry_duplicate(ht, hash, match, key, \ - iter, pos, member) \ - for (cds_lfht_lookup(ht, hash, match, key, iter), \ - pos = caa_container_of(cds_lfht_iter_get_node(iter), \ - typeof(*(pos)), member); \ - &(pos)->member != NULL; \ - cds_lfht_next_duplicate(ht, match, key, iter), \ - pos = caa_container_of(cds_lfht_iter_get_node(iter), \ - typeof(*(pos)), member)) - -#ifdef __cplusplus -} -#endif - -#endif /* _URCU_RCULFHASH_H */ diff --git a/liblttng-ht/urcu-flavor.h b/liblttng-ht/urcu-flavor.h deleted file mode 100644 index 9af4d0e63..000000000 --- a/liblttng-ht/urcu-flavor.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _URCU_FLAVOR_H -#define _URCU_FLAVOR_H - -/* - * urcu-flavor.h - * - * Userspace RCU header - rcu flavor declarations - * - * Copyright (c) 2011 Lai Jiangshan - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef __cplusplus -extern "C" { -#endif - -struct rcu_flavor_struct { - void (*read_lock)(void); - void (*read_unlock)(void); - void (*read_quiescent_state)(void); - void (*update_call_rcu)(struct rcu_head *head, - void (*func)(struct rcu_head *head)); - void (*update_synchronize_rcu)(void); - void (*update_defer_rcu)(void (*fct)(void *p), void *p); - - void (*thread_offline)(void); - void (*thread_online)(void); - void (*register_thread)(void); - void (*unregister_thread)(void); -}; - -#define DEFINE_RCU_FLAVOR(x) \ -const struct rcu_flavor_struct x = { \ - .read_lock = rcu_read_lock, \ - .read_unlock = rcu_read_unlock, \ - .read_quiescent_state = rcu_quiescent_state, \ - .update_call_rcu = call_rcu, \ - .update_synchronize_rcu = synchronize_rcu, \ - .update_defer_rcu = defer_rcu, \ - .thread_offline = rcu_thread_offline, \ - .thread_online = rcu_thread_online, \ - .register_thread = rcu_register_thread, \ - .unregister_thread = rcu_unregister_thread,\ -} - -extern const struct rcu_flavor_struct rcu_flavor; - -#ifdef __cplusplus -} -#endif - -#endif /* _URCU_FLAVOR_H */ diff --git a/liblttng-ht/utils.c b/liblttng-ht/utils.c deleted file mode 100644 index 0b3d53160..000000000 --- a/liblttng-ht/utils.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (C) - Bob Jenkins, May 2006, Public Domain. - * Copyright (C) 2011 - David Goulet - * Copyright (C) 2011 - Mathieu Desnoyers - * - * These are functions for producing 32-bit hashes for hash table lookup. - * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are - * externally useful functions. Routines to test the hash are included if - * SELF_TEST is defined. You can use this free for any purpose. It's in the - * public domain. It has no warranty. - * - * You probably want to use hashlittle(). hashlittle() and hashbig() hash byte - * arrays. hashlittle() is is faster than hashbig() on little-endian machines. - * Intel and AMD are little-endian machines. On second thought, you probably - * want hashlittle2(), which is identical to hashlittle() except it returns two - * 32-bit hashes for the price of one. You could implement hashbig2() if you - * wanted but I haven't bothered here. - * - * If you want to find a hash of, say, exactly 7 integers, do - * a = i1; b = i2; c = i3; - * mix(a,b,c); - * a += i4; b += i5; c += i6; - * mix(a,b,c); - * a += i7; - * final(a,b,c); - * then use c as the hash value. If you have a variable length array of - * 4-byte integers to hash, use hashword(). If you have a byte array (like - * a character string), use hashlittle(). If you have several byte arrays, or - * a mix of things, see the comments above hashlittle(). - * - * Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then - * mix those integers. This is fast (you can do a lot more thorough mixing - * with 12*3 instructions on 3 integers than you can with 3 instructions on 1 - * byte), but shoehorning those bytes into integers efficiently is messy. - */ - -#include -#include /* attempt to define endianness */ -#include /* defines uint32_t etc */ -#include /* defines printf for tests */ -#include -#include /* attempt to define endianness */ -#include /* defines time_t for timings in the test */ -#include - -#include "utils.h" - -/* - * My best guess at if you are big-endian or little-endian. This may - * need adjustment. - */ -#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ - __BYTE_ORDER == __LITTLE_ENDIAN) || \ - (defined(i386) || defined(__i386__) || defined(__i486__) || \ - defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) -# define HASH_LITTLE_ENDIAN 1 -# define HASH_BIG_ENDIAN 0 -#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ - __BYTE_ORDER == __BIG_ENDIAN) || \ - (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 1 -#else -# define HASH_LITTLE_ENDIAN 0 -# define HASH_BIG_ENDIAN 0 -#endif - -#define hashsize(n) ((uint32_t)1<<(n)) -#define hashmask(n) (hashsize(n)-1) -#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) - -/* - * mix -- mix 3 32-bit values reversibly. - * - * This is reversible, so any information in (a,b,c) before mix() is - * still in (a,b,c) after mix(). - * - * If four pairs of (a,b,c) inputs are run through mix(), or through - * mix() in reverse, there are at least 32 bits of the output that - * are sometimes the same for one pair and different for another pair. - * This was tested for: - * * pairs that differed by one bit, by two bits, in any combination - * of top bits of (a,b,c), or in any combination of bottom bits of - * (a,b,c). - * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - * is commonly produced by subtraction) look like a single 1-bit - * difference. - * * the base values were pseudorandom, all zero but one bit set, or - * all zero plus a counter that starts at zero. - * - * Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that - * satisfy this are - * 4 6 8 16 19 4 - * 9 15 3 18 27 15 - * 14 9 3 7 17 3 - * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing - * for "differ" defined as + with a one-bit base and a two-bit delta. I - * used http://burtleburtle.net/bob/hash/avalanche.html to choose - * the operations, constants, and arrangements of the variables. - * - * This does not achieve avalanche. There are input bits of (a,b,c) - * that fail to affect some output bits of (a,b,c), especially of a. The - * most thoroughly mixed value is c, but it doesn't really even achieve - * avalanche in c. - * - * This allows some parallelism. Read-after-writes are good at doubling - * the number of bits affected, so the goal of mixing pulls in the opposite - * direction as the goal of parallelism. I did what I could. Rotates - * seem to cost as much as shifts on every machine I could lay my hands - * on, and rotates are much kinder to the top and bottom bits, so I used - * rotates. - */ -#define mix(a,b,c) \ -{ \ - a -= c; a ^= rot(c, 4); c += b; \ - b -= a; b ^= rot(a, 6); a += c; \ - c -= b; c ^= rot(b, 8); b += a; \ - a -= c; a ^= rot(c,16); c += b; \ - b -= a; b ^= rot(a,19); a += c; \ - c -= b; c ^= rot(b, 4); b += a; \ -} - -/* - * final -- final mixing of 3 32-bit values (a,b,c) into c - * - * Pairs of (a,b,c) values differing in only a few bits will usually - * produce values of c that look totally different. This was tested for - * * pairs that differed by one bit, by two bits, in any combination - * of top bits of (a,b,c), or in any combination of bottom bits of - * (a,b,c). - * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed - * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as - * is commonly produced by subtraction) look like a single 1-bit - * difference. - * * the base values were pseudorandom, all zero but one bit set, or - * all zero plus a counter that starts at zero. - * - * These constants passed: - * 14 11 25 16 4 14 24 - * 12 14 25 16 4 14 24 - * and these came close: - * 4 8 15 26 3 22 24 - * 10 8 15 26 3 22 24 - * 11 8 15 26 3 22 24 - */ -#define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - -/* - * k - the key, an array of uint32_t values - * length - the length of the key, in uint32_ts - * initval - the previous hash, or an arbitrary value - */ -static uint32_t __attribute__((unused)) hashword(const uint32_t *k, - size_t length, uint32_t initval) -{ - uint32_t a, b, c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval; - - /*----------------------------------------- handle most of the key */ - while (length > 3) { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a, b, c); - length -= 3; - k += 3; - } - - /*----------------------------------- handle the last 3 uint32_t's */ - switch (length) { /* all the case statements fall through */ - case 3: c += k[2]; - case 2: b += k[1]; - case 1: a += k[0]; - final(a, b, c); - case 0: /* case 0: nothing left to add */ - break; - } - /*---------------------------------------------- report the result */ - return c; -} - - -/* - * hashword2() -- same as hashword(), but take two seeds and return two 32-bit - * values. pc and pb must both be nonnull, and *pc and *pb must both be - * initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be - * the same as the return value from hashword(). - */ -static void __attribute__((unused)) hashword2(const uint32_t *k, size_t length, - uint32_t *pc, uint32_t *pb) -{ - uint32_t a, b, c; - - /* Set up the internal state */ - a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc; - c += *pb; - - while (length > 3) { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a, b, c); - length -= 3; - k += 3; - } - - switch (length) { - case 3 : - c += k[2]; - case 2 : - b += k[1]; - case 1 : - a += k[0]; - final(a, b, c); - case 0: /* case 0: nothing left to add */ - break; - } - - *pc = c; - *pb = b; -} - -/* - * hashlittle() -- hash a variable-length key into a 32-bit value - * k : the key (the unaligned variable-length array of bytes) - * length : the length of the key, counting by bytes - * initval : can be any 4-byte value - * Returns a 32-bit value. Every bit of the key affects every bit of - * the return value. Two keys differing by one or two bits will have - * totally different hash values. - * - * The best hash table sizes are powers of 2. There is no need to do - * mod a prime (mod is sooo slow!). If you need less than 32 bits, - * use a bitmask. For example, if you need only 10 bits, do - * h = (h & hashmask(10)); - * In which case, the hash table should have hashsize(10) elements. - * - * If you are hashing n strings (uint8_t **)k, do it like this: - * for (i=0, h=0; i 12) { - a += k[0]; - b += k[1]; - c += k[2]; - mix(a,b,c); - length -= 12; - k += 3; - } - - /* - * "k[2]&0xffffff" actually reads beyond the end of the string, but - * then masks off the part it's not allowed to read. Because the - * string is aligned, the masked-off tail is in the same word as the - * rest of the string. Every machine with memory protection I've seen - * does it on word boundaries, so is OK with this. But VALGRIND will - * still catch it and complain. The masking trick does make the hash - * noticably faster for short strings (like English words). - */ -#ifndef VALGRIND - - switch (length) { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; - case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; - case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=k[1]&0xffffff; a+=k[0]; break; - case 6 : b+=k[1]&0xffff; a+=k[0]; break; - case 5 : b+=k[1]&0xff; a+=k[0]; break; - case 4 : a+=k[0]; break; - case 3 : a+=k[0]&0xffffff; break; - case 2 : a+=k[0]&0xffff; break; - case 1 : a+=k[0]&0xff; break; - case 0 : return c; /* zero length strings require no mixing */ - } -#else /* make valgrind happy */ - const uint8_t *k8; - - k8 = (const uint8_t *)k; - switch (length) { - case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; - case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ - case 9 : c+=k8[8]; /* fall through */ - case 8 : b+=k[1]; a+=k[0]; break; - case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ - case 5 : b+=k8[4]; /* fall through */ - case 4 : a+=k[0]; break; - case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ - case 1 : a+=k8[0]; break; - case 0 : return c; - } -#endif /* !valgrind */ - } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { - const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ - const uint8_t *k8; - - /*--------------- all but last block: aligned reads and different mixing */ - while (length > 12) { - a += k[0] + (((uint32_t)k[1])<<16); - b += k[2] + (((uint32_t)k[3])<<16); - c += k[4] + (((uint32_t)k[5])<<16); - mix(a,b,c); - length -= 12; - k += 6; - } - - k8 = (const uint8_t *)k; - switch (length) { - case 12: - c+=k[4]+(((uint32_t)k[5])<<16); - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 11: - c+=((uint32_t)k8[10])<<16; /* fall through */ - case 10: - c+=k[4]; - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 9: - c+=k8[8]; /* fall through */ - case 8: - b+=k[2]+(((uint32_t)k[3])<<16); - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 7: - b+=((uint32_t)k8[6])<<16; /* fall through */ - case 6: - b+=k[2]; - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 5: - b+=k8[4]; /* fall through */ - case 4: - a+=k[0]+(((uint32_t)k[1])<<16); - break; - case 3: - a+=((uint32_t)k8[2])<<16; /* fall through */ - case 2: - a+=k[0]; - break; - case 1: - a+=k8[0]; - break; - case 0: - return c; /* zero length requires no mixing */ - } - - } else { /* need to read the key one byte at a time */ - const uint8_t *k = (const uint8_t *)key; - - while (length > 12) { - a += k[0]; - a += ((uint32_t)k[1])<<8; - a += ((uint32_t)k[2])<<16; - a += ((uint32_t)k[3])<<24; - b += k[4]; - b += ((uint32_t)k[5])<<8; - b += ((uint32_t)k[6])<<16; - b += ((uint32_t)k[7])<<24; - c += k[8]; - c += ((uint32_t)k[9])<<8; - c += ((uint32_t)k[10])<<16; - c += ((uint32_t)k[11])<<24; - mix(a,b,c); - length -= 12; - k += 12; - } - - switch(length) { /* all the case statements fall through */ - case 12: c+=((uint32_t)k[11])<<24; - case 11: c+=((uint32_t)k[10])<<16; - case 10: c+=((uint32_t)k[9])<<8; - case 9: c+=k[8]; - case 8: b+=((uint32_t)k[7])<<24; - case 7: b+=((uint32_t)k[6])<<16; - case 6: b+=((uint32_t)k[5])<<8; - case 5: b+=k[4]; - case 4: a+=((uint32_t)k[3])<<24; - case 3: a+=((uint32_t)k[2])<<16; - case 2: a+=((uint32_t)k[1])<<8; - case 1: - a+=k[0]; - break; - case 0: - return c; - } - } - - final(a,b,c); - return c; -} - -#if (CAA_BITS_PER_LONG == 64) -/* - * Hash function for number value. - */ -unsigned long hash_key_ulong(void *_key, unsigned long seed) -{ - union { - uint64_t v64; - uint32_t v32[2]; - } v; - union { - uint64_t v64; - uint32_t v32[2]; - } key; - - v.v64 = (uint64_t) seed; - key.v64 = (uint64_t) _key; - hashword2(key.v32, 2, &v.v32[0], &v.v32[1]); - return v.v64; -} -#else -/* - * Hash function for number value. - */ -unsigned long hash_key_ulong(void *_key, unsigned long seed) -{ - uint32_t key = (uint32_t) _key; - - return hashword(&key, 1, seed); -} -#endif /* CAA_BITS_PER_LONG */ - -/* - * Hash function for string. - */ -unsigned long hash_key_str(void *key, unsigned long seed) -{ - return hashlittle(key, strlen((char *) key), seed); -} - -/* - * Hash function compare for number value. - */ -int hash_match_key_ulong(void *key1, void *key2) -{ - if (key1 == key2) { - return 1; - } - - return 0; -} - -/* - * Hash compare function for string. - */ -int hash_match_key_str(void *key1, void *key2) -{ - if (strcmp(key1, key2) == 0) { - return 1; - } - - return 0; -} diff --git a/liblttng-ht/utils.h b/liblttng-ht/utils.h deleted file mode 100644 index 86b340f72..000000000 --- a/liblttng-ht/utils.h +++ /dev/null @@ -1,28 +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_HT_UTILS_H -#define _LTT_HT_UTILS_H - -#include - -unsigned long hash_key_ulong(void *_key, unsigned long seed); -unsigned long hash_key_str(void *key, unsigned long seed); -int hash_match_key_ulong(void *key1, void *key2); -int hash_match_key_str(void *key1, void *key2); - -#endif /* _LTT_HT_UTILS_H */ diff --git a/liblttng-kconsumer/Makefile.am b/liblttng-kconsumer/Makefile.am deleted file mode 100644 index 15021cd05..000000000 --- a/liblttng-kconsumer/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = liblttng-kconsumer.la - -liblttng_kconsumer_la_SOURCES = lttng-kconsumer.c - -liblttng_kconsumer_la_LIBADD = \ - $(top_builddir)/libkernelctl/libkernelctl.la diff --git a/liblttng-kconsumer/lttng-kconsumer.c b/liblttng-kconsumer/lttng-kconsumer.c deleted file mode 100644 index 488d6b859..000000000 --- a/liblttng-kconsumer/lttng-kconsumer.c +++ /dev/null @@ -1,446 +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. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common/runas.h" - -extern struct lttng_consumer_global_data consumer_data; -extern int consumer_poll_timeout; -extern volatile int consumer_quit; - -/* - * Mmap the ring buffer, read it and write the data to the tracefile. - * - * Returns the number of bytes written - */ -int lttng_kconsumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - unsigned long mmap_offset; - long ret = 0; - off_t orig_offset = stream->out_fd_offset; - int fd = stream->wait_fd; - int outfd = stream->out_fd; - - /* get the offset inside the fd to mmap */ - ret = kernctl_get_mmap_read_offset(fd, &mmap_offset); - if (ret != 0) { - ret = -errno; - perror("kernctl_get_mmap_read_offset"); - goto end; - } - - while (len > 0) { - ret = write(outfd, stream->mmap_base + mmap_offset, len); - if (ret >= len) { - len = 0; - } else if (ret < 0) { - ret = -errno; - perror("Error in file write"); - goto end; - } - /* This won't block, but will start writeout asynchronously */ - sync_file_range(outfd, stream->out_fd_offset, ret, - SYNC_FILE_RANGE_WRITE); - stream->out_fd_offset += ret; - } - - lttng_consumer_sync_trace_file(stream, orig_offset); - - goto end; - -end: - return ret; -} - -/* - * Splice the data from the ring buffer to the tracefile. - * - * Returns the number of bytes spliced. - */ -int lttng_kconsumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - long ret = 0; - loff_t offset = 0; - off_t orig_offset = stream->out_fd_offset; - int fd = stream->wait_fd; - int outfd = stream->out_fd; - - while (len > 0) { - DBG("splice chan to pipe offset %lu (fd : %d)", - (unsigned long)offset, fd); - ret = splice(fd, &offset, ctx->consumer_thread_pipe[1], NULL, len, - SPLICE_F_MOVE | SPLICE_F_MORE); - DBG("splice chan to pipe ret %ld", ret); - if (ret < 0) { - ret = errno; - perror("Error in relay splice"); - goto splice_error; - } - - ret = splice(ctx->consumer_thread_pipe[0], NULL, outfd, NULL, ret, - SPLICE_F_MOVE | SPLICE_F_MORE); - DBG("splice pipe to file %ld", ret); - if (ret < 0) { - ret = errno; - perror("Error in file splice"); - goto splice_error; - } - len -= ret; - /* This won't block, but will start writeout asynchronously */ - sync_file_range(outfd, stream->out_fd_offset, ret, - SYNC_FILE_RANGE_WRITE); - stream->out_fd_offset += ret; - } - lttng_consumer_sync_trace_file(stream, orig_offset); - - goto end; - -splice_error: - /* send the appropriate error description to sessiond */ - switch(ret) { - case EBADF: - lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EBADF); - break; - case EINVAL: - lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EINVAL); - break; - case ENOMEM: - lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ENOMEM); - break; - case ESPIPE: - lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ESPIPE); - break; - } - -end: - return ret; -} - -/* - * Take a snapshot for a specific fd - * - * Returns 0 on success, < 0 on error - */ -int lttng_kconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream) -{ - int ret = 0; - int infd = stream->wait_fd; - - ret = kernctl_snapshot(infd); - if (ret != 0) { - ret = errno; - perror("Getting sub-buffer snapshot."); - } - - return ret; -} - -/* - * Get the produced position - * - * Returns 0 on success, < 0 on error - */ -int lttng_kconsumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos) -{ - int ret; - int infd = stream->wait_fd; - - ret = kernctl_snapshot_get_produced(infd, pos); - if (ret != 0) { - ret = errno; - perror("kernctl_snapshot_get_produced"); - } - - return ret; -} - -int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll) -{ - ssize_t ret; - struct lttcomm_consumer_msg msg; - - ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg)); - if (ret != sizeof(msg)) { - lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_CMD); - return ret; - } - if (msg.cmd_type == LTTNG_CONSUMER_STOP) { - return -ENOENT; - } - - switch (msg.cmd_type) { - case LTTNG_CONSUMER_ADD_CHANNEL: - { - struct lttng_consumer_channel *new_channel; - - DBG("consumer_add_channel %d", msg.u.channel.channel_key); - new_channel = consumer_allocate_channel(msg.u.channel.channel_key, - -1, -1, - msg.u.channel.mmap_len, - msg.u.channel.max_sb_size); - if (new_channel == NULL) { - lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); - goto end_nosignal; - } - if (ctx->on_recv_channel != NULL) { - ret = ctx->on_recv_channel(new_channel); - if (ret == 0) { - consumer_add_channel(new_channel); - } else if (ret < 0) { - goto end_nosignal; - } - } else { - consumer_add_channel(new_channel); - } - goto end_nosignal; - } - case LTTNG_CONSUMER_ADD_STREAM: - { - struct lttng_consumer_stream *new_stream; - int fd; - - /* block */ - if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { - return -EINTR; - } - ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); - if (ret != sizeof(fd)) { - lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); - return ret; - } - - DBG("consumer_add_stream %s (%d)", msg.u.stream.path_name, - fd); - new_stream = consumer_allocate_stream(msg.u.stream.channel_key, - msg.u.stream.stream_key, - fd, fd, - msg.u.stream.state, - msg.u.stream.mmap_len, - msg.u.stream.output, - msg.u.stream.path_name, - msg.u.stream.uid, - msg.u.stream.gid); - if (new_stream == NULL) { - lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); - goto end; - } - if (ctx->on_recv_stream != NULL) { - ret = ctx->on_recv_stream(new_stream); - if (ret == 0) { - consumer_add_stream(new_stream); - } else if (ret < 0) { - goto end; - } - } else { - consumer_add_stream(new_stream); - } - break; - } - case LTTNG_CONSUMER_UPDATE_STREAM: - { - if (ctx->on_update_stream != NULL) { - ret = ctx->on_update_stream(msg.u.stream.stream_key, msg.u.stream.state); - if (ret == 0) { - consumer_change_stream_state(msg.u.stream.stream_key, msg.u.stream.state); - } else if (ret < 0) { - goto end; - } - } else { - consumer_change_stream_state(msg.u.stream.stream_key, - msg.u.stream.state); - } - break; - } - default: - break; - } -end: - /* signal the poll thread */ - ret = write(ctx->consumer_poll_pipe[1], "4", 1); - if (ret < 0) { - perror("write consumer poll"); - } -end_nosignal: - return 0; -} - -/* - * Consume data on a file descriptor and write it on a trace file. - */ -int lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx) -{ - unsigned long len; - int err; - long ret = 0; - int infd = stream->wait_fd; - - DBG("In read_subbuffer (infd : %d)", infd); - /* Get the next subbuffer */ - err = kernctl_get_next_subbuf(infd); - if (err != 0) { - ret = errno; - /* - * This is a debug message even for single-threaded consumer, - * because poll() have more relaxed criterions than get subbuf, - * so get_subbuf may fail for short race windows where poll() - * would issue wakeups. - */ - DBG("Reserving sub buffer failed (everything is normal, " - "it is due to concurrency)"); - goto end; - } - - switch (stream->output) { - case LTTNG_EVENT_SPLICE: - /* read the whole subbuffer */ - err = kernctl_get_padded_subbuf_size(infd, &len); - if (err != 0) { - ret = errno; - perror("Getting sub-buffer len failed."); - goto end; - } - - /* splice the subbuffer to the tracefile */ - ret = lttng_consumer_on_read_subbuffer_splice(ctx, stream, len); - if (ret < 0) { - /* - * display the error but continue processing to try - * to release the subbuffer - */ - ERR("Error splicing to tracefile"); - } - break; - case LTTNG_EVENT_MMAP: - /* read the used subbuffer size */ - err = kernctl_get_padded_subbuf_size(infd, &len); - if (err != 0) { - ret = errno; - perror("Getting sub-buffer len failed."); - goto end; - } - /* write the subbuffer to the tracefile */ - ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len); - if (ret < 0) { - /* - * display the error but continue processing to try - * to release the subbuffer - */ - ERR("Error writing to tracefile"); - } - break; - default: - ERR("Unknown output method"); - ret = -1; - } - - err = kernctl_put_next_subbuf(infd); - if (err != 0) { - ret = errno; - if (errno == EFAULT) { - perror("Error in unreserving sub buffer\n"); - } else if (errno == EIO) { - /* Should never happen with newer LTTng versions */ - perror("Reader has been pushed by the writer, last sub-buffer corrupted."); - } - goto end; - } - -end: - return ret; -} - -int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream) -{ - int ret; - - /* Opening the tracefile in write mode */ - if (stream->path_name != NULL) { - ret = run_as_open(stream->path_name, - O_WRONLY|O_CREAT|O_TRUNC, - S_IRWXU|S_IRWXG|S_IRWXO, - stream->uid, stream->gid); - if (ret < 0) { - ERR("Opening %s", stream->path_name); - perror("open"); - goto error; - } - stream->out_fd = ret; - } - - if (stream->output == LTTNG_EVENT_MMAP) { - /* get the len of the mmap region */ - unsigned long mmap_len; - - ret = kernctl_get_mmap_len(stream->wait_fd, &mmap_len); - if (ret != 0) { - ret = errno; - perror("kernctl_get_mmap_len"); - goto error_close_fd; - } - stream->mmap_len = (size_t) mmap_len; - - stream->mmap_base = mmap(NULL, stream->mmap_len, - PROT_READ, MAP_PRIVATE, stream->wait_fd, 0); - if (stream->mmap_base == MAP_FAILED) { - perror("Error mmaping"); - ret = -1; - goto error_close_fd; - } - } - - /* we return 0 to let the library handle the FD internally */ - return 0; - -error_close_fd: - { - int err; - - err = close(stream->out_fd); - assert(!err); - } -error: - return ret; -} - diff --git a/liblttng-sessiond-comm/Makefile.am b/liblttng-sessiond-comm/Makefile.am deleted file mode 100644 index 61d6882ea..000000000 --- a/liblttng-sessiond-comm/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -noinst_LTLIBRARIES = liblttng-sessiond-comm.la - -liblttng_sessiond_comm_la_SOURCES = lttng-sessiond-comm.c diff --git a/liblttng-sessiond-comm/lttng-sessiond-comm.c b/liblttng-sessiond-comm/lttng-sessiond-comm.c deleted file mode 100644 index 483b346d6..000000000 --- a/liblttng-sessiond-comm/lttng-sessiond-comm.c +++ /dev/null @@ -1,534 +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 - -/* - * Human readable error message. - */ -static const char *lttcomm_readable_code[] = { - [ LTTCOMM_ERR_INDEX(LTTCOMM_OK) ] = "Success", - [ LTTCOMM_ERR_INDEX(LTTCOMM_ERR) ] = "Unknown error", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UND) ] = "Undefined command", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NOT_IMPLEMENTED) ] = "Not implemented", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UNKNOWN_DOMAIN) ] = "Unknown tracing domain", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_SESSION) ] = "No session found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_LIST_FAIL) ] = "Unable to list traceable apps", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_APPS) ] = "No traceable apps found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_SESS_NOT_FOUND) ] = "Session name not found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_TRACE) ] = "No trace found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_FATAL) ] = "Fatal error of the session daemon", - [ LTTCOMM_ERR_INDEX(LTTCOMM_CREATE_FAIL) ] = "Create trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_START_FAIL) ] = "Start trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_STOP_FAIL) ] = "Stop trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_TRACEABLE) ] = "App is not traceable", - [ LTTCOMM_ERR_INDEX(LTTCOMM_SELECT_SESS) ] = "A session MUST be selected", - [ LTTCOMM_ERR_INDEX(LTTCOMM_EXIST_SESS) ] = "Session name already exist", - [ LTTCOMM_ERR_INDEX(LTTCOMM_CONNECT_FAIL) ] = "Unable to connect to Unix socket", - [ LTTCOMM_ERR_INDEX(LTTCOMM_APP_NOT_FOUND) ] = "Application not found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_EPERM) ] = "Permission denied", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_NA) ] = "Kernel tracer not available", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_EVENT_EXIST) ] = "Kernel event already exists", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_SESS_FAIL) ] = "Kernel create session failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_FAIL) ] = "Kernel create channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_NOT_FOUND) ] = "Kernel channel not found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_DISABLE_FAIL) ] = "Disable kernel channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_ENABLE_FAIL) ] = "Enable kernel channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CONTEXT_FAIL) ] = "Add kernel context failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_ENABLE_FAIL) ] = "Enable kernel event failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DISABLE_FAIL) ] = "Disable kernel event failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_META_FAIL) ] = "Opening metadata failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_START_FAIL) ] = "Starting kernel trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_STOP_FAIL) ] = "Stoping kernel trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CONSUMER_FAIL) ] = "Kernel consumer start failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_STREAM_FAIL) ] = "Kernel create stream failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DIR_FAIL) ] = "Kernel trace directory creation failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DIR_EXIST) ] = "Kernel trace directory already exist", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_NO_SESSION) ] = "No kernel session found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_LIST_FAIL) ] = "Listing kernel events failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_SESS_FAIL) ] = "UST create session failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_FAIL) ] = "UST create channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_EXIST) ] = "UST channel already exist", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_NOT_FOUND) ] = "UST channel not found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_DISABLE_FAIL) ] = "Disable UST channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_ENABLE_FAIL) ] = "Enable UST channel failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONTEXT_FAIL) ] = "Add UST context failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_ENABLE_FAIL) ] = "Enable UST event failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DISABLE_FAIL) ] = "Disable UST event failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_META_FAIL) ] = "Opening metadata failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_START_FAIL) ] = "Starting UST trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_STOP_FAIL) ] = "Stoping UST trace failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONSUMER64_FAIL) ] = "64-bit UST consumer start failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONSUMER32_FAIL) ] = "32-bit UST consumer start failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_STREAM_FAIL) ] = "UST create stream failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DIR_FAIL) ] = "UST trace directory creation failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DIR_EXIST) ] = "UST trace directory already exist", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_NO_SESSION) ] = "No UST session found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_LIST_FAIL) ] = "Listing UST events failed", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_EVENT_EXIST) ] = "UST event already exist", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_EVENT_NOT_FOUND)] = "UST event not found", - [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONTEXT_EXIST)] = "UST context already exist", - [ LTTCOMM_ERR_INDEX(CONSUMERD_COMMAND_SOCK_READY) ] = "consumerd command socket ready", - [ LTTCOMM_ERR_INDEX(CONSUMERD_SUCCESS_RECV_FD) ] = "consumerd success on receiving fds", - [ LTTCOMM_ERR_INDEX(CONSUMERD_ERROR_RECV_FD) ] = "consumerd error on receiving fds", - [ LTTCOMM_ERR_INDEX(CONSUMERD_ERROR_RECV_CMD) ] = "consumerd error on receiving command", - [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_ERROR) ] = "consumerd error in polling thread", - [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_NVAL) ] = "consumerd polling on closed fd", - [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_HUP) ] = "consumerd all fd hung up", - [ LTTCOMM_ERR_INDEX(CONSUMERD_EXIT_SUCCESS) ] = "consumerd exiting normally", - [ LTTCOMM_ERR_INDEX(CONSUMERD_EXIT_FAILURE) ] = "consumerd exiting on error", - [ LTTCOMM_ERR_INDEX(CONSUMERD_OUTFD_ERROR) ] = "consumerd error opening the tracefile", - [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_EBADF) ] = "consumerd splice EBADF", - [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_EINVAL) ] = "consumerd splice EINVAL", - [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_ENOMEM) ] = "consumerd splice ENOMEM", - [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_ESPIPE) ] = "consumerd splice ESPIPE", - [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_EVENT) ] = "Event not found", -}; - -/* - * Return ptr to string representing a human readable error code from the - * lttcomm_return_code enum. - * - * These code MUST be negative in other to treat that as an error value. - */ -const char *lttcomm_get_readable_code(enum lttcomm_return_code code) -{ - int tmp_code = -code; - - if (tmp_code >= LTTCOMM_OK && tmp_code < LTTCOMM_NR) { - return lttcomm_readable_code[LTTCOMM_ERR_INDEX(tmp_code)]; - } - - return "Unknown error code"; -} - -/* - * Connect to unix socket using the path name. - */ -int lttcomm_connect_unix_sock(const char *pathname) -{ - struct sockaddr_un sun; - int fd; - int ret; - - fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - ret = fd; - goto error; - } - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - strncpy(sun.sun_path, pathname, sizeof(sun.sun_path)); - sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; - - ret = connect(fd, (struct sockaddr *) &sun, sizeof(sun)); - if (ret < 0) { - /* - * Don't print message on connect error, because connect is used in - * normal execution to detect if sessiond is alive. - */ - goto error_connect; - } - - return fd; - -error_connect: - close(fd); -error: - return ret; -} - -/* - * Do an accept(2) on the sock and return the new file descriptor. The socket - * MUST be bind(2) before. - */ -int lttcomm_accept_unix_sock(int sock) -{ - int new_fd; - struct sockaddr_un sun; - socklen_t len = 0; - - /* Blocking call */ - new_fd = accept(sock, (struct sockaddr *) &sun, &len); - if (new_fd < 0) { - perror("accept"); - } - - return new_fd; -} - -/* - * Creates a AF_UNIX local socket using pathname bind the socket upon creation - * and return the fd. - */ -int lttcomm_create_unix_sock(const char *pathname) -{ - struct sockaddr_un sun; - int fd; - int ret = -1; - - /* Create server socket */ - if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { - perror("socket"); - goto error; - } - - memset(&sun, 0, sizeof(sun)); - sun.sun_family = AF_UNIX; - strncpy(sun.sun_path, pathname, sizeof(sun.sun_path)); - sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; - - /* Unlink the old file if present */ - (void) unlink(pathname); - ret = bind(fd, (struct sockaddr *) &sun, sizeof(sun)); - if (ret < 0) { - perror("bind"); - goto error; - } - - return fd; - -error: - return ret; -} - -/* - * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN. - */ -int lttcomm_listen_unix_sock(int sock) -{ - int ret; - - ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN); - if (ret < 0) { - perror("listen"); - } - - return ret; -} - -/* - * Receive data of size len in put that data into the buf param. Using recvmsg - * API. - * - * Return the size of received data. - */ -ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len) -{ - struct msghdr msg = { 0 }; - struct iovec iov[1]; - ssize_t ret = -1; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - ret = recvmsg(sock, &msg, 0); - if (ret < 0) { - perror("recvmsg"); - } - - return ret; -} - -/* - * Send buf data of size len. Using sendmsg API. - * - * Return the size of sent data. - */ -ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len) -{ - struct msghdr msg = { 0 }; - struct iovec iov[1]; - ssize_t ret = -1; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - perror("sendmsg"); - } - - return ret; -} - -/* - * Shutdown cleanly a unix socket. - */ -int lttcomm_close_unix_sock(int sock) -{ - int ret; - - /* Shutdown receptions and transmissions */ - ret = shutdown(sock, SHUT_RDWR); - if (ret < 0) { - perror("shutdown"); - } - - return ret; -} - -/* - * Send a message accompanied by fd(s) over a unix socket. - * - * Returns the size of data sent, or negative error value. - */ -ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd) -{ - struct msghdr msg = { 0 }; - struct cmsghdr *cmptr; - struct iovec iov[1]; - ssize_t ret = -1; - unsigned int sizeof_fds = nb_fd * sizeof(int); - char tmp[CMSG_SPACE(sizeof_fds)]; - char dummy = 0; - - if (nb_fd > LTTCOMM_MAX_SEND_FDS) - return -EINVAL; - - msg.msg_control = (caddr_t)tmp; - msg.msg_controllen = CMSG_LEN(sizeof_fds); - - cmptr = CMSG_FIRSTHDR(&msg); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_RIGHTS; - cmptr->cmsg_len = CMSG_LEN(sizeof_fds); - memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); - /* Sum of the length of all control messages in the buffer: */ - msg.msg_controllen = cmptr->cmsg_len; - - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - perror("sendmsg"); - } - return ret; -} - -/* - * Recv a message accompanied by fd(s) from a unix socket. - * - * Returns the size of received data, or negative error value. - * - * Expect at most "nb_fd" file descriptors. Returns the number of fd - * actually received in nb_fd. - */ -ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd) -{ - struct iovec iov[1]; - ssize_t ret = 0; - struct cmsghdr *cmsg; - size_t sizeof_fds = nb_fd * sizeof(int); - char recv_fd[CMSG_SPACE(sizeof_fds)]; - struct msghdr msg = { 0 }; - char dummy; - - /* Prepare to receive the structures */ - iov[0].iov_base = &dummy; - iov[0].iov_len = 1; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = recv_fd; - msg.msg_controllen = sizeof(recv_fd); - - ret = recvmsg(sock, &msg, 0); - if (ret < 0) { - perror("recvmsg fds"); - goto end; - } - if (ret != 1) { - fprintf(stderr, "Error: Received %zd bytes, expected %d\n", - ret, 1); - goto end; - } - if (msg.msg_flags & MSG_CTRUNC) { - fprintf(stderr, "Error: Control message truncated.\n"); - ret = -1; - goto end; - } - cmsg = CMSG_FIRSTHDR(&msg); - if (!cmsg) { - fprintf(stderr, "Error: Invalid control message header\n"); - ret = -1; - goto end; - } - if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { - fprintf(stderr, "Didn't received any fd\n"); - ret = -1; - goto end; - } - if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { - fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", - cmsg->cmsg_len, CMSG_LEN(sizeof_fds)); - ret = -1; - goto end; - } - memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); - ret = sizeof_fds; -end: - return ret; -} - -/* - * Send a message with credentials over a unix socket. - * - * Returns the size of data sent, or negative error value. - */ -ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len) -{ - struct msghdr msg = { 0 }; - struct cmsghdr *cmptr; - struct iovec iov[1]; - ssize_t ret = -1; - struct ucred *creds; - size_t sizeof_cred = sizeof(struct ucred); - char anc_buf[CMSG_SPACE(sizeof_cred)]; - - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - msg.msg_control = (caddr_t) anc_buf; - msg.msg_controllen = CMSG_LEN(sizeof_cred); - - cmptr = CMSG_FIRSTHDR(&msg); - cmptr->cmsg_level = SOL_SOCKET; - cmptr->cmsg_type = SCM_CREDENTIALS; - cmptr->cmsg_len = CMSG_LEN(sizeof_cred); - - creds = (struct ucred *) CMSG_DATA(cmptr); - - creds->uid = geteuid(); - creds->gid = getegid(); - creds->pid = getpid(); - - ret = sendmsg(sock, &msg, 0); - if (ret < 0) { - perror("sendmsg"); - } - - return ret; -} - -/* - * Recv a message accompanied with credentials from a unix socket. - * - * Returns the size of received data, or negative error value. - */ -ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, - struct ucred *creds) -{ - struct msghdr msg = { 0 }; - struct cmsghdr *cmptr; - struct iovec iov[1]; - ssize_t ret; - size_t sizeof_cred = sizeof(struct ucred); - char anc_buf[CMSG_SPACE(sizeof_cred)]; - - /* Not allowed */ - if (creds == NULL) { - ret = -1; - goto end; - } - - /* Prepare to receive the structures */ - iov[0].iov_base = buf; - iov[0].iov_len = len; - msg.msg_iov = iov; - msg.msg_iovlen = 1; - - msg.msg_control = anc_buf; - msg.msg_controllen = sizeof(anc_buf); - - ret = recvmsg(sock, &msg, 0); - if (ret < 0) { - perror("recvmsg fds"); - goto end; - } - - if (msg.msg_flags & MSG_CTRUNC) { - fprintf(stderr, "Error: Control message truncated.\n"); - ret = -1; - goto end; - } - - cmptr = CMSG_FIRSTHDR(&msg); - if (cmptr == NULL) { - fprintf(stderr, "Error: Invalid control message header\n"); - ret = -1; - goto end; - } - - if (cmptr->cmsg_level != SOL_SOCKET || - cmptr->cmsg_type != SCM_CREDENTIALS) { - fprintf(stderr, "Didn't received any credentials\n"); - ret = -1; - goto end; - } - - if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) { - fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", - cmptr->cmsg_len, CMSG_LEN(sizeof_cred)); - ret = -1; - goto end; - } - - memcpy(creds, CMSG_DATA(cmptr), sizeof_cred); - -end: - return ret; -} - -/* - * Set socket option to use credentials passing. - */ -int lttcomm_setsockopt_creds_unix_sock(int sock) -{ - int ret, on = 1; - - /* Set socket for credentials retrieval */ - ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); - if (ret < 0) { - perror("setsockopt creds unix sock"); - } - - return ret; -} diff --git a/liblttng-ustconsumer/Makefile.am b/liblttng-ustconsumer/Makefile.am deleted file mode 100644 index e01064fd7..000000000 --- a/liblttng-ustconsumer/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -if HAVE_LIBLTTNG_UST_CTL -noinst_LTLIBRARIES = liblttng-ustconsumer.la - -liblttng_ustconsumer_la_SOURCES = lttng-ustconsumer.c - -liblttng_ustconsumer_la_LIBADD = -llttng-ust-ctl - -endif diff --git a/liblttng-ustconsumer/lttng-ustconsumer.c b/liblttng-ustconsumer/lttng-ustconsumer.c deleted file mode 100644 index 10213c1c9..000000000 --- a/liblttng-ustconsumer/lttng-ustconsumer.c +++ /dev/null @@ -1,415 +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. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common/runas.h" - -extern struct lttng_consumer_global_data consumer_data; -extern int consumer_poll_timeout; -extern volatile int consumer_quit; - -/* - * Mmap the ring buffer, read it and write the data to the tracefile. - * - * Returns the number of bytes written - */ -int lttng_ustconsumer_on_read_subbuffer_mmap( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - unsigned long mmap_offset; - long ret = 0; - off_t orig_offset = stream->out_fd_offset; - int outfd = stream->out_fd; - - /* get the offset inside the fd to mmap */ - ret = ustctl_get_mmap_read_offset(stream->chan->handle, - stream->buf, &mmap_offset); - if (ret != 0) { - ret = -errno; - perror("ustctl_get_mmap_read_offset"); - goto end; - } - while (len > 0) { - ret = write(outfd, stream->mmap_base + mmap_offset, len); - if (ret >= len) { - len = 0; - } else if (ret < 0) { - ret = -errno; - perror("Error in file write"); - goto end; - } - /* This won't block, but will start writeout asynchronously */ - sync_file_range(outfd, stream->out_fd_offset, ret, - SYNC_FILE_RANGE_WRITE); - stream->out_fd_offset += ret; - } - - lttng_consumer_sync_trace_file(stream, orig_offset); - - goto end; - -end: - return ret; -} - -/* - * Splice the data from the ring buffer to the tracefile. - * - * Returns the number of bytes spliced. - */ -int lttng_ustconsumer_on_read_subbuffer_splice( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, unsigned long len) -{ - return -ENOSYS; -} - -/* - * Take a snapshot for a specific fd - * - * Returns 0 on success, < 0 on error - */ -int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream) -{ - int ret = 0; - - ret = ustctl_snapshot(stream->chan->handle, stream->buf); - if (ret != 0) { - ret = errno; - perror("Getting sub-buffer snapshot."); - } - - return ret; -} - -/* - * Get the produced position - * - * Returns 0 on success, < 0 on error - */ -int lttng_ustconsumer_get_produced_snapshot( - struct lttng_consumer_local_data *ctx, - struct lttng_consumer_stream *stream, - unsigned long *pos) -{ - int ret; - - ret = ustctl_snapshot_get_produced(stream->chan->handle, - stream->buf, pos); - if (ret != 0) { - ret = errno; - perror("kernctl_snapshot_get_produced"); - } - - return ret; -} - -int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, - int sock, struct pollfd *consumer_sockpoll) -{ - ssize_t ret; - struct lttcomm_consumer_msg msg; - - ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg)); - if (ret != sizeof(msg)) { - lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); - return ret; - } - if (msg.cmd_type == LTTNG_CONSUMER_STOP) { - return -ENOENT; - } - - switch (msg.cmd_type) { - case LTTNG_CONSUMER_ADD_CHANNEL: - { - struct lttng_consumer_channel *new_channel; - int fds[1]; - size_t nb_fd = 1; - - /* block */ - if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { - return -EINTR; - } - ret = lttcomm_recv_fds_unix_sock(sock, fds, nb_fd); - if (ret != sizeof(fds)) { - lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); - return ret; - } - - DBG("consumer_add_channel %d", msg.u.channel.channel_key); - - new_channel = consumer_allocate_channel(msg.u.channel.channel_key, - fds[0], -1, - msg.u.channel.mmap_len, - msg.u.channel.max_sb_size); - if (new_channel == NULL) { - lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); - goto end_nosignal; - } - if (ctx->on_recv_channel != NULL) { - ret = ctx->on_recv_channel(new_channel); - if (ret == 0) { - consumer_add_channel(new_channel); - } else if (ret < 0) { - goto end_nosignal; - } - } else { - consumer_add_channel(new_channel); - } - goto end_nosignal; - } - case LTTNG_CONSUMER_ADD_STREAM: - { - struct lttng_consumer_stream *new_stream; - int fds[2]; - size_t nb_fd = 2; - - /* block */ - if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { - return -EINTR; - } - ret = lttcomm_recv_fds_unix_sock(sock, fds, nb_fd); - if (ret != sizeof(fds)) { - lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); - return ret; - } - - DBG("consumer_add_stream %s (%d,%d)", msg.u.stream.path_name, - fds[0], fds[1]); - assert(msg.u.stream.output == LTTNG_EVENT_MMAP); - new_stream = consumer_allocate_stream(msg.u.channel.channel_key, - msg.u.stream.stream_key, - fds[0], fds[1], - msg.u.stream.state, - msg.u.stream.mmap_len, - msg.u.stream.output, - msg.u.stream.path_name, - msg.u.stream.uid, - msg.u.stream.gid); - if (new_stream == NULL) { - lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); - goto end; - } - if (ctx->on_recv_stream != NULL) { - ret = ctx->on_recv_stream(new_stream); - if (ret == 0) { - consumer_add_stream(new_stream); - } else if (ret < 0) { - goto end; - } - } else { - consumer_add_stream(new_stream); - } - break; - } - case LTTNG_CONSUMER_UPDATE_STREAM: - { - return -ENOSYS; -#if 0 - if (ctx->on_update_stream != NULL) { - ret = ctx->on_update_stream(msg.u.stream.stream_key, msg.u.stream.state); - if (ret == 0) { - consumer_change_stream_state(msg.u.stream.stream_key, msg.u.stream.state); - } else if (ret < 0) { - goto end; - } - } else { - consumer_change_stream_state(msg.u.stream.stream_key, - msg.u.stream.state); - } -#endif - break; - } - default: - break; - } -end: - /* signal the poll thread */ - ret = write(ctx->consumer_poll_pipe[1], "4", 1); - if (ret < 0) { - perror("write consumer poll"); - } -end_nosignal: - return 0; -} - -int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan) -{ - struct lttng_ust_object_data obj; - - obj.handle = -1; - obj.shm_fd = chan->shm_fd; - obj.wait_fd = chan->wait_fd; - obj.memory_map_size = chan->mmap_len; - chan->handle = ustctl_map_channel(&obj); - if (!chan->handle) { - return -ENOMEM; - } - chan->wait_fd_is_copy = 1; - chan->shm_fd = -1; - - return 0; -} - -void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream) -{ - ustctl_flush_buffer(stream->chan->handle, stream->buf, 0); - stream->hangup_flush_done = 1; -} - -void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) -{ - ustctl_unmap_channel(chan->handle); -} - -int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream) -{ - struct lttng_ust_object_data obj; - int ret; - - obj.handle = -1; - obj.shm_fd = stream->shm_fd; - obj.wait_fd = stream->wait_fd; - obj.memory_map_size = stream->mmap_len; - ret = ustctl_add_stream(stream->chan->handle, &obj); - if (ret) - return ret; - stream->buf = ustctl_open_stream_read(stream->chan->handle, stream->cpu); - if (!stream->buf) - return -EBUSY; - /* ustctl_open_stream_read has closed the shm fd. */ - stream->wait_fd_is_copy = 1; - stream->shm_fd = -1; - - stream->mmap_base = ustctl_get_mmap_base(stream->chan->handle, stream->buf); - if (!stream->mmap_base) { - return -EINVAL; - } - - return 0; -} - -void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream) -{ - ustctl_close_stream_read(stream->chan->handle, stream->buf); -} - - -int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, - struct lttng_consumer_local_data *ctx) -{ - unsigned long len; - int err; - long ret = 0; - struct lttng_ust_shm_handle *handle; - struct lttng_ust_lib_ring_buffer *buf; - char dummy; - ssize_t readlen; - - DBG("In read_subbuffer (wait_fd: %d, stream key: %d)", - stream->wait_fd, stream->key); - - /* We can consume the 1 byte written into the wait_fd by UST */ - if (!stream->hangup_flush_done) { - do { - readlen = read(stream->wait_fd, &dummy, 1); - } while (readlen == -1 && errno == -EINTR); - if (readlen == -1) { - ret = readlen; - goto end; - } - } - - buf = stream->buf; - handle = stream->chan->handle; - /* Get the next subbuffer */ - err = ustctl_get_next_subbuf(handle, buf); - if (err != 0) { - ret = -ret; /* ustctl_get_next_subbuf returns negative, caller expect positive. */ - /* - * This is a debug message even for single-threaded consumer, - * because poll() have more relaxed criterions than get subbuf, - * so get_subbuf may fail for short race windows where poll() - * would issue wakeups. - */ - DBG("Reserving sub buffer failed (everything is normal, " - "it is due to concurrency)"); - goto end; - } - assert(stream->output == LTTNG_EVENT_MMAP); - /* read the used subbuffer size */ - err = ustctl_get_padded_subbuf_size(handle, buf, &len); - assert(err == 0); - /* write the subbuffer to the tracefile */ - ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len); - if (ret < 0) { - /* - * display the error but continue processing to try - * to release the subbuffer - */ - ERR("Error writing to tracefile"); - } - err = ustctl_put_next_subbuf(handle, buf); - assert(err == 0); -end: - return ret; -} - -int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream) -{ - int ret; - - /* Opening the tracefile in write mode */ - if (stream->path_name != NULL) { - ret = run_as_open(stream->path_name, - O_WRONLY|O_CREAT|O_TRUNC, - S_IRWXU|S_IRWXG|S_IRWXO, - stream->uid, stream->gid); - if (ret < 0) { - ERR("Opening %s", stream->path_name); - perror("open"); - goto error; - } - stream->out_fd = ret; - } - - /* we return 0 to let the library handle the FD internally */ - return 0; - -error: - return ret; -} diff --git a/liblttngctl/Makefile.am b/liblttngctl/Makefile.am deleted file mode 100644 index 87b94dc86..000000000 --- a/liblttngctl/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -lib_LTLIBRARIES = liblttngctl.la - -liblttngctl_la_SOURCES = lttngctl.c - -liblttngctl_la_LIBADD = \ - $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la diff --git a/liblttngctl/lttngctl.c b/liblttngctl/lttngctl.c deleted file mode 100644 index c65760155..000000000 --- a/liblttngctl/lttngctl.c +++ /dev/null @@ -1,906 +0,0 @@ -/* - * liblttngctl.c - * - * Linux Trace Toolkit Control Library - * - * Copyright (C) 2011 David Goulet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; only - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Socket to session daemon for communication */ -static int sessiond_socket; -static char sessiond_sock_path[PATH_MAX]; - -/* Variables */ -static char *tracing_group; -static int connected; - -/* - * Copy string from src to dst and enforce null terminated byte. - */ -static void copy_string(char *dst, const char *src, size_t len) -{ - if (src && dst) { - strncpy(dst, src, len); - /* Enforce the NULL terminated byte */ - dst[len - 1] = '\0'; - } else if (dst) { - dst[0] = '\0'; - } -} - -/* - * Copy domain to lttcomm_session_msg domain. - * - * If domain is unknown, default domain will be the kernel. - */ -static void copy_lttng_domain(struct lttng_domain *dst, struct lttng_domain *src) -{ - if (src && dst) { - switch (src->type) { - case LTTNG_DOMAIN_KERNEL: - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - memcpy(dst, src, sizeof(struct lttng_domain)); - break; - default: - dst->type = LTTNG_DOMAIN_KERNEL; - break; - } - } -} - -/* - * Send lttcomm_session_msg to the session daemon. - * - * On success, return 0 - * On error, return error code - */ -static int send_session_msg(struct lttcomm_session_msg *lsm) -{ - int ret; - - if (!connected) { - ret = -ENOTCONN; - goto end; - } - - ret = lttcomm_send_creds_unix_sock(sessiond_socket, lsm, - sizeof(struct lttcomm_session_msg)); - -end: - return ret; -} - -/* - * Receive data from the sessiond socket. - * - * On success, return 0 - * On error, return recv() error code - */ -static int recv_data_sessiond(void *buf, size_t len) -{ - int ret; - - if (!connected) { - ret = -ENOTCONN; - goto end; - } - - ret = lttcomm_recv_unix_sock(sessiond_socket, buf, len); - -end: - return ret; -} - -/* - * Check if the specified group name exist. - * - * If yes return 1, else return -1. - */ -static int check_tracing_group(const char *grp_name) -{ - struct group *grp_tracing; /* no free(). See getgrnam(3) */ - gid_t *grp_list; - int grp_list_size, grp_id, i; - int ret = -1; - - /* Get GID of group 'tracing' */ - grp_tracing = getgrnam(grp_name); - if (grp_tracing == NULL) { - /* NULL means not found also. getgrnam(3) */ - if (errno != 0) { - perror("getgrnam"); - } - goto end; - } - - /* Get number of supplementary group IDs */ - grp_list_size = getgroups(0, NULL); - if (grp_list_size < 0) { - perror("getgroups"); - goto end; - } - - /* Alloc group list of the right size */ - grp_list = malloc(grp_list_size * sizeof(gid_t)); - if (!grp_list) { - ret = -1; - goto end; - } - grp_id = getgroups(grp_list_size, grp_list); - if (grp_id < -1) { - perror("getgroups"); - goto free_list; - } - - for (i = 0; i < grp_list_size; i++) { - if (grp_list[i] == grp_tracing->gr_gid) { - ret = 1; - break; - } - } - -free_list: - free(grp_list); - -end: - return ret; -} - -/* - * Try connect to session daemon with sock_path. - * - * Return 0 on success, else -1 - */ -static int try_connect_sessiond(const char *sock_path) -{ - int ret; - - /* If socket exist, we check if the daemon listens for connect. */ - ret = access(sock_path, F_OK); - if (ret < 0) { - /* Not alive */ - return -1; - } - - ret = lttcomm_connect_unix_sock(sock_path); - if (ret < 0) { - /* Not alive */ - return -1; - } - - ret = lttcomm_close_unix_sock(ret); - if (ret < 0) { - perror("lttcomm_close_unix_sock"); - } - - return 0; -} - -/* - * Set sessiond socket path by putting it in the global sessiond_sock_path - * variable. - */ -static int set_session_daemon_path(void) -{ - int ret; - int in_tgroup = 0; /* In tracing group */ - uid_t uid; - - uid = getuid(); - - if (uid != 0) { - /* Are we in the tracing group ? */ - in_tgroup = check_tracing_group(tracing_group); - } - - if (uid == 0) { - /* Root */ - copy_string(sessiond_sock_path, - DEFAULT_GLOBAL_CLIENT_UNIX_SOCK, - sizeof(sessiond_sock_path)); - } else if (in_tgroup) { - /* Tracing group */ - copy_string(sessiond_sock_path, - DEFAULT_GLOBAL_CLIENT_UNIX_SOCK, - sizeof(sessiond_sock_path)); - - ret = try_connect_sessiond(sessiond_sock_path); - if (ret < 0) { - /* Global session daemon not available */ - if (snprintf(sessiond_sock_path, sizeof(sessiond_sock_path), - DEFAULT_HOME_CLIENT_UNIX_SOCK, - getenv("HOME")) < 0) { - return -ENOMEM; - } - } - } else { - /* Not in tracing group and not root, default */ - if (snprintf(sessiond_sock_path, PATH_MAX, - DEFAULT_HOME_CLIENT_UNIX_SOCK, - getenv("HOME")) < 0) { - return -ENOMEM; - } - } - - return 0; -} - -/* - * Connect to the LTTng session daemon. - * - * On success, return 0. On error, return -1. - */ -static int connect_sessiond(void) -{ - int ret; - - ret = set_session_daemon_path(); - if (ret < 0) { - return ret; - } - - /* Connect to the sesssion daemon */ - ret = lttcomm_connect_unix_sock(sessiond_sock_path); - if (ret < 0) { - return ret; - } - - sessiond_socket = ret; - connected = 1; - - return 0; -} - -/* - * Clean disconnect the session daemon. - */ -static int disconnect_sessiond(void) -{ - int ret = 0; - - if (connected) { - ret = lttcomm_close_unix_sock(sessiond_socket); - sessiond_socket = 0; - connected = 0; - } - - return ret; -} - -/* - * Ask the session daemon a specific command and put the data into buf. - * - * Return size of data (only payload, not header). - */ -static int ask_sessiond(struct lttcomm_session_msg *lsm, void **buf) -{ - int ret; - size_t size; - void *data = NULL; - struct lttcomm_lttng_msg llm; - - ret = connect_sessiond(); - if (ret < 0) { - goto end; - } - - /* Send command to session daemon */ - ret = send_session_msg(lsm); - if (ret < 0) { - goto end; - } - - /* Get header from data transmission */ - ret = recv_data_sessiond(&llm, sizeof(llm)); - if (ret < 0) { - goto end; - } - - /* Check error code if OK */ - if (llm.ret_code != LTTCOMM_OK) { - ret = -llm.ret_code; - goto end; - } - - size = llm.data_size; - if (size == 0) { - /* If client free with size 0 */ - if (buf != NULL) { - *buf = NULL; - } - ret = 0; - goto end; - } - - data = (void*) malloc(size); - - /* Get payload data */ - ret = recv_data_sessiond(data, size); - if (ret < 0) { - free(data); - goto end; - } - - /* - * Extra protection not to dereference a NULL pointer. If buf is NULL at - * this point, an error is returned and data is freed. - */ - if (buf == NULL) { - ret = -1; - free(data); - goto end; - } - - *buf = data; - ret = size; - -end: - disconnect_sessiond(); - return ret; -} - -/* - * Create lttng handle and return pointer. - */ -struct lttng_handle *lttng_create_handle(const char *session_name, - struct lttng_domain *domain) -{ - struct lttng_handle *handle; - - handle = malloc(sizeof(struct lttng_handle)); - if (handle == NULL) { - perror("malloc handle"); - goto end; - } - - /* Copy session name */ - copy_string(handle->session_name, session_name, - sizeof(handle->session_name)); - - /* Copy lttng domain */ - copy_lttng_domain(&handle->domain, domain); - -end: - return handle; -} - -/* - * Destroy handle by free(3) the pointer. - */ -void lttng_destroy_handle(struct lttng_handle *handle) -{ - if (handle) { - free(handle); - } -} - -/* - * Register an outside consumer. - */ -int lttng_register_consumer(struct lttng_handle *handle, - const char *socket_path) -{ - struct lttcomm_session_msg lsm; - - lsm.cmd_type = LTTNG_REGISTER_CONSUMER; - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - copy_lttng_domain(&lsm.domain, &handle->domain); - - copy_string(lsm.u.reg.path, socket_path, sizeof(lsm.u.reg.path)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Start tracing for all trace of the session. - */ -int lttng_start_tracing(const char *session_name) -{ - struct lttcomm_session_msg lsm; - - if (session_name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_START_TRACE; - - copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Stop tracing for all trace of the session. - */ -int lttng_stop_tracing(const char *session_name) -{ - struct lttcomm_session_msg lsm; - - if (session_name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_STOP_TRACE; - - copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Add context to event or/and channel. - */ -int lttng_add_context(struct lttng_handle *handle, - struct lttng_event_context *ctx, const char *event_name, - const char *channel_name) -{ - struct lttcomm_session_msg lsm; - - /* Safety check. Both are mandatory */ - if (handle == NULL || ctx == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_ADD_CONTEXT; - - /* Copy channel name */ - copy_string(lsm.u.context.channel_name, channel_name, - sizeof(lsm.u.context.channel_name)); - /* Copy event name */ - copy_string(lsm.u.context.event_name, event_name, - sizeof(lsm.u.context.event_name)); - - copy_lttng_domain(&lsm.domain, &handle->domain); - - memcpy(&lsm.u.context.ctx, ctx, sizeof(struct lttng_event_context)); - - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Enable event - */ -int lttng_enable_event(struct lttng_handle *handle, - struct lttng_event *ev, const char *channel_name) -{ - struct lttcomm_session_msg lsm; - - if (handle == NULL || ev == NULL) { - return -1; - } - - /* If no channel name, we put the default name */ - if (channel_name == NULL) { - copy_string(lsm.u.enable.channel_name, DEFAULT_CHANNEL_NAME, - sizeof(lsm.u.enable.channel_name)); - } else { - copy_string(lsm.u.enable.channel_name, channel_name, - sizeof(lsm.u.enable.channel_name)); - } - - copy_lttng_domain(&lsm.domain, &handle->domain); - - if (ev->name[0] != '\0') { - lsm.cmd_type = LTTNG_ENABLE_EVENT; - } else { - lsm.cmd_type = LTTNG_ENABLE_ALL_EVENT; - } - memcpy(&lsm.u.enable.event, ev, sizeof(lsm.u.enable.event)); - - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Disable event of a channel and domain. - */ -int lttng_disable_event(struct lttng_handle *handle, const char *name, - const char *channel_name) -{ - struct lttcomm_session_msg lsm; - - if (handle == NULL) { - return -1; - } - - if (channel_name) { - copy_string(lsm.u.disable.channel_name, channel_name, - sizeof(lsm.u.disable.channel_name)); - } else { - copy_string(lsm.u.disable.channel_name, DEFAULT_CHANNEL_NAME, - sizeof(lsm.u.disable.channel_name)); - } - - copy_lttng_domain(&lsm.domain, &handle->domain); - - if (name != NULL) { - copy_string(lsm.u.disable.name, name, sizeof(lsm.u.disable.name)); - lsm.cmd_type = LTTNG_DISABLE_EVENT; - } else { - lsm.cmd_type = LTTNG_DISABLE_ALL_EVENT; - } - - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Enable channel per domain - */ -int lttng_enable_channel(struct lttng_handle *handle, - struct lttng_channel *chan) -{ - struct lttcomm_session_msg lsm; - - /* - * NULL arguments are forbidden. No default values. - */ - if (handle == NULL || chan == NULL) { - return -1; - } - - memcpy(&lsm.u.channel.chan, chan, sizeof(lsm.u.channel.chan)); - - lsm.cmd_type = LTTNG_ENABLE_CHANNEL; - - copy_lttng_domain(&lsm.domain, &handle->domain); - - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * All tracing will be stopped for registered events of the channel. - */ -int lttng_disable_channel(struct lttng_handle *handle, const char *name) -{ - struct lttcomm_session_msg lsm; - - /* Safety check. Both are mandatory */ - if (handle == NULL || name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_DISABLE_CHANNEL; - - copy_string(lsm.u.disable.channel_name, name, - sizeof(lsm.u.disable.channel_name)); - - copy_lttng_domain(&lsm.domain, &handle->domain); - - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * List all available tracepoints of domain. - * - * Return the size (bytes) of the list and set the events array. - * On error, return negative value. - */ -int lttng_list_tracepoints(struct lttng_handle *handle, - struct lttng_event **events) -{ - int ret; - struct lttcomm_session_msg lsm; - - if (handle == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_LIST_TRACEPOINTS; - copy_lttng_domain(&lsm.domain, &handle->domain); - - ret = ask_sessiond(&lsm, (void **) events); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_event); -} - -/* - * Return a human readable string of code - */ -const char *lttng_strerror(int code) -{ - if (code > -LTTCOMM_OK) { - return "Ended with errors"; - } - - return lttcomm_get_readable_code(code); -} - -/* - * Create a brand new session using name. - */ -int lttng_create_session(const char *name, const char *path) -{ - struct lttcomm_session_msg lsm; - - lsm.cmd_type = LTTNG_CREATE_SESSION; - copy_string(lsm.session.name, name, sizeof(lsm.session.name)); - copy_string(lsm.session.path, path, sizeof(lsm.session.path)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Destroy session using name. - */ -int lttng_destroy_session(const char *session_name) -{ - struct lttcomm_session_msg lsm; - - if (session_name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_DESTROY_SESSION; - - copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Ask the session daemon for all available sessions. - * - * Return number of session. - * On error, return negative value. - */ -int lttng_list_sessions(struct lttng_session **sessions) -{ - int ret; - struct lttcomm_session_msg lsm; - - lsm.cmd_type = LTTNG_LIST_SESSIONS; - ret = ask_sessiond(&lsm, (void**) sessions); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_session); -} - -/* - * List domain of a session. - */ -int lttng_list_domains(const char *session_name, - struct lttng_domain **domains) -{ - int ret; - struct lttcomm_session_msg lsm; - - if (session_name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_LIST_DOMAINS; - - copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); - - ret = ask_sessiond(&lsm, (void**) domains); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_domain); -} - -/* - * List channels of a session - */ -int lttng_list_channels(struct lttng_handle *handle, - struct lttng_channel **channels) -{ - int ret; - struct lttcomm_session_msg lsm; - - if (handle == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_LIST_CHANNELS; - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - - copy_lttng_domain(&lsm.domain, &handle->domain); - - ret = ask_sessiond(&lsm, (void**) channels); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_channel); -} - -/* - * List events of a session channel. - */ -int lttng_list_events(struct lttng_handle *handle, - const char *channel_name, struct lttng_event **events) -{ - int ret; - struct lttcomm_session_msg lsm; - - /* Safety check. An handle and channel name are mandatory */ - if (handle == NULL || channel_name == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_LIST_EVENTS; - copy_string(lsm.session.name, handle->session_name, - sizeof(lsm.session.name)); - copy_string(lsm.u.list.channel_name, channel_name, - sizeof(lsm.u.list.channel_name)); - - copy_lttng_domain(&lsm.domain, &handle->domain); - - ret = ask_sessiond(&lsm, (void**) events); - if (ret < 0) { - return ret; - } - - return ret / sizeof(struct lttng_event); -} - -/* - * Set tracing group variable with name. This function allocate memory pointed - * by tracing_group. - */ -int lttng_set_tracing_group(const char *name) -{ - if (name == NULL) { - return -1; - } - - if (asprintf(&tracing_group, "%s", name) < 0) { - return -ENOMEM; - } - - return 0; -} - -/* - * lttng_calibrate - */ -int lttng_calibrate(struct lttng_handle *handle, - struct lttng_calibrate *calibrate) -{ - struct lttcomm_session_msg lsm; - - /* Safety check. NULL pointer are forbidden */ - if (handle == NULL || calibrate == NULL) { - return -1; - } - - lsm.cmd_type = LTTNG_CALIBRATE; - copy_lttng_domain(&lsm.domain, &handle->domain); - - memcpy(&lsm.u.calibrate, calibrate, sizeof(lsm.u.calibrate)); - - return ask_sessiond(&lsm, NULL); -} - -/* - * Set default channel attributes. - */ -void lttng_channel_set_default_attr(struct lttng_domain *domain, - struct lttng_channel_attr *attr) -{ - /* Safety check */ - if (attr == NULL || domain == NULL) { - return; - } - - switch (domain->type) { - case LTTNG_DOMAIN_KERNEL: - attr->overwrite = DEFAULT_CHANNEL_OVERWRITE; - attr->switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER; - attr->read_timer_interval = DEFAULT_CHANNEL_READ_TIMER; - - attr->subbuf_size = DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE; - attr->num_subbuf = DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM; - attr->output = DEFAULT_KERNEL_CHANNEL_OUTPUT; - break; - case LTTNG_DOMAIN_UST: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - attr->overwrite = DEFAULT_CHANNEL_OVERWRITE; - attr->switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER; - attr->read_timer_interval = DEFAULT_CHANNEL_READ_TIMER; - - attr->subbuf_size = DEFAULT_UST_CHANNEL_SUBBUF_SIZE; - attr->num_subbuf = DEFAULT_UST_CHANNEL_SUBBUF_NUM; - attr->output = DEFAULT_UST_CHANNEL_OUTPUT; - break; - default: - /* Default behavior */ - memset(attr, 0, sizeof(struct lttng_channel_attr)); - break; - } -} - -/* - * Check if session daemon is alive. - * - * Return 1 if alive or 0 if not. - * On error return -1 - */ -int lttng_session_daemon_alive(void) -{ - int ret; - - ret = set_session_daemon_path(); - if (ret < 0) { - /* Error */ - return ret; - } - - if (strlen(sessiond_sock_path) == 0) { - /* No socket path set. Weird error */ - return -1; - } - - ret = try_connect_sessiond(sessiond_sock_path); - if (ret < 0) { - /* Not alive */ - return 0; - } - - /* Is alive */ - return 1; -} - -/* - * lib constructor - */ -static void __attribute__((constructor)) init() -{ - /* Set default session group */ - lttng_set_tracing_group(LTTNG_DEFAULT_TRACING_GROUP); -} diff --git a/lttng-consumerd/Makefile.am b/lttng-consumerd/Makefile.am deleted file mode 100644 index 9e8d036da..000000000 --- a/lttng-consumerd/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include - -bin_PROGRAMS = lttng-consumerd - -lttng_consumerd_SOURCES = lttng-consumerd.c - -lttng_consumerd_LDADD = \ - $(top_builddir)/libkernelctl/libkernelctl.la \ - $(top_builddir)/liblttng-consumer/liblttng-consumer.la \ - $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ - $(top_builddir)/common/libcommon.la - -if HAVE_LIBLTTNG_UST_CTL -lttng_consumerd_LDADD += -llttng-ust-ctl -endif diff --git a/lttng-consumerd/lttng-consumerd.c b/lttng-consumerd/lttng-consumerd.c deleted file mode 100644 index e8e511702..000000000 --- a/lttng-consumerd/lttng-consumerd.c +++ /dev/null @@ -1,351 +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. - */ - -#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 -#include -#include - -/* TODO : support UST (all direct kernctl accesses). */ - -/* the two threads (receive fd and poll) */ -static pthread_t threads[2]; - -/* to count the number of time the user pressed ctrl+c */ -static int sigintcount = 0; - -/* Argument variables */ -int opt_quiet; -int opt_verbose; -static int opt_daemon; -static const char *progname; -static char command_sock_path[PATH_MAX]; /* Global command socket path */ -static char error_sock_path[PATH_MAX]; /* Global error path */ -static enum lttng_consumer_type opt_type = LTTNG_CONSUMER_KERNEL; - -/* the liblttngconsumerd context */ -static struct lttng_consumer_local_data *ctx; - -/* - * Signal handler for the daemon - */ -static void sighandler(int sig) -{ - if (sig == SIGINT && sigintcount++ == 0) { - DBG("ignoring first SIGINT"); - return; - } - - lttng_consumer_should_exit(ctx); -} - -/* - * 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; - } - - return ret; -} - -/* - * 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, --consumerd-cmd-sock PATH " - "Specify path for the command socket\n"); - fprintf(stderr, " -e, --consumerd-err-sock PATH " - "Specify path for the error socket\n"); - fprintf(stderr, " -d, --daemonize " - "Start as a daemon.\n"); - fprintf(stderr, " -q, --quiet " - "No output at all.\n"); - fprintf(stderr, " -v, --verbose " - "Verbose mode. Activate DBG() macro.\n"); - fprintf(stderr, " -V, --version " - "Show version number.\n"); - fprintf(stderr, " -k, --kernel " - "Consumer kernel buffers (default).\n"); - fprintf(stderr, " -u, --ust " - "Consumer UST buffers.%s\n", -#ifdef HAVE_LIBLTTNG_UST_CTL - "" -#else - " (support not compiled in)" -#endif - ); -} - -/* - * daemon argument parsing - */ -static void parse_args(int argc, char **argv) -{ - int c; - - static struct option long_options[] = { - { "consumerd-cmd-sock", 1, 0, 'c' }, - { "consumerd-err-sock", 1, 0, 'e' }, - { "daemonize", 0, 0, 'd' }, - { "help", 0, 0, 'h' }, - { "quiet", 0, 0, 'q' }, - { "verbose", 0, 0, 'v' }, - { "version", 0, 0, 'V' }, - { "kernel", 0, 0, 'k' }, -#ifdef HAVE_LIBLTTNG_UST_CTL - { "ust", 0, 0, 'u' }, -#endif - { NULL, 0, 0, 0 } - }; - - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "dhqvVku" "c:e:", 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(command_sock_path, PATH_MAX, "%s", optarg); - break; - case 'e': - snprintf(error_sock_path, PATH_MAX, "%s", optarg); - break; - case 'd': - opt_daemon = 1; - break; - case 'h': - usage(); - exit(EXIT_FAILURE); - case 'q': - opt_quiet = 1; - break; - case 'v': - opt_verbose = 1; - break; - case 'V': - fprintf(stdout, "%s\n", VERSION); - exit(EXIT_SUCCESS); - case 'k': - opt_type = LTTNG_CONSUMER_KERNEL; - break; -#ifdef HAVE_LIBLTTNG_UST_CTL - case 'u': -# if (CAA_BITS_PER_LONG == 64) - opt_type = LTTNG_CONSUMER64_UST; -# elif (CAA_BITS_PER_LONG == 32) - opt_type = LTTNG_CONSUMER32_UST; -# else -# error "Unknown bitness" -# endif - break; -#endif - default: - usage(); - exit(EXIT_FAILURE); - } - } -} - -/* - * main - */ -int main(int argc, char **argv) -{ - int i; - int ret = 0; - void *status; - - /* Parse arguments */ - progname = argv[0]; - parse_args(argc, argv); - - /* Daemonize */ - if (opt_daemon) { - ret = daemon(0, 0); - if (ret < 0) { - perror("daemon"); - goto error; - } - } - - if (strlen(command_sock_path) == 0) { - switch (opt_type) { - case LTTNG_CONSUMER_KERNEL: - snprintf(command_sock_path, PATH_MAX, KCONSUMERD_CMD_SOCK_PATH, - LTTNG_RUNDIR); - break; - case LTTNG_CONSUMER64_UST: - snprintf(command_sock_path, PATH_MAX, - USTCONSUMERD64_CMD_SOCK_PATH, LTTNG_RUNDIR); - break; - case LTTNG_CONSUMER32_UST: - snprintf(command_sock_path, PATH_MAX, - USTCONSUMERD32_CMD_SOCK_PATH, LTTNG_RUNDIR); - break; - default: - WARN("Unknown consumerd type"); - goto error; - } - } - - /* Init */ - lttng_consumer_init(); - - /* create the consumer instance with and assign the callbacks */ - ctx = lttng_consumer_create(opt_type, lttng_consumer_read_subbuffer, - NULL, lttng_consumer_on_recv_stream, NULL); - if (ctx == NULL) { - goto error; - } - - lttng_consumer_set_command_sock_path(ctx, command_sock_path); - if (strlen(error_sock_path) == 0) { - switch (opt_type) { - case LTTNG_CONSUMER_KERNEL: - snprintf(error_sock_path, PATH_MAX, KCONSUMERD_ERR_SOCK_PATH, - LTTNG_RUNDIR); - break; - case LTTNG_CONSUMER64_UST: - snprintf(error_sock_path, PATH_MAX, - USTCONSUMERD64_ERR_SOCK_PATH, LTTNG_RUNDIR); - break; - case LTTNG_CONSUMER32_UST: - snprintf(error_sock_path, PATH_MAX, - USTCONSUMERD32_ERR_SOCK_PATH, LTTNG_RUNDIR); - break; - default: - WARN("Unknown consumerd type"); - goto error; - } - } - - if (set_signal_handler() < 0) { - goto error; - } - - /* 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 lttng-sessiond will fail */ - if (ret < 0) { - WARN("Cannot connect to error socket, is lttng-sessiond started ?"); - } - lttng_consumer_set_error_sock(ctx, ret); - - /* Create the thread to manage the receive of fd */ - ret = pthread_create(&threads[0], NULL, lttng_consumer_thread_receive_fds, - (void *) ctx); - if (ret != 0) { - perror("pthread_create"); - goto error; - } - - /* Create thread to manage the polling/writing of traces */ - ret = pthread_create(&threads[1], NULL, lttng_consumer_thread_poll_fds, - (void *) ctx); - if (ret != 0) { - perror("pthread_create"); - goto error; - } - - for (i = 0; i < 2; i++) { - ret = pthread_join(threads[i], &status); - if (ret != 0) { - perror("pthread_join"); - goto error; - } - } - ret = EXIT_SUCCESS; - lttng_consumer_send_error(ctx, CONSUMERD_EXIT_SUCCESS); - goto end; - -error: - ret = EXIT_FAILURE; - lttng_consumer_send_error(ctx, CONSUMERD_EXIT_FAILURE); - -end: - lttng_consumer_destroy(ctx); - lttng_consumer_cleanup(); - - return ret; -} diff --git a/lttng-sessiond/Makefile.am b/lttng-sessiond/Makefile.am deleted file mode 100644 index 8fcea947e..000000000 --- a/lttng-sessiond/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include \ - -DINSTALL_BIN_PATH=\""$(bindir)"\" \ - -DINSTALL_LIB_PATH=\""$(libdir)"\" - -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 \ - compat/poll.h $(COMPAT) \ - trace-kernel.c trace-kernel.h \ - kernel.c kernel.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 \ - lttng-ust-ctl.h lttng-ust-abi.h - -if HAVE_LIBLTTNG_UST_CTL -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-common -lurcu \ - $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ - $(top_builddir)/libkernelctl/libkernelctl.la \ - $(top_builddir)/liblttngctl/liblttngctl.la \ - $(top_builddir)/common/libcommon.la \ - $(top_builddir)/liblttng-ht/liblttng-ht.la - -if HAVE_LIBLTTNG_UST_CTL -lttng_sessiond_LDADD += -llttng-ust-ctl -endif diff --git a/lttng-sessiond/channel.c b/lttng-sessiond/channel.c deleted file mode 100644 index 0e8b16720..000000000 --- a/lttng-sessiond/channel.c +++ /dev/null @@ -1,323 +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 "channel.h" -#include "kernel.h" -#include "ust-ctl.h" -#include "utils.h" -#include "ust-app.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("zmalloc 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; -} - -/* - * 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 && 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 *attr, int kernel_pipe) -{ - int ret; - struct lttng_channel *defattr = NULL; - - /* Creating channel attributes if needed */ - if (attr == NULL) { - defattr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL); - if (defattr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - attr = defattr; - } - - /* 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: - free(defattr); - return ret; -} - -/* - * Enable UST channel for session and domain. - */ -int channel_ust_enable(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan) -{ - int ret = LTTCOMM_OK; - - /* If already enabled, everything is OK */ - if (uchan->enabled) { - DBG3("Channel %s already enabled. Skipping", uchan->name); - goto end; - } - - switch (domain) { - case LTTNG_DOMAIN_UST: - DBG2("Channel %s being enabled in UST global domain", uchan->name); - /* Enable channel for global domain */ - ret = ust_app_enable_channel_glb(usess, uchan); - break; - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - if (ret < 0) { - if (ret != -EEXIST) { - ret = LTTCOMM_UST_CHAN_ENABLE_FAIL; - goto error; - } else { - ret = LTTCOMM_OK; - } - } - - uchan->enabled = 1; - DBG2("Channel %s enabled successfully", uchan->name); - -end: -error: - return ret; -} - -/* - * Create UST channel for session and domain. - */ -int channel_ust_create(struct ltt_ust_session *usess, int domain, - struct lttng_channel *attr) -{ - int ret = LTTCOMM_OK; - struct ltt_ust_channel *uchan = NULL; - struct lttng_channel *defattr = NULL; - - /* Creating channel attributes if needed */ - if (attr == NULL) { - defattr = channel_new_default_attr(domain); - if (defattr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - attr = defattr; - } - - /* Create UST channel */ - uchan = trace_ust_create_channel(attr, usess->pathname); - if (uchan == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - uchan->enabled = 1; - - switch (domain) { - case LTTNG_DOMAIN_UST: - DBG2("Channel %s being created in UST global domain", uchan->name); - - /* Enable channel for global domain */ - ret = ust_app_create_channel_glb(usess, uchan); - break; - case LTTNG_DOMAIN_UST_PID: - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - default: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error_free_chan; - } - - if (ret < 0 && ret != -EEXIST) { - ret = LTTCOMM_UST_CHAN_ENABLE_FAIL; - goto error_free_chan; - } - - /* Adding the channel to the channel hash table. */ - rcu_read_lock(); - lttng_ht_add_unique_str(usess->domain_global.channels, &uchan->node); - rcu_read_unlock(); - - DBG2("Channel %s created successfully", uchan->name); - - free(defattr); - return LTTCOMM_OK; - -error_free_chan: - /* - * No need to remove the channel from the hash table because at this point - * it was not added hence the direct call and no call_rcu(). - */ - trace_ust_destroy_channel(uchan); -error: - free(defattr); - return ret; -} - -/* - * Disable UST channel for session and domain. - */ -int channel_ust_disable(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan) -{ - int ret = LTTCOMM_OK; - - /* Already disabled */ - if (uchan->enabled == 0) { - DBG2("Channel UST %s already disabled", uchan->name); - goto end; - } - - /* Get the right channel's hashtable */ - switch (domain) { - case LTTNG_DOMAIN_UST: - DBG2("Channel %s being disabled in UST global domain", uchan->name); - /* Disable channel for global domain */ - ret = ust_app_disable_channel_glb(usess, uchan); - break; - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - if (ret < 0 && ret != -EEXIST) { - ret = LTTCOMM_UST_DISABLE_FAIL; - goto error; - } - - uchan->enabled = 0; - - DBG2("Channel %s disabled successfully", uchan->name); - - return LTTCOMM_OK; - -end: -error: - return ret; -} diff --git a/lttng-sessiond/channel.h b/lttng-sessiond/channel.h deleted file mode 100644 index 04052f6d7..000000000 --- a/lttng-sessiond/channel.h +++ /dev/null @@ -1,42 +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); - -struct lttng_channel *channel_new_default_attr(int domain); - -int channel_ust_create(struct ltt_ust_session *usess, int domain, - struct lttng_channel *attr); -int channel_ust_enable(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan); -int channel_ust_disable(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan); - -#endif /* _LTT_CHANNEL_H */ diff --git a/lttng-sessiond/compat/compat-epoll.c b/lttng-sessiond/compat/compat-epoll.c deleted file mode 100644 index aca80f331..000000000 --- a/lttng-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("zmalloc 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 deleted file mode 100644 index 50ef37472..000000000 --- a/lttng-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("zmalloc 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("zmalloc 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 deleted file mode 100644 index 3e804305f..000000000 --- a/lttng-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/lttng-sessiond/context.c b/lttng-sessiond/context.c deleted file mode 100644 index e29cc09a5..000000000 --- a/lttng-sessiond/context.c +++ /dev/null @@ -1,385 +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 "context.h" -#include "kernel.h" -#include "ust-app.h" -#include "trace-ust.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 UST context to channel. - */ -static int add_uctx_to_channel(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan, struct lttng_event_context *ctx) -{ - int ret; - struct ltt_ust_context *uctx; - - /* Create ltt UST context */ - uctx = trace_ust_create_context(ctx); - if (uctx == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - switch (domain) { - case LTTNG_DOMAIN_UST: - ret = ust_app_add_ctx_channel_glb(usess, uchan, uctx); - if (ret < 0) { - goto error; - } - break; - default: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - /* Add ltt UST context node to ltt UST channel */ - lttng_ht_add_unique_ulong(uchan->ctx, &uctx->node); - - return LTTCOMM_OK; - -error: - free(uctx); - return ret; -} - -/* - * Add UST context to event. - */ -static int add_uctx_to_event(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - struct lttng_event_context *ctx) -{ - int ret; - struct ltt_ust_context *uctx; - - /* Create ltt UST context */ - uctx = trace_ust_create_context(ctx); - if (uctx == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - switch (domain) { - case LTTNG_DOMAIN_UST: - ret = ust_app_add_ctx_event_glb(usess, uchan, uevent, uctx); - if (ret < 0) { - goto error; - } - break; - default: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - /* Add ltt UST context node to ltt UST event */ - lttng_ht_add_unique_ulong(uevent->ctx, &uctx->node); - - return LTTCOMM_OK; - -error: - free(uctx); - 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; -} - -/* - * Add UST context to tracer. - */ -int context_ust_add(struct ltt_ust_session *usess, int domain, - struct lttng_event_context *ctx, char *event_name, - char *channel_name) -{ - int ret = LTTCOMM_OK, have_event = 0; - struct lttng_ht_iter iter, uiter; - struct lttng_ht *chan_ht; - struct ltt_ust_channel *uchan = NULL; - struct ltt_ust_event *uevent = NULL; - - /* - * Define which channel's hashtable to use from the domain or quit if - * unknown domain. - */ - switch (domain) { - case LTTNG_DOMAIN_UST: - chan_ht = usess->domain_global.channels; - 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; - } - - /* Do we have an event name */ - if (strlen(event_name) != 0) { - have_event = 1; - } - - /* Get UST channel if defined */ - if (strlen(channel_name) != 0) { - uchan = trace_ust_find_channel_by_name(chan_ht, channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - } - - /* If UST channel specified and event name, get UST event ref */ - if (uchan && have_event) { - uevent = trace_ust_find_event_by_name(uchan->events, event_name); - if (uevent == NULL) { - ret = LTTCOMM_UST_EVENT_NOT_FOUND; - goto error; - } - } - - /* At this point, we have 4 possibilities */ - - if (uchan && uevent) { /* Add ctx to event in channel */ - ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); - } else if (uchan && !have_event) { /* Add ctx to channel */ - ret = add_uctx_to_channel(usess, domain, uchan, ctx); - } else if (!uchan && have_event) { /* Add ctx to event */ - /* Add context to event without having the channel name */ - cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) { - uevent = trace_ust_find_event_by_name(uchan->events, event_name); - if (uevent != NULL) { - ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); - /* - * LTTng UST does not allowed the same event to be registered - * multiple time in different or the same channel. So, if we - * found our event, we stop. - */ - goto end; - } - } - ret = LTTCOMM_UST_EVENT_NOT_FOUND; - goto error; - } else if (!uchan && !have_event) { /* Add ctx all events, all channels */ - /* For all channels */ - cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) { - ret = add_uctx_to_channel(usess, domain, uchan, ctx); - if (ret < 0) { - ERR("Context added to channel %s failed", uchan->name); - continue; - } - - /* For all events in channel */ - cds_lfht_for_each_entry(uchan->events->ht, &uiter.iter, uevent, - node.node) { - ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); - if (ret < 0) { - ERR("Context add to event %s in channel %s failed", - uevent->attr.name, uchan->name); - continue; - } - } - } - } - -end: - switch (ret) { - case -EEXIST: - ret = LTTCOMM_UST_CONTEXT_EXIST; - goto error; - case -ENOMEM: - ret = LTTCOMM_FATAL; - goto error; - } - - return LTTCOMM_OK; - -error: - return ret; -} diff --git a/lttng-sessiond/context.h b/lttng-sessiond/context.h deleted file mode 100644 index 4383f9abc..000000000 --- a/lttng-sessiond/context.h +++ /dev/null @@ -1,33 +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 - -#include "trace-kernel.h" -#include "trace-ust.h" -#include "ust-ctl.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 *usess, int domain, - struct lttng_event_context *ctx, char *event_name, - char *channel_name); - -#endif /* _LTT_CONTEXT_H */ diff --git a/lttng-sessiond/event.c b/lttng-sessiond/event.c deleted file mode 100644 index 106870743..000000000 --- a/lttng-sessiond/event.c +++ /dev/null @@ -1,556 +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 "channel.h" -#include "event.h" -#include "kernel.h" -#include "ust-ctl.h" -#include "ust-app.h" -#include "trace-kernel.h" -#include "trace-ust.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 = NULL; - - /* For each event in the kernel session */ - cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { - if (kevent->enabled == 0) { - 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) { - if (ret == -EEXIST) { - ret = LTTCOMM_KERN_EVENT_EXIST; - } else { - ret = LTTCOMM_KERN_ENABLE_FAIL; - } - 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; -} - -/* - * ============================ - * UST : The Ultimate Frontier! - * ============================ - */ - -/* - * Enable all UST tracepoints for a channel from a UST session. - */ -int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan) -{ - int ret, i; - size_t size; - struct lttng_ht_iter iter; - struct ltt_ust_event *uevent = NULL; - struct lttng_event *events = NULL; - - switch (domain) { - case LTTNG_DOMAIN_UST: - { - /* Enable existing events */ - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, - node.node) { - if (uevent->enabled == 0) { - ret = ust_app_enable_event_glb(usess, uchan, uevent); - if (ret < 0) { - continue; - } - uevent->enabled = 1; - } - } - - /* Get all UST available events */ - size = ust_app_list_events(&events); - if (size < 0) { - ret = LTTCOMM_UST_LIST_FAIL; - goto error; - } - - for (i = 0; i < size; i++) { - /* - * Check if event exist and if so, continue since it was enable - * previously. - */ - uevent = trace_ust_find_event_by_name(uchan->events, - events[i].name); - if (uevent != NULL) { - ret = ust_app_enable_event_pid(usess, uchan, uevent, - events[i].pid); - if (ret < 0) { - if (ret != -EEXIST) { - ret = LTTCOMM_UST_ENABLE_FAIL; - goto error; - } - } - continue; - } - - /* Create ust event */ - uevent = trace_ust_create_event(&events[i]); - if (uevent == NULL) { - ret = LTTCOMM_FATAL; - goto error_destroy; - } - - /* Create event for the specific PID */ - ret = ust_app_enable_event_pid(usess, uchan, uevent, - events[i].pid); - if (ret < 0) { - if (ret == -EEXIST) { - ret = LTTCOMM_UST_EVENT_EXIST; - goto error; - } else { - ret = LTTCOMM_UST_ENABLE_FAIL; - goto error_destroy; - } - } - - uevent->enabled = 1; - /* Add ltt ust event to channel */ - rcu_read_lock(); - lttng_ht_add_unique_str(uchan->events, &uevent->node); - rcu_read_unlock(); - } - - free(events); - 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; - } - - return LTTCOMM_OK; - -error_destroy: - trace_ust_destroy_event(uevent); - -error: - free(events); - return ret; -} - -/* - * Enable UST tracepoint event for a channel from a UST session. - */ -int event_ust_enable_tracepoint(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan, struct lttng_event *event) -{ - int ret = LTTCOMM_OK, to_create = 0; - struct ltt_ust_event *uevent; - - 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; - } - /* Valid to set it after the goto error since uevent is still NULL */ - to_create = 1; - } - - if (uevent->enabled) { - /* It's already enabled so everything is OK */ - goto end; - } - - uevent->enabled = 1; - - switch (domain) { - case LTTNG_DOMAIN_UST: - { - if (to_create) { - /* Create event on all UST registered apps for session */ - ret = ust_app_create_event_glb(usess, uchan, uevent); - } else { - /* Enable event on all UST registered apps for session */ - ret = ust_app_enable_event_glb(usess, uchan, uevent); - } - - if (ret < 0) { - if (ret == -EEXIST) { - ret = LTTCOMM_UST_EVENT_EXIST; - goto end; - } else { - 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 end; - } - - if (to_create) { - rcu_read_lock(); - /* Add ltt ust event to channel */ - lttng_ht_add_unique_str(uchan->events, &uevent->node); - rcu_read_unlock(); - } - - DBG("Event UST %s %s in channel %s", uevent->attr.name, - to_create ? "created" : "enabled", uchan->name); - - ret = LTTCOMM_OK; - -end: - return ret; - -error: - /* - * Only destroy event on creation time (not enabling time) because if the - * event is found in the channel (to_create == 0), it means that at some - * point the enable_event worked and it's thus valid to keep it alive. - * Destroying it also implies that we also destroy it's shadow copy to sync - * everyone up. - */ - if (to_create) { - /* In this code path, the uevent was not added to the hash table */ - trace_ust_destroy_event(uevent); - } - return ret; -} - -/* - * Disable UST tracepoint of a channel from a UST session. - */ -int event_ust_disable_tracepoint(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan, char *event_name) -{ - int ret; - struct ltt_ust_event *uevent; - - uevent = trace_ust_find_event_by_name(uchan->events, event_name); - if (uevent == NULL) { - ret = LTTCOMM_UST_EVENT_NOT_FOUND; - goto error; - } - - if (uevent->enabled == 0) { - /* It's already enabled so everything is OK */ - ret = LTTCOMM_OK; - goto end; - } - - switch (domain) { - case LTTNG_DOMAIN_UST: - ret = ust_app_disable_event_glb(usess, uchan, uevent); - if (ret < 0 && ret != -EEXIST) { - ret = LTTCOMM_UST_DISABLE_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; - } - - uevent->enabled = 0; - ret = LTTCOMM_OK; - -end: - DBG2("Event UST %s disabled in channel %s", uevent->attr.name, - uchan->name); - -error: - return ret; -} - -/* - * Disable all UST tracepoints for a channel from a UST session. - */ -int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan) -{ - int ret, i; - size_t size; - struct lttng_ht_iter iter; - struct ltt_ust_event *uevent = NULL; - struct lttng_event *events = NULL; - - switch (domain) { - case LTTNG_DOMAIN_UST: - { - /* Disabling existing events */ - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, - node.node) { - if (uevent->enabled == 1) { - ret = ust_app_disable_event_glb(usess, uchan, uevent); - if (ret < 0) { - continue; - } - uevent->enabled = 0; - } - } - - /* Get all UST available events */ - size = ust_app_list_events(&events); - if (size < 0) { - ret = LTTCOMM_UST_LIST_FAIL; - goto error; - } - - for (i = 0; i < size; i++) { - uevent = trace_ust_find_event_by_name(uchan->events, - events[i].name); - if (uevent != NULL && uevent->enabled == 1) { - ret = ust_app_disable_event_pid(usess, uchan, uevent, - events[i].pid); - if (ret < 0 && ret != -EEXIST) { - ret = LTTCOMM_UST_DISABLE_FAIL; - goto error; - } - uevent->enabled = 0; - continue; - } - } - - free(events); - 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; - } - - return LTTCOMM_OK; - -error: - free(events); - return ret; -} diff --git a/lttng-sessiond/event.h b/lttng-sessiond/event.h deleted file mode 100644 index 9ea0e4621..000000000 --- a/lttng-sessiond/event.h +++ /dev/null @@ -1,52 +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 *usess, int domain, - struct ltt_ust_channel *uchan, struct lttng_event *event); -int event_ust_disable_tracepoint(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan, char *event_name); -int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan); -int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain, - struct ltt_ust_channel *uchan); - -#endif /* _LTT_EVENT_H */ diff --git a/lttng-sessiond/futex.c b/lttng-sessiond/futex.c deleted file mode 100644 index e01771e6e..000000000 --- a/lttng-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 (caa_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 deleted file mode 100644 index a056ec22b..000000000 --- a/lttng-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/lttng-sessiond/kernel.c b/lttng-sessiond/kernel.c deleted file mode 100644 index 67c0d6fe9..000000000 --- a/lttng-sessiond/kernel.c +++ /dev/null @@ -1,608 +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.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 = zmalloc(sizeof(struct lttng_kernel_context)); - if (chan->ctx == NULL) { - perror("zmalloc 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 = zmalloc(sizeof(struct lttng_kernel_context)); - if (event->ctx == NULL) { - perror("zmalloc 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 = zmalloc(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 * sizeof(struct lttng_event)); - 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'; - elist[count].enabled = -1; - 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.h b/lttng-sessiond/kernel.h deleted file mode 100644 index 2fbaca91e..000000000 --- a/lttng-sessiond/kernel.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/lttng-sessiond/lttng-sessiond.h b/lttng-sessiond/lttng-sessiond.h deleted file mode 100644 index 7de024f80..000000000 --- a/lttng-sessiond/lttng-sessiond.h +++ /dev/null @@ -1,95 +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 ucred creds; - 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 deleted file mode 100644 index 28a040763..000000000 --- a/lttng-sessiond/lttng-ust-abi.h +++ /dev/null @@ -1,168 +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 256 - -#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, - LTTNG_UST_TRACEPOINT_LOGLEVEL = 3, -}; - -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 */ -}; - -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_tracepoint_iter { - char name[LTTNG_UST_SYM_NAME_LEN]; /* provider:name */ - char loglevel[LTTNG_UST_SYM_NAME_LEN]; /* loglevel */ - int64_t loglevel_value; -}; - -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) - -/* Tracepoint list commands */ -#define LTTNG_UST_TRACEPOINT_LIST_GET _UST_CMD(0x90) - -#define LTTNG_UST_ROOT_HANDLE 0 - -struct lttng_ust_obj; - -union ust_args { - struct { - int *shm_fd; - int *wait_fd; - uint64_t *memory_map_size; - } channel; - struct { - int *shm_fd; - int *wait_fd; - uint64_t *memory_map_size; - } stream; -}; - -struct lttng_ust_objd_ops { - long (*cmd)(int objd, unsigned int cmd, unsigned long arg, - union ust_args *args); - int (*release)(int objd); -}; - -/* Create root handle. Always ID 0. */ -int lttng_abi_create_root_handle(void); - -const struct lttng_ust_objd_ops *objd_ops(int id); -int lttng_ust_objd_unref(int id); - -void lttng_ust_abi_exit(void); -void lttng_ust_events_exit(void); - -#endif /* _LTTNG_UST_ABI_H */ diff --git a/lttng-sessiond/lttng-ust-ctl.h b/lttng-sessiond/lttng-ust-ctl.h deleted file mode 100644 index b8cb965b0..000000000 --- a/lttng-sessiond/lttng-ust-ctl.h +++ /dev/null @@ -1,139 +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 "lttng-ust-abi.h" - -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); - -/* - * ustctl_tracepoint_list returns a tracepoint list handle, or negative - * error value. - */ -int ustctl_tracepoint_list(int sock); -/* - * ustctl_tracepoint_list_get is used to iterate on the tp list - * handle. End is iteration is reached when -ENOENT is returned. - */ -int ustctl_tracepoint_list_get(int sock, int tp_list_handle, - struct lttng_ust_tracepoint_iter *iter); - -int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v); -int ustctl_wait_quiescent(int sock); - -int ustctl_sock_flush_buffer(int sock, struct lttng_ust_object_data *object); - -/* 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); - -void ustctl_flush_buffer(struct lttng_ust_shm_handle *handle, - struct lttng_ust_lib_ring_buffer *buf, - int producer_active); - -/* Release object created by members of this API */ -int ustctl_release_object(int sock, struct lttng_ust_object_data *data); -/* Release handle returned by create session. */ -int ustctl_release_handle(int sock, int handle); - -#endif /* _LTTNG_UST_CTL_H */ diff --git a/lttng-sessiond/main.c b/lttng-sessiond/main.c deleted file mode 100644 index 7d8546d8e..000000000 --- a/lttng-sessiond/main.c +++ /dev/null @@ -1,4569 +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 -#include "common/runas.h" - -#include "channel.h" -#include "compat/poll.h" -#include "context.h" -#include "event.h" -#include "futex.h" -#include "kernel.h" -#include "lttng-sessiond.h" -#include "shm.h" -#include "ust-app.h" -#include "ust-ctl.h" -#include "utils.h" - -#define CONSUMERD_FILE "lttng-consumerd" - -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 opt_no_kernel; -static int is_root; /* Set to 1 if the daemon is running as root */ -static pid_t ppid; /* Parent PID for --sig-parent option */ -static char *rundir; - -/* Consumer daemon specific control data */ -static struct consumer_data kconsumer_data = { - .type = LTTNG_CONSUMER_KERNEL, - .err_unix_sock_path = KCONSUMERD_ERR_SOCK_PATH, - .cmd_unix_sock_path = KCONSUMERD_CMD_SOCK_PATH, -}; -static struct consumer_data ustconsumer64_data = { - .type = LTTNG_CONSUMER64_UST, - .err_unix_sock_path = USTCONSUMERD64_ERR_SOCK_PATH, - .cmd_unix_sock_path = USTCONSUMERD64_CMD_SOCK_PATH, -}; -static struct consumer_data ustconsumer32_data = { - .type = LTTNG_CONSUMER32_UST, - .err_unix_sock_path = USTCONSUMERD32_ERR_SOCK_PATH, - .cmd_unix_sock_path = USTCONSUMERD32_CMD_SOCK_PATH, -}; - -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; - -int ust_consumerd64_fd = -1; -int ust_consumerd32_fd = -1; - -static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN; -static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; -static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; -static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; - -static -void setup_consumerd_path(void) -{ - const char *bin, *libdir; - - /* - * Allow INSTALL_BIN_PATH to be used as a target path for the - * native architecture size consumer if CONFIG_CONSUMER*_PATH - * has not been defined. - */ -#if (CAA_BITS_PER_LONG == 32) - if (!consumerd32_bin[0]) { - consumerd32_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE; - } - if (!consumerd32_libdir[0]) { - consumerd32_libdir = INSTALL_LIB_PATH; - } -#elif (CAA_BITS_PER_LONG == 64) - if (!consumerd64_bin[0]) { - consumerd64_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE; - } - if (!consumerd64_libdir[0]) { - consumerd64_libdir = INSTALL_LIB_PATH; - } -#else -#error "Unknown bitness" -#endif - - /* - * runtime env. var. overrides the build default. - */ - bin = getenv("LTTNG_CONSUMERD32_BIN"); - if (bin) { - consumerd32_bin = bin; - } - bin = getenv("LTTNG_CONSUMERD64_BIN"); - if (bin) { - consumerd64_bin = bin; - } - libdir = getenv("LTTNG_TOOLS_CONSUMERD32_LIBDIR"); - if (libdir) { - consumerd32_libdir = libdir; - } - libdir = getenv("LTTNG_TOOLS_CONSUMERD64_LIBDIR"); - if (libdir) { - consumerd64_libdir = libdir; - } -} - -/* - * 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) { - DBG3("No kernel session when tearingdown session"); - return; - } - - 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); -} - -/* - * 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) -{ - int ret; - - if (!session->ust_session) { - DBG3("No UST session when tearingdown session"); - return; - } - - DBG("Tearing down UST session(s)"); - - ret = ust_app_destroy_trace_all(session->ust_session); - if (ret) { - ERR("Error in ust_app_destroy_trace_all"); - } - - 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"); - - DBG("Removing %s directory", rundir); - ret = asprintf(&cmd, "rm -rf %s", 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 %s", rundir); - } - free(cmd); - - 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); - - if (is_root && !opt_no_kernel) { - DBG2("Closing kernel fd"); - close(kernel_tracer_fd); - DBG("Unloading kernel modules"); - modprobe_remove_kernel_modules(); - } - - close(thread_quit_pipe[0]); - close(thread_quit_pipe[1]); - - /* */ - DBG("%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); - /* */ -} - -/* - * 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, - uid_t uid, gid_t gid) -{ - 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 */ - lkm.u.stream.uid = uid; - lkm.u.stream.gid = gid; - 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 */ - lkm.u.stream.uid = session->uid; - lkm.u.stream.gid = session->gid; - 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, - session->uid, session->gid); - 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 = zmalloc(sizeof(struct lttcomm_lttng_msg) + buf_size); - if (cmd_ctx->llm == NULL) { - perror("zmalloc"); - 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, - session->uid, session->gid); - if (ret < 0) { - goto error; - } - } - goto error; - } - } - session_unlock(session); - } - session_unlock_list(); - return ret; - -error: - session_unlock(session); - session_unlock_list(); - return ret; -} - -/* - * For each tracing session, update newly registered apps. - */ -static void update_ust_app(int app_sock) -{ - struct ltt_session *sess, *stmp; - - /* For all tracing session(s) */ - cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { - if (sess->ust_session) { - ust_app_global_update(sess->ust_session, app_sock); - } - } -} - -/* - * 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 == -ENOMEM) { - goto error; - } else if (ret < 0) { - break; - } - - /* - * Add channel(s) and event(s) to newly registered apps - * from lttng global UST domain. - */ - update_ust_app(ust_cmd.sock); - - ret = ust_app_register_done(ust_cmd.sock); - if (ret < 0) { - /* - * If the registration is not possible, we simply - * unregister the apps and continue - */ - ust_app_unregister(ust_cmd.sock); - } else { - /* - * We 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 on remote end. */ - 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 = zmalloc(sizeof(struct ust_command)); - if (ust_cmd == NULL) { - perror("ust command zmalloc"); - 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 *consumer_to_use; - const char *verbosity; - struct stat st; - - DBG("Spawning consumerd"); - - pid = fork(); - if (pid == 0) { - /* - * Exec consumerd. - */ - if (opt_verbose_consumer) { - verbosity = "--verbose"; - } else { - verbosity = "--quiet"; - } - switch (consumer_data->type) { - case LTTNG_CONSUMER_KERNEL: - /* - * Find out which consumerd to execute. We will first try the - * 64-bit path, then the sessiond's installation directory, and - * fallback on the 32-bit one, - */ - DBG3("Looking for a kernel consumer at these locations:"); - DBG3(" 1) %s", consumerd64_bin); - DBG3(" 2) %s/%s", INSTALL_BIN_PATH, CONSUMERD_FILE); - DBG3(" 3) %s", consumerd32_bin); - if (stat(consumerd64_bin, &st) == 0) { - DBG3("Found location #1"); - consumer_to_use = consumerd64_bin; - } else if (stat(INSTALL_BIN_PATH "/" CONSUMERD_FILE, &st) == 0) { - DBG3("Found location #2"); - consumer_to_use = INSTALL_BIN_PATH "/" CONSUMERD_FILE; - } else if (stat(consumerd32_bin, &st) == 0) { - DBG3("Found location #3"); - consumer_to_use = consumerd32_bin; - } else { - DBG("Could not find any valid consumerd executable"); - break; - } - DBG("Using kernel consumer at: %s", consumer_to_use); - execl(consumer_to_use, - "lttng-consumerd", verbosity, "-k", - "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, - "--consumerd-err-sock", consumer_data->err_unix_sock_path, - NULL); - break; - case LTTNG_CONSUMER64_UST: - { - char *tmpnew = NULL; - - if (consumerd64_libdir[0] != '\0') { - char *tmp; - size_t tmplen; - - tmp = getenv("LD_LIBRARY_PATH"); - if (!tmp) { - tmp = ""; - } - tmplen = strlen("LD_LIBRARY_PATH=") - + strlen(consumerd64_libdir) + 1 /* : */ + strlen(tmp); - tmpnew = zmalloc(tmplen + 1 /* \0 */); - if (!tmpnew) { - ret = -ENOMEM; - goto error; - } - strcpy(tmpnew, "LD_LIBRARY_PATH="); - strcat(tmpnew, consumerd64_libdir); - if (tmp[0] != '\0') { - strcat(tmpnew, ":"); - strcat(tmpnew, tmp); - } - ret = putenv(tmpnew); - if (ret) { - ret = -errno; - goto error; - } - } - DBG("Using 64-bit UST consumer at: %s", consumerd64_bin); - ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u", - "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, - "--consumerd-err-sock", consumer_data->err_unix_sock_path, - NULL); - if (consumerd64_libdir[0] != '\0') { - free(tmpnew); - } - if (ret) { - goto error; - } - break; - } - case LTTNG_CONSUMER32_UST: - { - char *tmpnew = NULL; - - if (consumerd32_libdir[0] != '\0') { - char *tmp; - size_t tmplen; - - tmp = getenv("LD_LIBRARY_PATH"); - if (!tmp) { - tmp = ""; - } - tmplen = strlen("LD_LIBRARY_PATH=") - + strlen(consumerd32_libdir) + 1 /* : */ + strlen(tmp); - tmpnew = zmalloc(tmplen + 1 /* \0 */); - if (!tmpnew) { - ret = -ENOMEM; - goto error; - } - strcpy(tmpnew, "LD_LIBRARY_PATH="); - strcat(tmpnew, consumerd32_libdir); - if (tmp[0] != '\0') { - strcat(tmpnew, ":"); - strcat(tmpnew, tmp); - } - ret = putenv(tmpnew); - if (ret) { - ret = -errno; - goto error; - } - } - DBG("Using 32-bit UST consumer at: %s", consumerd32_bin); - ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u", - "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, - "--consumerd-err-sock", consumer_data->err_unix_sock_path, - NULL); - if (consumerd32_libdir[0] != '\0') { - free(tmpnew); - } - if (ret) { - goto error; - } - 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; - } -error: - 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 = run_as_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) -{ - struct ltt_ust_session *lus = NULL; - int ret; - - switch (domain->type) { - case LTTNG_DOMAIN_UST: - break; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - DBG("Creating UST session"); - - lus = trace_ust_create_session(session->path, session->id, domain); - if (lus == NULL) { - ret = LTTCOMM_UST_SESS_FAIL; - goto error; - } - - ret = run_as_mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, - session->uid, session->gid); - 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: - ERR("Unknown UST domain on create session %d", domain->type); - goto error; - } - lus->uid = session->uid; - lus->gid = session->gid; - 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 = run_as_mkdir_recursive(session->kernel_session->trace_path, - S_IRWXU | S_IRWXG, session->uid, session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - goto error; - } - } - session->kernel_session->uid = session->uid; - session->kernel_session->gid = session->gid; - -error: - return ret; -} - -/* - * Check if the UID or GID match the session. Root user has access to all - * sessions. - */ -static int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) -{ - if (uid != session->uid && gid != session->gid && uid != 0) { - return 0; - } else { - return 1; - } -} - -static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) -{ - unsigned int i = 0; - struct ltt_session *session; - - DBG("Counting number of available session for UID %d GID %d", - uid, gid); - cds_list_for_each_entry(session, &session_list_ptr->head, list) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { - continue; - } - i++; - } - return i; -} - -/* - * 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, uid_t uid, - gid_t gid) -{ - unsigned int i = 0; - struct ltt_session *session; - - DBG("Getting all available session for UID %d GID %d", - uid, gid); - /* - * 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) { - /* - * Only list the sessions the user can control. - */ - if (!session_access_ok(session, uid, gid)) { - continue; - } - 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'; - sessions[i].enabled = session->enabled; - i++; - } -} - -/* - * Fill lttng_channel array of all channels. - */ -static void list_lttng_channels(int domain, struct ltt_session *session, - struct lttng_channel *channels) -{ - int i = 0; - struct ltt_kernel_channel *kchan; - - DBG("Listing channels for session %s", session->name); - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - /* 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++; - } - } - break; - case LTTNG_DOMAIN_UST: - { - struct lttng_ht_iter iter; - struct ltt_ust_channel *uchan; - - cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, - &iter.iter, uchan, node.node) { - strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN); - channels[i].attr.overwrite = uchan->attr.overwrite; - channels[i].attr.subbuf_size = uchan->attr.subbuf_size; - channels[i].attr.num_subbuf = uchan->attr.num_subbuf; - channels[i].attr.switch_timer_interval = - uchan->attr.switch_timer_interval; - channels[i].attr.read_timer_interval = - uchan->attr.read_timer_interval; - channels[i].enabled = uchan->enabled; - switch (uchan->attr.output) { - case LTTNG_UST_MMAP: - default: - channels[i].attr.output = LTTNG_EVENT_MMAP; - break; - } - i++; - } - break; - } - default: - break; - } -} - -/* - * Create a list of ust global domain events. - */ -static int list_lttng_ust_global_events(char *channel_name, - struct ltt_ust_domain_global *ust_global, struct lttng_event **events) -{ - int i = 0, ret = 0; - unsigned int nb_event = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *node; - struct ltt_ust_channel *uchan; - struct ltt_ust_event *uevent; - struct lttng_event *tmp; - - DBG("Listing UST global events for channel %s", channel_name); - - rcu_read_lock(); - - lttng_ht_lookup(ust_global->channels, (void *)channel_name, &iter); - node = lttng_ht_iter_get_node_str(&iter); - if (node == NULL) { - ret = -LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); - - nb_event += lttng_ht_get_count(uchan->events); - - if (nb_event == 0) { - ret = nb_event; - goto error; - } - - DBG3("Listing UST global %d events", nb_event); - - tmp = zmalloc(nb_event * sizeof(struct lttng_event)); - if (tmp == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN); - tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - tmp[i].enabled = uevent->enabled; - switch (uevent->attr.instrumentation) { - case LTTNG_UST_TRACEPOINT: - tmp[i].type = LTTNG_EVENT_TRACEPOINT; - break; - case LTTNG_UST_PROBE: - tmp[i].type = LTTNG_EVENT_PROBE; - break; - case LTTNG_UST_FUNCTION: - tmp[i].type = LTTNG_EVENT_FUNCTION; - break; - case LTTNG_UST_TRACEPOINT_LOGLEVEL: - tmp[i].type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; - break; - } - i++; - } - - ret = nb_event; - *events = tmp; - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Fill lttng_event array of all kernel events in the channel. - */ -static int list_lttng_kernel_events(char *channel_name, - struct ltt_kernel_session *kernel_session, struct lttng_event **events) -{ - int i = 0, ret; - unsigned int nb_event; - struct ltt_kernel_event *event; - struct ltt_kernel_channel *kchan; - - kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - nb_event = kchan->event_count; - - DBG("Listing events for channel %s", kchan->channel->name); - - if (nb_event == 0) { - ret = nb_event; - goto error; - } - - *events = zmalloc(nb_event * sizeof(struct lttng_event)); - if (*events == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - - /* 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++; - } - - return nb_event; - -error: - return ret; -} - -/* - * 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; - struct ltt_ust_session *usess; - - usess = session->ust_session; - - 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: - { - struct ltt_ust_channel *uchan; - struct lttng_ht *chan_ht; - - chan_ht = usess->domain_global.channels; - - uchan = trace_ust_find_channel_by_name(chan_ht, channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = channel_ust_disable(usess, domain, uchan); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - } - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_ENABLE_CHANNEL processed by the client thread. - */ -static int cmd_enable_channel(struct ltt_session *session, - int domain, struct lttng_channel *attr) -{ - int ret; - struct ltt_ust_session *usess = session->ust_session; - struct lttng_ht *chan_ht; - - DBG("Enabling channel %s for session %s", attr->name, session->name); - - switch (domain) { - 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; - - chan_ht = usess->domain_global.channels; - - uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); - if (uchan == NULL) { - ret = channel_ust_create(usess, domain, attr); - } else { - ret = channel_ust_enable(usess, domain, uchan); - } - break; - } - case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: - case LTTNG_DOMAIN_UST_EXEC_NAME: - case LTTNG_DOMAIN_UST_PID: - ret = LTTCOMM_NOT_IMPLEMENTED; - goto error; - default: - ret = LTTCOMM_UNKNOWN_DOMAIN; - goto error; - } - -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; - struct ltt_kernel_session *ksess; - - ksess = session->kernel_session; - - kchan = trace_kernel_get_channel_by_name(channel_name, ksess); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_tracepoint(ksess, kchan, event_name); - 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_session *usess; - - usess = session->ust_session; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = event_ust_disable_tracepoint(usess, domain, uchan, event_name); - if (ret != LTTCOMM_OK) { - goto error; - } - - DBG3("Disable UST event %s in channel %s completed", event_name, - channel_name); - 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_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; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - { - struct ltt_kernel_session *ksess; - struct ltt_kernel_channel *kchan; - - ksess = session->kernel_session; - - kchan = trace_kernel_get_channel_by_name(channel_name, ksess); - if (kchan == NULL) { - ret = LTTCOMM_KERN_CHAN_NOT_FOUND; - goto error; - } - - ret = event_kernel_disable_all(ksess, kchan); - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - } - case LTTNG_DOMAIN_UST: - { - struct ltt_ust_session *usess; - struct ltt_ust_channel *uchan; - - usess = session->ust_session; - - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - ret = LTTCOMM_UST_CHAN_NOT_FOUND; - goto error; - } - - ret = event_ust_disable_all_tracepoints(usess, domain, uchan); - if (ret != 0) { - goto error; - } - - DBG3("Disable all UST events in channel %s completed", channel_name); - - 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_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 = session->ust_session; - - ret = context_ust_add(usess, domain, ctx, event_name, channel_name); - if (ret != LTTCOMM_OK) { - 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_EVENT processed by the client thread. - * - * TODO: currently, both events and loglevels are kept within the same - * namespace for UST global registry/app registery, so if an event - * happen to have the same name as the loglevel (very unlikely though), - * and an attempt is made to enable/disable both in the same session, - * the first to be created will be the only one allowed to exist. - */ -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) { - free(attr); - goto error; - } - free(attr); - } - - /* 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 lttng_channel *attr; - struct ltt_ust_channel *uchan; - - /* Get channel from global UST domain */ - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - /* Create default channel */ - attr = channel_new_default_attr(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - attr->name[NAME_MAX - 1] = '\0'; - - ret = channel_ust_create(usess, domain, attr); - if (ret != LTTCOMM_OK) { - free(attr); - goto error; - } - free(attr); - - /* Get the newly created channel reference back */ - uchan = trace_ust_find_channel_by_name( - usess->domain_global.channels, channel_name); - if (uchan == NULL) { - /* Something is really wrong */ - ret = LTTCOMM_FATAL; - goto error; - } - } - - /* At this point, the session and channel exist on the tracer */ - ret = event_ust_enable_tracepoint(usess, domain, uchan, event); - if (ret != LTTCOMM_OK) { - 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_EVENT_SYSCALL: - ret = event_kernel_enable_all_syscalls(session->kernel_session, - kchan, kernel_tracer_fd); - break; - case LTTNG_EVENT_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_EVENT_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; - } - - /* Manage return value */ - if (ret != LTTCOMM_OK) { - goto error; - } - - kernel_wait_quiescent(kernel_tracer_fd); - break; - case LTTNG_DOMAIN_UST: - { - struct lttng_channel *attr; - struct ltt_ust_channel *uchan; - struct ltt_ust_session *usess = session->ust_session; - - /* Get channel from global UST domain */ - uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, - channel_name); - if (uchan == NULL) { - /* Create default channel */ - attr = channel_new_default_attr(domain); - if (attr == NULL) { - ret = LTTCOMM_FATAL; - goto error; - } - snprintf(attr->name, NAME_MAX, "%s", channel_name); - attr->name[NAME_MAX - 1] = '\0'; - - /* Use the internal command enable channel */ - ret = channel_ust_create(usess, domain, attr); - if (ret != LTTCOMM_OK) { - free(attr); - goto error; - } - free(attr); - - /* Get the newly created channel reference back */ - uchan = trace_ust_find_channel_by_name( - usess->domain_global.channels, channel_name); - if (uchan == NULL) { - /* Something is really wrong */ - ret = LTTCOMM_FATAL; - goto error; - } - } - - /* At this point, the session and channel exist on the tracer */ - - switch (event_type) { - case LTTNG_EVENT_ALL: - case LTTNG_EVENT_TRACEPOINT: - ret = event_ust_enable_all_tracepoints(usess, domain, uchan); - if (ret != LTTCOMM_OK) { - goto error; - } - break; - default: - ret = LTTCOMM_UST_ENABLE_FAIL; - goto error; - } - - /* Manage return value */ - if (ret != LTTCOMM_OK) { - 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_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; - case LTTNG_DOMAIN_UST: - nb_events = ust_app_list_events(events); - if (nb_events < 0) { - ret = LTTCOMM_UST_LIST_FAIL; - goto error; - } - break; - default: - 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; - - /* Short cut */ - ksession = session->kernel_session; - usess = session->ust_session; - - if (session->enabled) { - ret = LTTCOMM_UST_START_FAIL; - goto error; - } - - session->enabled = 1; - - /* 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); - } - - /* Flag session that trace should start automatically */ - if (usess) { - usess->start_trace = 1; - - ret = ust_app_start_trace_all(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; - - /* Short cut */ - ksession = session->kernel_session; - usess = session->ust_session; - - if (!session->enabled) { - ret = LTTCOMM_UST_STOP_FAIL; - goto error; - } - - session->enabled = 0; - - /* 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); - } - - if (usess) { - usess->start_trace = 0; - - ret = ust_app_stop_trace_all(usess); - if (ret < 0) { - ret = LTTCOMM_UST_STOP_FAIL; - goto error; - } - } - - ret = LTTCOMM_OK; - -error: - return ret; -} - -/* - * Command LTTNG_CREATE_SESSION processed by the client thread. - */ -static int cmd_create_session(char *name, char *path, struct ucred *creds) -{ - int ret; - - ret = session_create(name, path, creds->uid, creds->gid); - 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); - /* UST session teardown */ - teardown_ust_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_fds_sent != 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, index = 0; - ssize_t nb_dom = 0; - - if (session->kernel_session != NULL) { - DBG3("Listing domains found kernel domain"); - nb_dom++; - } - - if (session->ust_session != NULL) { - DBG3("Listing domains found UST global domain"); - nb_dom++; - } - - *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); - if (*domains == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - if (session->kernel_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_KERNEL; - index++; - } - - if (session->ust_session != NULL) { - (*domains)[index].type = LTTNG_DOMAIN_UST; - index++; - } - - return nb_dom; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_CHANNELS processed by the client thread. - */ -static ssize_t cmd_list_channels(int domain, struct ltt_session *session, - struct lttng_channel **channels) -{ - int ret; - ssize_t nb_chan = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_chan = session->kernel_session->channel_count; - } - DBG3("Number of kernel channels %zd", nb_chan); - break; - case LTTNG_DOMAIN_UST: - if (session->ust_session != NULL) { - nb_chan = lttng_ht_get_count( - session->ust_session->domain_global.channels); - } - DBG3("Number of UST global channels %zd", nb_chan); - break; - default: - *channels = NULL; - ret = -LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - if (nb_chan > 0) { - *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); - if (*channels == NULL) { - ret = -LTTCOMM_FATAL; - goto error; - } - - list_lttng_channels(domain, session, *channels); - } else { - *channels = NULL; - } - - return nb_chan; - -error: - return ret; -} - -/* - * Command LTTNG_LIST_EVENTS processed by the client thread. - */ -static ssize_t cmd_list_events(int domain, struct ltt_session *session, - char *channel_name, struct lttng_event **events) -{ - int ret = 0; - ssize_t nb_event = 0; - - switch (domain) { - case LTTNG_DOMAIN_KERNEL: - if (session->kernel_session != NULL) { - nb_event = list_lttng_kernel_events(channel_name, - session->kernel_session, events); - } - break; - case LTTNG_DOMAIN_UST: - { - if (session->ust_session != NULL) { - nb_event = list_lttng_ust_global_events(channel_name, - &session->ust_session->domain_global, events); - } - break; - } - default: - ret = -LTTCOMM_NOT_IMPLEMENTED; - goto error; - } - - ret = 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); - - if (opt_no_kernel && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) { - ret = LTTCOMM_KERN_NA; - goto error; - } - - /* - * 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: - if (!is_root) { - ret = LTTCOMM_KERN_NA; - goto error; - } - - /* 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; - } - } else { - 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 UST consumer daemons */ - /* 64-bit */ - pthread_mutex_lock(&ustconsumer64_data.pid_mutex); - if (consumerd64_bin[0] != '\0' && - ustconsumer64_data.pid == 0 && - cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { - pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); - ret = start_consumerd(&ustconsumer64_data); - if (ret < 0) { - ret = LTTCOMM_UST_CONSUMER64_FAIL; - ust_consumerd64_fd = -EINVAL; - goto error; - } - - ust_consumerd64_fd = ustconsumer64_data.cmd_sock; - } else { - pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); - } - /* 32-bit */ - if (consumerd32_bin[0] != '\0' && - ustconsumer32_data.pid == 0 && - cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { - pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); - ret = start_consumerd(&ustconsumer32_data); - if (ret < 0) { - ret = LTTCOMM_UST_CONSUMER32_FAIL; - ust_consumerd32_fd = -EINVAL; - goto error; - } - ust_consumerd32_fd = ustconsumer32_data.cmd_sock; - } else { - pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); - } - } - break; - } - default: - break; - } - - /* - * Check that the UID or GID match that of the tracing session. - * The root user can interact with all sessions. - */ - if (need_tracing_session) { - if (!session_access_ok(cmd_ctx->session, - cmd_ctx->creds.uid, cmd_ctx->creds.gid)) { - ret = LTTCOMM_EPERM; - goto error; - } - } - - /* 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.type, - &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, &cmd_ctx->creds); - 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->lsm->domain.type, - 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: - { - ssize_t nb_event; - struct lttng_event *events = NULL; - - nb_event = cmd_list_events(cmd_ctx->lsm->domain.type, 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: - { - unsigned int nr_sessions; - - session_lock_list(); - nr_sessions = lttng_sessions_count(cmd_ctx->creds.uid, cmd_ctx->creds.gid); - if (nr_sessions == 0) { - ret = LTTCOMM_NO_SESSION; - session_unlock_list(); - goto error; - } - ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * nr_sessions); - if (ret < 0) { - session_unlock_list(); - goto setup_error; - } - - /* Filled the session array */ - list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload), - cmd_ctx->creds.uid, cmd_ctx->creds.gid); - - 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; - } - - /* Set socket option for credentials retrieval */ - ret = lttcomm_setsockopt_creds_unix_sock(sock); - if (ret < 0) { - goto error; - } - - /* Allocate context command to process the client request */ - cmd_ctx = zmalloc(sizeof(struct command_ctx)); - if (cmd_ctx == NULL) { - perror("zmalloc cmd_ctx"); - goto error; - } - - /* Allocate data buffer for reception */ - cmd_ctx->lsm = zmalloc(sizeof(struct lttcomm_session_msg)); - if (cmd_ctx->lsm == NULL) { - perror("zmalloc 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_creds_unix_sock(sock, cmd_ctx->lsm, - sizeof(struct lttcomm_session_msg), &cmd_ctx->creds); - 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 zmalloc 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"); - } - - /* End of transmission */ - close(sock); - - clean_command_ctx(&cmd_ctx); - } - -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, " --ustconsumerd32-err-sock PATH Specify path for the 32-bit UST consumer error socket\n"); - fprintf(stderr, " --ustconsumerd64-err-sock PATH Specify path for the 64-bit UST consumer error socket\n"); - fprintf(stderr, " --ustconsumerd32-cmd-sock PATH Specify path for the 32-bit UST consumer command socket\n"); - fprintf(stderr, " --ustconsumerd64-cmd-sock PATH Specify path for the 64-bit UST consumer command socket\n"); - fprintf(stderr, " --consumerd32-path PATH Specify path for the 32-bit UST consumer daemon binary\n"); - fprintf(stderr, " --consumerd32-libdir PATH Specify path for the 32-bit UST consumer daemon libraries\n"); - fprintf(stderr, " --consumerd64-path PATH Specify path for the 64-bit UST consumer daemon binary\n"); - fprintf(stderr, " --consumerd64-libdir PATH Specify path for the 64-bit UST consumer daemon libraries\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"); - fprintf(stderr, " --no-kernel Disable kernel tracer\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' }, - { "ustconsumerd32-cmd-sock", 1, 0, 'G' }, - { "ustconsumerd32-err-sock", 1, 0, 'H' }, - { "ustconsumerd64-cmd-sock", 1, 0, 'D' }, - { "ustconsumerd64-err-sock", 1, 0, 'F' }, - { "consumerd32-path", 1, 0, 'u' }, - { "consumerd32-libdir", 1, 0, 'U' }, - { "consumerd64-path", 1, 0, 't' }, - { "consumerd64-libdir", 1, 0, 'T' }, - { "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' }, - { "no-kernel", 0, 0, 'N' }, - { NULL, 0, 0, 0 } - }; - - while (1) { - int option_index = 0; - c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t", - 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 = 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(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'D': - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'H': - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'G': - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); - break; - case 'N': - opt_no_kernel = 1; - 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; - case 'u': - consumerd32_bin= optarg; - break; - case 'U': - consumerd32_libdir = optarg; - break; - case 't': - consumerd64_bin = optarg; - break; - case 'T': - consumerd64_libdir = optarg; - 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(char *rundir) -{ - int ret; - gid_t gid; - - gid = allowed_group(); - if (gid < 0) { - WARN("No tracing group detected"); - ret = 0; - goto end; - } - - /* Set lttng run dir */ - ret = chown(rundir, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", 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"); - } - - /* 64-bit ustconsumer error socket path */ - ret = chown(ustconsumer64_data.err_unix_sock_path, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", ustconsumer64_data.err_unix_sock_path); - perror("chown"); - } - - /* 32-bit ustconsumer compat32 error socket path */ - ret = chown(ustconsumer32_data.err_unix_sock_path, 0, gid); - if (ret < 0) { - ERR("Unable to set group on %s", ustconsumer32_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(const char *rundir) -{ - int ret; - - DBG3("Creating LTTng run directory: %s", rundir); - - ret = mkdir(rundir, S_IRWXU | S_IRWXG ); - if (ret < 0) { - if (errno != EEXIST) { - ERR("Unable to create %s", 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, - const char *rundir) -{ - int ret; - char path[PATH_MAX]; - - switch (consumer_data->type) { - case LTTNG_CONSUMER_KERNEL: - snprintf(path, PATH_MAX, KCONSUMERD_PATH, rundir); - break; - case LTTNG_CONSUMER64_UST: - snprintf(path, PATH_MAX, USTCONSUMERD64_PATH, rundir); - break; - case LTTNG_CONSUMER32_UST: - snprintf(path, PATH_MAX, USTCONSUMERD32_PATH, rundir); - break; - default: - ERR("Consumer type unknown"); - ret = -EINVAL; - goto error; - } - - DBG2("Creating consumer directory: %s", 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; - } - - setup_consumerd_path(); - - /* 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) { - rundir = strdup(LTTNG_RUNDIR); - - /* Create global run dir with root access */ - ret = create_lttng_rundir(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); - } - - /* Setup kernel consumerd path */ - snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, - KCONSUMERD_ERR_SOCK_PATH, rundir); - snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, - KCONSUMERD_CMD_SOCK_PATH, rundir); - - DBG2("Kernel consumer err path: %s", - kconsumer_data.err_unix_sock_path); - DBG2("Kernel consumer cmd path: %s", - kconsumer_data.cmd_unix_sock_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; - } - - /* - * Create rundir from home path. This will create something like - * $HOME/.lttng - */ - ret = asprintf(&rundir, LTTNG_HOME_RUNDIR, home_path); - if (ret < 0) { - ret = -ENOMEM; - goto error; - } - - ret = create_lttng_rundir(rundir); - if (ret < 0) { - 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); - DBG("LTTng run directory path: %s", rundir); - - /* 32 bits consumerd path setup */ - snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, - USTCONSUMERD32_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, - USTCONSUMERD32_CMD_SOCK_PATH, rundir); - - DBG2("UST consumer 32 bits err path: %s", - ustconsumer32_data.err_unix_sock_path); - DBG2("UST consumer 32 bits cmd path: %s", - ustconsumer32_data.cmd_unix_sock_path); - - /* 64 bits consumerd path setup */ - snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, - USTCONSUMERD64_ERR_SOCK_PATH, rundir); - snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, - USTCONSUMERD64_CMD_SOCK_PATH, rundir); - - DBG2("UST consumer 64 bits err path: %s", - ustconsumer64_data.err_unix_sock_path); - DBG2("UST consumer 64 bits cmd path: %s", - ustconsumer64_data.cmd_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, rundir); - if (ret < 0) { - goto exit; - } - - /* Setup kernel tracer */ - if (!opt_no_kernel) { - init_kernel_tracer(); - } - - /* Set ulimit for open files */ - set_ulimit(); - } - - ret = set_consumer_sockets(&ustconsumer64_data, rundir); - if (ret < 0) { - goto exit; - } - - ret = set_consumer_sockets(&ustconsumer32_data, rundir); - if (ret < 0) { - goto exit; - } - - 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(rundir)) < 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 deleted file mode 100644 index 1a79fd564..000000000 --- a/lttng-sessiond/session.c +++ /dev/null @@ -1,250 +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 "common/runas.h" -#include "session.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. - * Returns the unique identifier for the session. - */ -static int add_session_list(struct ltt_session *ls) -{ - cds_list_add(&ls->list, <t_session_list.head); - return ++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, uid_t uid, gid_t gid) -{ - 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 = zmalloc(sizeof(struct ltt_session)); - if (new_session == NULL) { - perror("zmalloc"); - 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); - - new_session->uid = uid; - new_session->gid = gid; - - ret = run_as_mkdir_recursive(new_session->path, S_IRWXU | S_IRWXG, - new_session->uid, new_session->gid); - if (ret < 0) { - if (ret != -EEXIST) { - ERR("Trace directory creation error"); - ret = LTTCOMM_CREATE_FAIL; - goto error; - } - } - - /* Add new session to the session list */ - session_lock_list(); - new_session->id = add_session_list(new_session); - session_unlock_list(); - - DBG("Tracing session %s created in %s with ID %d by UID %d GID %d", - name, path, new_session->id, - new_session->uid, new_session->gid); - - 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 deleted file mode 100644 index 6264e14ac..000000000 --- a/lttng-sessiond/session.h +++ /dev/null @@ -1,90 +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 - -#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; - int enabled; /* enabled/started flag */ - int id; /* session unique identifier */ - /* UID/GID of the user owning the session */ - uid_t uid; - gid_t gid; -}; - -/* Prototypes */ -int session_create(char *name, char *path, uid_t uid, gid_t gid); -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 deleted file mode 100644 index 7dac1659f..000000000 --- a/lttng-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/lttng-sessiond/shm.h b/lttng-sessiond/shm.h deleted file mode 100644 index 2d301bbac..000000000 --- a/lttng-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/lttng-sessiond/trace-kernel.c b/lttng-sessiond/trace-kernel.c deleted file mode 100644 index c5b81f1e0..000000000 --- a/lttng-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 = zmalloc(sizeof(struct ltt_kernel_session)); - if (lks == NULL) { - perror("create kernel session zmalloc"); - 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 = zmalloc(sizeof(struct ltt_kernel_channel)); - if (lkc == NULL) { - perror("ltt_kernel_channel zmalloc"); - goto error; - } - - lkc->channel = zmalloc(sizeof(struct lttng_channel)); - if (lkc->channel == NULL) { - perror("lttng_channel zmalloc"); - 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 = zmalloc(sizeof(struct ltt_kernel_event)); - attr = zmalloc(sizeof(struct lttng_kernel_event)); - if (lke == NULL || attr == NULL) { - perror("kernel event zmalloc"); - 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 = zmalloc(sizeof(struct ltt_kernel_metadata)); - chan = zmalloc(sizeof(struct lttng_channel)); - if (lkm == NULL || chan == NULL) { - perror("kernel metadata zmalloc"); - 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 = zmalloc(sizeof(struct ltt_kernel_stream)); - if (lks == NULL) { - perror("kernel stream zmalloc"); - 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 deleted file mode 100644 index 1057c1e07..000000000 --- a/lttng-sessiond/trace-kernel.h +++ /dev/null @@ -1,132 +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; - /* UID/GID of the user owning the session */ - uid_t uid; - gid_t gid; -}; - -/* - * 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 deleted file mode 100644 index c85e79ce1..000000000 --- a/lttng-sessiond/trace-ust.c +++ /dev/null @@ -1,513 +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 "trace-ust.h" - -/* - * Find the channel in the hashtable. - */ -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, - char *name) -{ - struct lttng_ht_node_str *node; - struct lttng_ht_iter iter; - - rcu_read_lock(); - lttng_ht_lookup(ht, (void *)name, &iter); - node = lttng_ht_iter_get_node_str(&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 lttng_ht *ht, - char *name) -{ - struct lttng_ht_node_str *node; - struct lttng_ht_iter iter; - - rcu_read_lock(); - lttng_ht_lookup(ht, (void *) name, &iter); - node = lttng_ht_iter_get_node_str(&iter); - if (node == NULL) { - rcu_read_unlock(); - goto error; - } - rcu_read_unlock(); - - DBG2("Trace UST event found by name %s", name); - - return caa_container_of(node, struct ltt_ust_event, node); - -error: - DBG2("Trace UST event NOT found by name %s", name); - 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, int session_id, - struct lttng_domain *domain) -{ - int ret; - struct ltt_ust_session *lus; - - /* Allocate a new ltt ust session */ - lus = zmalloc(sizeof(struct ltt_ust_session)); - if (lus == NULL) { - PERROR("create ust session zmalloc"); - goto error; - } - - /* Init data structure */ - lus->id = session_id; - lus->start_trace = 0; - - /* Alloc UST domain hash tables */ - lus->domain_pid = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - lus->domain_exec = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - - /* Alloc UST global domain channels' HT */ - lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); - - /* Set session path */ - ret = snprintf(lus->pathname, PATH_MAX, "%s/ust", path); - if (ret < 0) { - PERROR("snprintf kernel traces path"); - goto error_free_session; - } - - DBG2("UST trace session create successful"); - - return lus; - -error_free_session: - free(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 = zmalloc(sizeof(struct ltt_ust_channel)); - if (luc == NULL) { - perror("ltt_ust_channel zmalloc"); - 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 */ - lttng_ht_node_init_str(&luc->node, luc->name); - /* Alloc hash tables */ - luc->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); - luc->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - - /* Set trace output path */ - ret = snprintf(luc->pathname, PATH_MAX, "%s", path); - if (ret < 0) { - perror("asprintf ust create channel"); - goto error_free_channel; - } - - DBG2("Trace UST channel %s created", luc->name); - - return luc; - -error_free_channel: - free(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 = zmalloc(sizeof(struct ltt_ust_event)); - if (lue == NULL) { - PERROR("ust event zmalloc"); - 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; - case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: - lue->attr.instrumentation = LTTNG_UST_TRACEPOINT_LOGLEVEL; - break; - default: - ERR("Unknown ust instrumentation type (%d)", ev->type); - goto error_free_event; - } - - /* 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 */ - lttng_ht_node_init_str(&lue->node, lue->attr.name); - /* Alloc context hash tables */ - lue->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - - DBG2("Trace UST event %s created", lue->attr.name); - - return lue; - -error_free_event: - free(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 = zmalloc(sizeof(struct ltt_ust_metadata)); - if (lum == NULL) { - perror("ust metadata zmalloc"); - 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 = LTTNG_UST_MMAP; - - 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_free_metadata; - } - - return lum; - -error_free_metadata: - free(lum); -error: - return NULL; -} - -/* - * Allocate and initialize an UST context. - * - * Return pointer to structure or NULL. - */ -struct ltt_ust_context *trace_ust_create_context( - struct lttng_event_context *ctx) -{ - struct ltt_ust_context *uctx; - - uctx = zmalloc(sizeof(struct ltt_ust_context)); - if (uctx == NULL) { - PERROR("zmalloc ltt_ust_context"); - goto error; - } - - uctx->ctx.ctx = ctx->ctx; - lttng_ht_node_init_ulong(&uctx->node, (unsigned long) uctx->ctx.ctx); - - return uctx; - -error: - return NULL; -} - -/* - * RCU safe free context structure. - */ -static void destroy_context_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_ulong *node = - caa_container_of(head, struct lttng_ht_node_ulong, 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_contexts(struct lttng_ht *ht) -{ - int ret; - struct lttng_ht_node_ulong *node; - struct lttng_ht_iter iter; - - cds_lfht_for_each_entry(ht->ht, &iter.iter, node, node) { - ret = lttng_ht_del(ht, &iter); - if (!ret) { - call_rcu(&node->head, destroy_context_rcu); - } - } - - lttng_ht_destroy(ht); -} - -/* - * Cleanup ust event structure. - */ -void trace_ust_destroy_event(struct ltt_ust_event *event) -{ - DBG2("Trace destroy UST event %s", event->attr.name); - destroy_contexts(event->ctx); - - free(event); -} - -/* - * URCU intermediate call to complete destroy event. - */ -static void destroy_event_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_str *node = - caa_container_of(head, struct lttng_ht_node_str, head); - struct ltt_ust_event *event = - caa_container_of(node, struct ltt_ust_event, node); - - trace_ust_destroy_event(event); -} - -/* - * Cleanup UST events hashtable. - */ -static void destroy_events(struct lttng_ht *events) -{ - int ret; - struct lttng_ht_node_str *node; - struct lttng_ht_iter iter; - - cds_lfht_for_each_entry(events->ht, &iter.iter, node, node) { - ret = lttng_ht_del(events, &iter); - assert(!ret); - call_rcu(&node->head, destroy_event_rcu); - } - - lttng_ht_destroy(events); -} - -/* - * Cleanup ust channel structure. - */ -void trace_ust_destroy_channel(struct ltt_ust_channel *channel) -{ - DBG2("Trace destroy UST channel %s", channel->name); - - rcu_read_lock(); - - /* Destroying all events of the channel */ - destroy_events(channel->events); - /* Destroying all context of the channel */ - destroy_contexts(channel->ctx); - - free(channel); - - rcu_read_unlock(); -} - -/* - * URCU intermediate call to complete destroy channel. - */ -static void destroy_channel_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_str *node = - caa_container_of(head, struct lttng_ht_node_str, 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 lttng_ht *channels) -{ - int ret; - struct lttng_ht_node_str *node; - struct lttng_ht_iter iter; - - rcu_read_lock(); - - cds_lfht_for_each_entry(channels->ht, &iter.iter, node, node) { - ret = lttng_ht_del(channels, &iter); - assert(!ret); - call_rcu(&node->head, destroy_channel_rcu); - } - - lttng_ht_destroy(channels); - - rcu_read_unlock(); -} - -/* - * Cleanup UST pid domain. - */ -static void destroy_domain_pid(struct lttng_ht *ht) -{ - int ret; - struct lttng_ht_iter iter; - struct ltt_ust_domain_pid *dpid; - - cds_lfht_for_each_entry(ht->ht, &iter.iter, dpid, node.node) { - ret = lttng_ht_del(ht , &iter); - assert(!ret); - destroy_channels(dpid->channels); - } - - lttng_ht_destroy(ht); -} - -/* - * Cleanup UST exec name domain. - */ -static void destroy_domain_exec(struct lttng_ht *ht) -{ - int ret; - struct lttng_ht_iter iter; - struct ltt_ust_domain_exec *dexec; - - cds_lfht_for_each_entry(ht->ht, &iter.iter, dexec, node.node) { - ret = lttng_ht_del(ht , &iter); - assert(!ret); - destroy_channels(dexec->channels); - } - - lttng_ht_destroy(ht); -} - -/* - * 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 protection */ - if (session == NULL) { - return; - } - - rcu_read_lock(); - - DBG2("Trace UST destroy session %d", session->id); - - /* 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 deleted file mode 100644 index c033ed6c5..000000000 --- a/lttng-sessiond/trace-ust.h +++ /dev/null @@ -1,216 +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 - -#include "ust-ctl.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 lttng_ht_node_ulong node; -}; - -/* UST event */ -struct ltt_ust_event { - unsigned int enabled; - struct lttng_ust_event attr; - struct lttng_ht *ctx; - struct lttng_ht_node_str node; -}; - -/* UST stream */ -struct ltt_ust_stream { - int handle; - char pathname[PATH_MAX]; - struct lttng_ust_object_data *obj; - /* Using a list of streams to keep order. */ - struct cds_list_head list; -}; - -/* UST channel */ -struct ltt_ust_channel { - unsigned int enabled; - char name[LTTNG_UST_SYM_NAME_LEN]; - char pathname[PATH_MAX]; - struct lttng_ust_channel attr; - struct lttng_ht *ctx; - struct lttng_ht *events; - struct lttng_ht_node_str 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 lttng_ht *channels; -}; - -/* UST domain pid (LTTNG_DOMAIN_UST_PID) */ -struct ltt_ust_domain_pid { - pid_t pid; - struct lttng_ht *channels; - struct lttng_ht_node_ulong node; -}; - -/* UST domain exec name (LTTNG_DOMAIN_UST_EXEC_NAME) */ -struct ltt_ust_domain_exec { - char exec_name[LTTNG_UST_SYM_NAME_LEN]; - struct lttng_ht *channels; - struct lttng_ht_node_str node; -}; - -/* UST session */ -struct ltt_ust_session { - int id; /* Unique identifier of session */ - int start_trace; - 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 lttng_ht *domain_pid; - struct lttng_ht *domain_exec; - /* UID/GID of the user owning the session */ - uid_t uid; - gid_t gid; -}; - -#ifdef HAVE_LIBLTTNG_UST_CTL - -/* - * Lookup functions. NULL is returned if not found. - */ -struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht, - char *name); -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, - char *name); - -/* - * Create functions malloc() the data structure. - */ -struct ltt_ust_session *trace_ust_create_session(char *path, int session_id, - 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); -struct ltt_ust_context *trace_ust_create_context( - struct lttng_event_context *ctx); - -/* - * 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 /* HAVE_LIBLTTNG_UST_CTL */ - -static inline -struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht, - char *name) -{ - return NULL; -} - -static inline -struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *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) -{ -} -static inline -struct ltt_ust_context *trace_ust_create_context( - struct lttng_event_context *ctx) -{ - return NULL; -} - -#endif /* HAVE_LIBLTTNG_UST_CTL */ - -#endif /* _LTT_TRACE_UST_H */ diff --git a/lttng-sessiond/ust-app.c b/lttng-sessiond/ust-app.c deleted file mode 100644 index 597627060..000000000 --- a/lttng-sessiond/ust-app.c +++ /dev/null @@ -1,2408 +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 -#include -#include - -#include "common/runas.h" -#include "ust-app.h" -#include "ust-consumer.h" -#include "ust-ctl.h" - -/* - * Delete ust context safely. RCU read lock must be held before calling - * this function. - */ -static -void delete_ust_app_ctx(int sock, struct ust_app_ctx *ua_ctx) -{ - if (ua_ctx->obj) { - ustctl_release_object(sock, ua_ctx->obj); - free(ua_ctx->obj); - } - free(ua_ctx); -} - -/* - * Delete ust app event safely. RCU read lock must be held before calling - * this function. - */ -static -void delete_ust_app_event(int sock, struct ust_app_event *ua_event) -{ - int ret; - struct lttng_ht_iter iter; - struct ust_app_ctx *ua_ctx; - - /* Destroy each context of event */ - cds_lfht_for_each_entry(ua_event->ctx->ht, &iter.iter, ua_ctx, - node.node) { - ret = lttng_ht_del(ua_event->ctx, &iter); - assert(!ret); - delete_ust_app_ctx(sock, ua_ctx); - } - lttng_ht_destroy(ua_event->ctx); - - if (ua_event->obj != NULL) { - ustctl_release_object(sock, ua_event->obj); - free(ua_event->obj); - } - free(ua_event); -} - -/* - * Delete ust app stream safely. RCU read lock must be held before calling - * this function. - */ -static -void delete_ust_app_stream(int sock, struct ltt_ust_stream *stream) -{ - if (stream->obj) { - ustctl_release_object(sock, stream->obj); - free(stream->obj); - } - free(stream); -} - -/* - * Delete ust app channel safely. RCU read lock must be held before calling - * this function. - */ -static -void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan) -{ - int ret; - struct lttng_ht_iter iter; - struct ust_app_event *ua_event; - struct ust_app_ctx *ua_ctx; - struct ltt_ust_stream *stream, *stmp; - - /* Wipe stream */ - cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) { - cds_list_del(&stream->list); - delete_ust_app_stream(sock, stream); - } - - /* Wipe context */ - cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter.iter, ua_ctx, node.node) { - ret = lttng_ht_del(ua_chan->ctx, &iter); - assert(!ret); - delete_ust_app_ctx(sock, ua_ctx); - } - lttng_ht_destroy(ua_chan->ctx); - - /* Wipe events */ - cds_lfht_for_each_entry(ua_chan->events->ht, &iter.iter, ua_event, - node.node) { - ret = lttng_ht_del(ua_chan->events, &iter); - assert(!ret); - delete_ust_app_event(sock, ua_event); - } - lttng_ht_destroy(ua_chan->events); - - if (ua_chan->obj != NULL) { - ustctl_release_object(sock, ua_chan->obj); - free(ua_chan->obj); - } - free(ua_chan); -} - -/* - * Delete ust app session safely. RCU read lock must be held before calling - * this function. - */ -static -void delete_ust_app_session(int sock, struct ust_app_session *ua_sess) -{ - int ret; - struct lttng_ht_iter iter; - struct ust_app_channel *ua_chan; - - if (ua_sess->metadata) { - if (ua_sess->metadata->stream_obj) { - ustctl_release_object(sock, ua_sess->metadata->stream_obj); - free(ua_sess->metadata->stream_obj); - } - if (ua_sess->metadata->obj) { - ustctl_release_object(sock, ua_sess->metadata->obj); - free(ua_sess->metadata->obj); - } - } - - cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, - node.node) { - ret = lttng_ht_del(ua_sess->channels, &iter); - assert(!ret); - delete_ust_app_channel(sock, ua_chan); - } - lttng_ht_destroy(ua_sess->channels); - - if (ua_sess->handle != -1) { - ustctl_release_handle(sock, ua_sess->handle); - } - free(ua_sess); -} - -/* - * Delete a traceable application structure from the global list. Never call - * this function outside of a call_rcu call. - */ -static -void delete_ust_app(struct ust_app *app) -{ - int ret, sock; - struct lttng_ht_iter iter; - struct ust_app_session *ua_sess; - - rcu_read_lock(); - - /* Delete ust app sessions info */ - sock = app->key.sock; - app->key.sock = -1; - - /* Wipe sessions */ - cds_lfht_for_each_entry(app->sessions->ht, &iter.iter, ua_sess, - node.node) { - ret = lttng_ht_del(app->sessions, &iter); - assert(!ret); - delete_ust_app_session(app->key.sock, ua_sess); - } - lttng_ht_destroy(app->sessions); - - /* - * Wait until we have removed the key from the sock hash table before - * closing this socket, otherwise an application could re-use the socket ID - * and race with the teardown, using the same hash table entry. - */ - close(sock); - - DBG2("UST app pid %d deleted", app->key.pid); - free(app); - - rcu_read_unlock(); -} - -/* - * URCU intermediate call to delete an UST app. - */ -static -void delete_ust_app_rcu(struct rcu_head *head) -{ - struct lttng_ht_node_ulong *node = - caa_container_of(head, struct lttng_ht_node_ulong, head); - struct ust_app *app = - caa_container_of(node, struct ust_app, node); - - DBG3("Call RCU deleting app PID %d", app->key.pid); - delete_ust_app(app); -} - -/* - * Alloc new UST app session. - */ -static -struct ust_app_session *alloc_ust_app_session(void) -{ - struct ust_app_session *ua_sess; - - /* Init most of the default value by allocating and zeroing */ - ua_sess = zmalloc(sizeof(struct ust_app_session)); - if (ua_sess == NULL) { - PERROR("malloc"); - goto error; - } - - ua_sess->handle = -1; - ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); - - return ua_sess; - -error: - return NULL; -} - -/* - * Alloc new UST app channel. - */ -static -struct ust_app_channel *alloc_ust_app_channel(char *name, - struct lttng_ust_channel *attr) -{ - struct ust_app_channel *ua_chan; - - /* Init most of the default value by allocating and zeroing */ - ua_chan = zmalloc(sizeof(struct ust_app_channel)); - if (ua_chan == NULL) { - PERROR("malloc"); - goto error; - } - - /* Setup channel name */ - strncpy(ua_chan->name, name, sizeof(ua_chan->name)); - ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; - - ua_chan->enabled = 1; - ua_chan->handle = -1; - ua_chan->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - ua_chan->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); - lttng_ht_node_init_str(&ua_chan->node, ua_chan->name); - - CDS_INIT_LIST_HEAD(&ua_chan->streams.head); - - /* Copy attributes */ - if (attr) { - memcpy(&ua_chan->attr, attr, sizeof(ua_chan->attr)); - } - - DBG3("UST app channel %s allocated", ua_chan->name); - - return ua_chan; - -error: - return NULL; -} - -/* - * Alloc new UST app event. - */ -static -struct ust_app_event *alloc_ust_app_event(char *name, - struct lttng_ust_event *attr) -{ - struct ust_app_event *ua_event; - - /* Init most of the default value by allocating and zeroing */ - ua_event = zmalloc(sizeof(struct ust_app_event)); - if (ua_event == NULL) { - PERROR("malloc"); - goto error; - } - - ua_event->enabled = 1; - strncpy(ua_event->name, name, sizeof(ua_event->name)); - ua_event->name[sizeof(ua_event->name) - 1] = '\0'; - ua_event->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - lttng_ht_node_init_str(&ua_event->node, ua_event->name); - - /* Copy attributes */ - if (attr) { - memcpy(&ua_event->attr, attr, sizeof(ua_event->attr)); - } - - DBG3("UST app event %s allocated", ua_event->name); - - return ua_event; - -error: - return NULL; -} - -/* - * Alloc new UST app context. - */ -static -struct ust_app_ctx *alloc_ust_app_ctx(struct lttng_ust_context *uctx) -{ - struct ust_app_ctx *ua_ctx; - - ua_ctx = zmalloc(sizeof(struct ust_app_ctx)); - if (ua_ctx == NULL) { - goto error; - } - - if (uctx) { - memcpy(&ua_ctx->ctx, uctx, sizeof(ua_ctx->ctx)); - } - - DBG3("UST app context %d allocated", ua_ctx->ctx.ctx); - -error: - return ua_ctx; -} - -/* - * Find an ust_app using the sock and return it. RCU read side lock must be - * held before calling this helper function. - */ -static -struct ust_app *find_app_by_sock(int sock) -{ - struct lttng_ht_node_ulong *node; - struct ust_app_key *key; - struct lttng_ht_iter iter; - - lttng_ht_lookup(ust_app_sock_key_map, (void *)((unsigned long) sock), - &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - DBG2("UST app find by sock %d key not found", sock); - goto error; - } - key = caa_container_of(node, struct ust_app_key, node); - - lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) key->pid), &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - DBG2("UST app find by sock %d not found", sock); - goto error; - } - return caa_container_of(node, struct ust_app, node); - -error: - return NULL; -} - -/* - * Create the channel context on the tracer. - */ -static -int create_ust_channel_context(struct ust_app_channel *ua_chan, - struct ust_app_ctx *ua_ctx, struct ust_app *app) -{ - int ret; - - ret = ustctl_add_context(app->key.sock, &ua_ctx->ctx, - ua_chan->obj, &ua_ctx->obj); - if (ret < 0) { - goto error; - } - - ua_ctx->handle = ua_ctx->obj->handle; - - DBG2("UST app context added to channel %s successfully", ua_chan->name); - -error: - return ret; -} - -/* - * Create the event context on the tracer. - */ -static -int create_ust_event_context(struct ust_app_event *ua_event, - struct ust_app_ctx *ua_ctx, struct ust_app *app) -{ - int ret; - - ret = ustctl_add_context(app->key.sock, &ua_ctx->ctx, - ua_event->obj, &ua_ctx->obj); - if (ret < 0) { - goto error; - } - - ua_ctx->handle = ua_ctx->obj->handle; - - DBG2("UST app context added to event %s successfully", ua_event->name); - -error: - return ret; -} - -/* - * Disable the specified event on to UST tracer for the UST session. - */ -static int disable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) -{ - int ret; - - ret = ustctl_disable(app->key.sock, ua_event->obj); - if (ret < 0) { - ERR("UST app event %s disable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->key.pid, ua_sess->handle, ret); - goto error; - } - - DBG2("UST app event %s disabled successfully for app (pid: %d)", - ua_event->attr.name, app->key.pid); - -error: - return ret; -} - -/* - * Disable the specified channel on to UST tracer for the UST session. - */ -static int disable_ust_channel(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) -{ - int ret; - - ret = ustctl_disable(app->key.sock, ua_chan->obj); - if (ret < 0) { - ERR("UST app channel %s disable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_chan->name, app->key.pid, ua_sess->handle, ret); - goto error; - } - - DBG2("UST app channel %s disabled successfully for app (pid: %d)", - ua_chan->name, app->key.pid); - -error: - return ret; -} - -/* - * Enable the specified channel on to UST tracer for the UST session. - */ -static int enable_ust_channel(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) -{ - int ret; - - ret = ustctl_enable(app->key.sock, ua_chan->obj); - if (ret < 0) { - ERR("UST app channel %s enable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_chan->name, app->key.pid, ua_sess->handle, ret); - goto error; - } - - ua_chan->enabled = 1; - - DBG2("UST app channel %s enabled successfully for app (pid: %d)", - ua_chan->name, app->key.pid); - -error: - return ret; -} - -/* - * Enable the specified event on to UST tracer for the UST session. - */ -static int enable_ust_event(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_event *ua_event) -{ - int ret; - - ret = ustctl_enable(app->key.sock, ua_event->obj); - if (ret < 0) { - ERR("UST app event %s enable failed for app (pid: %d) " - "and session handle %d with ret %d", - ua_event->attr.name, app->key.pid, ua_sess->handle, ret); - goto error; - } - - DBG2("UST app event %s enabled successfully for app (pid: %d)", - ua_event->attr.name, app->key.pid); - -error: - return ret; -} - -/* - * Open metadata onto the UST tracer for a UST session. - */ -static int open_ust_metadata(struct ust_app *app, - struct ust_app_session *ua_sess) -{ - int ret; - struct lttng_ust_channel_attr uattr; - - 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 with ret %d", - app->key.pid, ret); - goto error; - } - - ua_sess->metadata->handle = ua_sess->metadata->obj->handle; - -error: - return ret; -} - -/* - * Create stream onto the UST tracer for a UST session. - */ -static int create_ust_stream(struct ust_app *app, - struct ust_app_session *ua_sess) -{ - int ret; - - 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 error; - } - -error: - return ret; -} - -/* - * Create the specified channel onto the UST tracer for a UST session. - */ -static int create_ust_channel(struct ust_app *app, - struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) -{ - int ret; - - /* TODO: remove cast and use lttng-ust-abi.h */ - ret = ustctl_create_channel(app->key.sock, ua_sess->handle, - (struct lttng_ust_channel_attr *)&ua_chan->attr, &ua_chan->obj); - if (ret < 0) { - ERR("Creating channel %s for app (pid: %d, sock: %d) " - "and session handle %d with ret %d", - ua_chan->name, app->key.pid, app->key.sock, - ua_sess->handle, ret); - goto error; - } - - ua_chan->handle = ua_chan->obj->handle; - - DBG2("UST app channel %s created successfully for pid:%d and sock:%d", - ua_chan->name, app->key.pid, app->key.sock); - - /* If channel is not enabled, disable it on the tracer */ - if (!ua_chan->enabled) { - ret = disable_ust_channel(app, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - } - -error: - return ret; -} - -/* - * Create the specified event onto the UST tracer for a UST session. - */ -static -int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan, struct ust_app_event *ua_event) -{ - int ret = 0; - - /* Create UST event on tracer */ - ret = ustctl_create_event(app->key.sock, &ua_event->attr, ua_chan->obj, - &ua_event->obj); - if (ret < 0) { - if (ret == -EEXIST) { - ret = 0; - goto error; - } - ERR("Error ustctl create event %s for app pid: %d with ret %d", - ua_event->attr.name, app->key.pid, ret); - goto error; - } - - ua_event->handle = ua_event->obj->handle; - - DBG2("UST app event %s created successfully for pid:%d", - ua_event->attr.name, app->key.pid); - - /* If event not enabled, disable it on the tracer */ - if (ua_event->enabled == 0) { - ret = disable_ust_event(app, ua_sess, ua_event); - if (ret < 0) { - /* - * If we hit an EPERM, something is wrong with our disable call. If - * we get an EEXIST, there is a problem on the tracer side since we - * just created it. - */ - switch (ret) { - case -EPERM: - /* Code flow problem */ - assert(0); - case -EEXIST: - /* It's OK for our use case. */ - ret = 0; - break; - default: - break; - } - goto error; - } - } - -error: - return ret; -} - -/* - * Copy data between an UST app event and a LTT event. - */ -static void shadow_copy_event(struct ust_app_event *ua_event, - struct ltt_ust_event *uevent) -{ - struct lttng_ht_iter iter; - struct ltt_ust_context *uctx; - struct ust_app_ctx *ua_ctx; - - strncpy(ua_event->name, uevent->attr.name, sizeof(ua_event->name)); - ua_event->name[sizeof(ua_event->name) - 1] = '\0'; - - ua_event->enabled = uevent->enabled; - - /* Copy event attributes */ - memcpy(&ua_event->attr, &uevent->attr, sizeof(ua_event->attr)); - - cds_lfht_for_each_entry(uevent->ctx->ht, &iter.iter, uctx, node.node) { - ua_ctx = alloc_ust_app_ctx(&uctx->ctx); - if (ua_ctx == NULL) { - /* malloc() failed. We should simply stop */ - return; - } - - lttng_ht_node_init_ulong(&ua_ctx->node, - (unsigned long) ua_ctx->ctx.ctx); - lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node); - } -} - -/* - * Copy data between an UST app channel and a LTT channel. - */ -static void shadow_copy_channel(struct ust_app_channel *ua_chan, - struct ltt_ust_channel *uchan) -{ - struct lttng_ht_iter iter; - struct lttng_ht_node_str *ua_event_node; - struct ltt_ust_event *uevent; - struct ltt_ust_context *uctx; - struct ust_app_event *ua_event; - struct ust_app_ctx *ua_ctx; - - DBG2("UST app shadow copy of channel %s started", ua_chan->name); - - strncpy(ua_chan->name, uchan->name, sizeof(ua_chan->name)); - ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; - /* Copy event attributes */ - memcpy(&ua_chan->attr, &uchan->attr, sizeof(ua_chan->attr)); - - ua_chan->enabled = uchan->enabled; - - cds_lfht_for_each_entry(uchan->ctx->ht, &iter.iter, uctx, node.node) { - ua_ctx = alloc_ust_app_ctx(&uctx->ctx); - if (ua_ctx == NULL) { - continue; - } - lttng_ht_node_init_ulong(&ua_ctx->node, - (unsigned long) ua_ctx->ctx.ctx); - lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node); - } - - /* Copy all events from ltt ust channel to ust app channel */ - cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { - struct lttng_ht_iter uiter; - - lttng_ht_lookup(ua_chan->events, (void *) uevent->attr.name, &uiter); - ua_event_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_event_node == NULL) { - DBG2("UST event %s not found on shadow copy channel", - uevent->attr.name); - ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); - if (ua_event == NULL) { - continue; - } - shadow_copy_event(ua_event, uevent); - lttng_ht_add_unique_str(ua_chan->events, &ua_event->node); - } - } - - DBG3("UST app shadow copy of channel %s done", ua_chan->name); -} - -/* - * Copy data between a UST app session and a regular LTT session. - */ -static void shadow_copy_session(struct ust_app_session *ua_sess, - struct ltt_ust_session *usess, struct ust_app *app) -{ - struct lttng_ht_node_str *ua_chan_node; - struct lttng_ht_iter iter; - struct ltt_ust_channel *uchan; - struct ust_app_channel *ua_chan; - time_t rawtime; - struct tm *timeinfo; - char datetime[16]; - int ret; - - /* Get date and time for unique app path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); - - DBG2("Shadow copy of session handle %d", ua_sess->handle); - - ua_sess->id = usess->id; - ua_sess->uid = usess->uid; - ua_sess->gid = usess->gid; - - ret = snprintf(ua_sess->path, PATH_MAX, "%s/%s-%d-%s", usess->pathname, - app->name, app->key.pid, datetime); - if (ret < 0) { - PERROR("asprintf UST shadow copy session"); - /* TODO: We cannot return an error from here.. */ - assert(0); - } - - /* TODO: support all UST domain */ - - /* Iterate over all channels in global domain. */ - cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter, - uchan, node.node) { - struct lttng_ht_iter uiter; - - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_chan_node != NULL) { - /* Session exist. Contiuing. */ - continue; - } - - DBG2("Channel %s not found on shadow session copy, creating it", - uchan->name); - ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); - if (ua_chan == NULL) { - /* malloc failed FIXME: Might want to do handle ENOMEM .. */ - continue; - } - - shadow_copy_channel(ua_chan, uchan); - lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); - } -} - -/* - * Lookup sesison wrapper. - */ -static -void __lookup_session_by_app(struct ltt_ust_session *usess, - struct ust_app *app, struct lttng_ht_iter *iter) -{ - /* Get right UST app session from app */ - lttng_ht_lookup(app->sessions, (void *)((unsigned long) usess->id), iter); -} - -/* - * Return ust app session from the app session hashtable using the UST session - * id. - */ -static struct ust_app_session *lookup_session_by_app( - struct ltt_ust_session *usess, struct ust_app *app) -{ - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - - __lookup_session_by_app(usess, app, &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - goto error; - } - - return caa_container_of(node, struct ust_app_session, node); - -error: - return NULL; -} - -/* - * Create a UST session onto the tracer of app and add it the session - * hashtable. - * - * Return ust app session or NULL on error. - */ -static struct ust_app_session *create_ust_app_session( - struct ltt_ust_session *usess, struct ust_app *app) -{ - int ret; - struct ust_app_session *ua_sess; - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - DBG2("UST app pid: %d session id %d not found, creating it", - app->key.pid, usess->id); - ua_sess = alloc_ust_app_session(); - if (ua_sess == NULL) { - /* Only malloc can failed so something is really wrong */ - goto end; - } - shadow_copy_session(ua_sess, usess, app); - } - - if (ua_sess->handle == -1) { - ret = ustctl_create_session(app->key.sock); - if (ret < 0) { - ERR("Creating session for app pid %d", app->key.pid); - goto error; - } - - ua_sess->handle = ret; - - /* Add ust app session to app's HT */ - lttng_ht_node_init_ulong(&ua_sess->node, (unsigned long) ua_sess->id); - lttng_ht_add_unique_ulong(app->sessions, &ua_sess->node); - - DBG2("UST app session created successfully with handle %d", ret); - } - -end: - return ua_sess; - -error: - delete_ust_app_session(-1, ua_sess); - return NULL; -} - -/* - * Create a context for the channel on the tracer. - */ -static -int create_ust_app_channel_context(struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan, struct lttng_ust_context *uctx, - struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - struct ust_app_ctx *ua_ctx; - - DBG2("UST app adding context to channel %s", ua_chan->name); - - lttng_ht_lookup(ua_chan->ctx, (void *)((unsigned long)uctx->ctx), &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node != NULL) { - ret = -EEXIST; - goto error; - } - - ua_ctx = alloc_ust_app_ctx(uctx); - if (ua_ctx == NULL) { - /* malloc failed */ - ret = -1; - goto error; - } - - lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx); - lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node); - - ret = create_ust_channel_context(ua_chan, ua_ctx, app); - if (ret < 0) { - goto error; - } - -error: - return ret; -} - -/* - * Create an UST context and enable it for the event on the tracer. - */ -static -int create_ust_app_event_context(struct ust_app_session *ua_sess, - struct ust_app_event *ua_event, struct lttng_ust_context *uctx, - struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - struct ust_app_ctx *ua_ctx; - - DBG2("UST app adding context to event %s", ua_event->name); - - lttng_ht_lookup(ua_event->ctx, (void *)((unsigned long)uctx->ctx), &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node != NULL) { - ret = -EEXIST; - goto error; - } - - ua_ctx = alloc_ust_app_ctx(uctx); - if (ua_ctx == NULL) { - /* malloc failed */ - ret = -1; - goto error; - } - - lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx); - lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node); - - ret = create_ust_event_context(ua_event, ua_ctx, app); - if (ret < 0) { - goto error; - } - -error: - return ret; -} - -/* - * Enable on the tracer side a ust app event for the session and channel. - */ -static -int enable_ust_app_event(struct ust_app_session *ua_sess, - struct ust_app_event *ua_event, struct ust_app *app) -{ - int ret; - - ret = enable_ust_event(app, ua_sess, ua_event); - if (ret < 0) { - goto error; - } - - ua_event->enabled = 1; - -error: - return ret; -} - -/* - * Disable on the tracer side a ust app event for the session and channel. - */ -static int disable_ust_app_event(struct ust_app_session *ua_sess, - struct ust_app_event *ua_event, struct ust_app *app) -{ - int ret; - - ret = disable_ust_event(app, ua_sess, ua_event); - if (ret < 0) { - goto error; - } - - ua_event->enabled = 0; - -error: - return ret; -} - -/* - * Lookup ust app channel for session and disable it on the tracer side. - */ -static -int disable_ust_app_channel(struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan, struct ust_app *app) -{ - int ret; - - ret = disable_ust_channel(app, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - - ua_chan->enabled = 0; - -error: - return ret; -} - -/* - * Lookup ust app channel for session and enable it on the tracer side. - */ -static int enable_ust_app_channel(struct ust_app_session *ua_sess, - struct ltt_ust_channel *uchan, struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *ua_chan_node; - struct ust_app_channel *ua_chan; - - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); - ua_chan_node = lttng_ht_iter_get_node_str(&iter); - if (ua_chan_node == NULL) { - DBG2("Unable to find channel %s in ust session id %u", - uchan->name, ua_sess->id); - goto error; - } - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - ret = enable_ust_channel(app, ua_sess, ua_chan); - if (ret < 0) { - goto error; - } - -error: - return ret; -} - -/* - * Create UST app channel and create it on the tracer. - */ -static struct ust_app_channel *create_ust_app_channel( - struct ust_app_session *ua_sess, struct ltt_ust_channel *uchan, - struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *ua_chan_node; - struct ust_app_channel *ua_chan; - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); - ua_chan_node = lttng_ht_iter_get_node_str(&iter); - if (ua_chan_node != NULL) { - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - goto end; - } - - ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); - if (ua_chan == NULL) { - /* Only malloc can fail here */ - goto error; - } - shadow_copy_channel(ua_chan, uchan); - - ret = create_ust_channel(app, ua_sess, ua_chan); - if (ret < 0) { - /* Not found previously means that it does not exist on the tracer */ - assert(ret != -EEXIST); - goto error; - } - - lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); - - DBG2("UST app create channel %s for PID %d completed", ua_chan->name, - app->key.pid); - -end: - return ua_chan; - -error: - delete_ust_app_channel(-1, ua_chan); - return NULL; -} - -/* - * Create UST app event and create it on the tracer side. - */ -static -int create_ust_app_event(struct ust_app_session *ua_sess, - struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, - struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *ua_event_node; - struct ust_app_event *ua_event; - - /* Get event node */ - lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); - ua_event_node = lttng_ht_iter_get_node_str(&iter); - if (ua_event_node != NULL) { - ret = -EEXIST; - goto end; - } - - /* Does not exist so create one */ - ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); - if (ua_event == NULL) { - /* Only malloc can failed so something is really wrong */ - ret = -ENOMEM; - goto end; - } - shadow_copy_event(ua_event, uevent); - - /* Create it on the tracer side */ - ret = create_ust_event(app, ua_sess, ua_chan, ua_event); - if (ret < 0) { - /* Not found previously means that it does not exist on the tracer */ - assert(ret != -EEXIST); - goto error; - } - - lttng_ht_add_unique_str(ua_chan->events, &ua_event->node); - - DBG2("UST app create event %s for PID %d completed", ua_event->name, - app->key.pid); - -end: - return ret; - -error: - /* Valid. Calling here is already in a read side lock */ - delete_ust_app_event(-1, ua_event); - return ret; -} - -/* - * Create UST metadata and open it on the tracer side. - */ -static int create_ust_app_metadata(struct ust_app_session *ua_sess, - char *pathname, struct ust_app *app) -{ - int ret = 0; - - if (ua_sess->metadata == NULL) { - /* Allocate UST metadata */ - ua_sess->metadata = trace_ust_create_metadata(pathname); - if (ua_sess->metadata == NULL) { - /* malloc() failed */ - goto error; - } - - ret = open_ust_metadata(app, ua_sess); - if (ret < 0) { - DBG3("Opening metadata failed. Cleaning up memory"); - - /* Cleanup failed metadata struct */ - free(ua_sess->metadata); - /* - * This is very important because delete_ust_app_session check if - * the pointer is null or not in order to delete the metadata. - */ - ua_sess->metadata = NULL; - goto error; - } - - DBG2("UST metadata opened for app pid %d", app->key.pid); - } - - /* Open UST metadata stream */ - if (ua_sess->metadata->stream_obj == NULL) { - ret = create_ust_stream(app, ua_sess); - if (ret < 0) { - goto error; - } - - ret = run_as_mkdir(ua_sess->path, S_IRWXU | S_IRWXG, - ua_sess->uid, ua_sess->gid); - if (ret < 0) { - PERROR("mkdir UST metadata"); - goto error; - } - - ret = snprintf(ua_sess->metadata->pathname, PATH_MAX, - "%s/metadata", ua_sess->path); - if (ret < 0) { - PERROR("asprintf UST create stream"); - goto error; - } - - DBG2("UST metadata stream object created for app pid %d", - app->key.pid); - } else { - ERR("Attempting to create stream without metadata opened"); - goto error; - } - - return 0; - -error: - return -1; -} - -/* - * Return pointer to traceable apps list. - */ -struct lttng_ht *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 lttng_ht_node_ulong *node; - struct lttng_ht_iter iter; - - rcu_read_lock(); - lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) pid), &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - DBG2("UST app no found with pid %d", pid); - goto error; - } - rcu_read_unlock(); - - DBG2("Found UST app by pid %d", pid); - - return caa_container_of(node, struct ust_app, node); - -error: - rcu_read_unlock(); - return NULL; -} - -/* - * Using pid and uid (of the app), allocate a new ust_app struct and - * add it to the global traceable app list. - * - * On success, return 0, else return malloc -ENOMEM, or -EINVAL if app - * bitness is not supported. - */ -int ust_app_register(struct ust_register_msg *msg, int sock) -{ - struct ust_app *lta; - - if ((msg->bits_per_long == 64 && ust_consumerd64_fd == -EINVAL) - || (msg->bits_per_long == 32 && ust_consumerd32_fd == -EINVAL)) { - ERR("Registration failed: application \"%s\" (pid: %d) has " - "%d-bit long, but no consumerd for this long size is available.\n", - msg->name, msg->pid, msg->bits_per_long); - close(sock); - return -EINVAL; - } - lta = zmalloc(sizeof(struct ust_app)); - if (lta == NULL) { - PERROR("malloc"); - return -ENOMEM; - } - - lta->ppid = msg->ppid; - lta->uid = msg->uid; - lta->gid = msg->gid; - lta->bits_per_long = msg->bits_per_long; - lta->v_major = msg->major; - lta->v_minor = msg->minor; - strncpy(lta->name, msg->name, sizeof(lta->name)); - lta->name[16] = '\0'; - lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - - /* Set key map */ - lta->key.pid = msg->pid; - lttng_ht_node_init_ulong(<a->node, (unsigned long)lta->key.pid); - lta->key.sock = sock; - lttng_ht_node_init_ulong(<a->key.node, (unsigned long)lta->key.sock); - - rcu_read_lock(); - lttng_ht_add_unique_ulong(ust_app_sock_key_map, <a->key.node); - lttng_ht_add_unique_ulong(ust_app_ht, <a->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; - struct lttng_ht_node_ulong *node; - struct lttng_ht_iter iter; - int ret; - - rcu_read_lock(); - lta = find_app_by_sock(sock); - if (lta == NULL) { - ERR("Unregister app sock %d not found!", sock); - goto error; - } - - DBG("PID %d unregistering with sock %d", lta->key.pid, sock); - - /* Remove application from socket hash table */ - lttng_ht_lookup(ust_app_sock_key_map, (void *)((unsigned long) sock), &iter); - ret = lttng_ht_del(ust_app_sock_key_map, &iter); - assert(!ret); - - /* Get the node reference for a call_rcu */ - lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) lta->key.pid), &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - ERR("Unable to find app sock %d by pid %d", sock, lta->key.pid); - goto error; - } - - /* Remove application from PID hash table */ - ret = lttng_ht_del(ust_app_ht, &iter); - assert(!ret); - call_rcu(&node->head, delete_ust_app_rcu); -error: - rcu_read_unlock(); - return; -} - -/* - * Return traceable_app_count - */ -unsigned long ust_app_list_count(void) -{ - unsigned long count; - - rcu_read_lock(); - count = lttng_ht_get_count(ust_app_ht); - rcu_read_unlock(); - - return count; -} - -/* - * Fill events array with all events name of all registered apps. - */ -int ust_app_list_events(struct lttng_event **events) -{ - int ret, handle; - size_t nbmem, count = 0; - struct lttng_ht_iter iter; - struct ust_app *app; - struct lttng_event *tmp; - - nbmem = UST_APP_EVENT_LIST_SIZE; - tmp = zmalloc(nbmem * sizeof(struct lttng_event)); - if (tmp == NULL) { - PERROR("zmalloc ust app events"); - ret = -ENOMEM; - goto error; - } - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - struct lttng_ust_tracepoint_iter uiter; - - handle = ustctl_tracepoint_list(app->key.sock); - if (handle < 0) { - ERR("UST app list events getting handle failed for app pid %d", - app->key.pid); - continue; - } - - while ((ret = ustctl_tracepoint_list_get(app->key.sock, handle, - &uiter)) != -ENOENT) { - if (count >= nbmem) { - DBG2("Reallocating event list from %zu to %zu entries", nbmem, - 2 * nbmem); - nbmem *= 2; - tmp = realloc(tmp, nbmem * sizeof(struct lttng_event)); - if (tmp == NULL) { - PERROR("realloc ust app events"); - ret = -ENOMEM; - goto rcu_error; - } - } - memcpy(tmp[count].name, uiter.name, LTTNG_UST_SYM_NAME_LEN); - memcpy(tmp[count].loglevel, uiter.loglevel, LTTNG_UST_SYM_NAME_LEN); - tmp[count].loglevel_value = uiter.loglevel_value; - tmp[count].type = LTTNG_UST_TRACEPOINT; - tmp[count].pid = app->key.pid; - tmp[count].enabled = -1; - count++; - } - } - - ret = count; - *events = tmp; - - DBG2("UST app list events done (%zu events)", count); - -rcu_error: - rcu_read_unlock(); -error: - return ret; -} - -/* - * Free and clean all traceable apps of the global list. - */ -void ust_app_clean_list(void) -{ - int ret; - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - - DBG2("UST app cleaning registered apps hash table"); - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, node, node) { - ret = lttng_ht_del(ust_app_ht, &iter); - assert(!ret); - call_rcu(&node->head, delete_ust_app_rcu); - } - /* Destroy is done only when the ht is empty */ - lttng_ht_destroy(ust_app_ht); - - cds_lfht_for_each_entry(ust_app_sock_key_map->ht, &iter.iter, node, node) { - ret = lttng_ht_del(ust_app_sock_key_map, &iter); - assert(!ret); - } - /* Destroy is done only when the ht is empty */ - lttng_ht_destroy(ust_app_sock_key_map); - - rcu_read_unlock(); -} - -/* - * Init UST app hash table. - */ -void ust_app_ht_alloc(void) -{ - ust_app_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - ust_app_sock_key_map = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); -} - -/* - * For a specific UST session, disable the channel for all registered apps. - */ -int ust_app_disable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *ua_chan_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - - if (usess == NULL || uchan == NULL) { - ERR("Disabling UST global channel with NULL values"); - ret = -1; - goto error; - } - - DBG2("UST app disabling channel %s from global domain for session id %d", - uchan->name, usess->id); - - rcu_read_lock(); - - /* For every registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - struct lttng_ht_iter uiter; - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - continue; - } - - /* Get channel */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the session if found for the app, the channel must be there */ - assert(ua_chan_node); - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - /* The channel must not be already disabled */ - assert(ua_chan->enabled == 1); - - /* Disable channel onto application */ - ret = disable_ust_app_channel(ua_sess, ua_chan, app); - if (ret < 0) { - /* XXX: We might want to report this error at some point... */ - continue; - } - } - - rcu_read_unlock(); - -error: - return ret; -} - -/* - * For a specific UST session, enable the channel for all registered apps. - */ -int ust_app_enable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app *app; - struct ust_app_session *ua_sess; - - if (usess == NULL || uchan == NULL) { - ERR("Adding UST global channel to NULL values"); - ret = -1; - goto error; - } - - DBG2("UST app enabling channel %s to global domain for session id %d", - uchan->name, usess->id); - - rcu_read_lock(); - - /* For every registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - continue; - } - - /* Enable channel onto application */ - ret = enable_ust_app_channel(ua_sess, uchan, app); - if (ret < 0) { - /* XXX: We might want to report this error at some point... */ - continue; - } - } - - rcu_read_unlock(); - -error: - return ret; -} - -/* - * Disable an event in a channel and for a specific session. - */ -int ust_app_disable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - int ret = 0; - struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *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; - - DBG("UST app disabling event %s for all apps in channel " - "%s for session id %d", uevent->attr.name, uchan->name, usess->id); - - rcu_read_lock(); - - /* For all registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - /* Next app */ - continue; - } - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_chan_node == NULL) { - DBG2("Channel %s not found in session id %d for app pid %d." - "Skipping", uchan->name, usess->id, app->key.pid); - continue; - } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter); - ua_event_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_event_node == NULL) { - DBG2("Event %s not found in channel %s for app pid %d." - "Skipping", uevent->attr.name, uchan->name, app->key.pid); - continue; - } - ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); - - ret = disable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - /* XXX: Report error someday... */ - continue; - } - } - - rcu_read_unlock(); - - return ret; -} - -/* - * For a specific UST session and UST channel, the event for all - * registered apps. - */ -int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - int ret = 0; - struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *ua_chan_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - struct ust_app_event *ua_event; - - DBG("UST app disabling all event for all apps in channel " - "%s for session id %d", uchan->name, usess->id); - - rcu_read_lock(); - - /* For all registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - /* If ua_sess is NULL, there is a code flow error */ - assert(ua_sess); - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - /* Disable each events of channel */ - cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event, - node.node) { - ret = disable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - /* XXX: Report error someday... */ - continue; - } - } - } - - rcu_read_unlock(); - - return ret; -} - -/* - * For a specific UST session, create the channel for all registered apps. - */ -int ust_app_create_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - struct lttng_ht_iter iter; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - - /* Very wrong code flow */ - assert(usess); - assert(uchan); - - DBG2("UST app adding channel %s to global domain for session id %d", - uchan->name, usess->id); - - rcu_read_lock(); - - /* For every registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - /* - * Create session on the tracer side and add it to app session HT. Note - * that if session exist, it will simply return a pointer to the ust - * app session. - */ - ua_sess = create_ust_app_session(usess, app); - if (ua_sess == NULL) { - /* Major problem here and it's maybe the tracer or malloc() */ - goto error; - } - - /* Create channel onto application */ - ua_chan = create_ust_app_channel(ua_sess, uchan, app); - if (ua_chan == NULL) { - /* Major problem here and it's maybe the tracer or malloc() */ - goto error; - } - } - - rcu_read_unlock(); - - return 0; - -error: - return -1; -} - -/* - * Enable event for a specific session and channel on the tracer. - */ -int ust_app_enable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - int ret = 0; - struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *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; - - DBG("UST app enabling event %s for all apps for session id %d", - uevent->attr.name, usess->id); - - /* - * NOTE: At this point, this function is called only if the session and - * channel passed are already created for all apps. and enabled on the - * tracer also. - */ - - rcu_read_lock(); - - /* For all registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - /* If ua_sess is NULL, there is a code flow error */ - assert(ua_sess); - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - lttng_ht_lookup(ua_chan->events, (void*)uevent->attr.name, &uiter); - ua_event_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_event_node == NULL) { - DBG3("UST app enable event %s not found for app PID %d." - "Skipping app", uevent->attr.name, app->key.pid); - continue; - } - ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); - - ret = enable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - goto error; - } - } - -error: - rcu_read_unlock(); - return ret; -} - -/* - * For a specific existing UST session and UST channel, creates the event for - * all registered apps. - */ -int ust_app_create_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - int ret = 0; - struct lttng_ht_iter iter, uiter; - struct lttng_ht_node_str *ua_chan_node; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - - DBG("UST app creating event %s for all apps for session id %d", - uevent->attr.name, usess->id); - - rcu_read_lock(); - - /* For all registered applications */ - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - /* If ua_sess is NULL, there is a code flow error */ - assert(ua_sess); - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); - if (ret < 0) { - if (ret != -EEXIST) { - /* Possible value at this point: -ENOMEM. If so, we stop! */ - break; - } - DBG2("UST app event %s already exist on app PID %d", - uevent->attr.name, app->key.pid); - continue; - } - } - - rcu_read_unlock(); - - return ret; -} - -/* - * Start tracing for a specific UST session and app. - */ -int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - struct ltt_ust_stream *ustream; - int consumerd_fd; - - DBG("Starting tracing for ust app pid %d", app->key.pid); - - rcu_read_lock(); - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - goto error_rcu_unlock; - } - - /* Upon restart, we skip the setup, already done */ - if (ua_sess->started) { - goto skip_setup; - } - - ret = create_ust_app_metadata(ua_sess, usess->pathname, app); - if (ret < 0) { - goto error_rcu_unlock; - } - - /* For each channel */ - cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, - node.node) { - /* Create all streams */ - while (1) { - /* Create UST stream */ - ustream = zmalloc(sizeof(*ustream)); - if (ustream == NULL) { - PERROR("zmalloc ust stream"); - goto error_rcu_unlock; - } - - ret = ustctl_create_stream(app->key.sock, ua_chan->obj, - &ustream->obj); - if (ret < 0) { - /* Got all streams */ - break; - } - ustream->handle = ustream->obj->handle; - - /* Order is important */ - cds_list_add_tail(&ustream->list, &ua_chan->streams.head); - ret = snprintf(ustream->pathname, PATH_MAX, "%s/%s_%u", - ua_sess->path, ua_chan->name, - ua_chan->streams.count++); - if (ret < 0) { - PERROR("asprintf UST create stream"); - continue; - } - DBG2("UST stream %d ready at %s", ua_chan->streams.count, - ustream->pathname); - } - } - - switch (app->bits_per_long) { - case 64: - consumerd_fd = ust_consumerd64_fd; - break; - case 32: - consumerd_fd = ust_consumerd32_fd; - break; - default: - ret = -EINVAL; - goto error_rcu_unlock; - } - - /* Setup UST consumer socket and send fds to it */ - ret = ust_consumer_send_session(consumerd_fd, ua_sess); - if (ret < 0) { - goto error_rcu_unlock; - } - ua_sess->started = 1; - -skip_setup: - /* 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 error_rcu_unlock; - } - - rcu_read_unlock(); - - /* Quiescent wait after starting trace */ - ustctl_wait_quiescent(app->key.sock); - - return 0; - -error_rcu_unlock: - rcu_read_unlock(); - return -1; -} - -/* - * Stop tracing for a specific UST session and app. - */ -int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - - DBG("Stopping tracing for ust app pid %d", app->key.pid); - - rcu_read_lock(); - - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - /* Only malloc can failed so something is really wrong */ - goto error_rcu_unlock; - } - - /* Not started, continuing. */ - if (ua_sess->started == 0) { - goto end; - } - - /* This inhibits UST tracing */ - ret = ustctl_stop_session(app->key.sock, ua_sess->handle); - if (ret < 0) { - ERR("Error stopping tracing for app pid: %d", app->key.pid); - goto error_rcu_unlock; - } - - /* Quiescent wait after stopping trace */ - ustctl_wait_quiescent(app->key.sock); - - /* Flushing buffers */ - cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, - node.node) { - ret = ustctl_sock_flush_buffer(app->key.sock, ua_chan->obj); - if (ret < 0) { - ERR("UST app PID %d channel %s flush failed with ret %d", - app->key.pid, ua_chan->name, ret); - /* Continuing flushing all buffers */ - continue; - } - } - - /* Flush all buffers before stopping */ - ret = ustctl_sock_flush_buffer(app->key.sock, ua_sess->metadata->obj); - if (ret < 0) { - ERR("UST app PID %d metadata flush failed with ret %d", app->key.pid, - ret); - } - - ua_sess->started = 0; - -end: - rcu_read_unlock(); - return 0; - -error_rcu_unlock: - rcu_read_unlock(); - return -1; -} - -/* - * Destroy a specific UST session in apps. - */ -int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app) -{ - struct ust_app_session *ua_sess; - struct lttng_ust_object_data obj; - struct lttng_ht_iter iter; - struct lttng_ht_node_ulong *node; - int ret; - - DBG("Destroy tracing for ust app pid %d", app->key.pid); - - rcu_read_lock(); - - __lookup_session_by_app(usess, app, &iter); - node = lttng_ht_iter_get_node_ulong(&iter); - if (node == NULL) { - /* Only malloc can failed so something is really wrong */ - goto error_rcu_unlock; - } - ua_sess = caa_container_of(node, struct ust_app_session, node); - ret = lttng_ht_del(app->sessions, &iter); - assert(!ret); - obj.handle = ua_sess->handle; - obj.shm_fd = -1; - obj.wait_fd = -1; - obj.memory_map_size = 0; - ustctl_release_object(app->key.sock, &obj); - - delete_ust_app_session(app->key.sock, ua_sess); - - rcu_read_unlock(); - - /* Quiescent wait after stopping trace */ - ustctl_wait_quiescent(app->key.sock); - - return 0; - -error_rcu_unlock: - rcu_read_unlock(); - return -1; -} - -/* - * Start tracing for the UST session. - */ -int ust_app_start_trace_all(struct ltt_ust_session *usess) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app *app; - - DBG("Starting all UST traces"); - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ret = ust_app_start_trace(usess, app); - if (ret < 0) { - /* Continue to next apps even on error */ - continue; - } - } - - rcu_read_unlock(); - - return 0; -} - -/* - * Start tracing for the UST session. - */ -int ust_app_stop_trace_all(struct ltt_ust_session *usess) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app *app; - - DBG("Stopping all UST traces"); - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ret = ust_app_stop_trace(usess, app); - if (ret < 0) { - /* Continue to next apps even on error */ - continue; - } - } - - rcu_read_unlock(); - - return 0; -} - -/* - * Destroy app UST session. - */ -int ust_app_destroy_trace_all(struct ltt_ust_session *usess) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct ust_app *app; - - DBG("Destroy all UST traces"); - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ret = ust_app_destroy_trace(usess, app); - if (ret < 0) { - /* Continue to next apps even on error */ - continue; - } - } - - rcu_read_unlock(); - - return 0; -} - -/* - * Add channels/events from UST global domain to registered apps at sock. - */ -void ust_app_global_update(struct ltt_ust_session *usess, int sock) -{ - int ret = 0; - struct lttng_ht_iter iter, uiter; - struct ust_app *app; - struct ust_app_session *ua_sess; - struct ust_app_channel *ua_chan; - struct ust_app_event *ua_event; - - if (usess == NULL) { - ERR("No UST session on global update. Returning"); - goto error; - } - - DBG2("UST app global update for app sock %d for session id %d", sock, - usess->id); - - rcu_read_lock(); - - app = find_app_by_sock(sock); - if (app == NULL) { - ERR("Failed to update app sock %d", sock); - goto error; - } - - ua_sess = create_ust_app_session(usess, app); - if (ua_sess == NULL) { - goto error; - } - - /* - * We can iterate safely here over all UST app session sicne the create ust - * app session above made a shadow copy of the UST global domain from the - * ltt ust session. - */ - cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, - node.node) { - ret = create_ust_channel(app, ua_sess, ua_chan); - if (ret < 0) { - /* FIXME: Should we quit here or continue... */ - continue; - } - - /* For each events */ - cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event, - node.node) { - ret = create_ust_event(app, ua_sess, ua_chan, ua_event); - if (ret < 0) { - /* FIXME: Should we quit here or continue... */ - continue; - } - } - } - - if (usess->start_trace) { - ret = ust_app_start_trace(usess, app); - if (ret < 0) { - goto error; - } - - DBG2("UST trace started for app pid %d", app->key.pid); - } - -error: - rcu_read_unlock(); - return; -} - -/* - * Add context to a specific channel for global UST domain. - */ -int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) -{ - int ret = 0; - struct lttng_ht_node_str *ua_chan_node; - struct lttng_ht_iter iter, uiter; - struct ust_app_channel *ua_chan = NULL; - struct ust_app_session *ua_sess; - struct ust_app *app; - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - continue; - } - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_chan_node == NULL) { - continue; - } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, - node); - - ret = create_ust_app_channel_context(ua_sess, ua_chan, &uctx->ctx, app); - if (ret < 0) { - continue; - } - } - - rcu_read_unlock(); - return ret; -} - -/* - * Add context to a specific event in a channel for global UST domain. - */ -int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - struct ltt_ust_context *uctx) -{ - int ret = 0; - struct lttng_ht_node_str *ua_chan_node, *ua_event_node; - struct lttng_ht_iter iter, uiter; - struct ust_app_session *ua_sess; - struct ust_app_event *ua_event; - struct ust_app_channel *ua_chan = NULL; - struct ust_app *app; - - rcu_read_lock(); - - cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { - ua_sess = lookup_session_by_app(usess, app); - if (ua_sess == NULL) { - continue; - } - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); - ua_chan_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_chan_node == NULL) { - continue; - } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, - node); - - lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter); - ua_event_node = lttng_ht_iter_get_node_str(&uiter); - if (ua_event_node == NULL) { - continue; - } - ua_event = caa_container_of(ua_event_node, struct ust_app_event, - node); - - ret = create_ust_app_event_context(ua_sess, ua_event, &uctx->ctx, app); - if (ret < 0) { - continue; - } - } - - rcu_read_unlock(); - return ret; -} - -/* - * Enable event for a channel from a UST session for a specific PID. - */ -int ust_app_enable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *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; - - DBG("UST app enabling event %s for PID %d", uevent->attr.name, pid); - - rcu_read_lock(); - - app = ust_app_find_by_pid(pid); - if (app == NULL) { - ERR("UST app enable event per PID %d not found", pid); - ret = -1; - goto error; - } - - ua_sess = lookup_session_by_app(usess, app); - /* If ua_sess is NULL, there is a code flow error */ - assert(ua_sess); - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); - ua_chan_node = lttng_ht_iter_get_node_str(&iter); - /* If the channel is not found, there is a code flow error */ - assert(ua_chan_node); - - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); - ua_event_node = lttng_ht_iter_get_node_str(&iter); - if (ua_event_node == NULL) { - ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); - if (ret < 0) { - goto error; - } - } else { - ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); - - ret = enable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - goto error; - } - } - -error: - rcu_read_unlock(); - return ret; -} - -/* - * Disable event for a channel from a UST session for a specific PID. - */ -int ust_app_disable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid) -{ - int ret = 0; - struct lttng_ht_iter iter; - struct lttng_ht_node_str *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; - - DBG("UST app disabling event %s for PID %d", uevent->attr.name, pid); - - rcu_read_lock(); - - app = ust_app_find_by_pid(pid); - if (app == NULL) { - ERR("UST app disable event per PID %d not found", pid); - ret = -1; - goto error; - } - - ua_sess = lookup_session_by_app(usess, app); - /* If ua_sess is NULL, there is a code flow error */ - assert(ua_sess); - - /* Lookup channel in the ust app session */ - lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); - ua_chan_node = lttng_ht_iter_get_node_str(&iter); - if (ua_chan_node == NULL) { - /* Channel does not exist, skip disabling */ - goto error; - } - ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); - - lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); - ua_event_node = lttng_ht_iter_get_node_str(&iter); - if (ua_event_node == NULL) { - /* Event does not exist, skip disabling */ - goto error; - } - ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); - - ret = disable_ust_app_event(ua_sess, ua_event, app); - if (ret < 0) { - goto error; - } - -error: - rcu_read_unlock(); - return ret; -} diff --git a/lttng-sessiond/ust-app.h b/lttng-sessiond/ust-app.h deleted file mode 100644 index 071ad0d90..000000000 --- a/lttng-sessiond/ust-app.h +++ /dev/null @@ -1,327 +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" - -#define UST_APP_EVENT_LIST_SIZE 32 - -extern int ust_consumerd64_fd, ust_consumerd32_fd; - -/* - * 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; - uint32_t bits_per_long; - char name[16]; -}; - -/* - * Global applications HT used by the session daemon. - */ -struct lttng_ht *ust_app_ht; - -struct lttng_ht *ust_app_sock_key_map; - -struct ust_app_key { - pid_t pid; - int sock; - struct lttng_ht_node_ulong node; -}; - -struct ust_app_ctx { - int handle; - struct lttng_ust_context ctx; - struct lttng_ust_object_data *obj; - struct lttng_ht_node_ulong node; -}; - -struct ust_app_event { - int enabled; - int handle; - struct lttng_ust_object_data *obj; - struct lttng_ust_event attr; - char name[LTTNG_UST_SYM_NAME_LEN]; - struct lttng_ht *ctx; - struct lttng_ht_node_str 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 ltt_ust_stream_list streams; - struct lttng_ht *ctx; - struct lttng_ht *events; - struct lttng_ht_node_str node; -}; - -struct ust_app_session { - int enabled; - /* started: has the session been in started state at any time ? */ - int started; /* allows detection of start vs restart. */ - int handle; /* used has unique identifier for app session */ - int id; /* session unique identifier */ - struct ltt_ust_metadata *metadata; - struct lttng_ht *channels; /* Registered channels */ - struct lttng_ht_node_ulong node; - char path[PATH_MAX]; - /* UID/GID of the user owning the session */ - uid_t uid; - gid_t gid; -}; - -/* - * 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 */ - int bits_per_long; - uint32_t v_major; /* Verion major number */ - uint32_t v_minor; /* Verion minor number */ - char name[17]; /* Process name (short) */ - struct lttng_ht *sessions; - struct lttng_ht_node_ulong node; - struct ust_app_key key; -}; - -#ifdef HAVE_LIBLTTNG_UST_CTL - -int ust_app_register(struct ust_register_msg *msg, int sock); -static inline -int ust_app_register_done(int sock) -{ - return ustctl_register_done(sock); -} -void ust_app_unregister(int sock); -unsigned long ust_app_list_count(void); -int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app); -int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app); -int ust_app_start_trace_all(struct ltt_ust_session *usess); -int ust_app_stop_trace_all(struct ltt_ust_session *usess); -int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app); -int ust_app_destroy_trace_all(struct ltt_ust_session *usess); -int ust_app_list_events(struct lttng_event **events); -int ust_app_create_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_create_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -int ust_app_disable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - pid_t pid); -int ust_app_enable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - pid_t pid); -int ust_app_disable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_enable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_enable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_enable_all_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan); -int ust_app_disable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); -int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - struct ltt_ust_context *uctx); -int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx); -void ust_app_global_update(struct ltt_ust_session *usess, int sock); - -void ust_app_clean_list(void); -void ust_app_ht_alloc(void); -struct lttng_ht *ust_app_get_ht(void); -struct ust_app *ust_app_find_by_pid(pid_t pid); - -#else /* HAVE_LIBLTTNG_UST_CTL */ - -static inline -int ust_app_destroy_trace_all(struct ltt_ust_session *usess) -{ - return 0; -} -static inline -int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) -{ - return 0; -} -static inline -int ust_app_start_trace_all(struct ltt_ust_session *usess) -{ - return 0; -} -static inline -int ust_app_stop_trace_all(struct ltt_ust_session *usess) -{ - return 0; -} -static inline -int ust_app_list_events(struct lttng_event **events) -{ - return 0; -} -static inline -int ust_app_register(struct ust_register_msg *msg, int sock) -{ - return -ENOSYS; -} -static inline -int ust_app_register_done(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 -struct lttng_ht *ust_app_get_ht(void) -{ - return NULL; -} -static inline -void ust_app_ht_alloc(void) -{} -static inline -void ust_app_global_update(struct ltt_ust_session *usess, int sock) -{} -static inline -int ust_app_disable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} -static inline -int ust_app_enable_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} -static inline -int ust_app_create_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} -static inline -int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} -static inline -int ust_app_enable_all_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan) -{ - return 0; -} -static inline -int ust_app_create_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - return 0; -} -static inline -int ust_app_disable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - return 0; -} -static inline -int ust_app_enable_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) -{ - return 0; -} -static inline -int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - struct ltt_ust_context *uctx) -{ - return 0; -} -static inline -int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) -{ - return 0; -} -static inline -int ust_app_enable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - pid_t pid) -{ - return 0; -} -static inline -int ust_app_disable_event_pid(struct ltt_ust_session *usess, - struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, - pid_t pid) -{ - return 0; -} - -#endif /* HAVE_LIBLTTNG_UST_CTL */ - -#endif /* _LTT_UST_APP_H */ diff --git a/lttng-sessiond/ust-consumer.c b/lttng-sessiond/ust-consumer.c deleted file mode 100644 index c02cbf965..000000000 --- a/lttng-sessiond/ust-consumer.c +++ /dev/null @@ -1,200 +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 - -#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, - uid_t uid, gid_t gid) -{ - int ret, fd; - struct lttcomm_consumer_msg lum; - struct ltt_ust_stream *stream, *tmp; - - 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 while we transfer the stream file - * descriptors to make sure this key stays unique within the - * session daemon. We can free the channel shm_fd without - * problem after we finished sending stream fds for that - * channel. - */ - 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; - } - fd = uchan->obj->shm_fd; - ret = lttcomm_send_fds_unix_sock(sock, &fd, 1); - if (ret < 0) { - perror("send consumer channel ancillary data"); - goto error; - } - - cds_list_for_each_entry_safe(stream, tmp, &uchan->streams.head, list) { - int fds[2]; - - if (!stream->obj->shm_fd) { - continue; - } - 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; - /* - * FIXME Hack alert! we force MMAP for now. Mixup - * between EVENT and UST enums elsewhere. - */ - lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT; - lum.u.stream.mmap_len = stream->obj->memory_map_size; - lum.u.stream.uid = uid; - lum.u.stream.gid = gid; - 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; - } - } - - 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 lttng_ht_iter iter; - struct lttcomm_consumer_msg lum; - struct ust_app_channel *ua_chan; - - DBG("Sending metadata stream fd"); - - if (consumer_fd < 0) { - ERR("Consumer has negative file descriptor"); - return -EINVAL; - } - - if (usess->metadata->obj->shm_fd != 0) { - int fd; - 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 = usess->metadata->obj->memory_map_size; - DBG("Sending metadata 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; - } - fd = usess->metadata->obj->shm_fd; - ret = lttcomm_send_fds_unix_sock(sock, &fd, 1); - 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; - lum.u.stream.uid = usess->uid; - lum.u.stream.gid = usess->gid; - 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(); - cds_lfht_for_each_entry(usess->channels->ht, &iter.iter, ua_chan, - node.node) { - ret = send_channel_streams(sock, ua_chan, usess->uid, usess->gid); - if (ret < 0) { - rcu_read_unlock(); - goto error; - } - } - 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 deleted file mode 100644 index 85169e6b8..000000000 --- a/lttng-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/lttng-sessiond/ust-ctl.h b/lttng-sessiond/ust-ctl.h deleted file mode 100644 index f4f34ee2f..000000000 --- a/lttng-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 HAVE_LIBLTTNG_UST_CTL -#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 deleted file mode 100644 index 22a07ffae..000000000 --- a/lttng-sessiond/utils.c +++ /dev/null @@ -1,54 +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 "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")); -} diff --git a/lttng-sessiond/utils.h b/lttng-sessiond/utils.h deleted file mode 100644 index e20ec4e9e..000000000 --- a/lttng-sessiond/utils.h +++ /dev/null @@ -1,24 +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 - -const char *get_home_dir(void); -int notify_thread_pipe(int wpipe); - -#endif /* _LTT_UTILS_H */ diff --git a/lttng/Makefile.am b/lttng/Makefile.am deleted file mode 100644 index 1105143f3..000000000 --- a/lttng/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -AM_CPPFLAGS = -I$(top_srcdir)/include \ - -DINSTALL_BIN_PATH=\""$(bindir)"\" - -bin_PROGRAMS = lttng - -lttng_SOURCES = cmd.h conf.c conf.h commands/start.c \ - commands/list.c commands/create.c commands/destroy.c \ - commands/stop.c commands/enable_events.c \ - commands/disable_events.c commands/enable_channels.c \ - commands/disable_channels.c commands/add_context.c \ - commands/set_session.c commands/version.c \ - commands/calibrate.c utils.c utils.h lttng.c - -lttng_LDADD = \ - $(top_builddir)/liblttngctl/liblttngctl.la diff --git a/lttng/cmd.h b/lttng/cmd.h deleted file mode 100644 index 257d0a1c4..000000000 --- a/lttng/cmd.h +++ /dev/null @@ -1,54 +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 _LTTNG_CMD_H -#define _LTTNG_CMD_H - -#include -#include "lttngerr.h" -#include "lttng-share.h" -#include "lttng-kernel.h" - -enum cmd_error_code { - CMD_SUCCESS, - CMD_ERROR, - CMD_UNDEFINED, - CMD_NOT_IMPLEMENTED, - CMD_FATAL, -}; - -struct cmd_struct { - const char *name; - int (*func)(int argc, const char **argv); -}; - -extern int cmd_list(int argc, const char **argv); -extern int cmd_create(int argc, const char **argv); -extern int cmd_destroy(int argc, const char **argv); -extern int cmd_start(int argc, const char **argv); -extern int cmd_stop(int argc, const char **argv); -extern int cmd_enable_events(int argc, const char **argv); -extern int cmd_disable_events(int argc, const char **argv); -extern int cmd_enable_channels(int argc, const char **argv); -extern int cmd_disable_channels(int argc, const char **argv); -extern int cmd_add_context(int argc, const char **argv); -extern int cmd_set_session(int argc, const char **argv); -extern int cmd_version(int argc, const char **argv); -extern int cmd_calibrate(int argc, const char **argv); - -#endif /* _LTTNG_CMD_H */ diff --git a/lttng/commands/add_context.c b/lttng/commands/add_context.c deleted file mode 100644 index 2d9699ce2..000000000 --- a/lttng/commands/add_context.c +++ /dev/null @@ -1,487 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -#define PRINT_LINE_LEN 80 - -static char *opt_event_name; -static char *opt_channel_name; -static char *opt_session_name; -static int opt_kernel; -static int opt_userspace; -static char *opt_cmd_name; -static pid_t opt_pid; -static char *opt_type; - -enum { - OPT_HELP = 1, - OPT_TYPE, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -/* - * Taken from the LTTng ABI - */ -enum context_type { - CONTEXT_PID = 0, - CONTEXT_PERF_COUNTER = 1, - CONTEXT_PROCNAME = 2, - CONTEXT_PRIO = 3, - CONTEXT_NICE = 4, - CONTEXT_VPID = 5, - CONTEXT_TID = 6, - CONTEXT_VTID = 7, - CONTEXT_PPID = 8, - CONTEXT_VPPID = 9, -}; - -/* - * Taken from the Perf ABI (all enum perf_*) - */ -enum perf_type { - PERF_TYPE_HARDWARE = 0, - PERF_TYPE_SOFTWARE = 1, - PERF_TYPE_HW_CACHE = 3, -}; - -enum perf_count_hard { - PERF_COUNT_HW_CPU_CYCLES = 0, - PERF_COUNT_HW_INSTRUCTIONS = 1, - PERF_COUNT_HW_CACHE_REFERENCES = 2, - PERF_COUNT_HW_CACHE_MISSES = 3, - PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, - PERF_COUNT_HW_BRANCH_MISSES = 5, - PERF_COUNT_HW_BUS_CYCLES = 6, - PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, - PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, -}; - -enum perf_count_soft { - PERF_COUNT_SW_CPU_CLOCK = 0, - PERF_COUNT_SW_TASK_CLOCK = 1, - PERF_COUNT_SW_PAGE_FAULTS = 2, - PERF_COUNT_SW_CONTEXT_SWITCHES = 3, - PERF_COUNT_SW_CPU_MIGRATIONS = 4, - PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, - PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, - PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, - PERF_COUNT_SW_EMULATION_FAULTS = 8, -}; - -/* - * Generalized hardware cache events: - * - * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x - * { read, write, prefetch } x - * { accesses, misses } - */ -enum perf_hw_cache_id { - PERF_COUNT_HW_CACHE_L1D = 0, - PERF_COUNT_HW_CACHE_L1I = 1, - PERF_COUNT_HW_CACHE_LL = 2, - PERF_COUNT_HW_CACHE_DTLB = 3, - PERF_COUNT_HW_CACHE_ITLB = 4, - PERF_COUNT_HW_CACHE_BPU = 5, - - PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_id { - PERF_COUNT_HW_CACHE_OP_READ = 0, - PERF_COUNT_HW_CACHE_OP_WRITE = 1, - PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, - - PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_result_id { - PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, - PERF_COUNT_HW_CACHE_RESULT_MISS = 1, - - PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"event", 'e', POPT_ARG_STRING, &opt_event_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {"type", 't', POPT_ARG_STRING, &opt_type, OPT_TYPE, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * Context options - */ -#define PERF_HW(opt, name) \ - { \ - "perf:" #opt, CONTEXT_PERF_COUNTER, \ - .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\ - } - -#define PERF_SW(opt, name) \ - { \ - "perf:" #opt, CONTEXT_PERF_COUNTER, \ - .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\ - } - -#define _PERF_HW_CACHE(optstr, name, op, result) \ - { \ - "perf:" optstr, CONTEXT_PERF_COUNTER, \ - .u.perf = { \ - PERF_TYPE_HW_CACHE, \ - (uint64_t) PERF_COUNT_HW_CACHE_##name \ - | ((uint64_t) PERF_COUNT_HW_CACHE_OP_##op << 8) \ - | ((uint64_t) PERF_COUNT_HW_CACHE_RESULT_##result << 16), \ - }, \ - } - -#define PERF_HW_CACHE(opt, name) \ - _PERF_HW_CACHE(#opt "-loads", name, READ, ACCESS), \ - _PERF_HW_CACHE(#opt "-load-misses", name, READ, MISS), \ - _PERF_HW_CACHE(#opt "-stores", name, WRITE, ACCESS), \ - _PERF_HW_CACHE(#opt "-store-misses", name, WRITE, MISS), \ - _PERF_HW_CACHE(#opt "-prefetches", name, PREFETCH, ACCESS), \ - _PERF_HW_CACHE(#opt "-prefetch-misses", name, PREFETCH, MISS) \ - -static -const struct ctx_opts { - char *symbol; - enum context_type ctx_type; - union { - struct { - uint32_t type; - uint64_t config; - } perf; - } u; -} ctx_opts[] = { - { "pid", CONTEXT_PID }, - { "procname", CONTEXT_PROCNAME }, - { "prio", CONTEXT_PRIO }, - { "nice", CONTEXT_NICE }, - { "vpid", CONTEXT_VPID }, - { "tid", CONTEXT_TID }, - { "vtid", CONTEXT_VTID }, - { "ppid", CONTEXT_PPID }, - { "vppid", CONTEXT_VPPID }, - /* Perf options */ - PERF_HW(cpu-cycles, CPU_CYCLES), - PERF_HW(cycles, CPU_CYCLES), - PERF_HW(stalled-cycles-frontend, STALLED_CYCLES_FRONTEND), - PERF_HW(idle-cycles-frontend, STALLED_CYCLES_FRONTEND), - PERF_HW(stalled-cycles-backend, STALLED_CYCLES_BACKEND), - PERF_HW(idle-cycles-backend, STALLED_CYCLES_BACKEND), - PERF_HW(instructions, INSTRUCTIONS), - PERF_HW(cache-references, CACHE_REFERENCES), - PERF_HW(cache-misses, CACHE_MISSES), - PERF_HW(branch-instructions, BRANCH_INSTRUCTIONS), - PERF_HW(branches, BRANCH_INSTRUCTIONS), - PERF_HW(branch-misses, BRANCH_MISSES), - PERF_HW(bus-cycles, BUS_CYCLES), - - PERF_HW_CACHE(L1-dcache, L1D), - PERF_HW_CACHE(L1-icache, L1I), - PERF_HW_CACHE(LLC, LL), - PERF_HW_CACHE(dTLB, DTLB), - _PERF_HW_CACHE("iTLB-loads", ITLB, READ, ACCESS), - _PERF_HW_CACHE("iTLB-load-misses", ITLB, READ, MISS), - _PERF_HW_CACHE("branch-loads", BPU, READ, ACCESS), - _PERF_HW_CACHE("branch-load-misses", BPU, READ, MISS), - - - PERF_SW(cpu-clock, CPU_CLOCK), - PERF_SW(task-clock, TASK_CLOCK), - PERF_SW(page-fault, PAGE_FAULTS), - PERF_SW(faults, PAGE_FAULTS), - PERF_SW(major-faults, PAGE_FAULTS_MAJ), - PERF_SW(minor-faults, PAGE_FAULTS_MIN), - PERF_SW(context-switches, CONTEXT_SWITCHES), - PERF_SW(cs, CONTEXT_SWITCHES), - PERF_SW(cpu-migrations, CPU_MIGRATIONS), - PERF_SW(migrations, CPU_MIGRATIONS), - PERF_SW(alignment-faults, ALIGNMENT_FAULTS), - PERF_SW(emulation-faults, EMULATION_FAULTS), - { NULL, -1 }, /* Closure */ -}; - -#undef PERF_SW -#undef PERF_HW - -/* - * Context type for command line option parsing. - */ -struct ctx_type { - const struct ctx_opts *opt; - struct cds_list_head list; -}; - -/* - * List of context type. Use to enable multiple context on a single command - * line entry. - */ -struct ctx_type_list { - struct cds_list_head head; -} ctx_type_list = { - .head = CDS_LIST_HEAD_INIT(ctx_type_list.head), -}; - -/* - * Pretty print context type. - */ -static void print_ctx_type(FILE *ofp) -{ - const char *indent = " "; - int indent_len = strlen(indent); - int len, i = 0; - - fprintf(ofp, "%s", indent); - len = indent_len; - while (ctx_opts[i].symbol != NULL) { - if (len > indent_len) { - if (len + strlen(ctx_opts[i].symbol) + 2 - >= PRINT_LINE_LEN) { - fprintf(ofp, ",\n"); - fprintf(ofp, "%s", indent); - len = indent_len; - } else { - len += fprintf(ofp, ", "); - } - } - len += fprintf(ofp, "%s", ctx_opts[i].symbol); - i++; - } -} - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng add-context -t TYPE\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "If no event name is given (-e), the context will be added to the channel\n"); - fprintf(ofp, "If no channel and no event is given (-c/-e), the context\n"); - fprintf(ofp, "will be added to all events and all channels.\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Options:\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -s, --session Apply on session name\n"); - fprintf(ofp, " -c, --channel NAME Apply on channel\n"); - fprintf(ofp, " -e, --event NAME Apply on event\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " If no CMD, the domain used is UST global\n"); - fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); - fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); - fprintf(ofp, " -t, --type TYPE Context type. You can repeat that option on\n"); - fprintf(ofp, " the command line.\n"); - fprintf(ofp, " TYPE can be one of the strings below:\n"); - print_ctx_type(ofp); - fprintf(ofp, "\n"); - fprintf(ofp, "Example:\n"); - fprintf(ofp, "This command will add the context information 'prio' and two perf\n" - "counters: hardware branch misses and cache misses, to all events\n" - "in the trace data output:\n"); - fprintf(ofp, "# lttng add-context -k -t prio -t perf:branch-misses -t perf:cache-misses\n"); - fprintf(ofp, "\n"); -} - -/* - * Find context numerical value from string. - */ -static int find_ctx_type_idx(const char *opt) -{ - int ret = -1, i = 0; - - while (ctx_opts[i].symbol != NULL) { - if (strcmp(opt, ctx_opts[i].symbol) == 0) { - ret = i; - goto end; - } - i++; - } - -end: - return ret; -} - -/* - * Add context to channel or event. - */ -static int add_context(char *session_name) -{ - int ret = CMD_SUCCESS; - struct lttng_event_context context; - struct lttng_domain dom; - struct ctx_type *type; - char *ptr; - - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_pid != 0) { - dom.type = LTTNG_DOMAIN_UST_PID; - dom.attr.pid = opt_pid; - DBG("PID %d set to lttng handle", opt_pid); - } else if (opt_userspace && opt_cmd_name == NULL) { - dom.type = LTTNG_DOMAIN_UST; - } else if (opt_userspace && opt_cmd_name != NULL) { - dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; - strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Iterate over all context type given */ - cds_list_for_each_entry(type, &ctx_type_list.head, list) { - context.ctx = type->opt->ctx_type; - if (context.ctx == LTTNG_EVENT_CONTEXT_PERF_COUNTER) { - context.u.perf_counter.type = type->opt->u.perf.type; - context.u.perf_counter.config = type->opt->u.perf.config; - strcpy(context.u.perf_counter.name, type->opt->symbol); - /* Replace : and - by _ */ - while ((ptr = strchr(context.u.perf_counter.name, '-')) != NULL) { - *ptr = '_'; - } - while ((ptr = strchr(context.u.perf_counter.name, ':')) != NULL) { - *ptr = '_'; - } - } - DBG("Adding context..."); - - ret = lttng_add_context(handle, &context, opt_event_name, - opt_channel_name); - if (ret < 0) { - fprintf(stderr, "%s: ", type->opt->symbol); - continue; - } else { - MSG("%s context %s added to %s event in %s", - opt_kernel ? "kernel" : "UST", type->opt->symbol, - opt_event_name ? opt_event_name : "all", - opt_channel_name ? opt_channel_name : "all channels"); - } - } - -error: - lttng_destroy_handle(handle); - - return ret; -} - -/* - * Add context on channel or event. - */ -int cmd_add_context(int argc, const char **argv) -{ - int index, opt, ret = CMD_SUCCESS; - static poptContext pc; - struct ctx_type *type, *tmptype; - char *session_name = NULL; - - if (argc < 2) { - usage(stderr); - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_TYPE: - type = malloc(sizeof(struct ctx_type)); - if (type == NULL) { - perror("malloc ctx_type"); - ret = -1; - goto end; - } - index = find_ctx_type_idx(opt_type); - if (index < 0) { - ERR("Unknown context type %s", opt_type); - goto end; - } - type->opt = &ctx_opts[index]; - if (type->opt->ctx_type == -1) { - ERR("Unknown context type %s", opt_type); - } else { - cds_list_add(&type->list, &ctx_type_list.head); - } - break; - case OPT_USERSPACE: - opt_userspace = 1; - opt_cmd_name = poptGetOptArg(pc); - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = -1; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = add_context(session_name); - - /* Cleanup allocated memory */ - cds_list_for_each_entry_safe(type, tmptype, &ctx_type_list.head, list) { - free(type); - } - -end: - return ret; -} diff --git a/lttng/commands/calibrate.c b/lttng/commands/calibrate.c deleted file mode 100644 index 3fa87e3ec..000000000 --- a/lttng/commands/calibrate.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2011 - David Goulet - * Copyright (C) 2011 - 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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static int opt_event_type; -static char *opt_kernel; -static int opt_pid_all; -static int opt_userspace; -static char *opt_cmd_name; -static pid_t opt_pid; - -enum { - OPT_HELP = 1, - OPT_TRACEPOINT, - OPT_MARKER, - OPT_PROBE, - OPT_FUNCTION, - OPT_FUNCTION_ENTRY, - OPT_SYSCALL, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"all", 0, POPT_ARG_VAL, &opt_pid_all, 1, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, - {"marker", 0, POPT_ARG_NONE, 0, OPT_MARKER, 0, 0}, - {"probe", 0, POPT_ARG_NONE, 0, OPT_PROBE, 0, 0}, - {"function", 0, POPT_ARG_NONE, 0, OPT_FUNCTION, 0, 0}, -#if 0 - /* - * Removed from options to discourage its use. Not in kernel - * tracer anymore. - */ - {"function:entry", 0, POPT_ARG_NONE, 0, OPT_FUNCTION_ENTRY, 0, 0}, -#endif - {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng calibrate [options] [calibrate_options]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " --all If -u, apply on all traceable apps\n"); - fprintf(ofp, " -p, --pid PID If -u, apply on a specific PID\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Calibrate options:\n"); - fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); - fprintf(ofp, " --probe\n"); - fprintf(ofp, " Dynamic probe.\n"); - fprintf(ofp, " --function\n"); - fprintf(ofp, " Dynamic function entry/return probe.\n"); -#if 0 - fprintf(ofp, " --function:entry symbol\n"); - fprintf(ofp, " Function tracer event\n"); -#endif - fprintf(ofp, " --syscall System call eventl\n"); - fprintf(ofp, " --marker User-space marker (deprecated)\n"); - fprintf(ofp, "\n"); -} - -/* - * calibrate_lttng - * - * Calibrate LTTng. - */ -static int calibrate_lttng(void) -{ - int ret = CMD_SUCCESS; - struct lttng_domain dom; - struct lttng_calibrate calibrate; - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } - - handle = lttng_create_handle(NULL, &dom); - if (handle == NULL) { - ret = -1; - goto end; - } - - /* Kernel tracer action */ - if (opt_kernel) { - switch (opt_event_type) { - case LTTNG_EVENT_TRACEPOINT: - DBG("Calibrating kernel tracepoints"); - break; - case LTTNG_EVENT_PROBE: - DBG("Calibrating kernel probes"); - break; - case LTTNG_EVENT_FUNCTION: - DBG("Calibrating kernel functions"); - calibrate.type = LTTNG_CALIBRATE_FUNCTION; - ret = lttng_calibrate(handle, &calibrate); - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - DBG("Calibrating kernel function entry"); - break; - case LTTNG_EVENT_SYSCALL: - DBG("Calibrating kernel syscall"); - break; - default: - ret = CMD_NOT_IMPLEMENTED; - goto end; - } - } else if (opt_userspace) { /* User-space tracer action */ - /* - * TODO: Waiting on lttng UST 2.0 - */ - if (opt_pid_all) { - } else if (opt_pid != 0) { - } - ret = CMD_NOT_IMPLEMENTED; - goto end; - } else { - ERR("Please specify a tracer (--kernel or --userspace)"); - goto end; - } -end: - lttng_destroy_handle(handle); - - return ret; -} - -/* - * cmd_calibrate - * - * Calibrate LTTng tracer. - */ -int cmd_calibrate(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - /* Default event type */ - opt_event_type = LTTNG_EVENT_TRACEPOINT; - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_TRACEPOINT: - ret = CMD_NOT_IMPLEMENTED; - break; - case OPT_MARKER: - ret = CMD_NOT_IMPLEMENTED; - goto end; - case OPT_PROBE: - ret = CMD_NOT_IMPLEMENTED; - break; - case OPT_FUNCTION: - opt_event_type = LTTNG_EVENT_FUNCTION; - break; - case OPT_FUNCTION_ENTRY: - ret = CMD_NOT_IMPLEMENTED; - break; - case OPT_SYSCALL: - ret = CMD_NOT_IMPLEMENTED; - break; - case OPT_USERSPACE: - opt_userspace = 1; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - ret = calibrate_lttng(); - -end: - return ret; -} diff --git a/lttng/commands/create.c b/lttng/commands/create.c deleted file mode 100644 index 4083e4195..000000000 --- a/lttng/commands/create.c +++ /dev/null @@ -1,178 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_output_path; -static char *opt_session_name; - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"output", 'o', POPT_ARG_STRING, &opt_output_path, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng create [options] [NAME]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -o, --output PATH Specify output path for traces\n"); - fprintf(ofp, "\n"); -} - -/* - * create_session - * - * Create a tracing session. If no name specified, a default name will be - * generated. - */ -static int create_session() -{ - int ret, have_name = 0; - char datetime[16]; - char *session_name, *traces_path = NULL, *alloc_path = NULL; - time_t rawtime; - struct tm *timeinfo; - - /* Get date and time for automatic session name/path */ - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); - - /* Auto session name creation */ - if (opt_session_name == NULL) { - ret = asprintf(&session_name, "auto-%s", datetime); - if (ret < 0) { - perror("asprintf session name"); - goto error; - } - DBG("Auto session name set to %s", session_name); - } else { - session_name = opt_session_name; - have_name = 1; - } - - /* Auto output path */ - if (opt_output_path == NULL) { - alloc_path = strdup(config_get_default_path()); - if (alloc_path == NULL) { - ERR("Home path not found.\n \ - Please specify an output path using -o, --output PATH"); - ret = CMD_FATAL; - goto error; - } - - if (have_name) { - ret = asprintf(&traces_path, "%s/" LTTNG_DEFAULT_TRACE_DIR_NAME - "/%s-%s", alloc_path, session_name, datetime); - } else { - ret = asprintf(&traces_path, "%s/" LTTNG_DEFAULT_TRACE_DIR_NAME - "/%s", alloc_path, session_name); - } - - if (ret < 0) { - perror("asprintf trace dir name"); - goto error; - } - } else { - traces_path = opt_output_path; - } - - ret = lttng_create_session(session_name, traces_path); - if (ret < 0) { - goto error; - } - - /* Init lttng session config */ - ret = config_init(session_name); - if (ret < 0) { - if (ret == -1) { - ret = CMD_ERROR; - } - goto error; - } - - MSG("Session %s created.", session_name); - MSG("Traces will be written in %s" , traces_path); - - ret = CMD_SUCCESS; - -error: - if (alloc_path) { - free(alloc_path); - } - - if (traces_path) { - free(traces_path); - } - return ret; -} - -/* - * cmd_list - * - * The 'list ' first level command - */ -int cmd_create(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - ret = create_session(); - -end: - return ret; -} diff --git a/lttng/commands/destroy.c b/lttng/commands/destroy.c deleted file mode 100644 index 151686ff9..000000000 --- a/lttng/commands/destroy.c +++ /dev/null @@ -1,134 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_session_name; - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng destroy [options] [NAME]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); - fprintf(ofp, "get it from the configuration directory (.lttng).\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, "\n"); -} - -/* - * Destroy a session removing the config directory and unregistering to the - * session daemon. - */ -static int destroy_session() -{ - int ret; - char *session_name, *path; - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - ret = lttng_destroy_session(session_name); - if (ret < 0) { - goto free_name; - } - - path = config_get_default_path(); - if (path == NULL) { - ret = CMD_FATAL; - goto free_name; - } - - if (opt_session_name == NULL) { - config_destroy(path); - MSG("Session %s destroyed at %s", session_name, path); - } else { - MSG("Session %s destroyed", session_name); - } - - ret = CMD_SUCCESS; - -free_name: - if (opt_session_name == NULL) { - free(session_name); - } -error: - return ret; -} - -/* - * The 'destroy ' first level command - */ -int cmd_destroy(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - ret = destroy_session(); - -end: - return ret; -} diff --git a/lttng/commands/disable_channels.c b/lttng/commands/disable_channels.c deleted file mode 100644 index 59ad9fc28..000000000 --- a/lttng/commands/disable_channels.c +++ /dev/null @@ -1,181 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_channels; -static int opt_kernel; -static char *opt_session_name; -static int opt_userspace; -static char *opt_cmd_name; -static pid_t opt_pid; - -enum { - OPT_HELP = 1, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng disable-channel NAME[,NAME2,...] [options]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -s, --session Apply on session name\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " If no CMD, the domain used is UST global\n"); - fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); - fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); - fprintf(ofp, "\n"); -} - -/* - * Disabling channel using the lttng API. - */ -static int disable_channels(char *session_name) -{ - int ret = CMD_SUCCESS; - char *channel_name; - struct lttng_domain dom; - - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_pid != 0) { - dom.type = LTTNG_DOMAIN_UST_PID; - dom.attr.pid = opt_pid; - DBG("PID %d set to lttng handle", opt_pid); - } else if (opt_userspace && opt_cmd_name == NULL) { - dom.type = LTTNG_DOMAIN_UST; - } else if (opt_userspace && opt_cmd_name != NULL) { - dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; - strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Strip channel list */ - channel_name = strtok(opt_channels, ","); - while (channel_name != NULL) { - DBG("Disabling channel %s", channel_name); - - ret = lttng_disable_channel(handle, channel_name); - if (ret < 0) { - goto error; - } else { - MSG("%s channel %s disabled for session %s", - opt_kernel ? "Kernel" : "UST", channel_name, - session_name); - } - - /* Next channel */ - channel_name = strtok(NULL, ","); - } - -error: - lttng_destroy_handle(handle); - - return ret; -} - -/* - * cmd_disable_channels - * - * Disable channel to trace session - */ -int cmd_disable_channels(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - char *session_name = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_USERSPACE: - opt_userspace = 1; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_channels = (char*) poptGetArg(pc); - if (opt_channels == NULL) { - ERR("Missing channel name(s).\n"); - usage(stderr); - ret = CMD_SUCCESS; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = -1; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = disable_channels(session_name); - -end: - return ret; -} diff --git a/lttng/commands/disable_events.c b/lttng/commands/disable_events.c deleted file mode 100644 index 5247110ef..000000000 --- a/lttng/commands/disable_events.c +++ /dev/null @@ -1,239 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_event_list; -static int opt_kernel; -static char *opt_channel_name; -static char *opt_session_name; -static int opt_userspace; -static char *opt_cmd_name; -static int opt_disable_all; -static pid_t opt_pid; - -enum { - OPT_HELP = 1, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"all-events", 'a', POPT_ARG_VAL, &opt_disable_all, 1, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng disable-event NAME[,NAME2,...] [options]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -s, --session Apply on session name\n"); - fprintf(ofp, " -c, --channel Apply on this channel\n"); - fprintf(ofp, " -a, --all-events Disable all tracepoints\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " If no CMD, the domain used is UST global\n"); - fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); - fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); - fprintf(ofp, "\n"); -} - -/* - * disable_events - * - * Disabling event using the lttng API. - */ -static int disable_events(char *session_name) -{ - int err, ret = CMD_SUCCESS; - char *event_name, *channel_name = NULL; - struct lttng_domain dom; - - if (opt_channel_name == NULL) { - err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME); - if (err < 0) { - ret = CMD_FATAL; - goto error; - } - } else { - channel_name = opt_channel_name; - } - - if (opt_kernel && opt_userspace) { - ERR("Can't use -k/--kernel and -u/--userspace together"); - ret = CMD_FATAL; - goto error; - } - - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_pid != 0) { - dom.type = LTTNG_DOMAIN_UST_PID; - dom.attr.pid = opt_pid; - DBG("PID %d set to lttng handle", opt_pid); - } else if (opt_userspace && opt_cmd_name == NULL) { - dom.type = LTTNG_DOMAIN_UST; - DBG("UST global domain selected"); - } else if (opt_userspace && opt_cmd_name != NULL) { - dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; - strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); - dom.attr.exec_name[NAME_MAX - 1] = '\0'; - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - if (opt_disable_all) { - ret = lttng_disable_event(handle, NULL, channel_name); - if (ret < 0) { - goto error; - } - - MSG("All %s events are disabled in channel %s", - opt_kernel ? "kernel" : "UST", channel_name); - goto end; - } - - /* Strip event list */ - event_name = strtok(opt_event_list, ","); - while (event_name != NULL) { - /* Kernel tracer action */ - if (opt_kernel) { - DBG("Disabling kernel event %s in channel %s", - event_name, channel_name); - } else if (opt_userspace) { /* User-space tracer action */ - if (opt_cmd_name != NULL || opt_pid) { - MSG("Only supporting tracing all UST processes (-u) for now."); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - DBG("Disabling UST event %s in channel %s", - event_name, channel_name); - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - goto error; - } - - ret = lttng_disable_event(handle, event_name, channel_name); - if (ret < 0) { - MSG("Unable to disable %s event %s in channel %s", - opt_kernel ? "kernel" : "UST", event_name, - channel_name); - } else { - MSG("%s event %s disabled in channel %s", - opt_kernel ? "kernel" : "UST", event_name, - channel_name); - } - - /* Next event */ - event_name = strtok(NULL, ","); - } - -end: -error: - if (opt_channel_name == NULL) { - free(channel_name); - } - lttng_destroy_handle(handle); - - return ret; -} - -/* - * cmd_disable_events - * - * Disable event to trace session - */ -int cmd_disable_events(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - char *session_name = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_USERSPACE: - opt_userspace = 1; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_event_list = (char*) poptGetArg(pc); - if (opt_event_list == NULL && opt_disable_all == 0) { - ERR("Missing event name(s).\n"); - usage(stderr); - ret = CMD_SUCCESS; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = -1; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = disable_events(session_name); - -end: - return ret; -} diff --git a/lttng/commands/enable_channels.c b/lttng/commands/enable_channels.c deleted file mode 100644 index 52745f69a..000000000 --- a/lttng/commands/enable_channels.c +++ /dev/null @@ -1,287 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_channels; -static int opt_kernel; -static char *opt_cmd_name; -static char *opt_session_name; -static int opt_userspace; -static char *opt_cmd_name; -static pid_t opt_pid; -static struct lttng_channel chan; - -enum { - OPT_HELP = 1, - OPT_DISCARD, - OPT_OVERWRITE, - OPT_SUBBUF_SIZE, - OPT_NUM_SUBBUF, - OPT_SWITCH_TIMER, - OPT_READ_TIMER, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {"discard", 0, POPT_ARG_NONE, 0, OPT_DISCARD, 0, 0}, - {"overwrite", 0, POPT_ARG_NONE, 0, OPT_OVERWRITE, 0, 0}, - {"subbuf-size", 0, POPT_ARG_DOUBLE, 0, OPT_SUBBUF_SIZE, 0, 0}, - {"num-subbuf", 0, POPT_ARG_INT, 0, OPT_NUM_SUBBUF, 0, 0}, - {"switch-timer", 0, POPT_ARG_INT, 0, OPT_SWITCH_TIMER, 0, 0}, - {"read-timer", 0, POPT_ARG_INT, 0, OPT_READ_TIMER, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng enable-channel NAME[,NAME2,...] [options] [channel_options]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -s, --session Apply on session name\n"); - fprintf(ofp, " -k, --kernel Apply on the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " If no CMD, the domain used is UST global\n"); - fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); - fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Channel options:\n"); - fprintf(ofp, " --discard Discard event when buffers are full%s\n", - DEFAULT_CHANNEL_OVERWRITE ? "" : " (default)"); - fprintf(ofp, " --overwrite Flight recorder mode%s\n", - DEFAULT_CHANNEL_OVERWRITE ? " (default)" : ""); - fprintf(ofp, " --subbuf-size Subbuffer size in bytes\n"); - fprintf(ofp, " (default: %u, kernel default: %u)\n", - DEFAULT_CHANNEL_SUBBUF_SIZE, - DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE); - fprintf(ofp, " --num-subbuf Number of subbufers\n"); - fprintf(ofp, " (default: %u, kernel default: %u)\n", - DEFAULT_CHANNEL_SUBBUF_NUM, - DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM); - fprintf(ofp, " --switch-timer Switch timer interval in usec (default: %u)\n", - DEFAULT_CHANNEL_SWITCH_TIMER); - fprintf(ofp, " --read-timer Read timer interval in usec (default: %u)\n", - DEFAULT_CHANNEL_READ_TIMER); - fprintf(ofp, "\n"); -} - -/* - * Set default attributes depending on those already defined from the command - * line. - */ -static void set_default_attr(struct lttng_domain *dom) -{ - struct lttng_channel_attr default_attr; - - /* Set attributes */ - lttng_channel_set_default_attr(dom, &default_attr); - - if (chan.attr.overwrite == -1) { - chan.attr.overwrite = default_attr.overwrite; - } - if (chan.attr.subbuf_size == -1) { - chan.attr.subbuf_size = default_attr.subbuf_size; - } - if (chan.attr.num_subbuf == -1) { - chan.attr.num_subbuf = default_attr.num_subbuf; - } - if (chan.attr.switch_timer_interval == -1) { - chan.attr.switch_timer_interval = default_attr.switch_timer_interval; - } - if (chan.attr.read_timer_interval == -1) { - chan.attr.read_timer_interval = default_attr.read_timer_interval; - } - if (chan.attr.output == -1) { - chan.attr.output = default_attr.output; - } -} - -/* - * Adding channel using the lttng API. - */ -static int enable_channel(char *session_name) -{ - int ret = CMD_SUCCESS; - char *channel_name; - struct lttng_domain dom; - - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_pid != 0) { - dom.type = LTTNG_DOMAIN_UST_PID; - dom.attr.pid = opt_pid; - DBG("PID %d set to lttng handle", opt_pid); - } else if (opt_userspace && opt_cmd_name == NULL) { - dom.type = LTTNG_DOMAIN_UST; - } else if (opt_userspace && opt_cmd_name != NULL) { - dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; - strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - set_default_attr(&dom); - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - /* Strip channel list (format: chan1,chan2,...) */ - channel_name = strtok(opt_channels, ","); - while (channel_name != NULL) { - /* Copy channel name and normalize it */ - strncpy(chan.name, channel_name, NAME_MAX); - chan.name[NAME_MAX - 1] = '\0'; - - DBG("Enabling channel %s", channel_name); - - ret = lttng_enable_channel(handle, &chan); - if (ret < 0) { - goto error; - } else { - MSG("%s channel %s enabled for session %s", - opt_kernel ? "Kernel" : "UST", channel_name, - session_name); - } - - /* Next event */ - channel_name = strtok(NULL, ","); - } - -error: - lttng_destroy_handle(handle); - - return ret; -} - -/* - * Default value for channel configuration. - */ -static void init_channel_config(void) -{ - /* - * Put -1 everywhere so we can identify those set by the command line and - * those needed to be set by the default values. - */ - memset(&chan.attr, -1, sizeof(chan.attr)); -} - -/* - * Add channel to trace session - */ -int cmd_enable_channels(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - char *session_name = NULL; - - init_channel_config(); - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_DISCARD: - chan.attr.overwrite = 0; - DBG("Channel set to discard"); - break; - case OPT_OVERWRITE: - chan.attr.overwrite = 1; - DBG("Channel set to overwrite"); - break; - case OPT_SUBBUF_SIZE: - chan.attr.subbuf_size = atol(poptGetOptArg(pc)); - DBG("Channel subbuf size set to %" PRIu64, chan.attr.subbuf_size); - break; - case OPT_NUM_SUBBUF: - chan.attr.num_subbuf = atoi(poptGetOptArg(pc)); - DBG("Channel subbuf num set to %" PRIu64, chan.attr.num_subbuf); - break; - case OPT_SWITCH_TIMER: - chan.attr.switch_timer_interval = atoi(poptGetOptArg(pc)); - DBG("Channel switch timer interval set to %d", chan.attr.switch_timer_interval); - break; - case OPT_READ_TIMER: - chan.attr.read_timer_interval = atoi(poptGetOptArg(pc)); - DBG("Channel read timer interval set to %d", chan.attr.read_timer_interval); - break; - case OPT_USERSPACE: - opt_userspace = 1; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_channels = (char*) poptGetArg(pc); - if (opt_channels == NULL) { - ERR("Missing channel name.\n"); - usage(stderr); - ret = CMD_SUCCESS; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = -1; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = enable_channel(session_name); - -end: - return ret; -} diff --git a/lttng/commands/enable_events.c b/lttng/commands/enable_events.c deleted file mode 100644 index 3c1c3916e..000000000 --- a/lttng/commands/enable_events.c +++ /dev/null @@ -1,463 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_event_list; -static int opt_event_type; -static int opt_kernel; -static char *opt_session_name; -static int opt_userspace; -static char *opt_cmd_name; -static int opt_enable_all; -static pid_t opt_pid; -static char *opt_probe; -static char *opt_function; -static char *opt_function_entry_symbol; -static char *opt_channel_name; - -enum { - OPT_HELP = 1, - OPT_TRACEPOINT, - OPT_PROBE, - OPT_FUNCTION, - OPT_FUNCTION_ENTRY, - OPT_SYSCALL, - OPT_USERSPACE, - OPT_TRACEPOINT_LOGLEVEL, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, - {"all", 'a', POPT_ARG_VAL, &opt_enable_all, 1, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, - {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, - {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, -#if 0 - /* - * Currently removed from lttng kernel tracer. Removed from - * lttng UI to discourage its use. - */ - {"function:entry", 0, POPT_ARG_STRING, &opt_function_entry_symbol, OPT_FUNCTION_ENTRY, 0, 0}, -#endif - {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, - {"loglevel", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT_LOGLEVEL, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng enable-event NAME[,NAME2,...] [options] [event_options]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -s, --session Apply on session name\n"); - fprintf(ofp, " -c, --channel Apply on this channel\n"); - fprintf(ofp, " -a, --all Enable all tracepoints\n"); - fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); - fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); - fprintf(ofp, " If no CMD, the domain used is UST global\n"); - fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); - fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Event options:\n"); - fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); - fprintf(ofp, " - userspace tracer supports wildcards at end of string.\n"); - fprintf(ofp, " Don't forget to quote to deal with bash expansion.\n"); - fprintf(ofp, " e.g.:\n"); - fprintf(ofp, " \"*\"\n"); - fprintf(ofp, " \"app_component:na*\"\n"); - fprintf(ofp, " --loglevel Tracepoint loglevel\n"); - fprintf(ofp, " --probe [addr | symbol | symbol+offset]\n"); - fprintf(ofp, " Dynamic probe.\n"); - fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); - fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); - fprintf(ofp, " --function [addr | symbol | symbol+offset]\n"); - fprintf(ofp, " Dynamic function entry/return probe.\n"); - fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); - fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); -#if 0 - fprintf(ofp, " --function:entry symbol\n"); - fprintf(ofp, " Function tracer event\n"); -#endif - fprintf(ofp, " --syscall System call event\n"); - fprintf(ofp, "\n"); -} - -/* - * Parse probe options. - */ -static int parse_probe_opts(struct lttng_event *ev, char *opt) -{ - int ret; - char s_hex[19]; - char name[LTTNG_SYMBOL_NAME_LEN]; - - if (opt == NULL) { - ret = -1; - goto end; - } - - /* Check for symbol+offset */ - ret = sscanf(opt, "%[^'+']+%s", name, s_hex); - if (ret == 2) { - strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); - ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - DBG("probe symbol %s", ev->attr.probe.symbol_name); - if (strlen(s_hex) == 0) { - ERR("Invalid probe offset %s", s_hex); - ret = -1; - goto end; - } - ev->attr.probe.offset = strtoul(s_hex, NULL, 0); - DBG("probe offset %" PRIu64, ev->attr.probe.offset); - ev->attr.probe.addr = 0; - goto end; - } - - /* Check for symbol */ - if (isalpha(name[0])) { - ret = sscanf(opt, "%s", name); - if (ret == 1) { - strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); - ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - DBG("probe symbol %s", ev->attr.probe.symbol_name); - ev->attr.probe.offset = 0; - DBG("probe offset %" PRIu64, ev->attr.probe.offset); - ev->attr.probe.addr = 0; - goto end; - } - } - - /* Check for address */ - ret = sscanf(opt, "%s", s_hex); - if (ret > 0) { - if (strlen(s_hex) == 0) { - ERR("Invalid probe address %s", s_hex); - ret = -1; - goto end; - } - ev->attr.probe.addr = strtoul(s_hex, NULL, 0); - DBG("probe addr %" PRIu64, ev->attr.probe.addr); - ev->attr.probe.offset = 0; - memset(ev->attr.probe.symbol_name, 0, LTTNG_SYMBOL_NAME_LEN); - goto end; - } - - /* No match */ - ret = -1; - -end: - return ret; -} - -/* - * Enabling event using the lttng API. - */ -static int enable_events(char *session_name) -{ - int err, ret = CMD_SUCCESS; - char *event_name, *channel_name = NULL; - struct lttng_event ev; - struct lttng_domain dom; - - if (opt_channel_name == NULL) { - err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME); - if (err < 0) { - ret = CMD_FATAL; - goto error; - } - } else { - channel_name = opt_channel_name; - } - - if (opt_kernel && opt_userspace) { - ERR("Can't use -k/--kernel and -u/--userspace together"); - ret = CMD_FATAL; - goto error; - } - - /* Create lttng domain */ - if (opt_kernel) { - dom.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_pid != 0) { - dom.type = LTTNG_DOMAIN_UST_PID; - dom.attr.pid = opt_pid; - DBG("PID %d set to lttng handle", opt_pid); - } else if (opt_userspace && opt_cmd_name == NULL) { - dom.type = LTTNG_DOMAIN_UST; - } else if (opt_userspace && opt_cmd_name != NULL) { - dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; - strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - handle = lttng_create_handle(session_name, &dom); - if (handle == NULL) { - ret = -1; - goto error; - } - - if (opt_enable_all) { - /* Default setup for enable all */ - - if (opt_kernel) { - ev.type = opt_event_type; - ev.name[0] = '\0'; - } else { - ev.type = LTTNG_EVENT_TRACEPOINT; - strcpy(ev.name, "*"); - } - - ret = lttng_enable_event(handle, &ev, channel_name); - if (ret < 0) { - goto error; - } - - switch (opt_event_type) { - case LTTNG_EVENT_TRACEPOINT: - MSG("All %s tracepoints are enabled in channel %s", - opt_kernel ? "kernel" : "UST", channel_name); - break; - case LTTNG_EVENT_SYSCALL: - if (opt_kernel) { - MSG("All kernel system calls are enabled in channel %s", - channel_name); - } - break; - case LTTNG_EVENT_ALL: - MSG("All %s events are enabled in channel %s", - opt_kernel ? "kernel" : "UST", channel_name); - break; - default: - /* - * We should not be here since lttng_enable_event should have - * failed on the event type. - */ - goto error; - } - goto end; - } - - /* Strip event list */ - event_name = strtok(opt_event_list, ","); - while (event_name != NULL) { - /* Copy name and type of the event */ - strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - ev.type = opt_event_type; - - /* Kernel tracer action */ - if (opt_kernel) { - DBG("Enabling kernel event %s for channel %s", - event_name, channel_name); - - switch (opt_event_type) { - case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ - ev.type = LTTNG_EVENT_TRACEPOINT; - /* Fall-through */ - case LTTNG_EVENT_TRACEPOINT: - break; - case LTTNG_EVENT_PROBE: - ret = parse_probe_opts(&ev, opt_probe); - if (ret < 0) { - ERR("Unable to parse probe options"); - ret = 0; - goto error; - } - break; - case LTTNG_EVENT_FUNCTION: - ret = parse_probe_opts(&ev, opt_function); - if (ret < 0) { - ERR("Unable to parse function probe options"); - ret = 0; - goto error; - } - break; - case LTTNG_EVENT_FUNCTION_ENTRY: - strncpy(ev.attr.ftrace.symbol_name, opt_function_entry_symbol, - LTTNG_SYMBOL_NAME_LEN); - ev.attr.ftrace.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_SYSCALL: - MSG("per-syscall selection not supported yet. Use \"-a\" " - "for all syscalls."); - default: - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - } else if (opt_userspace) { /* User-space tracer action */ - if (opt_cmd_name != NULL || opt_pid) { - MSG("Only supporting tracing all UST processes (-u) for now."); - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - - DBG("Enabling UST event %s for channel %s", event_name, - channel_name); - - switch (opt_event_type) { - case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ - /* Fall-through */ - case LTTNG_EVENT_TRACEPOINT: - /* Copy name and type of the event */ - ev.type = LTTNG_EVENT_TRACEPOINT; - strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: - /* Copy name and type of the event */ - ev.type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; - strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); - ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; - break; - case LTTNG_EVENT_PROBE: - case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_FUNCTION_ENTRY: - case LTTNG_EVENT_SYSCALL: - default: - ret = CMD_NOT_IMPLEMENTED; - goto error; - } - } else { - ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); - goto error; - } - - ret = lttng_enable_event(handle, &ev, channel_name); - if (ret == 0) { - MSG("%s event %s created in channel %s", - opt_kernel ? "kernel": "UST", event_name, channel_name); - } - - /* Next event */ - event_name = strtok(NULL, ","); - } - -end: -error: - if (opt_channel_name == NULL) { - free(channel_name); - } - lttng_destroy_handle(handle); - - return ret; -} - -/* - * Add event to trace session - */ -int cmd_enable_events(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - char *session_name = NULL; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - /* Default event type */ - opt_event_type = LTTNG_EVENT_ALL; - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - case OPT_TRACEPOINT: - opt_event_type = LTTNG_EVENT_TRACEPOINT; - break; - case OPT_PROBE: - opt_event_type = LTTNG_EVENT_PROBE; - break; - case OPT_FUNCTION: - opt_event_type = LTTNG_EVENT_FUNCTION; - break; - case OPT_FUNCTION_ENTRY: - opt_event_type = LTTNG_EVENT_FUNCTION_ENTRY; - break; - case OPT_SYSCALL: - opt_event_type = LTTNG_EVENT_SYSCALL; - break; - case OPT_USERSPACE: - opt_userspace = 1; - break; - case OPT_TRACEPOINT_LOGLEVEL: - opt_event_type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_event_list = (char*) poptGetArg(pc); - if (opt_event_list == NULL && opt_enable_all == 0) { - ERR("Missing event name(s).\n"); - usage(stderr); - ret = CMD_SUCCESS; - goto end; - } - - if (!opt_session_name) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = -1; - goto end; - } - } else { - session_name = opt_session_name; - } - - ret = enable_events(session_name); - -end: - if (opt_session_name == NULL) { - free(session_name); - } - - return ret; -} diff --git a/lttng/commands/list.c b/lttng/commands/list.c deleted file mode 100644 index 6f37254e4..000000000 --- a/lttng/commands/list.c +++ /dev/null @@ -1,658 +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 "../cmd.h" - -static int opt_pid; -static int opt_userspace; -static char *opt_cmd_name; -static int opt_kernel; -static char *opt_channel; -static int opt_domain; - -const char *indent4 = " "; -const char *indent6 = " "; -const char *indent8 = " "; - -enum { - OPT_HELP = 1, - OPT_USERSPACE, -}; - -static struct lttng_handle *handle; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, - {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, - {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, - {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, - {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng list [[-k] [-u] [-p PID] [SESSION []]]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "With no arguments, list available tracing session(s)\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "With -k alone, list available kernel events\n"); - fprintf(ofp, "With -u alone, list available userspace events\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -k, --kernel Select kernel domain\n"); - fprintf(ofp, " -u, --userspace Select user-space domain.\n"); - fprintf(ofp, " -p, --pid PID List user-space events by PID\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Options:\n"); - fprintf(ofp, " -c, --channel NAME List details of a channel\n"); - fprintf(ofp, " -d, --domain List available domain(s)\n"); - fprintf(ofp, "\n"); -} - -/* - * Get command line from /proc for a specific pid. - * - * On success, return an allocated string pointer to the proc cmdline. - * On error, return NULL. - */ -static char *get_cmdline_by_pid(pid_t pid) -{ - int ret; - FILE *fp; - char *cmdline = NULL; - char path[24]; /* Can't go bigger than /proc/65535/cmdline */ - - snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); - fp = fopen(path, "r"); - if (fp == NULL) { - goto end; - } - - /* Caller must free() *cmdline */ - cmdline = malloc(PATH_MAX); - ret = fread(cmdline, 1, PATH_MAX, fp); - if (ret < 0) { - perror("fread proc list"); - } - fclose(fp); - -end: - return cmdline; -} - -static -const char *active_string(int value) -{ - switch (value) { - case 0: return " [inactive]"; - case 1: return " [active]"; - case -1: return ""; - default: return NULL; - } -} - -static -const char *enabled_string(int value) -{ - switch (value) { - case 0: return " [disabled]"; - case 1: return " [enabled]"; - case -1: return ""; - default: return NULL; - } -} - -static -const char *loglevel_string_pre(const char *loglevel) -{ - if (loglevel[0] == '\0') { - return ""; - } else { - return " (loglevel: "; - } -} - -static -const char *loglevel_string_post(const char *loglevel) -{ - if (loglevel[0] == '\0') { - return ""; - } else { - return ")"; - } -} - -/* - * Pretty print single event. - */ -static void print_events(struct lttng_event *event) -{ - switch (event->type) { - case LTTNG_EVENT_TRACEPOINT: - { - char ll_value[LTTNG_SYMBOL_NAME_LEN] = ""; - - if (event->loglevel[0] != '\0') { - int ret; - - ret = snprintf(ll_value, LTTNG_SYMBOL_NAME_LEN, - " (%lld)", (long long) event->loglevel_value); - if (ret < 0) - ERR("snprintf error"); - } - MSG("%s%s%s%s%s%s (type: tracepoint)%s", indent6, - event->name, - loglevel_string_pre(event->loglevel), - event->loglevel, - ll_value, - loglevel_string_post(event->loglevel), - enabled_string(event->enabled)); - break; - } - case LTTNG_EVENT_PROBE: - MSG("%s%s (type: probe)%s", indent6, - event->name, enabled_string(event->enabled)); - if (event->attr.probe.addr != 0) { - MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); - } else { - MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); - MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); - } - break; - case LTTNG_EVENT_FUNCTION: - case LTTNG_EVENT_FUNCTION_ENTRY: - MSG("%s%s (type: function)%s", indent6, - event->name, enabled_string(event->enabled)); - MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name); - break; - case LTTNG_EVENT_SYSCALL: - MSG("%s (type: syscall)%s", indent6, - enabled_string(event->enabled)); - break; - case LTTNG_EVENT_NOOP: - MSG("%s (type: noop)%s", indent6, - enabled_string(event->enabled)); - break; - case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: - MSG("%s%s (type: tracepoint loglevel)%s", indent6, - event->name, - enabled_string(event->enabled)); - break; - case LTTNG_EVENT_ALL: - /* We should never have "all" events in list. */ - assert(0); - break; - } -} - -/* - * Ask session daemon for all user space tracepoints available. - */ -static int list_ust_events(void) -{ - int i, size; - struct lttng_domain domain; - struct lttng_handle *handle; - struct lttng_event *event_list; - pid_t cur_pid = 0; - - DBG("Getting UST tracing events"); - - domain.type = LTTNG_DOMAIN_UST; - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - goto error; - } - - size = lttng_list_tracepoints(handle, &event_list); - if (size < 0) { - ERR("Unable to list UST events"); - return size; - } - - MSG("UST events:\n-------------"); - - if (size == 0) { - MSG("None"); - } - - for (i = 0; i < size; i++) { - if (cur_pid != event_list[i].pid) { - cur_pid = event_list[i].pid; - MSG("\nPID: %d - Name: %s", cur_pid, get_cmdline_by_pid(cur_pid)); - } - print_events(&event_list[i]); - } - - MSG(""); - - free(event_list); - - return CMD_SUCCESS; - -error: - return -1; -} - -/* - * Ask for all trace events in the kernel and pretty print them. - */ -static int list_kernel_events(void) -{ - int i, size; - struct lttng_domain domain; - struct lttng_handle *handle; - struct lttng_event *event_list; - - DBG("Getting kernel tracing events"); - - domain.type = LTTNG_DOMAIN_KERNEL; - - handle = lttng_create_handle(NULL, &domain); - if (handle == NULL) { - goto error; - } - - size = lttng_list_tracepoints(handle, &event_list); - if (size < 0) { - ERR("Unable to list kernel events"); - return size; - } - - MSG("Kernel events:\n-------------"); - - for (i = 0; i < size; i++) { - print_events(&event_list[i]); - } - - MSG(""); - - free(event_list); - - return CMD_SUCCESS; - -error: - return -1; -} - -/* - * List events of channel of session and domain. - */ -static int list_events(const char *channel_name) -{ - int ret, count, i; - struct lttng_event *events = NULL; - - count = lttng_list_events(handle, channel_name, &events); - if (count < 0) { - ret = count; - goto error; - } - - MSG("\n%sEvents:", indent4); - if (count == 0) { - MSG("%sNone\n", indent6); - goto end; - } - - for (i = 0; i < count; i++) { - print_events(&events[i]); - } - - MSG(""); - -end: - if (events) { - free(events); - } - ret = CMD_SUCCESS; - -error: - return ret; -} - -/* - * Pretty print channel - */ -static void print_channel(struct lttng_channel *channel) -{ - MSG("- %s:%s\n", channel->name, enabled_string(channel->enabled)); - - MSG("%sAttributes:", indent4); - MSG("%soverwrite mode: %d", indent6, channel->attr.overwrite); - MSG("%ssubbufers size: %" PRIu64, indent6, channel->attr.subbuf_size); - MSG("%snumber of subbufers: %" PRIu64, indent6, channel->attr.num_subbuf); - MSG("%sswitch timer interval: %u", indent6, channel->attr.switch_timer_interval); - MSG("%sread timer interval: %u", indent6, channel->attr.read_timer_interval); - switch (channel->attr.output) { - case LTTNG_EVENT_SPLICE: - MSG("%soutput: splice()", indent6); - break; - case LTTNG_EVENT_MMAP: - MSG("%soutput: mmap()", indent6); - break; - } -} - -/* - * List channel(s) of session and domain. - * - * If channel_name is NULL, all channels are listed. - */ -static int list_channels(const char *channel_name) -{ - int count, i, ret = CMD_SUCCESS; - unsigned int chan_found = 0; - struct lttng_channel *channels = NULL; - - DBG("Listing channel(s) (%s)", channel_name ? : ""); - - count = lttng_list_channels(handle, &channels); - if (count < 0) { - ret = count; - goto error; - } else if (count == 0) { - MSG("No channel found"); - goto end; - } - - if (channel_name == NULL) { - MSG("Channels:\n-------------"); - } - - for (i = 0; i < count; i++) { - if (channel_name != NULL) { - if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { - chan_found = 1; - } else { - continue; - } - } - print_channel(&channels[i]); - - /* Listing events per channel */ - ret = list_events(channels[i].name); - if (ret < 0) { - MSG("%s", lttng_strerror(ret)); - } - - if (chan_found) { - break; - } - } - - if (!chan_found && channel_name != NULL) { - MSG("Channel %s not found", channel_name); - } - -end: - free(channels); - ret = CMD_SUCCESS; - -error: - return ret; -} - -/* - * List available tracing session. List only basic information. - * - * If session_name is NULL, all sessions are listed. - */ -static int list_sessions(const char *session_name) -{ - int ret, count, i; - unsigned int session_found = 0; - struct lttng_session *sessions; - - count = lttng_list_sessions(&sessions); - DBG("Session count %d", count); - if (count < 0) { - ret = count; - goto error; - } - - if (session_name == NULL) { - MSG("Available tracing sessions:"); - } - - for (i = 0; i < count; i++) { - if (session_name != NULL) { - if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { - session_found = 1; - MSG("Tracing session %s:%s", session_name, active_string(sessions[i].enabled)); - MSG("%sTrace path: %s\n", indent4, sessions[i].path); - break; - } - continue; - } - - MSG(" %d) %s (%s)%s", i + 1, sessions[i].name, sessions[i].path, active_string(sessions[i].enabled)); - - if (session_found) { - break; - } - } - - free(sessions); - - if (!session_found && session_name != NULL) { - MSG("Session %s not found", session_name); - } - - if (session_name == NULL) { - MSG("\nUse lttng list for more details"); - } - - return CMD_SUCCESS; - -error: - return ret; -} - -/* - * List available domain(s) for a session. - */ -static int list_domains(const char *session_name) -{ - int i, count, ret = CMD_SUCCESS; - struct lttng_domain *domains = NULL; - - MSG("Domains:\n-------------"); - - count = lttng_list_domains(session_name, &domains); - if (count < 0) { - ret = count; - goto error; - } else if (count == 0) { - MSG(" None"); - goto end; - } - - for (i = 0; i < count; i++) { - switch (domains[i].type) { - case LTTNG_DOMAIN_KERNEL: - MSG(" - Kernel"); - break; - case LTTNG_DOMAIN_UST: - MSG(" - UST global"); - break; - default: - break; - } - } - -end: - free(domains); - -error: - return ret; -} - -/* - * The 'list ' first level command - */ -int cmd_list(int argc, const char **argv) -{ - int opt, i, ret = CMD_SUCCESS; - int nb_domain; - const char *session_name; - static poptContext pc; - struct lttng_domain domain; - struct lttng_domain *domains = NULL; - - if (argc < 1) { - usage(stderr); - goto end; - } - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - goto end; - case OPT_USERSPACE: - opt_userspace = 1; - break; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - if (opt_pid != 0) { - MSG("*** Userspace tracing not implemented for PID ***\n"); - } - - /* Get session name (trailing argument) */ - session_name = poptGetArg(pc); - DBG2("Session name: %s", session_name); - - if (opt_kernel) { - domain.type = LTTNG_DOMAIN_KERNEL; - } else if (opt_userspace) { - DBG2("Listing userspace global domain"); - domain.type = LTTNG_DOMAIN_UST; - } - - handle = lttng_create_handle(session_name, &domain); - if (handle == NULL) { - goto end; - } - - if (session_name == NULL) { - if (!opt_kernel && !opt_userspace) { - ret = list_sessions(NULL); - if (ret < 0) { - goto end; - } - } - if (opt_kernel) { - ret = list_kernel_events(); - if (ret < 0) { - goto end; - } - } - if (opt_userspace) { - ret = list_ust_events(); - if (ret < 0) { - goto end; - } - } - } else { - /* List session attributes */ - ret = list_sessions(session_name); - if (ret < 0) { - goto end; - } - - /* Domain listing */ - if (opt_domain) { - ret = list_domains(session_name); - goto end; - } - - if (opt_kernel) { - /* Channel listing */ - ret = list_channels(opt_channel); - if (ret < 0) { - goto end; - } - } else { - /* We want all domain(s) */ - nb_domain = lttng_list_domains(session_name, &domains); - if (nb_domain < 0) { - ret = nb_domain; - goto end; - } - - for (i = 0; i < nb_domain; i++) { - switch (domains[i].type) { - case LTTNG_DOMAIN_KERNEL: - MSG("=== Domain: Kernel ===\n"); - break; - case LTTNG_DOMAIN_UST: - MSG("=== Domain: UST global ===\n"); - break; - default: - MSG("=== Domain: Unimplemented ===\n"); - break; - } - - /* Clean handle before creating a new one */ - lttng_destroy_handle(handle); - - handle = lttng_create_handle(session_name, &domains[i]); - if (handle == NULL) { - goto end; - } - - ret = list_channels(opt_channel); - if (ret < 0) { - goto end; - } - } - } - } - -end: - if (domains) { - free(domains); - } - lttng_destroy_handle(handle); - - return ret; -} diff --git a/lttng/commands/set_session.c b/lttng/commands/set_session.c deleted file mode 100644 index 8a76403ad..000000000 --- a/lttng/commands/set_session.c +++ /dev/null @@ -1,112 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_session_name; - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng set-session NAME\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Options:\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, "\n"); -} - -/* - * set_session - */ -static int set_session(void) -{ - int ret = CMD_SUCCESS; - - ret = config_init(opt_session_name); - if (ret < 0) { - ERR("Unable to set session name"); - ret = CMD_ERROR; - goto error; - } - - MSG("Session set to %s", opt_session_name); - ret = CMD_SUCCESS; - -error: - return ret; -} - -/* - * cmd_set_session - */ -int cmd_set_session(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char *) poptGetArg(pc); - if (opt_session_name == NULL) { - ERR("Missing session name"); - usage(stderr); - goto end; - } - - ret = set_session(); - -end: - return ret; -} diff --git a/lttng/commands/start.c b/lttng/commands/start.c deleted file mode 100644 index 2eacc462f..000000000 --- a/lttng/commands/start.c +++ /dev/null @@ -1,127 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_session_name; - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng start [options] [NAME]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); - fprintf(ofp, "get it from the configuration directory (.lttng).\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, "\n"); -} - -/* - * start_tracing - * - * Start tracing for all trace of the session. - */ -static int start_tracing(void) -{ - int ret = CMD_SUCCESS; - char *session_name; - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - DBG("Starting tracing for session %s", session_name); - - ret = lttng_start_tracing(session_name); - if (ret < 0) { - goto free_name; - } - - MSG("Tracing started for session %s", session_name); - -free_name: - if (opt_session_name == NULL) { - free(session_name); - } -error: - return ret; -} - -/* - * cmd_start - * - * The 'start ' first level command - */ -int cmd_start(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - ret = start_tracing(); - -end: - return ret; -} diff --git a/lttng/commands/stop.c b/lttng/commands/stop.c deleted file mode 100644 index f1299b5bc..000000000 --- a/lttng/commands/stop.c +++ /dev/null @@ -1,124 +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 "../cmd.h" -#include "../conf.h" -#include "../utils.h" - -static char *opt_session_name; - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng stop [options] [NAME]\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); - fprintf(ofp, "get it from the configuration directory (.lttng).\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, "\n"); -} - -/* - * Start tracing for all trace of the session. - */ -static int stop_tracing(void) -{ - int ret = CMD_SUCCESS; - char *session_name; - - if (opt_session_name == NULL) { - session_name = get_session_name(); - if (session_name == NULL) { - ret = CMD_ERROR; - goto error; - } - } else { - session_name = opt_session_name; - } - - ret = lttng_stop_tracing(session_name); - if (ret < 0) { - goto free_name; - } - - MSG("Tracing stopped for session %s", session_name); - -free_name: - if (opt_session_name == NULL) { - free(session_name); - } - -error: - return ret; -} - -/* - * cmd_stop - * - * The 'stop ' first level command - */ -int cmd_stop(int argc, const char **argv) -{ - int opt, ret; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - opt_session_name = (char*) poptGetArg(pc); - - ret = stop_tracing(); - -end: - return ret; -} diff --git a/lttng/commands/version.c b/lttng/commands/version.c deleted file mode 100644 index 4d8d75b12..000000000 --- a/lttng/commands/version.c +++ /dev/null @@ -1,82 +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 "../cmd.h" - -enum { - OPT_HELP = 1, -}; - -static struct poptOption long_options[] = { - /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ - {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, - {0, 0, 0, 0, 0, 0, 0} -}; - -/* - * usage - */ -static void usage(FILE *ofp) -{ - fprintf(ofp, "usage: lttng version\n"); - fprintf(ofp, "\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, "\n"); -} - -/* - * cmd_version - */ -int cmd_version(int argc, const char **argv) -{ - int opt, ret = CMD_SUCCESS; - static poptContext pc; - - pc = poptGetContext(NULL, argc, argv, long_options, 0); - poptReadDefaultConfig(pc, 0); - - while ((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_HELP: - usage(stderr); - ret = CMD_SUCCESS; - goto end; - default: - usage(stderr); - ret = CMD_UNDEFINED; - goto end; - } - } - - MSG("lttng version " VERSION); - MSG("Web site: http://lttng.org/"); - MSG("\nlttng is free software and under the GPL license."); - -end: - return ret; -} diff --git a/lttng/conf.c b/lttng/conf.c deleted file mode 100644 index 7337d92ca..000000000 --- a/lttng/conf.c +++ /dev/null @@ -1,253 +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 - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include "conf.h" -#include "lttngerr.h" - -/* - * config_get_file_path - * - * Return the path with '/CONFIG_FILENAME' added to it. - */ -char *config_get_file_path(char *path) -{ - int ret; - char *file_path; - - ret = asprintf(&file_path, "%s/%s", path, CONFIG_FILENAME); - if (ret < 0) { - ERR("Fail allocating config file path"); - } - - return file_path; -} - -/* - * open_config - * - * Return an open FILE pointer to the config file. - */ -static FILE *open_config(char *path, const char *mode) -{ - FILE *fp = NULL; - char *file_path; - - file_path = config_get_file_path(path); - if (file_path == NULL) { - goto error; - } - - fp = fopen(file_path, mode); - if (fp == NULL) { - goto error; - } - -error: - if (file_path) { - free(file_path); - } - return fp; -} - -/* - * create_config_file - * - * Create the empty config file a the path. - */ -static int create_config_file(char *path) -{ - int ret; - FILE *fp; - - fp = open_config(path, "w+"); - if (fp == NULL) { - ERR("Unable to create config file"); - ret = -1; - goto error; - } - - ret = fclose(fp); - -error: - return ret; -} - -/* - * write_config - * - * Append data to the config file in file_path - */ -static int write_config(char *file_path, size_t size, char *data) -{ - FILE *fp; - size_t len; - int ret = 0; - - fp = open_config(file_path, "a"); - if (fp == NULL) { - ret = -1; - goto end; - } - - /* Write session name into config file */ - len = fwrite(data, size, 1, fp); - if (len < 1) { - ret = -1; - } - fclose(fp); -end: - return ret; -} - -/* - * config_get_default_path - * - * Return the HOME directory path. Caller MUST NOT free(3) the return pointer. - */ -char *config_get_default_path(void) -{ - return getenv("HOME"); -} - -/* - * config_destroy - * - * Destroy directory config and file config. - */ -void config_destroy(char *path) -{ - int ret; - char *config_path; - - config_path = config_get_file_path(path); - if (config_path == NULL) { - return; - } - - ret = remove(config_path); - if (ret < 0) { - perror("remove config file"); - } - - free(config_path); -} - -/* - * config_read_session_name - * - * Return sesson name from the config file. - */ -char *config_read_session_name(char *path) -{ - int ret; - FILE *fp; - char var[NAME_MAX], *session_name; - - fp = open_config(path, "r"); - if (fp == NULL) { - ERR("Can't find valid lttng config %s/.lttngrc", path); - MSG("Did you create a session? (lttng create )"); - goto error; - } - - session_name = malloc(NAME_MAX); - while (!feof(fp)) { - if ((ret = fscanf(fp, "%[^'=']=%s\n", var, session_name)) != 2) { - if (ret == -1) { - ERR("Missing session=NAME in config file."); - goto error; - } - continue; - } - - if (strcmp(var, "session") == 0) { - goto found; - } - } - - fclose(fp); - -error: - return NULL; - -found: - fclose(fp); - return session_name; - -} - -/* - * config_add_session_name - * - * Write session name option to the config file. - */ -int config_add_session_name(char *path, char *name) -{ - int ret; - char session_name[NAME_MAX]; - - ret = snprintf(session_name, NAME_MAX, "session=%s\n", name); - if (ret < 0) { - goto error; - } - ret = write_config(path, ret, session_name); -error: - return ret; -} - -/* - * config_init - * - * Init configuration directory and file. - */ -int config_init(char *session_name) -{ - int ret; - char *path; - - path = config_get_default_path(); - if (path == NULL) { - ret = -1; - goto error; - } - - /* Create default config file */ - ret = create_config_file(path); - if (ret < 0) { - goto error; - } - - ret = config_add_session_name(path, session_name); - if (ret < 0) { - goto error; - } - - DBG("Init config session in %s", path); - -error: - return ret; -} diff --git a/lttng/conf.h b/lttng/conf.h deleted file mode 100644 index a7672e413..000000000 --- a/lttng/conf.h +++ /dev/null @@ -1,33 +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 _LTTNG_CONFIG_H -#define _LTTNG_CONFIG_H - -#define CONFIG_FILENAME ".lttngrc" - -void config_destroy(char *path); -int config_init(char *path); -int config_add_session_name(char *path, char *name); -char *config_get_default_path(void); - -/* Must free() the return pointer */ -char *config_read_session_name(char *path); -char *config_get_file_path(char *path); - -#endif /* _LTTNG_CONFIG_H */ diff --git a/lttng/lttng.c b/lttng/lttng.c deleted file mode 100644 index 69b563d4a..000000000 --- a/lttng/lttng.c +++ /dev/null @@ -1,491 +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 - * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "cmd.h" -#include "conf.h" -#include "lttngerr.h" - -/* Variables */ -static char *progname; - -int opt_quiet; -int opt_verbose; -static int opt_no_sessiond; -static char *opt_sessiond_path; - -enum { - OPT_SESSION_PATH, - OPT_DUMP_OPTIONS, - OPT_DUMP_COMMANDS, -}; - -/* Getopt options. No first level command. */ -static struct option long_options[] = { - {"help", 0, NULL, 'h'}, - {"group", 1, NULL, 'g'}, - {"verbose", 0, NULL, 'v'}, - {"quiet", 0, NULL, 'q'}, - {"no-sessiond", 0, NULL, 'n'}, - {"sessiond-path", 1, NULL, OPT_SESSION_PATH}, - {"list-options", 0, NULL, OPT_DUMP_OPTIONS}, - {"list-commands", 0, NULL, OPT_DUMP_COMMANDS}, - {NULL, 0, NULL, 0} -}; - -/* First level command */ -static struct cmd_struct commands[] = { - { "list", cmd_list}, - { "create", cmd_create}, - { "destroy", cmd_destroy}, - { "start", cmd_start}, - { "stop", cmd_stop}, - { "enable-event", cmd_enable_events}, - { "disable-event", cmd_disable_events}, - { "enable-channel", cmd_enable_channels}, - { "disable-channel", cmd_disable_channels}, - { "add-context", cmd_add_context}, - { "set-session", cmd_set_session}, - { "version", cmd_version}, - { "calibrate", cmd_calibrate}, - { NULL, NULL} /* Array closure */ -}; - -static void usage(FILE *ofp) -{ - fprintf(ofp, "LTTng Trace Control " VERSION"\n\n"); - fprintf(ofp, "usage: lttng [options] \n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Options:\n"); - fprintf(ofp, " -h, --help Show this help\n"); - fprintf(ofp, " -g, --group NAME Unix tracing group name. (default: tracing)\n"); - fprintf(ofp, " -v, --verbose Verbose mode\n"); - fprintf(ofp, " -q, --quiet Quiet mode\n"); - fprintf(ofp, " -n, --no-sessiond Don't spawn a session daemon\n"); - fprintf(ofp, " --sessiond-path Session daemon full path\n"); - fprintf(ofp, " --list-options Simple listing of lttng options\n"); - fprintf(ofp, " --list-commands Simple listing of lttng commands\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Commands:\n"); - fprintf(ofp, " add-context Add context to event or/and channel\n"); - fprintf(ofp, " calibrate Quantify LTTng overhead\n"); - fprintf(ofp, " create Create tracing session\n"); - fprintf(ofp, " destroy Teardown tracing session\n"); - fprintf(ofp, " enable-channel Enable tracing channel\n"); - fprintf(ofp, " enable-event Enable tracing event\n"); - fprintf(ofp, " disable-channel Disable tracing channel\n"); - fprintf(ofp, " disable-event Disable tracing event\n"); - fprintf(ofp, " list List possible tracing options\n"); - fprintf(ofp, " set-session Set current session name\n"); - fprintf(ofp, " start Start tracing\n"); - fprintf(ofp, " stop Stop tracing\n"); - fprintf(ofp, " version Show version information\n"); - fprintf(ofp, "\n"); - fprintf(ofp, "Please see the lttng(1) man page for full documentation.\n"); - fprintf(ofp, "See http://lttng.org for updates, bug reports and news.\n"); -} - -/* - * list_options - * - * List options line by line. This is mostly for bash auto completion and to - * avoid difficult parsing. - */ -static void list_options(FILE *ofp) -{ - int i = 0; - struct option *option = NULL; - - option = &long_options[i]; - while (option->name != NULL) { - fprintf(ofp, "--%s\n", option->name); - - if (isprint(option->val)) { - fprintf(ofp, "-%c\n", option->val); - } - - i++; - option = &long_options[i]; - } -} - -/* - * list_commands - * - * List commands line by line. This is mostly for bash auto completion and to - * avoid difficult parsing. - */ -static void list_commands(FILE *ofp) -{ - int i = 0; - struct cmd_struct *cmd = NULL; - - cmd = &commands[i]; - while (cmd->name != NULL) { - fprintf(ofp, "%s\n", cmd->name); - i++; - cmd = &commands[i]; - } -} - -/* - * clean_exit - */ -static void clean_exit(int code) -{ - DBG("Clean exit"); - exit(code); -} - -/* - * sighandler - * - * Signal handler for the daemon - */ -static void sighandler(int sig) -{ - switch (sig) { - case SIGTERM: - DBG("SIGTERM catched"); - clean_exit(EXIT_FAILURE); - break; - case SIGCHLD: - /* Notify is done */ - DBG("SIGCHLD catched"); - break; - default: - DBG("Unknown signal %d catched", sig); - break; - } - - return; -} - -/* - * set_signal_handler - * - * Setup signal handler for SIGCHLD and SIGTERM. - */ -static int set_signal_handler(void) -{ - int ret = 0; - struct sigaction sa; - sigset_t sigset; - - if ((ret = sigemptyset(&sigset)) < 0) { - perror("sigemptyset"); - goto end; - } - - sa.sa_handler = sighandler; - sa.sa_mask = sigset; - sa.sa_flags = 0; - if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) { - perror("sigaction"); - goto end; - } - - if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { - perror("sigaction"); - goto end; - } - -end: - return ret; -} - -/* - * handle_command - * - * Handle the full argv list of a first level command. Will find the command - * in the global commands array and call the function callback associated. - * - * If command not found, return -1 - * else, return function command error code. - */ -static int handle_command(int argc, char **argv) -{ - int i = 0, ret; - struct cmd_struct *cmd; - - if (*argv == NULL) { - ret = CMD_SUCCESS; - goto end; - } - - cmd = &commands[i]; - while (cmd->func != NULL) { - /* Find command */ - if (strcmp(argv[0], cmd->name) == 0) { - ret = cmd->func(argc, (const char**) argv); - switch (ret) { - case CMD_ERROR: - ERR("Command error"); - break; - case CMD_NOT_IMPLEMENTED: - ERR("Options not implemented"); - break; - case CMD_UNDEFINED: - ERR("Undefined command"); - break; - case CMD_FATAL: - ERR("Fatal error"); - break; - } - goto end; - } - i++; - cmd = &commands[i]; - } - - /* Command not found */ - ret = -1; - -end: - return ret; -} - -/* - * spawn_sessiond - * - * Spawn a session daemon by forking and execv. - */ -static int spawn_sessiond(char *pathname) -{ - int ret = 0; - pid_t pid; - - MSG("Spawning a session daemon"); - pid = fork(); - if (pid == 0) { - /* - * Spawn session daemon and tell - * it to signal us when ready. - */ - 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."); - } else { - perror("execlp"); - } - kill(getppid(), SIGTERM); /* unpause parent */ - exit(EXIT_FAILURE); - } else if (pid > 0) { - /* Wait for lttng-sessiond to start */ - pause(); - goto end; - } else { - perror("fork"); - ret = -1; - goto end; - } - -end: - return ret; -} - -/* - * check_sessiond - * - * Check if the session daemon is available using - * the liblttngctl API for the check. If not, try to - * spawn a daemon. - */ -static int check_sessiond(void) -{ - int ret; - char *pathname = NULL, *alloc_pathname = NULL; - - ret = lttng_session_daemon_alive(); - if (ret == 0) { /* not alive */ - /* Try command line option path */ - if (opt_sessiond_path != NULL) { - ret = access(opt_sessiond_path, F_OK | X_OK); - if (ret < 0) { - ERR("No such file: %s", opt_sessiond_path); - goto end; - } - pathname = opt_sessiond_path; - } else { - /* Try LTTNG_SESSIOND_PATH env variable */ - pathname = getenv(LTTNG_SESSIOND_PATH_ENV); - } - - /* Let's rock and roll */ - if (pathname == NULL) { - ret = asprintf(&alloc_pathname, INSTALL_BIN_PATH "/lttng-sessiond"); - if (ret < 0) { - perror("asprintf spawn sessiond"); - goto end; - } - pathname = alloc_pathname; - } - - ret = spawn_sessiond(pathname); - free(alloc_pathname); - if (ret < 0) { - ERR("Problem occurs when starting %s", pathname); - goto end; - } - } - -end: - return ret; -} - -/* - * Check for the "help" option in the argv. If found, return 1 else return 0. - */ -static int check_help_command(int argc, char **argv) -{ - int i; - - for (i = 0; i < argc; i++) { - if ((strncmp(argv[i], "-h", 2) == 0) || - strncmp(argv[i], "--h", 3) == 0) { - return 1; - } - } - - return 0; -} - -/* - * parse_args - * - * Parse command line arguments. - * Return 0 if OK, else -1 - */ -static int parse_args(int argc, char **argv) -{ - int opt, ret; - - if (argc < 2) { - usage(stderr); - clean_exit(EXIT_FAILURE); - } - - while ((opt = getopt_long(argc, argv, "+hnvqg:", long_options, NULL)) != -1) { - switch (opt) { - case 'h': - usage(stderr); - goto error; - case 'v': - opt_verbose += 1; - break; - case 'q': - opt_quiet = 1; - break; - case 'g': - lttng_set_tracing_group(optarg); - break; - case 'n': - opt_no_sessiond = 1; - break; - case OPT_SESSION_PATH: - opt_sessiond_path = strdup(optarg); - break; - case OPT_DUMP_OPTIONS: - list_options(stdout); - ret = 0; - goto error; - case OPT_DUMP_COMMANDS: - list_commands(stdout); - ret = 0; - goto error; - default: - usage(stderr); - goto error; - } - } - - /* If both options are specified, quiet wins */ - if (opt_verbose && opt_quiet) { - opt_verbose = 0; - } - - /* Spawn session daemon if needed */ - if (opt_no_sessiond == 0 && check_help_command(argc, argv) == 0 && - (check_sessiond() < 0)) { - goto error; - } - - /* No leftovers, print usage and quit */ - if ((argc - optind) == 0) { - usage(stderr); - goto error; - } - - /* - * Handle leftovers which is a first level command with the trailing - * options. - */ - ret = handle_command(argc - optind, argv + optind); - if (ret < 0) { - if (ret == -1) { - usage(stderr); - } else { - ERR("%s", lttng_strerror(ret)); - } - goto error; - } - - return 0; - -error: - return -1; -} - - -/* - * main - */ -int main(int argc, char *argv[]) -{ - int ret; - - progname = argv[0] ? argv[0] : "lttng"; - - /* For Mathieu Desnoyers aka Dr Tracing */ - if (strncmp(progname, "drtrace", 7) == 0) { - MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0); - } - - ret = set_signal_handler(); - if (ret < 0) { - clean_exit(ret); - } - - ret = parse_args(argc, argv); - if (ret < 0) { - clean_exit(EXIT_FAILURE); - } - - return 0; -} diff --git a/lttng/utils.c b/lttng/utils.c deleted file mode 100644 index db7fd38fe..000000000 --- a/lttng/utils.c +++ /dev/null @@ -1,51 +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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - -#include - -#include "conf.h" -#include "lttngerr.h" - -/* - * get_session_name - * - * Return allocated string with the session name found in the config - * directory. - */ -char *get_session_name(void) -{ - char *path, *session_name = NULL; - - /* Get path to config file */ - path = config_get_default_path(); - if (path == NULL) { - goto error; - } - - /* Get session name from config */ - session_name = config_read_session_name(path); - if (session_name == NULL) { - goto error; - } - -error: - DBG("Session name found: %s", session_name); - return session_name; -} diff --git a/lttng/utils.h b/lttng/utils.h deleted file mode 100644 index 5492d5e67..000000000 --- a/lttng/utils.h +++ /dev/null @@ -1,26 +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 _LTTNG_UTILS_H -#define _LTTNG_UTILS_H - -char *get_config_file_path(void); -char *get_session_name(void); -int set_session_name(char *name); - -#endif /* _LTTNG_UTILS_H */ diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..103337c45 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common lib bin diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am new file mode 100644 index 000000000..07b694918 --- /dev/null +++ b/src/bin/Makefile.am @@ -0,0 +1,8 @@ +ACLOCAL_AMFLAGS = -I config + +SUBDIRS = lttng-consumerd + +if ! BUILD_CONSUMERD_ONLY +SUBDIRS += lttng \ + lttng-sessiond +endif diff --git a/src/bin/lttng-consumerd/Makefile.am b/src/bin/lttng-consumerd/Makefile.am new file mode 100644 index 000000000..25cd6e922 --- /dev/null +++ b/src/bin/lttng-consumerd/Makefile.am @@ -0,0 +1,15 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src + +bin_PROGRAMS = lttng-consumerd + +lttng_consumerd_SOURCES = lttng-consumerd.c + +lttng_consumerd_LDADD = \ + $(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la \ + $(top_builddir)/src/common/libconsumer.la \ + $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ + $(top_builddir)/src/common/libcommon.la + +if HAVE_LIBLTTNG_UST_CTL +lttng_consumerd_LDADD += -llttng-ust-ctl +endif diff --git a/src/bin/lttng-consumerd/lttng-consumerd.c b/src/bin/lttng-consumerd/lttng-consumerd.c new file mode 100644 index 000000000..8cb264ab1 --- /dev/null +++ b/src/bin/lttng-consumerd/lttng-consumerd.c @@ -0,0 +1,352 @@ +/* + * 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. + */ + +#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 +#include + +#include "lttng-consumerd.h" + +/* TODO : support UST (all direct kernctl accesses). */ + +/* the two threads (receive fd and poll) */ +static pthread_t threads[2]; + +/* to count the number of time the user pressed ctrl+c */ +static int sigintcount = 0; + +/* Argument variables */ +int opt_quiet; +int opt_verbose; +static int opt_daemon; +static const char *progname; +static char command_sock_path[PATH_MAX]; /* Global command socket path */ +static char error_sock_path[PATH_MAX]; /* Global error path */ +static enum lttng_consumer_type opt_type = LTTNG_CONSUMER_KERNEL; + +/* the liblttngconsumerd context */ +static struct lttng_consumer_local_data *ctx; + +/* + * Signal handler for the daemon + */ +static void sighandler(int sig) +{ + if (sig == SIGINT && sigintcount++ == 0) { + DBG("ignoring first SIGINT"); + return; + } + + lttng_consumer_should_exit(ctx); +} + +/* + * 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; + } + + return ret; +} + +/* + * 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, --consumerd-cmd-sock PATH " + "Specify path for the command socket\n"); + fprintf(stderr, " -e, --consumerd-err-sock PATH " + "Specify path for the error socket\n"); + fprintf(stderr, " -d, --daemonize " + "Start as a daemon.\n"); + fprintf(stderr, " -q, --quiet " + "No output at all.\n"); + fprintf(stderr, " -v, --verbose " + "Verbose mode. Activate DBG() macro.\n"); + fprintf(stderr, " -V, --version " + "Show version number.\n"); + fprintf(stderr, " -k, --kernel " + "Consumer kernel buffers (default).\n"); + fprintf(stderr, " -u, --ust " + "Consumer UST buffers.%s\n", +#ifdef HAVE_LIBLTTNG_UST_CTL + "" +#else + " (support not compiled in)" +#endif + ); +} + +/* + * daemon argument parsing + */ +static void parse_args(int argc, char **argv) +{ + int c; + + static struct option long_options[] = { + { "consumerd-cmd-sock", 1, 0, 'c' }, + { "consumerd-err-sock", 1, 0, 'e' }, + { "daemonize", 0, 0, 'd' }, + { "help", 0, 0, 'h' }, + { "quiet", 0, 0, 'q' }, + { "verbose", 0, 0, 'v' }, + { "version", 0, 0, 'V' }, + { "kernel", 0, 0, 'k' }, +#ifdef HAVE_LIBLTTNG_UST_CTL + { "ust", 0, 0, 'u' }, +#endif + { NULL, 0, 0, 0 } + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "dhqvVku" "c:e:", 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(command_sock_path, PATH_MAX, "%s", optarg); + break; + case 'e': + snprintf(error_sock_path, PATH_MAX, "%s", optarg); + break; + case 'd': + opt_daemon = 1; + break; + case 'h': + usage(); + exit(EXIT_FAILURE); + case 'q': + opt_quiet = 1; + break; + case 'v': + opt_verbose = 1; + break; + case 'V': + fprintf(stdout, "%s\n", VERSION); + exit(EXIT_SUCCESS); + case 'k': + opt_type = LTTNG_CONSUMER_KERNEL; + break; +#ifdef HAVE_LIBLTTNG_UST_CTL + case 'u': +# if (CAA_BITS_PER_LONG == 64) + opt_type = LTTNG_CONSUMER64_UST; +# elif (CAA_BITS_PER_LONG == 32) + opt_type = LTTNG_CONSUMER32_UST; +# else +# error "Unknown bitness" +# endif + break; +#endif + default: + usage(); + exit(EXIT_FAILURE); + } + } +} + +/* + * main + */ +int main(int argc, char **argv) +{ + int i; + int ret = 0; + void *status; + + /* Parse arguments */ + progname = argv[0]; + parse_args(argc, argv); + + /* Daemonize */ + if (opt_daemon) { + ret = daemon(0, 0); + if (ret < 0) { + perror("daemon"); + goto error; + } + } + + if (strlen(command_sock_path) == 0) { + switch (opt_type) { + case LTTNG_CONSUMER_KERNEL: + snprintf(command_sock_path, PATH_MAX, KCONSUMERD_CMD_SOCK_PATH, + LTTNG_RUNDIR); + break; + case LTTNG_CONSUMER64_UST: + snprintf(command_sock_path, PATH_MAX, + USTCONSUMERD64_CMD_SOCK_PATH, LTTNG_RUNDIR); + break; + case LTTNG_CONSUMER32_UST: + snprintf(command_sock_path, PATH_MAX, + USTCONSUMERD32_CMD_SOCK_PATH, LTTNG_RUNDIR); + break; + default: + WARN("Unknown consumerd type"); + goto error; + } + } + + /* Init */ + lttng_consumer_init(); + + /* create the consumer instance with and assign the callbacks */ + ctx = lttng_consumer_create(opt_type, lttng_consumer_read_subbuffer, + NULL, lttng_consumer_on_recv_stream, NULL); + if (ctx == NULL) { + goto error; + } + + lttng_consumer_set_command_sock_path(ctx, command_sock_path); + if (strlen(error_sock_path) == 0) { + switch (opt_type) { + case LTTNG_CONSUMER_KERNEL: + snprintf(error_sock_path, PATH_MAX, KCONSUMERD_ERR_SOCK_PATH, + LTTNG_RUNDIR); + break; + case LTTNG_CONSUMER64_UST: + snprintf(error_sock_path, PATH_MAX, + USTCONSUMERD64_ERR_SOCK_PATH, LTTNG_RUNDIR); + break; + case LTTNG_CONSUMER32_UST: + snprintf(error_sock_path, PATH_MAX, + USTCONSUMERD32_ERR_SOCK_PATH, LTTNG_RUNDIR); + break; + default: + WARN("Unknown consumerd type"); + goto error; + } + } + + if (set_signal_handler() < 0) { + goto error; + } + + /* 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 lttng-sessiond will fail */ + if (ret < 0) { + WARN("Cannot connect to error socket, is lttng-sessiond started ?"); + } + lttng_consumer_set_error_sock(ctx, ret); + + /* Create the thread to manage the receive of fd */ + ret = pthread_create(&threads[0], NULL, lttng_consumer_thread_receive_fds, + (void *) ctx); + if (ret != 0) { + perror("pthread_create"); + goto error; + } + + /* Create thread to manage the polling/writing of traces */ + ret = pthread_create(&threads[1], NULL, lttng_consumer_thread_poll_fds, + (void *) ctx); + if (ret != 0) { + perror("pthread_create"); + goto error; + } + + for (i = 0; i < 2; i++) { + ret = pthread_join(threads[i], &status); + if (ret != 0) { + perror("pthread_join"); + goto error; + } + } + ret = EXIT_SUCCESS; + lttng_consumer_send_error(ctx, CONSUMERD_EXIT_SUCCESS); + goto end; + +error: + ret = EXIT_FAILURE; + lttng_consumer_send_error(ctx, CONSUMERD_EXIT_FAILURE); + +end: + lttng_consumer_destroy(ctx); + lttng_consumer_cleanup(); + + return ret; +} diff --git a/src/bin/lttng-consumerd/lttng-consumerd.h b/src/bin/lttng-consumerd/lttng-consumerd.h new file mode 100644 index 000000000..e42b2fea5 --- /dev/null +++ b/src/bin/lttng-consumerd/lttng-consumerd.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * 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 verion 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_CONSUMERD_H +#define _LTTNG_CONSUMERD_H + +#define CONSUMERD_RUNDIR "%s" + +/* Kernel consumer path */ +#define KCONSUMERD_PATH CONSUMERD_RUNDIR "/kconsumerd" +#define KCONSUMERD_CMD_SOCK_PATH KCONSUMERD_PATH "/command" +#define KCONSUMERD_ERR_SOCK_PATH KCONSUMERD_PATH "/error" + +/* UST 64-bit consumer path */ +#define USTCONSUMERD64_PATH CONSUMERD_RUNDIR "/ustconsumerd64" +#define USTCONSUMERD64_CMD_SOCK_PATH USTCONSUMERD64_PATH "/command" +#define USTCONSUMERD64_ERR_SOCK_PATH USTCONSUMERD64_PATH "/error" + +/* UST 32-bit consumer path */ +#define USTCONSUMERD32_PATH CONSUMERD_RUNDIR "/ustconsumerd32" +#define USTCONSUMERD32_CMD_SOCK_PATH USTCONSUMERD32_PATH "/command" +#define USTCONSUMERD32_ERR_SOCK_PATH USTCONSUMERD32_PATH "/error" + +#endif /* _LTTNG_CONSUMERD_H */ diff --git a/src/bin/lttng-sessiond/Makefile.am b/src/bin/lttng-sessiond/Makefile.am new file mode 100644 index 000000000..e824d4939 --- /dev/null +++ b/src/bin/lttng-sessiond/Makefile.am @@ -0,0 +1,45 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src \ + -DINSTALL_BIN_PATH=\""$(bindir)"\" \ + -DINSTALL_LIB_PATH=\""$(libdir)"\" + +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 \ + compat/poll.h $(COMPAT) \ + trace-kernel.c trace-kernel.h \ + kernel.c kernel.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 \ + lttng-ust-ctl.h lttng-ust-abi.h + +if HAVE_LIBLTTNG_UST_CTL +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-common -lurcu \ + $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la \ + $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ + $(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la \ + $(top_builddir)/src/common/hashtable/libhashtable.la \ + $(top_builddir)/src/common/libcommon.la + +if HAVE_LIBLTTNG_UST_CTL +lttng_sessiond_LDADD += -llttng-ust-ctl +endif diff --git a/src/bin/lttng-sessiond/channel.c b/src/bin/lttng-sessiond/channel.c new file mode 100644 index 000000000..df411b228 --- /dev/null +++ b/src/bin/lttng-sessiond/channel.c @@ -0,0 +1,322 @@ +/* + * 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 "channel.h" +#include "kernel.h" +#include "ust-ctl.h" +#include "utils.h" +#include "ust-app.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("zmalloc 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; +} + +/* + * 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 && 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 *attr, int kernel_pipe) +{ + int ret; + struct lttng_channel *defattr = NULL; + + /* Creating channel attributes if needed */ + if (attr == NULL) { + defattr = channel_new_default_attr(LTTNG_DOMAIN_KERNEL); + if (defattr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + attr = defattr; + } + + /* 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: + free(defattr); + return ret; +} + +/* + * Enable UST channel for session and domain. + */ +int channel_ust_enable(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan) +{ + int ret = LTTCOMM_OK; + + /* If already enabled, everything is OK */ + if (uchan->enabled) { + DBG3("Channel %s already enabled. Skipping", uchan->name); + goto end; + } + + switch (domain) { + case LTTNG_DOMAIN_UST: + DBG2("Channel %s being enabled in UST global domain", uchan->name); + /* Enable channel for global domain */ + ret = ust_app_enable_channel_glb(usess, uchan); + break; + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + if (ret < 0) { + if (ret != -EEXIST) { + ret = LTTCOMM_UST_CHAN_ENABLE_FAIL; + goto error; + } else { + ret = LTTCOMM_OK; + } + } + + uchan->enabled = 1; + DBG2("Channel %s enabled successfully", uchan->name); + +end: +error: + return ret; +} + +/* + * Create UST channel for session and domain. + */ +int channel_ust_create(struct ltt_ust_session *usess, int domain, + struct lttng_channel *attr) +{ + int ret = LTTCOMM_OK; + struct ltt_ust_channel *uchan = NULL; + struct lttng_channel *defattr = NULL; + + /* Creating channel attributes if needed */ + if (attr == NULL) { + defattr = channel_new_default_attr(domain); + if (defattr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + attr = defattr; + } + + /* Create UST channel */ + uchan = trace_ust_create_channel(attr, usess->pathname); + if (uchan == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + uchan->enabled = 1; + + switch (domain) { + case LTTNG_DOMAIN_UST: + DBG2("Channel %s being created in UST global domain", uchan->name); + + /* Enable channel for global domain */ + ret = ust_app_create_channel_glb(usess, uchan); + break; + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + default: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error_free_chan; + } + + if (ret < 0 && ret != -EEXIST) { + ret = LTTCOMM_UST_CHAN_ENABLE_FAIL; + goto error_free_chan; + } + + /* Adding the channel to the channel hash table. */ + rcu_read_lock(); + lttng_ht_add_unique_str(usess->domain_global.channels, &uchan->node); + rcu_read_unlock(); + + DBG2("Channel %s created successfully", uchan->name); + + free(defattr); + return LTTCOMM_OK; + +error_free_chan: + /* + * No need to remove the channel from the hash table because at this point + * it was not added hence the direct call and no call_rcu(). + */ + trace_ust_destroy_channel(uchan); +error: + free(defattr); + return ret; +} + +/* + * Disable UST channel for session and domain. + */ +int channel_ust_disable(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan) +{ + int ret = LTTCOMM_OK; + + /* Already disabled */ + if (uchan->enabled == 0) { + DBG2("Channel UST %s already disabled", uchan->name); + goto end; + } + + /* Get the right channel's hashtable */ + switch (domain) { + case LTTNG_DOMAIN_UST: + DBG2("Channel %s being disabled in UST global domain", uchan->name); + /* Disable channel for global domain */ + ret = ust_app_disable_channel_glb(usess, uchan); + break; + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + if (ret < 0 && ret != -EEXIST) { + ret = LTTCOMM_UST_DISABLE_FAIL; + goto error; + } + + uchan->enabled = 0; + + DBG2("Channel %s disabled successfully", uchan->name); + + return LTTCOMM_OK; + +end: +error: + return ret; +} diff --git a/src/bin/lttng-sessiond/channel.h b/src/bin/lttng-sessiond/channel.h new file mode 100644 index 000000000..04052f6d7 --- /dev/null +++ b/src/bin/lttng-sessiond/channel.h @@ -0,0 +1,42 @@ +/* + * 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); + +struct lttng_channel *channel_new_default_attr(int domain); + +int channel_ust_create(struct ltt_ust_session *usess, int domain, + struct lttng_channel *attr); +int channel_ust_enable(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan); +int channel_ust_disable(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan); + +#endif /* _LTT_CHANNEL_H */ diff --git a/src/bin/lttng-sessiond/compat/compat-epoll.c b/src/bin/lttng-sessiond/compat/compat-epoll.c new file mode 100644 index 000000000..5e969ee01 --- /dev/null +++ b/src/bin/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("zmalloc 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/src/bin/lttng-sessiond/compat/compat-poll.c b/src/bin/lttng-sessiond/compat/compat-poll.c new file mode 100644 index 000000000..50ef37472 --- /dev/null +++ b/src/bin/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("zmalloc 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("zmalloc 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/src/bin/lttng-sessiond/compat/poll.h b/src/bin/lttng-sessiond/compat/poll.h new file mode 100644 index 000000000..a7766400d --- /dev/null +++ b/src/bin/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/src/bin/lttng-sessiond/context.c b/src/bin/lttng-sessiond/context.c new file mode 100644 index 000000000..7fc455438 --- /dev/null +++ b/src/bin/lttng-sessiond/context.c @@ -0,0 +1,384 @@ +/* + * 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 "context.h" +#include "kernel.h" +#include "ust-app.h" +#include "trace-ust.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 UST context to channel. + */ +static int add_uctx_to_channel(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan, struct lttng_event_context *ctx) +{ + int ret; + struct ltt_ust_context *uctx; + + /* Create ltt UST context */ + uctx = trace_ust_create_context(ctx); + if (uctx == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + switch (domain) { + case LTTNG_DOMAIN_UST: + ret = ust_app_add_ctx_channel_glb(usess, uchan, uctx); + if (ret < 0) { + goto error; + } + break; + default: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + /* Add ltt UST context node to ltt UST channel */ + lttng_ht_add_unique_ulong(uchan->ctx, &uctx->node); + + return LTTCOMM_OK; + +error: + free(uctx); + return ret; +} + +/* + * Add UST context to event. + */ +static int add_uctx_to_event(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + struct lttng_event_context *ctx) +{ + int ret; + struct ltt_ust_context *uctx; + + /* Create ltt UST context */ + uctx = trace_ust_create_context(ctx); + if (uctx == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + switch (domain) { + case LTTNG_DOMAIN_UST: + ret = ust_app_add_ctx_event_glb(usess, uchan, uevent, uctx); + if (ret < 0) { + goto error; + } + break; + default: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + /* Add ltt UST context node to ltt UST event */ + lttng_ht_add_unique_ulong(uevent->ctx, &uctx->node); + + return LTTCOMM_OK; + +error: + free(uctx); + 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; +} + +/* + * Add UST context to tracer. + */ +int context_ust_add(struct ltt_ust_session *usess, int domain, + struct lttng_event_context *ctx, char *event_name, + char *channel_name) +{ + int ret = LTTCOMM_OK, have_event = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht *chan_ht; + struct ltt_ust_channel *uchan = NULL; + struct ltt_ust_event *uevent = NULL; + + /* + * Define which channel's hashtable to use from the domain or quit if + * unknown domain. + */ + switch (domain) { + case LTTNG_DOMAIN_UST: + chan_ht = usess->domain_global.channels; + 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; + } + + /* Do we have an event name */ + if (strlen(event_name) != 0) { + have_event = 1; + } + + /* Get UST channel if defined */ + if (strlen(channel_name) != 0) { + uchan = trace_ust_find_channel_by_name(chan_ht, channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + } + + /* If UST channel specified and event name, get UST event ref */ + if (uchan && have_event) { + uevent = trace_ust_find_event_by_name(uchan->events, event_name); + if (uevent == NULL) { + ret = LTTCOMM_UST_EVENT_NOT_FOUND; + goto error; + } + } + + /* At this point, we have 4 possibilities */ + + if (uchan && uevent) { /* Add ctx to event in channel */ + ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); + } else if (uchan && !have_event) { /* Add ctx to channel */ + ret = add_uctx_to_channel(usess, domain, uchan, ctx); + } else if (!uchan && have_event) { /* Add ctx to event */ + /* Add context to event without having the channel name */ + cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) { + uevent = trace_ust_find_event_by_name(uchan->events, event_name); + if (uevent != NULL) { + ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); + /* + * LTTng UST does not allowed the same event to be registered + * multiple time in different or the same channel. So, if we + * found our event, we stop. + */ + goto end; + } + } + ret = LTTCOMM_UST_EVENT_NOT_FOUND; + goto error; + } else if (!uchan && !have_event) { /* Add ctx all events, all channels */ + /* For all channels */ + cds_lfht_for_each_entry(chan_ht->ht, &iter.iter, uchan, node.node) { + ret = add_uctx_to_channel(usess, domain, uchan, ctx); + if (ret < 0) { + ERR("Context added to channel %s failed", uchan->name); + continue; + } + + /* For all events in channel */ + cds_lfht_for_each_entry(uchan->events->ht, &uiter.iter, uevent, + node.node) { + ret = add_uctx_to_event(usess, domain, uchan, uevent, ctx); + if (ret < 0) { + ERR("Context add to event %s in channel %s failed", + uevent->attr.name, uchan->name); + continue; + } + } + } + } + +end: + switch (ret) { + case -EEXIST: + ret = LTTCOMM_UST_CONTEXT_EXIST; + goto error; + case -ENOMEM: + ret = LTTCOMM_FATAL; + goto error; + } + + return LTTCOMM_OK; + +error: + return ret; +} diff --git a/src/bin/lttng-sessiond/context.h b/src/bin/lttng-sessiond/context.h new file mode 100644 index 000000000..4383f9abc --- /dev/null +++ b/src/bin/lttng-sessiond/context.h @@ -0,0 +1,33 @@ +/* + * 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 + +#include "trace-kernel.h" +#include "trace-ust.h" +#include "ust-ctl.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 *usess, int domain, + struct lttng_event_context *ctx, char *event_name, + char *channel_name); + +#endif /* _LTT_CONTEXT_H */ diff --git a/src/bin/lttng-sessiond/event.c b/src/bin/lttng-sessiond/event.c new file mode 100644 index 000000000..31c0c9715 --- /dev/null +++ b/src/bin/lttng-sessiond/event.c @@ -0,0 +1,555 @@ +/* + * 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 "channel.h" +#include "event.h" +#include "kernel.h" +#include "ust-ctl.h" +#include "ust-app.h" +#include "trace-kernel.h" +#include "trace-ust.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 = NULL; + + /* For each event in the kernel session */ + cds_list_for_each_entry(kevent, &kchan->events_list.head, list) { + if (kevent->enabled == 0) { + 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) { + if (ret == -EEXIST) { + ret = LTTCOMM_KERN_EVENT_EXIST; + } else { + ret = LTTCOMM_KERN_ENABLE_FAIL; + } + 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; +} + +/* + * ============================ + * UST : The Ultimate Frontier! + * ============================ + */ + +/* + * Enable all UST tracepoints for a channel from a UST session. + */ +int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan) +{ + int ret, i; + size_t size; + struct lttng_ht_iter iter; + struct ltt_ust_event *uevent = NULL; + struct lttng_event *events = NULL; + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + /* Enable existing events */ + cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, + node.node) { + if (uevent->enabled == 0) { + ret = ust_app_enable_event_glb(usess, uchan, uevent); + if (ret < 0) { + continue; + } + uevent->enabled = 1; + } + } + + /* Get all UST available events */ + size = ust_app_list_events(&events); + if (size < 0) { + ret = LTTCOMM_UST_LIST_FAIL; + goto error; + } + + for (i = 0; i < size; i++) { + /* + * Check if event exist and if so, continue since it was enable + * previously. + */ + uevent = trace_ust_find_event_by_name(uchan->events, + events[i].name); + if (uevent != NULL) { + ret = ust_app_enable_event_pid(usess, uchan, uevent, + events[i].pid); + if (ret < 0) { + if (ret != -EEXIST) { + ret = LTTCOMM_UST_ENABLE_FAIL; + goto error; + } + } + continue; + } + + /* Create ust event */ + uevent = trace_ust_create_event(&events[i]); + if (uevent == NULL) { + ret = LTTCOMM_FATAL; + goto error_destroy; + } + + /* Create event for the specific PID */ + ret = ust_app_enable_event_pid(usess, uchan, uevent, + events[i].pid); + if (ret < 0) { + if (ret == -EEXIST) { + ret = LTTCOMM_UST_EVENT_EXIST; + goto error; + } else { + ret = LTTCOMM_UST_ENABLE_FAIL; + goto error_destroy; + } + } + + uevent->enabled = 1; + /* Add ltt ust event to channel */ + rcu_read_lock(); + lttng_ht_add_unique_str(uchan->events, &uevent->node); + rcu_read_unlock(); + } + + free(events); + 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; + } + + return LTTCOMM_OK; + +error_destroy: + trace_ust_destroy_event(uevent); + +error: + free(events); + return ret; +} + +/* + * Enable UST tracepoint event for a channel from a UST session. + */ +int event_ust_enable_tracepoint(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan, struct lttng_event *event) +{ + int ret = LTTCOMM_OK, to_create = 0; + struct ltt_ust_event *uevent; + + 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; + } + /* Valid to set it after the goto error since uevent is still NULL */ + to_create = 1; + } + + if (uevent->enabled) { + /* It's already enabled so everything is OK */ + goto end; + } + + uevent->enabled = 1; + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + if (to_create) { + /* Create event on all UST registered apps for session */ + ret = ust_app_create_event_glb(usess, uchan, uevent); + } else { + /* Enable event on all UST registered apps for session */ + ret = ust_app_enable_event_glb(usess, uchan, uevent); + } + + if (ret < 0) { + if (ret == -EEXIST) { + ret = LTTCOMM_UST_EVENT_EXIST; + goto end; + } else { + 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 end; + } + + if (to_create) { + rcu_read_lock(); + /* Add ltt ust event to channel */ + lttng_ht_add_unique_str(uchan->events, &uevent->node); + rcu_read_unlock(); + } + + DBG("Event UST %s %s in channel %s", uevent->attr.name, + to_create ? "created" : "enabled", uchan->name); + + ret = LTTCOMM_OK; + +end: + return ret; + +error: + /* + * Only destroy event on creation time (not enabling time) because if the + * event is found in the channel (to_create == 0), it means that at some + * point the enable_event worked and it's thus valid to keep it alive. + * Destroying it also implies that we also destroy it's shadow copy to sync + * everyone up. + */ + if (to_create) { + /* In this code path, the uevent was not added to the hash table */ + trace_ust_destroy_event(uevent); + } + return ret; +} + +/* + * Disable UST tracepoint of a channel from a UST session. + */ +int event_ust_disable_tracepoint(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan, char *event_name) +{ + int ret; + struct ltt_ust_event *uevent; + + uevent = trace_ust_find_event_by_name(uchan->events, event_name); + if (uevent == NULL) { + ret = LTTCOMM_UST_EVENT_NOT_FOUND; + goto error; + } + + if (uevent->enabled == 0) { + /* It's already enabled so everything is OK */ + ret = LTTCOMM_OK; + goto end; + } + + switch (domain) { + case LTTNG_DOMAIN_UST: + ret = ust_app_disable_event_glb(usess, uchan, uevent); + if (ret < 0 && ret != -EEXIST) { + ret = LTTCOMM_UST_DISABLE_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; + } + + uevent->enabled = 0; + ret = LTTCOMM_OK; + +end: + DBG2("Event UST %s disabled in channel %s", uevent->attr.name, + uchan->name); + +error: + return ret; +} + +/* + * Disable all UST tracepoints for a channel from a UST session. + */ +int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan) +{ + int ret, i; + size_t size; + struct lttng_ht_iter iter; + struct ltt_ust_event *uevent = NULL; + struct lttng_event *events = NULL; + + switch (domain) { + case LTTNG_DOMAIN_UST: + { + /* Disabling existing events */ + cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, + node.node) { + if (uevent->enabled == 1) { + ret = ust_app_disable_event_glb(usess, uchan, uevent); + if (ret < 0) { + continue; + } + uevent->enabled = 0; + } + } + + /* Get all UST available events */ + size = ust_app_list_events(&events); + if (size < 0) { + ret = LTTCOMM_UST_LIST_FAIL; + goto error; + } + + for (i = 0; i < size; i++) { + uevent = trace_ust_find_event_by_name(uchan->events, + events[i].name); + if (uevent != NULL && uevent->enabled == 1) { + ret = ust_app_disable_event_pid(usess, uchan, uevent, + events[i].pid); + if (ret < 0 && ret != -EEXIST) { + ret = LTTCOMM_UST_DISABLE_FAIL; + goto error; + } + uevent->enabled = 0; + continue; + } + } + + free(events); + 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; + } + + return LTTCOMM_OK; + +error: + free(events); + return ret; +} diff --git a/src/bin/lttng-sessiond/event.h b/src/bin/lttng-sessiond/event.h new file mode 100644 index 000000000..6103240b3 --- /dev/null +++ b/src/bin/lttng-sessiond/event.h @@ -0,0 +1,50 @@ +/* + * 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 "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 *usess, int domain, + struct ltt_ust_channel *uchan, struct lttng_event *event); +int event_ust_disable_tracepoint(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan, char *event_name); +int event_ust_enable_all_tracepoints(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan); +int event_ust_disable_all_tracepoints(struct ltt_ust_session *usess, int domain, + struct ltt_ust_channel *uchan); + +#endif /* _LTT_EVENT_H */ diff --git a/src/bin/lttng-sessiond/futex.c b/src/bin/lttng-sessiond/futex.c new file mode 100644 index 000000000..eda1fa313 --- /dev/null +++ b/src/bin/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 (caa_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/src/bin/lttng-sessiond/futex.h b/src/bin/lttng-sessiond/futex.h new file mode 100644 index 000000000..a056ec22b --- /dev/null +++ b/src/bin/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/src/bin/lttng-sessiond/kernel.c b/src/bin/lttng-sessiond/kernel.c new file mode 100644 index 000000000..a8fd844d9 --- /dev/null +++ b/src/bin/lttng-sessiond/kernel.c @@ -0,0 +1,609 @@ +/* + * 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 "kernel.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 = zmalloc(sizeof(struct lttng_kernel_context)); + if (chan->ctx == NULL) { + perror("zmalloc 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 = zmalloc(sizeof(struct lttng_kernel_context)); + if (event->ctx == NULL) { + perror("zmalloc 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 = zmalloc(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 * sizeof(struct lttng_event)); + 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'; + elist[count].enabled = -1; + 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/src/bin/lttng-sessiond/kernel.h b/src/bin/lttng-sessiond/kernel.h new file mode 100644 index 000000000..2fbaca91e --- /dev/null +++ b/src/bin/lttng-sessiond/kernel.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/src/bin/lttng-sessiond/lttng-sessiond.h b/src/bin/lttng-sessiond/lttng-sessiond.h new file mode 100644 index 000000000..6a7006c0b --- /dev/null +++ b/src/bin/lttng-sessiond/lttng-sessiond.h @@ -0,0 +1,98 @@ +/* + * 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 + +#include "session.h" +#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 ucred creds; + 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/src/bin/lttng-sessiond/lttng-ust-abi.h b/src/bin/lttng-sessiond/lttng-ust-abi.h new file mode 100644 index 000000000..28a040763 --- /dev/null +++ b/src/bin/lttng-sessiond/lttng-ust-abi.h @@ -0,0 +1,168 @@ +#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 256 + +#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, + LTTNG_UST_TRACEPOINT_LOGLEVEL = 3, +}; + +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 */ +}; + +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_tracepoint_iter { + char name[LTTNG_UST_SYM_NAME_LEN]; /* provider:name */ + char loglevel[LTTNG_UST_SYM_NAME_LEN]; /* loglevel */ + int64_t loglevel_value; +}; + +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) + +/* Tracepoint list commands */ +#define LTTNG_UST_TRACEPOINT_LIST_GET _UST_CMD(0x90) + +#define LTTNG_UST_ROOT_HANDLE 0 + +struct lttng_ust_obj; + +union ust_args { + struct { + int *shm_fd; + int *wait_fd; + uint64_t *memory_map_size; + } channel; + struct { + int *shm_fd; + int *wait_fd; + uint64_t *memory_map_size; + } stream; +}; + +struct lttng_ust_objd_ops { + long (*cmd)(int objd, unsigned int cmd, unsigned long arg, + union ust_args *args); + int (*release)(int objd); +}; + +/* Create root handle. Always ID 0. */ +int lttng_abi_create_root_handle(void); + +const struct lttng_ust_objd_ops *objd_ops(int id); +int lttng_ust_objd_unref(int id); + +void lttng_ust_abi_exit(void); +void lttng_ust_events_exit(void); + +#endif /* _LTTNG_UST_ABI_H */ diff --git a/src/bin/lttng-sessiond/lttng-ust-ctl.h b/src/bin/lttng-sessiond/lttng-ust-ctl.h new file mode 100644 index 000000000..b8cb965b0 --- /dev/null +++ b/src/bin/lttng-sessiond/lttng-ust-ctl.h @@ -0,0 +1,139 @@ +/* + * 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 "lttng-ust-abi.h" + +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); + +/* + * ustctl_tracepoint_list returns a tracepoint list handle, or negative + * error value. + */ +int ustctl_tracepoint_list(int sock); +/* + * ustctl_tracepoint_list_get is used to iterate on the tp list + * handle. End is iteration is reached when -ENOENT is returned. + */ +int ustctl_tracepoint_list_get(int sock, int tp_list_handle, + struct lttng_ust_tracepoint_iter *iter); + +int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v); +int ustctl_wait_quiescent(int sock); + +int ustctl_sock_flush_buffer(int sock, struct lttng_ust_object_data *object); + +/* 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); + +void ustctl_flush_buffer(struct lttng_ust_shm_handle *handle, + struct lttng_ust_lib_ring_buffer *buf, + int producer_active); + +/* Release object created by members of this API */ +int ustctl_release_object(int sock, struct lttng_ust_object_data *data); +/* Release handle returned by create session. */ +int ustctl_release_handle(int sock, int handle); + +#endif /* _LTTNG_UST_CTL_H */ diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c new file mode 100644 index 000000000..0d1a916c2 --- /dev/null +++ b/src/bin/lttng-sessiond/main.c @@ -0,0 +1,4566 @@ +/* + * 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 + +#include "lttng-sessiond.h" +#include "channel.h" +#include "compat/poll.h" +#include "context.h" +#include "event.h" +#include "futex.h" +#include "kernel.h" +#include "shm.h" +#include "ust-ctl.h" +#include "utils.h" + +#define CONSUMERD_FILE "lttng-consumerd" + +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 opt_no_kernel; +static int is_root; /* Set to 1 if the daemon is running as root */ +static pid_t ppid; /* Parent PID for --sig-parent option */ +static char *rundir; + +/* Consumer daemon specific control data */ +static struct consumer_data kconsumer_data = { + .type = LTTNG_CONSUMER_KERNEL, + .err_unix_sock_path = KCONSUMERD_ERR_SOCK_PATH, + .cmd_unix_sock_path = KCONSUMERD_CMD_SOCK_PATH, +}; +static struct consumer_data ustconsumer64_data = { + .type = LTTNG_CONSUMER64_UST, + .err_unix_sock_path = USTCONSUMERD64_ERR_SOCK_PATH, + .cmd_unix_sock_path = USTCONSUMERD64_CMD_SOCK_PATH, +}; +static struct consumer_data ustconsumer32_data = { + .type = LTTNG_CONSUMER32_UST, + .err_unix_sock_path = USTCONSUMERD32_ERR_SOCK_PATH, + .cmd_unix_sock_path = USTCONSUMERD32_CMD_SOCK_PATH, +}; + +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; + +int ust_consumerd64_fd = -1; +int ust_consumerd32_fd = -1; + +static const char *consumerd32_bin = CONFIG_CONSUMERD32_BIN; +static const char *consumerd64_bin = CONFIG_CONSUMERD64_BIN; +static const char *consumerd32_libdir = CONFIG_CONSUMERD32_LIBDIR; +static const char *consumerd64_libdir = CONFIG_CONSUMERD64_LIBDIR; + +static +void setup_consumerd_path(void) +{ + const char *bin, *libdir; + + /* + * Allow INSTALL_BIN_PATH to be used as a target path for the + * native architecture size consumer if CONFIG_CONSUMER*_PATH + * has not been defined. + */ +#if (CAA_BITS_PER_LONG == 32) + if (!consumerd32_bin[0]) { + consumerd32_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE; + } + if (!consumerd32_libdir[0]) { + consumerd32_libdir = INSTALL_LIB_PATH; + } +#elif (CAA_BITS_PER_LONG == 64) + if (!consumerd64_bin[0]) { + consumerd64_bin = INSTALL_BIN_PATH "/" CONSUMERD_FILE; + } + if (!consumerd64_libdir[0]) { + consumerd64_libdir = INSTALL_LIB_PATH; + } +#else +#error "Unknown bitness" +#endif + + /* + * runtime env. var. overrides the build default. + */ + bin = getenv("LTTNG_CONSUMERD32_BIN"); + if (bin) { + consumerd32_bin = bin; + } + bin = getenv("LTTNG_CONSUMERD64_BIN"); + if (bin) { + consumerd64_bin = bin; + } + libdir = getenv("LTTNG_TOOLS_CONSUMERD32_LIBDIR"); + if (libdir) { + consumerd32_libdir = libdir; + } + libdir = getenv("LTTNG_TOOLS_CONSUMERD64_LIBDIR"); + if (libdir) { + consumerd64_libdir = libdir; + } +} + +/* + * 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) { + DBG3("No kernel session when tearingdown session"); + return; + } + + 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); +} + +/* + * 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) +{ + int ret; + + if (!session->ust_session) { + DBG3("No UST session when tearingdown session"); + return; + } + + DBG("Tearing down UST session(s)"); + + ret = ust_app_destroy_trace_all(session->ust_session); + if (ret) { + ERR("Error in ust_app_destroy_trace_all"); + } + + 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"); + + DBG("Removing %s directory", rundir); + ret = asprintf(&cmd, "rm -rf %s", 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 %s", rundir); + } + free(cmd); + + 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); + + if (is_root && !opt_no_kernel) { + DBG2("Closing kernel fd"); + close(kernel_tracer_fd); + DBG("Unloading kernel modules"); + modprobe_remove_kernel_modules(); + } + + close(thread_quit_pipe[0]); + close(thread_quit_pipe[1]); + + /* */ + DBG("%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); + /* */ +} + +/* + * 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, + uid_t uid, gid_t gid) +{ + 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 */ + lkm.u.stream.uid = uid; + lkm.u.stream.gid = gid; + 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 */ + lkm.u.stream.uid = session->uid; + lkm.u.stream.gid = session->gid; + 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, + session->uid, session->gid); + 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 = zmalloc(sizeof(struct lttcomm_lttng_msg) + buf_size); + if (cmd_ctx->llm == NULL) { + perror("zmalloc"); + 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, + session->uid, session->gid); + if (ret < 0) { + goto error; + } + } + goto error; + } + } + session_unlock(session); + } + session_unlock_list(); + return ret; + +error: + session_unlock(session); + session_unlock_list(); + return ret; +} + +/* + * For each tracing session, update newly registered apps. + */ +static void update_ust_app(int app_sock) +{ + struct ltt_session *sess, *stmp; + + /* For all tracing session(s) */ + cds_list_for_each_entry_safe(sess, stmp, &session_list_ptr->head, list) { + if (sess->ust_session) { + ust_app_global_update(sess->ust_session, app_sock); + } + } +} + +/* + * 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 == -ENOMEM) { + goto error; + } else if (ret < 0) { + break; + } + + /* + * Add channel(s) and event(s) to newly registered apps + * from lttng global UST domain. + */ + update_ust_app(ust_cmd.sock); + + ret = ust_app_register_done(ust_cmd.sock); + if (ret < 0) { + /* + * If the registration is not possible, we simply + * unregister the apps and continue + */ + ust_app_unregister(ust_cmd.sock); + } else { + /* + * We 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 on remote end. */ + 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 = zmalloc(sizeof(struct ust_command)); + if (ust_cmd == NULL) { + perror("ust command zmalloc"); + 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 *consumer_to_use; + const char *verbosity; + struct stat st; + + DBG("Spawning consumerd"); + + pid = fork(); + if (pid == 0) { + /* + * Exec consumerd. + */ + if (opt_verbose_consumer) { + verbosity = "--verbose"; + } else { + verbosity = "--quiet"; + } + switch (consumer_data->type) { + case LTTNG_CONSUMER_KERNEL: + /* + * Find out which consumerd to execute. We will first try the + * 64-bit path, then the sessiond's installation directory, and + * fallback on the 32-bit one, + */ + DBG3("Looking for a kernel consumer at these locations:"); + DBG3(" 1) %s", consumerd64_bin); + DBG3(" 2) %s/%s", INSTALL_BIN_PATH, CONSUMERD_FILE); + DBG3(" 3) %s", consumerd32_bin); + if (stat(consumerd64_bin, &st) == 0) { + DBG3("Found location #1"); + consumer_to_use = consumerd64_bin; + } else if (stat(INSTALL_BIN_PATH "/" CONSUMERD_FILE, &st) == 0) { + DBG3("Found location #2"); + consumer_to_use = INSTALL_BIN_PATH "/" CONSUMERD_FILE; + } else if (stat(consumerd32_bin, &st) == 0) { + DBG3("Found location #3"); + consumer_to_use = consumerd32_bin; + } else { + DBG("Could not find any valid consumerd executable"); + break; + } + DBG("Using kernel consumer at: %s", consumer_to_use); + execl(consumer_to_use, + "lttng-consumerd", verbosity, "-k", + "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, + "--consumerd-err-sock", consumer_data->err_unix_sock_path, + NULL); + break; + case LTTNG_CONSUMER64_UST: + { + char *tmpnew = NULL; + + if (consumerd64_libdir[0] != '\0') { + char *tmp; + size_t tmplen; + + tmp = getenv("LD_LIBRARY_PATH"); + if (!tmp) { + tmp = ""; + } + tmplen = strlen("LD_LIBRARY_PATH=") + + strlen(consumerd64_libdir) + 1 /* : */ + strlen(tmp); + tmpnew = zmalloc(tmplen + 1 /* \0 */); + if (!tmpnew) { + ret = -ENOMEM; + goto error; + } + strcpy(tmpnew, "LD_LIBRARY_PATH="); + strcat(tmpnew, consumerd64_libdir); + if (tmp[0] != '\0') { + strcat(tmpnew, ":"); + strcat(tmpnew, tmp); + } + ret = putenv(tmpnew); + if (ret) { + ret = -errno; + goto error; + } + } + DBG("Using 64-bit UST consumer at: %s", consumerd64_bin); + ret = execl(consumerd64_bin, "lttng-consumerd", verbosity, "-u", + "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, + "--consumerd-err-sock", consumer_data->err_unix_sock_path, + NULL); + if (consumerd64_libdir[0] != '\0') { + free(tmpnew); + } + if (ret) { + goto error; + } + break; + } + case LTTNG_CONSUMER32_UST: + { + char *tmpnew = NULL; + + if (consumerd32_libdir[0] != '\0') { + char *tmp; + size_t tmplen; + + tmp = getenv("LD_LIBRARY_PATH"); + if (!tmp) { + tmp = ""; + } + tmplen = strlen("LD_LIBRARY_PATH=") + + strlen(consumerd32_libdir) + 1 /* : */ + strlen(tmp); + tmpnew = zmalloc(tmplen + 1 /* \0 */); + if (!tmpnew) { + ret = -ENOMEM; + goto error; + } + strcpy(tmpnew, "LD_LIBRARY_PATH="); + strcat(tmpnew, consumerd32_libdir); + if (tmp[0] != '\0') { + strcat(tmpnew, ":"); + strcat(tmpnew, tmp); + } + ret = putenv(tmpnew); + if (ret) { + ret = -errno; + goto error; + } + } + DBG("Using 32-bit UST consumer at: %s", consumerd32_bin); + ret = execl(consumerd32_bin, "lttng-consumerd", verbosity, "-u", + "--consumerd-cmd-sock", consumer_data->cmd_unix_sock_path, + "--consumerd-err-sock", consumer_data->err_unix_sock_path, + NULL); + if (consumerd32_libdir[0] != '\0') { + free(tmpnew); + } + if (ret) { + goto error; + } + 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; + } +error: + 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 = run_as_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) +{ + struct ltt_ust_session *lus = NULL; + int ret; + + switch (domain->type) { + case LTTNG_DOMAIN_UST: + break; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + DBG("Creating UST session"); + + lus = trace_ust_create_session(session->path, session->id, domain); + if (lus == NULL) { + ret = LTTCOMM_UST_SESS_FAIL; + goto error; + } + + ret = run_as_mkdir_recursive(lus->pathname, S_IRWXU | S_IRWXG, + session->uid, session->gid); + 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: + ERR("Unknown UST domain on create session %d", domain->type); + goto error; + } + lus->uid = session->uid; + lus->gid = session->gid; + 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 = run_as_mkdir_recursive(session->kernel_session->trace_path, + S_IRWXU | S_IRWXG, session->uid, session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + goto error; + } + } + session->kernel_session->uid = session->uid; + session->kernel_session->gid = session->gid; + +error: + return ret; +} + +/* + * Check if the UID or GID match the session. Root user has access to all + * sessions. + */ +static int session_access_ok(struct ltt_session *session, uid_t uid, gid_t gid) +{ + if (uid != session->uid && gid != session->gid && uid != 0) { + return 0; + } else { + return 1; + } +} + +static unsigned int lttng_sessions_count(uid_t uid, gid_t gid) +{ + unsigned int i = 0; + struct ltt_session *session; + + DBG("Counting number of available session for UID %d GID %d", + uid, gid); + cds_list_for_each_entry(session, &session_list_ptr->head, list) { + /* + * Only list the sessions the user can control. + */ + if (!session_access_ok(session, uid, gid)) { + continue; + } + i++; + } + return i; +} + +/* + * 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, uid_t uid, + gid_t gid) +{ + unsigned int i = 0; + struct ltt_session *session; + + DBG("Getting all available session for UID %d GID %d", + uid, gid); + /* + * 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) { + /* + * Only list the sessions the user can control. + */ + if (!session_access_ok(session, uid, gid)) { + continue; + } + 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'; + sessions[i].enabled = session->enabled; + i++; + } +} + +/* + * Fill lttng_channel array of all channels. + */ +static void list_lttng_channels(int domain, struct ltt_session *session, + struct lttng_channel *channels) +{ + int i = 0; + struct ltt_kernel_channel *kchan; + + DBG("Listing channels for session %s", session->name); + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + /* 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++; + } + } + break; + case LTTNG_DOMAIN_UST: + { + struct lttng_ht_iter iter; + struct ltt_ust_channel *uchan; + + cds_lfht_for_each_entry(session->ust_session->domain_global.channels->ht, + &iter.iter, uchan, node.node) { + strncpy(channels[i].name, uchan->name, LTTNG_SYMBOL_NAME_LEN); + channels[i].attr.overwrite = uchan->attr.overwrite; + channels[i].attr.subbuf_size = uchan->attr.subbuf_size; + channels[i].attr.num_subbuf = uchan->attr.num_subbuf; + channels[i].attr.switch_timer_interval = + uchan->attr.switch_timer_interval; + channels[i].attr.read_timer_interval = + uchan->attr.read_timer_interval; + channels[i].enabled = uchan->enabled; + switch (uchan->attr.output) { + case LTTNG_UST_MMAP: + default: + channels[i].attr.output = LTTNG_EVENT_MMAP; + break; + } + i++; + } + break; + } + default: + break; + } +} + +/* + * Create a list of ust global domain events. + */ +static int list_lttng_ust_global_events(char *channel_name, + struct ltt_ust_domain_global *ust_global, struct lttng_event **events) +{ + int i = 0, ret = 0; + unsigned int nb_event = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *node; + struct ltt_ust_channel *uchan; + struct ltt_ust_event *uevent; + struct lttng_event *tmp; + + DBG("Listing UST global events for channel %s", channel_name); + + rcu_read_lock(); + + lttng_ht_lookup(ust_global->channels, (void *)channel_name, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node == NULL) { + ret = -LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + uchan = caa_container_of(&node->node, struct ltt_ust_channel, node.node); + + nb_event += lttng_ht_get_count(uchan->events); + + if (nb_event == 0) { + ret = nb_event; + goto error; + } + + DBG3("Listing UST global %d events", nb_event); + + tmp = zmalloc(nb_event * sizeof(struct lttng_event)); + if (tmp == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { + strncpy(tmp[i].name, uevent->attr.name, LTTNG_SYMBOL_NAME_LEN); + tmp[i].name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + tmp[i].enabled = uevent->enabled; + switch (uevent->attr.instrumentation) { + case LTTNG_UST_TRACEPOINT: + tmp[i].type = LTTNG_EVENT_TRACEPOINT; + break; + case LTTNG_UST_PROBE: + tmp[i].type = LTTNG_EVENT_PROBE; + break; + case LTTNG_UST_FUNCTION: + tmp[i].type = LTTNG_EVENT_FUNCTION; + break; + case LTTNG_UST_TRACEPOINT_LOGLEVEL: + tmp[i].type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; + break; + } + i++; + } + + ret = nb_event; + *events = tmp; + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Fill lttng_event array of all kernel events in the channel. + */ +static int list_lttng_kernel_events(char *channel_name, + struct ltt_kernel_session *kernel_session, struct lttng_event **events) +{ + int i = 0, ret; + unsigned int nb_event; + struct ltt_kernel_event *event; + struct ltt_kernel_channel *kchan; + + kchan = trace_kernel_get_channel_by_name(channel_name, kernel_session); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + nb_event = kchan->event_count; + + DBG("Listing events for channel %s", kchan->channel->name); + + if (nb_event == 0) { + ret = nb_event; + goto error; + } + + *events = zmalloc(nb_event * sizeof(struct lttng_event)); + if (*events == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + + /* 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++; + } + + return nb_event; + +error: + return ret; +} + +/* + * 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; + struct ltt_ust_session *usess; + + usess = session->ust_session; + + 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: + { + struct ltt_ust_channel *uchan; + struct lttng_ht *chan_ht; + + chan_ht = usess->domain_global.channels; + + uchan = trace_ust_find_channel_by_name(chan_ht, channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = channel_ust_disable(usess, domain, uchan); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + } + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_ENABLE_CHANNEL processed by the client thread. + */ +static int cmd_enable_channel(struct ltt_session *session, + int domain, struct lttng_channel *attr) +{ + int ret; + struct ltt_ust_session *usess = session->ust_session; + struct lttng_ht *chan_ht; + + DBG("Enabling channel %s for session %s", attr->name, session->name); + + switch (domain) { + 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; + + chan_ht = usess->domain_global.channels; + + uchan = trace_ust_find_channel_by_name(chan_ht, attr->name); + if (uchan == NULL) { + ret = channel_ust_create(usess, domain, attr); + } else { + ret = channel_ust_enable(usess, domain, uchan); + } + break; + } + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + ret = LTTCOMM_NOT_IMPLEMENTED; + goto error; + default: + ret = LTTCOMM_UNKNOWN_DOMAIN; + goto error; + } + +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; + struct ltt_kernel_session *ksess; + + ksess = session->kernel_session; + + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_tracepoint(ksess, kchan, event_name); + 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_session *usess; + + usess = session->ust_session; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = event_ust_disable_tracepoint(usess, domain, uchan, event_name); + if (ret != LTTCOMM_OK) { + goto error; + } + + DBG3("Disable UST event %s in channel %s completed", event_name, + channel_name); + 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_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; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + { + struct ltt_kernel_session *ksess; + struct ltt_kernel_channel *kchan; + + ksess = session->kernel_session; + + kchan = trace_kernel_get_channel_by_name(channel_name, ksess); + if (kchan == NULL) { + ret = LTTCOMM_KERN_CHAN_NOT_FOUND; + goto error; + } + + ret = event_kernel_disable_all(ksess, kchan); + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + } + case LTTNG_DOMAIN_UST: + { + struct ltt_ust_session *usess; + struct ltt_ust_channel *uchan; + + usess = session->ust_session; + + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + ret = LTTCOMM_UST_CHAN_NOT_FOUND; + goto error; + } + + ret = event_ust_disable_all_tracepoints(usess, domain, uchan); + if (ret != 0) { + goto error; + } + + DBG3("Disable all UST events in channel %s completed", channel_name); + + 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_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 = session->ust_session; + + ret = context_ust_add(usess, domain, ctx, event_name, channel_name); + if (ret != LTTCOMM_OK) { + 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_EVENT processed by the client thread. + * + * TODO: currently, both events and loglevels are kept within the same + * namespace for UST global registry/app registery, so if an event + * happen to have the same name as the loglevel (very unlikely though), + * and an attempt is made to enable/disable both in the same session, + * the first to be created will be the only one allowed to exist. + */ +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) { + free(attr); + goto error; + } + free(attr); + } + + /* 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 lttng_channel *attr; + struct ltt_ust_channel *uchan; + + /* Get channel from global UST domain */ + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + /* Create default channel */ + attr = channel_new_default_attr(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + snprintf(attr->name, NAME_MAX, "%s", channel_name); + attr->name[NAME_MAX - 1] = '\0'; + + ret = channel_ust_create(usess, domain, attr); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + + /* Get the newly created channel reference back */ + uchan = trace_ust_find_channel_by_name( + usess->domain_global.channels, channel_name); + if (uchan == NULL) { + /* Something is really wrong */ + ret = LTTCOMM_FATAL; + goto error; + } + } + + /* At this point, the session and channel exist on the tracer */ + ret = event_ust_enable_tracepoint(usess, domain, uchan, event); + if (ret != LTTCOMM_OK) { + 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_EVENT_SYSCALL: + ret = event_kernel_enable_all_syscalls(session->kernel_session, + kchan, kernel_tracer_fd); + break; + case LTTNG_EVENT_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_EVENT_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; + } + + /* Manage return value */ + if (ret != LTTCOMM_OK) { + goto error; + } + + kernel_wait_quiescent(kernel_tracer_fd); + break; + case LTTNG_DOMAIN_UST: + { + struct lttng_channel *attr; + struct ltt_ust_channel *uchan; + struct ltt_ust_session *usess = session->ust_session; + + /* Get channel from global UST domain */ + uchan = trace_ust_find_channel_by_name(usess->domain_global.channels, + channel_name); + if (uchan == NULL) { + /* Create default channel */ + attr = channel_new_default_attr(domain); + if (attr == NULL) { + ret = LTTCOMM_FATAL; + goto error; + } + snprintf(attr->name, NAME_MAX, "%s", channel_name); + attr->name[NAME_MAX - 1] = '\0'; + + /* Use the internal command enable channel */ + ret = channel_ust_create(usess, domain, attr); + if (ret != LTTCOMM_OK) { + free(attr); + goto error; + } + free(attr); + + /* Get the newly created channel reference back */ + uchan = trace_ust_find_channel_by_name( + usess->domain_global.channels, channel_name); + if (uchan == NULL) { + /* Something is really wrong */ + ret = LTTCOMM_FATAL; + goto error; + } + } + + /* At this point, the session and channel exist on the tracer */ + + switch (event_type) { + case LTTNG_EVENT_ALL: + case LTTNG_EVENT_TRACEPOINT: + ret = event_ust_enable_all_tracepoints(usess, domain, uchan); + if (ret != LTTCOMM_OK) { + goto error; + } + break; + default: + ret = LTTCOMM_UST_ENABLE_FAIL; + goto error; + } + + /* Manage return value */ + if (ret != LTTCOMM_OK) { + 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_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; + case LTTNG_DOMAIN_UST: + nb_events = ust_app_list_events(events); + if (nb_events < 0) { + ret = LTTCOMM_UST_LIST_FAIL; + goto error; + } + break; + default: + 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; + + /* Short cut */ + ksession = session->kernel_session; + usess = session->ust_session; + + if (session->enabled) { + ret = LTTCOMM_UST_START_FAIL; + goto error; + } + + session->enabled = 1; + + /* 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); + } + + /* Flag session that trace should start automatically */ + if (usess) { + usess->start_trace = 1; + + ret = ust_app_start_trace_all(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; + + /* Short cut */ + ksession = session->kernel_session; + usess = session->ust_session; + + if (!session->enabled) { + ret = LTTCOMM_UST_STOP_FAIL; + goto error; + } + + session->enabled = 0; + + /* 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); + } + + if (usess) { + usess->start_trace = 0; + + ret = ust_app_stop_trace_all(usess); + if (ret < 0) { + ret = LTTCOMM_UST_STOP_FAIL; + goto error; + } + } + + ret = LTTCOMM_OK; + +error: + return ret; +} + +/* + * Command LTTNG_CREATE_SESSION processed by the client thread. + */ +static int cmd_create_session(char *name, char *path, struct ucred *creds) +{ + int ret; + + ret = session_create(name, path, creds->uid, creds->gid); + 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); + /* UST session teardown */ + teardown_ust_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_fds_sent != 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, index = 0; + ssize_t nb_dom = 0; + + if (session->kernel_session != NULL) { + DBG3("Listing domains found kernel domain"); + nb_dom++; + } + + if (session->ust_session != NULL) { + DBG3("Listing domains found UST global domain"); + nb_dom++; + } + + *domains = zmalloc(nb_dom * sizeof(struct lttng_domain)); + if (*domains == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + if (session->kernel_session != NULL) { + (*domains)[index].type = LTTNG_DOMAIN_KERNEL; + index++; + } + + if (session->ust_session != NULL) { + (*domains)[index].type = LTTNG_DOMAIN_UST; + index++; + } + + return nb_dom; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_CHANNELS processed by the client thread. + */ +static ssize_t cmd_list_channels(int domain, struct ltt_session *session, + struct lttng_channel **channels) +{ + int ret; + ssize_t nb_chan = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + nb_chan = session->kernel_session->channel_count; + } + DBG3("Number of kernel channels %zd", nb_chan); + break; + case LTTNG_DOMAIN_UST: + if (session->ust_session != NULL) { + nb_chan = lttng_ht_get_count( + session->ust_session->domain_global.channels); + } + DBG3("Number of UST global channels %zd", nb_chan); + break; + default: + *channels = NULL; + ret = -LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + if (nb_chan > 0) { + *channels = zmalloc(nb_chan * sizeof(struct lttng_channel)); + if (*channels == NULL) { + ret = -LTTCOMM_FATAL; + goto error; + } + + list_lttng_channels(domain, session, *channels); + } else { + *channels = NULL; + } + + return nb_chan; + +error: + return ret; +} + +/* + * Command LTTNG_LIST_EVENTS processed by the client thread. + */ +static ssize_t cmd_list_events(int domain, struct ltt_session *session, + char *channel_name, struct lttng_event **events) +{ + int ret = 0; + ssize_t nb_event = 0; + + switch (domain) { + case LTTNG_DOMAIN_KERNEL: + if (session->kernel_session != NULL) { + nb_event = list_lttng_kernel_events(channel_name, + session->kernel_session, events); + } + break; + case LTTNG_DOMAIN_UST: + { + if (session->ust_session != NULL) { + nb_event = list_lttng_ust_global_events(channel_name, + &session->ust_session->domain_global, events); + } + break; + } + default: + ret = -LTTCOMM_NOT_IMPLEMENTED; + goto error; + } + + ret = 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); + + if (opt_no_kernel && cmd_ctx->lsm->domain.type == LTTNG_DOMAIN_KERNEL) { + ret = LTTCOMM_KERN_NA; + goto error; + } + + /* + * 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: + if (!is_root) { + ret = LTTCOMM_KERN_NA; + goto error; + } + + /* 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; + } + } else { + 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 UST consumer daemons */ + /* 64-bit */ + pthread_mutex_lock(&ustconsumer64_data.pid_mutex); + if (consumerd64_bin[0] != '\0' && + ustconsumer64_data.pid == 0 && + cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { + pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); + ret = start_consumerd(&ustconsumer64_data); + if (ret < 0) { + ret = LTTCOMM_UST_CONSUMER64_FAIL; + ust_consumerd64_fd = -EINVAL; + goto error; + } + + ust_consumerd64_fd = ustconsumer64_data.cmd_sock; + } else { + pthread_mutex_unlock(&ustconsumer64_data.pid_mutex); + } + /* 32-bit */ + if (consumerd32_bin[0] != '\0' && + ustconsumer32_data.pid == 0 && + cmd_ctx->lsm->cmd_type != LTTNG_REGISTER_CONSUMER) { + pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); + ret = start_consumerd(&ustconsumer32_data); + if (ret < 0) { + ret = LTTCOMM_UST_CONSUMER32_FAIL; + ust_consumerd32_fd = -EINVAL; + goto error; + } + ust_consumerd32_fd = ustconsumer32_data.cmd_sock; + } else { + pthread_mutex_unlock(&ustconsumer32_data.pid_mutex); + } + } + break; + } + default: + break; + } + + /* + * Check that the UID or GID match that of the tracing session. + * The root user can interact with all sessions. + */ + if (need_tracing_session) { + if (!session_access_ok(cmd_ctx->session, + cmd_ctx->creds.uid, cmd_ctx->creds.gid)) { + ret = LTTCOMM_EPERM; + goto error; + } + } + + /* 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.type, + &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, &cmd_ctx->creds); + 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->lsm->domain.type, + 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: + { + ssize_t nb_event; + struct lttng_event *events = NULL; + + nb_event = cmd_list_events(cmd_ctx->lsm->domain.type, 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: + { + unsigned int nr_sessions; + + session_lock_list(); + nr_sessions = lttng_sessions_count(cmd_ctx->creds.uid, cmd_ctx->creds.gid); + if (nr_sessions == 0) { + ret = LTTCOMM_NO_SESSION; + session_unlock_list(); + goto error; + } + ret = setup_lttng_msg(cmd_ctx, sizeof(struct lttng_session) * nr_sessions); + if (ret < 0) { + session_unlock_list(); + goto setup_error; + } + + /* Filled the session array */ + list_lttng_sessions((struct lttng_session *)(cmd_ctx->llm->payload), + cmd_ctx->creds.uid, cmd_ctx->creds.gid); + + 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; + } + + /* Set socket option for credentials retrieval */ + ret = lttcomm_setsockopt_creds_unix_sock(sock); + if (ret < 0) { + goto error; + } + + /* Allocate context command to process the client request */ + cmd_ctx = zmalloc(sizeof(struct command_ctx)); + if (cmd_ctx == NULL) { + perror("zmalloc cmd_ctx"); + goto error; + } + + /* Allocate data buffer for reception */ + cmd_ctx->lsm = zmalloc(sizeof(struct lttcomm_session_msg)); + if (cmd_ctx->lsm == NULL) { + perror("zmalloc 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_creds_unix_sock(sock, cmd_ctx->lsm, + sizeof(struct lttcomm_session_msg), &cmd_ctx->creds); + 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 zmalloc 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"); + } + + /* End of transmission */ + close(sock); + + clean_command_ctx(&cmd_ctx); + } + +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, " --ustconsumerd32-err-sock PATH Specify path for the 32-bit UST consumer error socket\n"); + fprintf(stderr, " --ustconsumerd64-err-sock PATH Specify path for the 64-bit UST consumer error socket\n"); + fprintf(stderr, " --ustconsumerd32-cmd-sock PATH Specify path for the 32-bit UST consumer command socket\n"); + fprintf(stderr, " --ustconsumerd64-cmd-sock PATH Specify path for the 64-bit UST consumer command socket\n"); + fprintf(stderr, " --consumerd32-path PATH Specify path for the 32-bit UST consumer daemon binary\n"); + fprintf(stderr, " --consumerd32-libdir PATH Specify path for the 32-bit UST consumer daemon libraries\n"); + fprintf(stderr, " --consumerd64-path PATH Specify path for the 64-bit UST consumer daemon binary\n"); + fprintf(stderr, " --consumerd64-libdir PATH Specify path for the 64-bit UST consumer daemon libraries\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"); + fprintf(stderr, " --no-kernel Disable kernel tracer\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' }, + { "ustconsumerd32-cmd-sock", 1, 0, 'G' }, + { "ustconsumerd32-err-sock", 1, 0, 'H' }, + { "ustconsumerd64-cmd-sock", 1, 0, 'D' }, + { "ustconsumerd64-err-sock", 1, 0, 'F' }, + { "consumerd32-path", 1, 0, 'u' }, + { "consumerd32-libdir", 1, 0, 'U' }, + { "consumerd64-path", 1, 0, 't' }, + { "consumerd64-libdir", 1, 0, 'T' }, + { "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' }, + { "no-kernel", 0, 0, 'N' }, + { NULL, 0, 0, 0 } + }; + + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "dhqvVSN" "a:c:g:s:C:E:D:F:Z:u:t", + 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 = 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(ustconsumer64_data.err_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'D': + snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'H': + snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'G': + snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, "%s", optarg); + break; + case 'N': + opt_no_kernel = 1; + 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; + case 'u': + consumerd32_bin= optarg; + break; + case 'U': + consumerd32_libdir = optarg; + break; + case 't': + consumerd64_bin = optarg; + break; + case 'T': + consumerd64_libdir = optarg; + 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(char *rundir) +{ + int ret; + gid_t gid; + + gid = allowed_group(); + if (gid < 0) { + WARN("No tracing group detected"); + ret = 0; + goto end; + } + + /* Set lttng run dir */ + ret = chown(rundir, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", 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"); + } + + /* 64-bit ustconsumer error socket path */ + ret = chown(ustconsumer64_data.err_unix_sock_path, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", ustconsumer64_data.err_unix_sock_path); + perror("chown"); + } + + /* 32-bit ustconsumer compat32 error socket path */ + ret = chown(ustconsumer32_data.err_unix_sock_path, 0, gid); + if (ret < 0) { + ERR("Unable to set group on %s", ustconsumer32_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(const char *rundir) +{ + int ret; + + DBG3("Creating LTTng run directory: %s", rundir); + + ret = mkdir(rundir, S_IRWXU | S_IRWXG ); + if (ret < 0) { + if (errno != EEXIST) { + ERR("Unable to create %s", 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, + const char *rundir) +{ + int ret; + char path[PATH_MAX]; + + switch (consumer_data->type) { + case LTTNG_CONSUMER_KERNEL: + snprintf(path, PATH_MAX, KCONSUMERD_PATH, rundir); + break; + case LTTNG_CONSUMER64_UST: + snprintf(path, PATH_MAX, USTCONSUMERD64_PATH, rundir); + break; + case LTTNG_CONSUMER32_UST: + snprintf(path, PATH_MAX, USTCONSUMERD32_PATH, rundir); + break; + default: + ERR("Consumer type unknown"); + ret = -EINVAL; + goto error; + } + + DBG2("Creating consumer directory: %s", 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; + } + + setup_consumerd_path(); + + /* 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) { + rundir = strdup(LTTNG_RUNDIR); + + /* Create global run dir with root access */ + ret = create_lttng_rundir(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); + } + + /* Setup kernel consumerd path */ + snprintf(kconsumer_data.err_unix_sock_path, PATH_MAX, + KCONSUMERD_ERR_SOCK_PATH, rundir); + snprintf(kconsumer_data.cmd_unix_sock_path, PATH_MAX, + KCONSUMERD_CMD_SOCK_PATH, rundir); + + DBG2("Kernel consumer err path: %s", + kconsumer_data.err_unix_sock_path); + DBG2("Kernel consumer cmd path: %s", + kconsumer_data.cmd_unix_sock_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; + } + + /* + * Create rundir from home path. This will create something like + * $HOME/.lttng + */ + ret = asprintf(&rundir, LTTNG_HOME_RUNDIR, home_path); + if (ret < 0) { + ret = -ENOMEM; + goto error; + } + + ret = create_lttng_rundir(rundir); + if (ret < 0) { + 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); + DBG("LTTng run directory path: %s", rundir); + + /* 32 bits consumerd path setup */ + snprintf(ustconsumer32_data.err_unix_sock_path, PATH_MAX, + USTCONSUMERD32_ERR_SOCK_PATH, rundir); + snprintf(ustconsumer32_data.cmd_unix_sock_path, PATH_MAX, + USTCONSUMERD32_CMD_SOCK_PATH, rundir); + + DBG2("UST consumer 32 bits err path: %s", + ustconsumer32_data.err_unix_sock_path); + DBG2("UST consumer 32 bits cmd path: %s", + ustconsumer32_data.cmd_unix_sock_path); + + /* 64 bits consumerd path setup */ + snprintf(ustconsumer64_data.err_unix_sock_path, PATH_MAX, + USTCONSUMERD64_ERR_SOCK_PATH, rundir); + snprintf(ustconsumer64_data.cmd_unix_sock_path, PATH_MAX, + USTCONSUMERD64_CMD_SOCK_PATH, rundir); + + DBG2("UST consumer 64 bits err path: %s", + ustconsumer64_data.err_unix_sock_path); + DBG2("UST consumer 64 bits cmd path: %s", + ustconsumer64_data.cmd_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, rundir); + if (ret < 0) { + goto exit; + } + + /* Setup kernel tracer */ + if (!opt_no_kernel) { + init_kernel_tracer(); + } + + /* Set ulimit for open files */ + set_ulimit(); + } + + ret = set_consumer_sockets(&ustconsumer64_data, rundir); + if (ret < 0) { + goto exit; + } + + ret = set_consumer_sockets(&ustconsumer32_data, rundir); + if (ret < 0) { + goto exit; + } + + 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(rundir)) < 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/src/bin/lttng-sessiond/session.c b/src/bin/lttng-sessiond/session.c new file mode 100644 index 000000000..68edeb9ee --- /dev/null +++ b/src/bin/lttng-sessiond/session.c @@ -0,0 +1,251 @@ +/* + * 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 +#include + +#include "session.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. + * Returns the unique identifier for the session. + */ +static int add_session_list(struct ltt_session *ls) +{ + cds_list_add(&ls->list, <t_session_list.head); + return ++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, uid_t uid, gid_t gid) +{ + 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 = zmalloc(sizeof(struct ltt_session)); + if (new_session == NULL) { + perror("zmalloc"); + 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); + + new_session->uid = uid; + new_session->gid = gid; + + ret = run_as_mkdir_recursive(new_session->path, S_IRWXU | S_IRWXG, + new_session->uid, new_session->gid); + if (ret < 0) { + if (ret != -EEXIST) { + ERR("Trace directory creation error"); + ret = LTTCOMM_CREATE_FAIL; + goto error; + } + } + + /* Add new session to the session list */ + session_lock_list(); + new_session->id = add_session_list(new_session); + session_unlock_list(); + + DBG("Tracing session %s created in %s with ID %d by UID %d GID %d", + name, path, new_session->id, + new_session->uid, new_session->gid); + + return LTTCOMM_OK; + +error: +error_asprintf: + if (new_session != NULL) { + free(new_session); + } + +error_exist: +error_malloc: + return ret; +} diff --git a/src/bin/lttng-sessiond/session.h b/src/bin/lttng-sessiond/session.h new file mode 100644 index 000000000..6264e14ac --- /dev/null +++ b/src/bin/lttng-sessiond/session.h @@ -0,0 +1,90 @@ +/* + * 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 + +#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; + int enabled; /* enabled/started flag */ + int id; /* session unique identifier */ + /* UID/GID of the user owning the session */ + uid_t uid; + gid_t gid; +}; + +/* Prototypes */ +int session_create(char *name, char *path, uid_t uid, gid_t gid); +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/src/bin/lttng-sessiond/shm.c b/src/bin/lttng-sessiond/shm.c new file mode 100644 index 000000000..64c712e85 --- /dev/null +++ b/src/bin/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/src/bin/lttng-sessiond/shm.h b/src/bin/lttng-sessiond/shm.h new file mode 100644 index 000000000..2d301bbac --- /dev/null +++ b/src/bin/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/src/bin/lttng-sessiond/trace-kernel.c b/src/bin/lttng-sessiond/trace-kernel.c new file mode 100644 index 000000000..d1225c07c --- /dev/null +++ b/src/bin/lttng-sessiond/trace-kernel.c @@ -0,0 +1,407 @@ +/* + * 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 "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 = zmalloc(sizeof(struct ltt_kernel_session)); + if (lks == NULL) { + perror("create kernel session zmalloc"); + 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 = zmalloc(sizeof(struct ltt_kernel_channel)); + if (lkc == NULL) { + perror("ltt_kernel_channel zmalloc"); + goto error; + } + + lkc->channel = zmalloc(sizeof(struct lttng_channel)); + if (lkc->channel == NULL) { + perror("lttng_channel zmalloc"); + 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 = zmalloc(sizeof(struct ltt_kernel_event)); + attr = zmalloc(sizeof(struct lttng_kernel_event)); + if (lke == NULL || attr == NULL) { + perror("kernel event zmalloc"); + 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 = zmalloc(sizeof(struct ltt_kernel_metadata)); + chan = zmalloc(sizeof(struct lttng_channel)); + if (lkm == NULL || chan == NULL) { + perror("kernel metadata zmalloc"); + 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 = zmalloc(sizeof(struct ltt_kernel_stream)); + if (lks == NULL) { + perror("kernel stream zmalloc"); + 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/src/bin/lttng-sessiond/trace-kernel.h b/src/bin/lttng-sessiond/trace-kernel.h new file mode 100644 index 000000000..ae993c8d9 --- /dev/null +++ b/src/bin/lttng-sessiond/trace-kernel.h @@ -0,0 +1,132 @@ +/* + * 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; + /* UID/GID of the user owning the session */ + uid_t uid; + gid_t gid; +}; + +/* + * 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/src/bin/lttng-sessiond/trace-ust.c b/src/bin/lttng-sessiond/trace-ust.c new file mode 100644 index 000000000..a947dada5 --- /dev/null +++ b/src/bin/lttng-sessiond/trace-ust.c @@ -0,0 +1,512 @@ +/* + * 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 "trace-ust.h" + +/* + * Find the channel in the hashtable. + */ +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, + char *name) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + rcu_read_lock(); + lttng_ht_lookup(ht, (void *)name, &iter); + node = lttng_ht_iter_get_node_str(&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 lttng_ht *ht, + char *name) +{ + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + rcu_read_lock(); + lttng_ht_lookup(ht, (void *) name, &iter); + node = lttng_ht_iter_get_node_str(&iter); + if (node == NULL) { + rcu_read_unlock(); + goto error; + } + rcu_read_unlock(); + + DBG2("Trace UST event found by name %s", name); + + return caa_container_of(node, struct ltt_ust_event, node); + +error: + DBG2("Trace UST event NOT found by name %s", name); + 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, int session_id, + struct lttng_domain *domain) +{ + int ret; + struct ltt_ust_session *lus; + + /* Allocate a new ltt ust session */ + lus = zmalloc(sizeof(struct ltt_ust_session)); + if (lus == NULL) { + PERROR("create ust session zmalloc"); + goto error; + } + + /* Init data structure */ + lus->id = session_id; + lus->start_trace = 0; + + /* Alloc UST domain hash tables */ + lus->domain_pid = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lus->domain_exec = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + + /* Alloc UST global domain channels' HT */ + lus->domain_global.channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + /* Set session path */ + ret = snprintf(lus->pathname, PATH_MAX, "%s/ust", path); + if (ret < 0) { + PERROR("snprintf kernel traces path"); + goto error_free_session; + } + + DBG2("UST trace session create successful"); + + return lus; + +error_free_session: + free(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 = zmalloc(sizeof(struct ltt_ust_channel)); + if (luc == NULL) { + perror("ltt_ust_channel zmalloc"); + 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 */ + lttng_ht_node_init_str(&luc->node, luc->name); + /* Alloc hash tables */ + luc->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + luc->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + + /* Set trace output path */ + ret = snprintf(luc->pathname, PATH_MAX, "%s", path); + if (ret < 0) { + perror("asprintf ust create channel"); + goto error_free_channel; + } + + DBG2("Trace UST channel %s created", luc->name); + + return luc; + +error_free_channel: + free(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 = zmalloc(sizeof(struct ltt_ust_event)); + if (lue == NULL) { + PERROR("ust event zmalloc"); + 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; + case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: + lue->attr.instrumentation = LTTNG_UST_TRACEPOINT_LOGLEVEL; + break; + default: + ERR("Unknown ust instrumentation type (%d)", ev->type); + goto error_free_event; + } + + /* 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 */ + lttng_ht_node_init_str(&lue->node, lue->attr.name); + /* Alloc context hash tables */ + lue->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + + DBG2("Trace UST event %s created", lue->attr.name); + + return lue; + +error_free_event: + free(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 = zmalloc(sizeof(struct ltt_ust_metadata)); + if (lum == NULL) { + perror("ust metadata zmalloc"); + 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 = LTTNG_UST_MMAP; + + 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_free_metadata; + } + + return lum; + +error_free_metadata: + free(lum); +error: + return NULL; +} + +/* + * Allocate and initialize an UST context. + * + * Return pointer to structure or NULL. + */ +struct ltt_ust_context *trace_ust_create_context( + struct lttng_event_context *ctx) +{ + struct ltt_ust_context *uctx; + + uctx = zmalloc(sizeof(struct ltt_ust_context)); + if (uctx == NULL) { + PERROR("zmalloc ltt_ust_context"); + goto error; + } + + uctx->ctx.ctx = ctx->ctx; + lttng_ht_node_init_ulong(&uctx->node, (unsigned long) uctx->ctx.ctx); + + return uctx; + +error: + return NULL; +} + +/* + * RCU safe free context structure. + */ +static void destroy_context_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_ulong *node = + caa_container_of(head, struct lttng_ht_node_ulong, 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_contexts(struct lttng_ht *ht) +{ + int ret; + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + + cds_lfht_for_each_entry(ht->ht, &iter.iter, node, node) { + ret = lttng_ht_del(ht, &iter); + if (!ret) { + call_rcu(&node->head, destroy_context_rcu); + } + } + + lttng_ht_destroy(ht); +} + +/* + * Cleanup ust event structure. + */ +void trace_ust_destroy_event(struct ltt_ust_event *event) +{ + DBG2("Trace destroy UST event %s", event->attr.name); + destroy_contexts(event->ctx); + + free(event); +} + +/* + * URCU intermediate call to complete destroy event. + */ +static void destroy_event_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, head); + struct ltt_ust_event *event = + caa_container_of(node, struct ltt_ust_event, node); + + trace_ust_destroy_event(event); +} + +/* + * Cleanup UST events hashtable. + */ +static void destroy_events(struct lttng_ht *events) +{ + int ret; + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + cds_lfht_for_each_entry(events->ht, &iter.iter, node, node) { + ret = lttng_ht_del(events, &iter); + assert(!ret); + call_rcu(&node->head, destroy_event_rcu); + } + + lttng_ht_destroy(events); +} + +/* + * Cleanup ust channel structure. + */ +void trace_ust_destroy_channel(struct ltt_ust_channel *channel) +{ + DBG2("Trace destroy UST channel %s", channel->name); + + rcu_read_lock(); + + /* Destroying all events of the channel */ + destroy_events(channel->events); + /* Destroying all context of the channel */ + destroy_contexts(channel->ctx); + + free(channel); + + rcu_read_unlock(); +} + +/* + * URCU intermediate call to complete destroy channel. + */ +static void destroy_channel_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_str *node = + caa_container_of(head, struct lttng_ht_node_str, 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 lttng_ht *channels) +{ + int ret; + struct lttng_ht_node_str *node; + struct lttng_ht_iter iter; + + rcu_read_lock(); + + cds_lfht_for_each_entry(channels->ht, &iter.iter, node, node) { + ret = lttng_ht_del(channels, &iter); + assert(!ret); + call_rcu(&node->head, destroy_channel_rcu); + } + + lttng_ht_destroy(channels); + + rcu_read_unlock(); +} + +/* + * Cleanup UST pid domain. + */ +static void destroy_domain_pid(struct lttng_ht *ht) +{ + int ret; + struct lttng_ht_iter iter; + struct ltt_ust_domain_pid *dpid; + + cds_lfht_for_each_entry(ht->ht, &iter.iter, dpid, node.node) { + ret = lttng_ht_del(ht , &iter); + assert(!ret); + destroy_channels(dpid->channels); + } + + lttng_ht_destroy(ht); +} + +/* + * Cleanup UST exec name domain. + */ +static void destroy_domain_exec(struct lttng_ht *ht) +{ + int ret; + struct lttng_ht_iter iter; + struct ltt_ust_domain_exec *dexec; + + cds_lfht_for_each_entry(ht->ht, &iter.iter, dexec, node.node) { + ret = lttng_ht_del(ht , &iter); + assert(!ret); + destroy_channels(dexec->channels); + } + + lttng_ht_destroy(ht); +} + +/* + * 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 protection */ + if (session == NULL) { + return; + } + + rcu_read_lock(); + + DBG2("Trace UST destroy session %d", session->id); + + /* 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/src/bin/lttng-sessiond/trace-ust.h b/src/bin/lttng-sessiond/trace-ust.h new file mode 100644 index 000000000..7459218d3 --- /dev/null +++ b/src/bin/lttng-sessiond/trace-ust.h @@ -0,0 +1,216 @@ +/* + * 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 + +#include "ust-ctl.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 lttng_ht_node_ulong node; +}; + +/* UST event */ +struct ltt_ust_event { + unsigned int enabled; + struct lttng_ust_event attr; + struct lttng_ht *ctx; + struct lttng_ht_node_str node; +}; + +/* UST stream */ +struct ltt_ust_stream { + int handle; + char pathname[PATH_MAX]; + struct lttng_ust_object_data *obj; + /* Using a list of streams to keep order. */ + struct cds_list_head list; +}; + +/* UST channel */ +struct ltt_ust_channel { + unsigned int enabled; + char name[LTTNG_UST_SYM_NAME_LEN]; + char pathname[PATH_MAX]; + struct lttng_ust_channel attr; + struct lttng_ht *ctx; + struct lttng_ht *events; + struct lttng_ht_node_str 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 lttng_ht *channels; +}; + +/* UST domain pid (LTTNG_DOMAIN_UST_PID) */ +struct ltt_ust_domain_pid { + pid_t pid; + struct lttng_ht *channels; + struct lttng_ht_node_ulong node; +}; + +/* UST domain exec name (LTTNG_DOMAIN_UST_EXEC_NAME) */ +struct ltt_ust_domain_exec { + char exec_name[LTTNG_UST_SYM_NAME_LEN]; + struct lttng_ht *channels; + struct lttng_ht_node_str node; +}; + +/* UST session */ +struct ltt_ust_session { + int id; /* Unique identifier of session */ + int start_trace; + 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 lttng_ht *domain_pid; + struct lttng_ht *domain_exec; + /* UID/GID of the user owning the session */ + uid_t uid; + gid_t gid; +}; + +#ifdef HAVE_LIBLTTNG_UST_CTL + +/* + * Lookup functions. NULL is returned if not found. + */ +struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht, + char *name); +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *ht, + char *name); + +/* + * Create functions malloc() the data structure. + */ +struct ltt_ust_session *trace_ust_create_session(char *path, int session_id, + 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); +struct ltt_ust_context *trace_ust_create_context( + struct lttng_event_context *ctx); + +/* + * 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 /* HAVE_LIBLTTNG_UST_CTL */ + +static inline +struct ltt_ust_event *trace_ust_find_event_by_name(struct lttng_ht *ht, + char *name) +{ + return NULL; +} + +static inline +struct ltt_ust_channel *trace_ust_find_channel_by_name(struct lttng_ht *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) +{ +} +static inline +struct ltt_ust_context *trace_ust_create_context( + struct lttng_event_context *ctx) +{ + return NULL; +} + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +#endif /* _LTT_TRACE_UST_H */ diff --git a/src/bin/lttng-sessiond/ust-app.c b/src/bin/lttng-sessiond/ust-app.c new file mode 100644 index 000000000..057f675e4 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-app.c @@ -0,0 +1,2406 @@ +/* + * 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 +#include +#include + +#include "ust-app.h" +#include "ust-consumer.h" +#include "ust-ctl.h" + +/* + * Delete ust context safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_ctx(int sock, struct ust_app_ctx *ua_ctx) +{ + if (ua_ctx->obj) { + ustctl_release_object(sock, ua_ctx->obj); + free(ua_ctx->obj); + } + free(ua_ctx); +} + +/* + * Delete ust app event safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_event(int sock, struct ust_app_event *ua_event) +{ + int ret; + struct lttng_ht_iter iter; + struct ust_app_ctx *ua_ctx; + + /* Destroy each context of event */ + cds_lfht_for_each_entry(ua_event->ctx->ht, &iter.iter, ua_ctx, + node.node) { + ret = lttng_ht_del(ua_event->ctx, &iter); + assert(!ret); + delete_ust_app_ctx(sock, ua_ctx); + } + lttng_ht_destroy(ua_event->ctx); + + if (ua_event->obj != NULL) { + ustctl_release_object(sock, ua_event->obj); + free(ua_event->obj); + } + free(ua_event); +} + +/* + * Delete ust app stream safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_stream(int sock, struct ltt_ust_stream *stream) +{ + if (stream->obj) { + ustctl_release_object(sock, stream->obj); + free(stream->obj); + } + free(stream); +} + +/* + * Delete ust app channel safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_channel(int sock, struct ust_app_channel *ua_chan) +{ + int ret; + struct lttng_ht_iter iter; + struct ust_app_event *ua_event; + struct ust_app_ctx *ua_ctx; + struct ltt_ust_stream *stream, *stmp; + + /* Wipe stream */ + cds_list_for_each_entry_safe(stream, stmp, &ua_chan->streams.head, list) { + cds_list_del(&stream->list); + delete_ust_app_stream(sock, stream); + } + + /* Wipe context */ + cds_lfht_for_each_entry(ua_chan->ctx->ht, &iter.iter, ua_ctx, node.node) { + ret = lttng_ht_del(ua_chan->ctx, &iter); + assert(!ret); + delete_ust_app_ctx(sock, ua_ctx); + } + lttng_ht_destroy(ua_chan->ctx); + + /* Wipe events */ + cds_lfht_for_each_entry(ua_chan->events->ht, &iter.iter, ua_event, + node.node) { + ret = lttng_ht_del(ua_chan->events, &iter); + assert(!ret); + delete_ust_app_event(sock, ua_event); + } + lttng_ht_destroy(ua_chan->events); + + if (ua_chan->obj != NULL) { + ustctl_release_object(sock, ua_chan->obj); + free(ua_chan->obj); + } + free(ua_chan); +} + +/* + * Delete ust app session safely. RCU read lock must be held before calling + * this function. + */ +static +void delete_ust_app_session(int sock, struct ust_app_session *ua_sess) +{ + int ret; + struct lttng_ht_iter iter; + struct ust_app_channel *ua_chan; + + if (ua_sess->metadata) { + if (ua_sess->metadata->stream_obj) { + ustctl_release_object(sock, ua_sess->metadata->stream_obj); + free(ua_sess->metadata->stream_obj); + } + if (ua_sess->metadata->obj) { + ustctl_release_object(sock, ua_sess->metadata->obj); + free(ua_sess->metadata->obj); + } + } + + cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, + node.node) { + ret = lttng_ht_del(ua_sess->channels, &iter); + assert(!ret); + delete_ust_app_channel(sock, ua_chan); + } + lttng_ht_destroy(ua_sess->channels); + + if (ua_sess->handle != -1) { + ustctl_release_handle(sock, ua_sess->handle); + } + free(ua_sess); +} + +/* + * Delete a traceable application structure from the global list. Never call + * this function outside of a call_rcu call. + */ +static +void delete_ust_app(struct ust_app *app) +{ + int ret, sock; + struct lttng_ht_iter iter; + struct ust_app_session *ua_sess; + + rcu_read_lock(); + + /* Delete ust app sessions info */ + sock = app->key.sock; + app->key.sock = -1; + + /* Wipe sessions */ + cds_lfht_for_each_entry(app->sessions->ht, &iter.iter, ua_sess, + node.node) { + ret = lttng_ht_del(app->sessions, &iter); + assert(!ret); + delete_ust_app_session(app->key.sock, ua_sess); + } + lttng_ht_destroy(app->sessions); + + /* + * Wait until we have removed the key from the sock hash table before + * closing this socket, otherwise an application could re-use the socket ID + * and race with the teardown, using the same hash table entry. + */ + close(sock); + + DBG2("UST app pid %d deleted", app->key.pid); + free(app); + + rcu_read_unlock(); +} + +/* + * URCU intermediate call to delete an UST app. + */ +static +void delete_ust_app_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_ulong *node = + caa_container_of(head, struct lttng_ht_node_ulong, head); + struct ust_app *app = + caa_container_of(node, struct ust_app, node); + + DBG3("Call RCU deleting app PID %d", app->key.pid); + delete_ust_app(app); +} + +/* + * Alloc new UST app session. + */ +static +struct ust_app_session *alloc_ust_app_session(void) +{ + struct ust_app_session *ua_sess; + + /* Init most of the default value by allocating and zeroing */ + ua_sess = zmalloc(sizeof(struct ust_app_session)); + if (ua_sess == NULL) { + PERROR("malloc"); + goto error; + } + + ua_sess->handle = -1; + ua_sess->channels = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + + return ua_sess; + +error: + return NULL; +} + +/* + * Alloc new UST app channel. + */ +static +struct ust_app_channel *alloc_ust_app_channel(char *name, + struct lttng_ust_channel *attr) +{ + struct ust_app_channel *ua_chan; + + /* Init most of the default value by allocating and zeroing */ + ua_chan = zmalloc(sizeof(struct ust_app_channel)); + if (ua_chan == NULL) { + PERROR("malloc"); + goto error; + } + + /* Setup channel name */ + strncpy(ua_chan->name, name, sizeof(ua_chan->name)); + ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; + + ua_chan->enabled = 1; + ua_chan->handle = -1; + ua_chan->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + ua_chan->events = lttng_ht_new(0, LTTNG_HT_TYPE_STRING); + lttng_ht_node_init_str(&ua_chan->node, ua_chan->name); + + CDS_INIT_LIST_HEAD(&ua_chan->streams.head); + + /* Copy attributes */ + if (attr) { + memcpy(&ua_chan->attr, attr, sizeof(ua_chan->attr)); + } + + DBG3("UST app channel %s allocated", ua_chan->name); + + return ua_chan; + +error: + return NULL; +} + +/* + * Alloc new UST app event. + */ +static +struct ust_app_event *alloc_ust_app_event(char *name, + struct lttng_ust_event *attr) +{ + struct ust_app_event *ua_event; + + /* Init most of the default value by allocating and zeroing */ + ua_event = zmalloc(sizeof(struct ust_app_event)); + if (ua_event == NULL) { + PERROR("malloc"); + goto error; + } + + ua_event->enabled = 1; + strncpy(ua_event->name, name, sizeof(ua_event->name)); + ua_event->name[sizeof(ua_event->name) - 1] = '\0'; + ua_event->ctx = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + lttng_ht_node_init_str(&ua_event->node, ua_event->name); + + /* Copy attributes */ + if (attr) { + memcpy(&ua_event->attr, attr, sizeof(ua_event->attr)); + } + + DBG3("UST app event %s allocated", ua_event->name); + + return ua_event; + +error: + return NULL; +} + +/* + * Alloc new UST app context. + */ +static +struct ust_app_ctx *alloc_ust_app_ctx(struct lttng_ust_context *uctx) +{ + struct ust_app_ctx *ua_ctx; + + ua_ctx = zmalloc(sizeof(struct ust_app_ctx)); + if (ua_ctx == NULL) { + goto error; + } + + if (uctx) { + memcpy(&ua_ctx->ctx, uctx, sizeof(ua_ctx->ctx)); + } + + DBG3("UST app context %d allocated", ua_ctx->ctx.ctx); + +error: + return ua_ctx; +} + +/* + * Find an ust_app using the sock and return it. RCU read side lock must be + * held before calling this helper function. + */ +static +struct ust_app *find_app_by_sock(int sock) +{ + struct lttng_ht_node_ulong *node; + struct ust_app_key *key; + struct lttng_ht_iter iter; + + lttng_ht_lookup(ust_app_sock_key_map, (void *)((unsigned long) sock), + &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app find by sock %d key not found", sock); + goto error; + } + key = caa_container_of(node, struct ust_app_key, node); + + lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) key->pid), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app find by sock %d not found", sock); + goto error; + } + return caa_container_of(node, struct ust_app, node); + +error: + return NULL; +} + +/* + * Create the channel context on the tracer. + */ +static +int create_ust_channel_context(struct ust_app_channel *ua_chan, + struct ust_app_ctx *ua_ctx, struct ust_app *app) +{ + int ret; + + ret = ustctl_add_context(app->key.sock, &ua_ctx->ctx, + ua_chan->obj, &ua_ctx->obj); + if (ret < 0) { + goto error; + } + + ua_ctx->handle = ua_ctx->obj->handle; + + DBG2("UST app context added to channel %s successfully", ua_chan->name); + +error: + return ret; +} + +/* + * Create the event context on the tracer. + */ +static +int create_ust_event_context(struct ust_app_event *ua_event, + struct ust_app_ctx *ua_ctx, struct ust_app *app) +{ + int ret; + + ret = ustctl_add_context(app->key.sock, &ua_ctx->ctx, + ua_event->obj, &ua_ctx->obj); + if (ret < 0) { + goto error; + } + + ua_ctx->handle = ua_ctx->obj->handle; + + DBG2("UST app context added to event %s successfully", ua_event->name); + +error: + return ret; +} + +/* + * Disable the specified event on to UST tracer for the UST session. + */ +static int disable_ust_event(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +{ + int ret; + + ret = ustctl_disable(app->key.sock, ua_event->obj); + if (ret < 0) { + ERR("UST app event %s disable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_event->attr.name, app->key.pid, ua_sess->handle, ret); + goto error; + } + + DBG2("UST app event %s disabled successfully for app (pid: %d)", + ua_event->attr.name, app->key.pid); + +error: + return ret; +} + +/* + * Disable the specified channel on to UST tracer for the UST session. + */ +static int disable_ust_channel(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) +{ + int ret; + + ret = ustctl_disable(app->key.sock, ua_chan->obj); + if (ret < 0) { + ERR("UST app channel %s disable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_chan->name, app->key.pid, ua_sess->handle, ret); + goto error; + } + + DBG2("UST app channel %s disabled successfully for app (pid: %d)", + ua_chan->name, app->key.pid); + +error: + return ret; +} + +/* + * Enable the specified channel on to UST tracer for the UST session. + */ +static int enable_ust_channel(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) +{ + int ret; + + ret = ustctl_enable(app->key.sock, ua_chan->obj); + if (ret < 0) { + ERR("UST app channel %s enable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_chan->name, app->key.pid, ua_sess->handle, ret); + goto error; + } + + ua_chan->enabled = 1; + + DBG2("UST app channel %s enabled successfully for app (pid: %d)", + ua_chan->name, app->key.pid); + +error: + return ret; +} + +/* + * Enable the specified event on to UST tracer for the UST session. + */ +static int enable_ust_event(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_event *ua_event) +{ + int ret; + + ret = ustctl_enable(app->key.sock, ua_event->obj); + if (ret < 0) { + ERR("UST app event %s enable failed for app (pid: %d) " + "and session handle %d with ret %d", + ua_event->attr.name, app->key.pid, ua_sess->handle, ret); + goto error; + } + + DBG2("UST app event %s enabled successfully for app (pid: %d)", + ua_event->attr.name, app->key.pid); + +error: + return ret; +} + +/* + * Open metadata onto the UST tracer for a UST session. + */ +static int open_ust_metadata(struct ust_app *app, + struct ust_app_session *ua_sess) +{ + int ret; + struct lttng_ust_channel_attr uattr; + + 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 with ret %d", + app->key.pid, ret); + goto error; + } + + ua_sess->metadata->handle = ua_sess->metadata->obj->handle; + +error: + return ret; +} + +/* + * Create stream onto the UST tracer for a UST session. + */ +static int create_ust_stream(struct ust_app *app, + struct ust_app_session *ua_sess) +{ + int ret; + + 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 error; + } + +error: + return ret; +} + +/* + * Create the specified channel onto the UST tracer for a UST session. + */ +static int create_ust_channel(struct ust_app *app, + struct ust_app_session *ua_sess, struct ust_app_channel *ua_chan) +{ + int ret; + + /* TODO: remove cast and use lttng-ust-abi.h */ + ret = ustctl_create_channel(app->key.sock, ua_sess->handle, + (struct lttng_ust_channel_attr *)&ua_chan->attr, &ua_chan->obj); + if (ret < 0) { + ERR("Creating channel %s for app (pid: %d, sock: %d) " + "and session handle %d with ret %d", + ua_chan->name, app->key.pid, app->key.sock, + ua_sess->handle, ret); + goto error; + } + + ua_chan->handle = ua_chan->obj->handle; + + DBG2("UST app channel %s created successfully for pid:%d and sock:%d", + ua_chan->name, app->key.pid, app->key.sock); + + /* If channel is not enabled, disable it on the tracer */ + if (!ua_chan->enabled) { + ret = disable_ust_channel(app, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + } + +error: + return ret; +} + +/* + * Create the specified event onto the UST tracer for a UST session. + */ +static +int create_ust_event(struct ust_app *app, struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan, struct ust_app_event *ua_event) +{ + int ret = 0; + + /* Create UST event on tracer */ + ret = ustctl_create_event(app->key.sock, &ua_event->attr, ua_chan->obj, + &ua_event->obj); + if (ret < 0) { + if (ret == -EEXIST) { + ret = 0; + goto error; + } + ERR("Error ustctl create event %s for app pid: %d with ret %d", + ua_event->attr.name, app->key.pid, ret); + goto error; + } + + ua_event->handle = ua_event->obj->handle; + + DBG2("UST app event %s created successfully for pid:%d", + ua_event->attr.name, app->key.pid); + + /* If event not enabled, disable it on the tracer */ + if (ua_event->enabled == 0) { + ret = disable_ust_event(app, ua_sess, ua_event); + if (ret < 0) { + /* + * If we hit an EPERM, something is wrong with our disable call. If + * we get an EEXIST, there is a problem on the tracer side since we + * just created it. + */ + switch (ret) { + case -EPERM: + /* Code flow problem */ + assert(0); + case -EEXIST: + /* It's OK for our use case. */ + ret = 0; + break; + default: + break; + } + goto error; + } + } + +error: + return ret; +} + +/* + * Copy data between an UST app event and a LTT event. + */ +static void shadow_copy_event(struct ust_app_event *ua_event, + struct ltt_ust_event *uevent) +{ + struct lttng_ht_iter iter; + struct ltt_ust_context *uctx; + struct ust_app_ctx *ua_ctx; + + strncpy(ua_event->name, uevent->attr.name, sizeof(ua_event->name)); + ua_event->name[sizeof(ua_event->name) - 1] = '\0'; + + ua_event->enabled = uevent->enabled; + + /* Copy event attributes */ + memcpy(&ua_event->attr, &uevent->attr, sizeof(ua_event->attr)); + + cds_lfht_for_each_entry(uevent->ctx->ht, &iter.iter, uctx, node.node) { + ua_ctx = alloc_ust_app_ctx(&uctx->ctx); + if (ua_ctx == NULL) { + /* malloc() failed. We should simply stop */ + return; + } + + lttng_ht_node_init_ulong(&ua_ctx->node, + (unsigned long) ua_ctx->ctx.ctx); + lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node); + } +} + +/* + * Copy data between an UST app channel and a LTT channel. + */ +static void shadow_copy_channel(struct ust_app_channel *ua_chan, + struct ltt_ust_channel *uchan) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_event_node; + struct ltt_ust_event *uevent; + struct ltt_ust_context *uctx; + struct ust_app_event *ua_event; + struct ust_app_ctx *ua_ctx; + + DBG2("UST app shadow copy of channel %s started", ua_chan->name); + + strncpy(ua_chan->name, uchan->name, sizeof(ua_chan->name)); + ua_chan->name[sizeof(ua_chan->name) - 1] = '\0'; + /* Copy event attributes */ + memcpy(&ua_chan->attr, &uchan->attr, sizeof(ua_chan->attr)); + + ua_chan->enabled = uchan->enabled; + + cds_lfht_for_each_entry(uchan->ctx->ht, &iter.iter, uctx, node.node) { + ua_ctx = alloc_ust_app_ctx(&uctx->ctx); + if (ua_ctx == NULL) { + continue; + } + lttng_ht_node_init_ulong(&ua_ctx->node, + (unsigned long) ua_ctx->ctx.ctx); + lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node); + } + + /* Copy all events from ltt ust channel to ust app channel */ + cds_lfht_for_each_entry(uchan->events->ht, &iter.iter, uevent, node.node) { + struct lttng_ht_iter uiter; + + lttng_ht_lookup(ua_chan->events, (void *) uevent->attr.name, &uiter); + ua_event_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_event_node == NULL) { + DBG2("UST event %s not found on shadow copy channel", + uevent->attr.name); + ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); + if (ua_event == NULL) { + continue; + } + shadow_copy_event(ua_event, uevent); + lttng_ht_add_unique_str(ua_chan->events, &ua_event->node); + } + } + + DBG3("UST app shadow copy of channel %s done", ua_chan->name); +} + +/* + * Copy data between a UST app session and a regular LTT session. + */ +static void shadow_copy_session(struct ust_app_session *ua_sess, + struct ltt_ust_session *usess, struct ust_app *app) +{ + struct lttng_ht_node_str *ua_chan_node; + struct lttng_ht_iter iter; + struct ltt_ust_channel *uchan; + struct ust_app_channel *ua_chan; + time_t rawtime; + struct tm *timeinfo; + char datetime[16]; + int ret; + + /* Get date and time for unique app path */ + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); + + DBG2("Shadow copy of session handle %d", ua_sess->handle); + + ua_sess->id = usess->id; + ua_sess->uid = usess->uid; + ua_sess->gid = usess->gid; + + ret = snprintf(ua_sess->path, PATH_MAX, "%s/%s-%d-%s", usess->pathname, + app->name, app->key.pid, datetime); + if (ret < 0) { + PERROR("asprintf UST shadow copy session"); + /* TODO: We cannot return an error from here.. */ + assert(0); + } + + /* TODO: support all UST domain */ + + /* Iterate over all channels in global domain. */ + cds_lfht_for_each_entry(usess->domain_global.channels->ht, &iter.iter, + uchan, node.node) { + struct lttng_ht_iter uiter; + + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_chan_node != NULL) { + /* Session exist. Contiuing. */ + continue; + } + + DBG2("Channel %s not found on shadow session copy, creating it", + uchan->name); + ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); + if (ua_chan == NULL) { + /* malloc failed FIXME: Might want to do handle ENOMEM .. */ + continue; + } + + shadow_copy_channel(ua_chan, uchan); + lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); + } +} + +/* + * Lookup sesison wrapper. + */ +static +void __lookup_session_by_app(struct ltt_ust_session *usess, + struct ust_app *app, struct lttng_ht_iter *iter) +{ + /* Get right UST app session from app */ + lttng_ht_lookup(app->sessions, (void *)((unsigned long) usess->id), iter); +} + +/* + * Return ust app session from the app session hashtable using the UST session + * id. + */ +static struct ust_app_session *lookup_session_by_app( + struct ltt_ust_session *usess, struct ust_app *app) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + + __lookup_session_by_app(usess, app, &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + goto error; + } + + return caa_container_of(node, struct ust_app_session, node); + +error: + return NULL; +} + +/* + * Create a UST session onto the tracer of app and add it the session + * hashtable. + * + * Return ust app session or NULL on error. + */ +static struct ust_app_session *create_ust_app_session( + struct ltt_ust_session *usess, struct ust_app *app) +{ + int ret; + struct ust_app_session *ua_sess; + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + DBG2("UST app pid: %d session id %d not found, creating it", + app->key.pid, usess->id); + ua_sess = alloc_ust_app_session(); + if (ua_sess == NULL) { + /* Only malloc can failed so something is really wrong */ + goto end; + } + shadow_copy_session(ua_sess, usess, app); + } + + if (ua_sess->handle == -1) { + ret = ustctl_create_session(app->key.sock); + if (ret < 0) { + ERR("Creating session for app pid %d", app->key.pid); + goto error; + } + + ua_sess->handle = ret; + + /* Add ust app session to app's HT */ + lttng_ht_node_init_ulong(&ua_sess->node, (unsigned long) ua_sess->id); + lttng_ht_add_unique_ulong(app->sessions, &ua_sess->node); + + DBG2("UST app session created successfully with handle %d", ret); + } + +end: + return ua_sess; + +error: + delete_ust_app_session(-1, ua_sess); + return NULL; +} + +/* + * Create a context for the channel on the tracer. + */ +static +int create_ust_app_channel_context(struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan, struct lttng_ust_context *uctx, + struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + struct ust_app_ctx *ua_ctx; + + DBG2("UST app adding context to channel %s", ua_chan->name); + + lttng_ht_lookup(ua_chan->ctx, (void *)((unsigned long)uctx->ctx), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node != NULL) { + ret = -EEXIST; + goto error; + } + + ua_ctx = alloc_ust_app_ctx(uctx); + if (ua_ctx == NULL) { + /* malloc failed */ + ret = -1; + goto error; + } + + lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx); + lttng_ht_add_unique_ulong(ua_chan->ctx, &ua_ctx->node); + + ret = create_ust_channel_context(ua_chan, ua_ctx, app); + if (ret < 0) { + goto error; + } + +error: + return ret; +} + +/* + * Create an UST context and enable it for the event on the tracer. + */ +static +int create_ust_app_event_context(struct ust_app_session *ua_sess, + struct ust_app_event *ua_event, struct lttng_ust_context *uctx, + struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + struct ust_app_ctx *ua_ctx; + + DBG2("UST app adding context to event %s", ua_event->name); + + lttng_ht_lookup(ua_event->ctx, (void *)((unsigned long)uctx->ctx), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node != NULL) { + ret = -EEXIST; + goto error; + } + + ua_ctx = alloc_ust_app_ctx(uctx); + if (ua_ctx == NULL) { + /* malloc failed */ + ret = -1; + goto error; + } + + lttng_ht_node_init_ulong(&ua_ctx->node, (unsigned long) ua_ctx->ctx.ctx); + lttng_ht_add_unique_ulong(ua_event->ctx, &ua_ctx->node); + + ret = create_ust_event_context(ua_event, ua_ctx, app); + if (ret < 0) { + goto error; + } + +error: + return ret; +} + +/* + * Enable on the tracer side a ust app event for the session and channel. + */ +static +int enable_ust_app_event(struct ust_app_session *ua_sess, + struct ust_app_event *ua_event, struct ust_app *app) +{ + int ret; + + ret = enable_ust_event(app, ua_sess, ua_event); + if (ret < 0) { + goto error; + } + + ua_event->enabled = 1; + +error: + return ret; +} + +/* + * Disable on the tracer side a ust app event for the session and channel. + */ +static int disable_ust_app_event(struct ust_app_session *ua_sess, + struct ust_app_event *ua_event, struct ust_app *app) +{ + int ret; + + ret = disable_ust_event(app, ua_sess, ua_event); + if (ret < 0) { + goto error; + } + + ua_event->enabled = 0; + +error: + return ret; +} + +/* + * Lookup ust app channel for session and disable it on the tracer side. + */ +static +int disable_ust_app_channel(struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan, struct ust_app *app) +{ + int ret; + + ret = disable_ust_channel(app, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + + ua_chan->enabled = 0; + +error: + return ret; +} + +/* + * Lookup ust app channel for session and enable it on the tracer side. + */ +static int enable_ust_app_channel(struct ust_app_session *ua_sess, + struct ltt_ust_channel *uchan, struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app_channel *ua_chan; + + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); + ua_chan_node = lttng_ht_iter_get_node_str(&iter); + if (ua_chan_node == NULL) { + DBG2("Unable to find channel %s in ust session id %u", + uchan->name, ua_sess->id); + goto error; + } + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + ret = enable_ust_channel(app, ua_sess, ua_chan); + if (ret < 0) { + goto error; + } + +error: + return ret; +} + +/* + * Create UST app channel and create it on the tracer. + */ +static struct ust_app_channel *create_ust_app_channel( + struct ust_app_session *ua_sess, struct ltt_ust_channel *uchan, + struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app_channel *ua_chan; + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); + ua_chan_node = lttng_ht_iter_get_node_str(&iter); + if (ua_chan_node != NULL) { + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + goto end; + } + + ua_chan = alloc_ust_app_channel(uchan->name, &uchan->attr); + if (ua_chan == NULL) { + /* Only malloc can fail here */ + goto error; + } + shadow_copy_channel(ua_chan, uchan); + + ret = create_ust_channel(app, ua_sess, ua_chan); + if (ret < 0) { + /* Not found previously means that it does not exist on the tracer */ + assert(ret != -EEXIST); + goto error; + } + + lttng_ht_add_unique_str(ua_sess->channels, &ua_chan->node); + + DBG2("UST app create channel %s for PID %d completed", ua_chan->name, + app->key.pid); + +end: + return ua_chan; + +error: + delete_ust_app_channel(-1, ua_chan); + return NULL; +} + +/* + * Create UST app event and create it on the tracer side. + */ +static +int create_ust_app_event(struct ust_app_session *ua_sess, + struct ust_app_channel *ua_chan, struct ltt_ust_event *uevent, + struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_event_node; + struct ust_app_event *ua_event; + + /* Get event node */ + lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); + ua_event_node = lttng_ht_iter_get_node_str(&iter); + if (ua_event_node != NULL) { + ret = -EEXIST; + goto end; + } + + /* Does not exist so create one */ + ua_event = alloc_ust_app_event(uevent->attr.name, &uevent->attr); + if (ua_event == NULL) { + /* Only malloc can failed so something is really wrong */ + ret = -ENOMEM; + goto end; + } + shadow_copy_event(ua_event, uevent); + + /* Create it on the tracer side */ + ret = create_ust_event(app, ua_sess, ua_chan, ua_event); + if (ret < 0) { + /* Not found previously means that it does not exist on the tracer */ + assert(ret != -EEXIST); + goto error; + } + + lttng_ht_add_unique_str(ua_chan->events, &ua_event->node); + + DBG2("UST app create event %s for PID %d completed", ua_event->name, + app->key.pid); + +end: + return ret; + +error: + /* Valid. Calling here is already in a read side lock */ + delete_ust_app_event(-1, ua_event); + return ret; +} + +/* + * Create UST metadata and open it on the tracer side. + */ +static int create_ust_app_metadata(struct ust_app_session *ua_sess, + char *pathname, struct ust_app *app) +{ + int ret = 0; + + if (ua_sess->metadata == NULL) { + /* Allocate UST metadata */ + ua_sess->metadata = trace_ust_create_metadata(pathname); + if (ua_sess->metadata == NULL) { + /* malloc() failed */ + goto error; + } + + ret = open_ust_metadata(app, ua_sess); + if (ret < 0) { + DBG3("Opening metadata failed. Cleaning up memory"); + + /* Cleanup failed metadata struct */ + free(ua_sess->metadata); + /* + * This is very important because delete_ust_app_session check if + * the pointer is null or not in order to delete the metadata. + */ + ua_sess->metadata = NULL; + goto error; + } + + DBG2("UST metadata opened for app pid %d", app->key.pid); + } + + /* Open UST metadata stream */ + if (ua_sess->metadata->stream_obj == NULL) { + ret = create_ust_stream(app, ua_sess); + if (ret < 0) { + goto error; + } + + ret = run_as_mkdir(ua_sess->path, S_IRWXU | S_IRWXG, + ua_sess->uid, ua_sess->gid); + if (ret < 0) { + PERROR("mkdir UST metadata"); + goto error; + } + + ret = snprintf(ua_sess->metadata->pathname, PATH_MAX, + "%s/metadata", ua_sess->path); + if (ret < 0) { + PERROR("asprintf UST create stream"); + goto error; + } + + DBG2("UST metadata stream object created for app pid %d", + app->key.pid); + } else { + ERR("Attempting to create stream without metadata opened"); + goto error; + } + + return 0; + +error: + return -1; +} + +/* + * Return pointer to traceable apps list. + */ +struct lttng_ht *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 lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + + rcu_read_lock(); + lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) pid), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + DBG2("UST app no found with pid %d", pid); + goto error; + } + rcu_read_unlock(); + + DBG2("Found UST app by pid %d", pid); + + return caa_container_of(node, struct ust_app, node); + +error: + rcu_read_unlock(); + return NULL; +} + +/* + * Using pid and uid (of the app), allocate a new ust_app struct and + * add it to the global traceable app list. + * + * On success, return 0, else return malloc -ENOMEM, or -EINVAL if app + * bitness is not supported. + */ +int ust_app_register(struct ust_register_msg *msg, int sock) +{ + struct ust_app *lta; + + if ((msg->bits_per_long == 64 && ust_consumerd64_fd == -EINVAL) + || (msg->bits_per_long == 32 && ust_consumerd32_fd == -EINVAL)) { + ERR("Registration failed: application \"%s\" (pid: %d) has " + "%d-bit long, but no consumerd for this long size is available.\n", + msg->name, msg->pid, msg->bits_per_long); + close(sock); + return -EINVAL; + } + lta = zmalloc(sizeof(struct ust_app)); + if (lta == NULL) { + PERROR("malloc"); + return -ENOMEM; + } + + lta->ppid = msg->ppid; + lta->uid = msg->uid; + lta->gid = msg->gid; + lta->bits_per_long = msg->bits_per_long; + lta->v_major = msg->major; + lta->v_minor = msg->minor; + strncpy(lta->name, msg->name, sizeof(lta->name)); + lta->name[16] = '\0'; + lta->sessions = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + + /* Set key map */ + lta->key.pid = msg->pid; + lttng_ht_node_init_ulong(<a->node, (unsigned long)lta->key.pid); + lta->key.sock = sock; + lttng_ht_node_init_ulong(<a->key.node, (unsigned long)lta->key.sock); + + rcu_read_lock(); + lttng_ht_add_unique_ulong(ust_app_sock_key_map, <a->key.node); + lttng_ht_add_unique_ulong(ust_app_ht, <a->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; + struct lttng_ht_node_ulong *node; + struct lttng_ht_iter iter; + int ret; + + rcu_read_lock(); + lta = find_app_by_sock(sock); + if (lta == NULL) { + ERR("Unregister app sock %d not found!", sock); + goto error; + } + + DBG("PID %d unregistering with sock %d", lta->key.pid, sock); + + /* Remove application from socket hash table */ + lttng_ht_lookup(ust_app_sock_key_map, (void *)((unsigned long) sock), &iter); + ret = lttng_ht_del(ust_app_sock_key_map, &iter); + assert(!ret); + + /* Get the node reference for a call_rcu */ + lttng_ht_lookup(ust_app_ht, (void *)((unsigned long) lta->key.pid), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + ERR("Unable to find app sock %d by pid %d", sock, lta->key.pid); + goto error; + } + + /* Remove application from PID hash table */ + ret = lttng_ht_del(ust_app_ht, &iter); + assert(!ret); + call_rcu(&node->head, delete_ust_app_rcu); +error: + rcu_read_unlock(); + return; +} + +/* + * Return traceable_app_count + */ +unsigned long ust_app_list_count(void) +{ + unsigned long count; + + rcu_read_lock(); + count = lttng_ht_get_count(ust_app_ht); + rcu_read_unlock(); + + return count; +} + +/* + * Fill events array with all events name of all registered apps. + */ +int ust_app_list_events(struct lttng_event **events) +{ + int ret, handle; + size_t nbmem, count = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct lttng_event *tmp; + + nbmem = UST_APP_EVENT_LIST_SIZE; + tmp = zmalloc(nbmem * sizeof(struct lttng_event)); + if (tmp == NULL) { + PERROR("zmalloc ust app events"); + ret = -ENOMEM; + goto error; + } + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + struct lttng_ust_tracepoint_iter uiter; + + handle = ustctl_tracepoint_list(app->key.sock); + if (handle < 0) { + ERR("UST app list events getting handle failed for app pid %d", + app->key.pid); + continue; + } + + while ((ret = ustctl_tracepoint_list_get(app->key.sock, handle, + &uiter)) != -ENOENT) { + if (count >= nbmem) { + DBG2("Reallocating event list from %zu to %zu entries", nbmem, + 2 * nbmem); + nbmem *= 2; + tmp = realloc(tmp, nbmem * sizeof(struct lttng_event)); + if (tmp == NULL) { + PERROR("realloc ust app events"); + ret = -ENOMEM; + goto rcu_error; + } + } + memcpy(tmp[count].name, uiter.name, LTTNG_UST_SYM_NAME_LEN); + memcpy(tmp[count].loglevel, uiter.loglevel, LTTNG_UST_SYM_NAME_LEN); + tmp[count].loglevel_value = uiter.loglevel_value; + tmp[count].type = LTTNG_UST_TRACEPOINT; + tmp[count].pid = app->key.pid; + tmp[count].enabled = -1; + count++; + } + } + + ret = count; + *events = tmp; + + DBG2("UST app list events done (%zu events)", count); + +rcu_error: + rcu_read_unlock(); +error: + return ret; +} + +/* + * Free and clean all traceable apps of the global list. + */ +void ust_app_clean_list(void) +{ + int ret; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + + DBG2("UST app cleaning registered apps hash table"); + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, node, node) { + ret = lttng_ht_del(ust_app_ht, &iter); + assert(!ret); + call_rcu(&node->head, delete_ust_app_rcu); + } + /* Destroy is done only when the ht is empty */ + lttng_ht_destroy(ust_app_ht); + + cds_lfht_for_each_entry(ust_app_sock_key_map->ht, &iter.iter, node, node) { + ret = lttng_ht_del(ust_app_sock_key_map, &iter); + assert(!ret); + } + /* Destroy is done only when the ht is empty */ + lttng_ht_destroy(ust_app_sock_key_map); + + rcu_read_unlock(); +} + +/* + * Init UST app hash table. + */ +void ust_app_ht_alloc(void) +{ + ust_app_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + ust_app_sock_key_map = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); +} + +/* + * For a specific UST session, disable the channel for all registered apps. + */ +int ust_app_disable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + if (usess == NULL || uchan == NULL) { + ERR("Disabling UST global channel with NULL values"); + ret = -1; + goto error; + } + + DBG2("UST app disabling channel %s from global domain for session id %d", + uchan->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + struct lttng_ht_iter uiter; + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Get channel */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the session if found for the app, the channel must be there */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + /* The channel must not be already disabled */ + assert(ua_chan->enabled == 1); + + /* Disable channel onto application */ + ret = disable_ust_app_channel(ua_sess, ua_chan, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + +error: + return ret; +} + +/* + * For a specific UST session, enable the channel for all registered apps. + */ +int ust_app_enable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + + if (usess == NULL || uchan == NULL) { + ERR("Adding UST global channel to NULL values"); + ret = -1; + goto error; + } + + DBG2("UST app enabling channel %s to global domain for session id %d", + uchan->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Enable channel onto application */ + ret = enable_ust_app_channel(ua_sess, uchan, app); + if (ret < 0) { + /* XXX: We might want to report this error at some point... */ + continue; + } + } + + rcu_read_unlock(); + +error: + return ret; +} + +/* + * Disable an event in a channel and for a specific session. + */ +int ust_app_disable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *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; + + DBG("UST app disabling event %s for all apps in channel " + "%s for session id %d", uevent->attr.name, uchan->name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + /* Next app */ + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_chan_node == NULL) { + DBG2("Channel %s not found in session id %d for app pid %d." + "Skipping", uchan->name, usess->id, app->key.pid); + continue; + } + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter); + ua_event_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_event_node == NULL) { + DBG2("Event %s not found in channel %s for app pid %d." + "Skipping", uevent->attr.name, uchan->name, app->key.pid); + continue; + } + ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); + + ret = disable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + /* XXX: Report error someday... */ + continue; + } + } + + rcu_read_unlock(); + + return ret; +} + +/* + * For a specific UST session and UST channel, the event for all + * registered apps. + */ +int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + struct ust_app_event *ua_event; + + DBG("UST app disabling all event for all apps in channel " + "%s for session id %d", uchan->name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + /* If ua_sess is NULL, there is a code flow error */ + assert(ua_sess); + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + /* Disable each events of channel */ + cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event, + node.node) { + ret = disable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + /* XXX: Report error someday... */ + continue; + } + } + } + + rcu_read_unlock(); + + return ret; +} + +/* + * For a specific UST session, create the channel for all registered apps. + */ +int ust_app_create_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + struct lttng_ht_iter iter; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + /* Very wrong code flow */ + assert(usess); + assert(uchan); + + DBG2("UST app adding channel %s to global domain for session id %d", + uchan->name, usess->id); + + rcu_read_lock(); + + /* For every registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + /* + * Create session on the tracer side and add it to app session HT. Note + * that if session exist, it will simply return a pointer to the ust + * app session. + */ + ua_sess = create_ust_app_session(usess, app); + if (ua_sess == NULL) { + /* Major problem here and it's maybe the tracer or malloc() */ + goto error; + } + + /* Create channel onto application */ + ua_chan = create_ust_app_channel(ua_sess, uchan, app); + if (ua_chan == NULL) { + /* Major problem here and it's maybe the tracer or malloc() */ + goto error; + } + } + + rcu_read_unlock(); + + return 0; + +error: + return -1; +} + +/* + * Enable event for a specific session and channel on the tracer. + */ +int ust_app_enable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *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; + + DBG("UST app enabling event %s for all apps for session id %d", + uevent->attr.name, usess->id); + + /* + * NOTE: At this point, this function is called only if the session and + * channel passed are already created for all apps. and enabled on the + * tracer also. + */ + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + /* If ua_sess is NULL, there is a code flow error */ + assert(ua_sess); + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + lttng_ht_lookup(ua_chan->events, (void*)uevent->attr.name, &uiter); + ua_event_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_event_node == NULL) { + DBG3("UST app enable event %s not found for app PID %d." + "Skipping app", uevent->attr.name, app->key.pid); + continue; + } + ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + goto error; + } + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * For a specific existing UST session and UST channel, creates the event for + * all registered apps. + */ +int ust_app_create_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct lttng_ht_node_str *ua_chan_node; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + DBG("UST app creating event %s for all apps for session id %d", + uevent->attr.name, usess->id); + + rcu_read_lock(); + + /* For all registered applications */ + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + /* If ua_sess is NULL, there is a code flow error */ + assert(ua_sess); + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + if (ret < 0) { + if (ret != -EEXIST) { + /* Possible value at this point: -ENOMEM. If so, we stop! */ + break; + } + DBG2("UST app event %s already exist on app PID %d", + uevent->attr.name, app->key.pid); + continue; + } + } + + rcu_read_unlock(); + + return ret; +} + +/* + * Start tracing for a specific UST session and app. + */ +int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + struct ltt_ust_stream *ustream; + int consumerd_fd; + + DBG("Starting tracing for ust app pid %d", app->key.pid); + + rcu_read_lock(); + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + goto error_rcu_unlock; + } + + /* Upon restart, we skip the setup, already done */ + if (ua_sess->started) { + goto skip_setup; + } + + ret = create_ust_app_metadata(ua_sess, usess->pathname, app); + if (ret < 0) { + goto error_rcu_unlock; + } + + /* For each channel */ + cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, + node.node) { + /* Create all streams */ + while (1) { + /* Create UST stream */ + ustream = zmalloc(sizeof(*ustream)); + if (ustream == NULL) { + PERROR("zmalloc ust stream"); + goto error_rcu_unlock; + } + + ret = ustctl_create_stream(app->key.sock, ua_chan->obj, + &ustream->obj); + if (ret < 0) { + /* Got all streams */ + break; + } + ustream->handle = ustream->obj->handle; + + /* Order is important */ + cds_list_add_tail(&ustream->list, &ua_chan->streams.head); + ret = snprintf(ustream->pathname, PATH_MAX, "%s/%s_%u", + ua_sess->path, ua_chan->name, + ua_chan->streams.count++); + if (ret < 0) { + PERROR("asprintf UST create stream"); + continue; + } + DBG2("UST stream %d ready at %s", ua_chan->streams.count, + ustream->pathname); + } + } + + switch (app->bits_per_long) { + case 64: + consumerd_fd = ust_consumerd64_fd; + break; + case 32: + consumerd_fd = ust_consumerd32_fd; + break; + default: + ret = -EINVAL; + goto error_rcu_unlock; + } + + /* Setup UST consumer socket and send fds to it */ + ret = ust_consumer_send_session(consumerd_fd, ua_sess); + if (ret < 0) { + goto error_rcu_unlock; + } + ua_sess->started = 1; + +skip_setup: + /* 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 error_rcu_unlock; + } + + rcu_read_unlock(); + + /* Quiescent wait after starting trace */ + ustctl_wait_quiescent(app->key.sock); + + return 0; + +error_rcu_unlock: + rcu_read_unlock(); + return -1; +} + +/* + * Stop tracing for a specific UST session and app. + */ +int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + + DBG("Stopping tracing for ust app pid %d", app->key.pid); + + rcu_read_lock(); + + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + /* Only malloc can failed so something is really wrong */ + goto error_rcu_unlock; + } + + /* Not started, continuing. */ + if (ua_sess->started == 0) { + goto end; + } + + /* This inhibits UST tracing */ + ret = ustctl_stop_session(app->key.sock, ua_sess->handle); + if (ret < 0) { + ERR("Error stopping tracing for app pid: %d", app->key.pid); + goto error_rcu_unlock; + } + + /* Quiescent wait after stopping trace */ + ustctl_wait_quiescent(app->key.sock); + + /* Flushing buffers */ + cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, + node.node) { + ret = ustctl_sock_flush_buffer(app->key.sock, ua_chan->obj); + if (ret < 0) { + ERR("UST app PID %d channel %s flush failed with ret %d", + app->key.pid, ua_chan->name, ret); + /* Continuing flushing all buffers */ + continue; + } + } + + /* Flush all buffers before stopping */ + ret = ustctl_sock_flush_buffer(app->key.sock, ua_sess->metadata->obj); + if (ret < 0) { + ERR("UST app PID %d metadata flush failed with ret %d", app->key.pid, + ret); + } + + ua_sess->started = 0; + +end: + rcu_read_unlock(); + return 0; + +error_rcu_unlock: + rcu_read_unlock(); + return -1; +} + +/* + * Destroy a specific UST session in apps. + */ +int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app) +{ + struct ust_app_session *ua_sess; + struct lttng_ust_object_data obj; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + int ret; + + DBG("Destroy tracing for ust app pid %d", app->key.pid); + + rcu_read_lock(); + + __lookup_session_by_app(usess, app, &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node == NULL) { + /* Only malloc can failed so something is really wrong */ + goto error_rcu_unlock; + } + ua_sess = caa_container_of(node, struct ust_app_session, node); + ret = lttng_ht_del(app->sessions, &iter); + assert(!ret); + obj.handle = ua_sess->handle; + obj.shm_fd = -1; + obj.wait_fd = -1; + obj.memory_map_size = 0; + ustctl_release_object(app->key.sock, &obj); + + delete_ust_app_session(app->key.sock, ua_sess); + + rcu_read_unlock(); + + /* Quiescent wait after stopping trace */ + ustctl_wait_quiescent(app->key.sock); + + return 0; + +error_rcu_unlock: + rcu_read_unlock(); + return -1; +} + +/* + * Start tracing for the UST session. + */ +int ust_app_start_trace_all(struct ltt_ust_session *usess) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + + DBG("Starting all UST traces"); + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ret = ust_app_start_trace(usess, app); + if (ret < 0) { + /* Continue to next apps even on error */ + continue; + } + } + + rcu_read_unlock(); + + return 0; +} + +/* + * Start tracing for the UST session. + */ +int ust_app_stop_trace_all(struct ltt_ust_session *usess) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + + DBG("Stopping all UST traces"); + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ret = ust_app_stop_trace(usess, app); + if (ret < 0) { + /* Continue to next apps even on error */ + continue; + } + } + + rcu_read_unlock(); + + return 0; +} + +/* + * Destroy app UST session. + */ +int ust_app_destroy_trace_all(struct ltt_ust_session *usess) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct ust_app *app; + + DBG("Destroy all UST traces"); + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ret = ust_app_destroy_trace(usess, app); + if (ret < 0) { + /* Continue to next apps even on error */ + continue; + } + } + + rcu_read_unlock(); + + return 0; +} + +/* + * Add channels/events from UST global domain to registered apps at sock. + */ +void ust_app_global_update(struct ltt_ust_session *usess, int sock) +{ + int ret = 0; + struct lttng_ht_iter iter, uiter; + struct ust_app *app; + struct ust_app_session *ua_sess; + struct ust_app_channel *ua_chan; + struct ust_app_event *ua_event; + + if (usess == NULL) { + ERR("No UST session on global update. Returning"); + goto error; + } + + DBG2("UST app global update for app sock %d for session id %d", sock, + usess->id); + + rcu_read_lock(); + + app = find_app_by_sock(sock); + if (app == NULL) { + ERR("Failed to update app sock %d", sock); + goto error; + } + + ua_sess = create_ust_app_session(usess, app); + if (ua_sess == NULL) { + goto error; + } + + /* + * We can iterate safely here over all UST app session sicne the create ust + * app session above made a shadow copy of the UST global domain from the + * ltt ust session. + */ + cds_lfht_for_each_entry(ua_sess->channels->ht, &iter.iter, ua_chan, + node.node) { + ret = create_ust_channel(app, ua_sess, ua_chan); + if (ret < 0) { + /* FIXME: Should we quit here or continue... */ + continue; + } + + /* For each events */ + cds_lfht_for_each_entry(ua_chan->events->ht, &uiter.iter, ua_event, + node.node) { + ret = create_ust_event(app, ua_sess, ua_chan, ua_event); + if (ret < 0) { + /* FIXME: Should we quit here or continue... */ + continue; + } + } + } + + if (usess->start_trace) { + ret = ust_app_start_trace(usess, app); + if (ret < 0) { + goto error; + } + + DBG2("UST trace started for app pid %d", app->key.pid); + } + +error: + rcu_read_unlock(); + return; +} + +/* + * Add context to a specific channel for global UST domain. + */ +int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) +{ + int ret = 0; + struct lttng_ht_node_str *ua_chan_node; + struct lttng_ht_iter iter, uiter; + struct ust_app_channel *ua_chan = NULL; + struct ust_app_session *ua_sess; + struct ust_app *app; + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_chan_node == NULL) { + continue; + } + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, + node); + + ret = create_ust_app_channel_context(ua_sess, ua_chan, &uctx->ctx, app); + if (ret < 0) { + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * Add context to a specific event in a channel for global UST domain. + */ +int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + struct ltt_ust_context *uctx) +{ + int ret = 0; + struct lttng_ht_node_str *ua_chan_node, *ua_event_node; + struct lttng_ht_iter iter, uiter; + struct ust_app_session *ua_sess; + struct ust_app_event *ua_event; + struct ust_app_channel *ua_chan = NULL; + struct ust_app *app; + + rcu_read_lock(); + + cds_lfht_for_each_entry(ust_app_ht->ht, &iter.iter, app, node.node) { + ua_sess = lookup_session_by_app(usess, app); + if (ua_sess == NULL) { + continue; + } + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &uiter); + ua_chan_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_chan_node == NULL) { + continue; + } + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, + node); + + lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &uiter); + ua_event_node = lttng_ht_iter_get_node_str(&uiter); + if (ua_event_node == NULL) { + continue; + } + ua_event = caa_container_of(ua_event_node, struct ust_app_event, + node); + + ret = create_ust_app_event_context(ua_sess, ua_event, &uctx->ctx, app); + if (ret < 0) { + continue; + } + } + + rcu_read_unlock(); + return ret; +} + +/* + * Enable event for a channel from a UST session for a specific PID. + */ +int ust_app_enable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *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; + + DBG("UST app enabling event %s for PID %d", uevent->attr.name, pid); + + rcu_read_lock(); + + app = ust_app_find_by_pid(pid); + if (app == NULL) { + ERR("UST app enable event per PID %d not found", pid); + ret = -1; + goto error; + } + + ua_sess = lookup_session_by_app(usess, app); + /* If ua_sess is NULL, there is a code flow error */ + assert(ua_sess); + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); + ua_chan_node = lttng_ht_iter_get_node_str(&iter); + /* If the channel is not found, there is a code flow error */ + assert(ua_chan_node); + + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); + ua_event_node = lttng_ht_iter_get_node_str(&iter); + if (ua_event_node == NULL) { + ret = create_ust_app_event(ua_sess, ua_chan, uevent, app); + if (ret < 0) { + goto error; + } + } else { + ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); + + ret = enable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + goto error; + } + } + +error: + rcu_read_unlock(); + return ret; +} + +/* + * Disable event for a channel from a UST session for a specific PID. + */ +int ust_app_disable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, pid_t pid) +{ + int ret = 0; + struct lttng_ht_iter iter; + struct lttng_ht_node_str *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; + + DBG("UST app disabling event %s for PID %d", uevent->attr.name, pid); + + rcu_read_lock(); + + app = ust_app_find_by_pid(pid); + if (app == NULL) { + ERR("UST app disable event per PID %d not found", pid); + ret = -1; + goto error; + } + + ua_sess = lookup_session_by_app(usess, app); + /* If ua_sess is NULL, there is a code flow error */ + assert(ua_sess); + + /* Lookup channel in the ust app session */ + lttng_ht_lookup(ua_sess->channels, (void *)uchan->name, &iter); + ua_chan_node = lttng_ht_iter_get_node_str(&iter); + if (ua_chan_node == NULL) { + /* Channel does not exist, skip disabling */ + goto error; + } + ua_chan = caa_container_of(ua_chan_node, struct ust_app_channel, node); + + lttng_ht_lookup(ua_chan->events, (void *)uevent->attr.name, &iter); + ua_event_node = lttng_ht_iter_get_node_str(&iter); + if (ua_event_node == NULL) { + /* Event does not exist, skip disabling */ + goto error; + } + ua_event = caa_container_of(ua_event_node, struct ust_app_event, node); + + ret = disable_ust_app_event(ua_sess, ua_event, app); + if (ret < 0) { + goto error; + } + +error: + rcu_read_unlock(); + return ret; +} diff --git a/src/bin/lttng-sessiond/ust-app.h b/src/bin/lttng-sessiond/ust-app.h new file mode 100644 index 000000000..d7fbcd5d0 --- /dev/null +++ b/src/bin/lttng-sessiond/ust-app.h @@ -0,0 +1,326 @@ +/* + * 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 "trace-ust.h" + +#define UST_APP_EVENT_LIST_SIZE 32 + +extern int ust_consumerd64_fd, ust_consumerd32_fd; + +/* + * 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; + uint32_t bits_per_long; + char name[16]; +}; + +/* + * Global applications HT used by the session daemon. + */ +struct lttng_ht *ust_app_ht; + +struct lttng_ht *ust_app_sock_key_map; + +struct ust_app_key { + pid_t pid; + int sock; + struct lttng_ht_node_ulong node; +}; + +struct ust_app_ctx { + int handle; + struct lttng_ust_context ctx; + struct lttng_ust_object_data *obj; + struct lttng_ht_node_ulong node; +}; + +struct ust_app_event { + int enabled; + int handle; + struct lttng_ust_object_data *obj; + struct lttng_ust_event attr; + char name[LTTNG_UST_SYM_NAME_LEN]; + struct lttng_ht *ctx; + struct lttng_ht_node_str 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 ltt_ust_stream_list streams; + struct lttng_ht *ctx; + struct lttng_ht *events; + struct lttng_ht_node_str node; +}; + +struct ust_app_session { + int enabled; + /* started: has the session been in started state at any time ? */ + int started; /* allows detection of start vs restart. */ + int handle; /* used has unique identifier for app session */ + int id; /* session unique identifier */ + struct ltt_ust_metadata *metadata; + struct lttng_ht *channels; /* Registered channels */ + struct lttng_ht_node_ulong node; + char path[PATH_MAX]; + /* UID/GID of the user owning the session */ + uid_t uid; + gid_t gid; +}; + +/* + * 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 */ + int bits_per_long; + uint32_t v_major; /* Verion major number */ + uint32_t v_minor; /* Verion minor number */ + char name[17]; /* Process name (short) */ + struct lttng_ht *sessions; + struct lttng_ht_node_ulong node; + struct ust_app_key key; +}; + +#ifdef HAVE_LIBLTTNG_UST_CTL + +int ust_app_register(struct ust_register_msg *msg, int sock); +static inline +int ust_app_register_done(int sock) +{ + return ustctl_register_done(sock); +} +void ust_app_unregister(int sock); +unsigned long ust_app_list_count(void); +int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app); +int ust_app_stop_trace(struct ltt_ust_session *usess, struct ust_app *app); +int ust_app_start_trace_all(struct ltt_ust_session *usess); +int ust_app_stop_trace_all(struct ltt_ust_session *usess); +int ust_app_destroy_trace(struct ltt_ust_session *usess, struct ust_app *app); +int ust_app_destroy_trace_all(struct ltt_ust_session *usess); +int ust_app_list_events(struct lttng_event **events); +int ust_app_create_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_create_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +int ust_app_disable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + pid_t pid); +int ust_app_enable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + pid_t pid); +int ust_app_disable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_enable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_enable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_enable_all_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan); +int ust_app_disable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent); +int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + struct ltt_ust_context *uctx); +int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx); +void ust_app_global_update(struct ltt_ust_session *usess, int sock); + +void ust_app_clean_list(void); +void ust_app_ht_alloc(void); +struct lttng_ht *ust_app_get_ht(void); +struct ust_app *ust_app_find_by_pid(pid_t pid); + +#else /* HAVE_LIBLTTNG_UST_CTL */ + +static inline +int ust_app_destroy_trace_all(struct ltt_ust_session *usess) +{ + return 0; +} +static inline +int ust_app_start_trace(struct ltt_ust_session *usess, struct ust_app *app) +{ + return 0; +} +static inline +int ust_app_start_trace_all(struct ltt_ust_session *usess) +{ + return 0; +} +static inline +int ust_app_stop_trace_all(struct ltt_ust_session *usess) +{ + return 0; +} +static inline +int ust_app_list_events(struct lttng_event **events) +{ + return 0; +} +static inline +int ust_app_register(struct ust_register_msg *msg, int sock) +{ + return -ENOSYS; +} +static inline +int ust_app_register_done(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 +struct lttng_ht *ust_app_get_ht(void) +{ + return NULL; +} +static inline +void ust_app_ht_alloc(void) +{} +static inline +void ust_app_global_update(struct ltt_ust_session *usess, int sock) +{} +static inline +int ust_app_disable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} +static inline +int ust_app_enable_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} +static inline +int ust_app_create_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} +static inline +int ust_app_disable_all_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} +static inline +int ust_app_enable_all_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan) +{ + return 0; +} +static inline +int ust_app_create_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_disable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_enable_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent) +{ + return 0; +} +static inline +int ust_app_add_ctx_event_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + struct ltt_ust_context *uctx) +{ + return 0; +} +static inline +int ust_app_add_ctx_channel_glb(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_context *uctx) +{ + return 0; +} +static inline +int ust_app_enable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + pid_t pid) +{ + return 0; +} +static inline +int ust_app_disable_event_pid(struct ltt_ust_session *usess, + struct ltt_ust_channel *uchan, struct ltt_ust_event *uevent, + pid_t pid) +{ + return 0; +} + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +#endif /* _LTT_UST_APP_H */ diff --git a/src/bin/lttng-sessiond/ust-consumer.c b/src/bin/lttng-sessiond/ust-consumer.c new file mode 100644 index 000000000..c0d5069e7 --- /dev/null +++ b/src/bin/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 "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, + uid_t uid, gid_t gid) +{ + int ret, fd; + struct lttcomm_consumer_msg lum; + struct ltt_ust_stream *stream, *tmp; + + 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 while we transfer the stream file + * descriptors to make sure this key stays unique within the + * session daemon. We can free the channel shm_fd without + * problem after we finished sending stream fds for that + * channel. + */ + 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; + } + fd = uchan->obj->shm_fd; + ret = lttcomm_send_fds_unix_sock(sock, &fd, 1); + if (ret < 0) { + perror("send consumer channel ancillary data"); + goto error; + } + + cds_list_for_each_entry_safe(stream, tmp, &uchan->streams.head, list) { + int fds[2]; + + if (!stream->obj->shm_fd) { + continue; + } + 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; + /* + * FIXME Hack alert! we force MMAP for now. Mixup + * between EVENT and UST enums elsewhere. + */ + lum.u.stream.output = DEFAULT_UST_CHANNEL_OUTPUT; + lum.u.stream.mmap_len = stream->obj->memory_map_size; + lum.u.stream.uid = uid; + lum.u.stream.gid = gid; + 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; + } + } + + 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 lttng_ht_iter iter; + struct lttcomm_consumer_msg lum; + struct ust_app_channel *ua_chan; + + DBG("Sending metadata stream fd"); + + if (consumer_fd < 0) { + ERR("Consumer has negative file descriptor"); + return -EINVAL; + } + + if (usess->metadata->obj->shm_fd != 0) { + int fd; + 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 = usess->metadata->obj->memory_map_size; + DBG("Sending metadata 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; + } + fd = usess->metadata->obj->shm_fd; + ret = lttcomm_send_fds_unix_sock(sock, &fd, 1); + 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; + lum.u.stream.uid = usess->uid; + lum.u.stream.gid = usess->gid; + 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(); + cds_lfht_for_each_entry(usess->channels->ht, &iter.iter, ua_chan, + node.node) { + ret = send_channel_streams(sock, ua_chan, usess->uid, usess->gid); + if (ret < 0) { + rcu_read_unlock(); + goto error; + } + } + rcu_read_unlock(); + + DBG("consumer fds (metadata and channel streams) sent"); + + return 0; + +error: + return ret; +} diff --git a/src/bin/lttng-sessiond/ust-consumer.h b/src/bin/lttng-sessiond/ust-consumer.h new file mode 100644 index 000000000..85169e6b8 --- /dev/null +++ b/src/bin/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/src/bin/lttng-sessiond/ust-ctl.h b/src/bin/lttng-sessiond/ust-ctl.h new file mode 100644 index 000000000..f4f34ee2f --- /dev/null +++ b/src/bin/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 HAVE_LIBLTTNG_UST_CTL +#include +#include +#else +#include "lttng-ust-ctl.h" +#include "lttng-ust-abi.h" +#endif + +#endif /* _LTT_UST_CTL_H */ diff --git a/src/bin/lttng-sessiond/utils.c b/src/bin/lttng-sessiond/utils.c new file mode 100644 index 000000000..313f4cf78 --- /dev/null +++ b/src/bin/lttng-sessiond/utils.c @@ -0,0 +1,54 @@ +/* + * 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 "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")); +} diff --git a/src/bin/lttng-sessiond/utils.h b/src/bin/lttng-sessiond/utils.h new file mode 100644 index 000000000..e20ec4e9e --- /dev/null +++ b/src/bin/lttng-sessiond/utils.h @@ -0,0 +1,24 @@ +/* + * 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 + +const char *get_home_dir(void); +int notify_thread_pipe(int wpipe); + +#endif /* _LTT_UTILS_H */ diff --git a/src/bin/lttng/Makefile.am b/src/bin/lttng/Makefile.am new file mode 100644 index 000000000..34c47dfcf --- /dev/null +++ b/src/bin/lttng/Makefile.am @@ -0,0 +1,14 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src \ + -DINSTALL_BIN_PATH=\""$(bindir)"\" + +bin_PROGRAMS = lttng + +lttng_SOURCES = cmd.h conf.c conf.h commands/start.c \ + commands/list.c commands/create.c commands/destroy.c \ + commands/stop.c commands/enable_events.c \ + commands/disable_events.c commands/enable_channels.c \ + commands/disable_channels.c commands/add_context.c \ + commands/set_session.c commands/version.c \ + commands/calibrate.c utils.c utils.h lttng.c + +lttng_LDADD = $(top_builddir)/src/lib/lttng-ctl/liblttng-ctl.la diff --git a/src/bin/lttng/cmd.h b/src/bin/lttng/cmd.h new file mode 100644 index 000000000..f66d6d953 --- /dev/null +++ b/src/bin/lttng/cmd.h @@ -0,0 +1,53 @@ +/* + * 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 _LTTNG_CMD_H +#define _LTTNG_CMD_H + +#include +#include +#include + +enum cmd_error_code { + CMD_SUCCESS, + CMD_ERROR, + CMD_UNDEFINED, + CMD_NOT_IMPLEMENTED, + CMD_FATAL, +}; + +struct cmd_struct { + const char *name; + int (*func)(int argc, const char **argv); +}; + +extern int cmd_list(int argc, const char **argv); +extern int cmd_create(int argc, const char **argv); +extern int cmd_destroy(int argc, const char **argv); +extern int cmd_start(int argc, const char **argv); +extern int cmd_stop(int argc, const char **argv); +extern int cmd_enable_events(int argc, const char **argv); +extern int cmd_disable_events(int argc, const char **argv); +extern int cmd_enable_channels(int argc, const char **argv); +extern int cmd_disable_channels(int argc, const char **argv); +extern int cmd_add_context(int argc, const char **argv); +extern int cmd_set_session(int argc, const char **argv); +extern int cmd_version(int argc, const char **argv); +extern int cmd_calibrate(int argc, const char **argv); + +#endif /* _LTTNG_CMD_H */ diff --git a/src/bin/lttng/commands/add_context.c b/src/bin/lttng/commands/add_context.c new file mode 100644 index 000000000..2d9699ce2 --- /dev/null +++ b/src/bin/lttng/commands/add_context.c @@ -0,0 +1,487 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +#define PRINT_LINE_LEN 80 + +static char *opt_event_name; +static char *opt_channel_name; +static char *opt_session_name; +static int opt_kernel; +static int opt_userspace; +static char *opt_cmd_name; +static pid_t opt_pid; +static char *opt_type; + +enum { + OPT_HELP = 1, + OPT_TYPE, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +/* + * Taken from the LTTng ABI + */ +enum context_type { + CONTEXT_PID = 0, + CONTEXT_PERF_COUNTER = 1, + CONTEXT_PROCNAME = 2, + CONTEXT_PRIO = 3, + CONTEXT_NICE = 4, + CONTEXT_VPID = 5, + CONTEXT_TID = 6, + CONTEXT_VTID = 7, + CONTEXT_PPID = 8, + CONTEXT_VPPID = 9, +}; + +/* + * Taken from the Perf ABI (all enum perf_*) + */ +enum perf_type { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_HW_CACHE = 3, +}; + +enum perf_count_hard { + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + PERF_COUNT_HW_STALLED_CYCLES_FRONTEND = 7, + PERF_COUNT_HW_STALLED_CYCLES_BACKEND = 8, +}; + +enum perf_count_soft { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, + PERF_COUNT_SW_EMULATION_FAULTS = 8, +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"event", 'e', POPT_ARG_STRING, &opt_event_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {"type", 't', POPT_ARG_STRING, &opt_type, OPT_TYPE, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * Context options + */ +#define PERF_HW(opt, name) \ + { \ + "perf:" #opt, CONTEXT_PERF_COUNTER, \ + .u.perf = { PERF_TYPE_HARDWARE, PERF_COUNT_HW_##name, },\ + } + +#define PERF_SW(opt, name) \ + { \ + "perf:" #opt, CONTEXT_PERF_COUNTER, \ + .u.perf = { PERF_TYPE_SOFTWARE, PERF_COUNT_SW_##name, },\ + } + +#define _PERF_HW_CACHE(optstr, name, op, result) \ + { \ + "perf:" optstr, CONTEXT_PERF_COUNTER, \ + .u.perf = { \ + PERF_TYPE_HW_CACHE, \ + (uint64_t) PERF_COUNT_HW_CACHE_##name \ + | ((uint64_t) PERF_COUNT_HW_CACHE_OP_##op << 8) \ + | ((uint64_t) PERF_COUNT_HW_CACHE_RESULT_##result << 16), \ + }, \ + } + +#define PERF_HW_CACHE(opt, name) \ + _PERF_HW_CACHE(#opt "-loads", name, READ, ACCESS), \ + _PERF_HW_CACHE(#opt "-load-misses", name, READ, MISS), \ + _PERF_HW_CACHE(#opt "-stores", name, WRITE, ACCESS), \ + _PERF_HW_CACHE(#opt "-store-misses", name, WRITE, MISS), \ + _PERF_HW_CACHE(#opt "-prefetches", name, PREFETCH, ACCESS), \ + _PERF_HW_CACHE(#opt "-prefetch-misses", name, PREFETCH, MISS) \ + +static +const struct ctx_opts { + char *symbol; + enum context_type ctx_type; + union { + struct { + uint32_t type; + uint64_t config; + } perf; + } u; +} ctx_opts[] = { + { "pid", CONTEXT_PID }, + { "procname", CONTEXT_PROCNAME }, + { "prio", CONTEXT_PRIO }, + { "nice", CONTEXT_NICE }, + { "vpid", CONTEXT_VPID }, + { "tid", CONTEXT_TID }, + { "vtid", CONTEXT_VTID }, + { "ppid", CONTEXT_PPID }, + { "vppid", CONTEXT_VPPID }, + /* Perf options */ + PERF_HW(cpu-cycles, CPU_CYCLES), + PERF_HW(cycles, CPU_CYCLES), + PERF_HW(stalled-cycles-frontend, STALLED_CYCLES_FRONTEND), + PERF_HW(idle-cycles-frontend, STALLED_CYCLES_FRONTEND), + PERF_HW(stalled-cycles-backend, STALLED_CYCLES_BACKEND), + PERF_HW(idle-cycles-backend, STALLED_CYCLES_BACKEND), + PERF_HW(instructions, INSTRUCTIONS), + PERF_HW(cache-references, CACHE_REFERENCES), + PERF_HW(cache-misses, CACHE_MISSES), + PERF_HW(branch-instructions, BRANCH_INSTRUCTIONS), + PERF_HW(branches, BRANCH_INSTRUCTIONS), + PERF_HW(branch-misses, BRANCH_MISSES), + PERF_HW(bus-cycles, BUS_CYCLES), + + PERF_HW_CACHE(L1-dcache, L1D), + PERF_HW_CACHE(L1-icache, L1I), + PERF_HW_CACHE(LLC, LL), + PERF_HW_CACHE(dTLB, DTLB), + _PERF_HW_CACHE("iTLB-loads", ITLB, READ, ACCESS), + _PERF_HW_CACHE("iTLB-load-misses", ITLB, READ, MISS), + _PERF_HW_CACHE("branch-loads", BPU, READ, ACCESS), + _PERF_HW_CACHE("branch-load-misses", BPU, READ, MISS), + + + PERF_SW(cpu-clock, CPU_CLOCK), + PERF_SW(task-clock, TASK_CLOCK), + PERF_SW(page-fault, PAGE_FAULTS), + PERF_SW(faults, PAGE_FAULTS), + PERF_SW(major-faults, PAGE_FAULTS_MAJ), + PERF_SW(minor-faults, PAGE_FAULTS_MIN), + PERF_SW(context-switches, CONTEXT_SWITCHES), + PERF_SW(cs, CONTEXT_SWITCHES), + PERF_SW(cpu-migrations, CPU_MIGRATIONS), + PERF_SW(migrations, CPU_MIGRATIONS), + PERF_SW(alignment-faults, ALIGNMENT_FAULTS), + PERF_SW(emulation-faults, EMULATION_FAULTS), + { NULL, -1 }, /* Closure */ +}; + +#undef PERF_SW +#undef PERF_HW + +/* + * Context type for command line option parsing. + */ +struct ctx_type { + const struct ctx_opts *opt; + struct cds_list_head list; +}; + +/* + * List of context type. Use to enable multiple context on a single command + * line entry. + */ +struct ctx_type_list { + struct cds_list_head head; +} ctx_type_list = { + .head = CDS_LIST_HEAD_INIT(ctx_type_list.head), +}; + +/* + * Pretty print context type. + */ +static void print_ctx_type(FILE *ofp) +{ + const char *indent = " "; + int indent_len = strlen(indent); + int len, i = 0; + + fprintf(ofp, "%s", indent); + len = indent_len; + while (ctx_opts[i].symbol != NULL) { + if (len > indent_len) { + if (len + strlen(ctx_opts[i].symbol) + 2 + >= PRINT_LINE_LEN) { + fprintf(ofp, ",\n"); + fprintf(ofp, "%s", indent); + len = indent_len; + } else { + len += fprintf(ofp, ", "); + } + } + len += fprintf(ofp, "%s", ctx_opts[i].symbol); + i++; + } +} + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng add-context -t TYPE\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "If no event name is given (-e), the context will be added to the channel\n"); + fprintf(ofp, "If no channel and no event is given (-c/-e), the context\n"); + fprintf(ofp, "will be added to all events and all channels.\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -s, --session Apply on session name\n"); + fprintf(ofp, " -c, --channel NAME Apply on channel\n"); + fprintf(ofp, " -e, --event NAME Apply on event\n"); + fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " If no CMD, the domain used is UST global\n"); + fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); + fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); + fprintf(ofp, " -t, --type TYPE Context type. You can repeat that option on\n"); + fprintf(ofp, " the command line.\n"); + fprintf(ofp, " TYPE can be one of the strings below:\n"); + print_ctx_type(ofp); + fprintf(ofp, "\n"); + fprintf(ofp, "Example:\n"); + fprintf(ofp, "This command will add the context information 'prio' and two perf\n" + "counters: hardware branch misses and cache misses, to all events\n" + "in the trace data output:\n"); + fprintf(ofp, "# lttng add-context -k -t prio -t perf:branch-misses -t perf:cache-misses\n"); + fprintf(ofp, "\n"); +} + +/* + * Find context numerical value from string. + */ +static int find_ctx_type_idx(const char *opt) +{ + int ret = -1, i = 0; + + while (ctx_opts[i].symbol != NULL) { + if (strcmp(opt, ctx_opts[i].symbol) == 0) { + ret = i; + goto end; + } + i++; + } + +end: + return ret; +} + +/* + * Add context to channel or event. + */ +static int add_context(char *session_name) +{ + int ret = CMD_SUCCESS; + struct lttng_event_context context; + struct lttng_domain dom; + struct ctx_type *type; + char *ptr; + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else if (opt_userspace && opt_cmd_name == NULL) { + dom.type = LTTNG_DOMAIN_UST; + } else if (opt_userspace && opt_cmd_name != NULL) { + dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; + strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Iterate over all context type given */ + cds_list_for_each_entry(type, &ctx_type_list.head, list) { + context.ctx = type->opt->ctx_type; + if (context.ctx == LTTNG_EVENT_CONTEXT_PERF_COUNTER) { + context.u.perf_counter.type = type->opt->u.perf.type; + context.u.perf_counter.config = type->opt->u.perf.config; + strcpy(context.u.perf_counter.name, type->opt->symbol); + /* Replace : and - by _ */ + while ((ptr = strchr(context.u.perf_counter.name, '-')) != NULL) { + *ptr = '_'; + } + while ((ptr = strchr(context.u.perf_counter.name, ':')) != NULL) { + *ptr = '_'; + } + } + DBG("Adding context..."); + + ret = lttng_add_context(handle, &context, opt_event_name, + opt_channel_name); + if (ret < 0) { + fprintf(stderr, "%s: ", type->opt->symbol); + continue; + } else { + MSG("%s context %s added to %s event in %s", + opt_kernel ? "kernel" : "UST", type->opt->symbol, + opt_event_name ? opt_event_name : "all", + opt_channel_name ? opt_channel_name : "all channels"); + } + } + +error: + lttng_destroy_handle(handle); + + return ret; +} + +/* + * Add context on channel or event. + */ +int cmd_add_context(int argc, const char **argv) +{ + int index, opt, ret = CMD_SUCCESS; + static poptContext pc; + struct ctx_type *type, *tmptype; + char *session_name = NULL; + + if (argc < 2) { + usage(stderr); + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_TYPE: + type = malloc(sizeof(struct ctx_type)); + if (type == NULL) { + perror("malloc ctx_type"); + ret = -1; + goto end; + } + index = find_ctx_type_idx(opt_type); + if (index < 0) { + ERR("Unknown context type %s", opt_type); + goto end; + } + type->opt = &ctx_opts[index]; + if (type->opt->ctx_type == -1) { + ERR("Unknown context type %s", opt_type); + } else { + cds_list_add(&type->list, &ctx_type_list.head); + } + break; + case OPT_USERSPACE: + opt_userspace = 1; + opt_cmd_name = poptGetOptArg(pc); + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = -1; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = add_context(session_name); + + /* Cleanup allocated memory */ + cds_list_for_each_entry_safe(type, tmptype, &ctx_type_list.head, list) { + free(type); + } + +end: + return ret; +} diff --git a/src/bin/lttng/commands/calibrate.c b/src/bin/lttng/commands/calibrate.c new file mode 100644 index 000000000..3fa87e3ec --- /dev/null +++ b/src/bin/lttng/commands/calibrate.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 - 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static int opt_event_type; +static char *opt_kernel; +static int opt_pid_all; +static int opt_userspace; +static char *opt_cmd_name; +static pid_t opt_pid; + +enum { + OPT_HELP = 1, + OPT_TRACEPOINT, + OPT_MARKER, + OPT_PROBE, + OPT_FUNCTION, + OPT_FUNCTION_ENTRY, + OPT_SYSCALL, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"all", 0, POPT_ARG_VAL, &opt_pid_all, 1, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, + {"marker", 0, POPT_ARG_NONE, 0, OPT_MARKER, 0, 0}, + {"probe", 0, POPT_ARG_NONE, 0, OPT_PROBE, 0, 0}, + {"function", 0, POPT_ARG_NONE, 0, OPT_FUNCTION, 0, 0}, +#if 0 + /* + * Removed from options to discourage its use. Not in kernel + * tracer anymore. + */ + {"function:entry", 0, POPT_ARG_NONE, 0, OPT_FUNCTION_ENTRY, 0, 0}, +#endif + {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng calibrate [options] [calibrate_options]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " --all If -u, apply on all traceable apps\n"); + fprintf(ofp, " -p, --pid PID If -u, apply on a specific PID\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Calibrate options:\n"); + fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); + fprintf(ofp, " --probe\n"); + fprintf(ofp, " Dynamic probe.\n"); + fprintf(ofp, " --function\n"); + fprintf(ofp, " Dynamic function entry/return probe.\n"); +#if 0 + fprintf(ofp, " --function:entry symbol\n"); + fprintf(ofp, " Function tracer event\n"); +#endif + fprintf(ofp, " --syscall System call eventl\n"); + fprintf(ofp, " --marker User-space marker (deprecated)\n"); + fprintf(ofp, "\n"); +} + +/* + * calibrate_lttng + * + * Calibrate LTTng. + */ +static int calibrate_lttng(void) +{ + int ret = CMD_SUCCESS; + struct lttng_domain dom; + struct lttng_calibrate calibrate; + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } + + handle = lttng_create_handle(NULL, &dom); + if (handle == NULL) { + ret = -1; + goto end; + } + + /* Kernel tracer action */ + if (opt_kernel) { + switch (opt_event_type) { + case LTTNG_EVENT_TRACEPOINT: + DBG("Calibrating kernel tracepoints"); + break; + case LTTNG_EVENT_PROBE: + DBG("Calibrating kernel probes"); + break; + case LTTNG_EVENT_FUNCTION: + DBG("Calibrating kernel functions"); + calibrate.type = LTTNG_CALIBRATE_FUNCTION; + ret = lttng_calibrate(handle, &calibrate); + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + DBG("Calibrating kernel function entry"); + break; + case LTTNG_EVENT_SYSCALL: + DBG("Calibrating kernel syscall"); + break; + default: + ret = CMD_NOT_IMPLEMENTED; + goto end; + } + } else if (opt_userspace) { /* User-space tracer action */ + /* + * TODO: Waiting on lttng UST 2.0 + */ + if (opt_pid_all) { + } else if (opt_pid != 0) { + } + ret = CMD_NOT_IMPLEMENTED; + goto end; + } else { + ERR("Please specify a tracer (--kernel or --userspace)"); + goto end; + } +end: + lttng_destroy_handle(handle); + + return ret; +} + +/* + * cmd_calibrate + * + * Calibrate LTTng tracer. + */ +int cmd_calibrate(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + /* Default event type */ + opt_event_type = LTTNG_EVENT_TRACEPOINT; + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_TRACEPOINT: + ret = CMD_NOT_IMPLEMENTED; + break; + case OPT_MARKER: + ret = CMD_NOT_IMPLEMENTED; + goto end; + case OPT_PROBE: + ret = CMD_NOT_IMPLEMENTED; + break; + case OPT_FUNCTION: + opt_event_type = LTTNG_EVENT_FUNCTION; + break; + case OPT_FUNCTION_ENTRY: + ret = CMD_NOT_IMPLEMENTED; + break; + case OPT_SYSCALL: + ret = CMD_NOT_IMPLEMENTED; + break; + case OPT_USERSPACE: + opt_userspace = 1; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + ret = calibrate_lttng(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/create.c b/src/bin/lttng/commands/create.c new file mode 100644 index 000000000..4083e4195 --- /dev/null +++ b/src/bin/lttng/commands/create.c @@ -0,0 +1,178 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_output_path; +static char *opt_session_name; + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"output", 'o', POPT_ARG_STRING, &opt_output_path, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng create [options] [NAME]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -o, --output PATH Specify output path for traces\n"); + fprintf(ofp, "\n"); +} + +/* + * create_session + * + * Create a tracing session. If no name specified, a default name will be + * generated. + */ +static int create_session() +{ + int ret, have_name = 0; + char datetime[16]; + char *session_name, *traces_path = NULL, *alloc_path = NULL; + time_t rawtime; + struct tm *timeinfo; + + /* Get date and time for automatic session name/path */ + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(datetime, sizeof(datetime), "%Y%m%d-%H%M%S", timeinfo); + + /* Auto session name creation */ + if (opt_session_name == NULL) { + ret = asprintf(&session_name, "auto-%s", datetime); + if (ret < 0) { + perror("asprintf session name"); + goto error; + } + DBG("Auto session name set to %s", session_name); + } else { + session_name = opt_session_name; + have_name = 1; + } + + /* Auto output path */ + if (opt_output_path == NULL) { + alloc_path = strdup(config_get_default_path()); + if (alloc_path == NULL) { + ERR("Home path not found.\n \ + Please specify an output path using -o, --output PATH"); + ret = CMD_FATAL; + goto error; + } + + if (have_name) { + ret = asprintf(&traces_path, "%s/" LTTNG_DEFAULT_TRACE_DIR_NAME + "/%s-%s", alloc_path, session_name, datetime); + } else { + ret = asprintf(&traces_path, "%s/" LTTNG_DEFAULT_TRACE_DIR_NAME + "/%s", alloc_path, session_name); + } + + if (ret < 0) { + perror("asprintf trace dir name"); + goto error; + } + } else { + traces_path = opt_output_path; + } + + ret = lttng_create_session(session_name, traces_path); + if (ret < 0) { + goto error; + } + + /* Init lttng session config */ + ret = config_init(session_name); + if (ret < 0) { + if (ret == -1) { + ret = CMD_ERROR; + } + goto error; + } + + MSG("Session %s created.", session_name); + MSG("Traces will be written in %s" , traces_path); + + ret = CMD_SUCCESS; + +error: + if (alloc_path) { + free(alloc_path); + } + + if (traces_path) { + free(traces_path); + } + return ret; +} + +/* + * cmd_list + * + * The 'list ' first level command + */ +int cmd_create(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + ret = create_session(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/destroy.c b/src/bin/lttng/commands/destroy.c new file mode 100644 index 000000000..151686ff9 --- /dev/null +++ b/src/bin/lttng/commands/destroy.c @@ -0,0 +1,134 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_session_name; + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng destroy [options] [NAME]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); + fprintf(ofp, "get it from the configuration directory (.lttng).\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, "\n"); +} + +/* + * Destroy a session removing the config directory and unregistering to the + * session daemon. + */ +static int destroy_session() +{ + int ret; + char *session_name, *path; + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + ret = lttng_destroy_session(session_name); + if (ret < 0) { + goto free_name; + } + + path = config_get_default_path(); + if (path == NULL) { + ret = CMD_FATAL; + goto free_name; + } + + if (opt_session_name == NULL) { + config_destroy(path); + MSG("Session %s destroyed at %s", session_name, path); + } else { + MSG("Session %s destroyed", session_name); + } + + ret = CMD_SUCCESS; + +free_name: + if (opt_session_name == NULL) { + free(session_name); + } +error: + return ret; +} + +/* + * The 'destroy ' first level command + */ +int cmd_destroy(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + ret = destroy_session(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/disable_channels.c b/src/bin/lttng/commands/disable_channels.c new file mode 100644 index 000000000..59ad9fc28 --- /dev/null +++ b/src/bin/lttng/commands/disable_channels.c @@ -0,0 +1,181 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_channels; +static int opt_kernel; +static char *opt_session_name; +static int opt_userspace; +static char *opt_cmd_name; +static pid_t opt_pid; + +enum { + OPT_HELP = 1, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng disable-channel NAME[,NAME2,...] [options]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -s, --session Apply on session name\n"); + fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " If no CMD, the domain used is UST global\n"); + fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); + fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); + fprintf(ofp, "\n"); +} + +/* + * Disabling channel using the lttng API. + */ +static int disable_channels(char *session_name) +{ + int ret = CMD_SUCCESS; + char *channel_name; + struct lttng_domain dom; + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else if (opt_userspace && opt_cmd_name == NULL) { + dom.type = LTTNG_DOMAIN_UST; + } else if (opt_userspace && opt_cmd_name != NULL) { + dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; + strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Strip channel list */ + channel_name = strtok(opt_channels, ","); + while (channel_name != NULL) { + DBG("Disabling channel %s", channel_name); + + ret = lttng_disable_channel(handle, channel_name); + if (ret < 0) { + goto error; + } else { + MSG("%s channel %s disabled for session %s", + opt_kernel ? "Kernel" : "UST", channel_name, + session_name); + } + + /* Next channel */ + channel_name = strtok(NULL, ","); + } + +error: + lttng_destroy_handle(handle); + + return ret; +} + +/* + * cmd_disable_channels + * + * Disable channel to trace session + */ +int cmd_disable_channels(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + char *session_name = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_USERSPACE: + opt_userspace = 1; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_channels = (char*) poptGetArg(pc); + if (opt_channels == NULL) { + ERR("Missing channel name(s).\n"); + usage(stderr); + ret = CMD_SUCCESS; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = -1; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = disable_channels(session_name); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/disable_events.c b/src/bin/lttng/commands/disable_events.c new file mode 100644 index 000000000..5247110ef --- /dev/null +++ b/src/bin/lttng/commands/disable_events.c @@ -0,0 +1,239 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_event_list; +static int opt_kernel; +static char *opt_channel_name; +static char *opt_session_name; +static int opt_userspace; +static char *opt_cmd_name; +static int opt_disable_all; +static pid_t opt_pid; + +enum { + OPT_HELP = 1, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"all-events", 'a', POPT_ARG_VAL, &opt_disable_all, 1, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng disable-event NAME[,NAME2,...] [options]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -s, --session Apply on session name\n"); + fprintf(ofp, " -c, --channel Apply on this channel\n"); + fprintf(ofp, " -a, --all-events Disable all tracepoints\n"); + fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " If no CMD, the domain used is UST global\n"); + fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); + fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); + fprintf(ofp, "\n"); +} + +/* + * disable_events + * + * Disabling event using the lttng API. + */ +static int disable_events(char *session_name) +{ + int err, ret = CMD_SUCCESS; + char *event_name, *channel_name = NULL; + struct lttng_domain dom; + + if (opt_channel_name == NULL) { + err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME); + if (err < 0) { + ret = CMD_FATAL; + goto error; + } + } else { + channel_name = opt_channel_name; + } + + if (opt_kernel && opt_userspace) { + ERR("Can't use -k/--kernel and -u/--userspace together"); + ret = CMD_FATAL; + goto error; + } + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else if (opt_userspace && opt_cmd_name == NULL) { + dom.type = LTTNG_DOMAIN_UST; + DBG("UST global domain selected"); + } else if (opt_userspace && opt_cmd_name != NULL) { + dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; + strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); + dom.attr.exec_name[NAME_MAX - 1] = '\0'; + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + if (opt_disable_all) { + ret = lttng_disable_event(handle, NULL, channel_name); + if (ret < 0) { + goto error; + } + + MSG("All %s events are disabled in channel %s", + opt_kernel ? "kernel" : "UST", channel_name); + goto end; + } + + /* Strip event list */ + event_name = strtok(opt_event_list, ","); + while (event_name != NULL) { + /* Kernel tracer action */ + if (opt_kernel) { + DBG("Disabling kernel event %s in channel %s", + event_name, channel_name); + } else if (opt_userspace) { /* User-space tracer action */ + if (opt_cmd_name != NULL || opt_pid) { + MSG("Only supporting tracing all UST processes (-u) for now."); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + DBG("Disabling UST event %s in channel %s", + event_name, channel_name); + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + goto error; + } + + ret = lttng_disable_event(handle, event_name, channel_name); + if (ret < 0) { + MSG("Unable to disable %s event %s in channel %s", + opt_kernel ? "kernel" : "UST", event_name, + channel_name); + } else { + MSG("%s event %s disabled in channel %s", + opt_kernel ? "kernel" : "UST", event_name, + channel_name); + } + + /* Next event */ + event_name = strtok(NULL, ","); + } + +end: +error: + if (opt_channel_name == NULL) { + free(channel_name); + } + lttng_destroy_handle(handle); + + return ret; +} + +/* + * cmd_disable_events + * + * Disable event to trace session + */ +int cmd_disable_events(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + char *session_name = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_USERSPACE: + opt_userspace = 1; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_event_list = (char*) poptGetArg(pc); + if (opt_event_list == NULL && opt_disable_all == 0) { + ERR("Missing event name(s).\n"); + usage(stderr); + ret = CMD_SUCCESS; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = -1; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = disable_events(session_name); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/enable_channels.c b/src/bin/lttng/commands/enable_channels.c new file mode 100644 index 000000000..52745f69a --- /dev/null +++ b/src/bin/lttng/commands/enable_channels.c @@ -0,0 +1,287 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_channels; +static int opt_kernel; +static char *opt_cmd_name; +static char *opt_session_name; +static int opt_userspace; +static char *opt_cmd_name; +static pid_t opt_pid; +static struct lttng_channel chan; + +enum { + OPT_HELP = 1, + OPT_DISCARD, + OPT_OVERWRITE, + OPT_SUBBUF_SIZE, + OPT_NUM_SUBBUF, + OPT_SWITCH_TIMER, + OPT_READ_TIMER, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {"discard", 0, POPT_ARG_NONE, 0, OPT_DISCARD, 0, 0}, + {"overwrite", 0, POPT_ARG_NONE, 0, OPT_OVERWRITE, 0, 0}, + {"subbuf-size", 0, POPT_ARG_DOUBLE, 0, OPT_SUBBUF_SIZE, 0, 0}, + {"num-subbuf", 0, POPT_ARG_INT, 0, OPT_NUM_SUBBUF, 0, 0}, + {"switch-timer", 0, POPT_ARG_INT, 0, OPT_SWITCH_TIMER, 0, 0}, + {"read-timer", 0, POPT_ARG_INT, 0, OPT_READ_TIMER, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng enable-channel NAME[,NAME2,...] [options] [channel_options]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -s, --session Apply on session name\n"); + fprintf(ofp, " -k, --kernel Apply on the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " If no CMD, the domain used is UST global\n"); + fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); + fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Channel options:\n"); + fprintf(ofp, " --discard Discard event when buffers are full%s\n", + DEFAULT_CHANNEL_OVERWRITE ? "" : " (default)"); + fprintf(ofp, " --overwrite Flight recorder mode%s\n", + DEFAULT_CHANNEL_OVERWRITE ? " (default)" : ""); + fprintf(ofp, " --subbuf-size Subbuffer size in bytes\n"); + fprintf(ofp, " (default: %u, kernel default: %u)\n", + DEFAULT_CHANNEL_SUBBUF_SIZE, + DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE); + fprintf(ofp, " --num-subbuf Number of subbufers\n"); + fprintf(ofp, " (default: %u, kernel default: %u)\n", + DEFAULT_CHANNEL_SUBBUF_NUM, + DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM); + fprintf(ofp, " --switch-timer Switch timer interval in usec (default: %u)\n", + DEFAULT_CHANNEL_SWITCH_TIMER); + fprintf(ofp, " --read-timer Read timer interval in usec (default: %u)\n", + DEFAULT_CHANNEL_READ_TIMER); + fprintf(ofp, "\n"); +} + +/* + * Set default attributes depending on those already defined from the command + * line. + */ +static void set_default_attr(struct lttng_domain *dom) +{ + struct lttng_channel_attr default_attr; + + /* Set attributes */ + lttng_channel_set_default_attr(dom, &default_attr); + + if (chan.attr.overwrite == -1) { + chan.attr.overwrite = default_attr.overwrite; + } + if (chan.attr.subbuf_size == -1) { + chan.attr.subbuf_size = default_attr.subbuf_size; + } + if (chan.attr.num_subbuf == -1) { + chan.attr.num_subbuf = default_attr.num_subbuf; + } + if (chan.attr.switch_timer_interval == -1) { + chan.attr.switch_timer_interval = default_attr.switch_timer_interval; + } + if (chan.attr.read_timer_interval == -1) { + chan.attr.read_timer_interval = default_attr.read_timer_interval; + } + if (chan.attr.output == -1) { + chan.attr.output = default_attr.output; + } +} + +/* + * Adding channel using the lttng API. + */ +static int enable_channel(char *session_name) +{ + int ret = CMD_SUCCESS; + char *channel_name; + struct lttng_domain dom; + + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else if (opt_userspace && opt_cmd_name == NULL) { + dom.type = LTTNG_DOMAIN_UST; + } else if (opt_userspace && opt_cmd_name != NULL) { + dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; + strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + set_default_attr(&dom); + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + /* Strip channel list (format: chan1,chan2,...) */ + channel_name = strtok(opt_channels, ","); + while (channel_name != NULL) { + /* Copy channel name and normalize it */ + strncpy(chan.name, channel_name, NAME_MAX); + chan.name[NAME_MAX - 1] = '\0'; + + DBG("Enabling channel %s", channel_name); + + ret = lttng_enable_channel(handle, &chan); + if (ret < 0) { + goto error; + } else { + MSG("%s channel %s enabled for session %s", + opt_kernel ? "Kernel" : "UST", channel_name, + session_name); + } + + /* Next event */ + channel_name = strtok(NULL, ","); + } + +error: + lttng_destroy_handle(handle); + + return ret; +} + +/* + * Default value for channel configuration. + */ +static void init_channel_config(void) +{ + /* + * Put -1 everywhere so we can identify those set by the command line and + * those needed to be set by the default values. + */ + memset(&chan.attr, -1, sizeof(chan.attr)); +} + +/* + * Add channel to trace session + */ +int cmd_enable_channels(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + char *session_name = NULL; + + init_channel_config(); + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_DISCARD: + chan.attr.overwrite = 0; + DBG("Channel set to discard"); + break; + case OPT_OVERWRITE: + chan.attr.overwrite = 1; + DBG("Channel set to overwrite"); + break; + case OPT_SUBBUF_SIZE: + chan.attr.subbuf_size = atol(poptGetOptArg(pc)); + DBG("Channel subbuf size set to %" PRIu64, chan.attr.subbuf_size); + break; + case OPT_NUM_SUBBUF: + chan.attr.num_subbuf = atoi(poptGetOptArg(pc)); + DBG("Channel subbuf num set to %" PRIu64, chan.attr.num_subbuf); + break; + case OPT_SWITCH_TIMER: + chan.attr.switch_timer_interval = atoi(poptGetOptArg(pc)); + DBG("Channel switch timer interval set to %d", chan.attr.switch_timer_interval); + break; + case OPT_READ_TIMER: + chan.attr.read_timer_interval = atoi(poptGetOptArg(pc)); + DBG("Channel read timer interval set to %d", chan.attr.read_timer_interval); + break; + case OPT_USERSPACE: + opt_userspace = 1; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_channels = (char*) poptGetArg(pc); + if (opt_channels == NULL) { + ERR("Missing channel name.\n"); + usage(stderr); + ret = CMD_SUCCESS; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = -1; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = enable_channel(session_name); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/enable_events.c b/src/bin/lttng/commands/enable_events.c new file mode 100644 index 000000000..3c1c3916e --- /dev/null +++ b/src/bin/lttng/commands/enable_events.c @@ -0,0 +1,463 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_event_list; +static int opt_event_type; +static int opt_kernel; +static char *opt_session_name; +static int opt_userspace; +static char *opt_cmd_name; +static int opt_enable_all; +static pid_t opt_pid; +static char *opt_probe; +static char *opt_function; +static char *opt_function_entry_symbol; +static char *opt_channel_name; + +enum { + OPT_HELP = 1, + OPT_TRACEPOINT, + OPT_PROBE, + OPT_FUNCTION, + OPT_FUNCTION_ENTRY, + OPT_SYSCALL, + OPT_USERSPACE, + OPT_TRACEPOINT_LOGLEVEL, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"session", 's', POPT_ARG_STRING, &opt_session_name, 0, 0, 0}, + {"all", 'a', POPT_ARG_VAL, &opt_enable_all, 1, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel_name, 0, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {"tracepoint", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT, 0, 0}, + {"probe", 0, POPT_ARG_STRING, &opt_probe, OPT_PROBE, 0, 0}, + {"function", 0, POPT_ARG_STRING, &opt_function, OPT_FUNCTION, 0, 0}, +#if 0 + /* + * Currently removed from lttng kernel tracer. Removed from + * lttng UI to discourage its use. + */ + {"function:entry", 0, POPT_ARG_STRING, &opt_function_entry_symbol, OPT_FUNCTION_ENTRY, 0, 0}, +#endif + {"syscall", 0, POPT_ARG_NONE, 0, OPT_SYSCALL, 0, 0}, + {"loglevel", 0, POPT_ARG_NONE, 0, OPT_TRACEPOINT_LOGLEVEL, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng enable-event NAME[,NAME2,...] [options] [event_options]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -s, --session Apply on session name\n"); + fprintf(ofp, " -c, --channel Apply on this channel\n"); + fprintf(ofp, " -a, --all Enable all tracepoints\n"); + fprintf(ofp, " -k, --kernel Apply for the kernel tracer\n"); + fprintf(ofp, " -u, --userspace [CMD] Apply for the user-space tracer\n"); + fprintf(ofp, " If no CMD, the domain used is UST global\n"); + fprintf(ofp, " or else the domain is UST EXEC_NAME\n"); + fprintf(ofp, " -p, --pid PID If -u, apply to specific PID (domain: UST PID)\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Event options:\n"); + fprintf(ofp, " --tracepoint Tracepoint event (default)\n"); + fprintf(ofp, " - userspace tracer supports wildcards at end of string.\n"); + fprintf(ofp, " Don't forget to quote to deal with bash expansion.\n"); + fprintf(ofp, " e.g.:\n"); + fprintf(ofp, " \"*\"\n"); + fprintf(ofp, " \"app_component:na*\"\n"); + fprintf(ofp, " --loglevel Tracepoint loglevel\n"); + fprintf(ofp, " --probe [addr | symbol | symbol+offset]\n"); + fprintf(ofp, " Dynamic probe.\n"); + fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); + fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); + fprintf(ofp, " --function [addr | symbol | symbol+offset]\n"); + fprintf(ofp, " Dynamic function entry/return probe.\n"); + fprintf(ofp, " Addr and offset can be octal (0NNN...),\n"); + fprintf(ofp, " decimal (NNN...) or hexadecimal (0xNNN...)\n"); +#if 0 + fprintf(ofp, " --function:entry symbol\n"); + fprintf(ofp, " Function tracer event\n"); +#endif + fprintf(ofp, " --syscall System call event\n"); + fprintf(ofp, "\n"); +} + +/* + * Parse probe options. + */ +static int parse_probe_opts(struct lttng_event *ev, char *opt) +{ + int ret; + char s_hex[19]; + char name[LTTNG_SYMBOL_NAME_LEN]; + + if (opt == NULL) { + ret = -1; + goto end; + } + + /* Check for symbol+offset */ + ret = sscanf(opt, "%[^'+']+%s", name, s_hex); + if (ret == 2) { + strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); + ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + DBG("probe symbol %s", ev->attr.probe.symbol_name); + if (strlen(s_hex) == 0) { + ERR("Invalid probe offset %s", s_hex); + ret = -1; + goto end; + } + ev->attr.probe.offset = strtoul(s_hex, NULL, 0); + DBG("probe offset %" PRIu64, ev->attr.probe.offset); + ev->attr.probe.addr = 0; + goto end; + } + + /* Check for symbol */ + if (isalpha(name[0])) { + ret = sscanf(opt, "%s", name); + if (ret == 1) { + strncpy(ev->attr.probe.symbol_name, name, LTTNG_SYMBOL_NAME_LEN); + ev->attr.probe.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + DBG("probe symbol %s", ev->attr.probe.symbol_name); + ev->attr.probe.offset = 0; + DBG("probe offset %" PRIu64, ev->attr.probe.offset); + ev->attr.probe.addr = 0; + goto end; + } + } + + /* Check for address */ + ret = sscanf(opt, "%s", s_hex); + if (ret > 0) { + if (strlen(s_hex) == 0) { + ERR("Invalid probe address %s", s_hex); + ret = -1; + goto end; + } + ev->attr.probe.addr = strtoul(s_hex, NULL, 0); + DBG("probe addr %" PRIu64, ev->attr.probe.addr); + ev->attr.probe.offset = 0; + memset(ev->attr.probe.symbol_name, 0, LTTNG_SYMBOL_NAME_LEN); + goto end; + } + + /* No match */ + ret = -1; + +end: + return ret; +} + +/* + * Enabling event using the lttng API. + */ +static int enable_events(char *session_name) +{ + int err, ret = CMD_SUCCESS; + char *event_name, *channel_name = NULL; + struct lttng_event ev; + struct lttng_domain dom; + + if (opt_channel_name == NULL) { + err = asprintf(&channel_name, DEFAULT_CHANNEL_NAME); + if (err < 0) { + ret = CMD_FATAL; + goto error; + } + } else { + channel_name = opt_channel_name; + } + + if (opt_kernel && opt_userspace) { + ERR("Can't use -k/--kernel and -u/--userspace together"); + ret = CMD_FATAL; + goto error; + } + + /* Create lttng domain */ + if (opt_kernel) { + dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else if (opt_userspace && opt_cmd_name == NULL) { + dom.type = LTTNG_DOMAIN_UST; + } else if (opt_userspace && opt_cmd_name != NULL) { + dom.type = LTTNG_DOMAIN_UST_EXEC_NAME; + strncpy(dom.attr.exec_name, opt_cmd_name, NAME_MAX); + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + handle = lttng_create_handle(session_name, &dom); + if (handle == NULL) { + ret = -1; + goto error; + } + + if (opt_enable_all) { + /* Default setup for enable all */ + + if (opt_kernel) { + ev.type = opt_event_type; + ev.name[0] = '\0'; + } else { + ev.type = LTTNG_EVENT_TRACEPOINT; + strcpy(ev.name, "*"); + } + + ret = lttng_enable_event(handle, &ev, channel_name); + if (ret < 0) { + goto error; + } + + switch (opt_event_type) { + case LTTNG_EVENT_TRACEPOINT: + MSG("All %s tracepoints are enabled in channel %s", + opt_kernel ? "kernel" : "UST", channel_name); + break; + case LTTNG_EVENT_SYSCALL: + if (opt_kernel) { + MSG("All kernel system calls are enabled in channel %s", + channel_name); + } + break; + case LTTNG_EVENT_ALL: + MSG("All %s events are enabled in channel %s", + opt_kernel ? "kernel" : "UST", channel_name); + break; + default: + /* + * We should not be here since lttng_enable_event should have + * failed on the event type. + */ + goto error; + } + goto end; + } + + /* Strip event list */ + event_name = strtok(opt_event_list, ","); + while (event_name != NULL) { + /* Copy name and type of the event */ + strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + ev.type = opt_event_type; + + /* Kernel tracer action */ + if (opt_kernel) { + DBG("Enabling kernel event %s for channel %s", + event_name, channel_name); + + switch (opt_event_type) { + case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ + ev.type = LTTNG_EVENT_TRACEPOINT; + /* Fall-through */ + case LTTNG_EVENT_TRACEPOINT: + break; + case LTTNG_EVENT_PROBE: + ret = parse_probe_opts(&ev, opt_probe); + if (ret < 0) { + ERR("Unable to parse probe options"); + ret = 0; + goto error; + } + break; + case LTTNG_EVENT_FUNCTION: + ret = parse_probe_opts(&ev, opt_function); + if (ret < 0) { + ERR("Unable to parse function probe options"); + ret = 0; + goto error; + } + break; + case LTTNG_EVENT_FUNCTION_ENTRY: + strncpy(ev.attr.ftrace.symbol_name, opt_function_entry_symbol, + LTTNG_SYMBOL_NAME_LEN); + ev.attr.ftrace.symbol_name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_SYSCALL: + MSG("per-syscall selection not supported yet. Use \"-a\" " + "for all syscalls."); + default: + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + } else if (opt_userspace) { /* User-space tracer action */ + if (opt_cmd_name != NULL || opt_pid) { + MSG("Only supporting tracing all UST processes (-u) for now."); + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + + DBG("Enabling UST event %s for channel %s", event_name, + channel_name); + + switch (opt_event_type) { + case LTTNG_EVENT_ALL: /* Default behavior is tracepoint */ + /* Fall-through */ + case LTTNG_EVENT_TRACEPOINT: + /* Copy name and type of the event */ + ev.type = LTTNG_EVENT_TRACEPOINT; + strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: + /* Copy name and type of the event */ + ev.type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; + strncpy(ev.name, event_name, LTTNG_SYMBOL_NAME_LEN); + ev.name[LTTNG_SYMBOL_NAME_LEN - 1] = '\0'; + break; + case LTTNG_EVENT_PROBE: + case LTTNG_EVENT_FUNCTION: + case LTTNG_EVENT_FUNCTION_ENTRY: + case LTTNG_EVENT_SYSCALL: + default: + ret = CMD_NOT_IMPLEMENTED; + goto error; + } + } else { + ERR("Please specify a tracer (-k/--kernel or -u/--userspace)"); + goto error; + } + + ret = lttng_enable_event(handle, &ev, channel_name); + if (ret == 0) { + MSG("%s event %s created in channel %s", + opt_kernel ? "kernel": "UST", event_name, channel_name); + } + + /* Next event */ + event_name = strtok(NULL, ","); + } + +end: +error: + if (opt_channel_name == NULL) { + free(channel_name); + } + lttng_destroy_handle(handle); + + return ret; +} + +/* + * Add event to trace session + */ +int cmd_enable_events(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + char *session_name = NULL; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + /* Default event type */ + opt_event_type = LTTNG_EVENT_ALL; + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + case OPT_TRACEPOINT: + opt_event_type = LTTNG_EVENT_TRACEPOINT; + break; + case OPT_PROBE: + opt_event_type = LTTNG_EVENT_PROBE; + break; + case OPT_FUNCTION: + opt_event_type = LTTNG_EVENT_FUNCTION; + break; + case OPT_FUNCTION_ENTRY: + opt_event_type = LTTNG_EVENT_FUNCTION_ENTRY; + break; + case OPT_SYSCALL: + opt_event_type = LTTNG_EVENT_SYSCALL; + break; + case OPT_USERSPACE: + opt_userspace = 1; + break; + case OPT_TRACEPOINT_LOGLEVEL: + opt_event_type = LTTNG_EVENT_TRACEPOINT_LOGLEVEL; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_event_list = (char*) poptGetArg(pc); + if (opt_event_list == NULL && opt_enable_all == 0) { + ERR("Missing event name(s).\n"); + usage(stderr); + ret = CMD_SUCCESS; + goto end; + } + + if (!opt_session_name) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = -1; + goto end; + } + } else { + session_name = opt_session_name; + } + + ret = enable_events(session_name); + +end: + if (opt_session_name == NULL) { + free(session_name); + } + + return ret; +} diff --git a/src/bin/lttng/commands/list.c b/src/bin/lttng/commands/list.c new file mode 100644 index 000000000..6f37254e4 --- /dev/null +++ b/src/bin/lttng/commands/list.c @@ -0,0 +1,658 @@ +/* + * 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 "../cmd.h" + +static int opt_pid; +static int opt_userspace; +static char *opt_cmd_name; +static int opt_kernel; +static char *opt_channel; +static int opt_domain; + +const char *indent4 = " "; +const char *indent6 = " "; +const char *indent8 = " "; + +enum { + OPT_HELP = 1, + OPT_USERSPACE, +}; + +static struct lttng_handle *handle; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {"kernel", 'k', POPT_ARG_VAL, &opt_kernel, 1, 0, 0}, + {"userspace", 'u', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL, &opt_cmd_name, OPT_USERSPACE, 0, 0}, + {"pid", 'p', POPT_ARG_INT, &opt_pid, 0, 0, 0}, + {"channel", 'c', POPT_ARG_STRING, &opt_channel, 0, 0, 0}, + {"domain", 'd', POPT_ARG_VAL, &opt_domain, 1, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng list [[-k] [-u] [-p PID] [SESSION []]]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "With no arguments, list available tracing session(s)\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "With -k alone, list available kernel events\n"); + fprintf(ofp, "With -u alone, list available userspace events\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -k, --kernel Select kernel domain\n"); + fprintf(ofp, " -u, --userspace Select user-space domain.\n"); + fprintf(ofp, " -p, --pid PID List user-space events by PID\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -c, --channel NAME List details of a channel\n"); + fprintf(ofp, " -d, --domain List available domain(s)\n"); + fprintf(ofp, "\n"); +} + +/* + * Get command line from /proc for a specific pid. + * + * On success, return an allocated string pointer to the proc cmdline. + * On error, return NULL. + */ +static char *get_cmdline_by_pid(pid_t pid) +{ + int ret; + FILE *fp; + char *cmdline = NULL; + char path[24]; /* Can't go bigger than /proc/65535/cmdline */ + + snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); + fp = fopen(path, "r"); + if (fp == NULL) { + goto end; + } + + /* Caller must free() *cmdline */ + cmdline = malloc(PATH_MAX); + ret = fread(cmdline, 1, PATH_MAX, fp); + if (ret < 0) { + perror("fread proc list"); + } + fclose(fp); + +end: + return cmdline; +} + +static +const char *active_string(int value) +{ + switch (value) { + case 0: return " [inactive]"; + case 1: return " [active]"; + case -1: return ""; + default: return NULL; + } +} + +static +const char *enabled_string(int value) +{ + switch (value) { + case 0: return " [disabled]"; + case 1: return " [enabled]"; + case -1: return ""; + default: return NULL; + } +} + +static +const char *loglevel_string_pre(const char *loglevel) +{ + if (loglevel[0] == '\0') { + return ""; + } else { + return " (loglevel: "; + } +} + +static +const char *loglevel_string_post(const char *loglevel) +{ + if (loglevel[0] == '\0') { + return ""; + } else { + return ")"; + } +} + +/* + * Pretty print single event. + */ +static void print_events(struct lttng_event *event) +{ + switch (event->type) { + case LTTNG_EVENT_TRACEPOINT: + { + char ll_value[LTTNG_SYMBOL_NAME_LEN] = ""; + + if (event->loglevel[0] != '\0') { + int ret; + + ret = snprintf(ll_value, LTTNG_SYMBOL_NAME_LEN, + " (%lld)", (long long) event->loglevel_value); + if (ret < 0) + ERR("snprintf error"); + } + MSG("%s%s%s%s%s%s (type: tracepoint)%s", indent6, + event->name, + loglevel_string_pre(event->loglevel), + event->loglevel, + ll_value, + loglevel_string_post(event->loglevel), + enabled_string(event->enabled)); + break; + } + case LTTNG_EVENT_PROBE: + MSG("%s%s (type: probe)%s", indent6, + event->name, enabled_string(event->enabled)); + if (event->attr.probe.addr != 0) { + MSG("%saddr: 0x%" PRIx64, indent8, event->attr.probe.addr); + } else { + MSG("%soffset: 0x%" PRIx64, indent8, event->attr.probe.offset); + MSG("%ssymbol: %s", indent8, event->attr.probe.symbol_name); + } + break; + case LTTNG_EVENT_FUNCTION: + case LTTNG_EVENT_FUNCTION_ENTRY: + MSG("%s%s (type: function)%s", indent6, + event->name, enabled_string(event->enabled)); + MSG("%ssymbol: \"%s\"", indent8, event->attr.ftrace.symbol_name); + break; + case LTTNG_EVENT_SYSCALL: + MSG("%s (type: syscall)%s", indent6, + enabled_string(event->enabled)); + break; + case LTTNG_EVENT_NOOP: + MSG("%s (type: noop)%s", indent6, + enabled_string(event->enabled)); + break; + case LTTNG_EVENT_TRACEPOINT_LOGLEVEL: + MSG("%s%s (type: tracepoint loglevel)%s", indent6, + event->name, + enabled_string(event->enabled)); + break; + case LTTNG_EVENT_ALL: + /* We should never have "all" events in list. */ + assert(0); + break; + } +} + +/* + * Ask session daemon for all user space tracepoints available. + */ +static int list_ust_events(void) +{ + int i, size; + struct lttng_domain domain; + struct lttng_handle *handle; + struct lttng_event *event_list; + pid_t cur_pid = 0; + + DBG("Getting UST tracing events"); + + domain.type = LTTNG_DOMAIN_UST; + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + goto error; + } + + size = lttng_list_tracepoints(handle, &event_list); + if (size < 0) { + ERR("Unable to list UST events"); + return size; + } + + MSG("UST events:\n-------------"); + + if (size == 0) { + MSG("None"); + } + + for (i = 0; i < size; i++) { + if (cur_pid != event_list[i].pid) { + cur_pid = event_list[i].pid; + MSG("\nPID: %d - Name: %s", cur_pid, get_cmdline_by_pid(cur_pid)); + } + print_events(&event_list[i]); + } + + MSG(""); + + free(event_list); + + return CMD_SUCCESS; + +error: + return -1; +} + +/* + * Ask for all trace events in the kernel and pretty print them. + */ +static int list_kernel_events(void) +{ + int i, size; + struct lttng_domain domain; + struct lttng_handle *handle; + struct lttng_event *event_list; + + DBG("Getting kernel tracing events"); + + domain.type = LTTNG_DOMAIN_KERNEL; + + handle = lttng_create_handle(NULL, &domain); + if (handle == NULL) { + goto error; + } + + size = lttng_list_tracepoints(handle, &event_list); + if (size < 0) { + ERR("Unable to list kernel events"); + return size; + } + + MSG("Kernel events:\n-------------"); + + for (i = 0; i < size; i++) { + print_events(&event_list[i]); + } + + MSG(""); + + free(event_list); + + return CMD_SUCCESS; + +error: + return -1; +} + +/* + * List events of channel of session and domain. + */ +static int list_events(const char *channel_name) +{ + int ret, count, i; + struct lttng_event *events = NULL; + + count = lttng_list_events(handle, channel_name, &events); + if (count < 0) { + ret = count; + goto error; + } + + MSG("\n%sEvents:", indent4); + if (count == 0) { + MSG("%sNone\n", indent6); + goto end; + } + + for (i = 0; i < count; i++) { + print_events(&events[i]); + } + + MSG(""); + +end: + if (events) { + free(events); + } + ret = CMD_SUCCESS; + +error: + return ret; +} + +/* + * Pretty print channel + */ +static void print_channel(struct lttng_channel *channel) +{ + MSG("- %s:%s\n", channel->name, enabled_string(channel->enabled)); + + MSG("%sAttributes:", indent4); + MSG("%soverwrite mode: %d", indent6, channel->attr.overwrite); + MSG("%ssubbufers size: %" PRIu64, indent6, channel->attr.subbuf_size); + MSG("%snumber of subbufers: %" PRIu64, indent6, channel->attr.num_subbuf); + MSG("%sswitch timer interval: %u", indent6, channel->attr.switch_timer_interval); + MSG("%sread timer interval: %u", indent6, channel->attr.read_timer_interval); + switch (channel->attr.output) { + case LTTNG_EVENT_SPLICE: + MSG("%soutput: splice()", indent6); + break; + case LTTNG_EVENT_MMAP: + MSG("%soutput: mmap()", indent6); + break; + } +} + +/* + * List channel(s) of session and domain. + * + * If channel_name is NULL, all channels are listed. + */ +static int list_channels(const char *channel_name) +{ + int count, i, ret = CMD_SUCCESS; + unsigned int chan_found = 0; + struct lttng_channel *channels = NULL; + + DBG("Listing channel(s) (%s)", channel_name ? : ""); + + count = lttng_list_channels(handle, &channels); + if (count < 0) { + ret = count; + goto error; + } else if (count == 0) { + MSG("No channel found"); + goto end; + } + + if (channel_name == NULL) { + MSG("Channels:\n-------------"); + } + + for (i = 0; i < count; i++) { + if (channel_name != NULL) { + if (strncmp(channels[i].name, channel_name, NAME_MAX) == 0) { + chan_found = 1; + } else { + continue; + } + } + print_channel(&channels[i]); + + /* Listing events per channel */ + ret = list_events(channels[i].name); + if (ret < 0) { + MSG("%s", lttng_strerror(ret)); + } + + if (chan_found) { + break; + } + } + + if (!chan_found && channel_name != NULL) { + MSG("Channel %s not found", channel_name); + } + +end: + free(channels); + ret = CMD_SUCCESS; + +error: + return ret; +} + +/* + * List available tracing session. List only basic information. + * + * If session_name is NULL, all sessions are listed. + */ +static int list_sessions(const char *session_name) +{ + int ret, count, i; + unsigned int session_found = 0; + struct lttng_session *sessions; + + count = lttng_list_sessions(&sessions); + DBG("Session count %d", count); + if (count < 0) { + ret = count; + goto error; + } + + if (session_name == NULL) { + MSG("Available tracing sessions:"); + } + + for (i = 0; i < count; i++) { + if (session_name != NULL) { + if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) { + session_found = 1; + MSG("Tracing session %s:%s", session_name, active_string(sessions[i].enabled)); + MSG("%sTrace path: %s\n", indent4, sessions[i].path); + break; + } + continue; + } + + MSG(" %d) %s (%s)%s", i + 1, sessions[i].name, sessions[i].path, active_string(sessions[i].enabled)); + + if (session_found) { + break; + } + } + + free(sessions); + + if (!session_found && session_name != NULL) { + MSG("Session %s not found", session_name); + } + + if (session_name == NULL) { + MSG("\nUse lttng list for more details"); + } + + return CMD_SUCCESS; + +error: + return ret; +} + +/* + * List available domain(s) for a session. + */ +static int list_domains(const char *session_name) +{ + int i, count, ret = CMD_SUCCESS; + struct lttng_domain *domains = NULL; + + MSG("Domains:\n-------------"); + + count = lttng_list_domains(session_name, &domains); + if (count < 0) { + ret = count; + goto error; + } else if (count == 0) { + MSG(" None"); + goto end; + } + + for (i = 0; i < count; i++) { + switch (domains[i].type) { + case LTTNG_DOMAIN_KERNEL: + MSG(" - Kernel"); + break; + case LTTNG_DOMAIN_UST: + MSG(" - UST global"); + break; + default: + break; + } + } + +end: + free(domains); + +error: + return ret; +} + +/* + * The 'list ' first level command + */ +int cmd_list(int argc, const char **argv) +{ + int opt, i, ret = CMD_SUCCESS; + int nb_domain; + const char *session_name; + static poptContext pc; + struct lttng_domain domain; + struct lttng_domain *domains = NULL; + + if (argc < 1) { + usage(stderr); + goto end; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + goto end; + case OPT_USERSPACE: + opt_userspace = 1; + break; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + if (opt_pid != 0) { + MSG("*** Userspace tracing not implemented for PID ***\n"); + } + + /* Get session name (trailing argument) */ + session_name = poptGetArg(pc); + DBG2("Session name: %s", session_name); + + if (opt_kernel) { + domain.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_userspace) { + DBG2("Listing userspace global domain"); + domain.type = LTTNG_DOMAIN_UST; + } + + handle = lttng_create_handle(session_name, &domain); + if (handle == NULL) { + goto end; + } + + if (session_name == NULL) { + if (!opt_kernel && !opt_userspace) { + ret = list_sessions(NULL); + if (ret < 0) { + goto end; + } + } + if (opt_kernel) { + ret = list_kernel_events(); + if (ret < 0) { + goto end; + } + } + if (opt_userspace) { + ret = list_ust_events(); + if (ret < 0) { + goto end; + } + } + } else { + /* List session attributes */ + ret = list_sessions(session_name); + if (ret < 0) { + goto end; + } + + /* Domain listing */ + if (opt_domain) { + ret = list_domains(session_name); + goto end; + } + + if (opt_kernel) { + /* Channel listing */ + ret = list_channels(opt_channel); + if (ret < 0) { + goto end; + } + } else { + /* We want all domain(s) */ + nb_domain = lttng_list_domains(session_name, &domains); + if (nb_domain < 0) { + ret = nb_domain; + goto end; + } + + for (i = 0; i < nb_domain; i++) { + switch (domains[i].type) { + case LTTNG_DOMAIN_KERNEL: + MSG("=== Domain: Kernel ===\n"); + break; + case LTTNG_DOMAIN_UST: + MSG("=== Domain: UST global ===\n"); + break; + default: + MSG("=== Domain: Unimplemented ===\n"); + break; + } + + /* Clean handle before creating a new one */ + lttng_destroy_handle(handle); + + handle = lttng_create_handle(session_name, &domains[i]); + if (handle == NULL) { + goto end; + } + + ret = list_channels(opt_channel); + if (ret < 0) { + goto end; + } + } + } + } + +end: + if (domains) { + free(domains); + } + lttng_destroy_handle(handle); + + return ret; +} diff --git a/src/bin/lttng/commands/set_session.c b/src/bin/lttng/commands/set_session.c new file mode 100644 index 000000000..8a76403ad --- /dev/null +++ b/src/bin/lttng/commands/set_session.c @@ -0,0 +1,112 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_session_name; + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng set-session NAME\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, "\n"); +} + +/* + * set_session + */ +static int set_session(void) +{ + int ret = CMD_SUCCESS; + + ret = config_init(opt_session_name); + if (ret < 0) { + ERR("Unable to set session name"); + ret = CMD_ERROR; + goto error; + } + + MSG("Session set to %s", opt_session_name); + ret = CMD_SUCCESS; + +error: + return ret; +} + +/* + * cmd_set_session + */ +int cmd_set_session(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char *) poptGetArg(pc); + if (opt_session_name == NULL) { + ERR("Missing session name"); + usage(stderr); + goto end; + } + + ret = set_session(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/start.c b/src/bin/lttng/commands/start.c new file mode 100644 index 000000000..2eacc462f --- /dev/null +++ b/src/bin/lttng/commands/start.c @@ -0,0 +1,127 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_session_name; + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng start [options] [NAME]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); + fprintf(ofp, "get it from the configuration directory (.lttng).\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, "\n"); +} + +/* + * start_tracing + * + * Start tracing for all trace of the session. + */ +static int start_tracing(void) +{ + int ret = CMD_SUCCESS; + char *session_name; + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + DBG("Starting tracing for session %s", session_name); + + ret = lttng_start_tracing(session_name); + if (ret < 0) { + goto free_name; + } + + MSG("Tracing started for session %s", session_name); + +free_name: + if (opt_session_name == NULL) { + free(session_name); + } +error: + return ret; +} + +/* + * cmd_start + * + * The 'start ' first level command + */ +int cmd_start(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + ret = start_tracing(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/stop.c b/src/bin/lttng/commands/stop.c new file mode 100644 index 000000000..f1299b5bc --- /dev/null +++ b/src/bin/lttng/commands/stop.c @@ -0,0 +1,124 @@ +/* + * 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 "../cmd.h" +#include "../conf.h" +#include "../utils.h" + +static char *opt_session_name; + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng stop [options] [NAME]\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Where NAME is an optional session name. If not specified, lttng will\n"); + fprintf(ofp, "get it from the configuration directory (.lttng).\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, "\n"); +} + +/* + * Start tracing for all trace of the session. + */ +static int stop_tracing(void) +{ + int ret = CMD_SUCCESS; + char *session_name; + + if (opt_session_name == NULL) { + session_name = get_session_name(); + if (session_name == NULL) { + ret = CMD_ERROR; + goto error; + } + } else { + session_name = opt_session_name; + } + + ret = lttng_stop_tracing(session_name); + if (ret < 0) { + goto free_name; + } + + MSG("Tracing stopped for session %s", session_name); + +free_name: + if (opt_session_name == NULL) { + free(session_name); + } + +error: + return ret; +} + +/* + * cmd_stop + * + * The 'stop ' first level command + */ +int cmd_stop(int argc, const char **argv) +{ + int opt, ret; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + opt_session_name = (char*) poptGetArg(pc); + + ret = stop_tracing(); + +end: + return ret; +} diff --git a/src/bin/lttng/commands/version.c b/src/bin/lttng/commands/version.c new file mode 100644 index 000000000..4d8d75b12 --- /dev/null +++ b/src/bin/lttng/commands/version.c @@ -0,0 +1,82 @@ +/* + * 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 "../cmd.h" + +enum { + OPT_HELP = 1, +}; + +static struct poptOption long_options[] = { + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */ + {"help", 'h', POPT_ARG_NONE, 0, OPT_HELP, 0, 0}, + {0, 0, 0, 0, 0, 0, 0} +}; + +/* + * usage + */ +static void usage(FILE *ofp) +{ + fprintf(ofp, "usage: lttng version\n"); + fprintf(ofp, "\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, "\n"); +} + +/* + * cmd_version + */ +int cmd_version(int argc, const char **argv) +{ + int opt, ret = CMD_SUCCESS; + static poptContext pc; + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptReadDefaultConfig(pc, 0); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_HELP: + usage(stderr); + ret = CMD_SUCCESS; + goto end; + default: + usage(stderr); + ret = CMD_UNDEFINED; + goto end; + } + } + + MSG("lttng version " VERSION); + MSG("Web site: http://lttng.org/"); + MSG("\nlttng is free software and under the GPL license."); + +end: + return ret; +} diff --git a/src/bin/lttng/conf.c b/src/bin/lttng/conf.c new file mode 100644 index 000000000..9d2bfbb66 --- /dev/null +++ b/src/bin/lttng/conf.c @@ -0,0 +1,254 @@ +/* + * 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 + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "conf.h" + +/* + * config_get_file_path + * + * Return the path with '/CONFIG_FILENAME' added to it. + */ +char *config_get_file_path(char *path) +{ + int ret; + char *file_path; + + ret = asprintf(&file_path, "%s/%s", path, CONFIG_FILENAME); + if (ret < 0) { + ERR("Fail allocating config file path"); + } + + return file_path; +} + +/* + * open_config + * + * Return an open FILE pointer to the config file. + */ +static FILE *open_config(char *path, const char *mode) +{ + FILE *fp = NULL; + char *file_path; + + file_path = config_get_file_path(path); + if (file_path == NULL) { + goto error; + } + + fp = fopen(file_path, mode); + if (fp == NULL) { + goto error; + } + +error: + if (file_path) { + free(file_path); + } + return fp; +} + +/* + * create_config_file + * + * Create the empty config file a the path. + */ +static int create_config_file(char *path) +{ + int ret; + FILE *fp; + + fp = open_config(path, "w+"); + if (fp == NULL) { + ERR("Unable to create config file"); + ret = -1; + goto error; + } + + ret = fclose(fp); + +error: + return ret; +} + +/* + * write_config + * + * Append data to the config file in file_path + */ +static int write_config(char *file_path, size_t size, char *data) +{ + FILE *fp; + size_t len; + int ret = 0; + + fp = open_config(file_path, "a"); + if (fp == NULL) { + ret = -1; + goto end; + } + + /* Write session name into config file */ + len = fwrite(data, size, 1, fp); + if (len < 1) { + ret = -1; + } + fclose(fp); +end: + return ret; +} + +/* + * config_get_default_path + * + * Return the HOME directory path. Caller MUST NOT free(3) the return pointer. + */ +char *config_get_default_path(void) +{ + return getenv("HOME"); +} + +/* + * config_destroy + * + * Destroy directory config and file config. + */ +void config_destroy(char *path) +{ + int ret; + char *config_path; + + config_path = config_get_file_path(path); + if (config_path == NULL) { + return; + } + + ret = remove(config_path); + if (ret < 0) { + perror("remove config file"); + } + + free(config_path); +} + +/* + * config_read_session_name + * + * Return sesson name from the config file. + */ +char *config_read_session_name(char *path) +{ + int ret; + FILE *fp; + char var[NAME_MAX], *session_name; + + fp = open_config(path, "r"); + if (fp == NULL) { + ERR("Can't find valid lttng config %s/.lttngrc", path); + MSG("Did you create a session? (lttng create )"); + goto error; + } + + session_name = malloc(NAME_MAX); + while (!feof(fp)) { + if ((ret = fscanf(fp, "%[^'=']=%s\n", var, session_name)) != 2) { + if (ret == -1) { + ERR("Missing session=NAME in config file."); + goto error; + } + continue; + } + + if (strcmp(var, "session") == 0) { + goto found; + } + } + + fclose(fp); + +error: + return NULL; + +found: + fclose(fp); + return session_name; + +} + +/* + * config_add_session_name + * + * Write session name option to the config file. + */ +int config_add_session_name(char *path, char *name) +{ + int ret; + char session_name[NAME_MAX]; + + ret = snprintf(session_name, NAME_MAX, "session=%s\n", name); + if (ret < 0) { + goto error; + } + ret = write_config(path, ret, session_name); +error: + return ret; +} + +/* + * config_init + * + * Init configuration directory and file. + */ +int config_init(char *session_name) +{ + int ret; + char *path; + + path = config_get_default_path(); + if (path == NULL) { + ret = -1; + goto error; + } + + /* Create default config file */ + ret = create_config_file(path); + if (ret < 0) { + goto error; + } + + ret = config_add_session_name(path, session_name); + if (ret < 0) { + goto error; + } + + DBG("Init config session in %s", path); + +error: + return ret; +} diff --git a/src/bin/lttng/conf.h b/src/bin/lttng/conf.h new file mode 100644 index 000000000..a7672e413 --- /dev/null +++ b/src/bin/lttng/conf.h @@ -0,0 +1,33 @@ +/* + * 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 _LTTNG_CONFIG_H +#define _LTTNG_CONFIG_H + +#define CONFIG_FILENAME ".lttngrc" + +void config_destroy(char *path); +int config_init(char *path); +int config_add_session_name(char *path, char *name); +char *config_get_default_path(void); + +/* Must free() the return pointer */ +char *config_read_session_name(char *path); +char *config_get_file_path(char *path); + +#endif /* _LTTNG_CONFIG_H */ diff --git a/src/bin/lttng/lttng.c b/src/bin/lttng/lttng.c new file mode 100644 index 000000000..65cea55a2 --- /dev/null +++ b/src/bin/lttng/lttng.c @@ -0,0 +1,491 @@ +/* + * 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 + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cmd.h" +#include "conf.h" + +/* Variables */ +static char *progname; + +int opt_quiet; +int opt_verbose; +static int opt_no_sessiond; +static char *opt_sessiond_path; + +enum { + OPT_SESSION_PATH, + OPT_DUMP_OPTIONS, + OPT_DUMP_COMMANDS, +}; + +/* Getopt options. No first level command. */ +static struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"group", 1, NULL, 'g'}, + {"verbose", 0, NULL, 'v'}, + {"quiet", 0, NULL, 'q'}, + {"no-sessiond", 0, NULL, 'n'}, + {"sessiond-path", 1, NULL, OPT_SESSION_PATH}, + {"list-options", 0, NULL, OPT_DUMP_OPTIONS}, + {"list-commands", 0, NULL, OPT_DUMP_COMMANDS}, + {NULL, 0, NULL, 0} +}; + +/* First level command */ +static struct cmd_struct commands[] = { + { "list", cmd_list}, + { "create", cmd_create}, + { "destroy", cmd_destroy}, + { "start", cmd_start}, + { "stop", cmd_stop}, + { "enable-event", cmd_enable_events}, + { "disable-event", cmd_disable_events}, + { "enable-channel", cmd_enable_channels}, + { "disable-channel", cmd_disable_channels}, + { "add-context", cmd_add_context}, + { "set-session", cmd_set_session}, + { "version", cmd_version}, + { "calibrate", cmd_calibrate}, + { NULL, NULL} /* Array closure */ +}; + +static void usage(FILE *ofp) +{ + fprintf(ofp, "LTTng Trace Control " VERSION"\n\n"); + fprintf(ofp, "usage: lttng [options] \n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -g, --group NAME Unix tracing group name. (default: tracing)\n"); + fprintf(ofp, " -v, --verbose Verbose mode\n"); + fprintf(ofp, " -q, --quiet Quiet mode\n"); + fprintf(ofp, " -n, --no-sessiond Don't spawn a session daemon\n"); + fprintf(ofp, " --sessiond-path Session daemon full path\n"); + fprintf(ofp, " --list-options Simple listing of lttng options\n"); + fprintf(ofp, " --list-commands Simple listing of lttng commands\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Commands:\n"); + fprintf(ofp, " add-context Add context to event or/and channel\n"); + fprintf(ofp, " calibrate Quantify LTTng overhead\n"); + fprintf(ofp, " create Create tracing session\n"); + fprintf(ofp, " destroy Teardown tracing session\n"); + fprintf(ofp, " enable-channel Enable tracing channel\n"); + fprintf(ofp, " enable-event Enable tracing event\n"); + fprintf(ofp, " disable-channel Disable tracing channel\n"); + fprintf(ofp, " disable-event Disable tracing event\n"); + fprintf(ofp, " list List possible tracing options\n"); + fprintf(ofp, " set-session Set current session name\n"); + fprintf(ofp, " start Start tracing\n"); + fprintf(ofp, " stop Stop tracing\n"); + fprintf(ofp, " version Show version information\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Please see the lttng(1) man page for full documentation.\n"); + fprintf(ofp, "See http://lttng.org for updates, bug reports and news.\n"); +} + +/* + * list_options + * + * List options line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +static void list_options(FILE *ofp) +{ + int i = 0; + struct option *option = NULL; + + option = &long_options[i]; + while (option->name != NULL) { + fprintf(ofp, "--%s\n", option->name); + + if (isprint(option->val)) { + fprintf(ofp, "-%c\n", option->val); + } + + i++; + option = &long_options[i]; + } +} + +/* + * list_commands + * + * List commands line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +static void list_commands(FILE *ofp) +{ + int i = 0; + struct cmd_struct *cmd = NULL; + + cmd = &commands[i]; + while (cmd->name != NULL) { + fprintf(ofp, "%s\n", cmd->name); + i++; + cmd = &commands[i]; + } +} + +/* + * clean_exit + */ +static void clean_exit(int code) +{ + DBG("Clean exit"); + exit(code); +} + +/* + * sighandler + * + * Signal handler for the daemon + */ +static void sighandler(int sig) +{ + switch (sig) { + case SIGTERM: + DBG("SIGTERM catched"); + clean_exit(EXIT_FAILURE); + break; + case SIGCHLD: + /* Notify is done */ + DBG("SIGCHLD catched"); + break; + default: + DBG("Unknown signal %d catched", sig); + break; + } + + return; +} + +/* + * set_signal_handler + * + * Setup signal handler for SIGCHLD and SIGTERM. + */ +static int set_signal_handler(void) +{ + int ret = 0; + struct sigaction sa; + sigset_t sigset; + + if ((ret = sigemptyset(&sigset)) < 0) { + perror("sigemptyset"); + goto end; + } + + sa.sa_handler = sighandler; + sa.sa_mask = sigset; + sa.sa_flags = 0; + if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) { + perror("sigaction"); + goto end; + } + + if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { + perror("sigaction"); + goto end; + } + +end: + return ret; +} + +/* + * handle_command + * + * Handle the full argv list of a first level command. Will find the command + * in the global commands array and call the function callback associated. + * + * If command not found, return -1 + * else, return function command error code. + */ +static int handle_command(int argc, char **argv) +{ + int i = 0, ret; + struct cmd_struct *cmd; + + if (*argv == NULL) { + ret = CMD_SUCCESS; + goto end; + } + + cmd = &commands[i]; + while (cmd->func != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + ret = cmd->func(argc, (const char**) argv); + switch (ret) { + case CMD_ERROR: + ERR("Command error"); + break; + case CMD_NOT_IMPLEMENTED: + ERR("Options not implemented"); + break; + case CMD_UNDEFINED: + ERR("Undefined command"); + break; + case CMD_FATAL: + ERR("Fatal error"); + break; + } + goto end; + } + i++; + cmd = &commands[i]; + } + + /* Command not found */ + ret = -1; + +end: + return ret; +} + +/* + * spawn_sessiond + * + * Spawn a session daemon by forking and execv. + */ +static int spawn_sessiond(char *pathname) +{ + int ret = 0; + pid_t pid; + + MSG("Spawning a session daemon"); + pid = fork(); + if (pid == 0) { + /* + * Spawn session daemon and tell + * it to signal us when ready. + */ + 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."); + } else { + perror("execlp"); + } + kill(getppid(), SIGTERM); /* unpause parent */ + exit(EXIT_FAILURE); + } else if (pid > 0) { + /* Wait for lttng-sessiond to start */ + pause(); + goto end; + } else { + perror("fork"); + ret = -1; + goto end; + } + +end: + return ret; +} + +/* + * check_sessiond + * + * Check if the session daemon is available using + * the liblttngctl API for the check. If not, try to + * spawn a daemon. + */ +static int check_sessiond(void) +{ + int ret; + char *pathname = NULL, *alloc_pathname = NULL; + + ret = lttng_session_daemon_alive(); + if (ret == 0) { /* not alive */ + /* Try command line option path */ + if (opt_sessiond_path != NULL) { + ret = access(opt_sessiond_path, F_OK | X_OK); + if (ret < 0) { + ERR("No such file: %s", opt_sessiond_path); + goto end; + } + pathname = opt_sessiond_path; + } else { + /* Try LTTNG_SESSIOND_PATH env variable */ + pathname = getenv(LTTNG_SESSIOND_PATH_ENV); + } + + /* Let's rock and roll */ + if (pathname == NULL) { + ret = asprintf(&alloc_pathname, INSTALL_BIN_PATH "/lttng-sessiond"); + if (ret < 0) { + perror("asprintf spawn sessiond"); + goto end; + } + pathname = alloc_pathname; + } + + ret = spawn_sessiond(pathname); + free(alloc_pathname); + if (ret < 0) { + ERR("Problem occurs when starting %s", pathname); + goto end; + } + } + +end: + return ret; +} + +/* + * Check for the "help" option in the argv. If found, return 1 else return 0. + */ +static int check_help_command(int argc, char **argv) +{ + int i; + + for (i = 0; i < argc; i++) { + if ((strncmp(argv[i], "-h", 2) == 0) || + strncmp(argv[i], "--h", 3) == 0) { + return 1; + } + } + + return 0; +} + +/* + * parse_args + * + * Parse command line arguments. + * Return 0 if OK, else -1 + */ +static int parse_args(int argc, char **argv) +{ + int opt, ret; + + if (argc < 2) { + usage(stderr); + clean_exit(EXIT_FAILURE); + } + + while ((opt = getopt_long(argc, argv, "+hnvqg:", long_options, NULL)) != -1) { + switch (opt) { + case 'h': + usage(stderr); + goto error; + case 'v': + opt_verbose += 1; + break; + case 'q': + opt_quiet = 1; + break; + case 'g': + lttng_set_tracing_group(optarg); + break; + case 'n': + opt_no_sessiond = 1; + break; + case OPT_SESSION_PATH: + opt_sessiond_path = strdup(optarg); + break; + case OPT_DUMP_OPTIONS: + list_options(stdout); + ret = 0; + goto error; + case OPT_DUMP_COMMANDS: + list_commands(stdout); + ret = 0; + goto error; + default: + usage(stderr); + goto error; + } + } + + /* If both options are specified, quiet wins */ + if (opt_verbose && opt_quiet) { + opt_verbose = 0; + } + + /* Spawn session daemon if needed */ + if (opt_no_sessiond == 0 && check_help_command(argc, argv) == 0 && + (check_sessiond() < 0)) { + goto error; + } + + /* No leftovers, print usage and quit */ + if ((argc - optind) == 0) { + usage(stderr); + goto error; + } + + /* + * Handle leftovers which is a first level command with the trailing + * options. + */ + ret = handle_command(argc - optind, argv + optind); + if (ret < 0) { + if (ret == -1) { + usage(stderr); + } else { + ERR("%s", lttng_strerror(ret)); + } + goto error; + } + + return 0; + +error: + return -1; +} + + +/* + * main + */ +int main(int argc, char *argv[]) +{ + int ret; + + progname = argv[0] ? argv[0] : "lttng"; + + /* For Mathieu Desnoyers aka Dr Tracing */ + if (strncmp(progname, "drtrace", 7) == 0) { + MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0); + } + + ret = set_signal_handler(); + if (ret < 0) { + clean_exit(ret); + } + + ret = parse_args(argc, argv); + if (ret < 0) { + clean_exit(EXIT_FAILURE); + } + + return 0; +} diff --git a/src/bin/lttng/utils.c b/src/bin/lttng/utils.c new file mode 100644 index 000000000..25d463adf --- /dev/null +++ b/src/bin/lttng/utils.c @@ -0,0 +1,50 @@ +/* + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include + +#include "conf.h" + +/* + * get_session_name + * + * Return allocated string with the session name found in the config + * directory. + */ +char *get_session_name(void) +{ + char *path, *session_name = NULL; + + /* Get path to config file */ + path = config_get_default_path(); + if (path == NULL) { + goto error; + } + + /* Get session name from config */ + session_name = config_read_session_name(path); + if (session_name == NULL) { + goto error; + } + +error: + DBG("Session name found: %s", session_name); + return session_name; +} diff --git a/src/bin/lttng/utils.h b/src/bin/lttng/utils.h new file mode 100644 index 000000000..5492d5e67 --- /dev/null +++ b/src/bin/lttng/utils.h @@ -0,0 +1,26 @@ +/* + * 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 _LTTNG_UTILS_H +#define _LTTNG_UTILS_H + +char *get_config_file_path(void); +char *get_session_name(void); +int set_session_name(char *name); + +#endif /* _LTTNG_UTILS_H */ diff --git a/src/common/Makefile.am b/src/common/Makefile.am new file mode 100644 index 000000000..84c0b16a1 --- /dev/null +++ b/src/common/Makefile.am @@ -0,0 +1,24 @@ +AM_CPPFLAGS = + +SUBDIRS = hashtable kernel-ctl sessiond-comm kernel-consumer ust-consumer + +AM_CFLAGS = -fno-strict-aliasing + +noinst_LTLIBRARIES = libcommon.la + +libcommon_la_SOURCES = runas.c runas.h + +# Consumer library +noinst_LTLIBRARIES += libconsumer.la + +libconsumer_la_SOURCES = consumer.c + +libconsumer_la_LIBADD = \ + $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la \ + $(top_builddir)/src/common/kernel-consumer/libkernel-consumer.la \ + $(top_builddir)/src/common/hashtable/libhashtable.la + +if HAVE_LIBLTTNG_UST_CTL +libconsumer_la_LIBADD += \ + $(top_builddir)/src/common/ust-consumer/libust-consumer.la +endif diff --git a/src/common/consumer.c b/src/common/consumer.c new file mode 100644 index 000000000..9e7256568 --- /dev/null +++ b/src/common/consumer.c @@ -0,0 +1,1168 @@ +/* + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "consumer.h" + +struct lttng_consumer_global_data consumer_data = { + .stream_count = 0, + .need_update = 1, + .type = LTTNG_CONSUMER_UNKNOWN, +}; + +/* timeout parameter, to control the polling thread grace period. */ +int consumer_poll_timeout = -1; + +/* + * Flag to inform the polling thread to quit when all fd hung up. Updated by + * the consumer_thread_receive_fds when it notices that all fds has hung up. + * Also updated by the signal handler (consumer_should_exit()). Read by the + * polling threads. + */ +volatile int consumer_quit = 0; + +/* + * Find a stream. The consumer_data.lock must be locked during this + * call. + */ +static struct lttng_consumer_stream *consumer_find_stream(int key) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + struct lttng_consumer_stream *stream = NULL; + + /* Negative keys are lookup failures */ + if (key < 0) + return NULL; + + rcu_read_lock(); + + lttng_ht_lookup(consumer_data.stream_ht, (void *)((unsigned long) key), + &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node != NULL) { + stream = caa_container_of(node, struct lttng_consumer_stream, node); + } + + rcu_read_unlock(); + + return stream; +} + +static void consumer_steal_stream_key(int key) +{ + struct lttng_consumer_stream *stream; + + stream = consumer_find_stream(key); + if (stream) + stream->key = -1; +} + +static struct lttng_consumer_channel *consumer_find_channel(int key) +{ + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + struct lttng_consumer_channel *channel = NULL; + + /* Negative keys are lookup failures */ + if (key < 0) + return NULL; + + rcu_read_lock(); + + lttng_ht_lookup(consumer_data.channel_ht, (void *)((unsigned long) key), + &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + if (node != NULL) { + channel = caa_container_of(node, struct lttng_consumer_channel, node); + } + + rcu_read_unlock(); + + return channel; +} + +static void consumer_steal_channel_key(int key) +{ + struct lttng_consumer_channel *channel; + + channel = consumer_find_channel(key); + if (channel) + channel->key = -1; +} + +/* + * Remove a stream from the global list protected by a mutex. This + * function is also responsible for freeing its data structures. + */ +void consumer_del_stream(struct lttng_consumer_stream *stream) +{ + int ret; + struct lttng_ht_iter iter; + struct lttng_consumer_channel *free_chan = NULL; + + pthread_mutex_lock(&consumer_data.lock); + + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + if (stream->mmap_base != NULL) { + ret = munmap(stream->mmap_base, stream->mmap_len); + if (ret != 0) { + perror("munmap"); + } + } + break; + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + lttng_ustconsumer_del_stream(stream); + break; + default: + ERR("Unknown consumer_data type"); + assert(0); + goto end; + } + + rcu_read_lock(); + + /* Get stream node from hash table */ + lttng_ht_lookup(consumer_data.stream_ht, + (void *)((unsigned long) stream->key), &iter); + /* Remove stream node from hash table */ + ret = lttng_ht_del(consumer_data.stream_ht, &iter); + assert(!ret); + + rcu_read_unlock(); + + if (consumer_data.stream_count <= 0) { + goto end; + } + consumer_data.stream_count--; + if (!stream) { + goto end; + } + if (stream->out_fd >= 0) { + close(stream->out_fd); + } + if (stream->wait_fd >= 0 && !stream->wait_fd_is_copy) { + close(stream->wait_fd); + } + if (stream->shm_fd >= 0 && stream->wait_fd != stream->shm_fd) { + close(stream->shm_fd); + } + if (!--stream->chan->refcount) + free_chan = stream->chan; + free(stream); +end: + consumer_data.need_update = 1; + pthread_mutex_unlock(&consumer_data.lock); + + if (free_chan) + consumer_del_channel(free_chan); +} + +static void consumer_del_stream_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_ulong *node = + caa_container_of(head, struct lttng_ht_node_ulong, head); + struct lttng_consumer_stream *stream = + caa_container_of(node, struct lttng_consumer_stream, node); + + consumer_del_stream(stream); +} + +struct lttng_consumer_stream *consumer_allocate_stream( + int channel_key, int stream_key, + int shm_fd, int wait_fd, + enum lttng_consumer_stream_state state, + uint64_t mmap_len, + enum lttng_event_output output, + const char *path_name, + uid_t uid, + gid_t gid) +{ + struct lttng_consumer_stream *stream; + int ret; + + stream = zmalloc(sizeof(*stream)); + if (stream == NULL) { + perror("malloc struct lttng_consumer_stream"); + goto end; + } + stream->chan = consumer_find_channel(channel_key); + if (!stream->chan) { + perror("Unable to find channel key"); + goto end; + } + stream->chan->refcount++; + stream->key = stream_key; + stream->shm_fd = shm_fd; + stream->wait_fd = wait_fd; + stream->out_fd = -1; + stream->out_fd_offset = 0; + stream->state = state; + stream->mmap_len = mmap_len; + stream->mmap_base = NULL; + stream->output = output; + stream->uid = uid; + stream->gid = gid; + strncpy(stream->path_name, path_name, PATH_MAX - 1); + stream->path_name[PATH_MAX - 1] = '\0'; + lttng_ht_node_init_ulong(&stream->node, stream->key); + + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + break; + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + stream->cpu = stream->chan->cpucount++; + ret = lttng_ustconsumer_allocate_stream(stream); + if (ret) { + free(stream); + return NULL; + } + break; + default: + ERR("Unknown consumer_data type"); + assert(0); + goto end; + } + DBG("Allocated stream %s (key %d, shm_fd %d, wait_fd %d, mmap_len %llu, out_fd %d)", + stream->path_name, stream->key, + stream->shm_fd, + stream->wait_fd, + (unsigned long long) stream->mmap_len, + stream->out_fd); +end: + return stream; +} + +/* + * Add a stream to the global list protected by a mutex. + */ +int consumer_add_stream(struct lttng_consumer_stream *stream) +{ + int ret = 0; + + pthread_mutex_lock(&consumer_data.lock); + /* Steal stream identifier, for UST */ + consumer_steal_stream_key(stream->key); + rcu_read_lock(); + lttng_ht_add_unique_ulong(consumer_data.stream_ht, &stream->node); + rcu_read_unlock(); + consumer_data.stream_count++; + consumer_data.need_update = 1; + + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + break; + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + /* Streams are in CPU number order (we rely on this) */ + stream->cpu = stream->chan->nr_streams++; + break; + default: + ERR("Unknown consumer_data type"); + assert(0); + goto end; + } + +end: + pthread_mutex_unlock(&consumer_data.lock); + return ret; +} + +/* + * Update a stream according to what we just received. + */ +void consumer_change_stream_state(int stream_key, + enum lttng_consumer_stream_state state) +{ + struct lttng_consumer_stream *stream; + + pthread_mutex_lock(&consumer_data.lock); + stream = consumer_find_stream(stream_key); + if (stream) { + stream->state = state; + } + consumer_data.need_update = 1; + pthread_mutex_unlock(&consumer_data.lock); +} + +/* + * Remove a channel from the global list protected by a mutex. This + * function is also responsible for freeing its data structures. + */ +void consumer_del_channel(struct lttng_consumer_channel *channel) +{ + int ret; + struct lttng_ht_iter iter; + + pthread_mutex_lock(&consumer_data.lock); + + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + break; + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + lttng_ustconsumer_del_channel(channel); + break; + default: + ERR("Unknown consumer_data type"); + assert(0); + goto end; + } + + rcu_read_lock(); + + lttng_ht_lookup(consumer_data.channel_ht, + (void *)((unsigned long) channel->key), &iter); + ret = lttng_ht_del(consumer_data.channel_ht, &iter); + assert(!ret); + + rcu_read_unlock(); + + if (channel->mmap_base != NULL) { + ret = munmap(channel->mmap_base, channel->mmap_len); + if (ret != 0) { + perror("munmap"); + } + } + if (channel->wait_fd >= 0 && !channel->wait_fd_is_copy) { + close(channel->wait_fd); + } + if (channel->shm_fd >= 0 && channel->wait_fd != channel->shm_fd) { + close(channel->shm_fd); + } + free(channel); +end: + pthread_mutex_unlock(&consumer_data.lock); +} + +static void consumer_del_channel_rcu(struct rcu_head *head) +{ + struct lttng_ht_node_ulong *node = + caa_container_of(head, struct lttng_ht_node_ulong, head); + struct lttng_consumer_channel *channel= + caa_container_of(node, struct lttng_consumer_channel, node); + + consumer_del_channel(channel); +} + +struct lttng_consumer_channel *consumer_allocate_channel( + int channel_key, + int shm_fd, int wait_fd, + uint64_t mmap_len, + uint64_t max_sb_size) +{ + struct lttng_consumer_channel *channel; + int ret; + + channel = zmalloc(sizeof(*channel)); + if (channel == NULL) { + perror("malloc struct lttng_consumer_channel"); + goto end; + } + channel->key = channel_key; + channel->shm_fd = shm_fd; + channel->wait_fd = wait_fd; + channel->mmap_len = mmap_len; + channel->max_sb_size = max_sb_size; + channel->refcount = 0; + channel->nr_streams = 0; + lttng_ht_node_init_ulong(&channel->node, channel->key); + + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + channel->mmap_base = NULL; + channel->mmap_len = 0; + break; + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + ret = lttng_ustconsumer_allocate_channel(channel); + if (ret) { + free(channel); + return NULL; + } + break; + default: + ERR("Unknown consumer_data type"); + assert(0); + goto end; + } + DBG("Allocated channel (key %d, shm_fd %d, wait_fd %d, mmap_len %llu, max_sb_size %llu)", + channel->key, + channel->shm_fd, + channel->wait_fd, + (unsigned long long) channel->mmap_len, + (unsigned long long) channel->max_sb_size); +end: + return channel; +} + +/* + * Add a channel to the global list protected by a mutex. + */ +int consumer_add_channel(struct lttng_consumer_channel *channel) +{ + pthread_mutex_lock(&consumer_data.lock); + /* Steal channel identifier, for UST */ + consumer_steal_channel_key(channel->key); + rcu_read_lock(); + lttng_ht_add_unique_ulong(consumer_data.channel_ht, &channel->node); + rcu_read_unlock(); + pthread_mutex_unlock(&consumer_data.lock); + return 0; +} + +/* + * Allocate the pollfd structure and the local view of the out fds to avoid + * doing a lookup in the linked list and concurrency issues when writing is + * needed. Called with consumer_data.lock held. + * + * Returns the number of fds in the structures. + */ +int consumer_update_poll_array( + struct lttng_consumer_local_data *ctx, struct pollfd **pollfd, + struct lttng_consumer_stream **local_stream) +{ + int i = 0; + struct lttng_ht_iter iter; + struct lttng_consumer_stream *stream; + + DBG("Updating poll fd array"); + cds_lfht_for_each_entry(consumer_data.stream_ht->ht, &iter.iter, stream, + node.node) { + if (stream->state != LTTNG_CONSUMER_ACTIVE_STREAM) { + continue; + } + DBG("Active FD %d", stream->wait_fd); + (*pollfd)[i].fd = stream->wait_fd; + (*pollfd)[i].events = POLLIN | POLLPRI; + local_stream[i] = stream; + i++; + } + + /* + * Insert the consumer_poll_pipe at the end of the array and don't + * increment i so nb_fd is the number of real FD. + */ + (*pollfd)[i].fd = ctx->consumer_poll_pipe[0]; + (*pollfd)[i].events = POLLIN; + return i; +} + +/* + * Poll on the should_quit pipe and the command socket return -1 on error and + * should exit, 0 if data is available on the command socket + */ +int lttng_consumer_poll_socket(struct pollfd *consumer_sockpoll) +{ + int num_rdy; + + num_rdy = poll(consumer_sockpoll, 2, -1); + if (num_rdy == -1) { + perror("Poll error"); + goto exit; + } + if (consumer_sockpoll[0].revents == POLLIN) { + DBG("consumer_should_quit wake up"); + goto exit; + } + return 0; + +exit: + return -1; +} + +/* + * Set the error socket. + */ +void lttng_consumer_set_error_sock( + struct lttng_consumer_local_data *ctx, int sock) +{ + ctx->consumer_error_socket = sock; +} + +/* + * Set the command socket path. + */ + +void lttng_consumer_set_command_sock_path( + struct lttng_consumer_local_data *ctx, char *sock) +{ + ctx->consumer_command_sock_path = sock; +} + +/* + * Send return code to the session daemon. + * If the socket is not defined, we return 0, it is not a fatal error + */ +int lttng_consumer_send_error( + struct lttng_consumer_local_data *ctx, int cmd) +{ + if (ctx->consumer_error_socket > 0) { + return lttcomm_send_unix_sock(ctx->consumer_error_socket, &cmd, + sizeof(enum lttcomm_sessiond_command)); + } + + return 0; +} + +/* + * Close all the tracefiles and stream fds, should be called when all instances + * are destroyed. + */ +void lttng_consumer_cleanup(void) +{ + int ret; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; + + rcu_read_lock(); + + /* + * close all outfd. Called when there are no more threads running (after + * joining on the threads), no need to protect list iteration with mutex. + */ + cds_lfht_for_each_entry(consumer_data.stream_ht->ht, &iter.iter, node, + node) { + ret = lttng_ht_del(consumer_data.stream_ht, &iter); + assert(!ret); + call_rcu(&node->head, consumer_del_stream_rcu); + } + + cds_lfht_for_each_entry(consumer_data.channel_ht->ht, &iter.iter, node, + node) { + ret = lttng_ht_del(consumer_data.channel_ht, &iter); + assert(!ret); + call_rcu(&node->head, consumer_del_channel_rcu); + } + + rcu_read_unlock(); +} + +/* + * Called from signal handler. + */ +void lttng_consumer_should_exit(struct lttng_consumer_local_data *ctx) +{ + int ret; + consumer_quit = 1; + ret = write(ctx->consumer_should_quit[1], "4", 1); + if (ret < 0) { + perror("write consumer quit"); + } +} + +void lttng_consumer_sync_trace_file( + struct lttng_consumer_stream *stream, off_t orig_offset) +{ + int outfd = stream->out_fd; + + /* + * This does a blocking write-and-wait on any page that belongs to the + * subbuffer prior to the one we just wrote. + * Don't care about error values, as these are just hints and ways to + * limit the amount of page cache used. + */ + if (orig_offset < stream->chan->max_sb_size) { + return; + } + sync_file_range(outfd, orig_offset - stream->chan->max_sb_size, + stream->chan->max_sb_size, + SYNC_FILE_RANGE_WAIT_BEFORE + | SYNC_FILE_RANGE_WRITE + | SYNC_FILE_RANGE_WAIT_AFTER); + /* + * Give hints to the kernel about how we access the file: + * POSIX_FADV_DONTNEED : we won't re-access data in a near future after + * we write it. + * + * We need to call fadvise again after the file grows because the + * kernel does not seem to apply fadvise to non-existing parts of the + * file. + * + * Call fadvise _after_ having waited for the page writeback to + * complete because the dirty page writeback semantic is not well + * defined. So it can be expected to lead to lower throughput in + * streaming. + */ + posix_fadvise(outfd, orig_offset - stream->chan->max_sb_size, + stream->chan->max_sb_size, POSIX_FADV_DONTNEED); +} + +/* + * Initialise the necessary environnement : + * - create a new context + * - create the poll_pipe + * - create the should_quit pipe (for signal handler) + * - create the thread pipe (for splice) + * + * Takes a function pointer as argument, this function is called when data is + * available on a buffer. This function is responsible to do the + * kernctl_get_next_subbuf, read the data with mmap or splice depending on the + * buffer configuration and then kernctl_put_next_subbuf at the end. + * + * Returns a pointer to the new context or NULL on error. + */ +struct lttng_consumer_local_data *lttng_consumer_create( + enum lttng_consumer_type type, + int (*buffer_ready)(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx), + int (*recv_channel)(struct lttng_consumer_channel *channel), + int (*recv_stream)(struct lttng_consumer_stream *stream), + int (*update_stream)(int stream_key, uint32_t state)) +{ + int ret, i; + struct lttng_consumer_local_data *ctx; + + assert(consumer_data.type == LTTNG_CONSUMER_UNKNOWN || + consumer_data.type == type); + consumer_data.type = type; + + ctx = zmalloc(sizeof(struct lttng_consumer_local_data)); + if (ctx == NULL) { + perror("allocating context"); + goto error; + } + + ctx->consumer_error_socket = -1; + /* assign the callbacks */ + ctx->on_buffer_ready = buffer_ready; + ctx->on_recv_channel = recv_channel; + ctx->on_recv_stream = recv_stream; + ctx->on_update_stream = update_stream; + + ret = pipe(ctx->consumer_poll_pipe); + if (ret < 0) { + perror("Error creating poll pipe"); + goto error_poll_pipe; + } + + ret = pipe(ctx->consumer_should_quit); + if (ret < 0) { + perror("Error creating recv pipe"); + goto error_quit_pipe; + } + + ret = pipe(ctx->consumer_thread_pipe); + if (ret < 0) { + perror("Error creating thread pipe"); + goto error_thread_pipe; + } + + return ctx; + + +error_thread_pipe: + for (i = 0; i < 2; i++) { + int err; + + err = close(ctx->consumer_should_quit[i]); + assert(!err); + } +error_quit_pipe: + for (i = 0; i < 2; i++) { + int err; + + err = close(ctx->consumer_poll_pipe[i]); + assert(!err); + } +error_poll_pipe: + free(ctx); +error: + return NULL; +} + +/* + * Close all fds associated with the instance and free the context. + */ +void lttng_consumer_destroy(struct lttng_consumer_local_data *ctx) +{ + close(ctx->consumer_error_socket); + close(ctx->consumer_thread_pipe[0]); + close(ctx->consumer_thread_pipe[1]); + close(ctx->consumer_poll_pipe[0]); + close(ctx->consumer_poll_pipe[1]); + close(ctx->consumer_should_quit[0]); + close(ctx->consumer_should_quit[1]); + unlink(ctx->consumer_command_sock_path); + free(ctx); +} + +/* + * Mmap the ring buffer, read it and write the data to the tracefile. + * + * Returns the number of bytes written + */ +int lttng_consumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_on_read_subbuffer_mmap(ctx, stream, len); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_on_read_subbuffer_mmap(ctx, stream, len); + default: + ERR("Unknown consumer_data type"); + assert(0); + } +} + +/* + * Splice the data from the ring buffer to the tracefile. + * + * Returns the number of bytes spliced. + */ +int lttng_consumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_on_read_subbuffer_splice(ctx, stream, len); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return -ENOSYS; + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } + +} + +/* + * Take a snapshot for a specific fd + * + * Returns 0 on success, < 0 on error + */ +int lttng_consumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_take_snapshot(ctx, stream); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_take_snapshot(ctx, stream); + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } + +} + +/* + * Get the produced position + * + * Returns 0 on success, < 0 on error + */ +int lttng_consumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_get_produced_snapshot(ctx, stream, pos); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_get_produced_snapshot(ctx, stream, pos); + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } +} + +int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_recv_cmd(ctx, sock, consumer_sockpoll); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_recv_cmd(ctx, sock, consumer_sockpoll); + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } +} + +/* + * This thread polls the fds in the set to consume the data and write + * it to tracefile if necessary. + */ +void *lttng_consumer_thread_poll_fds(void *data) +{ + int num_rdy, num_hup, high_prio, ret, i; + struct pollfd *pollfd = NULL; + /* local view of the streams */ + struct lttng_consumer_stream **local_stream = NULL; + /* local view of consumer_data.fds_count */ + int nb_fd = 0; + char tmp; + int tmp2; + struct lttng_consumer_local_data *ctx = data; + + rcu_register_thread(); + + local_stream = zmalloc(sizeof(struct lttng_consumer_stream)); + + while (1) { + high_prio = 0; + num_hup = 0; + + /* + * the fds set has been updated, we need to update our + * local array as well + */ + pthread_mutex_lock(&consumer_data.lock); + if (consumer_data.need_update) { + if (pollfd != NULL) { + free(pollfd); + pollfd = NULL; + } + if (local_stream != NULL) { + free(local_stream); + local_stream = NULL; + } + + /* allocate for all fds + 1 for the consumer_poll_pipe */ + pollfd = zmalloc((consumer_data.stream_count + 1) * sizeof(struct pollfd)); + if (pollfd == NULL) { + perror("pollfd malloc"); + pthread_mutex_unlock(&consumer_data.lock); + goto end; + } + + /* allocate for all fds + 1 for the consumer_poll_pipe */ + local_stream = zmalloc((consumer_data.stream_count + 1) * + sizeof(struct lttng_consumer_stream)); + if (local_stream == NULL) { + perror("local_stream malloc"); + pthread_mutex_unlock(&consumer_data.lock); + goto end; + } + ret = consumer_update_poll_array(ctx, &pollfd, local_stream); + if (ret < 0) { + ERR("Error in allocating pollfd or local_outfds"); + lttng_consumer_send_error(ctx, CONSUMERD_POLL_ERROR); + pthread_mutex_unlock(&consumer_data.lock); + goto end; + } + nb_fd = ret; + consumer_data.need_update = 0; + } + pthread_mutex_unlock(&consumer_data.lock); + + /* poll on the array of fds */ + DBG("polling on %d fd", nb_fd + 1); + num_rdy = poll(pollfd, nb_fd + 1, consumer_poll_timeout); + DBG("poll num_rdy : %d", num_rdy); + if (num_rdy == -1) { + perror("Poll error"); + lttng_consumer_send_error(ctx, CONSUMERD_POLL_ERROR); + goto end; + } else if (num_rdy == 0) { + DBG("Polling thread timed out"); + goto end; + } + + /* No FDs and consumer_quit, consumer_cleanup the thread */ + if (nb_fd == 0 && consumer_quit == 1) { + goto end; + } + + /* + * If the consumer_poll_pipe triggered poll go + * directly to the beginning of the loop to update the + * array. We want to prioritize array update over + * low-priority reads. + */ + if (pollfd[nb_fd].revents & POLLIN) { + DBG("consumer_poll_pipe wake up"); + tmp2 = read(ctx->consumer_poll_pipe[0], &tmp, 1); + if (tmp2 < 0) { + perror("read consumer poll"); + } + continue; + } + + /* Take care of high priority channels first. */ + for (i = 0; i < nb_fd; i++) { + if (pollfd[i].revents & POLLPRI) { + DBG("Urgent read on fd %d", pollfd[i].fd); + high_prio = 1; + ret = ctx->on_buffer_ready(local_stream[i], ctx); + /* it's ok to have an unavailable sub-buffer */ + if (ret == EAGAIN) { + ret = 0; + } + } else if (pollfd[i].revents & POLLERR) { + ERR("Error returned in polling fd %d.", pollfd[i].fd); + rcu_read_lock(); + consumer_del_stream_rcu(&local_stream[i]->node.head); + rcu_read_unlock(); + num_hup++; + } else if (pollfd[i].revents & POLLNVAL) { + ERR("Polling fd %d tells fd is not open.", pollfd[i].fd); + rcu_read_lock(); + consumer_del_stream_rcu(&local_stream[i]->node.head); + rcu_read_unlock(); + num_hup++; + } else if ((pollfd[i].revents & POLLHUP) && + !(pollfd[i].revents & POLLIN)) { + if (consumer_data.type == LTTNG_CONSUMER32_UST + || consumer_data.type == LTTNG_CONSUMER64_UST) { + DBG("Polling fd %d tells it has hung up. Attempting flush and read.", + pollfd[i].fd); + if (!local_stream[i]->hangup_flush_done) { + lttng_ustconsumer_on_stream_hangup(local_stream[i]); + /* read after flush */ + do { + ret = ctx->on_buffer_ready(local_stream[i], ctx); + } while (ret == EAGAIN); + } + } else { + DBG("Polling fd %d tells it has hung up.", pollfd[i].fd); + } + rcu_read_lock(); + consumer_del_stream_rcu(&local_stream[i]->node.head); + rcu_read_unlock(); + num_hup++; + } + } + + /* If every buffer FD has hung up, we end the read loop here */ + if (nb_fd > 0 && num_hup == nb_fd) { + DBG("every buffer FD has hung up\n"); + if (consumer_quit == 1) { + goto end; + } + continue; + } + + /* Take care of low priority channels. */ + if (high_prio == 0) { + for (i = 0; i < nb_fd; i++) { + if (pollfd[i].revents & POLLIN) { + DBG("Normal read on fd %d", pollfd[i].fd); + ret = ctx->on_buffer_ready(local_stream[i], ctx); + /* it's ok to have an unavailable subbuffer */ + if (ret == EAGAIN) { + ret = 0; + } + } + } + } + } +end: + DBG("polling thread exiting"); + if (pollfd != NULL) { + free(pollfd); + pollfd = NULL; + } + if (local_stream != NULL) { + free(local_stream); + local_stream = NULL; + } + rcu_unregister_thread(); + return NULL; +} + +/* + * This thread listens on the consumerd socket and receives the file + * descriptors from the session daemon. + */ +void *lttng_consumer_thread_receive_fds(void *data) +{ + int sock, client_socket, ret; + /* + * structure to poll for incoming data on communication socket avoids + * making blocking sockets. + */ + struct pollfd consumer_sockpoll[2]; + struct lttng_consumer_local_data *ctx = data; + + rcu_register_thread(); + + DBG("Creating command socket %s", ctx->consumer_command_sock_path); + unlink(ctx->consumer_command_sock_path); + client_socket = lttcomm_create_unix_sock(ctx->consumer_command_sock_path); + if (client_socket < 0) { + ERR("Cannot create command socket"); + goto end; + } + + ret = lttcomm_listen_unix_sock(client_socket); + if (ret < 0) { + goto end; + } + + 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 lttng-sessiond"); + goto end; + } + + ret = fcntl(client_socket, F_SETFL, O_NONBLOCK); + if (ret < 0) { + perror("fcntl O_NONBLOCK"); + goto end; + } + + /* prepare the FDs to poll : to client socket and the should_quit pipe */ + consumer_sockpoll[0].fd = ctx->consumer_should_quit[0]; + consumer_sockpoll[0].events = POLLIN | POLLPRI; + consumer_sockpoll[1].fd = client_socket; + consumer_sockpoll[1].events = POLLIN | POLLPRI; + + if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { + goto end; + } + DBG("Connection on client_socket"); + + /* Blocking call, waiting for transmission */ + sock = lttcomm_accept_unix_sock(client_socket); + if (sock <= 0) { + WARN("On accept"); + goto end; + } + ret = fcntl(sock, F_SETFL, O_NONBLOCK); + if (ret < 0) { + perror("fcntl O_NONBLOCK"); + goto end; + } + + /* update the polling structure to poll on the established socket */ + consumer_sockpoll[1].fd = sock; + consumer_sockpoll[1].events = POLLIN | POLLPRI; + + while (1) { + if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { + goto end; + } + DBG("Incoming command on sock"); + ret = lttng_consumer_recv_cmd(ctx, sock, consumer_sockpoll); + if (ret == -ENOENT) { + DBG("Received STOP command"); + goto end; + } + if (ret < 0) { + ERR("Communication interrupted on command socket"); + goto end; + } + if (consumer_quit) { + DBG("consumer_thread_receive_fds received quit from signal"); + goto end; + } + DBG("received fds on sock"); + } +end: + DBG("consumer_thread_receive_fds exiting"); + + /* + * when all fds have hung up, the polling thread + * can exit cleanly + */ + consumer_quit = 1; + + /* + * 2s of grace period, if no polling events occur during + * this period, the polling thread will exit even if there + * are still open FDs (should not happen, but safety mechanism). + */ + consumer_poll_timeout = LTTNG_CONSUMER_POLL_TIMEOUT; + + /* wake up the polling thread */ + ret = write(ctx->consumer_poll_pipe[1], "4", 1); + if (ret < 0) { + perror("poll pipe write"); + } + rcu_unregister_thread(); + return NULL; +} + +int lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_read_subbuffer(stream, ctx); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_read_subbuffer(stream, ctx); + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } +} + +int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream) +{ + switch (consumer_data.type) { + case LTTNG_CONSUMER_KERNEL: + return lttng_kconsumer_on_recv_stream(stream); + case LTTNG_CONSUMER32_UST: + case LTTNG_CONSUMER64_UST: + return lttng_ustconsumer_on_recv_stream(stream); + default: + ERR("Unknown consumer_data type"); + assert(0); + return -ENOSYS; + } +} + +/* + * Allocate and set consumer data hash tables. + */ +void lttng_consumer_init(void) +{ + consumer_data.stream_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); +} + diff --git a/src/common/consumer.h b/src/common/consumer.h new file mode 100644 index 000000000..dc5fc9968 --- /dev/null +++ b/src/common/consumer.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * Copyright (C) 2011 - 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_CONSUMER_H +#define _LTTNG_CONSUMER_H + +#include +#include +#include + +#include + +#include "src/common/hashtable/hashtable.h" + +/* + * 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 + * 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 + * occurred on the FDs so we need to force an exit. This case should not happen + * but it is a safety to ensure we won't block the consumer indefinitely. + * + * The value of 2 seconds is an arbitrary choice. + */ +#define LTTNG_CONSUMER_POLL_TIMEOUT 2000 + +/* Commands for consumer */ +enum lttng_consumer_command { + LTTNG_CONSUMER_ADD_CHANNEL, + LTTNG_CONSUMER_ADD_STREAM, + /* pause, delete, active depending on fd state */ + LTTNG_CONSUMER_UPDATE_STREAM, + /* inform the consumer to quit when all fd has hang up */ + LTTNG_CONSUMER_STOP, +}; + +/* State of each fd in consumer */ +enum lttng_consumer_stream_state { + LTTNG_CONSUMER_ACTIVE_STREAM, + LTTNG_CONSUMER_PAUSE_STREAM, + LTTNG_CONSUMER_DELETE_STREAM, +}; + +enum lttng_consumer_type { + LTTNG_CONSUMER_UNKNOWN = 0, + LTTNG_CONSUMER_KERNEL, + LTTNG_CONSUMER64_UST, + LTTNG_CONSUMER32_UST, +}; + +struct lttng_consumer_channel { + struct lttng_ht_node_ulong node; + int key; + uint64_t max_sb_size; /* the subbuffer size for this channel */ + int refcount; /* Number of streams referencing this channel */ + /* For UST */ + int shm_fd; + int wait_fd; + void *mmap_base; + size_t mmap_len; + struct lttng_ust_shm_handle *handle; + int nr_streams; + int wait_fd_is_copy; + int cpucount; +}; + +/* Forward declaration for UST. */ +struct lttng_ust_lib_ring_buffer; + +/* + * Internal representation of the streams, sessiond_key is used to identify + * uniquely a stream. + */ +struct lttng_consumer_stream { + struct lttng_ht_node_ulong node; + struct lttng_consumer_channel *chan; /* associated channel */ + /* + * key is the key used by the session daemon to refer to the + * object in the consumer daemon. + */ + int key; + int shm_fd; + int wait_fd; + int out_fd; /* output file to write the data */ + off_t out_fd_offset; /* write position in the output file descriptor */ + char path_name[PATH_MAX]; /* tracefile name */ + enum lttng_consumer_stream_state state; + size_t shm_len; + void *mmap_base; + size_t mmap_len; + enum lttng_event_output output; /* splice or mmap */ + int shm_fd_is_copy; + int wait_fd_is_copy; + /* For UST */ + struct lttng_ust_lib_ring_buffer *buf; + int cpu; + int hangup_flush_done; + /* UID/GID of the user owning the session to which stream belongs */ + uid_t uid; + gid_t gid; +}; + +/* + * UST consumer local data to the program. One or more instance per + * process. + */ +struct lttng_consumer_local_data { + /* function to call when data is available on a buffer */ + int (*on_buffer_ready)(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx); + /* + * function to call when we receive a new channel, it receives a + * newly allocated channel, depending on the return code of this + * function, the new channel will be handled by the application + * or the library. + * + * Returns: + * > 0 (success, FD is kept by application) + * == 0 (success, FD is left to library) + * < 0 (error) + */ + int (*on_recv_channel)(struct lttng_consumer_channel *channel); + /* + * function to call when we receive a new stream, it receives a + * newly allocated stream, depending on the return code of this + * function, the new stream will be handled by the application + * or the library. + * + * Returns: + * > 0 (success, FD is kept by application) + * == 0 (success, FD is left to library) + * < 0 (error) + */ + int (*on_recv_stream)(struct lttng_consumer_stream *stream); + /* + * function to call when a stream is getting updated by the session + * daemon, this function receives the sessiond key and the new + * state, depending on the return code of this function the + * update of state for the stream is handled by the application + * or the library. + * + * Returns: + * > 0 (success, FD is kept by application) + * == 0 (success, FD is left to library) + * < 0 (error) + */ + int (*on_update_stream)(int sessiond_key, uint32_t state); + /* socket to communicate errors with sessiond */ + int consumer_error_socket; + /* socket to exchange commands with sessiond */ + char *consumer_command_sock_path; + /* communication with splice */ + int consumer_thread_pipe[2]; + /* pipe to wake the poll thread when necessary */ + int consumer_poll_pipe[2]; + /* to let the signal handler wake up the fd receiver thread */ + int consumer_should_quit[2]; +}; + +/* + * Library-level data. One instance per process. + */ +struct lttng_consumer_global_data { + + /* + * At this time, this lock is used to ensure coherence between the count + * and number of element in the hash table. It's also a protection for + * concurrent read/write between threads. + * + * XXX: We need to see if this lock is still needed with the lockless RCU + * hash tables. + */ + pthread_mutex_t lock; + + /* + * Number of streams in the hash table. Protected by consumer_data.lock. + */ + int stream_count; + /* + * Hash tables of streams and channels. Protected by consumer_data.lock. + */ + struct lttng_ht *stream_ht; + struct lttng_ht *channel_ht; + /* + * Flag specifying if the local array of FDs needs update in the + * poll function. Protected by consumer_data.lock. + */ + unsigned int need_update; + enum lttng_consumer_type type; +}; + +/* + * Init consumer data structures. + */ +extern void lttng_consumer_init(void); + +/* + * Set the error socket for communication with a session daemon. + */ +extern void lttng_consumer_set_error_sock( + struct lttng_consumer_local_data *ctx, int sock); + +/* + * Set the command socket path for communication with a session daemon. + */ +extern void lttng_consumer_set_command_sock_path( + struct lttng_consumer_local_data *ctx, char *sock); + +/* + * Send return code to session daemon. + * + * Returns the return code of sendmsg : the number of bytes transmitted or -1 + * on error. + */ +extern int lttng_consumer_send_error( + struct lttng_consumer_local_data *ctx, int cmd); + +/* + * Called from signal handler to ensure a clean exit. + */ +extern void lttng_consumer_should_exit( + struct lttng_consumer_local_data *ctx); + +/* + * Cleanup the daemon's socket on exit. + */ +extern void lttng_consumer_cleanup(void); + +/* + * Flush pending writes to trace output disk file. + */ +extern void lttng_consumer_sync_trace_file( + struct lttng_consumer_stream *stream, off_t orig_offset); + +/* + * Poll on the should_quit pipe and the command socket return -1 on error and + * should exit, 0 if data is available on the command socket + */ +extern int lttng_consumer_poll_socket(struct pollfd *kconsumer_sockpoll); + +extern int consumer_update_poll_array( + struct lttng_consumer_local_data *ctx, struct pollfd **pollfd, + struct lttng_consumer_stream **local_consumer_streams); + +extern struct lttng_consumer_stream *consumer_allocate_stream( + int channel_key, int stream_key, + int shm_fd, int wait_fd, + enum lttng_consumer_stream_state state, + uint64_t mmap_len, + enum lttng_event_output output, + const char *path_name, + uid_t uid, + gid_t gid); +extern int consumer_add_stream(struct lttng_consumer_stream *stream); +extern void consumer_del_stream(struct lttng_consumer_stream *stream); +extern void consumer_change_stream_state(int stream_key, + enum lttng_consumer_stream_state state); +extern void consumer_del_channel(struct lttng_consumer_channel *channel); +extern struct lttng_consumer_channel *consumer_allocate_channel( + int channel_key, + int shm_fd, int wait_fd, + uint64_t mmap_len, + uint64_t max_sb_size); +int consumer_add_channel(struct lttng_consumer_channel *channel); + +extern struct lttng_consumer_local_data *lttng_consumer_create( + enum lttng_consumer_type type, + int (*buffer_ready)(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx), + int (*recv_channel)(struct lttng_consumer_channel *channel), + int (*recv_stream)(struct lttng_consumer_stream *stream), + int (*update_stream)(int sessiond_key, uint32_t state)); +extern void lttng_consumer_destroy(struct lttng_consumer_local_data *ctx); +extern int lttng_consumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); +extern int lttng_consumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); +extern int lttng_consumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream); +extern int lttng_consumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos); +extern void *lttng_consumer_thread_poll_fds(void *data); +extern void *lttng_consumer_thread_receive_fds(void *data); +extern int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll); + +int lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx); +int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream); + +#endif /* _LTTNG_CONSUMER_H */ diff --git a/src/common/hashtable/Makefile.am b/src/common/hashtable/Makefile.am new file mode 100644 index 000000000..7a2b83517 --- /dev/null +++ b/src/common/hashtable/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libhashtable.la + +libhashtable_la_SOURCES = hashtable.c hashtable.h \ + utils.c utils.h \ + rculfhash-internal.h urcu-flavor.h \ + rculfhash.h rculfhash.c \ + rculfhash-mm-chunk.c \ + rculfhash-mm-mmap.c \ + rculfhash-mm-order.c + +libhashtable_la_LIBADD = -lurcu-common -lurcu diff --git a/src/common/hashtable/hashtable.c b/src/common/hashtable/hashtable.c new file mode 100644 index 000000000..4f64778df --- /dev/null +++ b/src/common/hashtable/hashtable.c @@ -0,0 +1,284 @@ +/* + * 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 "utils.h" + +#define HASH_SEED 0x42UL /* The answer to life */ + +static unsigned long min_hash_alloc_size = 1; +static unsigned long max_hash_buckets_size = (1UL << 20); + +/* + * Match function for string node. + */ +static int match_str(struct cds_lfht_node *node, const void *key) +{ + struct lttng_ht_node_str *match_node = + caa_container_of(node, struct lttng_ht_node_str, node); + + return hash_match_key_str(match_node->key, (void *) key); +} + +/* + * Match function for ulong node. + */ +static int match_ulong(struct cds_lfht_node *node, const void *key) +{ + struct lttng_ht_node_ulong *match_node = + caa_container_of(node, struct lttng_ht_node_ulong, node); + + return hash_match_key_ulong((void *) match_node->key, (void *) key); +} + +/* + * Return an allocated lttng hashtable. + */ +struct lttng_ht *lttng_ht_new(unsigned long size, int type) +{ + struct lttng_ht *ht; + + /* Test size */ + if (!size) + size = DEFAULT_HT_SIZE; + + ht = zmalloc(sizeof(*ht)); + if (ht == NULL) { + PERROR("zmalloc lttng_ht"); + goto error; + } + + ht->ht = cds_lfht_new(size, min_hash_alloc_size, max_hash_buckets_size, + CDS_LFHT_AUTO_RESIZE, NULL); + /* + * There is already an assert in the RCU hashtable code so if the ht is + * NULL here there is a *huge* problem. + */ + assert(ht->ht); + + switch (type) { + case LTTNG_HT_TYPE_STRING: + ht->match_fct = match_str; + ht->hash_fct = hash_key_str; + break; + case LTTNG_HT_TYPE_ULONG: + ht->match_fct = match_ulong; + ht->hash_fct = hash_key_ulong; + break; + default: + ERR("Unknown lttng hashtable type %d", type); + goto error; + } + + DBG3("Created hashtable size %lu at %p of type %d", size, ht->ht, type); + + return ht; + +error: + return NULL; +} + +/* + * Free a lttng hashtable. + */ +void lttng_ht_destroy(struct lttng_ht *ht) +{ + int ret; + + ret = cds_lfht_destroy(ht->ht, NULL); + assert(!ret); +} + +/* + * Init lttng ht node string. + */ +void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key) +{ + assert(node); + + node->key = key; + cds_lfht_node_init(&node->node); +} + +/* + * Init lttng ht node unsigned long. + */ +void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node, + unsigned long key) +{ + assert(node); + + node->key = key; + cds_lfht_node_init(&node->node); +} + +/* + * Free lttng ht node string. + */ +void lttng_ht_node_free_str(struct lttng_ht_node_str *node) +{ + assert(node); + free(node); +} + +/* + * Free lttng ht node unsigned long. + */ +void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node) +{ + assert(node); + free(node); +} + +/* + * Lookup function in hashtable. + */ +void lttng_ht_lookup(struct lttng_ht *ht, void *key, + struct lttng_ht_iter *iter) +{ + assert(ht); + assert(ht->ht); + + cds_lfht_lookup(ht->ht, ht->hash_fct(key, HASH_SEED), + ht->match_fct, key, &iter->iter); +} + +/* + * Add unique string node to hashtable. + */ +void lttng_ht_add_unique_str(struct lttng_ht *ht, + struct lttng_ht_node_str *node) +{ + struct cds_lfht_node *node_ptr; + assert(ht); + assert(ht->ht); + assert(node); + + node_ptr = cds_lfht_add_unique(ht->ht, ht->hash_fct(node->key, HASH_SEED), + ht->match_fct, node->key, &node->node); + assert(node_ptr == &node->node); +} + +/* + * Add unique unsigned long node to hashtable. + */ +void lttng_ht_add_unique_ulong(struct lttng_ht *ht, + struct lttng_ht_node_ulong *node) +{ + struct cds_lfht_node *node_ptr; + assert(ht); + assert(ht->ht); + assert(node); + + node_ptr = cds_lfht_add_unique(ht->ht, + ht->hash_fct((void *) node->key, HASH_SEED), ht->match_fct, + (void *) node->key, &node->node); + assert(node_ptr == &node->node); +} + +/* + * Delete node from hashtable. + */ +int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter) +{ + assert(ht); + assert(ht->ht); + assert(iter); + + return cds_lfht_del(ht->ht, iter->iter.node); +} + +/* + * Get first node in the hashtable. + */ +void lttng_ht_get_first(struct lttng_ht *ht, struct lttng_ht_iter *iter) +{ + assert(ht); + assert(ht->ht); + assert(iter); + + cds_lfht_first(ht->ht, &iter->iter); +} + +/* + * Get next node in the hashtable. + */ +void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter) +{ + assert(ht); + assert(ht->ht); + assert(iter); + + cds_lfht_next(ht->ht, &iter->iter); +} + +/* + * Return the number of nodes in the hashtable. + */ +unsigned long lttng_ht_get_count(struct lttng_ht *ht) +{ + long scb, sca; + unsigned long count; + + assert(ht); + assert(ht->ht); + + cds_lfht_count_nodes(ht->ht, &scb, &count, &sca); + + return count; +} + +/* + * Return lttng ht string node from iterator. + */ +struct lttng_ht_node_str *lttng_ht_iter_get_node_str( + struct lttng_ht_iter *iter) +{ + struct cds_lfht_node *node; + + assert(iter); + node = cds_lfht_iter_get_node(&iter->iter); + if (!node) { + return NULL; + } + return caa_container_of(node, struct lttng_ht_node_str, node); +} + +/* + * Return lttng ht unsigned long node from iterator. + */ +struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong( + struct lttng_ht_iter *iter) +{ + struct cds_lfht_node *node; + + assert(iter); + node = cds_lfht_iter_get_node(&iter->iter); + if (!node) { + return NULL; + } + return caa_container_of(node, struct lttng_ht_node_ulong, node); +} diff --git a/src/common/hashtable/hashtable.h b/src/common/hashtable/hashtable.h new file mode 100644 index 000000000..1c2889d3e --- /dev/null +++ b/src/common/hashtable/hashtable.h @@ -0,0 +1,89 @@ +/* + * 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_HT_H +#define _LTT_HT_H + +#include + +#include "rculfhash.h" +#include "rculfhash-internal.h" + +typedef unsigned long (*hash_fct)(void *_key, unsigned long seed); +typedef cds_lfht_match_fct hash_match_fct; + +enum lttng_ht_type { + LTTNG_HT_TYPE_STRING, + LTTNG_HT_TYPE_ULONG, +}; + +struct lttng_ht { + struct cds_lfht *ht; + cds_lfht_match_fct match_fct; + hash_fct hash_fct; +}; + +struct lttng_ht_iter { + struct cds_lfht_iter iter; +}; + +struct lttng_ht_node_str { + char *key; + struct cds_lfht_node node; + struct rcu_head head; +}; + +struct lttng_ht_node_ulong { + unsigned long key; + struct cds_lfht_node node; + struct rcu_head head; +}; + +/* Hashtable new and destroy */ +extern struct lttng_ht *lttng_ht_new(unsigned long size, int type); +extern void lttng_ht_destroy(struct lttng_ht *ht); + +/* Specialized node init and free functions */ +extern void lttng_ht_node_init_str(struct lttng_ht_node_str *node, char *key); +extern void lttng_ht_node_init_ulong(struct lttng_ht_node_ulong *node, + unsigned long key); +extern void lttng_ht_node_free_str(struct lttng_ht_node_str *node); +extern void lttng_ht_node_free_ulong(struct lttng_ht_node_ulong *node); + +extern void lttng_ht_lookup(struct lttng_ht *ht, void *key, + struct lttng_ht_iter *iter); + +/* Specialized add unique functions */ +extern void lttng_ht_add_unique_str(struct lttng_ht *ht, + struct lttng_ht_node_str *node); +extern void lttng_ht_add_unique_ulong(struct lttng_ht *ht, + struct lttng_ht_node_ulong *node); + +extern int lttng_ht_del(struct lttng_ht *ht, struct lttng_ht_iter *iter); + +extern void lttng_ht_get_first(struct lttng_ht *ht, + struct lttng_ht_iter *iter); +extern void lttng_ht_get_next(struct lttng_ht *ht, struct lttng_ht_iter *iter); + +extern unsigned long lttng_ht_get_count(struct lttng_ht *ht); + +extern struct lttng_ht_node_str *lttng_ht_iter_get_node_str( + struct lttng_ht_iter *iter); +extern struct lttng_ht_node_ulong *lttng_ht_iter_get_node_ulong( + struct lttng_ht_iter *iter); + +#endif /* _LTT_HT_H */ diff --git a/src/common/hashtable/rculfhash-internal.h b/src/common/hashtable/rculfhash-internal.h new file mode 100644 index 000000000..cb13ffa73 --- /dev/null +++ b/src/common/hashtable/rculfhash-internal.h @@ -0,0 +1,177 @@ +#ifndef _URCU_RCULFHASH_INTERNAL_H +#define _URCU_RCULFHASH_INTERNAL_H + +/* + * urcu/rculfhash-internal.h + * + * Internal header for Lock-Free RCU Hash Table + * + * Copyright 2011 - Mathieu Desnoyers + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "rculfhash.h" + +#ifdef DEBUG +#define dbg_printf(fmt, args...) printf("[debug rculfhash] " fmt, ## args) +#else +#define dbg_printf(fmt, args...) +#endif + +#if (CAA_BITS_PER_LONG == 32) +#define MAX_TABLE_ORDER 32 +#else +#define MAX_TABLE_ORDER 64 +#endif + +#define MAX_CHUNK_TABLE (1UL << 10) + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +struct ht_items_count; + +/* + * cds_lfht: Top-level data structure representing a lock-free hash + * table. Defined in the implementation file to make it be an opaque + * cookie to users. + * + * The fields used in fast-paths are placed near the end of the + * structure, because we need to have a variable-sized union to contain + * the mm plugin fields, which are used in the fast path. + */ +struct cds_lfht { + /* Initial configuration items */ + unsigned long max_nr_buckets; + const struct cds_lfht_mm_type *mm; /* memory management plugin */ + const struct rcu_flavor_struct *flavor; /* RCU flavor */ + + long count; /* global approximate item count */ + + /* + * We need to put the work threads offline (QSBR) when taking this + * mutex, because we use synchronize_rcu within this mutex critical + * section, which waits on read-side critical sections, and could + * therefore cause grace-period deadlock if we hold off RCU G.P. + * completion. + */ + pthread_mutex_t resize_mutex; /* resize mutex: add/del mutex */ + pthread_attr_t *resize_attr; /* Resize threads attributes */ + unsigned int in_progress_resize, in_progress_destroy; + unsigned long resize_target; + int resize_initiated; + + /* + * Variables needed for add and remove fast-paths. + */ + int flags; + unsigned long min_alloc_buckets_order; + unsigned long min_nr_alloc_buckets; + struct ht_items_count *split_count; /* split item count */ + + /* + * Variables needed for the lookup, add and remove fast-paths. + */ + unsigned long size; /* always a power of 2, shared (RCU) */ + /* + * bucket_at pointer is kept here to skip the extra level of + * dereference needed to get to "mm" (this is a fast-path). + */ + struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht, + unsigned long index); + /* + * Dynamic length "tbl_chunk" needs to be at the end of + * cds_lfht. + */ + union { + /* + * Contains the per order-index-level bucket node table. + * The size of each bucket node table is half the number + * of hashes contained in this order (except for order 0). + * The minimum allocation buckets size parameter allows + * combining the bucket node arrays of the lowermost + * levels to improve cache locality for small index orders. + */ + struct cds_lfht_node *tbl_order[MAX_TABLE_ORDER]; + + /* + * Contains the bucket node chunks. The size of each + * bucket node chunk is ->min_alloc_size (we avoid to + * allocate chunks with different size). Chunks improve + * cache locality for small index orders, and are more + * friendly with environments where allocation of large + * contiguous memory areas is challenging due to memory + * fragmentation concerns or inability to use virtual + * memory addressing. + */ + struct cds_lfht_node *tbl_chunk[0]; + + /* + * Memory mapping with room for all possible buckets. + * Their memory is allocated when needed. + */ + struct cds_lfht_node *tbl_mmap; + }; + /* + * End of variables needed for the lookup, add and remove + * fast-paths. + */ +}; + +extern unsigned int cds_lfht_fls_ulong(unsigned long x); +extern int cds_lfht_get_count_order_ulong(unsigned long x); + +#ifdef POISON_FREE +#define poison_free(ptr) \ + do { \ + if (ptr) { \ + memset(ptr, 0x42, sizeof(*(ptr))); \ + free(ptr); \ + } \ + } while (0) +#else +#define poison_free(ptr) free(ptr) +#endif + +static inline +struct cds_lfht *__default_alloc_cds_lfht( + const struct cds_lfht_mm_type *mm, + unsigned long cds_lfht_size, + unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets) +{ + struct cds_lfht *ht; + + ht = calloc(1, cds_lfht_size); + assert(ht); + + ht->mm = mm; + ht->bucket_at = mm->bucket_at; + ht->min_nr_alloc_buckets = min_nr_alloc_buckets; + ht->min_alloc_buckets_order = + cds_lfht_get_count_order_ulong(min_nr_alloc_buckets); + ht->max_nr_buckets = max_nr_buckets; + + return ht; +} + +#endif /* _URCU_RCULFHASH_INTERNAL_H */ diff --git a/src/common/hashtable/rculfhash-mm-chunk.c b/src/common/hashtable/rculfhash-mm-chunk.c new file mode 100644 index 000000000..7204831e5 --- /dev/null +++ b/src/common/hashtable/rculfhash-mm-chunk.c @@ -0,0 +1,97 @@ +/* + * rculfhash-mm-chunk.c + * + * Chunk based memory management for Lock-Free RCU Hash Table + * + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include "rculfhash-internal.h" + +static +void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) { + ht->tbl_chunk[0] = calloc(ht->min_nr_alloc_buckets, + sizeof(struct cds_lfht_node)); + assert(ht->tbl_chunk[0]); + } else if (order > ht->min_alloc_buckets_order) { + unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order); + + for (i = len; i < 2 * len; i++) { + ht->tbl_chunk[i] = calloc(ht->min_nr_alloc_buckets, + sizeof(struct cds_lfht_node)); + assert(ht->tbl_chunk[i]); + } + } + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +/* + * cds_lfht_free_bucket_table() should be called with decreasing order. + * When cds_lfht_free_bucket_table(0) is called, it means the whole + * lfht is destroyed. + */ +static +void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) + poison_free(ht->tbl_chunk[0]); + else if (order > ht->min_alloc_buckets_order) { + unsigned long i, len = 1UL << (order - 1 - ht->min_alloc_buckets_order); + + for (i = len; i < 2 * len; i++) + poison_free(ht->tbl_chunk[i]); + } + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +static +struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) +{ + unsigned long chunk, offset; + + chunk = index >> ht->min_alloc_buckets_order; + offset = index & (ht->min_nr_alloc_buckets - 1); + return &ht->tbl_chunk[chunk][offset]; +} + +static +struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets) +{ + unsigned long nr_chunks, cds_lfht_size; + + min_nr_alloc_buckets = max(min_nr_alloc_buckets, + max_nr_buckets / MAX_CHUNK_TABLE); + nr_chunks = max_nr_buckets / min_nr_alloc_buckets; + cds_lfht_size = offsetof(struct cds_lfht, tbl_chunk) + + sizeof(struct cds_lfht_node *) * nr_chunks; + cds_lfht_size = max(cds_lfht_size, sizeof(struct cds_lfht)); + + return __default_alloc_cds_lfht( + &cds_lfht_mm_chunk, cds_lfht_size, + min_nr_alloc_buckets, max_nr_buckets); +} + +const struct cds_lfht_mm_type cds_lfht_mm_chunk = { + .alloc_cds_lfht = alloc_cds_lfht, + .alloc_bucket_table = cds_lfht_alloc_bucket_table, + .free_bucket_table = cds_lfht_free_bucket_table, + .bucket_at = bucket_at, +}; diff --git a/src/common/hashtable/rculfhash-mm-mmap.c b/src/common/hashtable/rculfhash-mm-mmap.c new file mode 100644 index 000000000..4554ed600 --- /dev/null +++ b/src/common/hashtable/rculfhash-mm-mmap.c @@ -0,0 +1,156 @@ +/* + * rculfhash-mm-mmap.c + * + * mmap/reservation based memory management for Lock-Free RCU Hash Table + * + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include "rculfhash-internal.h" + +/* reserve inaccessible memory space without allocation any memory */ +static void *memory_map(size_t length) +{ + void *ret = mmap(NULL, length, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + assert(ret != MAP_FAILED); + return ret; +} + +static void memory_unmap(void *ptr, size_t length) +{ + int ret __attribute__((unused)); + + ret = munmap(ptr, length); + + assert(ret == 0); +} + +static void memory_populate(void *ptr, size_t length) +{ + void *ret __attribute__((unused)); + + ret = mmap(ptr, length, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + assert(ret == ptr); +} + +/* + * Discard garbage memory and avoid system save it when try to swap it out. + * Make it still reserved, inaccessible. + */ +static void memory_discard(void *ptr, size_t length) +{ + void *ret __attribute__((unused)); + + ret = mmap(ptr, length, PROT_NONE, + MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + assert(ret == ptr); +} + +static +void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) { + if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) { + /* small table */ + ht->tbl_mmap = calloc(ht->max_nr_buckets, + sizeof(*ht->tbl_mmap)); + assert(ht->tbl_mmap); + return; + } + /* large table */ + ht->tbl_mmap = memory_map(ht->max_nr_buckets + * sizeof(*ht->tbl_mmap)); + memory_populate(ht->tbl_mmap, + ht->min_nr_alloc_buckets * sizeof(*ht->tbl_mmap)); + } else if (order > ht->min_alloc_buckets_order) { + /* large table */ + unsigned long len = 1UL << (order - 1); + + assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets); + memory_populate(ht->tbl_mmap + len, + len * sizeof(*ht->tbl_mmap)); + } + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +/* + * cds_lfht_free_bucket_table() should be called with decreasing order. + * When cds_lfht_free_bucket_table(0) is called, it means the whole + * lfht is destroyed. + */ +static +void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) { + if (ht->min_nr_alloc_buckets == ht->max_nr_buckets) { + /* small table */ + poison_free(ht->tbl_mmap); + return; + } + /* large table */ + memory_unmap(ht->tbl_mmap, + ht->max_nr_buckets * sizeof(*ht->tbl_mmap)); + } else if (order > ht->min_alloc_buckets_order) { + /* large table */ + unsigned long len = 1UL << (order - 1); + + assert(ht->min_nr_alloc_buckets < ht->max_nr_buckets); + memory_discard(ht->tbl_mmap + len, len * sizeof(*ht->tbl_mmap)); + } + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +static +struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) +{ + return &ht->tbl_mmap[index]; +} + +static +struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets) +{ + unsigned long page_bucket_size; + + page_bucket_size = getpagesize() / sizeof(struct cds_lfht_node); + if (max_nr_buckets <= page_bucket_size) { + /* small table */ + min_nr_alloc_buckets = max_nr_buckets; + } else { + /* large table */ + min_nr_alloc_buckets = max(min_nr_alloc_buckets, + page_bucket_size); + } + + return __default_alloc_cds_lfht( + &cds_lfht_mm_mmap, sizeof(struct cds_lfht), + min_nr_alloc_buckets, max_nr_buckets); +} + +const struct cds_lfht_mm_type cds_lfht_mm_mmap = { + .alloc_cds_lfht = alloc_cds_lfht, + .alloc_bucket_table = cds_lfht_alloc_bucket_table, + .free_bucket_table = cds_lfht_free_bucket_table, + .bucket_at = bucket_at, +}; diff --git a/src/common/hashtable/rculfhash-mm-order.c b/src/common/hashtable/rculfhash-mm-order.c new file mode 100644 index 000000000..6e3d29bb3 --- /dev/null +++ b/src/common/hashtable/rculfhash-mm-order.c @@ -0,0 +1,90 @@ +/* + * rculfhash-mm-order.c + * + * Order based memory management for Lock-Free RCU Hash Table + * + * Copyright 2011 - Mathieu Desnoyers + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "rculfhash-internal.h" + +static +void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) { + ht->tbl_order[0] = calloc(ht->min_nr_alloc_buckets, + sizeof(struct cds_lfht_node)); + assert(ht->tbl_order[0]); + } else if (order > ht->min_alloc_buckets_order) { + ht->tbl_order[order] = calloc(1UL << (order -1), + sizeof(struct cds_lfht_node)); + assert(ht->tbl_order[order]); + } + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +/* + * cds_lfht_free_bucket_table() should be called with decreasing order. + * When cds_lfht_free_bucket_table(0) is called, it means the whole + * lfht is destroyed. + */ +static +void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + if (order == 0) + poison_free(ht->tbl_order[0]); + else if (order > ht->min_alloc_buckets_order) + poison_free(ht->tbl_order[order]); + /* Nothing to do for 0 < order && order <= ht->min_alloc_buckets_order */ +} + +static +struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) +{ + unsigned long order; + + if (index < ht->min_nr_alloc_buckets) { + dbg_printf("bucket index %lu order 0 aridx 0\n", index); + return &ht->tbl_order[0][index]; + } + /* + * equivalent to cds_lfht_get_count_order_ulong(index + 1), but + * optimizes away the non-existing 0 special-case for + * cds_lfht_get_count_order_ulong. + */ + order = cds_lfht_fls_ulong(index); + dbg_printf("bucket index %lu order %lu aridx %lu\n", + index, order, index & ((1UL << (order - 1)) - 1)); + return &ht->tbl_order[order][index & ((1UL << (order - 1)) - 1)]; +} + +static +struct cds_lfht *alloc_cds_lfht(unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets) +{ + return __default_alloc_cds_lfht( + &cds_lfht_mm_order, sizeof(struct cds_lfht), + min_nr_alloc_buckets, max_nr_buckets); +} + +const struct cds_lfht_mm_type cds_lfht_mm_order = { + .alloc_cds_lfht = alloc_cds_lfht, + .alloc_bucket_table = cds_lfht_alloc_bucket_table, + .free_bucket_table = cds_lfht_free_bucket_table, + .bucket_at = bucket_at, +}; diff --git a/src/common/hashtable/rculfhash.c b/src/common/hashtable/rculfhash.c new file mode 100644 index 000000000..840de351b --- /dev/null +++ b/src/common/hashtable/rculfhash.c @@ -0,0 +1,1832 @@ +/* + * rculfhash.c + * + * Userspace RCU library - Lock-Free Resizable RCU Hash Table + * + * Copyright 2010-2011 - Mathieu Desnoyers + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * Based on the following articles: + * - Ori Shalev and Nir Shavit. Split-ordered lists: Lock-free + * extensible hash tables. J. ACM 53, 3 (May 2006), 379-405. + * - Michael, M. M. High performance dynamic lock-free hash tables + * and list-based sets. In Proceedings of the fourteenth annual ACM + * symposium on Parallel algorithms and architectures, ACM Press, + * (2002), 73-82. + * + * Some specificities of this Lock-Free Resizable RCU Hash Table + * implementation: + * + * - RCU read-side critical section allows readers to perform hash + * table lookups and use the returned objects safely by delaying + * memory reclaim of a grace period. + * - Add and remove operations are lock-free, and do not need to + * allocate memory. They need to be executed within RCU read-side + * critical section to ensure the objects they read are valid and to + * deal with the cmpxchg ABA problem. + * - add and add_unique operations are supported. add_unique checks if + * the node key already exists in the hash table. It ensures no key + * duplicata exists. + * - The resize operation executes concurrently with add/remove/lookup. + * - Hash table nodes are contained within a split-ordered list. This + * list is ordered by incrementing reversed-bits-hash value. + * - An index of bucket nodes is kept. These bucket nodes are the hash + * table "buckets", and they are also chained together in the + * split-ordered list, which allows recursive expansion. + * - The resize operation for small tables only allows expanding the hash table. + * It is triggered automatically by detecting long chains in the add + * operation. + * - The resize operation for larger tables (and available through an + * API) allows both expanding and shrinking the hash table. + * - Split-counters are used to keep track of the number of + * nodes within the hash table for automatic resize triggering. + * - Resize operation initiated by long chain detection is executed by a + * call_rcu thread, which keeps lock-freedom of add and remove. + * - Resize operations are protected by a mutex. + * - The removal operation is split in two parts: first, a "removed" + * flag is set in the next pointer within the node to remove. Then, + * a "garbage collection" is performed in the bucket containing the + * removed node (from the start of the bucket up to the removed node). + * All encountered nodes with "removed" flag set in their next + * pointers are removed from the linked-list. If the cmpxchg used for + * removal fails (due to concurrent garbage-collection or concurrent + * add), we retry from the beginning of the bucket. This ensures that + * the node with "removed" flag set is removed from the hash table + * (not visible to lookups anymore) before the RCU read-side critical + * section held across removal ends. Furthermore, this ensures that + * the node with "removed" flag set is removed from the linked-list + * before its memory is reclaimed. Only the thread which removal + * successfully set the "removed" flag (with a cmpxchg) into a node's + * next pointer is considered to have succeeded its removal (and thus + * owns the node to reclaim). Because we garbage-collect starting from + * an invariant node (the start-of-bucket bucket node) up to the + * "removed" node (or find a reverse-hash that is higher), we are sure + * that a successful traversal of the chain leads to a chain that is + * present in the linked-list (the start node is never removed) and + * that is does not contain the "removed" node anymore, even if + * concurrent delete/add operations are changing the structure of the + * list concurrently. + * - The add operation performs gargage collection of buckets if it + * encounters nodes with removed flag set in the bucket where it wants + * to add its new node. This ensures lock-freedom of add operation by + * helping the remover unlink nodes from the list rather than to wait + * for it do to so. + * - A RCU "order table" indexed by log2(hash index) is copied and + * expanded by the resize operation. This order table allows finding + * the "bucket node" tables. + * - There is one bucket node table per hash index order. The size of + * each bucket node table is half the number of hashes contained in + * this order (except for order 0). + * - synchronzie_rcu is used to garbage-collect the old bucket node table. + * - The per-order bucket node tables contain a compact version of the + * hash table nodes. These tables are invariant after they are + * populated into the hash table. + * + * Bucket node tables: + * + * hash table hash table the last all bucket node tables + * order size bucket node 0 1 2 3 4 5 6(index) + * table size + * 0 1 1 1 + * 1 2 1 1 1 + * 2 4 2 1 1 2 + * 3 8 4 1 1 2 4 + * 4 16 8 1 1 2 4 8 + * 5 32 16 1 1 2 4 8 16 + * 6 64 32 1 1 2 4 8 16 32 + * + * When growing/shrinking, we only focus on the last bucket node table + * which size is (!order ? 1 : (1 << (order -1))). + * + * Example for growing/shrinking: + * grow hash table from order 5 to 6: init the index=6 bucket node table + * shrink hash table from order 6 to 5: fini the index=6 bucket node table + * + * A bit of ascii art explanation: + * + * Order index is the off-by-one compare to the actual power of 2 because + * we use index 0 to deal with the 0 special-case. + * + * This shows the nodes for a small table ordered by reversed bits: + * + * bits reverse + * 0 000 000 + * 4 100 001 + * 2 010 010 + * 6 110 011 + * 1 001 100 + * 5 101 101 + * 3 011 110 + * 7 111 111 + * + * This shows the nodes in order of non-reversed bits, linked by + * reversed-bit order. + * + * order bits reverse + * 0 0 000 000 + * 1 | 1 001 100 <- + * 2 | | 2 010 010 <- | + * | | | 3 011 110 | <- | + * 3 -> | | | 4 100 001 | | + * -> | | 5 101 101 | + * -> | 6 110 011 + * -> 7 111 111 + */ + +#define _LGPL_SOURCE +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include + +#include "rculfhash.h" +#include "rculfhash-internal.h" +#include "urcu-flavor.h" + +/* + * Split-counters lazily update the global counter each 1024 + * addition/removal. It automatically keeps track of resize required. + * We use the bucket length as indicator for need to expand for small + * tables and machines lacking per-cpu data suppport. + */ +#define COUNT_COMMIT_ORDER 10 +#define DEFAULT_SPLIT_COUNT_MASK 0xFUL +#define CHAIN_LEN_TARGET 1 +#define CHAIN_LEN_RESIZE_THRESHOLD 3 + +/* + * Define the minimum table size. + */ +#define MIN_TABLE_ORDER 0 +#define MIN_TABLE_SIZE (1UL << MIN_TABLE_ORDER) + +/* + * Minimum number of bucket nodes to touch per thread to parallelize grow/shrink. + */ +#define MIN_PARTITION_PER_THREAD_ORDER 12 +#define MIN_PARTITION_PER_THREAD (1UL << MIN_PARTITION_PER_THREAD_ORDER) + +/* + * The removed flag needs to be updated atomically with the pointer. + * It indicates that no node must attach to the node scheduled for + * removal, and that node garbage collection must be performed. + * The bucket flag does not require to be updated atomically with the + * pointer, but it is added as a pointer low bit flag to save space. + */ +#define REMOVED_FLAG (1UL << 0) +#define BUCKET_FLAG (1UL << 1) +#define REMOVAL_OWNER_FLAG (1UL << 2) +#define FLAGS_MASK ((1UL << 3) - 1) + +/* Value of the end pointer. Should not interact with flags. */ +#define END_VALUE NULL + +DEFINE_RCU_FLAVOR(rcu_flavor); + +/* + * ht_items_count: Split-counters counting the number of node addition + * and removal in the table. Only used if the CDS_LFHT_ACCOUNTING flag + * is set at hash table creation. + * + * These are free-running counters, never reset to zero. They count the + * number of add/remove, and trigger every (1 << COUNT_COMMIT_ORDER) + * operations to update the global counter. We choose a power-of-2 value + * for the trigger to deal with 32 or 64-bit overflow of the counter. + */ +struct ht_items_count { + unsigned long add, del; +} __attribute__((aligned(CAA_CACHE_LINE_SIZE))); + +/* + * rcu_resize_work: Contains arguments passed to RCU worker thread + * responsible for performing lazy resize. + */ +struct rcu_resize_work { + struct rcu_head head; + struct cds_lfht *ht; +}; + +/* + * partition_resize_work: Contains arguments passed to worker threads + * executing the hash table resize on partitions of the hash table + * assigned to each processor's worker thread. + */ +struct partition_resize_work { + pthread_t thread_id; + struct cds_lfht *ht; + unsigned long i, start, len; + void (*fct)(struct cds_lfht *ht, unsigned long i, + unsigned long start, unsigned long len); +}; + +/* + * Algorithm to reverse bits in a word by lookup table, extended to + * 64-bit words. + * Source: + * http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable + * Originally from Public Domain. + */ + +static const uint8_t BitReverseTable256[256] = +{ +#define R2(n) (n), (n) + 2*64, (n) + 1*64, (n) + 3*64 +#define R4(n) R2(n), R2((n) + 2*16), R2((n) + 1*16), R2((n) + 3*16) +#define R6(n) R4(n), R4((n) + 2*4 ), R4((n) + 1*4 ), R4((n) + 3*4 ) + R6(0), R6(2), R6(1), R6(3) +}; +#undef R2 +#undef R4 +#undef R6 + +static +uint8_t bit_reverse_u8(uint8_t v) +{ + return BitReverseTable256[v]; +} + +static __attribute__((unused)) +uint32_t bit_reverse_u32(uint32_t v) +{ + return ((uint32_t) bit_reverse_u8(v) << 24) | + ((uint32_t) bit_reverse_u8(v >> 8) << 16) | + ((uint32_t) bit_reverse_u8(v >> 16) << 8) | + ((uint32_t) bit_reverse_u8(v >> 24)); +} + +static __attribute__((unused)) +uint64_t bit_reverse_u64(uint64_t v) +{ + return ((uint64_t) bit_reverse_u8(v) << 56) | + ((uint64_t) bit_reverse_u8(v >> 8) << 48) | + ((uint64_t) bit_reverse_u8(v >> 16) << 40) | + ((uint64_t) bit_reverse_u8(v >> 24) << 32) | + ((uint64_t) bit_reverse_u8(v >> 32) << 24) | + ((uint64_t) bit_reverse_u8(v >> 40) << 16) | + ((uint64_t) bit_reverse_u8(v >> 48) << 8) | + ((uint64_t) bit_reverse_u8(v >> 56)); +} + +static +unsigned long bit_reverse_ulong(unsigned long v) +{ +#if (CAA_BITS_PER_LONG == 32) + return bit_reverse_u32(v); +#else + return bit_reverse_u64(v); +#endif +} + +/* + * fls: returns the position of the most significant bit. + * Returns 0 if no bit is set, else returns the position of the most + * significant bit (from 1 to 32 on 32-bit, from 1 to 64 on 64-bit). + */ +#if defined(__i386) || defined(__x86_64) +static inline +unsigned int fls_u32(uint32_t x) +{ + int r; + + asm("bsrl %1,%0\n\t" + "jnz 1f\n\t" + "movl $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U32 +#endif + +#if defined(__x86_64) +static inline +unsigned int fls_u64(uint64_t x) +{ + long r; + + asm("bsrq %1,%0\n\t" + "jnz 1f\n\t" + "movq $-1,%0\n\t" + "1:\n\t" + : "=r" (r) : "rm" (x)); + return r + 1; +} +#define HAS_FLS_U64 +#endif + +#ifndef HAS_FLS_U64 +static __attribute__((unused)) +unsigned int fls_u64(uint64_t x) +{ + unsigned int r = 64; + + if (!x) + return 0; + + if (!(x & 0xFFFFFFFF00000000ULL)) { + x <<= 32; + r -= 32; + } + if (!(x & 0xFFFF000000000000ULL)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF00000000000000ULL)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF000000000000000ULL)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC000000000000000ULL)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x8000000000000000ULL)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +#ifndef HAS_FLS_U32 +static __attribute__((unused)) +unsigned int fls_u32(uint32_t x) +{ + unsigned int r = 32; + + if (!x) + return 0; + if (!(x & 0xFFFF0000U)) { + x <<= 16; + r -= 16; + } + if (!(x & 0xFF000000U)) { + x <<= 8; + r -= 8; + } + if (!(x & 0xF0000000U)) { + x <<= 4; + r -= 4; + } + if (!(x & 0xC0000000U)) { + x <<= 2; + r -= 2; + } + if (!(x & 0x80000000U)) { + x <<= 1; + r -= 1; + } + return r; +} +#endif + +unsigned int cds_lfht_fls_ulong(unsigned long x) +{ +#if (CAA_BITS_PER_LONG == 32) + return fls_u32(x); +#else + return fls_u64(x); +#endif +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int cds_lfht_get_count_order_u32(uint32_t x) +{ + if (!x) + return -1; + + return fls_u32(x - 1); +} + +/* + * Return the minimum order for which x <= (1UL << order). + * Return -1 if x is 0. + */ +int cds_lfht_get_count_order_ulong(unsigned long x) +{ + if (!x) + return -1; + + return cds_lfht_fls_ulong(x - 1); +} + +static +void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth); + +static +void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size, + unsigned long count); + +static long nr_cpus_mask = -1; +static long split_count_mask = -1; + +#if defined(HAVE_SYSCONF) +static void ht_init_nr_cpus_mask(void) +{ + long maxcpus; + + maxcpus = sysconf(_SC_NPROCESSORS_CONF); + if (maxcpus <= 0) { + nr_cpus_mask = -2; + return; + } + /* + * round up number of CPUs to next power of two, so we + * can use & for modulo. + */ + maxcpus = 1UL << cds_lfht_get_count_order_ulong(maxcpus); + nr_cpus_mask = maxcpus - 1; +} +#else /* #if defined(HAVE_SYSCONF) */ +static void ht_init_nr_cpus_mask(void) +{ + nr_cpus_mask = -2; +} +#endif /* #else #if defined(HAVE_SYSCONF) */ + +static +void alloc_split_items_count(struct cds_lfht *ht) +{ + struct ht_items_count *count; + + if (nr_cpus_mask == -1) { + ht_init_nr_cpus_mask(); + if (nr_cpus_mask < 0) + split_count_mask = DEFAULT_SPLIT_COUNT_MASK; + else + split_count_mask = nr_cpus_mask; + } + + assert(split_count_mask >= 0); + + if (ht->flags & CDS_LFHT_ACCOUNTING) { + ht->split_count = calloc(split_count_mask + 1, sizeof(*count)); + assert(ht->split_count); + } else { + ht->split_count = NULL; + } +} + +static +void free_split_items_count(struct cds_lfht *ht) +{ + poison_free(ht->split_count); +} + +#if defined(HAVE_SCHED_GETCPU) +static +int ht_get_split_count_index(unsigned long hash) +{ + int cpu; + + assert(split_count_mask >= 0); + cpu = sched_getcpu(); + if (caa_unlikely(cpu < 0)) + return hash & split_count_mask; + else + return cpu & split_count_mask; +} +#else /* #if defined(HAVE_SCHED_GETCPU) */ +static +int ht_get_split_count_index(unsigned long hash) +{ + return hash & split_count_mask; +} +#endif /* #else #if defined(HAVE_SCHED_GETCPU) */ + +static +void ht_count_add(struct cds_lfht *ht, unsigned long size, unsigned long hash) +{ + unsigned long split_count; + int index; + long count; + + if (caa_unlikely(!ht->split_count)) + return; + index = ht_get_split_count_index(hash); + split_count = uatomic_add_return(&ht->split_count[index].add, 1); + if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1))) + return; + /* Only if number of add multiple of 1UL << COUNT_COMMIT_ORDER */ + + dbg_printf("add split count %lu\n", split_count); + count = uatomic_add_return(&ht->count, + 1UL << COUNT_COMMIT_ORDER); + if (caa_likely(count & (count - 1))) + return; + /* Only if global count is power of 2 */ + + if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) < size) + return; + dbg_printf("add set global %ld\n", count); + cds_lfht_resize_lazy_count(ht, size, + count >> (CHAIN_LEN_TARGET - 1)); +} + +static +void ht_count_del(struct cds_lfht *ht, unsigned long size, unsigned long hash) +{ + unsigned long split_count; + int index; + long count; + + if (caa_unlikely(!ht->split_count)) + return; + index = ht_get_split_count_index(hash); + split_count = uatomic_add_return(&ht->split_count[index].del, 1); + if (caa_likely(split_count & ((1UL << COUNT_COMMIT_ORDER) - 1))) + return; + /* Only if number of deletes multiple of 1UL << COUNT_COMMIT_ORDER */ + + dbg_printf("del split count %lu\n", split_count); + count = uatomic_add_return(&ht->count, + -(1UL << COUNT_COMMIT_ORDER)); + if (caa_likely(count & (count - 1))) + return; + /* Only if global count is power of 2 */ + + if ((count >> CHAIN_LEN_RESIZE_THRESHOLD) >= size) + return; + dbg_printf("del set global %ld\n", count); + /* + * Don't shrink table if the number of nodes is below a + * certain threshold. + */ + if (count < (1UL << COUNT_COMMIT_ORDER) * (split_count_mask + 1)) + return; + cds_lfht_resize_lazy_count(ht, size, + count >> (CHAIN_LEN_TARGET - 1)); +} + +static +void check_resize(struct cds_lfht *ht, unsigned long size, uint32_t chain_len) +{ + unsigned long count; + + if (!(ht->flags & CDS_LFHT_AUTO_RESIZE)) + return; + count = uatomic_read(&ht->count); + /* + * Use bucket-local length for small table expand and for + * environments lacking per-cpu data support. + */ + if (count >= (1UL << COUNT_COMMIT_ORDER)) + return; + if (chain_len > 100) + dbg_printf("WARNING: large chain length: %u.\n", + chain_len); + if (chain_len >= CHAIN_LEN_RESIZE_THRESHOLD) + cds_lfht_resize_lazy_grow(ht, size, + cds_lfht_get_count_order_u32(chain_len - (CHAIN_LEN_TARGET - 1))); +} + +static +struct cds_lfht_node *clear_flag(struct cds_lfht_node *node) +{ + return (struct cds_lfht_node *) (((unsigned long) node) & ~FLAGS_MASK); +} + +static +int is_removed(struct cds_lfht_node *node) +{ + return ((unsigned long) node) & REMOVED_FLAG; +} + +static +struct cds_lfht_node *flag_removed(struct cds_lfht_node *node) +{ + return (struct cds_lfht_node *) (((unsigned long) node) | REMOVED_FLAG); +} + +static +int is_bucket(struct cds_lfht_node *node) +{ + return ((unsigned long) node) & BUCKET_FLAG; +} + +static +struct cds_lfht_node *flag_bucket(struct cds_lfht_node *node) +{ + return (struct cds_lfht_node *) (((unsigned long) node) | BUCKET_FLAG); +} + +static +int is_removal_owner(struct cds_lfht_node *node) +{ + return ((unsigned long) node) & REMOVAL_OWNER_FLAG; +} + +static +struct cds_lfht_node *flag_removal_owner(struct cds_lfht_node *node) +{ + return (struct cds_lfht_node *) (((unsigned long) node) | REMOVAL_OWNER_FLAG); +} + +static +struct cds_lfht_node *get_end(void) +{ + return (struct cds_lfht_node *) END_VALUE; +} + +static +int is_end(struct cds_lfht_node *node) +{ + return clear_flag(node) == (struct cds_lfht_node *) END_VALUE; +} + +static +unsigned long _uatomic_xchg_monotonic_increase(unsigned long *ptr, + unsigned long v) +{ + unsigned long old1, old2; + + old1 = uatomic_read(ptr); + do { + old2 = old1; + if (old2 >= v) + return old2; + } while ((old1 = uatomic_cmpxchg(ptr, old2, v)) != old2); + return old2; +} + +static +void cds_lfht_alloc_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + return ht->mm->alloc_bucket_table(ht, order); +} + +/* + * cds_lfht_free_bucket_table() should be called with decreasing order. + * When cds_lfht_free_bucket_table(0) is called, it means the whole + * lfht is destroyed. + */ +static +void cds_lfht_free_bucket_table(struct cds_lfht *ht, unsigned long order) +{ + return ht->mm->free_bucket_table(ht, order); +} + +static inline +struct cds_lfht_node *bucket_at(struct cds_lfht *ht, unsigned long index) +{ + return ht->bucket_at(ht, index); +} + +static inline +struct cds_lfht_node *lookup_bucket(struct cds_lfht *ht, unsigned long size, + unsigned long hash) +{ + assert(size > 0); + return bucket_at(ht, hash & (size - 1)); +} + +/* + * Remove all logically deleted nodes from a bucket up to a certain node key. + */ +static +void _cds_lfht_gc_bucket(struct cds_lfht_node *bucket, struct cds_lfht_node *node) +{ + struct cds_lfht_node *iter_prev, *iter, *next, *new_next; + + assert(!is_bucket(bucket)); + assert(!is_removed(bucket)); + assert(!is_bucket(node)); + assert(!is_removed(node)); + for (;;) { + iter_prev = bucket; + /* We can always skip the bucket node initially */ + iter = rcu_dereference(iter_prev->next); + assert(!is_removed(iter)); + assert(iter_prev->reverse_hash <= node->reverse_hash); + /* + * We should never be called with bucket (start of chain) + * and logically removed node (end of path compression + * marker) being the actual same node. This would be a + * bug in the algorithm implementation. + */ + assert(bucket != node); + for (;;) { + if (caa_unlikely(is_end(iter))) + return; + if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash)) + return; + next = rcu_dereference(clear_flag(iter)->next); + if (caa_likely(is_removed(next))) + break; + iter_prev = clear_flag(iter); + iter = next; + } + assert(!is_removed(iter)); + if (is_bucket(iter)) + new_next = flag_bucket(clear_flag(next)); + else + new_next = clear_flag(next); + (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next); + } +} + +static +int _cds_lfht_replace(struct cds_lfht *ht, unsigned long size, + struct cds_lfht_node *old_node, + struct cds_lfht_node *old_next, + struct cds_lfht_node *new_node) +{ + struct cds_lfht_node *bucket, *ret_next; + + if (!old_node) /* Return -ENOENT if asked to replace NULL node */ + return -ENOENT; + + assert(!is_removed(old_node)); + assert(!is_bucket(old_node)); + assert(!is_removed(new_node)); + assert(!is_bucket(new_node)); + assert(new_node != old_node); + for (;;) { + /* Insert after node to be replaced */ + if (is_removed(old_next)) { + /* + * Too late, the old node has been removed under us + * between lookup and replace. Fail. + */ + return -ENOENT; + } + assert(old_next == clear_flag(old_next)); + assert(new_node != old_next); + new_node->next = old_next; + /* + * Here is the whole trick for lock-free replace: we add + * the replacement node _after_ the node we want to + * replace by atomically setting its next pointer at the + * same time we set its removal flag. Given that + * the lookups/get next use an iterator aware of the + * next pointer, they will either skip the old node due + * to the removal flag and see the new node, or use + * the old node, but will not see the new one. + * This is a replacement of a node with another node + * that has the same value: we are therefore not + * removing a value from the hash table. + */ + ret_next = uatomic_cmpxchg(&old_node->next, + old_next, flag_removed(new_node)); + if (ret_next == old_next) + break; /* We performed the replacement. */ + old_next = ret_next; + } + + /* + * Ensure that the old node is not visible to readers anymore: + * lookup for the node, and remove it (along with any other + * logically removed node) if found. + */ + bucket = lookup_bucket(ht, size, bit_reverse_ulong(old_node->reverse_hash)); + _cds_lfht_gc_bucket(bucket, new_node); + + assert(is_removed(rcu_dereference(old_node->next))); + return 0; +} + +/* + * A non-NULL unique_ret pointer uses the "add unique" (or uniquify) add + * mode. A NULL unique_ret allows creation of duplicate keys. + */ +static +void _cds_lfht_add(struct cds_lfht *ht, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + unsigned long size, + struct cds_lfht_node *node, + struct cds_lfht_iter *unique_ret, + int bucket_flag) +{ + struct cds_lfht_node *iter_prev, *iter, *next, *new_node, *new_next, + *return_node; + struct cds_lfht_node *bucket; + + assert(!is_bucket(node)); + assert(!is_removed(node)); + bucket = lookup_bucket(ht, size, hash); + for (;;) { + uint32_t chain_len = 0; + + /* + * iter_prev points to the non-removed node prior to the + * insert location. + */ + iter_prev = bucket; + /* We can always skip the bucket node initially */ + iter = rcu_dereference(iter_prev->next); + assert(iter_prev->reverse_hash <= node->reverse_hash); + for (;;) { + if (caa_unlikely(is_end(iter))) + goto insert; + if (caa_likely(clear_flag(iter)->reverse_hash > node->reverse_hash)) + goto insert; + + /* bucket node is the first node of the identical-hash-value chain */ + if (bucket_flag && clear_flag(iter)->reverse_hash == node->reverse_hash) + goto insert; + + next = rcu_dereference(clear_flag(iter)->next); + if (caa_unlikely(is_removed(next))) + goto gc_node; + + /* uniquely add */ + if (unique_ret + && !is_bucket(next) + && clear_flag(iter)->reverse_hash == node->reverse_hash) { + struct cds_lfht_iter d_iter = { .node = node, .next = iter, }; + + /* + * uniquely adding inserts the node as the first + * node of the identical-hash-value node chain. + * + * This semantic ensures no duplicated keys + * should ever be observable in the table + * (including observe one node by one node + * by forward iterations) + */ + cds_lfht_next_duplicate(ht, match, key, &d_iter); + if (!d_iter.node) + goto insert; + + *unique_ret = d_iter; + return; + } + + /* Only account for identical reverse hash once */ + if (iter_prev->reverse_hash != clear_flag(iter)->reverse_hash + && !is_bucket(next)) + check_resize(ht, size, ++chain_len); + iter_prev = clear_flag(iter); + iter = next; + } + + insert: + assert(node != clear_flag(iter)); + assert(!is_removed(iter_prev)); + assert(!is_removed(iter)); + assert(iter_prev != node); + if (!bucket_flag) + node->next = clear_flag(iter); + else + node->next = flag_bucket(clear_flag(iter)); + if (is_bucket(iter)) + new_node = flag_bucket(node); + else + new_node = node; + if (uatomic_cmpxchg(&iter_prev->next, iter, + new_node) != iter) { + continue; /* retry */ + } else { + return_node = node; + goto end; + } + + gc_node: + assert(!is_removed(iter)); + if (is_bucket(iter)) + new_next = flag_bucket(clear_flag(next)); + else + new_next = clear_flag(next); + (void) uatomic_cmpxchg(&iter_prev->next, iter, new_next); + /* retry */ + } +end: + if (unique_ret) { + unique_ret->node = return_node; + /* unique_ret->next left unset, never used. */ + } +} + +static +int _cds_lfht_del(struct cds_lfht *ht, unsigned long size, + struct cds_lfht_node *node) +{ + struct cds_lfht_node *bucket, *next; + + if (!node) /* Return -ENOENT if asked to delete NULL node */ + return -ENOENT; + + /* logically delete the node */ + assert(!is_bucket(node)); + assert(!is_removed(node)); + assert(!is_removal_owner(node)); + + /* + * We are first checking if the node had previously been + * logically removed (this check is not atomic with setting the + * logical removal flag). Return -ENOENT if the node had + * previously been removed. + */ + next = rcu_dereference(node->next); + if (caa_unlikely(is_removed(next))) + return -ENOENT; + assert(!is_bucket(next)); + /* + * We set the REMOVED_FLAG unconditionally. Note that there may + * be more than one concurrent thread setting this flag. + * Knowing which wins the race will be known after the garbage + * collection phase, stay tuned! + */ + uatomic_or(&node->next, REMOVED_FLAG); + /* We performed the (logical) deletion. */ + + /* + * Ensure that the node is not visible to readers anymore: lookup for + * the node, and remove it (along with any other logically removed node) + * if found. + */ + bucket = lookup_bucket(ht, size, bit_reverse_ulong(node->reverse_hash)); + _cds_lfht_gc_bucket(bucket, node); + + assert(is_removed(rcu_dereference(node->next))); + /* + * Last phase: atomically exchange node->next with a version + * having "REMOVAL_OWNER_FLAG" set. If the returned node->next + * pointer did _not_ have "REMOVAL_OWNER_FLAG" set, we now own + * the node and win the removal race. + * It is interesting to note that all "add" paths are forbidden + * to change the next pointer starting from the point where the + * REMOVED_FLAG is set, so here using a read, followed by a + * xchg() suffice to guarantee that the xchg() will ever only + * set the "REMOVAL_OWNER_FLAG" (or change nothing if the flag + * was already set). + */ + if (!is_removal_owner(uatomic_xchg(&node->next, + flag_removal_owner(node->next)))) + return 0; + else + return -ENOENT; +} + +static +void *partition_resize_thread(void *arg) +{ + struct partition_resize_work *work = arg; + + work->ht->flavor->register_thread(); + work->fct(work->ht, work->i, work->start, work->len); + work->ht->flavor->unregister_thread(); + return NULL; +} + +static +void partition_resize_helper(struct cds_lfht *ht, unsigned long i, + unsigned long len, + void (*fct)(struct cds_lfht *ht, unsigned long i, + unsigned long start, unsigned long len)) +{ + unsigned long partition_len; + struct partition_resize_work *work; + int thread, ret; + unsigned long nr_threads; + + /* + * Note: nr_cpus_mask + 1 is always power of 2. + * We spawn just the number of threads we need to satisfy the minimum + * partition size, up to the number of CPUs in the system. + */ + if (nr_cpus_mask > 0) { + nr_threads = min(nr_cpus_mask + 1, + len >> MIN_PARTITION_PER_THREAD_ORDER); + } else { + nr_threads = 1; + } + partition_len = len >> cds_lfht_get_count_order_ulong(nr_threads); + work = calloc(nr_threads, sizeof(*work)); + assert(work); + for (thread = 0; thread < nr_threads; thread++) { + work[thread].ht = ht; + work[thread].i = i; + work[thread].len = partition_len; + work[thread].start = thread * partition_len; + work[thread].fct = fct; + ret = pthread_create(&(work[thread].thread_id), ht->resize_attr, + partition_resize_thread, &work[thread]); + assert(!ret); + } + for (thread = 0; thread < nr_threads; thread++) { + ret = pthread_join(work[thread].thread_id, NULL); + assert(!ret); + } + free(work); +} + +/* + * Holding RCU read lock to protect _cds_lfht_add against memory + * reclaim that could be performed by other call_rcu worker threads (ABA + * problem). + * + * When we reach a certain length, we can split this population phase over + * many worker threads, based on the number of CPUs available in the system. + * This should therefore take care of not having the expand lagging behind too + * many concurrent insertion threads by using the scheduler's ability to + * schedule bucket node population fairly with insertions. + */ +static +void init_table_populate_partition(struct cds_lfht *ht, unsigned long i, + unsigned long start, unsigned long len) +{ + unsigned long j, size = 1UL << (i - 1); + + assert(i > MIN_TABLE_ORDER); + ht->flavor->read_lock(); + for (j = size + start; j < size + start + len; j++) { + struct cds_lfht_node *new_node = bucket_at(ht, j); + + assert(j >= size && j < (size << 1)); + dbg_printf("init populate: order %lu index %lu hash %lu\n", + i, j, j); + new_node->reverse_hash = bit_reverse_ulong(j); + _cds_lfht_add(ht, j, NULL, NULL, size, new_node, NULL, 1); + } + ht->flavor->read_unlock(); +} + +static +void init_table_populate(struct cds_lfht *ht, unsigned long i, + unsigned long len) +{ + assert(nr_cpus_mask != -1); + if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) { + ht->flavor->thread_online(); + init_table_populate_partition(ht, i, 0, len); + ht->flavor->thread_offline(); + return; + } + partition_resize_helper(ht, i, len, init_table_populate_partition); +} + +static +void init_table(struct cds_lfht *ht, + unsigned long first_order, unsigned long last_order) +{ + unsigned long i; + + dbg_printf("init table: first_order %lu last_order %lu\n", + first_order, last_order); + assert(first_order > MIN_TABLE_ORDER); + for (i = first_order; i <= last_order; i++) { + unsigned long len; + + len = 1UL << (i - 1); + dbg_printf("init order %lu len: %lu\n", i, len); + + /* Stop expand if the resize target changes under us */ + if (CMM_LOAD_SHARED(ht->resize_target) < (1UL << i)) + break; + + cds_lfht_alloc_bucket_table(ht, i); + + /* + * Set all bucket nodes reverse hash values for a level and + * link all bucket nodes into the table. + */ + init_table_populate(ht, i, len); + + /* + * Update table size. + */ + cmm_smp_wmb(); /* populate data before RCU size */ + CMM_STORE_SHARED(ht->size, 1UL << i); + + dbg_printf("init new size: %lu\n", 1UL << i); + if (CMM_LOAD_SHARED(ht->in_progress_destroy)) + break; + } +} + +/* + * Holding RCU read lock to protect _cds_lfht_remove against memory + * reclaim that could be performed by other call_rcu worker threads (ABA + * problem). + * For a single level, we logically remove and garbage collect each node. + * + * As a design choice, we perform logical removal and garbage collection on a + * node-per-node basis to simplify this algorithm. We also assume keeping good + * cache locality of the operation would overweight possible performance gain + * that could be achieved by batching garbage collection for multiple levels. + * However, this would have to be justified by benchmarks. + * + * Concurrent removal and add operations are helping us perform garbage + * collection of logically removed nodes. We guarantee that all logically + * removed nodes have been garbage-collected (unlinked) before call_rcu is + * invoked to free a hole level of bucket nodes (after a grace period). + * + * Logical removal and garbage collection can therefore be done in batch or on a + * node-per-node basis, as long as the guarantee above holds. + * + * When we reach a certain length, we can split this removal over many worker + * threads, based on the number of CPUs available in the system. This should + * take care of not letting resize process lag behind too many concurrent + * updater threads actively inserting into the hash table. + */ +static +void remove_table_partition(struct cds_lfht *ht, unsigned long i, + unsigned long start, unsigned long len) +{ + unsigned long j, size = 1UL << (i - 1); + + assert(i > MIN_TABLE_ORDER); + ht->flavor->read_lock(); + for (j = size + start; j < size + start + len; j++) { + struct cds_lfht_node *fini_bucket = bucket_at(ht, j); + struct cds_lfht_node *parent_bucket = bucket_at(ht, j - size); + + assert(j >= size && j < (size << 1)); + dbg_printf("remove entry: order %lu index %lu hash %lu\n", + i, j, j); + /* Set the REMOVED_FLAG to freeze the ->next for gc */ + uatomic_or(&fini_bucket->next, REMOVED_FLAG); + _cds_lfht_gc_bucket(parent_bucket, fini_bucket); + } + ht->flavor->read_unlock(); +} + +static +void remove_table(struct cds_lfht *ht, unsigned long i, unsigned long len) +{ + + assert(nr_cpus_mask != -1); + if (nr_cpus_mask < 0 || len < 2 * MIN_PARTITION_PER_THREAD) { + ht->flavor->thread_online(); + remove_table_partition(ht, i, 0, len); + ht->flavor->thread_offline(); + return; + } + partition_resize_helper(ht, i, len, remove_table_partition); +} + +/* + * fini_table() is never called for first_order == 0, which is why + * free_by_rcu_order == 0 can be used as criterion to know if free must + * be called. + */ +static +void fini_table(struct cds_lfht *ht, + unsigned long first_order, unsigned long last_order) +{ + long i; + unsigned long free_by_rcu_order = 0; + + dbg_printf("fini table: first_order %lu last_order %lu\n", + first_order, last_order); + assert(first_order > MIN_TABLE_ORDER); + for (i = last_order; i >= first_order; i--) { + unsigned long len; + + len = 1UL << (i - 1); + dbg_printf("fini order %lu len: %lu\n", i, len); + + /* Stop shrink if the resize target changes under us */ + if (CMM_LOAD_SHARED(ht->resize_target) > (1UL << (i - 1))) + break; + + cmm_smp_wmb(); /* populate data before RCU size */ + CMM_STORE_SHARED(ht->size, 1UL << (i - 1)); + + /* + * We need to wait for all add operations to reach Q.S. (and + * thus use the new table for lookups) before we can start + * releasing the old bucket nodes. Otherwise their lookup will + * return a logically removed node as insert position. + */ + ht->flavor->update_synchronize_rcu(); + if (free_by_rcu_order) + cds_lfht_free_bucket_table(ht, free_by_rcu_order); + + /* + * Set "removed" flag in bucket nodes about to be removed. + * Unlink all now-logically-removed bucket node pointers. + * Concurrent add/remove operation are helping us doing + * the gc. + */ + remove_table(ht, i, len); + + free_by_rcu_order = i; + + dbg_printf("fini new size: %lu\n", 1UL << i); + if (CMM_LOAD_SHARED(ht->in_progress_destroy)) + break; + } + + if (free_by_rcu_order) { + ht->flavor->update_synchronize_rcu(); + cds_lfht_free_bucket_table(ht, free_by_rcu_order); + } +} + +static +void cds_lfht_create_bucket(struct cds_lfht *ht, unsigned long size) +{ + struct cds_lfht_node *prev, *node; + unsigned long order, len, i; + + cds_lfht_alloc_bucket_table(ht, 0); + + dbg_printf("create bucket: order 0 index 0 hash 0\n"); + node = bucket_at(ht, 0); + node->next = flag_bucket(get_end()); + node->reverse_hash = 0; + + for (order = 1; order < cds_lfht_get_count_order_ulong(size) + 1; order++) { + len = 1UL << (order - 1); + cds_lfht_alloc_bucket_table(ht, order); + + for (i = 0; i < len; i++) { + /* + * Now, we are trying to init the node with the + * hash=(len+i) (which is also a bucket with the + * index=(len+i)) and insert it into the hash table, + * so this node has to be inserted after the bucket + * with the index=(len+i)&(len-1)=i. And because there + * is no other non-bucket node nor bucket node with + * larger index/hash inserted, so the bucket node + * being inserted should be inserted directly linked + * after the bucket node with index=i. + */ + prev = bucket_at(ht, i); + node = bucket_at(ht, len + i); + + dbg_printf("create bucket: order %lu index %lu hash %lu\n", + order, len + i, len + i); + node->reverse_hash = bit_reverse_ulong(len + i); + + /* insert after prev */ + assert(is_bucket(prev->next)); + node->next = prev->next; + prev->next = flag_bucket(node); + } + } +} + +struct cds_lfht *_cds_lfht_new(unsigned long init_size, + unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets, + int flags, + const struct cds_lfht_mm_type *mm, + const struct rcu_flavor_struct *flavor, + pthread_attr_t *attr) +{ + struct cds_lfht *ht; + unsigned long order; + + /* min_nr_alloc_buckets must be power of two */ + if (!min_nr_alloc_buckets || (min_nr_alloc_buckets & (min_nr_alloc_buckets - 1))) + return NULL; + + /* init_size must be power of two */ + if (!init_size || (init_size & (init_size - 1))) + return NULL; + + /* + * Memory management plugin default. + */ + if (!mm) { + if (CAA_BITS_PER_LONG > 32 + && max_nr_buckets + && max_nr_buckets <= (1ULL << 32)) { + /* + * For 64-bit architectures, with max number of + * buckets small enough not to use the entire + * 64-bit memory mapping space (and allowing a + * fair number of hash table instances), use the + * mmap allocator, which is faster than the + * order allocator. + */ + mm = &cds_lfht_mm_mmap; + } else { + /* + * The fallback is to use the order allocator. + */ + mm = &cds_lfht_mm_order; + } + } + + /* max_nr_buckets == 0 for order based mm means infinite */ + if (mm == &cds_lfht_mm_order && !max_nr_buckets) + max_nr_buckets = 1UL << (MAX_TABLE_ORDER - 1); + + /* max_nr_buckets must be power of two */ + if (!max_nr_buckets || (max_nr_buckets & (max_nr_buckets - 1))) + return NULL; + + min_nr_alloc_buckets = max(min_nr_alloc_buckets, MIN_TABLE_SIZE); + init_size = max(init_size, MIN_TABLE_SIZE); + max_nr_buckets = max(max_nr_buckets, min_nr_alloc_buckets); + init_size = min(init_size, max_nr_buckets); + + ht = mm->alloc_cds_lfht(min_nr_alloc_buckets, max_nr_buckets); + assert(ht); + assert(ht->mm == mm); + assert(ht->bucket_at == mm->bucket_at); + + ht->flags = flags; + ht->flavor = flavor; + ht->resize_attr = attr; + alloc_split_items_count(ht); + /* this mutex should not nest in read-side C.S. */ + pthread_mutex_init(&ht->resize_mutex, NULL); + order = cds_lfht_get_count_order_ulong(init_size); + ht->resize_target = 1UL << order; + cds_lfht_create_bucket(ht, 1UL << order); + ht->size = 1UL << order; + return ht; +} + +void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash, + cds_lfht_match_fct match, const void *key, + struct cds_lfht_iter *iter) +{ + struct cds_lfht_node *node, *next, *bucket; + unsigned long reverse_hash, size; + + reverse_hash = bit_reverse_ulong(hash); + + size = rcu_dereference(ht->size); + bucket = lookup_bucket(ht, size, hash); + /* We can always skip the bucket node initially */ + node = rcu_dereference(bucket->next); + node = clear_flag(node); + for (;;) { + if (caa_unlikely(is_end(node))) { + node = next = NULL; + break; + } + if (caa_unlikely(node->reverse_hash > reverse_hash)) { + node = next = NULL; + break; + } + next = rcu_dereference(node->next); + assert(node == clear_flag(node)); + if (caa_likely(!is_removed(next)) + && !is_bucket(next) + && node->reverse_hash == reverse_hash + && caa_likely(match(node, key))) { + break; + } + node = clear_flag(next); + } + assert(!node || !is_bucket(rcu_dereference(node->next))); + iter->node = node; + iter->next = next; +} + +void cds_lfht_next_duplicate(struct cds_lfht *ht, cds_lfht_match_fct match, + const void *key, struct cds_lfht_iter *iter) +{ + struct cds_lfht_node *node, *next; + unsigned long reverse_hash; + + node = iter->node; + reverse_hash = node->reverse_hash; + next = iter->next; + node = clear_flag(next); + + for (;;) { + if (caa_unlikely(is_end(node))) { + node = next = NULL; + break; + } + if (caa_unlikely(node->reverse_hash > reverse_hash)) { + node = next = NULL; + break; + } + next = rcu_dereference(node->next); + if (caa_likely(!is_removed(next)) + && !is_bucket(next) + && caa_likely(match(node, key))) { + break; + } + node = clear_flag(next); + } + assert(!node || !is_bucket(rcu_dereference(node->next))); + iter->node = node; + iter->next = next; +} + +void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter) +{ + struct cds_lfht_node *node, *next; + + node = clear_flag(iter->next); + for (;;) { + if (caa_unlikely(is_end(node))) { + node = next = NULL; + break; + } + next = rcu_dereference(node->next); + if (caa_likely(!is_removed(next)) + && !is_bucket(next)) { + break; + } + node = clear_flag(next); + } + assert(!node || !is_bucket(rcu_dereference(node->next))); + iter->node = node; + iter->next = next; +} + +void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter) +{ + /* + * Get next after first bucket node. The first bucket node is the + * first node of the linked list. + */ + iter->next = bucket_at(ht, 0)->next; + cds_lfht_next(ht, iter); +} + +void cds_lfht_add(struct cds_lfht *ht, unsigned long hash, + struct cds_lfht_node *node) +{ + unsigned long size; + + node->reverse_hash = bit_reverse_ulong(hash); + size = rcu_dereference(ht->size); + _cds_lfht_add(ht, hash, NULL, NULL, size, node, NULL, 0); + ht_count_add(ht, size, hash); +} + +struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *node) +{ + unsigned long size; + struct cds_lfht_iter iter; + + node->reverse_hash = bit_reverse_ulong(hash); + size = rcu_dereference(ht->size); + _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0); + if (iter.node == node) + ht_count_add(ht, size, hash); + return iter.node; +} + +struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *node) +{ + unsigned long size; + struct cds_lfht_iter iter; + + node->reverse_hash = bit_reverse_ulong(hash); + size = rcu_dereference(ht->size); + for (;;) { + _cds_lfht_add(ht, hash, match, key, size, node, &iter, 0); + if (iter.node == node) { + ht_count_add(ht, size, hash); + return NULL; + } + + if (!_cds_lfht_replace(ht, size, iter.node, iter.next, node)) + return iter.node; + } +} + +int cds_lfht_replace(struct cds_lfht *ht, + struct cds_lfht_iter *old_iter, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *new_node) +{ + unsigned long size; + + new_node->reverse_hash = bit_reverse_ulong(hash); + if (!old_iter->node) + return -ENOENT; + if (caa_unlikely(old_iter->node->reverse_hash != new_node->reverse_hash)) + return -EINVAL; + if (caa_unlikely(!match(old_iter->node, key))) + return -EINVAL; + size = rcu_dereference(ht->size); + return _cds_lfht_replace(ht, size, old_iter->node, old_iter->next, + new_node); +} + +int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node) +{ + unsigned long size, hash; + int ret; + + size = rcu_dereference(ht->size); + ret = _cds_lfht_del(ht, size, node); + if (!ret) { + hash = bit_reverse_ulong(node->reverse_hash); + ht_count_del(ht, size, hash); + } + return ret; +} + +static +int cds_lfht_delete_bucket(struct cds_lfht *ht) +{ + struct cds_lfht_node *node; + unsigned long order, i, size; + + /* Check that the table is empty */ + node = bucket_at(ht, 0); + do { + node = clear_flag(node)->next; + if (!is_bucket(node)) + return -EPERM; + assert(!is_removed(node)); + } while (!is_end(node)); + /* + * size accessed without rcu_dereference because hash table is + * being destroyed. + */ + size = ht->size; + /* Internal sanity check: all nodes left should be bucket */ + for (i = 0; i < size; i++) { + node = bucket_at(ht, i); + dbg_printf("delete bucket: index %lu expected hash %lu hash %lu\n", + i, i, bit_reverse_ulong(node->reverse_hash)); + assert(is_bucket(node->next)); + } + + for (order = cds_lfht_get_count_order_ulong(size); (long)order >= 0; order--) + cds_lfht_free_bucket_table(ht, order); + + return 0; +} + +/* + * Should only be called when no more concurrent readers nor writers can + * possibly access the table. + */ +int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr) +{ + int ret; + + /* Wait for in-flight resize operations to complete */ + _CMM_STORE_SHARED(ht->in_progress_destroy, 1); + cmm_smp_mb(); /* Store destroy before load resize */ + while (uatomic_read(&ht->in_progress_resize)) + poll(NULL, 0, 100); /* wait for 100ms */ + ret = cds_lfht_delete_bucket(ht); + if (ret) + return ret; + free_split_items_count(ht); + if (attr) + *attr = ht->resize_attr; + poison_free(ht); + return ret; +} + +void cds_lfht_count_nodes(struct cds_lfht *ht, + long *approx_before, + unsigned long *count, + long *approx_after) +{ + struct cds_lfht_node *node, *next; + unsigned long nr_bucket = 0, nr_removed = 0; + + *approx_before = 0; + if (ht->split_count) { + int i; + + for (i = 0; i < split_count_mask + 1; i++) { + *approx_before += uatomic_read(&ht->split_count[i].add); + *approx_before -= uatomic_read(&ht->split_count[i].del); + } + } + + *count = 0; + + /* Count non-bucket nodes in the table */ + node = bucket_at(ht, 0); + do { + next = rcu_dereference(node->next); + if (is_removed(next)) { + if (!is_bucket(next)) + (nr_removed)++; + else + (nr_bucket)++; + } else if (!is_bucket(next)) + (*count)++; + else + (nr_bucket)++; + node = clear_flag(next); + } while (!is_end(node)); + dbg_printf("number of logically removed nodes: %lu\n", nr_removed); + dbg_printf("number of bucket nodes: %lu\n", nr_bucket); + *approx_after = 0; + if (ht->split_count) { + int i; + + for (i = 0; i < split_count_mask + 1; i++) { + *approx_after += uatomic_read(&ht->split_count[i].add); + *approx_after -= uatomic_read(&ht->split_count[i].del); + } + } +} + +/* called with resize mutex held */ +static +void _do_cds_lfht_grow(struct cds_lfht *ht, + unsigned long old_size, unsigned long new_size) +{ + unsigned long old_order, new_order; + + old_order = cds_lfht_get_count_order_ulong(old_size); + new_order = cds_lfht_get_count_order_ulong(new_size); + dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n", + old_size, old_order, new_size, new_order); + assert(new_size > old_size); + init_table(ht, old_order + 1, new_order); +} + +/* called with resize mutex held */ +static +void _do_cds_lfht_shrink(struct cds_lfht *ht, + unsigned long old_size, unsigned long new_size) +{ + unsigned long old_order, new_order; + + new_size = max(new_size, MIN_TABLE_SIZE); + old_order = cds_lfht_get_count_order_ulong(old_size); + new_order = cds_lfht_get_count_order_ulong(new_size); + dbg_printf("resize from %lu (order %lu) to %lu (order %lu) buckets\n", + old_size, old_order, new_size, new_order); + assert(new_size < old_size); + + /* Remove and unlink all bucket nodes to remove. */ + fini_table(ht, new_order + 1, old_order); +} + + +/* called with resize mutex held */ +static +void _do_cds_lfht_resize(struct cds_lfht *ht) +{ + unsigned long new_size, old_size; + + /* + * Resize table, re-do if the target size has changed under us. + */ + do { + assert(uatomic_read(&ht->in_progress_resize)); + if (CMM_LOAD_SHARED(ht->in_progress_destroy)) + break; + ht->resize_initiated = 1; + old_size = ht->size; + new_size = CMM_LOAD_SHARED(ht->resize_target); + if (old_size < new_size) + _do_cds_lfht_grow(ht, old_size, new_size); + else if (old_size > new_size) + _do_cds_lfht_shrink(ht, old_size, new_size); + ht->resize_initiated = 0; + /* write resize_initiated before read resize_target */ + cmm_smp_mb(); + } while (ht->size != CMM_LOAD_SHARED(ht->resize_target)); +} + +static +unsigned long resize_target_grow(struct cds_lfht *ht, unsigned long new_size) +{ + return _uatomic_xchg_monotonic_increase(&ht->resize_target, new_size); +} + +static +void resize_target_update_count(struct cds_lfht *ht, + unsigned long count) +{ + count = max(count, MIN_TABLE_SIZE); + count = min(count, ht->max_nr_buckets); + uatomic_set(&ht->resize_target, count); +} + +void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size) +{ + resize_target_update_count(ht, new_size); + CMM_STORE_SHARED(ht->resize_initiated, 1); + ht->flavor->thread_offline(); + pthread_mutex_lock(&ht->resize_mutex); + _do_cds_lfht_resize(ht); + pthread_mutex_unlock(&ht->resize_mutex); + ht->flavor->thread_online(); +} + +static +void do_resize_cb(struct rcu_head *head) +{ + struct rcu_resize_work *work = + caa_container_of(head, struct rcu_resize_work, head); + struct cds_lfht *ht = work->ht; + + ht->flavor->thread_offline(); + pthread_mutex_lock(&ht->resize_mutex); + _do_cds_lfht_resize(ht); + pthread_mutex_unlock(&ht->resize_mutex); + ht->flavor->thread_online(); + poison_free(work); + cmm_smp_mb(); /* finish resize before decrement */ + uatomic_dec(&ht->in_progress_resize); +} + +static +void __cds_lfht_resize_lazy_launch(struct cds_lfht *ht) +{ + struct rcu_resize_work *work; + + /* Store resize_target before read resize_initiated */ + cmm_smp_mb(); + if (!CMM_LOAD_SHARED(ht->resize_initiated)) { + uatomic_inc(&ht->in_progress_resize); + cmm_smp_mb(); /* increment resize count before load destroy */ + if (CMM_LOAD_SHARED(ht->in_progress_destroy)) { + uatomic_dec(&ht->in_progress_resize); + return; + } + work = malloc(sizeof(*work)); + work->ht = ht; + ht->flavor->update_call_rcu(&work->head, do_resize_cb); + CMM_STORE_SHARED(ht->resize_initiated, 1); + } +} + +static +void cds_lfht_resize_lazy_grow(struct cds_lfht *ht, unsigned long size, int growth) +{ + unsigned long target_size = size << growth; + + target_size = min(target_size, ht->max_nr_buckets); + if (resize_target_grow(ht, target_size) >= target_size) + return; + + __cds_lfht_resize_lazy_launch(ht); +} + +/* + * We favor grow operations over shrink. A shrink operation never occurs + * if a grow operation is queued for lazy execution. A grow operation + * cancels any pending shrink lazy execution. + */ +static +void cds_lfht_resize_lazy_count(struct cds_lfht *ht, unsigned long size, + unsigned long count) +{ + if (!(ht->flags & CDS_LFHT_AUTO_RESIZE)) + return; + count = max(count, MIN_TABLE_SIZE); + count = min(count, ht->max_nr_buckets); + if (count == size) + return; /* Already the right size, no resize needed */ + if (count > size) { /* lazy grow */ + if (resize_target_grow(ht, count) >= count) + return; + } else { /* lazy shrink */ + for (;;) { + unsigned long s; + + s = uatomic_cmpxchg(&ht->resize_target, size, count); + if (s == size) + break; /* no resize needed */ + if (s > size) + return; /* growing is/(was just) in progress */ + if (s <= count) + return; /* some other thread do shrink */ + size = s; + } + } + __cds_lfht_resize_lazy_launch(ht); +} diff --git a/src/common/hashtable/rculfhash.h b/src/common/hashtable/rculfhash.h new file mode 100644 index 000000000..136c7259b --- /dev/null +++ b/src/common/hashtable/rculfhash.h @@ -0,0 +1,434 @@ +#ifndef _URCU_RCULFHASH_H +#define _URCU_RCULFHASH_H + +/* + * urcu/rculfhash.h + * + * Userspace RCU library - Lock-Free RCU Hash Table + * + * Copyright 2011 - Mathieu Desnoyers + * Copyright 2011 - Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Include this file _after_ including your URCU flavor. + */ + +#include +#include +#include + +#include "urcu-flavor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * cds_lfht_node: Contains the next pointers and reverse-hash + * value required for lookup and traversal of the hash table. + * + * struct cds_lfht_node should be aligned on 8-bytes boundaries because + * the three lower bits are used as flags. It is worth noting that the + * information contained within these three bits could be represented on + * two bits by re-using the same bit for REMOVAL_OWNER_FLAG and + * BUCKET_FLAG. This can be done if we ensure that no iterator nor + * updater check the BUCKET_FLAG after it detects that the REMOVED_FLAG + * is set. Given the minimum size of struct cds_lfht_node is 8 bytes on + * 32-bit architectures, we choose to go for simplicity and reserve + * three bits. + * + * struct cds_lfht_node can be embedded into a structure (as a field). + * caa_container_of() can be used to get the structure from the struct + * cds_lfht_node after a lookup. + * + * The structure which embeds it typically holds the key (or key-value + * pair) of the object. The caller code is responsible for calculation + * of the hash value for cds_lfht APIs. + */ +struct cds_lfht_node { + struct cds_lfht_node *next; /* ptr | REMOVAL_OWNER_FLAG | BUCKET_FLAG | REMOVED_FLAG */ + unsigned long reverse_hash; +} __attribute__((aligned(8))); + +/* cds_lfht_iter: Used to track state while traversing a hash chain. */ +struct cds_lfht_iter { + struct cds_lfht_node *node, *next; +}; + +static inline +struct cds_lfht_node *cds_lfht_iter_get_node(struct cds_lfht_iter *iter) +{ + return iter->node; +} + +struct cds_lfht; + +/* + * Caution ! + * Ensure reader and writer threads are registered as urcu readers. + */ + +typedef int (*cds_lfht_match_fct)(struct cds_lfht_node *node, const void *key); + +/* + * cds_lfht_node_init - initialize a hash table node + * @node: the node to initialize. + * + * This function is kept to be eventually used for debugging purposes + * (detection of memory corruption). + */ +static inline +void cds_lfht_node_init(struct cds_lfht_node *node) +{ +} + +/* + * Hash table creation flags. + */ +enum { + CDS_LFHT_AUTO_RESIZE = (1U << 0), + CDS_LFHT_ACCOUNTING = (1U << 1), +}; + +struct cds_lfht_mm_type { + struct cds_lfht *(*alloc_cds_lfht)(unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets); + void (*alloc_bucket_table)(struct cds_lfht *ht, unsigned long order); + void (*free_bucket_table)(struct cds_lfht *ht, unsigned long order); + struct cds_lfht_node *(*bucket_at)(struct cds_lfht *ht, + unsigned long index); +}; + +extern const struct cds_lfht_mm_type cds_lfht_mm_order; +extern const struct cds_lfht_mm_type cds_lfht_mm_chunk; +extern const struct cds_lfht_mm_type cds_lfht_mm_mmap; + +/* + * _cds_lfht_new - API used by cds_lfht_new wrapper. Do not use directly. + */ +struct cds_lfht *_cds_lfht_new(unsigned long init_size, + unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets, + int flags, + const struct cds_lfht_mm_type *mm, + const struct rcu_flavor_struct *flavor, + pthread_attr_t *attr); + +/* + * cds_lfht_new - allocate a hash table. + * @init_size: number of buckets to allocate initially. Must be power of two. + * @min_nr_alloc_buckets: the minimum number of allocated buckets. + * (must be power of two) + * @max_nr_buckets: the maximum number of hash table buckets allowed. + * (must be power of two) + * @flags: hash table creation flags (can be combined with bitwise or: '|'). + * 0: no flags. + * CDS_LFHT_AUTO_RESIZE: automatically resize hash table. + * CDS_LFHT_ACCOUNTING: count the number of node addition + * and removal in the table + * @attr: optional resize worker thread attributes. NULL for default. + * + * Return NULL on error. + * Note: the RCU flavor must be already included before the hash table header. + * + * The programmer is responsible for ensuring that resize operation has a + * priority equal to hash table updater threads. It should be performed by + * specifying the appropriate priority in the pthread "attr" argument, and, + * for CDS_LFHT_AUTO_RESIZE, by ensuring that call_rcu worker threads also have + * this priority level. Having lower priority for call_rcu and resize threads + * does not pose any correctness issue, but the resize operations could be + * starved by updates, thus leading to long hash table bucket chains. + * Threads calling this API are NOT required to be registered RCU read-side + * threads. It can be called very early.(before rcu is initialized ...etc.) + */ +static inline +struct cds_lfht *cds_lfht_new(unsigned long init_size, + unsigned long min_nr_alloc_buckets, + unsigned long max_nr_buckets, + int flags, + pthread_attr_t *attr) +{ + return _cds_lfht_new(init_size, min_nr_alloc_buckets, max_nr_buckets, + flags, NULL, &rcu_flavor, attr); +} + +/* + * cds_lfht_destroy - destroy a hash table. + * @ht: the hash table to destroy. + * @attr: (output) resize worker thread attributes, as received by cds_lfht_new. + * The caller will typically want to free this pointer if dynamically + * allocated. The attr point can be NULL if the caller does not + * need to be informed of the value passed to cds_lfht_new(). + * + * Return 0 on success, negative error value on error. + * Threads calling this API need to be registered RCU read-side threads. + */ +int cds_lfht_destroy(struct cds_lfht *ht, pthread_attr_t **attr); + +/* + * cds_lfht_count_nodes - count the number of nodes in the hash table. + * @ht: the hash table. + * @split_count_before: Sample the node count split-counter before traversal. + * @count: Traverse the hash table, count the number of nodes observed. + * @split_count_after: Sample the node count split-counter after traversal. + * + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_count_nodes(struct cds_lfht *ht, + long *split_count_before, + unsigned long *count, + long *split_count_after); + +/* + * cds_lfht_lookup - lookup a node by key. + * @ht: the hash table. + * @hash: the key hash. + * @match: the key match function. + * @key: the current node key. + * @iter: Node, if found (output). *iter->node set to NULL if not found. + * + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_lookup(struct cds_lfht *ht, unsigned long hash, + cds_lfht_match_fct match, const void *key, + struct cds_lfht_iter *iter); + +/* + * cds_lfht_next_duplicate - get the next item with same key (after a lookup). + * @ht: the hash table. + * @match: the key match function. + * @key: the current node key. + * @iter: Node, if found (output). *iter->node set to NULL if not found. + * + * Uses an iterator initialized by a lookup. + * Sets *iter-node to the following node with same key. + * Sets *iter->node to NULL if no following node exists with same key. + * RCU read-side lock must be held across cds_lfht_lookup and + * cds_lfht_next calls, and also between cds_lfht_next calls using the + * node returned by a previous cds_lfht_next. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_next_duplicate(struct cds_lfht *ht, + cds_lfht_match_fct match, const void *key, + struct cds_lfht_iter *iter); + +/* + * cds_lfht_first - get the first node in the table. + * @ht: the hash table. + * @iter: First node, if exists (output). *iter->node set to NULL if not found. + * + * Output in "*iter". *iter->node set to NULL if table is empty. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_first(struct cds_lfht *ht, struct cds_lfht_iter *iter); + +/* + * cds_lfht_next - get the next node in the table. + * @ht: the hash table. + * @iter: Next node, if exists (output). *iter->node set to NULL if not found. + * + * Input/Output in "*iter". *iter->node set to NULL if *iter was + * pointing to the last table node. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_next(struct cds_lfht *ht, struct cds_lfht_iter *iter); + +/* + * cds_lfht_add - add a node to the hash table. + * @ht: the hash table. + * @hash: the key hash. + * @node: the node to add. + * + * This function supports adding redundant keys into the table. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_add(struct cds_lfht *ht, unsigned long hash, + struct cds_lfht_node *node); + +/* + * cds_lfht_add_unique - add a node to hash table, if key is not present. + * @ht: the hash table. + * @hash: the node's hash. + * @match: the key match function. + * @key: the node's key. + * @node: the node to try adding. + * + * Return the node added upon success. + * Return the unique node already present upon failure. If + * cds_lfht_add_unique fails, the node passed as parameter should be + * freed by the caller. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + * + * The semantic of this function is that if only this function is used + * to add keys into the table, no duplicated keys should ever be + * observable in the table. The same guarantee apply for combination of + * add_unique and add_replace (see below). + */ +struct cds_lfht_node *cds_lfht_add_unique(struct cds_lfht *ht, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *node); + +/* + * cds_lfht_add_replace - replace or add a node within hash table. + * @ht: the hash table. + * @hash: the node's hash. + * @match: the key match function. + * @key: the node's key. + * @node: the node to add. + * + * Return the node replaced upon success. If no node matching the key + * was present, return NULL, which also means the operation succeeded. + * This replacement operation should never fail. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + * After successful replacement, a grace period must be waited for before + * freeing the memory reserved for the returned node. + * + * The semantic of replacement vs lookups is the following: if lookups + * are performed between a key unique insertion and its removal, we + * guarantee that the lookups and get next will always find exactly one + * instance of the key if it is replaced concurrently with the lookups. + * + * Providing this semantic allows us to ensure that replacement-only + * schemes will never generate duplicated keys. It also allows us to + * guarantee that a combination of add_replace and add_unique updates + * will never generate duplicated keys. + */ +struct cds_lfht_node *cds_lfht_add_replace(struct cds_lfht *ht, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *node); + +/* + * cds_lfht_replace - replace a node pointer to by iter within hash table. + * @ht: the hash table. + * @old_iter: the iterator position of the node to replace. + * @hash: the node's hash. + * @match: the key match function. + * @key: the node's key. + * @new_node: the new node to use as replacement. + * + * Return 0 if replacement is successful, negative value otherwise. + * Replacing a NULL old node or an already removed node will fail with + * -ENOENT. + * If the hash or value of the node to replace and the new node differ, + * this function returns -EINVAL without proceeding to the replacement. + * Old node can be looked up with cds_lfht_lookup and cds_lfht_next. + * RCU read-side lock must be held between lookup and replacement. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + * After successful replacement, a grace period must be waited for before + * freeing the memory reserved for the old node (which can be accessed + * with cds_lfht_iter_get_node). + * + * The semantic of replacement vs lookups is the following: if lookups + * are performed between a key unique insertion and its removal, we + * guarantee that the lookups and get next will always find exactly one + * instance of the key if it is replaced concurrently with the lookups. + * + * Providing this semantic allows us to ensure that replacement-only + * schemes will never generate duplicated keys. It also allows us to + * guarantee that a combination of add_replace and add_unique updates + * will never generate duplicated keys. + */ +int cds_lfht_replace(struct cds_lfht *ht, + struct cds_lfht_iter *old_iter, + unsigned long hash, + cds_lfht_match_fct match, + const void *key, + struct cds_lfht_node *new_node); + +/* + * cds_lfht_del - remove node pointed to by iterator from hash table. + * @ht: the hash table. + * @node: the node to delete. + * + * Return 0 if the node is successfully removed, negative value + * otherwise. + * Deleting a NULL node or an already removed node will fail with a + * negative value. + * Node can be looked up with cds_lfht_lookup and cds_lfht_next, + * followed by use of cds_lfht_iter_get_node. + * RCU read-side lock must be held between lookup and removal. + * Call with rcu_read_lock held. + * Threads calling this API need to be registered RCU read-side threads. + * After successful removal, a grace period must be waited for before + * freeing the memory reserved for old node (which can be accessed with + * cds_lfht_iter_get_node). + */ +int cds_lfht_del(struct cds_lfht *ht, struct cds_lfht_node *node); + +/* + * cds_lfht_resize - Force a hash table resize + * @ht: the hash table. + * @new_size: update to this hash table size. + * + * Threads calling this API need to be registered RCU read-side threads. + */ +void cds_lfht_resize(struct cds_lfht *ht, unsigned long new_size); + +/* + * Note: cds_lfht_for_each are safe for element removal during + * iteration. + */ +#define cds_lfht_for_each(ht, iter, node) \ + for (cds_lfht_first(ht, iter), \ + node = cds_lfht_iter_get_node(iter); \ + node != NULL; \ + cds_lfht_next(ht, iter), \ + node = cds_lfht_iter_get_node(iter)) + +#define cds_lfht_for_each_duplicate(ht, hash, match, key, iter, node) \ + for (cds_lfht_lookup(ht, hash, match, key, iter), \ + node = cds_lfht_iter_get_node(iter); \ + node != NULL; \ + cds_lfht_next_duplicate(ht, match, key, iter), \ + node = cds_lfht_iter_get_node(iter)) + +#define cds_lfht_for_each_entry(ht, iter, pos, member) \ + for (cds_lfht_first(ht, iter), \ + pos = caa_container_of(cds_lfht_iter_get_node(iter), \ + typeof(*(pos)), member); \ + &(pos)->member != NULL; \ + cds_lfht_next(ht, iter), \ + pos = caa_container_of(cds_lfht_iter_get_node(iter), \ + typeof(*(pos)), member)) + +#define cds_lfht_for_each_entry_duplicate(ht, hash, match, key, \ + iter, pos, member) \ + for (cds_lfht_lookup(ht, hash, match, key, iter), \ + pos = caa_container_of(cds_lfht_iter_get_node(iter), \ + typeof(*(pos)), member); \ + &(pos)->member != NULL; \ + cds_lfht_next_duplicate(ht, match, key, iter), \ + pos = caa_container_of(cds_lfht_iter_get_node(iter), \ + typeof(*(pos)), member)) + +#ifdef __cplusplus +} +#endif + +#endif /* _URCU_RCULFHASH_H */ diff --git a/src/common/hashtable/urcu-flavor.h b/src/common/hashtable/urcu-flavor.h new file mode 100644 index 000000000..9af4d0e63 --- /dev/null +++ b/src/common/hashtable/urcu-flavor.h @@ -0,0 +1,65 @@ +#ifndef _URCU_FLAVOR_H +#define _URCU_FLAVOR_H + +/* + * urcu-flavor.h + * + * Userspace RCU header - rcu flavor declarations + * + * Copyright (c) 2011 Lai Jiangshan + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rcu_flavor_struct { + void (*read_lock)(void); + void (*read_unlock)(void); + void (*read_quiescent_state)(void); + void (*update_call_rcu)(struct rcu_head *head, + void (*func)(struct rcu_head *head)); + void (*update_synchronize_rcu)(void); + void (*update_defer_rcu)(void (*fct)(void *p), void *p); + + void (*thread_offline)(void); + void (*thread_online)(void); + void (*register_thread)(void); + void (*unregister_thread)(void); +}; + +#define DEFINE_RCU_FLAVOR(x) \ +const struct rcu_flavor_struct x = { \ + .read_lock = rcu_read_lock, \ + .read_unlock = rcu_read_unlock, \ + .read_quiescent_state = rcu_quiescent_state, \ + .update_call_rcu = call_rcu, \ + .update_synchronize_rcu = synchronize_rcu, \ + .update_defer_rcu = defer_rcu, \ + .thread_offline = rcu_thread_offline, \ + .thread_online = rcu_thread_online, \ + .register_thread = rcu_register_thread, \ + .unregister_thread = rcu_unregister_thread,\ +} + +extern const struct rcu_flavor_struct rcu_flavor; + +#ifdef __cplusplus +} +#endif + +#endif /* _URCU_FLAVOR_H */ diff --git a/src/common/hashtable/utils.c b/src/common/hashtable/utils.c new file mode 100644 index 000000000..0b3d53160 --- /dev/null +++ b/src/common/hashtable/utils.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) - Bob Jenkins, May 2006, Public Domain. + * Copyright (C) 2011 - David Goulet + * Copyright (C) 2011 - Mathieu Desnoyers + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() are + * externally useful functions. Routines to test the hash are included if + * SELF_TEST is defined. You can use this free for any purpose. It's in the + * public domain. It has no warranty. + * + * You probably want to use hashlittle(). hashlittle() and hashbig() hash byte + * arrays. hashlittle() is is faster than hashbig() on little-endian machines. + * Intel and AMD are little-endian machines. On second thought, you probably + * want hashlittle2(), which is identical to hashlittle() except it returns two + * 32-bit hashes for the price of one. You could implement hashbig2() if you + * wanted but I haven't bothered here. + * + * If you want to find a hash of, say, exactly 7 integers, do + * a = i1; b = i2; c = i3; + * mix(a,b,c); + * a += i4; b += i5; c += i6; + * mix(a,b,c); + * a += i7; + * final(a,b,c); + * then use c as the hash value. If you have a variable length array of + * 4-byte integers to hash, use hashword(). If you have a byte array (like + * a character string), use hashlittle(). If you have several byte arrays, or + * a mix of things, see the comments above hashlittle(). + * + * Why is this so big? I read 12 bytes at a time into 3 4-byte integers, then + * mix those integers. This is fast (you can do a lot more thorough mixing + * with 12*3 instructions on 3 integers than you can with 3 instructions on 1 + * byte), but shoehorning those bytes into integers efficiently is messy. + */ + +#include +#include /* attempt to define endianness */ +#include /* defines uint32_t etc */ +#include /* defines printf for tests */ +#include +#include /* attempt to define endianness */ +#include /* defines time_t for timings in the test */ +#include + +#include "utils.h" + +/* + * My best guess at if you are big-endian or little-endian. This may + * need adjustment. + */ +#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ + __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(i386) || defined(__i386__) || defined(__i486__) || \ + defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL)) +# define HASH_LITTLE_ENDIAN 1 +# define HASH_BIG_ENDIAN 0 +#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ + __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel)) +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 1 +#else +# define HASH_LITTLE_ENDIAN 0 +# define HASH_BIG_ENDIAN 0 +#endif + +#define hashsize(n) ((uint32_t)1<<(n)) +#define hashmask(n) (hashsize(n)-1) +#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) + +/* + * mix -- mix 3 32-bit values reversibly. + * + * This is reversible, so any information in (a,b,c) before mix() is + * still in (a,b,c) after mix(). + * + * If four pairs of (a,b,c) inputs are run through mix(), or through + * mix() in reverse, there are at least 32 bits of the output that + * are sometimes the same for one pair and different for another pair. + * This was tested for: + * * pairs that differed by one bit, by two bits, in any combination + * of top bits of (a,b,c), or in any combination of bottom bits of + * (a,b,c). + * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + * is commonly produced by subtraction) look like a single 1-bit + * difference. + * * the base values were pseudorandom, all zero but one bit set, or + * all zero plus a counter that starts at zero. + * + * Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that + * satisfy this are + * 4 6 8 16 19 4 + * 9 15 3 18 27 15 + * 14 9 3 7 17 3 + * Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing + * for "differ" defined as + with a one-bit base and a two-bit delta. I + * used http://burtleburtle.net/bob/hash/avalanche.html to choose + * the operations, constants, and arrangements of the variables. + * + * This does not achieve avalanche. There are input bits of (a,b,c) + * that fail to affect some output bits of (a,b,c), especially of a. The + * most thoroughly mixed value is c, but it doesn't really even achieve + * avalanche in c. + * + * This allows some parallelism. Read-after-writes are good at doubling + * the number of bits affected, so the goal of mixing pulls in the opposite + * direction as the goal of parallelism. I did what I could. Rotates + * seem to cost as much as shifts on every machine I could lay my hands + * on, and rotates are much kinder to the top and bottom bits, so I used + * rotates. + */ +#define mix(a,b,c) \ +{ \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + +/* + * final -- final mixing of 3 32-bit values (a,b,c) into c + * + * Pairs of (a,b,c) values differing in only a few bits will usually + * produce values of c that look totally different. This was tested for + * * pairs that differed by one bit, by two bits, in any combination + * of top bits of (a,b,c), or in any combination of bottom bits of + * (a,b,c). + * * "differ" is defined as +, -, ^, or ~^. For + and -, I transformed + * the output delta to a Gray code (a^(a>>1)) so a string of 1's (as + * is commonly produced by subtraction) look like a single 1-bit + * difference. + * * the base values were pseudorandom, all zero but one bit set, or + * all zero plus a counter that starts at zero. + * + * These constants passed: + * 14 11 25 16 4 14 24 + * 12 14 25 16 4 14 24 + * and these came close: + * 4 8 15 26 3 22 24 + * 10 8 15 26 3 22 24 + * 11 8 15 26 3 22 24 + */ +#define final(a,b,c) \ +{ \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + +/* + * k - the key, an array of uint32_t values + * length - the length of the key, in uint32_ts + * initval - the previous hash, or an arbitrary value + */ +static uint32_t __attribute__((unused)) hashword(const uint32_t *k, + size_t length, uint32_t initval) +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (((uint32_t) length) << 2) + initval; + + /*----------------------------------------- handle most of the key */ + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + /*----------------------------------- handle the last 3 uint32_t's */ + switch (length) { /* all the case statements fall through */ + case 3: c += k[2]; + case 2: b += k[1]; + case 1: a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + /*---------------------------------------------- report the result */ + return c; +} + + +/* + * hashword2() -- same as hashword(), but take two seeds and return two 32-bit + * values. pc and pb must both be nonnull, and *pc and *pb must both be + * initialized with seeds. If you pass in (*pb)==0, the output (*pc) will be + * the same as the return value from hashword(). + */ +static void __attribute__((unused)) hashword2(const uint32_t *k, size_t length, + uint32_t *pc, uint32_t *pb) +{ + uint32_t a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + ((uint32_t) (length << 2)) + *pc; + c += *pb; + + while (length > 3) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a, b, c); + length -= 3; + k += 3; + } + + switch (length) { + case 3 : + c += k[2]; + case 2 : + b += k[1]; + case 1 : + a += k[0]; + final(a, b, c); + case 0: /* case 0: nothing left to add */ + break; + } + + *pc = c; + *pb = b; +} + +/* + * hashlittle() -- hash a variable-length key into a 32-bit value + * k : the key (the unaligned variable-length array of bytes) + * length : the length of the key, counting by bytes + * initval : can be any 4-byte value + * Returns a 32-bit value. Every bit of the key affects every bit of + * the return value. Two keys differing by one or two bits will have + * totally different hash values. + * + * The best hash table sizes are powers of 2. There is no need to do + * mod a prime (mod is sooo slow!). If you need less than 32 bits, + * use a bitmask. For example, if you need only 10 bits, do + * h = (h & hashmask(10)); + * In which case, the hash table should have hashsize(10) elements. + * + * If you are hashing n strings (uint8_t **)k, do it like this: + * for (i=0, h=0; i 12) { + a += k[0]; + b += k[1]; + c += k[2]; + mix(a,b,c); + length -= 12; + k += 3; + } + + /* + * "k[2]&0xffffff" actually reads beyond the end of the string, but + * then masks off the part it's not allowed to read. Because the + * string is aligned, the masked-off tail is in the same word as the + * rest of the string. Every machine with memory protection I've seen + * does it on word boundaries, so is OK with this. But VALGRIND will + * still catch it and complain. The masking trick does make the hash + * noticably faster for short strings (like English words). + */ +#ifndef VALGRIND + + switch (length) { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; + case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; + case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=k[1]&0xffffff; a+=k[0]; break; + case 6 : b+=k[1]&0xffff; a+=k[0]; break; + case 5 : b+=k[1]&0xff; a+=k[0]; break; + case 4 : a+=k[0]; break; + case 3 : a+=k[0]&0xffffff; break; + case 2 : a+=k[0]&0xffff; break; + case 1 : a+=k[0]&0xff; break; + case 0 : return c; /* zero length strings require no mixing */ + } +#else /* make valgrind happy */ + const uint8_t *k8; + + k8 = (const uint8_t *)k; + switch (length) { + case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; + case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ + case 9 : c+=k8[8]; /* fall through */ + case 8 : b+=k[1]; a+=k[0]; break; + case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ + case 5 : b+=k8[4]; /* fall through */ + case 4 : a+=k[0]; break; + case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ + case 1 : a+=k8[0]; break; + case 0 : return c; + } +#endif /* !valgrind */ + } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { + const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */ + const uint8_t *k8; + + /*--------------- all but last block: aligned reads and different mixing */ + while (length > 12) { + a += k[0] + (((uint32_t)k[1])<<16); + b += k[2] + (((uint32_t)k[3])<<16); + c += k[4] + (((uint32_t)k[5])<<16); + mix(a,b,c); + length -= 12; + k += 6; + } + + k8 = (const uint8_t *)k; + switch (length) { + case 12: + c+=k[4]+(((uint32_t)k[5])<<16); + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 11: + c+=((uint32_t)k8[10])<<16; /* fall through */ + case 10: + c+=k[4]; + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 9: + c+=k8[8]; /* fall through */ + case 8: + b+=k[2]+(((uint32_t)k[3])<<16); + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 7: + b+=((uint32_t)k8[6])<<16; /* fall through */ + case 6: + b+=k[2]; + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 5: + b+=k8[4]; /* fall through */ + case 4: + a+=k[0]+(((uint32_t)k[1])<<16); + break; + case 3: + a+=((uint32_t)k8[2])<<16; /* fall through */ + case 2: + a+=k[0]; + break; + case 1: + a+=k8[0]; + break; + case 0: + return c; /* zero length requires no mixing */ + } + + } else { /* need to read the key one byte at a time */ + const uint8_t *k = (const uint8_t *)key; + + while (length > 12) { + a += k[0]; + a += ((uint32_t)k[1])<<8; + a += ((uint32_t)k[2])<<16; + a += ((uint32_t)k[3])<<24; + b += k[4]; + b += ((uint32_t)k[5])<<8; + b += ((uint32_t)k[6])<<16; + b += ((uint32_t)k[7])<<24; + c += k[8]; + c += ((uint32_t)k[9])<<8; + c += ((uint32_t)k[10])<<16; + c += ((uint32_t)k[11])<<24; + mix(a,b,c); + length -= 12; + k += 12; + } + + switch(length) { /* all the case statements fall through */ + case 12: c+=((uint32_t)k[11])<<24; + case 11: c+=((uint32_t)k[10])<<16; + case 10: c+=((uint32_t)k[9])<<8; + case 9: c+=k[8]; + case 8: b+=((uint32_t)k[7])<<24; + case 7: b+=((uint32_t)k[6])<<16; + case 6: b+=((uint32_t)k[5])<<8; + case 5: b+=k[4]; + case 4: a+=((uint32_t)k[3])<<24; + case 3: a+=((uint32_t)k[2])<<16; + case 2: a+=((uint32_t)k[1])<<8; + case 1: + a+=k[0]; + break; + case 0: + return c; + } + } + + final(a,b,c); + return c; +} + +#if (CAA_BITS_PER_LONG == 64) +/* + * Hash function for number value. + */ +unsigned long hash_key_ulong(void *_key, unsigned long seed) +{ + union { + uint64_t v64; + uint32_t v32[2]; + } v; + union { + uint64_t v64; + uint32_t v32[2]; + } key; + + v.v64 = (uint64_t) seed; + key.v64 = (uint64_t) _key; + hashword2(key.v32, 2, &v.v32[0], &v.v32[1]); + return v.v64; +} +#else +/* + * Hash function for number value. + */ +unsigned long hash_key_ulong(void *_key, unsigned long seed) +{ + uint32_t key = (uint32_t) _key; + + return hashword(&key, 1, seed); +} +#endif /* CAA_BITS_PER_LONG */ + +/* + * Hash function for string. + */ +unsigned long hash_key_str(void *key, unsigned long seed) +{ + return hashlittle(key, strlen((char *) key), seed); +} + +/* + * Hash function compare for number value. + */ +int hash_match_key_ulong(void *key1, void *key2) +{ + if (key1 == key2) { + return 1; + } + + return 0; +} + +/* + * Hash compare function for string. + */ +int hash_match_key_str(void *key1, void *key2) +{ + if (strcmp(key1, key2) == 0) { + return 1; + } + + return 0; +} diff --git a/src/common/hashtable/utils.h b/src/common/hashtable/utils.h new file mode 100644 index 000000000..86b340f72 --- /dev/null +++ b/src/common/hashtable/utils.h @@ -0,0 +1,28 @@ +/* + * 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_HT_UTILS_H +#define _LTT_HT_UTILS_H + +#include + +unsigned long hash_key_ulong(void *_key, unsigned long seed); +unsigned long hash_key_str(void *key, unsigned long seed); +int hash_match_key_ulong(void *key1, void *key2); +int hash_match_key_str(void *key1, void *key2); + +#endif /* _LTT_HT_UTILS_H */ diff --git a/src/common/kernel-consumer/Makefile.am b/src/common/kernel-consumer/Makefile.am new file mode 100644 index 000000000..b914bf16c --- /dev/null +++ b/src/common/kernel-consumer/Makefile.am @@ -0,0 +1,8 @@ +# Kernel consumer library +noinst_LTLIBRARIES = libkernel-consumer.la + +libkernel_consumer_la_SOURCES = kernel-consumer.c + +libkernel_consumer_la_LIBADD = \ + $(top_builddir)/src/common/kernel-ctl/libkernel-ctl.la + diff --git a/src/common/kernel-consumer/kernel-consumer.c b/src/common/kernel-consumer/kernel-consumer.c new file mode 100644 index 000000000..87436371f --- /dev/null +++ b/src/common/kernel-consumer/kernel-consumer.c @@ -0,0 +1,446 @@ +/* + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "kernel-consumer.h" + +extern struct lttng_consumer_global_data consumer_data; +extern int consumer_poll_timeout; +extern volatile int consumer_quit; + +/* + * Mmap the ring buffer, read it and write the data to the tracefile. + * + * Returns the number of bytes written + */ +int lttng_kconsumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + unsigned long mmap_offset; + long ret = 0; + off_t orig_offset = stream->out_fd_offset; + int fd = stream->wait_fd; + int outfd = stream->out_fd; + + /* get the offset inside the fd to mmap */ + ret = kernctl_get_mmap_read_offset(fd, &mmap_offset); + if (ret != 0) { + ret = -errno; + perror("kernctl_get_mmap_read_offset"); + goto end; + } + + while (len > 0) { + ret = write(outfd, stream->mmap_base + mmap_offset, len); + if (ret >= len) { + len = 0; + } else if (ret < 0) { + ret = -errno; + perror("Error in file write"); + goto end; + } + /* This won't block, but will start writeout asynchronously */ + sync_file_range(outfd, stream->out_fd_offset, ret, + SYNC_FILE_RANGE_WRITE); + stream->out_fd_offset += ret; + } + + lttng_consumer_sync_trace_file(stream, orig_offset); + + goto end; + +end: + return ret; +} + +/* + * Splice the data from the ring buffer to the tracefile. + * + * Returns the number of bytes spliced. + */ +int lttng_kconsumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + long ret = 0; + loff_t offset = 0; + off_t orig_offset = stream->out_fd_offset; + int fd = stream->wait_fd; + int outfd = stream->out_fd; + + while (len > 0) { + DBG("splice chan to pipe offset %lu (fd : %d)", + (unsigned long)offset, fd); + ret = splice(fd, &offset, ctx->consumer_thread_pipe[1], NULL, len, + SPLICE_F_MOVE | SPLICE_F_MORE); + DBG("splice chan to pipe ret %ld", ret); + if (ret < 0) { + ret = errno; + perror("Error in relay splice"); + goto splice_error; + } + + ret = splice(ctx->consumer_thread_pipe[0], NULL, outfd, NULL, ret, + SPLICE_F_MOVE | SPLICE_F_MORE); + DBG("splice pipe to file %ld", ret); + if (ret < 0) { + ret = errno; + perror("Error in file splice"); + goto splice_error; + } + len -= ret; + /* This won't block, but will start writeout asynchronously */ + sync_file_range(outfd, stream->out_fd_offset, ret, + SYNC_FILE_RANGE_WRITE); + stream->out_fd_offset += ret; + } + lttng_consumer_sync_trace_file(stream, orig_offset); + + goto end; + +splice_error: + /* send the appropriate error description to sessiond */ + switch(ret) { + case EBADF: + lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EBADF); + break; + case EINVAL: + lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EINVAL); + break; + case ENOMEM: + lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ENOMEM); + break; + case ESPIPE: + lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ESPIPE); + break; + } + +end: + return ret; +} + +/* + * Take a snapshot for a specific fd + * + * Returns 0 on success, < 0 on error + */ +int lttng_kconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream) +{ + int ret = 0; + int infd = stream->wait_fd; + + ret = kernctl_snapshot(infd); + if (ret != 0) { + ret = errno; + perror("Getting sub-buffer snapshot."); + } + + return ret; +} + +/* + * Get the produced position + * + * Returns 0 on success, < 0 on error + */ +int lttng_kconsumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos) +{ + int ret; + int infd = stream->wait_fd; + + ret = kernctl_snapshot_get_produced(infd, pos); + if (ret != 0) { + ret = errno; + perror("kernctl_snapshot_get_produced"); + } + + return ret; +} + +int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll) +{ + ssize_t ret; + struct lttcomm_consumer_msg msg; + + ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_CMD); + return ret; + } + if (msg.cmd_type == LTTNG_CONSUMER_STOP) { + return -ENOENT; + } + + switch (msg.cmd_type) { + case LTTNG_CONSUMER_ADD_CHANNEL: + { + struct lttng_consumer_channel *new_channel; + + DBG("consumer_add_channel %d", msg.u.channel.channel_key); + new_channel = consumer_allocate_channel(msg.u.channel.channel_key, + -1, -1, + msg.u.channel.mmap_len, + msg.u.channel.max_sb_size); + if (new_channel == NULL) { + lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); + goto end_nosignal; + } + if (ctx->on_recv_channel != NULL) { + ret = ctx->on_recv_channel(new_channel); + if (ret == 0) { + consumer_add_channel(new_channel); + } else if (ret < 0) { + goto end_nosignal; + } + } else { + consumer_add_channel(new_channel); + } + goto end_nosignal; + } + case LTTNG_CONSUMER_ADD_STREAM: + { + struct lttng_consumer_stream *new_stream; + int fd; + + /* block */ + if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { + return -EINTR; + } + ret = lttcomm_recv_fds_unix_sock(sock, &fd, 1); + if (ret != sizeof(fd)) { + lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); + return ret; + } + + DBG("consumer_add_stream %s (%d)", msg.u.stream.path_name, + fd); + new_stream = consumer_allocate_stream(msg.u.stream.channel_key, + msg.u.stream.stream_key, + fd, fd, + msg.u.stream.state, + msg.u.stream.mmap_len, + msg.u.stream.output, + msg.u.stream.path_name, + msg.u.stream.uid, + msg.u.stream.gid); + if (new_stream == NULL) { + lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); + goto end; + } + if (ctx->on_recv_stream != NULL) { + ret = ctx->on_recv_stream(new_stream); + if (ret == 0) { + consumer_add_stream(new_stream); + } else if (ret < 0) { + goto end; + } + } else { + consumer_add_stream(new_stream); + } + break; + } + case LTTNG_CONSUMER_UPDATE_STREAM: + { + if (ctx->on_update_stream != NULL) { + ret = ctx->on_update_stream(msg.u.stream.stream_key, msg.u.stream.state); + if (ret == 0) { + consumer_change_stream_state(msg.u.stream.stream_key, msg.u.stream.state); + } else if (ret < 0) { + goto end; + } + } else { + consumer_change_stream_state(msg.u.stream.stream_key, + msg.u.stream.state); + } + break; + } + default: + break; + } +end: + /* signal the poll thread */ + ret = write(ctx->consumer_poll_pipe[1], "4", 1); + if (ret < 0) { + perror("write consumer poll"); + } +end_nosignal: + return 0; +} + +/* + * Consume data on a file descriptor and write it on a trace file. + */ +int lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx) +{ + unsigned long len; + int err; + long ret = 0; + int infd = stream->wait_fd; + + DBG("In read_subbuffer (infd : %d)", infd); + /* Get the next subbuffer */ + err = kernctl_get_next_subbuf(infd); + if (err != 0) { + ret = errno; + /* + * This is a debug message even for single-threaded consumer, + * because poll() have more relaxed criterions than get subbuf, + * so get_subbuf may fail for short race windows where poll() + * would issue wakeups. + */ + DBG("Reserving sub buffer failed (everything is normal, " + "it is due to concurrency)"); + goto end; + } + + switch (stream->output) { + case LTTNG_EVENT_SPLICE: + /* read the whole subbuffer */ + err = kernctl_get_padded_subbuf_size(infd, &len); + if (err != 0) { + ret = errno; + perror("Getting sub-buffer len failed."); + goto end; + } + + /* splice the subbuffer to the tracefile */ + ret = lttng_consumer_on_read_subbuffer_splice(ctx, stream, len); + if (ret < 0) { + /* + * display the error but continue processing to try + * to release the subbuffer + */ + ERR("Error splicing to tracefile"); + } + break; + case LTTNG_EVENT_MMAP: + /* read the used subbuffer size */ + err = kernctl_get_padded_subbuf_size(infd, &len); + if (err != 0) { + ret = errno; + perror("Getting sub-buffer len failed."); + goto end; + } + /* write the subbuffer to the tracefile */ + ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len); + if (ret < 0) { + /* + * display the error but continue processing to try + * to release the subbuffer + */ + ERR("Error writing to tracefile"); + } + break; + default: + ERR("Unknown output method"); + ret = -1; + } + + err = kernctl_put_next_subbuf(infd); + if (err != 0) { + ret = errno; + if (errno == EFAULT) { + perror("Error in unreserving sub buffer\n"); + } else if (errno == EIO) { + /* Should never happen with newer LTTng versions */ + perror("Reader has been pushed by the writer, last sub-buffer corrupted."); + } + goto end; + } + +end: + return ret; +} + +int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream) +{ + int ret; + + /* Opening the tracefile in write mode */ + if (stream->path_name != NULL) { + ret = run_as_open(stream->path_name, + O_WRONLY|O_CREAT|O_TRUNC, + S_IRWXU|S_IRWXG|S_IRWXO, + stream->uid, stream->gid); + if (ret < 0) { + ERR("Opening %s", stream->path_name); + perror("open"); + goto error; + } + stream->out_fd = ret; + } + + if (stream->output == LTTNG_EVENT_MMAP) { + /* get the len of the mmap region */ + unsigned long mmap_len; + + ret = kernctl_get_mmap_len(stream->wait_fd, &mmap_len); + if (ret != 0) { + ret = errno; + perror("kernctl_get_mmap_len"); + goto error_close_fd; + } + stream->mmap_len = (size_t) mmap_len; + + stream->mmap_base = mmap(NULL, stream->mmap_len, + PROT_READ, MAP_PRIVATE, stream->wait_fd, 0); + if (stream->mmap_base == MAP_FAILED) { + perror("Error mmaping"); + ret = -1; + goto error_close_fd; + } + } + + /* we return 0 to let the library handle the FD internally */ + return 0; + +error_close_fd: + { + int err; + + err = close(stream->out_fd); + assert(!err); + } +error: + return ret; +} + diff --git a/src/common/kernel-consumer/kernel-consumer.h b/src/common/kernel-consumer/kernel-consumer.h new file mode 100644 index 000000000..6e820044b --- /dev/null +++ b/src/common/kernel-consumer/kernel-consumer.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * Copyright (C) 2011 - 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_KCONSUMER_H +#define _LTTNG_KCONSUMER_H + +#include + +/* + * Mmap the ring buffer, read it and write the data to the tracefile. + * + * Returns the number of bytes written. + */ +extern int lttng_kconsumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); + +/* + * Splice the data from the ring buffer to the tracefile. + * + * Returns the number of bytes spliced. + */ +extern int lttng_kconsumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); + +/* + * Take a snapshot for a specific fd + * + * Returns 0 on success, < 0 on error + */ +int lttng_kconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream); + +/* + * Get the produced position + * + * Returns 0 on success, < 0 on error + */ +int lttng_kconsumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos); + +int lttng_kconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll); + + +int lttng_kconsumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx); +int lttng_kconsumer_on_recv_stream(struct lttng_consumer_stream *stream); + +#endif /* _LTTNG_KCONSUMER_H */ diff --git a/src/common/kernel-ctl/Makefile.am b/src/common/kernel-ctl/Makefile.am new file mode 100644 index 000000000..1c71c5ce9 --- /dev/null +++ b/src/common/kernel-ctl/Makefile.am @@ -0,0 +1,5 @@ +AM_CPPFLAGS = -I$(top_srcdir)/include + +noinst_LTLIBRARIES = libkernel-ctl.la + +libkernel_ctl_la_SOURCES = kernel-ctl.c kernel-ioctl.h diff --git a/src/common/kernel-ctl/kernel-ctl.c b/src/common/kernel-ctl/kernel-ctl.c new file mode 100644 index 000000000..973ea7993 --- /dev/null +++ b/src/common/kernel-ctl/kernel-ctl.c @@ -0,0 +1,189 @@ +/* + * 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. + */ + +#include + +#include "kernel-ctl.h" +#include "kernel-ioctl.h" + +int kernctl_create_session(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_SESSION); +} + +/* open the metadata global channel */ +int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops) +{ + return ioctl(fd, LTTNG_KERNEL_METADATA, chops); +} + +int kernctl_create_channel(int fd, struct lttng_channel_attr *chops) +{ + return ioctl(fd, LTTNG_KERNEL_CHANNEL, chops); +} + +int kernctl_create_stream(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_STREAM); +} + +int kernctl_create_event(int fd, struct lttng_kernel_event *ev) +{ + return ioctl(fd, LTTNG_KERNEL_EVENT, ev); +} + +int kernctl_add_context(int fd, struct lttng_kernel_context *ctx) +{ + return ioctl(fd, LTTNG_KERNEL_CONTEXT, ctx); +} + + +/* Enable event, channel and session ioctl */ +int kernctl_enable(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_ENABLE); +} + +/* Disable event, channel and session ioctl */ +int kernctl_disable(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_DISABLE); +} + +int kernctl_start_session(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_SESSION_START); +} + +int kernctl_stop_session(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_SESSION_STOP); +} + + +int kernctl_tracepoint_list(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_TRACEPOINT_LIST); +} + +int kernctl_tracer_version(int fd, struct lttng_kernel_tracer_version *v) +{ + return ioctl(fd, LTTNG_KERNEL_TRACER_VERSION, v); +} + +int kernctl_wait_quiescent(int fd) +{ + return ioctl(fd, LTTNG_KERNEL_WAIT_QUIESCENT); +} + +int kernctl_calibrate(int fd, struct lttng_kernel_calibrate *calibrate) +{ + return ioctl(fd, LTTNG_KERNEL_CALIBRATE, calibrate); +} + + +int kernctl_buffer_flush(int fd) +{ + return ioctl(fd, RING_BUFFER_FLUSH); +} + + +/* Buffer operations */ + +/* For mmap mode, readable without "get" operation */ + +/* returns the length to mmap. */ +int kernctl_get_mmap_len(int fd, unsigned long *len) +{ + return ioctl(fd, RING_BUFFER_GET_MMAP_LEN, len); +} + +/* returns the maximum size for sub-buffers. */ +int kernctl_get_max_subbuf_size(int fd, unsigned long *len) +{ + return ioctl(fd, RING_BUFFER_GET_MAX_SUBBUF_SIZE, len); +} + +/* + * For mmap mode, operate on the current packet (between get/put or + * get_next/put_next). + */ + +/* returns the offset of the subbuffer belonging to the mmap reader. */ +int kernctl_get_mmap_read_offset(int fd, unsigned long *off) +{ + return ioctl(fd, RING_BUFFER_GET_MMAP_READ_OFFSET, off); +} + +/* returns the size of the current sub-buffer, without padding (for mmap). */ +int kernctl_get_subbuf_size(int fd, unsigned long *len) +{ + return ioctl(fd, RING_BUFFER_GET_SUBBUF_SIZE, len); +} + +/* returns the size of the current sub-buffer, without padding (for mmap). */ +int kernctl_get_padded_subbuf_size(int fd, unsigned long *len) +{ + return ioctl(fd, RING_BUFFER_GET_PADDED_SUBBUF_SIZE, len); +} + +/* Get exclusive read access to the next sub-buffer that can be read. */ +int kernctl_get_next_subbuf(int fd) +{ + return ioctl(fd, RING_BUFFER_GET_NEXT_SUBBUF); +} + + +/* Release exclusive sub-buffer access, move consumer forward. */ +int kernctl_put_next_subbuf(int fd) +{ + return ioctl(fd, RING_BUFFER_PUT_NEXT_SUBBUF); +} + +/* snapshot */ + +/* Get a snapshot of the current ring buffer producer and consumer positions */ +int kernctl_snapshot(int fd) +{ + return ioctl(fd, RING_BUFFER_SNAPSHOT); +} + +/* Get the consumer position (iteration start) */ +int kernctl_snapshot_get_consumed(int fd, unsigned long *pos) +{ + return ioctl(fd, RING_BUFFER_SNAPSHOT_GET_CONSUMED, pos); +} + +/* Get the producer position (iteration end) */ +int kernctl_snapshot_get_produced(int fd, unsigned long *pos) +{ + return ioctl(fd, RING_BUFFER_SNAPSHOT_GET_PRODUCED, pos); +} + +/* Get exclusive read access to the specified sub-buffer position */ +int kernctl_get_subbuf(int fd, unsigned long *len) +{ + return ioctl(fd, RING_BUFFER_GET_SUBBUF, len); +} + +/* Release exclusive sub-buffer access */ +int kernctl_put_subbuf(int fd) +{ + return ioctl(fd, RING_BUFFER_PUT_SUBBUF); +} diff --git a/src/common/kernel-ctl/kernel-ctl.h b/src/common/kernel-ctl/kernel-ctl.h new file mode 100644 index 000000000..2bfb2b048 --- /dev/null +++ b/src/common/kernel-ctl/kernel-ctl.h @@ -0,0 +1,70 @@ +/* + * 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_KERNEL_CTL_H +#define _LTTNG_KERNEL_CTL_H + +#include +#include + +int kernctl_create_session(int fd); +int kernctl_open_metadata(int fd, struct lttng_channel_attr *chops); +int kernctl_create_channel(int fd, struct lttng_channel_attr *chops); +int kernctl_create_stream(int fd); +int kernctl_create_event(int fd, struct lttng_kernel_event *ev); +int kernctl_add_context(int fd, struct lttng_kernel_context *ctx); + +int kernctl_enable(int fd); +int kernctl_disable(int fd); +int kernctl_start_session(int fd); +int kernctl_stop_session(int fd); + +int kernctl_tracepoint_list(int fd); +int kernctl_tracer_version(int fd, struct lttng_kernel_tracer_version *v); +int kernctl_wait_quiescent(int fd); +int kernctl_calibrate(int fd, struct lttng_kernel_calibrate *calibrate); + + +/* Buffer operations */ + +/* For mmap mode, readable without "get" operation */ +int kernctl_get_mmap_len(int fd, unsigned long *len); +int kernctl_get_max_subbuf_size(int fd, unsigned long *len); + +/* + * For mmap mode, operate on the current packet (between get/put or + * get_next/put_next). + */ +int kernctl_get_mmap_read_offset(int fd, unsigned long *len); +int kernctl_get_subbuf_size(int fd, unsigned long *len); +int kernctl_get_padded_subbuf_size(int fd, unsigned long *len); + +int kernctl_get_next_subbuf(int fd); +int kernctl_put_next_subbuf(int fd); + +/* snapshot */ +int kernctl_snapshot(int fd); +int kernctl_snapshot_get_consumed(int fd, unsigned long *pos); +int kernctl_snapshot_get_produced(int fd, unsigned long *pos); +int kernctl_get_subbuf(int fd, unsigned long *pos); +int kernctl_put_subbuf(int fd); + +int kernctl_buffer_flush(int fd); + +#endif /* _LTTNG_KERNEL_CTL_H */ diff --git a/src/common/kernel-ctl/kernel-ioctl.h b/src/common/kernel-ctl/kernel-ioctl.h new file mode 100644 index 000000000..408535d43 --- /dev/null +++ b/src/common/kernel-ctl/kernel-ioctl.h @@ -0,0 +1,81 @@ +/* + * 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 _LTT_KERNEL_IOCTL_H +#define _LTT_KERNEL_IOCTL_H + +/* Get a snapshot of the current ring buffer producer and consumer positions */ +#define RING_BUFFER_SNAPSHOT _IO(0xF6, 0x00) +/* Get the consumer position (iteration start) */ +#define RING_BUFFER_SNAPSHOT_GET_CONSUMED _IOR(0xF6, 0x01, unsigned long) +/* Get the producer position (iteration end) */ +#define RING_BUFFER_SNAPSHOT_GET_PRODUCED _IOR(0xF6, 0x02, unsigned long) +/* Get exclusive read access to the specified sub-buffer position */ +#define RING_BUFFER_GET_SUBBUF _IOW(0xF6, 0x03, unsigned long) +/* Release exclusive sub-buffer access */ +#define RING_BUFFER_PUT_SUBBUF _IO(0xF6, 0x04) + +/* Get exclusive read access to the next sub-buffer that can be read. */ +#define RING_BUFFER_GET_NEXT_SUBBUF _IO(0xF6, 0x05) +/* Release exclusive sub-buffer access, move consumer forward. */ +#define RING_BUFFER_PUT_NEXT_SUBBUF _IO(0xF6, 0x06) +/* returns the size of the current sub-buffer, without padding (for mmap). */ +#define RING_BUFFER_GET_SUBBUF_SIZE _IOR(0xF6, 0x07, unsigned long) +/* returns the size of the current sub-buffer, with padding (for splice). */ +#define RING_BUFFER_GET_PADDED_SUBBUF_SIZE _IOR(0xF6, 0x08, unsigned long) +/* returns the maximum size for sub-buffers. */ +#define RING_BUFFER_GET_MAX_SUBBUF_SIZE _IOR(0xF6, 0x09, unsigned long) +/* returns the length to mmap. */ +#define RING_BUFFER_GET_MMAP_LEN _IOR(0xF6, 0x0A, unsigned long) +/* returns the offset of the subbuffer belonging to the mmap reader. */ +#define RING_BUFFER_GET_MMAP_READ_OFFSET _IOR(0xF6, 0x0B, unsigned long) +/* flush the current sub-buffer */ +#define RING_BUFFER_FLUSH _IO(0xF6, 0x0C) + +/* LTTng file descriptor ioctl */ +#define LTTNG_KERNEL_SESSION _IO(0xF6, 0x40) +#define LTTNG_KERNEL_TRACER_VERSION \ + _IOR(0xF6, 0x41, struct lttng_kernel_tracer_version) +#define LTTNG_KERNEL_TRACEPOINT_LIST _IO(0xF6, 0x42) +#define LTTNG_KERNEL_WAIT_QUIESCENT _IO(0xF6, 0x43) +#define LTTNG_KERNEL_CALIBRATE \ + _IOWR(0xF6, 0x44, struct lttng_kernel_calibrate) + +/* Session FD ioctl */ +#define LTTNG_KERNEL_METADATA \ + _IOW(0xF6, 0x50, struct lttng_channel_attr) +#define LTTNG_KERNEL_CHANNEL \ + _IOW(0xF6, 0x51, struct lttng_channel_attr) +#define LTTNG_KERNEL_SESSION_START _IO(0xF6, 0x52) +#define LTTNG_KERNEL_SESSION_STOP _IO(0xF6, 0x53) + +/* Channel FD ioctl */ +#define LTTNG_KERNEL_STREAM _IO(0xF6, 0x60) +#define LTTNG_KERNEL_EVENT \ + _IOW(0xF6, 0x61, struct lttng_kernel_event) + +/* Event and Channel FD ioctl */ +#define LTTNG_KERNEL_CONTEXT \ + _IOW(0xF6, 0x70, struct lttng_kernel_context) + +/* Event, Channel and Session ioctl */ +#define LTTNG_KERNEL_ENABLE _IO(0xF6, 0x80) +#define LTTNG_KERNEL_DISABLE _IO(0xF6, 0x81) + +#endif /* _LTT_KERNEL_IOCTL_H */ diff --git a/src/common/lttng-kernel.h b/src/common/lttng-kernel.h new file mode 100644 index 000000000..e8bc62626 --- /dev/null +++ b/src/common/lttng-kernel.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * Mathieu Desnoyers + * 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 _LTTNG_KERNEL_H +#define _LTTNG_KERNEL_H + +#include + +#define LTTNG_SYM_NAME_LEN 256 + +/* + * LTTng DebugFS ABI structures. + * + * This is the kernel ABI copied from lttng-modules tree. + */ + +enum lttng_kernel_instrumentation { + LTTNG_KERNEL_ALL = -1, /* Used within lttng-tools */ + LTTNG_KERNEL_TRACEPOINT = 0, + LTTNG_KERNEL_KPROBE = 1, + LTTNG_KERNEL_FUNCTION = 2, + LTTNG_KERNEL_KRETPROBE = 3, + LTTNG_KERNEL_NOOP = 4, /* not hooked */ + LTTNG_KERNEL_SYSCALL = 5, +}; + +enum lttng_kernel_context_type { + LTTNG_KERNEL_CONTEXT_PID = 0, + LTTNG_KERNEL_CONTEXT_PERF_COUNTER = 1, + LTTNG_KERNEL_CONTEXT_COMM = 2, + LTTNG_KERNEL_CONTEXT_PRIO = 3, + LTTNG_KERNEL_CONTEXT_NICE = 4, + LTTNG_KERNEL_CONTEXT_VPID = 5, + LTTNG_KERNEL_CONTEXT_TID = 6, + LTTNG_KERNEL_CONTEXT_VTID = 7, + LTTNG_KERNEL_CONTEXT_PPID = 8, + LTTNG_KERNEL_CONTEXT_VPPID = 9, +}; + +/* Perf counter attributes */ +struct lttng_kernel_perf_counter_ctx { + uint32_t type; + uint64_t config; + char name[LTTNG_SYM_NAME_LEN]; +}; + +/* Event/Channel context */ +struct lttng_kernel_context { + enum lttng_kernel_context_type ctx; + union { + struct lttng_kernel_perf_counter_ctx perf_counter; + } u; +}; + +struct lttng_kernel_kretprobe { + uint64_t addr; + + uint64_t offset; + char symbol_name[LTTNG_SYM_NAME_LEN]; +}; + +/* + * Either addr is used, or symbol_name and offset. + */ +struct lttng_kernel_kprobe { + uint64_t addr; + + uint64_t offset; + char symbol_name[LTTNG_SYM_NAME_LEN]; +}; + +/* Function tracer */ +struct lttng_kernel_function { + char symbol_name[LTTNG_SYM_NAME_LEN]; +}; + +struct lttng_kernel_event { + char name[LTTNG_SYM_NAME_LEN]; + enum lttng_kernel_instrumentation instrumentation; + /* Per instrumentation type configuration */ + union { + struct lttng_kernel_kretprobe kretprobe; + struct lttng_kernel_kprobe kprobe; + struct lttng_kernel_function ftrace; + } u; +}; + +struct lttng_kernel_tracer_version { + uint32_t version; + uint32_t patchlevel; + uint32_t sublevel; +}; + +enum lttng_kernel_calibrate_type { + LTTNG_KERNEL_CALIBRATE_KRETPROBE, +}; + +struct lttng_kernel_calibrate { + enum lttng_kernel_calibrate_type type; /* type (input) */ +}; + +#endif /* _LTTNG_KERNEL_H */ diff --git a/src/common/lttng-share.h b/src/common/lttng-share.h new file mode 100644 index 000000000..e859ead91 --- /dev/null +++ b/src/common/lttng-share.h @@ -0,0 +1,98 @@ +/* + * 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 _LTTNG_SHARE_H +#define _LTTNG_SHARE_H + +#include + +/* Default size of a hash table */ +#define DEFAULT_HT_SIZE 4 + +/* Default channel attributes */ +#define DEFAULT_CHANNEL_NAME "channel0" +#define DEFAULT_CHANNEL_OVERWRITE 0 /* usec */ +/* DEFAULT_CHANNEL_SUBBUF_SIZE must always be a power of 2 */ +#define DEFAULT_CHANNEL_SUBBUF_SIZE 4096 /* bytes */ +/* DEFAULT_CHANNEL_SUBBUF_NUM must always be a power of 2 */ +#define DEFAULT_CHANNEL_SUBBUF_NUM 8 +#define DEFAULT_CHANNEL_SWITCH_TIMER 0 /* usec */ +#define DEFAULT_CHANNEL_READ_TIMER 200 /* usec */ +#define DEFAULT_CHANNEL_OUTPUT LTTNG_EVENT_MMAP + +#define DEFAULT_METADATA_SUBBUF_SIZE 4096 +#define DEFAULT_METADATA_SUBBUF_NUM 2 + +/* Kernel has different defaults */ + +/* DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE must always be a power of 2 */ +#define DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE 262144 /* bytes */ +/* DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM must always be a power of 2 */ +#define DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM 4 +/* See lttng-kernel.h enum lttng_kernel_output for channel output */ +#define DEFAULT_KERNEL_CHANNEL_OUTPUT LTTNG_EVENT_SPLICE + +/* User space defaults */ + +/* Must be a power of 2 */ +#define DEFAULT_UST_CHANNEL_SUBBUF_SIZE 4096 /* bytes */ +/* Must be a power of 2 */ +#define DEFAULT_UST_CHANNEL_SUBBUF_NUM 4 +/* See lttng-ust.h enum lttng_ust_output */ +#define DEFAULT_UST_CHANNEL_OUTPUT LTTNG_EVENT_MMAP + +/* + * Default timeout value for the sem_timedwait() call. Blocking forever is not + * wanted so a timeout is used to control the data flow and not freeze the + * session daemon. + */ +#define DEFAULT_SEM_WAIT_TIMEOUT 30 /* in seconds */ + +/* + * Takes a pointer x and transform it so we can use it to access members + * without a function call. Here an example: + * + * #define GET_SIZE(x) LTTNG_REF(x)->size + * + * struct { int size; } s; + * + * printf("size : %d\n", GET_SIZE(&s)); + * + * For this example we can't use something like this for compatibility purpose + * since this will fail: + * + * #define GET_SIZE(x) x->size; + * + * This is mostly use for the compatibility layer of lttng-tools. See + * poll/epoll for a good example. Since x can be on the stack or allocated + * memory using malloc(), we must use generic accessors for compat in order to + * *not* use a function to access members and not the variable name. + */ +#define LTTNG_REF(x) ((typeof(*x) *)(x)) + +/* + * Memory allocation zeroed + */ +#define zmalloc(x) calloc(1, x) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(array) (sizeof(array) / (sizeof((array)[0]))) +#endif + + +#endif /* _LTTNG_SHARE_H */ diff --git a/src/common/lttngerr.h b/src/common/lttngerr.h new file mode 100644 index 000000000..b14f23218 --- /dev/null +++ b/src/common/lttngerr.h @@ -0,0 +1,88 @@ +/* + * 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 _LTTNGERR_H +#define _LTTNGERR_H + +#include +#include + +/* Stringify the expansion of a define */ +#define XSTR(d) STR(d) +#define STR(s) #s + +extern int opt_quiet; +extern int opt_verbose; + +#define PRINT_ERR 0x1 +#define PRINT_WARN 0x2 +#define PRINT_BUG 0x3 +#define PRINT_MSG 0x4 +#define PRINT_DBG 0x10 +#define PRINT_DBG2 0x20 +#define PRINT_DBG3 0x30 + +/* + * Macro for printing message depending on command line option and verbosity. + */ +#define __lttng_print(type, fmt, args...) \ + do { \ + if (opt_quiet == 0) { \ + if (type == PRINT_MSG) { \ + fprintf(stdout, fmt, ## args); \ + } else if (((type & PRINT_DBG) && opt_verbose == 1) || \ + ((type & (PRINT_DBG | PRINT_DBG2)) && \ + opt_verbose == 2) || \ + ((type & (PRINT_DBG | PRINT_DBG2 | PRINT_DBG3)) && \ + opt_verbose == 3)) { \ + fprintf(stderr, fmt, ## args); \ + } else if (type & (PRINT_ERR | PRINT_WARN | PRINT_BUG)) { \ + fprintf(stderr, fmt, ## args); \ + } \ + } \ + } while (0); + +#define MSG(fmt, args...) \ + __lttng_print(PRINT_MSG, fmt "\n", ## args) +#define ERR(fmt, args...) \ + __lttng_print(PRINT_ERR, "Error: " fmt "\n", ## args) +#define WARN(fmt, args...) \ + __lttng_print(PRINT_WARN, "Warning: " fmt "\n", ## args) +#define BUG(fmt, args...) \ + __lttng_print(PRINT_BUG, "BUG: " fmt "\n", ## args) + +/* Three level of debug. Use -v, -vv or -vvv for the levels */ +#define DBG(fmt, args...) __lttng_print(PRINT_DBG, "DEBUG1: " fmt \ + " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) +#define DBG2(fmt, args...) __lttng_print(PRINT_DBG2, "DEBUG2: " fmt \ + " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) +#define DBG3(fmt, args...) __lttng_print(PRINT_DBG3, "DEBUG3: " fmt \ + " [in %s() at " __FILE__ ":" XSTR(__LINE__) "]\n", ## args, __func__) + +#define _PERROR(fmt, args...) \ + __lttng_print(PRINT_ERR, "perror " fmt "\n", ## args) + +#define PERROR(call, args...) \ + do { \ + char *buf; \ + char tmp[200]; \ + buf = strerror_r(errno, tmp, sizeof(tmp)); \ + _PERROR(call ": %s", ## args, buf); \ + } while(0); + +#endif /* _LTTNGERR_H */ diff --git a/src/common/runas.c b/src/common/runas.c new file mode 100644 index 000000000..cc5034126 --- /dev/null +++ b/src/common/runas.c @@ -0,0 +1,313 @@ +/* + * 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 "runas.h" + +#define RUNAS_CHILD_STACK_SIZE 10485760 + +struct run_as_data { + int (*cmd)(void *data); + void *data; + uid_t uid; + gid_t gid; + int retval_pipe; +}; + +struct run_as_mkdir_data { + const char *path; + mode_t mode; +}; + +struct run_as_open_data { + const char *path; + int flags; + mode_t mode; +}; + +/* + * Create recursively directory using the FULL path. + */ +static +int _mkdir_recursive(void *_data) +{ + struct run_as_mkdir_data *data = _data; + const char *path; + char *p, tmp[PATH_MAX]; + struct stat statbuf; + mode_t mode; + size_t len; + int ret; + + path = data->path; + mode = data->mode; + + 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; + } + + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + ret = stat(tmp, &statbuf); + if (ret < 0) { + ret = mkdir(tmp, mode); + if (ret < 0) { + if (!(errno == EEXIST)) { + PERROR("mkdir recursive"); + ret = -errno; + goto error; + } + } + } + *p = '/'; + } + } + + ret = mkdir(tmp, mode); + if (ret < 0) { + if (!(errno == EEXIST)) { + PERROR("mkdir recursive last piece"); + ret = -errno; + } else { + ret = 0; + } + } + +error: + return ret; +} + +static +int _mkdir(void *_data) +{ + struct run_as_mkdir_data *data = _data; + return mkdir(data->path, data->mode); +} + +static +int _open(void *_data) +{ + struct run_as_open_data *data = _data; + return open(data->path, data->flags, data->mode); +} + +static +int child_run_as(void *_data) +{ + struct run_as_data *data = _data; + size_t writelen, writeleft, index; + union { + int i; + char c[sizeof(int)]; + } sendret; + int ret; + + /* + * Child: it is safe to drop egid and euid while sharing the + * file descriptors with the parent process, since we do not + * drop "uid": therefore, the user we are dropping egid/euid to + * cannot attach to this process with, e.g. ptrace, nor map this + * process memory. + */ + if (data->gid != getegid()) { + ret = setegid(data->gid); + if (ret < 0) { + perror("setegid"); + return EXIT_FAILURE; + } + } + if (data->uid != geteuid()) { + ret = seteuid(data->uid); + if (ret < 0) { + perror("seteuid"); + return EXIT_FAILURE; + } + } + /* + * Also set umask to 0 for mkdir executable bit. + */ + umask(0); + sendret.i = (*data->cmd)(data->data); + /* send back return value */ + writeleft = sizeof(sendret); + index = 0; + do { + writelen = write(data->retval_pipe, &sendret.c[index], + writeleft); + if (writelen < 0) { + perror("write"); + return EXIT_FAILURE; + } + writeleft -= writelen; + index += writelen; + } while (writeleft > 0); + return EXIT_SUCCESS; +} + +static +int run_as(int (*cmd)(void *data), void *data, uid_t uid, gid_t gid) +{ + struct run_as_data run_as_data; + int ret = 0; + int status; + pid_t pid; + int retval_pipe[2]; + ssize_t readlen, readleft, index; + void *child_stack; + union { + int i; + char c[sizeof(int)]; + } retval; + + /* + * If we are non-root, we can only deal with our own uid. + */ + if (geteuid() != 0) { + if (uid != geteuid()) { + ERR("Client (%d)/Server (%d) UID mismatch (and sessiond is not root)", + uid, geteuid()); + return -EPERM; + } + } + + ret = pipe(retval_pipe); + if (ret < 0) { + perror("pipe"); + goto end; + } + run_as_data.data = data; + run_as_data.cmd = cmd; + run_as_data.uid = uid; + run_as_data.gid = gid; + run_as_data.retval_pipe = retval_pipe[1]; /* write end */ + child_stack = mmap(NULL, RUNAS_CHILD_STACK_SIZE, + PROT_WRITE | PROT_READ, + MAP_PRIVATE | MAP_GROWSDOWN | MAP_ANONYMOUS | MAP_STACK, + -1, 0); + if (child_stack == MAP_FAILED) { + perror("mmap"); + ret = -ENOMEM; + goto close_pipe; + } + /* + * Pointing to the middle of the stack to support architectures + * where the stack grows up (HPPA). + */ + pid = clone(child_run_as, child_stack + (RUNAS_CHILD_STACK_SIZE / 2), + CLONE_FILES | SIGCHLD, + &run_as_data, NULL); + if (pid < 0) { + perror("clone"); + ret = pid; + goto unmap_stack; + } + /* receive return value */ + readleft = sizeof(retval); + index = 0; + do { + readlen = read(retval_pipe[0], &retval.c[index], readleft); + if (readlen < 0) { + perror("read"); + ret = -1; + break; + } + readleft -= readlen; + index += readlen; + } while (readleft > 0); + + /* + * Parent: wait for child to return, in which case the + * shared memory map will have been created. + */ + pid = waitpid(pid, &status, 0); + if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { + perror("wait"); + ret = -1; + } +unmap_stack: + ret = munmap(child_stack, RUNAS_CHILD_STACK_SIZE); + if (ret < 0) { + perror("munmap"); + } +close_pipe: + close(retval_pipe[0]); + close(retval_pipe[1]); +end: + return retval.i; +} + +int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + struct run_as_mkdir_data data; + + DBG3("mkdir() recursive %s with mode %d for uid %d and gid %d", + path, mode, uid, gid); + data.path = path; + data.mode = mode; + return run_as(_mkdir_recursive, &data, uid, gid); +} + +int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) +{ + struct run_as_mkdir_data data; + + DBG3("mkdir() %s with mode %d for uid %d and gid %d", + path, mode, uid, gid); + data.path = path; + data.mode = mode; + return run_as(_mkdir, &data, uid, gid); +} + +/* + * Note: open_run_as is currently not working. We'd need to pass the fd + * opened in the child to the parent. + */ +int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid) +{ + struct run_as_open_data data; + + DBG3("open() %s with flags %X mode %d for uid %d and gid %d", + path, flags, mode, uid, gid); + data.path = path; + data.flags = flags; + data.mode = mode; + return run_as(_open, &data, uid, gid); +} diff --git a/src/common/runas.h b/src/common/runas.h new file mode 100644 index 000000000..c8c485c1f --- /dev/null +++ b/src/common/runas.h @@ -0,0 +1,29 @@ +#ifndef _RUNAS_H +#define _RUNAS_H + +/* + * 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 verion 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 + +int run_as_mkdir_recursive(const char *path, mode_t mode, uid_t uid, gid_t gid); +int run_as_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid); +int run_as_open(const char *path, int flags, mode_t mode, uid_t uid, gid_t gid); + +#endif /* _RUNAS_H */ diff --git a/src/common/sessiond-comm/Makefile.am b/src/common/sessiond-comm/Makefile.am new file mode 100644 index 000000000..d8c2fa40e --- /dev/null +++ b/src/common/sessiond-comm/Makefile.am @@ -0,0 +1,5 @@ +# Session daemon communication lib +noinst_LTLIBRARIES = libsessiond-comm.la + +libsessiond_comm_la_SOURCES = sessiond-comm.c sessiond-comm.h + diff --git a/src/common/sessiond-comm/sessiond-comm.c b/src/common/sessiond-comm/sessiond-comm.c new file mode 100644 index 000000000..59b76aae1 --- /dev/null +++ b/src/common/sessiond-comm/sessiond-comm.c @@ -0,0 +1,534 @@ +/* + * 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 "sessiond-comm.h" + +/* + * Human readable error message. + */ +static const char *lttcomm_readable_code[] = { + [ LTTCOMM_ERR_INDEX(LTTCOMM_OK) ] = "Success", + [ LTTCOMM_ERR_INDEX(LTTCOMM_ERR) ] = "Unknown error", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UND) ] = "Undefined command", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NOT_IMPLEMENTED) ] = "Not implemented", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UNKNOWN_DOMAIN) ] = "Unknown tracing domain", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_SESSION) ] = "No session found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_LIST_FAIL) ] = "Unable to list traceable apps", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_APPS) ] = "No traceable apps found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_SESS_NOT_FOUND) ] = "Session name not found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_TRACE) ] = "No trace found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_FATAL) ] = "Fatal error of the session daemon", + [ LTTCOMM_ERR_INDEX(LTTCOMM_CREATE_FAIL) ] = "Create trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_START_FAIL) ] = "Start trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_STOP_FAIL) ] = "Stop trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_TRACEABLE) ] = "App is not traceable", + [ LTTCOMM_ERR_INDEX(LTTCOMM_SELECT_SESS) ] = "A session MUST be selected", + [ LTTCOMM_ERR_INDEX(LTTCOMM_EXIST_SESS) ] = "Session name already exist", + [ LTTCOMM_ERR_INDEX(LTTCOMM_CONNECT_FAIL) ] = "Unable to connect to Unix socket", + [ LTTCOMM_ERR_INDEX(LTTCOMM_APP_NOT_FOUND) ] = "Application not found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_EPERM) ] = "Permission denied", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_NA) ] = "Kernel tracer not available", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_EVENT_EXIST) ] = "Kernel event already exists", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_SESS_FAIL) ] = "Kernel create session failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_FAIL) ] = "Kernel create channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_NOT_FOUND) ] = "Kernel channel not found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_DISABLE_FAIL) ] = "Disable kernel channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CHAN_ENABLE_FAIL) ] = "Enable kernel channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CONTEXT_FAIL) ] = "Add kernel context failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_ENABLE_FAIL) ] = "Enable kernel event failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DISABLE_FAIL) ] = "Disable kernel event failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_META_FAIL) ] = "Opening metadata failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_START_FAIL) ] = "Starting kernel trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_STOP_FAIL) ] = "Stoping kernel trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_CONSUMER_FAIL) ] = "Kernel consumer start failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_STREAM_FAIL) ] = "Kernel create stream failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DIR_FAIL) ] = "Kernel trace directory creation failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_DIR_EXIST) ] = "Kernel trace directory already exist", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_NO_SESSION) ] = "No kernel session found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_KERN_LIST_FAIL) ] = "Listing kernel events failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_SESS_FAIL) ] = "UST create session failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_FAIL) ] = "UST create channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_EXIST) ] = "UST channel already exist", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_NOT_FOUND) ] = "UST channel not found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_DISABLE_FAIL) ] = "Disable UST channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CHAN_ENABLE_FAIL) ] = "Enable UST channel failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONTEXT_FAIL) ] = "Add UST context failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_ENABLE_FAIL) ] = "Enable UST event failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DISABLE_FAIL) ] = "Disable UST event failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_META_FAIL) ] = "Opening metadata failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_START_FAIL) ] = "Starting UST trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_STOP_FAIL) ] = "Stoping UST trace failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONSUMER64_FAIL) ] = "64-bit UST consumer start failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONSUMER32_FAIL) ] = "32-bit UST consumer start failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_STREAM_FAIL) ] = "UST create stream failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DIR_FAIL) ] = "UST trace directory creation failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_DIR_EXIST) ] = "UST trace directory already exist", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_NO_SESSION) ] = "No UST session found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_LIST_FAIL) ] = "Listing UST events failed", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_EVENT_EXIST) ] = "UST event already exist", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_EVENT_NOT_FOUND)] = "UST event not found", + [ LTTCOMM_ERR_INDEX(LTTCOMM_UST_CONTEXT_EXIST)] = "UST context already exist", + [ LTTCOMM_ERR_INDEX(CONSUMERD_COMMAND_SOCK_READY) ] = "consumerd command socket ready", + [ LTTCOMM_ERR_INDEX(CONSUMERD_SUCCESS_RECV_FD) ] = "consumerd success on receiving fds", + [ LTTCOMM_ERR_INDEX(CONSUMERD_ERROR_RECV_FD) ] = "consumerd error on receiving fds", + [ LTTCOMM_ERR_INDEX(CONSUMERD_ERROR_RECV_CMD) ] = "consumerd error on receiving command", + [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_ERROR) ] = "consumerd error in polling thread", + [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_NVAL) ] = "consumerd polling on closed fd", + [ LTTCOMM_ERR_INDEX(CONSUMERD_POLL_HUP) ] = "consumerd all fd hung up", + [ LTTCOMM_ERR_INDEX(CONSUMERD_EXIT_SUCCESS) ] = "consumerd exiting normally", + [ LTTCOMM_ERR_INDEX(CONSUMERD_EXIT_FAILURE) ] = "consumerd exiting on error", + [ LTTCOMM_ERR_INDEX(CONSUMERD_OUTFD_ERROR) ] = "consumerd error opening the tracefile", + [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_EBADF) ] = "consumerd splice EBADF", + [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_EINVAL) ] = "consumerd splice EINVAL", + [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_ENOMEM) ] = "consumerd splice ENOMEM", + [ LTTCOMM_ERR_INDEX(CONSUMERD_SPLICE_ESPIPE) ] = "consumerd splice ESPIPE", + [ LTTCOMM_ERR_INDEX(LTTCOMM_NO_EVENT) ] = "Event not found", +}; + +/* + * Return ptr to string representing a human readable error code from the + * lttcomm_return_code enum. + * + * These code MUST be negative in other to treat that as an error value. + */ +const char *lttcomm_get_readable_code(enum lttcomm_return_code code) +{ + int tmp_code = -code; + + if (tmp_code >= LTTCOMM_OK && tmp_code < LTTCOMM_NR) { + return lttcomm_readable_code[LTTCOMM_ERR_INDEX(tmp_code)]; + } + + return "Unknown error code"; +} + +/* + * Connect to unix socket using the path name. + */ +int lttcomm_connect_unix_sock(const char *pathname) +{ + struct sockaddr_un sun; + int fd; + int ret; + + fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { + perror("socket"); + ret = fd; + goto error; + } + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strncpy(sun.sun_path, pathname, sizeof(sun.sun_path)); + sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; + + ret = connect(fd, (struct sockaddr *) &sun, sizeof(sun)); + if (ret < 0) { + /* + * Don't print message on connect error, because connect is used in + * normal execution to detect if sessiond is alive. + */ + goto error_connect; + } + + return fd; + +error_connect: + close(fd); +error: + return ret; +} + +/* + * Do an accept(2) on the sock and return the new file descriptor. The socket + * MUST be bind(2) before. + */ +int lttcomm_accept_unix_sock(int sock) +{ + int new_fd; + struct sockaddr_un sun; + socklen_t len = 0; + + /* Blocking call */ + new_fd = accept(sock, (struct sockaddr *) &sun, &len); + if (new_fd < 0) { + perror("accept"); + } + + return new_fd; +} + +/* + * Creates a AF_UNIX local socket using pathname bind the socket upon creation + * and return the fd. + */ +int lttcomm_create_unix_sock(const char *pathname) +{ + struct sockaddr_un sun; + int fd; + int ret = -1; + + /* Create server socket */ + if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("socket"); + goto error; + } + + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + strncpy(sun.sun_path, pathname, sizeof(sun.sun_path)); + sun.sun_path[sizeof(sun.sun_path) - 1] = '\0'; + + /* Unlink the old file if present */ + (void) unlink(pathname); + ret = bind(fd, (struct sockaddr *) &sun, sizeof(sun)); + if (ret < 0) { + perror("bind"); + goto error; + } + + return fd; + +error: + return ret; +} + +/* + * Make the socket listen using LTTNG_SESSIOND_COMM_MAX_LISTEN. + */ +int lttcomm_listen_unix_sock(int sock) +{ + int ret; + + ret = listen(sock, LTTNG_SESSIOND_COMM_MAX_LISTEN); + if (ret < 0) { + perror("listen"); + } + + return ret; +} + +/* + * Receive data of size len in put that data into the buf param. Using recvmsg + * API. + * + * Return the size of received data. + */ +ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len) +{ + struct msghdr msg = { 0 }; + struct iovec iov[1]; + ssize_t ret = -1; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + perror("recvmsg"); + } + + return ret; +} + +/* + * Send buf data of size len. Using sendmsg API. + * + * Return the size of sent data. + */ +ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len) +{ + struct msghdr msg = { 0 }; + struct iovec iov[1]; + ssize_t ret = -1; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + perror("sendmsg"); + } + + return ret; +} + +/* + * Shutdown cleanly a unix socket. + */ +int lttcomm_close_unix_sock(int sock) +{ + int ret; + + /* Shutdown receptions and transmissions */ + ret = shutdown(sock, SHUT_RDWR); + if (ret < 0) { + perror("shutdown"); + } + + return ret; +} + +/* + * Send a message accompanied by fd(s) over a unix socket. + * + * Returns the size of data sent, or negative error value. + */ +ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd) +{ + struct msghdr msg = { 0 }; + struct cmsghdr *cmptr; + struct iovec iov[1]; + ssize_t ret = -1; + unsigned int sizeof_fds = nb_fd * sizeof(int); + char tmp[CMSG_SPACE(sizeof_fds)]; + char dummy = 0; + + if (nb_fd > LTTCOMM_MAX_SEND_FDS) + return -EINVAL; + + msg.msg_control = (caddr_t)tmp; + msg.msg_controllen = CMSG_LEN(sizeof_fds); + + cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + cmptr->cmsg_len = CMSG_LEN(sizeof_fds); + memcpy(CMSG_DATA(cmptr), fds, sizeof_fds); + /* Sum of the length of all control messages in the buffer: */ + msg.msg_controllen = cmptr->cmsg_len; + + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + perror("sendmsg"); + } + return ret; +} + +/* + * Recv a message accompanied by fd(s) from a unix socket. + * + * Returns the size of received data, or negative error value. + * + * Expect at most "nb_fd" file descriptors. Returns the number of fd + * actually received in nb_fd. + */ +ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd) +{ + struct iovec iov[1]; + ssize_t ret = 0; + struct cmsghdr *cmsg; + size_t sizeof_fds = nb_fd * sizeof(int); + char recv_fd[CMSG_SPACE(sizeof_fds)]; + struct msghdr msg = { 0 }; + char dummy; + + /* Prepare to receive the structures */ + iov[0].iov_base = &dummy; + iov[0].iov_len = 1; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = recv_fd; + msg.msg_controllen = sizeof(recv_fd); + + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + perror("recvmsg fds"); + goto end; + } + if (ret != 1) { + fprintf(stderr, "Error: Received %zd bytes, expected %d\n", + ret, 1); + goto end; + } + if (msg.msg_flags & MSG_CTRUNC) { + fprintf(stderr, "Error: Control message truncated.\n"); + ret = -1; + goto end; + } + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg) { + fprintf(stderr, "Error: Invalid control message header\n"); + ret = -1; + goto end; + } + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) { + fprintf(stderr, "Didn't received any fd\n"); + ret = -1; + goto end; + } + if (cmsg->cmsg_len != CMSG_LEN(sizeof_fds)) { + fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", + cmsg->cmsg_len, CMSG_LEN(sizeof_fds)); + ret = -1; + goto end; + } + memcpy(fds, CMSG_DATA(cmsg), sizeof_fds); + ret = sizeof_fds; +end: + return ret; +} + +/* + * Send a message with credentials over a unix socket. + * + * Returns the size of data sent, or negative error value. + */ +ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len) +{ + struct msghdr msg = { 0 }; + struct cmsghdr *cmptr; + struct iovec iov[1]; + ssize_t ret = -1; + struct ucred *creds; + size_t sizeof_cred = sizeof(struct ucred); + char anc_buf[CMSG_SPACE(sizeof_cred)]; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = (caddr_t) anc_buf; + msg.msg_controllen = CMSG_LEN(sizeof_cred); + + cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_CREDENTIALS; + cmptr->cmsg_len = CMSG_LEN(sizeof_cred); + + creds = (struct ucred *) CMSG_DATA(cmptr); + + creds->uid = geteuid(); + creds->gid = getegid(); + creds->pid = getpid(); + + ret = sendmsg(sock, &msg, 0); + if (ret < 0) { + perror("sendmsg"); + } + + return ret; +} + +/* + * Recv a message accompanied with credentials from a unix socket. + * + * Returns the size of received data, or negative error value. + */ +ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, + struct ucred *creds) +{ + struct msghdr msg = { 0 }; + struct cmsghdr *cmptr; + struct iovec iov[1]; + ssize_t ret; + size_t sizeof_cred = sizeof(struct ucred); + char anc_buf[CMSG_SPACE(sizeof_cred)]; + + /* Not allowed */ + if (creds == NULL) { + ret = -1; + goto end; + } + + /* Prepare to receive the structures */ + iov[0].iov_base = buf; + iov[0].iov_len = len; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + msg.msg_control = anc_buf; + msg.msg_controllen = sizeof(anc_buf); + + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + perror("recvmsg fds"); + goto end; + } + + if (msg.msg_flags & MSG_CTRUNC) { + fprintf(stderr, "Error: Control message truncated.\n"); + ret = -1; + goto end; + } + + cmptr = CMSG_FIRSTHDR(&msg); + if (cmptr == NULL) { + fprintf(stderr, "Error: Invalid control message header\n"); + ret = -1; + goto end; + } + + if (cmptr->cmsg_level != SOL_SOCKET || + cmptr->cmsg_type != SCM_CREDENTIALS) { + fprintf(stderr, "Didn't received any credentials\n"); + ret = -1; + goto end; + } + + if (cmptr->cmsg_len != CMSG_LEN(sizeof_cred)) { + fprintf(stderr, "Error: Received %zu bytes of ancillary data, expected %zu\n", + cmptr->cmsg_len, CMSG_LEN(sizeof_cred)); + ret = -1; + goto end; + } + + memcpy(creds, CMSG_DATA(cmptr), sizeof_cred); + +end: + return ret; +} + +/* + * Set socket option to use credentials passing. + */ +int lttcomm_setsockopt_creds_unix_sock(int sock) +{ + int ret, on = 1; + + /* Set socket for credentials retrieval */ + ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + if (ret < 0) { + perror("setsockopt creds unix sock"); + } + + return ret; +} diff --git a/src/common/sessiond-comm/sessiond-comm.h b/src/common/sessiond-comm/sessiond-comm.h new file mode 100644 index 000000000..48cf93321 --- /dev/null +++ b/src/common/sessiond-comm/sessiond-comm.h @@ -0,0 +1,303 @@ +#ifndef _LTTNG_SESSIOND_COMM_H +#define _LTTNG_SESSIOND_COMM_H + +/* + * Copyright (C) 2011 - David Goulet + * 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. + */ + +/* + * This header is meant for liblttng and libust internal use ONLY. + * These declarations should NOT be considered stable API. + */ + +#define _GNU_SOURCE +#include +#include +#include + +#define LTTNG_RUNDIR "/var/run/lttng" +#define LTTNG_HOME_RUNDIR "%s/.lttng" + +/* Default unix socket path */ +#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 LTTNG_HOME_RUNDIR "/apps-lttng-sessiond" +#define DEFAULT_HOME_CLIENT_UNIX_SOCK LTTNG_HOME_RUNDIR "/client-lttng-sessiond" + +/* Queue size of listen(2) */ +#define LTTNG_SESSIOND_COMM_MAX_LISTEN 64 + +/* + * Get the error code index from 0 since LTTCOMM_OK start at 1000 + */ +#define LTTCOMM_ERR_INDEX(code) (code - LTTCOMM_OK) + +enum lttcomm_sessiond_command { + /* Tracer command */ + LTTNG_ADD_CONTEXT, + LTTNG_CALIBRATE, + LTTNG_DISABLE_CHANNEL, + LTTNG_DISABLE_EVENT, + LTTNG_DISABLE_ALL_EVENT, + LTTNG_ENABLE_CHANNEL, + LTTNG_ENABLE_EVENT, + LTTNG_ENABLE_ALL_EVENT, + /* Session daemon command */ + LTTNG_CREATE_SESSION, + LTTNG_DESTROY_SESSION, + LTTNG_LIST_CHANNELS, + LTTNG_LIST_DOMAINS, + LTTNG_LIST_EVENTS, + LTTNG_LIST_SESSIONS, + LTTNG_LIST_TRACEPOINTS, + LTTNG_REGISTER_CONSUMER, + LTTNG_START_TRACE, + LTTNG_STOP_TRACE, +}; + +/* + * lttcomm error code. + */ +enum lttcomm_return_code { + LTTCOMM_OK = 1000, /* Ok */ + LTTCOMM_ERR, /* Unknown Error */ + LTTCOMM_UND, /* Undefine command */ + LTTCOMM_NOT_IMPLEMENTED, /* Command not implemented */ + LTTCOMM_UNKNOWN_DOMAIN, /* Tracing domain not known */ + LTTCOMM_ALLOC_FAIL, /* Trace allocation fail */ + LTTCOMM_NO_SESSION, /* No session found */ + LTTCOMM_CREATE_FAIL, /* Create trace fail */ + LTTCOMM_SESSION_FAIL, /* Create session fail */ + LTTCOMM_START_FAIL, /* Start tracing fail */ + LTTCOMM_STOP_FAIL, /* Stop tracing fail */ + LTTCOMM_LIST_FAIL, /* Listing apps fail */ + LTTCOMM_NO_APPS, /* No traceable application */ + LTTCOMM_SESS_NOT_FOUND, /* Session name not found */ + LTTCOMM_NO_TRACE, /* No trace exist */ + LTTCOMM_FATAL, /* Session daemon had a fatal error */ + LTTCOMM_NO_TRACEABLE, /* Error for non traceable app */ + LTTCOMM_SELECT_SESS, /* Must select a session */ + LTTCOMM_EXIST_SESS, /* Session name already exist */ + LTTCOMM_NO_EVENT, /* No event found */ + LTTCOMM_CONNECT_FAIL, /* Unable to connect to unix socket */ + LTTCOMM_APP_NOT_FOUND, /* App not found in traceable app list */ + LTTCOMM_EPERM, /* Permission denied */ + LTTCOMM_KERN_NA, /* Kernel tracer unavalable */ + LTTCOMM_KERN_EVENT_EXIST, /* Kernel event already exists */ + LTTCOMM_KERN_SESS_FAIL, /* Kernel create session failed */ + LTTCOMM_KERN_CHAN_FAIL, /* Kernel create channel failed */ + LTTCOMM_KERN_CHAN_NOT_FOUND, /* Kernel channel not found */ + LTTCOMM_KERN_CHAN_DISABLE_FAIL, /* Kernel disable channel failed */ + LTTCOMM_KERN_CHAN_ENABLE_FAIL, /* Kernel enable channel failed */ + LTTCOMM_KERN_CONTEXT_FAIL, /* Kernel add context failed */ + LTTCOMM_KERN_ENABLE_FAIL, /* Kernel enable event failed */ + LTTCOMM_KERN_DISABLE_FAIL, /* Kernel disable event failed */ + LTTCOMM_KERN_META_FAIL, /* Kernel open metadata failed */ + LTTCOMM_KERN_START_FAIL, /* Kernel start trace failed */ + LTTCOMM_KERN_STOP_FAIL, /* Kernel stop trace failed */ + LTTCOMM_KERN_CONSUMER_FAIL, /* Kernel consumer start failed */ + LTTCOMM_KERN_STREAM_FAIL, /* Kernel create stream failed */ + LTTCOMM_KERN_DIR_FAIL, /* Kernel trace directory creation failed */ + LTTCOMM_KERN_DIR_EXIST, /* Kernel trace directory exist */ + LTTCOMM_KERN_NO_SESSION, /* No kernel session found */ + LTTCOMM_KERN_LIST_FAIL, /* Kernel listing events failed */ + LTTCOMM_UST_SESS_FAIL, /* UST create session failed */ + LTTCOMM_UST_CHAN_EXIST, /* UST channel already exist */ + LTTCOMM_UST_CHAN_FAIL, /* UST create channel failed */ + LTTCOMM_UST_CHAN_NOT_FOUND, /* UST channel not found */ + LTTCOMM_UST_CHAN_DISABLE_FAIL, /* UST disable channel failed */ + LTTCOMM_UST_CHAN_ENABLE_FAIL, /* UST enable channel failed */ + LTTCOMM_UST_CONTEXT_FAIL, /* UST add context failed */ + LTTCOMM_UST_ENABLE_FAIL, /* UST enable event failed */ + LTTCOMM_UST_DISABLE_FAIL, /* UST disable event failed */ + LTTCOMM_UST_META_FAIL, /* UST open metadata failed */ + LTTCOMM_UST_START_FAIL, /* UST start trace failed */ + LTTCOMM_UST_STOP_FAIL, /* UST stop trace failed */ + LTTCOMM_UST_CONSUMER64_FAIL, /* 64-bit UST consumer start failed */ + LTTCOMM_UST_CONSUMER32_FAIL, /* 32-bit UST consumer start failed */ + LTTCOMM_UST_STREAM_FAIL, /* UST create stream failed */ + LTTCOMM_UST_DIR_FAIL, /* UST trace directory creation failed */ + LTTCOMM_UST_DIR_EXIST, /* UST trace directory exist */ + LTTCOMM_UST_NO_SESSION, /* No UST session found */ + LTTCOMM_UST_LIST_FAIL, /* UST listing events failed */ + LTTCOMM_UST_EVENT_EXIST, /* UST event exist */ + LTTCOMM_UST_EVENT_NOT_FOUND, /* UST event not found */ + LTTCOMM_UST_CONTEXT_EXIST, /* UST context exist */ + + CONSUMERD_COMMAND_SOCK_READY, /* when consumerd command socket ready */ + CONSUMERD_SUCCESS_RECV_FD, /* success on receiving fds */ + CONSUMERD_ERROR_RECV_FD, /* error on receiving fds */ + CONSUMERD_ERROR_RECV_CMD, /* error on receiving command */ + CONSUMERD_POLL_ERROR, /* Error in polling thread in kconsumerd */ + CONSUMERD_POLL_NVAL, /* Poll on closed fd */ + CONSUMERD_POLL_HUP, /* All fds have hungup */ + CONSUMERD_EXIT_SUCCESS, /* kconsumerd exiting normally */ + CONSUMERD_EXIT_FAILURE, /* kconsumerd exiting on error */ + CONSUMERD_OUTFD_ERROR, /* error opening the tracefile */ + CONSUMERD_SPLICE_EBADF, /* EBADF from splice(2) */ + CONSUMERD_SPLICE_EINVAL, /* EINVAL from splice(2) */ + CONSUMERD_SPLICE_ENOMEM, /* ENOMEM from splice(2) */ + CONSUMERD_SPLICE_ESPIPE, /* ESPIPE from splice(2) */ + /* MUST be last element */ + LTTCOMM_NR, /* Last element */ +}; + +/* + * Data structure received from lttng client to session daemon. + */ +struct lttcomm_session_msg { + uint32_t cmd_type; /* enum lttcomm_sessiond_command */ + struct lttng_session session; + struct lttng_domain domain; + union { + struct { + char channel_name[NAME_MAX]; + char name[NAME_MAX]; + } disable; + /* Event data */ + struct { + char channel_name[NAME_MAX]; + struct lttng_event event; + } enable; + /* Create channel */ + struct { + struct lttng_channel chan; + } channel; + /* Context */ + struct { + char channel_name[NAME_MAX]; + char event_name[NAME_MAX]; + struct lttng_event_context ctx; + } context; + /* Use by register_consumer */ + struct { + char path[PATH_MAX]; + } reg; + /* List */ + struct { + char channel_name[NAME_MAX]; + } list; + struct lttng_calibrate calibrate; + } u; +}; + +/* + * Data structure for the response from sessiond to the lttng client. + */ +struct lttcomm_lttng_msg { + uint32_t cmd_type; /* enum lttcomm_sessiond_command */ + uint32_t ret_code; /* enum lttcomm_return_code */ + uint32_t pid; /* pid_t */ + uint32_t data_size; + /* Contains: trace_name + data */ + char payload[]; +}; + +/* + * lttcomm_consumer_msg is the message sent from sessiond to consumerd + * to either add a channel, add a stream, update a stream, or stop + * operation. + */ +struct lttcomm_consumer_msg { + uint32_t cmd_type; /* enum consumerd_command */ + union { + struct { + int channel_key; + uint64_t max_sb_size; /* the subbuffer size for this channel */ + /* shm_fd and wait_fd are sent as ancillary data */ + uint64_t mmap_len; + } channel; + struct { + int channel_key; + int stream_key; + /* shm_fd and wait_fd are sent as ancillary data */ + uint32_t state; /* enum lttcomm_consumer_fd_state */ + enum lttng_event_output output; /* use splice or mmap to consume this fd */ + uint64_t mmap_len; + uid_t uid; /* User ID owning the session */ + gid_t gid; /* Group ID owning the session */ + char path_name[PATH_MAX]; + } stream; + } u; +}; + +#ifdef HAVE_LIBLTTNG_UST_CTL + +#include + +/* + * Data structure for the commands sent from sessiond to UST. + */ +struct lttcomm_ust_msg { + uint32_t handle; + uint32_t cmd; + union { + struct lttng_ust_channel channel; + struct lttng_ust_stream stream; + struct lttng_ust_event event; + struct lttng_ust_context context; + struct lttng_ust_tracer_version version; + } u; +}; + +/* + * Data structure for the response from UST to the session daemon. + * cmd_type is sent back in the reply for validation. + */ +struct lttcomm_ust_reply { + uint32_t handle; + uint32_t cmd; + uint32_t ret_code; /* enum lttcomm_return_code */ + uint32_t ret_val; /* return value */ + union { + struct { + uint64_t memory_map_size; + } channel; + struct { + uint64_t memory_map_size; + } stream; + struct lttng_ust_tracer_version version; + } u; +}; + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +extern int lttcomm_create_unix_sock(const char *pathname); +extern int lttcomm_connect_unix_sock(const char *pathname); +extern int lttcomm_accept_unix_sock(int sock); +extern int lttcomm_listen_unix_sock(int sock); +extern int lttcomm_close_unix_sock(int sock); + +#define LTTCOMM_MAX_SEND_FDS 4 +/* Send a message accompanied by fd(s) over a unix socket. */ +extern ssize_t lttcomm_send_fds_unix_sock(int sock, int *fds, size_t nb_fd); +/* Recv a message accompanied by fd(s) from a unix socket */ +extern ssize_t lttcomm_recv_fds_unix_sock(int sock, int *fds, size_t nb_fd); + +extern ssize_t lttcomm_recv_unix_sock(int sock, void *buf, size_t len); +extern ssize_t lttcomm_send_unix_sock(int sock, void *buf, size_t len); + +extern ssize_t lttcomm_send_creds_unix_sock(int sock, void *buf, size_t len); +extern ssize_t lttcomm_recv_creds_unix_sock(int sock, void *buf, size_t len, + struct ucred *creds); + +extern const char *lttcomm_get_readable_code(enum lttcomm_return_code code); +extern int lttcomm_setsockopt_creds_unix_sock(int sock); + +#endif /* _LTTNG_SESSIOND_COMM_H */ diff --git a/src/common/ust-consumer/Makefile.am b/src/common/ust-consumer/Makefile.am new file mode 100644 index 000000000..478aae793 --- /dev/null +++ b/src/common/ust-consumer/Makefile.am @@ -0,0 +1,8 @@ +if HAVE_LIBLTTNG_UST_CTL +noinst_LTLIBRARIES = libust-consumer.la + +libust_consumer_la_SOURCES = ust-consumer.c + +libust_consumer_la_LIBADD = -llttng-ust-ctl + +endif diff --git a/src/common/ust-consumer/ust-consumer.c b/src/common/ust-consumer/ust-consumer.c new file mode 100644 index 000000000..16c09af8a --- /dev/null +++ b/src/common/ust-consumer/ust-consumer.c @@ -0,0 +1,415 @@ +/* + * 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ust-consumer.h" + +extern struct lttng_consumer_global_data consumer_data; +extern int consumer_poll_timeout; +extern volatile int consumer_quit; + +/* + * Mmap the ring buffer, read it and write the data to the tracefile. + * + * Returns the number of bytes written + */ +int lttng_ustconsumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + unsigned long mmap_offset; + long ret = 0; + off_t orig_offset = stream->out_fd_offset; + int outfd = stream->out_fd; + + /* get the offset inside the fd to mmap */ + ret = ustctl_get_mmap_read_offset(stream->chan->handle, + stream->buf, &mmap_offset); + if (ret != 0) { + ret = -errno; + perror("ustctl_get_mmap_read_offset"); + goto end; + } + while (len > 0) { + ret = write(outfd, stream->mmap_base + mmap_offset, len); + if (ret >= len) { + len = 0; + } else if (ret < 0) { + ret = -errno; + perror("Error in file write"); + goto end; + } + /* This won't block, but will start writeout asynchronously */ + sync_file_range(outfd, stream->out_fd_offset, ret, + SYNC_FILE_RANGE_WRITE); + stream->out_fd_offset += ret; + } + + lttng_consumer_sync_trace_file(stream, orig_offset); + + goto end; + +end: + return ret; +} + +/* + * Splice the data from the ring buffer to the tracefile. + * + * Returns the number of bytes spliced. + */ +int lttng_ustconsumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + return -ENOSYS; +} + +/* + * Take a snapshot for a specific fd + * + * Returns 0 on success, < 0 on error + */ +int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream) +{ + int ret = 0; + + ret = ustctl_snapshot(stream->chan->handle, stream->buf); + if (ret != 0) { + ret = errno; + perror("Getting sub-buffer snapshot."); + } + + return ret; +} + +/* + * Get the produced position + * + * Returns 0 on success, < 0 on error + */ +int lttng_ustconsumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos) +{ + int ret; + + ret = ustctl_snapshot_get_produced(stream->chan->handle, + stream->buf, pos); + if (ret != 0) { + ret = errno; + perror("kernctl_snapshot_get_produced"); + } + + return ret; +} + +int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll) +{ + ssize_t ret; + struct lttcomm_consumer_msg msg; + + ret = lttcomm_recv_unix_sock(sock, &msg, sizeof(msg)); + if (ret != sizeof(msg)) { + lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); + return ret; + } + if (msg.cmd_type == LTTNG_CONSUMER_STOP) { + return -ENOENT; + } + + switch (msg.cmd_type) { + case LTTNG_CONSUMER_ADD_CHANNEL: + { + struct lttng_consumer_channel *new_channel; + int fds[1]; + size_t nb_fd = 1; + + /* block */ + if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { + return -EINTR; + } + ret = lttcomm_recv_fds_unix_sock(sock, fds, nb_fd); + if (ret != sizeof(fds)) { + lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); + return ret; + } + + DBG("consumer_add_channel %d", msg.u.channel.channel_key); + + new_channel = consumer_allocate_channel(msg.u.channel.channel_key, + fds[0], -1, + msg.u.channel.mmap_len, + msg.u.channel.max_sb_size); + if (new_channel == NULL) { + lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); + goto end_nosignal; + } + if (ctx->on_recv_channel != NULL) { + ret = ctx->on_recv_channel(new_channel); + if (ret == 0) { + consumer_add_channel(new_channel); + } else if (ret < 0) { + goto end_nosignal; + } + } else { + consumer_add_channel(new_channel); + } + goto end_nosignal; + } + case LTTNG_CONSUMER_ADD_STREAM: + { + struct lttng_consumer_stream *new_stream; + int fds[2]; + size_t nb_fd = 2; + + /* block */ + if (lttng_consumer_poll_socket(consumer_sockpoll) < 0) { + return -EINTR; + } + ret = lttcomm_recv_fds_unix_sock(sock, fds, nb_fd); + if (ret != sizeof(fds)) { + lttng_consumer_send_error(ctx, CONSUMERD_ERROR_RECV_FD); + return ret; + } + + DBG("consumer_add_stream %s (%d,%d)", msg.u.stream.path_name, + fds[0], fds[1]); + assert(msg.u.stream.output == LTTNG_EVENT_MMAP); + new_stream = consumer_allocate_stream(msg.u.channel.channel_key, + msg.u.stream.stream_key, + fds[0], fds[1], + msg.u.stream.state, + msg.u.stream.mmap_len, + msg.u.stream.output, + msg.u.stream.path_name, + msg.u.stream.uid, + msg.u.stream.gid); + if (new_stream == NULL) { + lttng_consumer_send_error(ctx, CONSUMERD_OUTFD_ERROR); + goto end; + } + if (ctx->on_recv_stream != NULL) { + ret = ctx->on_recv_stream(new_stream); + if (ret == 0) { + consumer_add_stream(new_stream); + } else if (ret < 0) { + goto end; + } + } else { + consumer_add_stream(new_stream); + } + break; + } + case LTTNG_CONSUMER_UPDATE_STREAM: + { + return -ENOSYS; +#if 0 + if (ctx->on_update_stream != NULL) { + ret = ctx->on_update_stream(msg.u.stream.stream_key, msg.u.stream.state); + if (ret == 0) { + consumer_change_stream_state(msg.u.stream.stream_key, msg.u.stream.state); + } else if (ret < 0) { + goto end; + } + } else { + consumer_change_stream_state(msg.u.stream.stream_key, + msg.u.stream.state); + } +#endif + break; + } + default: + break; + } +end: + /* signal the poll thread */ + ret = write(ctx->consumer_poll_pipe[1], "4", 1); + if (ret < 0) { + perror("write consumer poll"); + } +end_nosignal: + return 0; +} + +int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan) +{ + struct lttng_ust_object_data obj; + + obj.handle = -1; + obj.shm_fd = chan->shm_fd; + obj.wait_fd = chan->wait_fd; + obj.memory_map_size = chan->mmap_len; + chan->handle = ustctl_map_channel(&obj); + if (!chan->handle) { + return -ENOMEM; + } + chan->wait_fd_is_copy = 1; + chan->shm_fd = -1; + + return 0; +} + +void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream) +{ + ustctl_flush_buffer(stream->chan->handle, stream->buf, 0); + stream->hangup_flush_done = 1; +} + +void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) +{ + ustctl_unmap_channel(chan->handle); +} + +int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream) +{ + struct lttng_ust_object_data obj; + int ret; + + obj.handle = -1; + obj.shm_fd = stream->shm_fd; + obj.wait_fd = stream->wait_fd; + obj.memory_map_size = stream->mmap_len; + ret = ustctl_add_stream(stream->chan->handle, &obj); + if (ret) + return ret; + stream->buf = ustctl_open_stream_read(stream->chan->handle, stream->cpu); + if (!stream->buf) + return -EBUSY; + /* ustctl_open_stream_read has closed the shm fd. */ + stream->wait_fd_is_copy = 1; + stream->shm_fd = -1; + + stream->mmap_base = ustctl_get_mmap_base(stream->chan->handle, stream->buf); + if (!stream->mmap_base) { + return -EINVAL; + } + + return 0; +} + +void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream) +{ + ustctl_close_stream_read(stream->chan->handle, stream->buf); +} + + +int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx) +{ + unsigned long len; + int err; + long ret = 0; + struct lttng_ust_shm_handle *handle; + struct lttng_ust_lib_ring_buffer *buf; + char dummy; + ssize_t readlen; + + DBG("In read_subbuffer (wait_fd: %d, stream key: %d)", + stream->wait_fd, stream->key); + + /* We can consume the 1 byte written into the wait_fd by UST */ + if (!stream->hangup_flush_done) { + do { + readlen = read(stream->wait_fd, &dummy, 1); + } while (readlen == -1 && errno == -EINTR); + if (readlen == -1) { + ret = readlen; + goto end; + } + } + + buf = stream->buf; + handle = stream->chan->handle; + /* Get the next subbuffer */ + err = ustctl_get_next_subbuf(handle, buf); + if (err != 0) { + ret = -ret; /* ustctl_get_next_subbuf returns negative, caller expect positive. */ + /* + * This is a debug message even for single-threaded consumer, + * because poll() have more relaxed criterions than get subbuf, + * so get_subbuf may fail for short race windows where poll() + * would issue wakeups. + */ + DBG("Reserving sub buffer failed (everything is normal, " + "it is due to concurrency)"); + goto end; + } + assert(stream->output == LTTNG_EVENT_MMAP); + /* read the used subbuffer size */ + err = ustctl_get_padded_subbuf_size(handle, buf, &len); + assert(err == 0); + /* write the subbuffer to the tracefile */ + ret = lttng_consumer_on_read_subbuffer_mmap(ctx, stream, len); + if (ret < 0) { + /* + * display the error but continue processing to try + * to release the subbuffer + */ + ERR("Error writing to tracefile"); + } + err = ustctl_put_next_subbuf(handle, buf); + assert(err == 0); +end: + return ret; +} + +int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream) +{ + int ret; + + /* Opening the tracefile in write mode */ + if (stream->path_name != NULL) { + ret = run_as_open(stream->path_name, + O_WRONLY|O_CREAT|O_TRUNC, + S_IRWXU|S_IRWXG|S_IRWXO, + stream->uid, stream->gid); + if (ret < 0) { + ERR("Opening %s", stream->path_name); + perror("open"); + goto error; + } + stream->out_fd = ret; + } + + /* we return 0 to let the library handle the FD internally */ + return 0; + +error: + return ret; +} diff --git a/src/common/ust-consumer/ust-consumer.h b/src/common/ust-consumer/ust-consumer.h new file mode 100644 index 000000000..76b4eeece --- /dev/null +++ b/src/common/ust-consumer/ust-consumer.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2011 - Julien Desfossez + * Copyright (C) 2011 - 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_USTCONSUMER_H +#define _LTTNG_USTCONSUMER_H + +#include +#include + +#include + +#ifdef HAVE_LIBLTTNG_UST_CTL + +/* + * Mmap the ring buffer, read it and write the data to the tracefile. + * + * Returns the number of bytes written. + */ +extern int lttng_ustconsumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); + +/* Not implemented */ +extern int lttng_ustconsumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len); + +/* + * Take a snapshot for a specific fd + * + * Returns 0 on success, < 0 on error + */ +int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream); + +/* + * Get the produced position + * + * Returns 0 on success, < 0 on error + */ +int lttng_ustconsumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos); + +int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll); + +extern int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan); +extern void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan); +extern int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream); +extern void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream); + +int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx); +int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream); + +void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream); + +#else /* HAVE_LIBLTTNG_UST_CTL */ + +static inline +int lttng_ustconsumer_on_read_subbuffer_mmap( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, unsigned long len) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_on_read_subbuffer_splice( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *uststream, unsigned long len) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_take_snapshot(struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_get_produced_snapshot( + struct lttng_consumer_local_data *ctx, + struct lttng_consumer_stream *stream, + unsigned long *pos) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_recv_cmd(struct lttng_consumer_local_data *ctx, + int sock, struct pollfd *consumer_sockpoll) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_allocate_channel(struct lttng_consumer_channel *chan) +{ + return -ENOSYS; +} + +static inline +void lttng_ustconsumer_del_channel(struct lttng_consumer_channel *chan) +{ +} + +static inline +int lttng_ustconsumer_allocate_stream(struct lttng_consumer_stream *stream) +{ + return -ENOSYS; +} + +static inline +void lttng_ustconsumer_del_stream(struct lttng_consumer_stream *stream) +{ +} + +static inline +int lttng_ustconsumer_read_subbuffer(struct lttng_consumer_stream *stream, + struct lttng_consumer_local_data *ctx) +{ + return -ENOSYS; +} + +static inline +int lttng_ustconsumer_on_recv_stream(struct lttng_consumer_stream *stream) +{ + return -ENOSYS; +} + +static inline +void lttng_ustconsumer_on_stream_hangup(struct lttng_consumer_stream *stream) +{ +} + +#endif /* HAVE_LIBLTTNG_UST_CTL */ + +#endif /* _LTTNG_USTCONSUMER_H */ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am new file mode 100644 index 000000000..482f20c15 --- /dev/null +++ b/src/lib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = lttng-ctl diff --git a/src/lib/lttng-ctl/Makefile.am b/src/lib/lttng-ctl/Makefile.am new file mode 100644 index 000000000..6bc5432aa --- /dev/null +++ b/src/lib/lttng-ctl/Makefile.am @@ -0,0 +1,6 @@ +lib_LTLIBRARIES = liblttng-ctl.la + +liblttng_ctl_la_SOURCES = lttng-ctl.c + +liblttng_ctl_la_LIBADD = \ + $(top_builddir)/src/common/sessiond-comm/libsessiond-comm.la diff --git a/src/lib/lttng-ctl/lttng-ctl.c b/src/lib/lttng-ctl/lttng-ctl.c new file mode 100644 index 000000000..2125c600a --- /dev/null +++ b/src/lib/lttng-ctl/lttng-ctl.c @@ -0,0 +1,906 @@ +/* + * liblttngctl.c + * + * Linux Trace Toolkit Control Library + * + * Copyright (C) 2011 David Goulet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; only + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Socket to session daemon for communication */ +static int sessiond_socket; +static char sessiond_sock_path[PATH_MAX]; + +/* Variables */ +static char *tracing_group; +static int connected; + +/* + * Copy string from src to dst and enforce null terminated byte. + */ +static void copy_string(char *dst, const char *src, size_t len) +{ + if (src && dst) { + strncpy(dst, src, len); + /* Enforce the NULL terminated byte */ + dst[len - 1] = '\0'; + } else if (dst) { + dst[0] = '\0'; + } +} + +/* + * Copy domain to lttcomm_session_msg domain. + * + * If domain is unknown, default domain will be the kernel. + */ +static void copy_lttng_domain(struct lttng_domain *dst, struct lttng_domain *src) +{ + if (src && dst) { + switch (src->type) { + case LTTNG_DOMAIN_KERNEL: + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + memcpy(dst, src, sizeof(struct lttng_domain)); + break; + default: + dst->type = LTTNG_DOMAIN_KERNEL; + break; + } + } +} + +/* + * Send lttcomm_session_msg to the session daemon. + * + * On success, return 0 + * On error, return error code + */ +static int send_session_msg(struct lttcomm_session_msg *lsm) +{ + int ret; + + if (!connected) { + ret = -ENOTCONN; + goto end; + } + + ret = lttcomm_send_creds_unix_sock(sessiond_socket, lsm, + sizeof(struct lttcomm_session_msg)); + +end: + return ret; +} + +/* + * Receive data from the sessiond socket. + * + * On success, return 0 + * On error, return recv() error code + */ +static int recv_data_sessiond(void *buf, size_t len) +{ + int ret; + + if (!connected) { + ret = -ENOTCONN; + goto end; + } + + ret = lttcomm_recv_unix_sock(sessiond_socket, buf, len); + +end: + return ret; +} + +/* + * Check if the specified group name exist. + * + * If yes return 1, else return -1. + */ +static int check_tracing_group(const char *grp_name) +{ + struct group *grp_tracing; /* no free(). See getgrnam(3) */ + gid_t *grp_list; + int grp_list_size, grp_id, i; + int ret = -1; + + /* Get GID of group 'tracing' */ + grp_tracing = getgrnam(grp_name); + if (grp_tracing == NULL) { + /* NULL means not found also. getgrnam(3) */ + if (errno != 0) { + perror("getgrnam"); + } + goto end; + } + + /* Get number of supplementary group IDs */ + grp_list_size = getgroups(0, NULL); + if (grp_list_size < 0) { + perror("getgroups"); + goto end; + } + + /* Alloc group list of the right size */ + grp_list = malloc(grp_list_size * sizeof(gid_t)); + if (!grp_list) { + ret = -1; + goto end; + } + grp_id = getgroups(grp_list_size, grp_list); + if (grp_id < -1) { + perror("getgroups"); + goto free_list; + } + + for (i = 0; i < grp_list_size; i++) { + if (grp_list[i] == grp_tracing->gr_gid) { + ret = 1; + break; + } + } + +free_list: + free(grp_list); + +end: + return ret; +} + +/* + * Try connect to session daemon with sock_path. + * + * Return 0 on success, else -1 + */ +static int try_connect_sessiond(const char *sock_path) +{ + int ret; + + /* If socket exist, we check if the daemon listens for connect. */ + ret = access(sock_path, F_OK); + if (ret < 0) { + /* Not alive */ + return -1; + } + + ret = lttcomm_connect_unix_sock(sock_path); + if (ret < 0) { + /* Not alive */ + return -1; + } + + ret = lttcomm_close_unix_sock(ret); + if (ret < 0) { + perror("lttcomm_close_unix_sock"); + } + + return 0; +} + +/* + * Set sessiond socket path by putting it in the global sessiond_sock_path + * variable. + */ +static int set_session_daemon_path(void) +{ + int ret; + int in_tgroup = 0; /* In tracing group */ + uid_t uid; + + uid = getuid(); + + if (uid != 0) { + /* Are we in the tracing group ? */ + in_tgroup = check_tracing_group(tracing_group); + } + + if (uid == 0) { + /* Root */ + copy_string(sessiond_sock_path, + DEFAULT_GLOBAL_CLIENT_UNIX_SOCK, + sizeof(sessiond_sock_path)); + } else if (in_tgroup) { + /* Tracing group */ + copy_string(sessiond_sock_path, + DEFAULT_GLOBAL_CLIENT_UNIX_SOCK, + sizeof(sessiond_sock_path)); + + ret = try_connect_sessiond(sessiond_sock_path); + if (ret < 0) { + /* Global session daemon not available */ + if (snprintf(sessiond_sock_path, sizeof(sessiond_sock_path), + DEFAULT_HOME_CLIENT_UNIX_SOCK, + getenv("HOME")) < 0) { + return -ENOMEM; + } + } + } else { + /* Not in tracing group and not root, default */ + if (snprintf(sessiond_sock_path, PATH_MAX, + DEFAULT_HOME_CLIENT_UNIX_SOCK, + getenv("HOME")) < 0) { + return -ENOMEM; + } + } + + return 0; +} + +/* + * Connect to the LTTng session daemon. + * + * On success, return 0. On error, return -1. + */ +static int connect_sessiond(void) +{ + int ret; + + ret = set_session_daemon_path(); + if (ret < 0) { + return ret; + } + + /* Connect to the sesssion daemon */ + ret = lttcomm_connect_unix_sock(sessiond_sock_path); + if (ret < 0) { + return ret; + } + + sessiond_socket = ret; + connected = 1; + + return 0; +} + +/* + * Clean disconnect the session daemon. + */ +static int disconnect_sessiond(void) +{ + int ret = 0; + + if (connected) { + ret = lttcomm_close_unix_sock(sessiond_socket); + sessiond_socket = 0; + connected = 0; + } + + return ret; +} + +/* + * Ask the session daemon a specific command and put the data into buf. + * + * Return size of data (only payload, not header). + */ +static int ask_sessiond(struct lttcomm_session_msg *lsm, void **buf) +{ + int ret; + size_t size; + void *data = NULL; + struct lttcomm_lttng_msg llm; + + ret = connect_sessiond(); + if (ret < 0) { + goto end; + } + + /* Send command to session daemon */ + ret = send_session_msg(lsm); + if (ret < 0) { + goto end; + } + + /* Get header from data transmission */ + ret = recv_data_sessiond(&llm, sizeof(llm)); + if (ret < 0) { + goto end; + } + + /* Check error code if OK */ + if (llm.ret_code != LTTCOMM_OK) { + ret = -llm.ret_code; + goto end; + } + + size = llm.data_size; + if (size == 0) { + /* If client free with size 0 */ + if (buf != NULL) { + *buf = NULL; + } + ret = 0; + goto end; + } + + data = (void*) malloc(size); + + /* Get payload data */ + ret = recv_data_sessiond(data, size); + if (ret < 0) { + free(data); + goto end; + } + + /* + * Extra protection not to dereference a NULL pointer. If buf is NULL at + * this point, an error is returned and data is freed. + */ + if (buf == NULL) { + ret = -1; + free(data); + goto end; + } + + *buf = data; + ret = size; + +end: + disconnect_sessiond(); + return ret; +} + +/* + * Create lttng handle and return pointer. + */ +struct lttng_handle *lttng_create_handle(const char *session_name, + struct lttng_domain *domain) +{ + struct lttng_handle *handle; + + handle = malloc(sizeof(struct lttng_handle)); + if (handle == NULL) { + perror("malloc handle"); + goto end; + } + + /* Copy session name */ + copy_string(handle->session_name, session_name, + sizeof(handle->session_name)); + + /* Copy lttng domain */ + copy_lttng_domain(&handle->domain, domain); + +end: + return handle; +} + +/* + * Destroy handle by free(3) the pointer. + */ +void lttng_destroy_handle(struct lttng_handle *handle) +{ + if (handle) { + free(handle); + } +} + +/* + * Register an outside consumer. + */ +int lttng_register_consumer(struct lttng_handle *handle, + const char *socket_path) +{ + struct lttcomm_session_msg lsm; + + lsm.cmd_type = LTTNG_REGISTER_CONSUMER; + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + copy_lttng_domain(&lsm.domain, &handle->domain); + + copy_string(lsm.u.reg.path, socket_path, sizeof(lsm.u.reg.path)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Start tracing for all trace of the session. + */ +int lttng_start_tracing(const char *session_name) +{ + struct lttcomm_session_msg lsm; + + if (session_name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_START_TRACE; + + copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Stop tracing for all trace of the session. + */ +int lttng_stop_tracing(const char *session_name) +{ + struct lttcomm_session_msg lsm; + + if (session_name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_STOP_TRACE; + + copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Add context to event or/and channel. + */ +int lttng_add_context(struct lttng_handle *handle, + struct lttng_event_context *ctx, const char *event_name, + const char *channel_name) +{ + struct lttcomm_session_msg lsm; + + /* Safety check. Both are mandatory */ + if (handle == NULL || ctx == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_ADD_CONTEXT; + + /* Copy channel name */ + copy_string(lsm.u.context.channel_name, channel_name, + sizeof(lsm.u.context.channel_name)); + /* Copy event name */ + copy_string(lsm.u.context.event_name, event_name, + sizeof(lsm.u.context.event_name)); + + copy_lttng_domain(&lsm.domain, &handle->domain); + + memcpy(&lsm.u.context.ctx, ctx, sizeof(struct lttng_event_context)); + + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Enable event + */ +int lttng_enable_event(struct lttng_handle *handle, + struct lttng_event *ev, const char *channel_name) +{ + struct lttcomm_session_msg lsm; + + if (handle == NULL || ev == NULL) { + return -1; + } + + /* If no channel name, we put the default name */ + if (channel_name == NULL) { + copy_string(lsm.u.enable.channel_name, DEFAULT_CHANNEL_NAME, + sizeof(lsm.u.enable.channel_name)); + } else { + copy_string(lsm.u.enable.channel_name, channel_name, + sizeof(lsm.u.enable.channel_name)); + } + + copy_lttng_domain(&lsm.domain, &handle->domain); + + if (ev->name[0] != '\0') { + lsm.cmd_type = LTTNG_ENABLE_EVENT; + } else { + lsm.cmd_type = LTTNG_ENABLE_ALL_EVENT; + } + memcpy(&lsm.u.enable.event, ev, sizeof(lsm.u.enable.event)); + + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Disable event of a channel and domain. + */ +int lttng_disable_event(struct lttng_handle *handle, const char *name, + const char *channel_name) +{ + struct lttcomm_session_msg lsm; + + if (handle == NULL) { + return -1; + } + + if (channel_name) { + copy_string(lsm.u.disable.channel_name, channel_name, + sizeof(lsm.u.disable.channel_name)); + } else { + copy_string(lsm.u.disable.channel_name, DEFAULT_CHANNEL_NAME, + sizeof(lsm.u.disable.channel_name)); + } + + copy_lttng_domain(&lsm.domain, &handle->domain); + + if (name != NULL) { + copy_string(lsm.u.disable.name, name, sizeof(lsm.u.disable.name)); + lsm.cmd_type = LTTNG_DISABLE_EVENT; + } else { + lsm.cmd_type = LTTNG_DISABLE_ALL_EVENT; + } + + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Enable channel per domain + */ +int lttng_enable_channel(struct lttng_handle *handle, + struct lttng_channel *chan) +{ + struct lttcomm_session_msg lsm; + + /* + * NULL arguments are forbidden. No default values. + */ + if (handle == NULL || chan == NULL) { + return -1; + } + + memcpy(&lsm.u.channel.chan, chan, sizeof(lsm.u.channel.chan)); + + lsm.cmd_type = LTTNG_ENABLE_CHANNEL; + + copy_lttng_domain(&lsm.domain, &handle->domain); + + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * All tracing will be stopped for registered events of the channel. + */ +int lttng_disable_channel(struct lttng_handle *handle, const char *name) +{ + struct lttcomm_session_msg lsm; + + /* Safety check. Both are mandatory */ + if (handle == NULL || name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_DISABLE_CHANNEL; + + copy_string(lsm.u.disable.channel_name, name, + sizeof(lsm.u.disable.channel_name)); + + copy_lttng_domain(&lsm.domain, &handle->domain); + + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * List all available tracepoints of domain. + * + * Return the size (bytes) of the list and set the events array. + * On error, return negative value. + */ +int lttng_list_tracepoints(struct lttng_handle *handle, + struct lttng_event **events) +{ + int ret; + struct lttcomm_session_msg lsm; + + if (handle == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_LIST_TRACEPOINTS; + copy_lttng_domain(&lsm.domain, &handle->domain); + + ret = ask_sessiond(&lsm, (void **) events); + if (ret < 0) { + return ret; + } + + return ret / sizeof(struct lttng_event); +} + +/* + * Return a human readable string of code + */ +const char *lttng_strerror(int code) +{ + if (code > -LTTCOMM_OK) { + return "Ended with errors"; + } + + return lttcomm_get_readable_code(code); +} + +/* + * Create a brand new session using name. + */ +int lttng_create_session(const char *name, const char *path) +{ + struct lttcomm_session_msg lsm; + + lsm.cmd_type = LTTNG_CREATE_SESSION; + copy_string(lsm.session.name, name, sizeof(lsm.session.name)); + copy_string(lsm.session.path, path, sizeof(lsm.session.path)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Destroy session using name. + */ +int lttng_destroy_session(const char *session_name) +{ + struct lttcomm_session_msg lsm; + + if (session_name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_DESTROY_SESSION; + + copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Ask the session daemon for all available sessions. + * + * Return number of session. + * On error, return negative value. + */ +int lttng_list_sessions(struct lttng_session **sessions) +{ + int ret; + struct lttcomm_session_msg lsm; + + lsm.cmd_type = LTTNG_LIST_SESSIONS; + ret = ask_sessiond(&lsm, (void**) sessions); + if (ret < 0) { + return ret; + } + + return ret / sizeof(struct lttng_session); +} + +/* + * List domain of a session. + */ +int lttng_list_domains(const char *session_name, + struct lttng_domain **domains) +{ + int ret; + struct lttcomm_session_msg lsm; + + if (session_name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_LIST_DOMAINS; + + copy_string(lsm.session.name, session_name, sizeof(lsm.session.name)); + + ret = ask_sessiond(&lsm, (void**) domains); + if (ret < 0) { + return ret; + } + + return ret / sizeof(struct lttng_domain); +} + +/* + * List channels of a session + */ +int lttng_list_channels(struct lttng_handle *handle, + struct lttng_channel **channels) +{ + int ret; + struct lttcomm_session_msg lsm; + + if (handle == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_LIST_CHANNELS; + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + + copy_lttng_domain(&lsm.domain, &handle->domain); + + ret = ask_sessiond(&lsm, (void**) channels); + if (ret < 0) { + return ret; + } + + return ret / sizeof(struct lttng_channel); +} + +/* + * List events of a session channel. + */ +int lttng_list_events(struct lttng_handle *handle, + const char *channel_name, struct lttng_event **events) +{ + int ret; + struct lttcomm_session_msg lsm; + + /* Safety check. An handle and channel name are mandatory */ + if (handle == NULL || channel_name == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_LIST_EVENTS; + copy_string(lsm.session.name, handle->session_name, + sizeof(lsm.session.name)); + copy_string(lsm.u.list.channel_name, channel_name, + sizeof(lsm.u.list.channel_name)); + + copy_lttng_domain(&lsm.domain, &handle->domain); + + ret = ask_sessiond(&lsm, (void**) events); + if (ret < 0) { + return ret; + } + + return ret / sizeof(struct lttng_event); +} + +/* + * Set tracing group variable with name. This function allocate memory pointed + * by tracing_group. + */ +int lttng_set_tracing_group(const char *name) +{ + if (name == NULL) { + return -1; + } + + if (asprintf(&tracing_group, "%s", name) < 0) { + return -ENOMEM; + } + + return 0; +} + +/* + * lttng_calibrate + */ +int lttng_calibrate(struct lttng_handle *handle, + struct lttng_calibrate *calibrate) +{ + struct lttcomm_session_msg lsm; + + /* Safety check. NULL pointer are forbidden */ + if (handle == NULL || calibrate == NULL) { + return -1; + } + + lsm.cmd_type = LTTNG_CALIBRATE; + copy_lttng_domain(&lsm.domain, &handle->domain); + + memcpy(&lsm.u.calibrate, calibrate, sizeof(lsm.u.calibrate)); + + return ask_sessiond(&lsm, NULL); +} + +/* + * Set default channel attributes. + */ +void lttng_channel_set_default_attr(struct lttng_domain *domain, + struct lttng_channel_attr *attr) +{ + /* Safety check */ + if (attr == NULL || domain == NULL) { + return; + } + + switch (domain->type) { + case LTTNG_DOMAIN_KERNEL: + attr->overwrite = DEFAULT_CHANNEL_OVERWRITE; + attr->switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER; + attr->read_timer_interval = DEFAULT_CHANNEL_READ_TIMER; + + attr->subbuf_size = DEFAULT_KERNEL_CHANNEL_SUBBUF_SIZE; + attr->num_subbuf = DEFAULT_KERNEL_CHANNEL_SUBBUF_NUM; + attr->output = DEFAULT_KERNEL_CHANNEL_OUTPUT; + break; + case LTTNG_DOMAIN_UST: + case LTTNG_DOMAIN_UST_EXEC_NAME: + case LTTNG_DOMAIN_UST_PID: + case LTTNG_DOMAIN_UST_PID_FOLLOW_CHILDREN: + attr->overwrite = DEFAULT_CHANNEL_OVERWRITE; + attr->switch_timer_interval = DEFAULT_CHANNEL_SWITCH_TIMER; + attr->read_timer_interval = DEFAULT_CHANNEL_READ_TIMER; + + attr->subbuf_size = DEFAULT_UST_CHANNEL_SUBBUF_SIZE; + attr->num_subbuf = DEFAULT_UST_CHANNEL_SUBBUF_NUM; + attr->output = DEFAULT_UST_CHANNEL_OUTPUT; + break; + default: + /* Default behavior */ + memset(attr, 0, sizeof(struct lttng_channel_attr)); + break; + } +} + +/* + * Check if session daemon is alive. + * + * Return 1 if alive or 0 if not. + * On error return -1 + */ +int lttng_session_daemon_alive(void) +{ + int ret; + + ret = set_session_daemon_path(); + if (ret < 0) { + /* Error */ + return ret; + } + + if (strlen(sessiond_sock_path) == 0) { + /* No socket path set. Weird error */ + return -1; + } + + ret = try_connect_sessiond(sessiond_sock_path); + if (ret < 0) { + /* Not alive */ + return 0; + } + + /* Is alive */ + return 1; +} + +/* + * lib constructor + */ +static void __attribute__((constructor)) init() +{ + /* Set default session group */ + lttng_set_tracing_group(LTTNG_DEFAULT_TRACING_GROUP); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 8b13eac14..1dd8bbea7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,7 +1,6 @@ SUBDIRS = . -AM_CFLAGS=-I$(top_srcdir)/include -I$(top_srcdir)/libkernelctl \ - -I$(top_srcdir)/liblttngctl -g -Wall -lurcu -lurcu-cds +AM_CFLAGS=-g -Wall -lurcu -lurcu-cds EXTRA_DIST = runall.sh utils.sh lttng/runall.sh lttng/run-kernel-tests.sh @@ -9,18 +8,17 @@ noinst_PROGRAMS = test_sessions test_kernel_data_trace \ kernel_all_events_basic kernel_event_basic UTILS=utils.h -SESSIONS=$(top_srcdir)/lttng-sessiond/session.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 +SESSIONS=$(top_srcdir)/src/bin/lttng-sessiond/session.c +KERN_DATA_TRACE=$(top_srcdir)/src/bin/lttng-sessiond/trace-kernel.c +LIBLTTNG=$(top_srcdir)/src/lib/lttng-ctl/lttng-ctl.c \ + $(top_srcdir)/src/common/sessiond-comm/sessiond-comm.c test_sessions_SOURCES = test_sessions.c $(UTILS) $(SESSIONS) -test_sessions_LDADD = $(top_builddir)/common/libcommon.la \ - $(top_builddir)/liblttng-ht/liblttng-ht.la +test_sessions_LDADD = $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/common/hashtable/libhashtable.la test_kernel_data_trace_SOURCES = test_kernel_data_trace.c $(UTILS) $(KERN_DATA_TRACE) - kernel_all_events_basic_SOURCES = lttng/kernel_all_events_basic.c $(UTILS) $(LIBLTTNG) kernel_event_basic_SOURCES = lttng/kernel_event_basic.c $(UTILS) $(LIBLTTNG) @@ -30,11 +28,11 @@ SUBDIRS += ust-nevents ust-nprocesses EXTRA_DIST += lttng/run-ust-global-tests.sh noinst_PROGRAMS += ust_global_event_basic ust_global_all_events_basic test_ust_data_trace -UST_DATA_TRACE=$(top_srcdir)/lttng-sessiond/trace-ust.c +UST_DATA_TRACE=$(top_srcdir)/src/bin/lttng-sessiond/trace-ust.c test_ust_data_trace_SOURCES = test_ust_data_trace.c $(UTILS) $(UST_DATA_TRACE) -test_ust_data_trace_LDADD = $(top_builddir)/common/libcommon.la \ - $(top_builddir)/liblttng-ht/liblttng-ht.la +test_ust_data_trace_LDADD = $(top_builddir)/src/common/libcommon.la \ + $(top_builddir)/src/common/hashtable/libhashtable.la ust_global_all_events_basic_SOURCES = lttng/ust_global_all_events_basic.c $(UTILS) $(LIBLTTNG) diff --git a/tests/test_kernel_data_trace.c b/tests/test_kernel_data_trace.c index 05fac0949..c079e2c66 100644 --- a/tests/test_kernel_data_trace.c +++ b/tests/test_kernel_data_trace.c @@ -25,7 +25,9 @@ #include #include -#include "lttng-sessiond/trace-kernel.h" +#include +#include + #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 5b17e6391..f444e3043 100644 --- a/tests/test_sessions.c +++ b/tests/test_sessions.c @@ -26,9 +26,9 @@ #include #include -#include +#include +#include -#include #include "utils.h" #define SESSION1 "test1" diff --git a/tests/test_ust_data_trace.c b/tests/test_ust_data_trace.c index 4e4b29b32..e4d42b40a 100644 --- a/tests/test_ust_data_trace.c +++ b/tests/test_ust_data_trace.c @@ -25,10 +25,11 @@ #include #include -#include "lttng/lttng.h" -#include "lttng-sessiond/lttng-ust-abi.h" -#include "lttng-share.h" -#include "lttng-sessiond/trace-ust.h" +#include +#include +#include +#include + #include "utils.h" /* This path will NEVER be created in this test */ @@ -176,7 +177,7 @@ static void create_ust_context(void) PRINT_OK(); printf("Validating UST context: "); - assert(ctx.ctx == uctx->ctx.ctx); + assert((int) ctx.ctx == (int)uctx->ctx.ctx); PRINT_OK(); } diff --git a/tests/ust-nprocesses/run b/tests/ust-nprocesses/run index 4c82eba12..e1f584a2e 100755 --- a/tests/ust-nprocesses/run +++ b/tests/ust-nprocesses/run @@ -45,7 +45,7 @@ done echo -n "Validating registered apps: " -listing=$($TESTDIR/../lttng/$LTTNG_BIN list -u) +listing=$($TESTDIR/../src/bin/lttng/$LTTNG_BIN list -u) reg_app_count=$(echo -n $listing | sed "s/$TEST_BIN_NAME/$TEST_BIN_NAME\n/g" | grep "$TEST_BIN_NAME" | wc -l) if [ "$reg_app_count" -ne "$NR_ITER" ]; then echo -e "$reg_app_count apps listed. Expected $NR_ITER \e[1;31mFAILED\e[0m" diff --git a/tests/utils.sh b/tests/utils.sh index 352a19837..1d8f2ebd0 100644 --- a/tests/utils.sh +++ b/tests/utils.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/src/bin/bash # # Copyright (C) - 2012 David Goulet # @@ -23,7 +23,7 @@ function start_sessiond () { if [ -z $(pidof $SESSIOND_BIN) ]; then echo -n "Starting session daemon... " - $TESTDIR/../lttng-sessiond/$SESSIOND_BIN --daemonize --quiet + $TESTDIR/../src/bin/lttng-sessiond/$SESSIOND_BIN --daemonize --quiet if [ $? -eq 1 ]; then echo -e "\e[1;31mFAILED\e[0m" return 1 @@ -53,7 +53,7 @@ function create_lttng_session () trace_path=$2 echo -n "Creating lttng session $SESSION_NAME in $TRACE_PATH " - $TESTDIR/../lttng/$LTTNG_BIN create $sess_name -o $trace_path >/dev/null 2>&1 + $TESTDIR/../src/bin/lttng/$LTTNG_BIN create $sess_name -o $trace_path >/dev/null 2>&1 if [ $? -eq 1 ]; then echo -e "\e[1;31mFAILED\e[0m" return 1 @@ -69,7 +69,7 @@ function enable_ust_lttng_event () event_name=$2 echo -n "Enabling lttng event $event_name for session $sess_name " - $TESTDIR/../lttng/$LTTNG_BIN enable-event $event_name -s $sess_name -u >/dev/null 2>&1 + $TESTDIR/../src/bin/lttng/$LTTNG_BIN enable-event $event_name -s $sess_name -u >/dev/null 2>&1 if [ $? -eq 1 ]; then echo -e '\e[1;31mFAILED\e[0m' return 1 @@ -83,7 +83,7 @@ function start_tracing () sess_name=$1 echo -n "Start lttng tracing for session $sess_name " - $TESTDIR/../lttng/$LTTNG_BIN start $sess_name >/dev/null 2>&1 + $TESTDIR/../src/bin/lttng/$LTTNG_BIN start $sess_name >/dev/null 2>&1 if [ $? -eq 1 ]; then echo -e '\e[1;31mFAILED\e[0m' return 1 @@ -97,7 +97,7 @@ function stop_tracing () sess_name=$1 echo -n "Stop lttng tracing for session $sess_name " - $TESTDIR/../lttng/$LTTNG_BIN stop $sess_name >/dev/null 2>&1 + $TESTDIR/../src/bin/lttng/$LTTNG_BIN stop $sess_name >/dev/null 2>&1 if [ $? -eq 1 ]; then echo -e '\e[1;31mFAILED\e[0m' return 1 @@ -111,7 +111,7 @@ function destroy_lttng_session () sess_name=$1 echo -n "Destroy lttng session $sess_name " - $TESTDIR/../lttng/$LTTNG_BIN destroy $sess_name >/dev/null 2>&1 + $TESTDIR/../src/bin/lttng/$LTTNG_BIN destroy $sess_name >/dev/null 2>&1 if [ $? -eq 1 ]; then echo -e '\e[1;31mFAILED\e[0m' return 1