!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
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
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-
-AM_CFLAGS = -fno-strict-aliasing
-
-noinst_LTLIBRARIES = libcommon.la
-
-libcommon_la_SOURCES = runas.c runas.h
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sched.h>
-#include <sys/mman.h>
-
-#include <lttngerr.h>
-
-#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);
-}
+++ /dev/null
-#ifndef _RUNAS_H
-#define _RUNAS_H
-
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <unistd.h>
-
-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 */
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"
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
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <urcu.h>
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <lttng/lttng.h>
-#include <lttng-kernel.h>
-
-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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdint.h>
-
-#include <lttng-share.h>
-
-#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 */
+++ /dev/null
-#ifndef _LTTNG_SESSIOND_COMM_H
-#define _LTTNG_SESSIOND_COMM_H
-
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <limits.h>
-#include <lttng/lttng.h>
-#include <sys/socket.h>
-
-#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 <lttng/ust-abi.h>
-
-/*
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <stdlib.h>
-
-#include <lttng/lttng.h>
-
-/* 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <limits.h>
-#include <poll.h>
-#include <unistd.h>
-
-#include <lttng/lttng.h>
-#include <lttng-ht.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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <lttng/lttng-consumer.h>
-
-/*
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <config.h>
-#include <lttng/lttng-consumer.h>
-#include <errno.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <errno.h>
-#include <stdio.h>
-
-/* 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 */
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = libkernelctl.la
-
-libkernelctl_la_SOURCES = kernelctl.c kernel-ioctl.h
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <sys/ioctl.h>
-
-#include <lttng-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);
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <assert.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <lttng-kernel-ctl.h>
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-consumer.h>
-#include <lttng/lttng-kconsumer.h>
-#include <lttng/lttng-ustconsumer.h>
-#include <lttngerr.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);
-}
-
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <assert.h>
-#include <string.h>
-#include <urcu.h>
-#include <urcu/compiler.h>
-
-#include <lttng-ht.h>
-#include <lttng-share.h>
-#include <lttngerr.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);
-}
+++ /dev/null
-#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 <mathieu.desnoyers@efficios.com>
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 */
+++ /dev/null
-/*
- * rculfhash-mm-chunk.c
- *
- * Chunk based memory management for Lock-Free RCU Hash Table
- *
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 <stddef.h>
-#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,
-};
+++ /dev/null
-/*
- * rculfhash-mm-mmap.c
- *
- * mmap/reservation based memory management for Lock-Free RCU Hash Table
- *
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 <unistd.h>
-#include <sys/mman.h>
-#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,
-};
+++ /dev/null
-/*
- * rculfhash-mm-order.c
- *
- * Order based memory management for Lock-Free RCU Hash Table
- *
- * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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,
-};
+++ /dev/null
-/*
- * rculfhash.c
- *
- * Userspace RCU library - Lock-Free Resizable RCU Hash Table
- *
- * Copyright 2010-2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 <stdlib.h>
-#include <errno.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-
-#include "config.h"
-#include <urcu.h>
-#include <urcu-call-rcu.h>
-#include <urcu/arch.h>
-#include <urcu/uatomic.h>
-#include <urcu/compiler.h>
-#include <stdio.h>
-#include <pthread.h>
-
-#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);
-}
+++ /dev/null
-#ifndef _URCU_RCULFHASH_H
-#define _URCU_RCULFHASH_H
-
-/*
- * urcu/rculfhash.h
- *
- * Userspace RCU library - Lock-Free RCU Hash Table
- *
- * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 <stdint.h>
-#include <urcu/compiler.h>
-#include <urcu-call-rcu.h>
-
-#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 */
+++ /dev/null
-#ifndef _URCU_FLAVOR_H
-#define _URCU_FLAVOR_H
-
-/*
- * urcu-flavor.h
- *
- * Userspace RCU header - rcu flavor declarations
- *
- * Copyright (c) 2011 Lai Jiangshan <laijs@cn.fujitsu.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 */
+++ /dev/null
-/*
- * Copyright (C) - Bob Jenkins, May 2006, Public Domain.
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * 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 <assert.h>
-#include <endian.h> /* attempt to define endianness */
-#include <stdint.h> /* defines uint32_t etc */
-#include <stdio.h> /* defines printf for tests */
-#include <string.h>
-#include <sys/param.h> /* attempt to define endianness */
-#include <time.h> /* defines time_t for timings in the test */
-#include <urcu/compiler.h>
-
-#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<n; ++i) h = hashlittle( k[i], len[i], h);
- *
- * By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
- * code any way you wish, private, educational, or commercial. It's free.
- *
- * Use for hash table lookup, or anything where one collision in 2^^32 is
- * acceptable. Do NOT use for cryptographic purposes.
- */
-static uint32_t __attribute__((unused)) hashlittle(const void *key,
- size_t length, uint32_t initval)
-{
- uint32_t a,b,c;
- union {
- const void *ptr;
- size_t i;
- } u; /* needed for Mac Powerbook G4 */
-
- /* Set up the internal state */
- a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
-
- u.ptr = key;
- if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
- const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
-
- /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
- while (length > 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdint.h>
-
-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 */
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <assert.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <lttng-kernel-ctl.h>
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-kconsumer.h>
-#include <lttngerr.h>
-
-#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;
-}
-
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-
-noinst_LTLIBRARIES = liblttng-sessiond-comm.la
-
-liblttng_sessiond_comm_la_SOURCES = lttng-sessiond-comm.c
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <assert.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include <errno.h>
-
-#include <lttng-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;
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <assert.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-ustconsumer.h>
-#include <lttng/ust-ctl.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * liblttngctl.c
- *
- * Linux Trace Toolkit Control Library
- *
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <grp.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttng-sessiond-comm.h>
-#include <lttng-share.h>
-#include <lttng/lttng.h>
-#include <lttngerr.h>
-
-/* 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);
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <fcntl.h>
-#include <getopt.h>
-#include <grp.h>
-#include <limits.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <urcu/list.h>
-#include <poll.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <assert.h>
-#include <config.h>
-#include <urcu/compiler.h>
-
-#include <lttng-consumerd.h>
-#include <lttng-kernel-ctl.h>
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-kconsumer.h>
-#include <lttng/lttng-ustconsumer.h>
-#include <lttngerr.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;
-}
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <string.h>
-#include <unistd.h>
-
-#include <lttng/lttng.h>
-#include <lttng-ht.h>
-#include <lttng-sessiond-comm.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <lttng/lttng.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <fcntl.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <config.h>
-
-#include <lttngerr.h>
-
-#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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdlib.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-
-#include <lttngerr.h>
-
-#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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <string.h>
-#include <unistd.h>
-
-#include <lttng-share.h>
-
-/*
- * 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 <sys/epoll.h>
-
-/* 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 <poll.h>
-#include <stdint.h>
-
-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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <urcu/list.h>
-
-#include <lttng-ht.h>
-#include <lttng-sessiond-comm.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <lttng/lttng.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <errno.h>
-#include <urcu/list.h>
-#include <string.h>
-
-#include <lttng/lttng.h>
-#include <lttng-ht.h>
-#include <lttng-sessiond-comm.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <lttng/lttng.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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <limits.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-#include <urcu.h>
-#include <urcu/futex.h>
-
-#include <lttngerr.h>
-
-#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");
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttng-kernel-ctl.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <urcu.h>
-#include <urcu/wfqueue.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 */
+++ /dev/null
-#ifndef _LTTNG_UST_ABI_H
-#define _LTTNG_UST_ABI_H
-
-/*
- * lttng/ust-abi.h
- *
- * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng-UST ABI header
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <stdint.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <fcntl.h>
-#include <getopt.h>
-#include <grp.h>
-#include <limits.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <urcu/futex.h>
-#include <unistd.h>
-#include <config.h>
-
-#include <lttng-consumerd.h>
-#include <lttng-ht.h>
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-consumer.h>
-
-#include <lttngerr.h>
-#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]);
-
- /* <fun> */
- 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);
- /* </fun> */
-}
-
-/*
- * 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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <urcu.h>
-
-#include <lttng-sessiond-comm.h>
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <pthread.h>
-#include <unistd.h>
-#include <urcu/list.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <fcntl.h>
-#include <limits.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <urcu.h>
-
-#include <lttngerr.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttngerr.h>
-
-#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);
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <limits.h>
-#include <urcu/list.h>
-
-#include <lttng/lttng.h>
-#include <lttng-kernel.h>
-
-/* 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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttngerr.h>
-#include <lttng-ht.h>
-#include <lttng-share.h>
-
-#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();
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <config.h>
-#include <limits.h>
-#include <urcu.h>
-#include <urcu/list.h>
-
-#include <lttng/lttng.h>
-#include <lttng-ht.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <urcu/compiler.h>
-
-#include <lttngerr.h>
-#include <lttng-ht.h>
-#include <lttng-share.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdint.h>
-#include <urcu/list.h>
-
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttngerr.h>
-#include <lttng-ht.h>
-#include <lttng-share.h>
-#include <lttng-sessiond-comm.h>
-#include <lttng/lttng-consumer.h>
-
-#include "ust-consumer.h"
-
-/*
- * Send all stream fds of UST channel to the consumer.
- */
-static int send_channel_streams(int sock,
- struct ust_app_channel *uchan,
- 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
+++ /dev/null
-/*
- * ust-ctl.h
- *
- * Meta header used to include all relevant file from the lttng ust ABI.
- *
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <config.h>
-
-/*
- * 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 <lttng/ust-ctl.h>
-#include <lttng/ust-abi.h>
-#else
-#include "lttng-ust-ctl.h"
-#include "lttng-ust-abi.h"
-#endif
-
-#endif /* _LTT_UST_CTL_H */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License 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 <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <lttngerr.h>
-
-#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"));
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
+++ /dev/null
-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
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <lttng/lttng.h>
-#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 */
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <ctype.h>
-#include <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <urcu/list.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <ctype.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#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 <options>' 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <options>' 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <inttypes.h>
-#include <ctype.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <inttypes.h>
-#include <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#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 [<options>]]]\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 ? : "<all>");
-
- 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 <session_name> 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 <options>' 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <options>' 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <options>' 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <popt.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <config.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#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 <my_sesion>)");
- 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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
+++ /dev/null
-/*
- * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <getopt.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <config.h>
-
-#include <lttng/lttng.h>
-
-#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] <command>\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;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
- *
- * 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 <stdlib.h>
-
-#include <lttng/lttng.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- *
- * 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 */
--- /dev/null
+SUBDIRS = common lib bin
--- /dev/null
+ACLOCAL_AMFLAGS = -I config
+
+SUBDIRS = lttng-consumerd
+
+if ! BUILD_CONSUMERD_ONLY
+SUBDIRS += lttng \
+ lttng-sessiond
+endif
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <urcu/list.h>
+#include <poll.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <config.h>
+#include <urcu/compiler.h>
+
+#include <common/lttngerr.h>
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/kernel-consumer/kernel-consumer.h>
+#include <common/ust-consumer/ust-consumer.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <lttng/lttng.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <config.h>
+
+#include <common/lttngerr.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <lttngerr.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <string.h>
+#include <unistd.h>
+
+#include <common/lttng-share.h>
+
+/*
+ * 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 <sys/epoll.h>
+
+/* 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 <poll.h>
+#include <stdint.h>
+
+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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <urcu/list.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/lttngerr.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <lttng/lttng.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <errno.h>
+#include <urcu/list.h>
+#include <string.h>
+
+#include <lttng/lttng.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/lttngerr.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <limits.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <urcu.h>
+#include <urcu/futex.h>
+
+#include <common/lttngerr.h>
+
+#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");
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <urcu.h>
+#include <urcu/wfqueue.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+
+#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 */
--- /dev/null
+#ifndef _LTTNG_UST_ABI_H
+#define _LTTNG_UST_ABI_H
+
+/*
+ * lttng/ust-abi.h
+ *
+ * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng-UST ABI header
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <stdint.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <fcntl.h>
+#include <getopt.h>
+#include <grp.h>
+#include <limits.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <urcu/futex.h>
+#include <unistd.h>
+#include <config.h>
+
+#include <bin/lttng-consumerd/lttng-consumerd.h>
+#include <common/lttngerr.h>
+#include <common/kernel-consumer/kernel-consumer.h>
+#include <common/ust-consumer/ust-consumer.h>
+#include <common/runas.h>
+
+#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]);
+
+ /* <fun> */
+ 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);
+ /* </fun> */
+}
+
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <urcu.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/runas.h>
+#include <common/lttngerr.h>
+#include <common/lttng-share.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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <pthread.h>
+#include <unistd.h>
+#include <urcu/list.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include <common/lttngerr.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <limits.h>
+#include <urcu/list.h>
+
+#include <lttng/lttng.h>
+#include <common/lttng-kernel.h>
+
+/* 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+
+#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();
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <config.h>
+#include <limits.h>
+#include <urcu.h>
+#include <urcu/list.h>
+
+#include <lttng/lttng.h>
+#include <common/hashtable/hashtable.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <urcu/compiler.h>
+
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdint.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/consumer.h>
+
+#include "ust-consumer.h"
+
+/*
+ * Send all stream fds of UST channel to the consumer.
+ */
+static int send_channel_streams(int sock,
+ struct ust_app_channel *uchan,
+ 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+/*
+ * ust-ctl.h
+ *
+ * Meta header used to include all relevant file from the lttng ust ABI.
+ *
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <config.h>
+
+/*
+ * 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 <lttng/ust-ctl.h>
+#include <lttng/ust-abi.h>
+#else
+#include "lttng-ust-ctl.h"
+#include "lttng-ust-abi.h"
+#endif
+
+#endif /* _LTT_UST_CTL_H */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+
+#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"));
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <lttng/lttng.h>
+#include <common/lttngerr.h>
+#include <common/lttng-share.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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <ctype.h>
+#include <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <urcu/list.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#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 <options>' 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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 <options>' 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <inttypes.h>
+#include <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#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 [<options>]]]\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 ? : "<all>");
+
+ 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 <session_name> 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 <options>' 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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 <options>' 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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 <options>' 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <popt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <config.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+
+#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 <my_sesion>)");
+ 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+/*
+ * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <getopt.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <config.h>
+
+#include <lttng/lttng.h>
+#include <common/lttngerr.h>
+
+#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] <command>\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;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdlib.h>
+
+#include <common/lttngerr.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 */
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <common/lttngerr.h>
+#include <common/lttng-share.h>
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/kernel-consumer/kernel-consumer.h>
+#include <common/ust-consumer/ust-consumer.h>
+
+#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);
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <limits.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <lttng/lttng.h>
+
+#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 */
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <assert.h>
+#include <string.h>
+#include <urcu.h>
+#include <urcu/compiler.h>
+
+#include <common/lttng-share.h>
+#include <common/lttngerr.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <urcu.h>
+
+#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 */
--- /dev/null
+#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 <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 */
--- /dev/null
+/*
+ * rculfhash-mm-chunk.c
+ *
+ * Chunk based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 <stddef.h>
+#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,
+};
--- /dev/null
+/*
+ * rculfhash-mm-mmap.c
+ *
+ * mmap/reservation based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 <unistd.h>
+#include <sys/mman.h>
+#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,
+};
--- /dev/null
+/*
+ * rculfhash-mm-order.c
+ *
+ * Order based memory management for Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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,
+};
--- /dev/null
+/*
+ * rculfhash.c
+ *
+ * Userspace RCU library - Lock-Free Resizable RCU Hash Table
+ *
+ * Copyright 2010-2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "config.h"
+#include <urcu.h>
+#include <urcu-call-rcu.h>
+#include <urcu/arch.h>
+#include <urcu/uatomic.h>
+#include <urcu/compiler.h>
+#include <stdio.h>
+#include <pthread.h>
+
+#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);
+}
--- /dev/null
+#ifndef _URCU_RCULFHASH_H
+#define _URCU_RCULFHASH_H
+
+/*
+ * urcu/rculfhash.h
+ *
+ * Userspace RCU library - Lock-Free RCU Hash Table
+ *
+ * Copyright 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * Copyright 2011 - Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 <stdint.h>
+#include <urcu/compiler.h>
+#include <urcu-call-rcu.h>
+
+#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 */
--- /dev/null
+#ifndef _URCU_FLAVOR_H
+#define _URCU_FLAVOR_H
+
+/*
+ * urcu-flavor.h
+ *
+ * Userspace RCU header - rcu flavor declarations
+ *
+ * Copyright (c) 2011 Lai Jiangshan <laijs@cn.fujitsu.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 */
--- /dev/null
+/*
+ * Copyright (C) - Bob Jenkins, May 2006, Public Domain.
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <assert.h>
+#include <endian.h> /* attempt to define endianness */
+#include <stdint.h> /* defines uint32_t etc */
+#include <stdio.h> /* defines printf for tests */
+#include <string.h>
+#include <sys/param.h> /* attempt to define endianness */
+#include <time.h> /* defines time_t for timings in the test */
+#include <urcu/compiler.h>
+
+#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<n; ++i) h = hashlittle( k[i], len[i], h);
+ *
+ * By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+ * code any way you wish, private, educational, or commercial. It's free.
+ *
+ * Use for hash table lookup, or anything where one collision in 2^^32 is
+ * acceptable. Do NOT use for cryptographic purposes.
+ */
+static uint32_t __attribute__((unused)) hashlittle(const void *key,
+ size_t length, uint32_t initval)
+{
+ uint32_t a,b,c;
+ union {
+ const void *ptr;
+ size_t i;
+ } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdint.h>
+
+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 */
--- /dev/null
+# 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
+
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <common/kernel-ctl/kernel-ctl.h>
+#include <common/lttngerr.h>
+#include <common/runas.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+
+#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;
+}
+
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <common/consumer.h>
+
+/*
+ * 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 */
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+noinst_LTLIBRARIES = libkernel-ctl.la
+
+libkernel_ctl_la_SOURCES = kernel-ctl.c kernel-ioctl.h
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <sys/ioctl.h>
+
+#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);
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <lttng/lttng.h>
+#include <common/lttng-kernel.h>
+
+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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ * David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <stdint.h>
+
+#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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <stdlib.h>
+
+/* 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+
+/* 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 */
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <sys/mman.h>
+
+#include <common/lttngerr.h>
+
+#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);
+}
--- /dev/null
+#ifndef _RUNAS_H
+#define _RUNAS_H
+
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <unistd.h>
+
+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 */
--- /dev/null
+# Session daemon communication lib
+noinst_LTLIBRARIES = libsessiond-comm.la
+
+libsessiond_comm_la_SOURCES = sessiond-comm.c sessiond-comm.h
+
--- /dev/null
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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 <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <errno.h>
+
+#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;
+}
--- /dev/null
+#ifndef _LTTNG_SESSIOND_COMM_H
+#define _LTTNG_SESSIOND_COMM_H
+
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ * Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <limits.h>
+#include <lttng/lttng.h>
+#include <sys/socket.h>
+
+#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 <lttng/ust-abi.h>
+
+/*
+ * 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 */
--- /dev/null
+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
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <assert.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <lttng/ust-ctl.h>
+
+#include <common/lttngerr.h>
+#include <common/runas.h>
+#include <common/sessiond-comm/sessiond-comm.h>
+
+#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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2011 - Julien Desfossez <julien.desfossez@polymtl.ca>
+ * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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 <config.h>
+#include <errno.h>
+
+#include <common/consumer.h>
+
+#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 */
--- /dev/null
+SUBDIRS = lttng-ctl
--- /dev/null
+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
--- /dev/null
+/*
+ * liblttngctl.c
+ *
+ * Linux Trace Toolkit Control Library
+ *
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ *
+ * 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 <grp.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <common/sessiond-comm/sessiond-comm.h>
+#include <common/lttng-share.h>
+#include <common/lttngerr.h>
+#include <lttng/lttng.h>
+
+/* 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);
+}
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
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)
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)
#include <unistd.h>
#include <time.h>
-#include "lttng-sessiond/trace-kernel.h"
+#include <bin/lttng-sessiond/trace-kernel.h>
+#include <common/lttng-share.h>
+
#include "utils.h"
/* This path will NEVER be created in this test */
#include <time.h>
#include <sys/types.h>
-#include <lttng-sessiond-comm.h>
+#include <bin/lttng-sessiond/session.h>
+#include <common/sessiond-comm/sessiond-comm.h>
-#include <lttng-sessiond/session.h>
#include "utils.h"
#define SESSION1 "test1"
#include <unistd.h>
#include <time.h>
-#include "lttng/lttng.h"
-#include "lttng-sessiond/lttng-ust-abi.h"
-#include "lttng-share.h"
-#include "lttng-sessiond/trace-ust.h"
+#include <lttng/lttng.h>
+#include <bin/lttng-sessiond/lttng-ust-abi.h>
+#include <common/lttng-share.h>
+#include <bin/lttng-sessiond/trace-ust.h>
+
#include "utils.h"
/* This path will NEVER be created in this test */
PRINT_OK();
printf("Validating UST context: ");
- assert(ctx.ctx == uctx->ctx.ctx);
+ assert((int) ctx.ctx == (int)uctx->ctx.ctx);
PRINT_OK();
}
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"
-#!/bin/bash
+#!/src/bin/bash
#
# Copyright (C) - 2012 David Goulet <dgoulet@efficios.com>
#
{
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
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
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
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
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
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