ACLOCAL_AMFLAGS = -I config
-SUBDIRS = . snprintf liblttng-ust-comm libringbuffer libust libustctl \
- include doc libustjava libustfork tests
+SUBDIRS = . include doc snprintf libringbuffer liblttng-ust-comm \
+ liblttng-ust \
+ liblttng-ust-ctl \
+ liblttng-ust-fork \
+ liblttng-ust-java \
+ tests
#temporarily disabled
-# . libustinstr-malloc
+# liblttng-ust-malloc
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = ust.pc
AC_CONFIG_FILES([
Makefile
+ include/Makefile
+ include/lttng/version.h
doc/Makefile
doc/man/Makefile
doc/info/Makefile
- include/Makefile
- liblttng-ust-comm/Makefile
+ snprintf/Makefile
libringbuffer/Makefile
- libust/Makefile
- libustctl/Makefile
+ liblttng-ust-comm/Makefile
+ liblttng-ust/Makefile
+ liblttng-ust-ctl/Makefile
+ liblttng-ust-fork/Makefile
+ liblttng-ust-java/Makefile
+ liblttng-ust-malloc/Makefile
tests/Makefile
tests/hello/Makefile
tests/ust-basic-tracing/Makefile
tests/register_test/Makefile
tests/libustctl_function_tests/Makefile
tests/exit-fast/Makefile
- libustinstr-malloc/Makefile
- libustfork/Makefile
- snprintf/Makefile
ust.pc
- include/lttng/version.h
- libustjava/Makefile
])
AC_OUTPUT
/*
* Default timeout the application waits for the sessiond to send its
* "register done" command. Can be overridden with the environment
- * variable "UST_REGISTER_TIMEOUT". Note that if the sessiond is not
+ * variable "LTTNG_UST_REGISTER_TIMEOUT". Note that if the sessiond is not
* found, the application proceeds directly without any delay.
*/
#define LTTNG_UST_DEFAULT_CONSTRUCTOR_TIMEOUT_MS 3000
fflush(stderr); \
} while(0)
-#ifdef UST_DEBUG
+#ifdef LTTNG_UST_DEBUG
# define DBG(fmt, args...) ERRMSG(fmt, ## args)
# define DBG_raw(fmt, args...) \
do { \
__func__); \
} while(0)
-#ifdef UST_DEBUG
+#ifdef LTTNG_UST_DEBUG
# define DBG(fmt, args...) ERRMSG(fmt, ## args)
# define DBG_raw(fmt, args...) \
do { \
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/liblttng-ust-comm
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = liblttng-ust-ctl.la
+
+liblttng_ust_ctl_la_SOURCES = ustctl.c
+
+liblttng_ust_ctl_la_LIBADD = \
+ $(top_builddir)/liblttng-ust-comm/liblttng-ust-comm.la \
+ $(top_builddir)/libringbuffer/libringbuffer.la \
+ $(top_builddir)/snprintf/libustsnprintf.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.
+ */
+
+#include <string.h>
+#include <lttng/ust-ctl.h>
+#include <lttng/ust-abi.h>
+#include <lttng/usterr-signal-safe.h>
+#include <lttng/ust-comm.h>
+
+#include "../libringbuffer/backend.h"
+#include "../libringbuffer/frontend.h"
+
+volatile enum ust_loglevel ust_loglevel;
+
+static
+void init_object(struct lttng_ust_object_data *data)
+{
+ data->handle = -1;
+ data->shm_fd = -1;
+ data->wait_fd = -1;
+ data->memory_map_size = 0;
+}
+
+void release_object(int sock, struct lttng_ust_object_data *data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ if (data->shm_fd >= 0)
+ close(data->shm_fd);
+ if (data->wait_fd >= 0)
+ close(data->wait_fd);
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = data->handle;
+ lum.cmd = LTTNG_UST_RELEASE;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ assert(!ret);
+ free(data);
+}
+
+/*
+ * Send registration done packet to the application.
+ */
+int ustctl_register_done(int sock)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ DBG("Sending register done command to %d", sock);
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = LTTNG_UST_ROOT_HANDLE;
+ lum.cmd = LTTNG_UST_REGISTER_DONE;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ if (lur.ret_code != USTCOMM_OK) {
+ DBG("Return code: %s", ustcomm_get_readable_code(lur.ret_code));
+ goto error;
+ }
+ return 0;
+
+error:
+ return -1;
+}
+
+/*
+ * returns session handle.
+ */
+int ustctl_create_session(int sock)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret, session_handle;
+
+ /* Create session */
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = LTTNG_UST_ROOT_HANDLE;
+ lum.cmd = LTTNG_UST_SESSION;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ session_handle = lur.ret_val;
+ DBG("received session handle %u", session_handle);
+ return session_handle;
+}
+
+/* open the metadata global channel */
+int ustctl_open_metadata(int sock, int session_handle,
+ struct lttng_ust_channel_attr *chops,
+ struct lttng_ust_object_data **_metadata_data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ struct lttng_ust_object_data *metadata_data;
+ int ret;
+
+ metadata_data = malloc(sizeof(*metadata_data));
+ if (!metadata_data)
+ return -ENOMEM;
+ init_object(metadata_data);
+ /* Create metadata channel */
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = session_handle;
+ lum.cmd = LTTNG_UST_METADATA;
+ lum.u.channel.overwrite = chops->overwrite;
+ lum.u.channel.subbuf_size = chops->subbuf_size;
+ lum.u.channel.num_subbuf = chops->num_subbuf;
+ lum.u.channel.switch_timer_interval = chops->switch_timer_interval;
+ lum.u.channel.read_timer_interval = chops->read_timer_interval;
+ lum.u.channel.output = chops->output;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret) {
+ free(metadata_data);
+ return ret;
+ }
+ if (lur.ret_code != USTCOMM_OK) {
+ free(metadata_data);
+ return lur.ret_code;
+ }
+ metadata_data->handle = lur.ret_val;
+ DBG("received metadata handle %u", metadata_data->handle);
+ metadata_data->memory_map_size = lur.u.channel.memory_map_size;
+ /* get shm fd */
+ ret = ustcomm_recv_fd(sock);
+ if (ret < 0)
+ goto error;
+ metadata_data->shm_fd = ret;
+ /* get wait fd */
+ ret = ustcomm_recv_fd(sock);
+ if (ret < 0)
+ goto error;
+ metadata_data->wait_fd = ret;
+ *_metadata_data = metadata_data;
+ return 0;
+
+error:
+ release_object(sock, metadata_data);
+ return -EINVAL;
+}
+
+int ustctl_create_channel(int sock, int session_handle,
+ struct lttng_ust_channel_attr *chops,
+ struct lttng_ust_object_data **_channel_data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ struct lttng_ust_object_data *channel_data;
+ int ret;
+
+ channel_data = malloc(sizeof(*channel_data));
+ if (!channel_data)
+ return -ENOMEM;
+ init_object(channel_data);
+ /* Create metadata channel */
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = session_handle;
+ lum.cmd = LTTNG_UST_CHANNEL;
+ lum.u.channel.overwrite = chops->overwrite;
+ lum.u.channel.subbuf_size = chops->subbuf_size;
+ lum.u.channel.num_subbuf = chops->num_subbuf;
+ lum.u.channel.switch_timer_interval = chops->switch_timer_interval;
+ lum.u.channel.read_timer_interval = chops->read_timer_interval;
+ lum.u.channel.output = chops->output;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret) {
+ free(channel_data);
+ return ret;
+ }
+ if (lur.ret_code != USTCOMM_OK) {
+ free(channel_data);
+ return lur.ret_code;
+ }
+ channel_data->handle = lur.ret_val;
+ DBG("received channel handle %u", channel_data->handle);
+ channel_data->memory_map_size = lur.u.channel.memory_map_size;
+ /* get shm fd */
+ ret = ustcomm_recv_fd(sock);
+ if (ret < 0)
+ goto error;
+ channel_data->shm_fd = ret;
+ /* get wait fd */
+ ret = ustcomm_recv_fd(sock);
+ if (ret < 0)
+ goto error;
+ channel_data->wait_fd = ret;
+ *_channel_data = channel_data;
+ return 0;
+
+error:
+ release_object(sock, channel_data);
+ return -EINVAL;
+}
+
+/*
+ * Return -ENOENT if no more stream is available for creation.
+ * Return 0 on success.
+ * Return negative error value on error.
+ */
+int ustctl_create_stream(int sock, struct lttng_ust_object_data *channel_data,
+ struct lttng_ust_object_data **_stream_data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ struct lttng_ust_object_data *stream_data;
+ int ret, fd;
+
+ stream_data = malloc(sizeof(*stream_data));
+ if (!stream_data)
+ return -ENOMEM;
+ init_object(stream_data);
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = channel_data->handle;
+ lum.cmd = LTTNG_UST_STREAM;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret) {
+ free(stream_data);
+ return ret;
+ }
+ if (lur.ret_code != USTCOMM_OK) {
+ free(stream_data);
+ return lur.ret_code;
+ }
+
+ stream_data->handle = lur.ret_val;
+ DBG("received stream handle %u", stream_data->handle);
+ stream_data->memory_map_size = lur.u.stream.memory_map_size;
+ /* get shm fd */
+ fd = ustcomm_recv_fd(sock);
+ if (fd < 0)
+ goto error;
+ stream_data->shm_fd = fd;
+ /* get wait fd */
+ fd = ustcomm_recv_fd(sock);
+ if (fd < 0)
+ goto error;
+ stream_data->wait_fd = fd;
+ *_stream_data = stream_data;
+ return ret;
+
+error:
+ release_object(sock, stream_data);
+ return -EINVAL;
+}
+
+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)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ struct lttng_ust_object_data *event_data;
+ int ret;
+
+ event_data = malloc(sizeof(*event_data));
+ if (!event_data)
+ return -ENOMEM;
+ init_object(event_data);
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = channel_data->handle;
+ lum.cmd = LTTNG_UST_EVENT;
+ strncpy(lum.u.event.name, ev->name,
+ LTTNG_UST_SYM_NAME_LEN);
+ lum.u.event.instrumentation = ev->instrumentation;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret) {
+ free(event_data);
+ return ret;
+ }
+ event_data->handle = lur.ret_val;
+ DBG("received event handle %u", event_data->handle);
+ *_event_data = event_data;
+ return 0;
+}
+
+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)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ struct lttng_ust_object_data *context_data;
+ int ret;
+
+ context_data = malloc(sizeof(*context_data));
+ if (!context_data)
+ return -ENOMEM;
+ init_object(context_data);
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = obj_data->handle;
+ lum.cmd = LTTNG_UST_CONTEXT;
+ lum.u.context.ctx = ctx->ctx;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret) {
+ free(context_data);
+ return ret;
+ }
+ context_data->handle = lur.ret_val;
+ DBG("received context handle %u", context_data->handle);
+ *_context_data = context_data;
+ return ret;
+}
+
+/* Enable event, channel and session ioctl */
+int ustctl_enable(int sock, struct lttng_ust_object_data *object)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = object->handle;
+ lum.cmd = LTTNG_UST_ENABLE;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ DBG("enabled handle %u", object->handle);
+ return 0;
+}
+
+/* Disable event, channel and session ioctl */
+int ustctl_disable(int sock, struct lttng_ust_object_data *object)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = object->handle;
+ lum.cmd = LTTNG_UST_DISABLE;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ DBG("disable handle %u", object->handle);
+ return 0;
+}
+
+int ustctl_start_session(int sock, int handle)
+{
+ struct lttng_ust_object_data obj;
+
+ obj.handle = handle;
+ return ustctl_enable(sock, &obj);
+}
+
+int ustctl_stop_session(int sock, int handle)
+{
+ struct lttng_ust_object_data obj;
+
+ obj.handle = handle;
+ return ustctl_disable(sock, &obj);
+}
+
+
+int ustctl_tracepoint_list(int sock)
+{
+ return -ENOSYS; /* not implemented */
+}
+
+int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = LTTNG_UST_ROOT_HANDLE;
+ lum.cmd = LTTNG_UST_TRACER_VERSION;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ memcpy(v, &lur.u.version, sizeof(*v));
+ DBG("received tracer version");
+ return 0;
+}
+
+int ustctl_wait_quiescent(int sock)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+ int ret;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = LTTNG_UST_ROOT_HANDLE;
+ lum.cmd = LTTNG_UST_WAIT_QUIESCENT;
+ ret = ustcomm_send_app_cmd(sock, &lum, &lur);
+ if (ret)
+ return ret;
+ DBG("waited for quiescent state");
+ return 0;
+}
+
+int ustctl_flush_buffer(int sock, struct lttng_ust_object_data *channel_data)
+{
+ struct ustcomm_ust_msg lum;
+ struct ustcomm_ust_reply lur;
+
+ memset(&lum, 0, sizeof(lum));
+ lum.handle = channel_data->handle;
+ lum.cmd = LTTNG_UST_FLUSH_BUFFER;
+ return ustcomm_send_app_cmd(sock, &lum, &lur);
+}
+
+int ustctl_calibrate(int sock, struct lttng_ust_calibrate *calibrate)
+{
+ return -ENOSYS;
+}
+
+/* Buffer operations */
+
+/* Map channel shm into process memory */
+struct lttng_ust_shm_handle *ustctl_map_channel(struct lttng_ust_object_data *chan_data)
+{
+ struct lttng_ust_shm_handle *handle;
+ struct channel *chan;
+ size_t chan_size;
+
+ handle = channel_handle_create(chan_data->shm_fd,
+ chan_data->wait_fd,
+ chan_data->memory_map_size);
+ if (!handle) {
+ ERR("create handle error");
+ return NULL;
+ }
+ /*
+ * Set to -1 because the lttng_ust_shm_handle destruction will take care
+ * of closing shm_fd and wait_fd.
+ */
+ chan_data->shm_fd = -1;
+ chan_data->wait_fd = -1;
+
+ /*
+ * TODO: add consistency checks to be resilient if the
+ * application try to feed us with incoherent channel structure
+ * values.
+ */
+ chan = shmp(handle, handle->chan);
+ /* chan is object 0. This is hardcoded. */
+ chan_size = handle->table->objects[0].allocated_len;
+ handle->shadow_chan = malloc(chan_size);
+ if (!handle->shadow_chan) {
+ channel_destroy(chan, handle, 1);
+ return NULL;
+ }
+ memcpy(handle->shadow_chan, chan, chan_size);
+ return handle;
+}
+
+/* Add stream to channel shm and map its shm into process memory */
+int ustctl_add_stream(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_object_data *stream_data)
+{
+ int ret;
+
+ if (!stream_data->handle)
+ return -ENOENT;
+ /* map stream */
+ ret = channel_handle_add_stream(handle,
+ stream_data->shm_fd,
+ stream_data->wait_fd,
+ stream_data->memory_map_size);
+ if (ret) {
+ ERR("add stream error\n");
+ return ret;
+ }
+ /*
+ * Set to -1 because the lttng_ust_shm_handle destruction will take care
+ * of closing shm_fd and wait_fd.
+ */
+ stream_data->shm_fd = -1;
+ stream_data->wait_fd = -1;
+ return 0;
+}
+
+void ustctl_unmap_channel(struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan;
+
+ chan = shmp(handle, handle->chan);
+ channel_destroy(chan, handle, 1);
+}
+
+struct lttng_ust_lib_ring_buffer *ustctl_open_stream_read(struct lttng_ust_shm_handle *handle,
+ int cpu)
+{
+ struct channel *chan = handle->shadow_chan;
+ int shm_fd, wait_fd;
+ uint64_t memory_map_size;
+ struct lttng_ust_lib_ring_buffer *buf;
+ int ret;
+
+ buf = channel_get_ring_buffer(&chan->backend.config,
+ chan, cpu, handle, &shm_fd, &wait_fd, &memory_map_size);
+ if (!buf)
+ return NULL;
+ ret = lib_ring_buffer_open_read(buf, handle, 1);
+ if (ret)
+ return NULL;
+ return buf;
+}
+
+void ustctl_close_stream_read(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ lib_ring_buffer_release_read(buf, handle, 1);
+}
+
+/* For mmap mode, readable without "get" operation */
+
+void *ustctl_get_mmap_base(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ return shmp(handle, buf->backend.memory_map);
+}
+
+/* returns the length to mmap. */
+int ustctl_get_mmap_len(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf,
+ unsigned long *len)
+{
+ unsigned long mmap_buf_len;
+ struct channel *chan = handle->shadow_chan;
+
+ if (chan->backend.config.output != RING_BUFFER_MMAP)
+ return -EINVAL;
+ mmap_buf_len = chan->backend.buf_size;
+ if (chan->backend.extra_reader_sb)
+ mmap_buf_len += chan->backend.subbuf_size;
+ if (mmap_buf_len > INT_MAX)
+ return -EFBIG;
+ *len = mmap_buf_len;
+ return 0;
+}
+
+/* returns the maximum size for sub-buffers. */
+int ustctl_get_max_subbuf_size(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf,
+ unsigned long *len)
+{
+ struct channel *chan = handle->shadow_chan;
+
+ *len = chan->backend.subbuf_size;
+ return 0;
+}
+
+/*
+ * 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 ustctl_get_mmap_read_offset(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *off)
+{
+ struct channel *chan = handle->shadow_chan;
+ unsigned long sb_bindex;
+
+ if (chan->backend.config.output != RING_BUFFER_MMAP)
+ return -EINVAL;
+ sb_bindex = subbuffer_id_get_index(&chan->backend.config,
+ buf->backend.buf_rsb.id);
+ *off = shmp(handle, shmp_index(handle, buf->backend.array, sb_bindex)->shmp)->mmap_offset;
+ return 0;
+}
+
+/* returns the size of the current sub-buffer, without padding (for mmap). */
+int ustctl_get_subbuf_size(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *len)
+{
+ struct channel *chan = handle->shadow_chan;
+
+ *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
+ handle);
+ return 0;
+}
+
+/* returns the size of the current sub-buffer, without padding (for mmap). */
+int ustctl_get_padded_subbuf_size(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *len)
+{
+ struct channel *chan = handle->shadow_chan;
+
+ *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
+ handle);
+ *len = PAGE_ALIGN(*len);
+ return 0;
+}
+
+/* Get exclusive read access to the next sub-buffer that can be read. */
+int ustctl_get_next_subbuf(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ return lib_ring_buffer_get_next_subbuf(buf, handle);
+}
+
+
+/* Release exclusive sub-buffer access, move consumer forward. */
+int ustctl_put_next_subbuf(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ lib_ring_buffer_put_next_subbuf(buf, handle);
+ return 0;
+}
+
+/* snapshot */
+
+/* Get a snapshot of the current ring buffer producer and consumer positions */
+int ustctl_snapshot(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ return lib_ring_buffer_snapshot(buf, &buf->cons_snapshot,
+ &buf->prod_snapshot, handle);
+}
+
+/* Get the consumer position (iteration start) */
+int ustctl_snapshot_get_consumed(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
+{
+ *pos = buf->cons_snapshot;
+ return 0;
+}
+
+/* Get the producer position (iteration end) */
+int ustctl_snapshot_get_produced(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
+{
+ *pos = buf->prod_snapshot;
+ return 0;
+}
+
+/* Get exclusive read access to the specified sub-buffer position */
+int ustctl_get_subbuf(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
+{
+ return lib_ring_buffer_get_subbuf(buf, *pos, handle);
+}
+
+/* Release exclusive sub-buffer access */
+int ustctl_put_subbuf(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ lib_ring_buffer_put_subbuf(buf, handle);
+ return 0;
+}
+
+int ustctl_buffer_flush(struct lttng_ust_shm_handle *handle,
+ struct lttng_ust_lib_ring_buffer *buf)
+{
+ lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE, handle);
+ return 0;
+}
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = liblttng-ust-fork.la
+liblttng_ust_fork_la_SOURCES = ustfork.c
+liblttng_ust_fork_la_LIBADD = \
+ -ldl \
+ $(top_builddir)/liblttng-ust/liblttng-ust.la
+libustfork_CFLAGS = -DUST_COMPONENT=liblttng-ust-fork -fno-strict-aliasing
--- /dev/null
+/*
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1 of
+ * the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sched.h>
+#include <stdarg.h>
+#include "usterr.h"
+
+#include <lttng/ust.h>
+
+struct user_desc;
+
+pid_t fork(void)
+{
+ static pid_t (*plibc_func)(void) = NULL;
+ ust_fork_info_t fork_info;
+ pid_t retval;
+
+ if (plibc_func == NULL) {
+ plibc_func = dlsym(RTLD_NEXT, "fork");
+ if (plibc_func == NULL) {
+ fprintf(stderr, "libustfork: unable to find \"fork\" symbol\n");
+ return -1;
+ }
+ }
+
+ ust_before_fork(&fork_info);
+ /* Do the real fork */
+ retval = plibc_func();
+ if (retval == 0) {
+ /* child */
+ ust_after_fork_child(&fork_info);
+ } else {
+ ust_after_fork_parent(&fork_info);
+ }
+ return retval;
+}
+
+struct ustfork_clone_info {
+ int (*fn)(void *);
+ void *arg;
+ ust_fork_info_t fork_info;
+};
+
+static int clone_fn(void *arg)
+{
+ struct ustfork_clone_info *info = (struct ustfork_clone_info *) arg;
+
+ /* clone is now done and we are in child */
+ ust_after_fork_child(&info->fork_info);
+ return info->fn(info->arg);
+}
+
+int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
+{
+ static int (*plibc_func)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, pid_t *ptid,
+ struct user_desc *tls, pid_t *ctid) = NULL;
+ /* var args */
+ pid_t *ptid;
+ struct user_desc *tls;
+ pid_t *ctid;
+ /* end of var args */
+ va_list ap;
+ int retval;
+
+ va_start(ap, arg);
+ ptid = va_arg(ap, pid_t *);
+ tls = va_arg(ap, struct user_desc *);
+ ctid = va_arg(ap, pid_t *);
+ va_end(ap);
+
+ if (plibc_func == NULL) {
+ plibc_func = dlsym(RTLD_NEXT, "clone");
+ if (plibc_func == NULL) {
+ fprintf(stderr, "libustfork: unable to find \"clone\" symbol.\n");
+ return -1;
+ }
+ }
+
+ if (flags & CLONE_VM) {
+ /*
+ * Creating a thread, no need to intervene, just pass on
+ * the arguments.
+ */
+ retval = plibc_func(fn, child_stack, flags, arg, ptid,
+ tls, ctid);
+ } else {
+ /* Creating a real process, we need to intervene. */
+ struct ustfork_clone_info info = { fn: fn, arg: arg };
+
+ ust_before_fork(&info.fork_info);
+ retval = plibc_func(clone_fn, child_stack, flags, &info,
+ ptid, tls, ctid);
+ /* The child doesn't get here. */
+ ust_after_fork_parent(&info.fork_info);
+ }
+ return retval;
+}
--- /dev/null
+UST.class
+UST.h
--- /dev/null
+if BUILD_JNI_INTERFACE
+
+AM_CPPFLAGS = -I$(top_srcdir)/include
+
+lib_LTLIBRARIES = liblttng-ust-java.la
+liblttng_ust_java_la_SOURCES = UST.c UST.h ust_java.h
+dist_noinst_DATA = UST.java
+liblttng_ust_java_la_LIBADD = -lc -L$(top_builddir)/liblttng-ust/.libs -llttng-ust
+
+all: UST.class UST.h
+
+clean-local:
+ rm -rf UST.h UST.class
+
+UST.class: UST.java
+ javac -d "$(builddir)" "$(srcdir)/UST.java"
+
+UST.h: UST.class
+ javah -jni UST
+
+endif
--- /dev/null
+This directory contains a simple API for instrumenting java applications.
+
+Configuration examples to build this library:
+
+dependency: sun-java6-jdk
+./configure --with-java-jdk=/usr/lib/jvm/java-6-sun --with-jni-interface
+
+dependency: openjdk-6-jdk
+./configure --with-java-jdk=/usr/lib/jvm/java-6-openjdk --with-jni-interface
+
+dependency: gcj-4.4-jdk
+./configure --with-java-jdk=/usr/lib/jvm/java-gcj --with-jni-interface
--- /dev/null
+#include <jni.h>
+
+#define TRACEPOINT_CREATE_PROBES
+#include "ust_java.h"
+
+JNIEXPORT void JNICALL Java_UST_ust_1java_1event (JNIEnv *env, jobject jobj,
+ jstring ev_name, jstring args)
+{
+ jboolean iscopy;
+ const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name,
+ &iscopy);
+ const char *args_cstr = (*env)->GetStringUTFChars(env, args, &iscopy);
+
+ tracepoint(ust_java_event, ev_name_cstr, args_cstr);
+}
--- /dev/null
+import java.util.*;
+
+class UST {
+ public static native void ust_java_event(String name, String arg);
+ static {
+ System.loadLibrary("ustjava");
+ }
+}
+
--- /dev/null
+#undef TRACEPOINT_SYSTEM
+#define TRACEPOINT_SYSTEM ust_java
+
+#if !defined(_TRACEPOINT_UST_JAVA_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_UST_JAVA_H
+
+/*
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1 of
+ * the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <ust/tracepoint.h>
+
+TRACEPOINT_EVENT(ust_java_event,
+ TP_PROTO(const char *name, const char *args),
+ TP_ARGS(name, args),
+ TP_FIELDS(
+ ctf_string(name, name)
+ ctf_string(args, args)
+ )
+)
+
+#endif /* _TRACEPOINT_UST_JAVA_H */
+
+#undef TRACEPOINT_INCLUDE_PATH
+#define TRACEPOINT_INCLUDE_PATH .
+#undef TRACEPOINT_INCLUDE_FILE
+#define TRACEPOINT_INCLUDE_FILE ust_java
+
+/* This part must be outside protection */
+#include <ust/tracepoint-event.h>
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = liblttng-ust-malloc.la
+liblttng_ust_malloc_la_SOURCES = mallocwrap.c
+liblttng_ust_malloc_la_LIBADD = -ldl
+
+noinst_SCRIPTS = run
+EXTRA_DIST = run
--- /dev/null
+libustinstr-malloc is used for instrumenting calls to malloc(3) in a program,
+without need for recompiling it.
+
+libustinstr-malloc defines a malloc() function that is instrumented with a
+marker. It also calls the libc malloc afterwards. When loaded with LD_PRELOAD,
+it replaces the libc malloc() function, in effect instrumenting all calls to
+malloc().
+
+See the "run" script for a usage example.
--- /dev/null
+/*
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ *
+ * 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
+ */
+
+#define _GNU_SOURCE
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <ust/marker.h>
+
+void *malloc(size_t size)
+{
+ static void *(*plibc_malloc)(size_t size) = NULL;
+ void *retval;
+
+ if (plibc_malloc == NULL) {
+ plibc_malloc = dlsym(RTLD_NEXT, "malloc");
+ if (plibc_malloc == NULL) {
+ fprintf(stderr, "mallocwrap: unable to find malloc\n");
+ return NULL;
+ }
+ }
+
+ retval = plibc_malloc(size);
+
+ ust_marker(malloc, "size %d ptr %p", (int)size, retval);
+
+ return retval;
+}
+
+void free(void *ptr)
+{
+ static void *(*plibc_free)(void *ptr) = NULL;
+
+ if (plibc_free == NULL) {
+ plibc_free = dlsym(RTLD_NEXT, "free");
+ if (plibc_free == NULL) {
+ fprintf(stderr, "mallocwrap: unable to find free\n");
+ return;
+ }
+ }
+
+ ust_marker(free, "ptr %p", ptr);
+
+ plibc_free(ptr);
+}
+
+UST_MARKER_LIB
--- /dev/null
+#!/bin/sh
+
+LD_VERBOSE=1 LD_LIBRARY_PATH=.:../libust/.libs:../../liburcu LD_PRELOAD=liburcu.so:libust.so:.libs/libmallocwrap.so $1
--- /dev/null
+AM_CPPFLAGS = -I$(top_srcdir)/include
+AM_CFLAGS = -fno-strict-aliasing
+
+lib_LTLIBRARIES = liblttng-ust.la
+
+liblttng_ust_la_SOURCES = \
+ tracepoint.c \
+ ltt-tracer.h \
+ ltt-tracer-core.h \
+ ltt-ring-buffer-client.h \
+ ltt-ring-buffer-client-discard.c \
+ ltt-ring-buffer-client-overwrite.c \
+ ltt-ring-buffer-metadata-client.h \
+ ltt-ring-buffer-metadata-client.c \
+ ltt-events.c \
+ ltt-probes.c \
+ lttng-ust-abi.c \
+ lttng-ust-comm.c \
+ ust-core.c \
+ probes/lttng-probe-ust.c \
+ probes/lttng-probe-ust.h \
+ lttng-context-vtid.c \
+ lttng-context-vpid.c \
+ lttng-context-pthread-id.c \
+ lttng-context-procname.c \
+ ltt-context.c
+
+liblttng_ust_la_LDFLAGS = -no-undefined -version-info 0:0:0
+
+liblttng_ust_la_LIBADD = \
+ -lpthread \
+ -lrt \
+ -luuid \
+ $(top_builddir)/snprintf/libustsnprintf.la \
+ $(top_builddir)/libringbuffer/libringbuffer.la \
+ $(top_builddir)/liblttng-ust-comm/liblttng-ust-comm.la
+
+liblttng_ust_la_CFLAGS = -DUST_COMPONENT="liblttng-ust" -fno-strict-aliasing
--- /dev/null
+/*
+ * ltt-context.c
+ *
+ * Copyright 2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST trace/channel/event context management.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <lttng/ust-events.h>
+#include <lttng/ust-tracer.h>
+#include <lttng/core.h>
+#include <string.h>
+#include <assert.h>
+
+int lttng_find_context(struct lttng_ctx *ctx, const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->nr_fields; i++) {
+ /* Skip allocated (but non-initialized) contexts */
+ if (!ctx->fields[i].event_field.name)
+ continue;
+ if (!strcmp(ctx->fields[i].event_field.name, name))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Note: as we append context information, the pointer location may change.
+ */
+struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx_p)
+{
+ struct lttng_ctx_field *field;
+ struct lttng_ctx *ctx;
+
+ if (!*ctx_p) {
+ *ctx_p = zmalloc(sizeof(struct lttng_ctx));
+ if (!*ctx_p)
+ return NULL;
+ }
+ ctx = *ctx_p;
+ if (ctx->nr_fields + 1 > ctx->allocated_fields) {
+ struct lttng_ctx_field *new_fields;
+
+ ctx->allocated_fields = max_t(size_t, 1, 2 * ctx->allocated_fields);
+ new_fields = zmalloc(ctx->allocated_fields * sizeof(struct lttng_ctx_field));
+ if (!new_fields)
+ return NULL;
+ if (ctx->fields)
+ memcpy(new_fields, ctx->fields, sizeof(*ctx->fields) * ctx->nr_fields);
+ free(ctx->fields);
+ ctx->fields = new_fields;
+ }
+ field = &ctx->fields[ctx->nr_fields];
+ ctx->nr_fields++;
+ return field;
+}
+
+/*
+ * Remove last context field.
+ */
+void lttng_remove_context_field(struct lttng_ctx **ctx_p,
+ struct lttng_ctx_field *field)
+{
+ struct lttng_ctx *ctx;
+
+ ctx = *ctx_p;
+ ctx->nr_fields--;
+ assert(&ctx->fields[ctx->nr_fields] == field);
+ memset(&ctx->fields[ctx->nr_fields], 0, sizeof(struct lttng_ctx_field));
+}
+
+void lttng_destroy_context(struct lttng_ctx *ctx)
+{
+ int i;
+
+ if (!ctx)
+ return;
+ for (i = 0; i < ctx->nr_fields; i++) {
+ if (ctx->fields[i].destroy)
+ ctx->fields[i].destroy(&ctx->fields[i]);
+ }
+ free(ctx->fields);
+ free(ctx);
+}
--- /dev/null
+/*
+ * ltt-events.c
+ *
+ * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Holds LTTng per-session event registry.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <endian.h>
+#include <urcu/list.h>
+#include <urcu/hlist.h>
+#include <pthread.h>
+#include <urcu-bp.h>
+#include <urcu/compiler.h>
+#include <urcu/uatomic.h>
+#include <uuid/uuid.h>
+#include <lttng/tracepoint.h>
+#include <errno.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+#include <lttng/ust-events.h>
+#include <lttng/usterr-signal-safe.h>
+#include "lttng/core.h"
+#include "ltt-tracer.h"
+#include "ltt-tracer-core.h"
+#include "lttng/wait.h"
+#include "../libringbuffer/shm.h"
+
+typedef u32 uint32_t;
+#include <lttng/kcompat/jhash.h>
+
+/*
+ * The sessions mutex is the centralized mutex across UST tracing
+ * control and probe registration. All operations within this file are
+ * called by the communication thread, under ust_lock protection.
+ */
+static DEFINE_MUTEX(sessions_mutex);
+
+void ust_lock(void)
+{
+ pthread_mutex_lock(&sessions_mutex);
+}
+
+void ust_unlock(void)
+{
+ pthread_mutex_unlock(&sessions_mutex);
+}
+
+static CDS_LIST_HEAD(sessions);
+static CDS_LIST_HEAD(ltt_transport_list);
+
+/*
+ * Pending probes hash table, containing the registered ltt events for
+ * which tracepoint probes are still missing. Protected by the sessions
+ * mutex.
+ */
+#define PENDING_PROBE_HASH_BITS 6
+#define PENDING_PROBE_HASH_SIZE (1 << PENDING_PROBE_HASH_BITS)
+static struct cds_hlist_head pending_probe_table[PENDING_PROBE_HASH_SIZE];
+
+struct ust_pending_probe {
+ struct ltt_event *event;
+ struct cds_hlist_node node;
+ char name[];
+};
+
+static void _ltt_event_destroy(struct ltt_event *event);
+static void _ltt_channel_destroy(struct ltt_channel *chan);
+static int _ltt_event_unregister(struct ltt_event *event);
+static
+int _ltt_event_metadata_statedump(struct ltt_session *session,
+ struct ltt_channel *chan,
+ struct ltt_event *event);
+static
+int _ltt_session_metadata_statedump(struct ltt_session *session);
+
+/*
+ * called at event creation if probe is missing.
+ * called with session mutex held.
+ */
+static
+int add_pending_probe(struct ltt_event *event, const char *name)
+{
+ struct cds_hlist_head *head;
+ struct ust_pending_probe *e;
+ size_t name_len = strlen(name) + 1;
+ u32 hash = jhash(name, name_len - 1, 0);
+
+ head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
+ e = zmalloc(sizeof(struct ust_pending_probe) + name_len);
+ if (!e)
+ return -ENOMEM;
+ memcpy(&e->name[0], name, name_len);
+ cds_hlist_add_head(&e->node, head);
+ e->event = event;
+ event->pending_probe = e;
+ return 0;
+}
+
+/*
+ * remove a pending probe. called when at event teardown and when an
+ * event is fixed (probe is loaded).
+ * called with session mutex held.
+ */
+static
+void remove_pending_probe(struct ust_pending_probe *e)
+{
+ if (!e)
+ return;
+ cds_hlist_del(&e->node);
+ free(e);
+}
+
+/*
+ * Called at library load: connect the probe on the events pending on
+ * probe load.
+ * called with session mutex held.
+ */
+int pending_probe_fix_events(const struct lttng_event_desc *desc)
+{
+ struct cds_hlist_head *head;
+ struct cds_hlist_node *node, *p;
+ struct ust_pending_probe *e;
+ const char *name = desc->name;
+ size_t name_len = strlen(name) + 1;
+ u32 hash = jhash(name, name_len - 1, 0);
+ int ret = 0;
+
+ head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
+ cds_hlist_for_each_entry_safe(e, node, p, head, node) {
+ struct ltt_event *event;
+ struct ltt_channel *chan;
+
+ if (strcmp(name, e->name))
+ continue;
+ event = e->event;
+ chan = event->chan;
+ assert(!event->desc);
+ event->desc = desc;
+ event->pending_probe = NULL;
+ remove_pending_probe(e);
+ ret |= __tracepoint_probe_register(name,
+ event->desc->probe_callback,
+ event);
+ if (ret)
+ continue;
+ event->id = chan->free_event_id++;
+ ret |= _ltt_event_metadata_statedump(chan->session, chan,
+ event);
+ }
+ return ret;
+}
+
+void synchronize_trace(void)
+{
+ synchronize_rcu();
+}
+
+struct ltt_session *ltt_session_create(void)
+{
+ struct ltt_session *session;
+
+ session = zmalloc(sizeof(struct ltt_session));
+ if (!session)
+ return NULL;
+ CDS_INIT_LIST_HEAD(&session->chan);
+ CDS_INIT_LIST_HEAD(&session->events);
+ uuid_generate(session->uuid);
+ cds_list_add(&session->list, &sessions);
+ return session;
+}
+
+void ltt_session_destroy(struct ltt_session *session)
+{
+ struct ltt_channel *chan, *tmpchan;
+ struct ltt_event *event, *tmpevent;
+ int ret;
+
+ CMM_ACCESS_ONCE(session->active) = 0;
+ cds_list_for_each_entry(event, &session->events, list) {
+ ret = _ltt_event_unregister(event);
+ WARN_ON(ret);
+ }
+ synchronize_trace(); /* Wait for in-flight events to complete */
+ cds_list_for_each_entry_safe(event, tmpevent, &session->events, list)
+ _ltt_event_destroy(event);
+ cds_list_for_each_entry_safe(chan, tmpchan, &session->chan, list)
+ _ltt_channel_destroy(chan);
+ cds_list_del(&session->list);
+ free(session);
+}
+
+int ltt_session_enable(struct ltt_session *session)
+{
+ int ret = 0;
+ struct ltt_channel *chan;
+
+ if (session->active) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ /*
+ * Snapshot the number of events per channel to know the type of header
+ * we need to use.
+ */
+ cds_list_for_each_entry(chan, &session->chan, list) {
+ if (chan->header_type)
+ continue; /* don't change it if session stop/restart */
+ if (chan->free_event_id < 31)
+ chan->header_type = 1; /* compact */
+ else
+ chan->header_type = 2; /* large */
+ }
+
+ CMM_ACCESS_ONCE(session->active) = 1;
+ CMM_ACCESS_ONCE(session->been_active) = 1;
+ ret = _ltt_session_metadata_statedump(session);
+ if (ret)
+ CMM_ACCESS_ONCE(session->active) = 0;
+end:
+ return ret;
+}
+
+int ltt_session_disable(struct ltt_session *session)
+{
+ int ret = 0;
+
+ if (!session->active) {
+ ret = -EBUSY;
+ goto end;
+ }
+ CMM_ACCESS_ONCE(session->active) = 0;
+end:
+ return ret;
+}
+
+int ltt_channel_enable(struct ltt_channel *channel)
+{
+ int old;
+
+ if (channel == channel->session->metadata)
+ return -EPERM;
+ old = uatomic_xchg(&channel->enabled, 1);
+ if (old)
+ return -EEXIST;
+ return 0;
+}
+
+int ltt_channel_disable(struct ltt_channel *channel)
+{
+ int old;
+
+ if (channel == channel->session->metadata)
+ return -EPERM;
+ old = uatomic_xchg(&channel->enabled, 0);
+ if (!old)
+ return -EEXIST;
+ return 0;
+}
+
+int ltt_event_enable(struct ltt_event *event)
+{
+ int old;
+
+ if (event->chan == event->chan->session->metadata)
+ return -EPERM;
+ old = uatomic_xchg(&event->enabled, 1);
+ if (old)
+ return -EEXIST;
+ return 0;
+}
+
+int ltt_event_disable(struct ltt_event *event)
+{
+ int old;
+
+ if (event->chan == event->chan->session->metadata)
+ return -EPERM;
+ old = uatomic_xchg(&event->enabled, 0);
+ if (!old)
+ return -EEXIST;
+ return 0;
+}
+
+static struct ltt_transport *ltt_transport_find(const char *name)
+{
+ struct ltt_transport *transport;
+
+ cds_list_for_each_entry(transport, <t_transport_list, node) {
+ if (!strcmp(transport->name, name))
+ return transport;
+ }
+ return NULL;
+}
+
+struct ltt_channel *ltt_channel_create(struct ltt_session *session,
+ const char *transport_name,
+ void *buf_addr,
+ size_t subbuf_size, size_t num_subbuf,
+ unsigned int switch_timer_interval,
+ unsigned int read_timer_interval,
+ int *shm_fd, int *wait_fd,
+ uint64_t *memory_map_size)
+{
+ struct ltt_channel *chan;
+ struct ltt_transport *transport;
+
+ if (session->been_active)
+ goto active; /* Refuse to add channel to active session */
+ transport = ltt_transport_find(transport_name);
+ if (!transport) {
+ DBG("LTTng transport %s not found\n",
+ transport_name);
+ goto notransport;
+ }
+ chan = zmalloc(sizeof(struct ltt_channel));
+ if (!chan)
+ goto nomem;
+ chan->session = session;
+ chan->id = session->free_chan_id++;
+ /*
+ * Note: the channel creation op already writes into the packet
+ * headers. Therefore the "chan" information used as input
+ * should be already accessible.
+ */
+ transport->ops.channel_create("[lttng]", chan, buf_addr,
+ subbuf_size, num_subbuf, switch_timer_interval,
+ read_timer_interval, shm_fd, wait_fd,
+ memory_map_size);
+ if (!chan->chan)
+ goto create_error;
+ chan->enabled = 1;
+ chan->ops = &transport->ops;
+ cds_list_add(&chan->list, &session->chan);
+ return chan;
+
+create_error:
+ free(chan);
+nomem:
+notransport:
+active:
+ return NULL;
+}
+
+/*
+ * Only used internally at session destruction.
+ */
+static
+void _ltt_channel_destroy(struct ltt_channel *chan)
+{
+ chan->ops->channel_destroy(chan);
+ cds_list_del(&chan->list);
+ lttng_destroy_context(chan->ctx);
+ free(chan);
+}
+
+/*
+ * Supports event creation while tracing session is active.
+ */
+struct ltt_event *ltt_event_create(struct ltt_channel *chan,
+ struct lttng_ust_event *event_param,
+ void *filter)
+{
+ struct ltt_event *event;
+ int ret;
+
+ if (chan->used_event_id == -1UL)
+ goto full;
+ /*
+ * This is O(n^2) (for each event, the loop is called at event
+ * creation). Might require a hash if we have lots of events.
+ */
+ cds_list_for_each_entry(event, &chan->session->events, list)
+ if (event->desc && !strcmp(event->desc->name, event_param->name))
+ goto exist;
+ event = zmalloc(sizeof(struct ltt_event));
+ if (!event)
+ goto cache_error;
+ event->chan = chan;
+ event->filter = filter;
+ /*
+ * used_event_id counts the maximum number of event IDs that can
+ * register if all probes register.
+ */
+ chan->used_event_id++;
+ event->enabled = 1;
+ event->instrumentation = event_param->instrumentation;
+ /* Populate ltt_event structure before tracepoint registration. */
+ cmm_smp_wmb();
+ switch (event_param->instrumentation) {
+ case LTTNG_UST_TRACEPOINT:
+ event->desc = ltt_event_get(event_param->name);
+ if (event->desc) {
+ ret = __tracepoint_probe_register(event_param->name,
+ event->desc->probe_callback,
+ event);
+ if (ret)
+ goto register_error;
+ event->id = chan->free_event_id++;
+ } else {
+ /*
+ * If the probe is not present, event->desc stays NULL,
+ * waiting for the probe to register, and the event->id
+ * stays unallocated.
+ */
+ ret = add_pending_probe(event, event_param->name);
+ if (ret)
+ goto add_pending_error;
+ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ if (event->desc) {
+ ret = _ltt_event_metadata_statedump(chan->session, chan, event);
+ if (ret)
+ goto statedump_error;
+ }
+ cds_list_add(&event->list, &chan->session->events);
+ return event;
+
+statedump_error:
+ if (event->desc) {
+ WARN_ON_ONCE(__tracepoint_probe_unregister(event_param->name,
+ event->desc->probe_callback,
+ event));
+ ltt_event_put(event->desc);
+ }
+add_pending_error:
+register_error:
+ free(event);
+cache_error:
+exist:
+full:
+ return NULL;
+}
+
+/*
+ * Only used internally at session destruction.
+ */
+int _ltt_event_unregister(struct ltt_event *event)
+{
+ int ret = -EINVAL;
+
+ switch (event->instrumentation) {
+ case LTTNG_UST_TRACEPOINT:
+ if (event->desc) {
+ ret = __tracepoint_probe_unregister(event->desc->name,
+ event->desc->probe_callback,
+ event);
+ if (ret)
+ return ret;
+ } else {
+ remove_pending_probe(event->pending_probe);
+ ret = 0;
+ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ return ret;
+}
+
+/*
+ * Only used internally at session destruction.
+ */
+static
+void _ltt_event_destroy(struct ltt_event *event)
+{
+ switch (event->instrumentation) {
+ case LTTNG_UST_TRACEPOINT:
+ if (event->desc) {
+ ltt_event_put(event->desc);
+ }
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+ cds_list_del(&event->list);
+ lttng_destroy_context(event->ctx);
+ free(event);
+}
+
+/*
+ * We have exclusive access to our metadata buffer (protected by the
+ * ust_lock), so we can do racy operations such as looking for
+ * remaining space left in packet and write, since mutual exclusion
+ * protects us from concurrent writes.
+ */
+int lttng_metadata_printf(struct ltt_session *session,
+ const char *fmt, ...)
+{
+ struct lttng_ust_lib_ring_buffer_ctx ctx;
+ struct ltt_channel *chan = session->metadata;
+ char *str = NULL;
+ int ret = 0, waitret;
+ size_t len, reserve_len, pos;
+ va_list ap;
+
+ WARN_ON_ONCE(!CMM_ACCESS_ONCE(session->active));
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0)
+ return -ENOMEM;
+
+ len = strlen(str);
+ pos = 0;
+
+ for (pos = 0; pos < len; pos += reserve_len) {
+ reserve_len = min_t(size_t,
+ chan->ops->packet_avail_size(chan->chan, chan->handle),
+ len - pos);
+ lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len,
+ sizeof(char), -1, chan->handle);
+ /*
+ * We don't care about metadata buffer's records lost
+ * count, because we always retry here. Report error if
+ * we need to bail out after timeout or being
+ * interrupted.
+ */
+ waitret = wait_cond_interruptible_timeout(
+ ({
+ ret = chan->ops->event_reserve(&ctx, 0);
+ ret != -ENOBUFS || !ret;
+ }),
+ LTTNG_METADATA_TIMEOUT_MSEC);
+ if (waitret == -ETIMEDOUT || waitret == -EINTR || ret) {
+ DBG("LTTng: Failure to write metadata to buffers (%s)\n",
+ waitret == -EINTR ? "interrupted" :
+ (ret == -ENOBUFS ? "timeout" : "I/O error"));
+ if (waitret == -EINTR)
+ ret = waitret;
+ goto end;
+ }
+ chan->ops->event_write(&ctx, &str[pos], reserve_len);
+ chan->ops->event_commit(&ctx);
+ }
+end:
+ free(str);
+ return ret;
+}
+
+static
+int _ltt_field_statedump(struct ltt_session *session,
+ const struct lttng_event_field *field)
+{
+ int ret = 0;
+
+ switch (field->type.atype) {
+ case atype_integer:
+ ret = lttng_metadata_printf(session,
+ " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s;\n",
+ field->type.u.basic.integer.size,
+ field->type.u.basic.integer.alignment,
+ field->type.u.basic.integer.signedness,
+ (field->type.u.basic.integer.encoding == lttng_encode_none)
+ ? "none"
+ : (field->type.u.basic.integer.encoding == lttng_encode_UTF8)
+ ? "UTF8"
+ : "ASCII",
+ field->type.u.basic.integer.base,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+#else
+ field->type.u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+#endif
+ field->name);
+ break;
+ case atype_float:
+ ret = lttng_metadata_printf(session,
+ " floating_point { exp_dig = %u; mant_dig = %u; align = %u;%s } _%s;\n",
+ field->type.u.basic._float.exp_dig,
+ field->type.u.basic._float.mant_dig,
+ field->type.u.basic._float.alignment,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+#else
+ field->type.u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+#endif
+ field->name);
+ break;
+ case atype_enum:
+ ret = lttng_metadata_printf(session,
+ " %s %s;\n",
+ field->type.u.basic.enumeration.name,
+ field->name);
+ break;
+ case atype_array:
+ {
+ const struct lttng_basic_type *elem_type;
+
+ elem_type = &field->type.u.array.elem_type;
+ ret = lttng_metadata_printf(session,
+ " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n",
+ elem_type->u.basic.integer.size,
+ elem_type->u.basic.integer.alignment,
+ elem_type->u.basic.integer.signedness,
+ (elem_type->u.basic.integer.encoding == lttng_encode_none)
+ ? "none"
+ : (elem_type->u.basic.integer.encoding == lttng_encode_UTF8)
+ ? "UTF8"
+ : "ASCII",
+ elem_type->u.basic.integer.base,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+#else
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+#endif
+ field->name, field->type.u.array.length);
+ break;
+ }
+ case atype_sequence:
+ {
+ const struct lttng_basic_type *elem_type;
+ const struct lttng_basic_type *length_type;
+
+ elem_type = &field->type.u.sequence.elem_type;
+ length_type = &field->type.u.sequence.length_type;
+ ret = lttng_metadata_printf(session,
+ " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } __%s_length;\n",
+ length_type->u.basic.integer.size,
+ (unsigned int) length_type->u.basic.integer.alignment,
+ length_type->u.basic.integer.signedness,
+ (length_type->u.basic.integer.encoding == lttng_encode_none)
+ ? "none"
+ : ((length_type->u.basic.integer.encoding == lttng_encode_UTF8)
+ ? "UTF8"
+ : "ASCII"),
+ length_type->u.basic.integer.base,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ length_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+#else
+ length_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+#endif
+ field->name);
+ if (ret)
+ return ret;
+
+ ret = lttng_metadata_printf(session,
+ " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[ __%s_length ];\n",
+ elem_type->u.basic.integer.size,
+ (unsigned int) elem_type->u.basic.integer.alignment,
+ elem_type->u.basic.integer.signedness,
+ (elem_type->u.basic.integer.encoding == lttng_encode_none)
+ ? "none"
+ : ((elem_type->u.basic.integer.encoding == lttng_encode_UTF8)
+ ? "UTF8"
+ : "ASCII"),
+ elem_type->u.basic.integer.base,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+#else
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+#endif
+ field->name,
+ field->name);
+ break;
+ }
+
+ case atype_string:
+ /* Default encoding is UTF8 */
+ ret = lttng_metadata_printf(session,
+ " string%s _%s;\n",
+ field->type.u.basic.string.encoding == lttng_encode_ASCII ?
+ " { encoding = ASCII; }" : "",
+ field->name);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static
+int _ltt_context_metadata_statedump(struct ltt_session *session,
+ struct lttng_ctx *ctx)
+{
+ int ret = 0;
+ int i;
+
+ if (!ctx)
+ return 0;
+ for (i = 0; i < ctx->nr_fields; i++) {
+ const struct lttng_ctx_field *field = &ctx->fields[i];
+
+ ret = _ltt_field_statedump(session, &field->event_field);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static
+int _ltt_fields_metadata_statedump(struct ltt_session *session,
+ struct ltt_event *event)
+{
+ const struct lttng_event_desc *desc = event->desc;
+ int ret = 0;
+ int i;
+
+ for (i = 0; i < desc->nr_fields; i++) {
+ const struct lttng_event_field *field = &desc->fields[i];
+
+ ret = _ltt_field_statedump(session, field);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static
+int _ltt_event_metadata_statedump(struct ltt_session *session,
+ struct ltt_channel *chan,
+ struct ltt_event *event)
+{
+ int ret = 0;
+
+ if (event->metadata_dumped || !CMM_ACCESS_ONCE(session->active))
+ return 0;
+ if (chan == session->metadata)
+ return 0;
+ /*
+ * Don't print events for which probe load is pending.
+ */
+ if (!event->desc)
+ return 0;
+
+ ret = lttng_metadata_printf(session,
+ "event {\n"
+ " name = %s;\n"
+ " id = %u;\n"
+ " stream_id = %u;\n",
+ event->desc->name,
+ event->id,
+ event->chan->id);
+ if (ret)
+ goto end;
+
+ if (event->ctx) {
+ ret = lttng_metadata_printf(session,
+ " context := struct {\n");
+ if (ret)
+ goto end;
+ }
+ ret = _ltt_context_metadata_statedump(session, event->ctx);
+ if (ret)
+ goto end;
+ if (event->ctx) {
+ ret = lttng_metadata_printf(session,
+ " };\n");
+ if (ret)
+ goto end;
+ }
+
+ ret = lttng_metadata_printf(session,
+ " fields := struct {\n"
+ );
+ if (ret)
+ goto end;
+
+ ret = _ltt_fields_metadata_statedump(session, event);
+ if (ret)
+ goto end;
+
+ /*
+ * LTTng space reservation can only reserve multiples of the
+ * byte size.
+ */
+ ret = lttng_metadata_printf(session,
+ " };\n"
+ "};\n\n");
+ if (ret)
+ goto end;
+
+ event->metadata_dumped = 1;
+end:
+ return ret;
+
+}
+
+static
+int _ltt_channel_metadata_statedump(struct ltt_session *session,
+ struct ltt_channel *chan)
+{
+ int ret = 0;
+
+ if (chan->metadata_dumped || !CMM_ACCESS_ONCE(session->active))
+ return 0;
+ if (chan == session->metadata)
+ return 0;
+
+ WARN_ON_ONCE(!chan->header_type);
+ ret = lttng_metadata_printf(session,
+ "stream {\n"
+ " id = %u;\n"
+ " event.header := %s;\n"
+ " packet.context := struct packet_context;\n",
+ chan->id,
+ chan->header_type == 1 ? "struct event_header_compact" :
+ "struct event_header_large");
+ if (ret)
+ goto end;
+
+ if (chan->ctx) {
+ ret = lttng_metadata_printf(session,
+ " event.context := struct {\n");
+ if (ret)
+ goto end;
+ }
+ ret = _ltt_context_metadata_statedump(session, chan->ctx);
+ if (ret)
+ goto end;
+ if (chan->ctx) {
+ ret = lttng_metadata_printf(session,
+ " };\n");
+ if (ret)
+ goto end;
+ }
+
+ ret = lttng_metadata_printf(session,
+ "};\n\n");
+
+ chan->metadata_dumped = 1;
+end:
+ return ret;
+}
+
+static
+int _ltt_stream_packet_context_declare(struct ltt_session *session)
+{
+ return lttng_metadata_printf(session,
+ "struct packet_context {\n"
+ " uint64_t timestamp_begin;\n"
+ " uint64_t timestamp_end;\n"
+ " uint32_t events_discarded;\n"
+ " uint32_t content_size;\n"
+ " uint32_t packet_size;\n"
+ " uint32_t cpu_id;\n"
+ "};\n\n"
+ );
+}
+
+/*
+ * Compact header:
+ * id: range: 0 - 30.
+ * id 31 is reserved to indicate an extended header.
+ *
+ * Large header:
+ * id: range: 0 - 65534.
+ * id 65535 is reserved to indicate an extended header.
+ */
+static
+int _ltt_event_header_declare(struct ltt_session *session)
+{
+ return lttng_metadata_printf(session,
+ "struct event_header_compact {\n"
+ " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
+ " variant <id> {\n"
+ " struct {\n"
+ " uint27_t timestamp;\n"
+ " } compact;\n"
+ " struct {\n"
+ " uint32_t id;\n"
+ " uint64_t timestamp;\n"
+ " } extended;\n"
+ " } v;\n"
+ "} align(%u);\n"
+ "\n"
+ "struct event_header_large {\n"
+ " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
+ " variant <id> {\n"
+ " struct {\n"
+ " uint32_t timestamp;\n"
+ " } compact;\n"
+ " struct {\n"
+ " uint32_t id;\n"
+ " uint64_t timestamp;\n"
+ " } extended;\n"
+ " } v;\n"
+ "} align(%u);\n\n",
+ lttng_alignof(uint32_t) * CHAR_BIT,
+ lttng_alignof(uint16_t) * CHAR_BIT
+ );
+}
+
+/*
+ * Output metadata into this session's metadata buffers.
+ */
+static
+int _ltt_session_metadata_statedump(struct ltt_session *session)
+{
+ unsigned char *uuid_c = session->uuid;
+ char uuid_s[37];
+ struct ltt_channel *chan;
+ struct ltt_event *event;
+ int ret = 0;
+
+ if (!CMM_ACCESS_ONCE(session->active))
+ return 0;
+ if (session->metadata_dumped)
+ goto skip_session;
+ if (!session->metadata) {
+ DBG("LTTng: attempt to start tracing, but metadata channel is not found. Operation abort.\n");
+ return -EPERM;
+ }
+
+ snprintf(uuid_s, sizeof(uuid_s),
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid_c[0], uuid_c[1], uuid_c[2], uuid_c[3],
+ uuid_c[4], uuid_c[5], uuid_c[6], uuid_c[7],
+ uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11],
+ uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]);
+
+ ret = lttng_metadata_printf(session,
+ "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n"
+ "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n"
+ "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n"
+ "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n"
+ "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
+ "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
+ "\n"
+ "trace {\n"
+ " major = %u;\n"
+ " minor = %u;\n"
+ " uuid = \"%s\";\n"
+ " byte_order = %s;\n"
+ " packet.header := struct {\n"
+ " uint32_t magic;\n"
+ " uint8_t uuid[16];\n"
+ " uint32_t stream_id;\n"
+ " };\n"
+ "};\n\n",
+ lttng_alignof(uint8_t) * CHAR_BIT,
+ lttng_alignof(uint16_t) * CHAR_BIT,
+ lttng_alignof(uint32_t) * CHAR_BIT,
+ lttng_alignof(uint64_t) * CHAR_BIT,
+ CTF_VERSION_MAJOR,
+ CTF_VERSION_MINOR,
+ uuid_s,
+#if (BYTE_ORDER == BIG_ENDIAN)
+ "be"
+#else
+ "le"
+#endif
+ );
+ if (ret)
+ goto end;
+
+ ret = _ltt_stream_packet_context_declare(session);
+ if (ret)
+ goto end;
+
+ ret = _ltt_event_header_declare(session);
+ if (ret)
+ goto end;
+
+skip_session:
+ cds_list_for_each_entry(chan, &session->chan, list) {
+ ret = _ltt_channel_metadata_statedump(session, chan);
+ if (ret)
+ goto end;
+ }
+
+ cds_list_for_each_entry(event, &session->events, list) {
+ ret = _ltt_event_metadata_statedump(session, event->chan, event);
+ if (ret)
+ goto end;
+ }
+ session->metadata_dumped = 1;
+end:
+ return ret;
+}
+
+/**
+ * ltt_transport_register - LTT transport registration
+ * @transport: transport structure
+ *
+ * Registers a transport which can be used as output to extract the data out of
+ * LTTng. Called with ust_lock held.
+ */
+void ltt_transport_register(struct ltt_transport *transport)
+{
+ cds_list_add_tail(&transport->node, <t_transport_list);
+}
+
+/**
+ * ltt_transport_unregister - LTT transport unregistration
+ * @transport: transport structure
+ * Called with ust_lock held.
+ */
+void ltt_transport_unregister(struct ltt_transport *transport)
+{
+ cds_list_del(&transport->node);
+}
+
+void lttng_ust_events_exit(void)
+{
+ struct ltt_session *session, *tmpsession;
+
+ cds_list_for_each_entry_safe(session, tmpsession, &sessions, list)
+ ltt_session_destroy(session);
+}
--- /dev/null
+/*
+ * ltt-probes.c
+ *
+ * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * Holds LTTng probes registry.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <urcu/list.h>
+#include <lttng/core.h>
+#include <lttng/ust-events.h>
+
+#include "ltt-tracer-core.h"
+
+/*
+ * probe list is protected by ust_lock()/ust_unlock().
+ */
+static CDS_LIST_HEAD(probe_list);
+
+static
+const struct lttng_event_desc *find_event(const char *name)
+{
+ struct lttng_probe_desc *probe_desc;
+ int i;
+
+ cds_list_for_each_entry(probe_desc, &probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ if (!strcmp(probe_desc->event_desc[i].name, name))
+ return &probe_desc->event_desc[i];
+ }
+ }
+ return NULL;
+}
+
+int ltt_probe_register(struct lttng_probe_desc *desc)
+{
+ int ret = 0;
+ int i;
+
+ ust_lock();
+ /*
+ * TODO: This is O(N^2). Turn into a hash table when probe registration
+ * overhead becomes an issue.
+ */
+ for (i = 0; i < desc->nr_events; i++) {
+ if (find_event(desc->event_desc[i].name)) {
+ ret = -EEXIST;
+ goto end;
+ }
+ }
+ cds_list_add(&desc->head, &probe_list);
+
+ /*
+ * fix the events awaiting probe load.
+ */
+ for (i = 0; i < desc->nr_events; i++) {
+ ret = pending_probe_fix_events(&desc->event_desc[i]);
+ assert(!ret);
+ }
+end:
+ ust_unlock();
+ return ret;
+}
+
+void ltt_probe_unregister(struct lttng_probe_desc *desc)
+{
+ ust_lock();
+ cds_list_del(&desc->head);
+ ust_unlock();
+}
+
+/*
+ * called with UST lock held.
+ */
+const struct lttng_event_desc *ltt_event_get(const char *name)
+{
+ const struct lttng_event_desc *event;
+
+ event = find_event(name);
+ if (!event)
+ return NULL;
+ return event;
+}
+
+void ltt_event_put(const struct lttng_event_desc *event)
+{
+}
+
+#if 0
+static
+void *tp_list_start(struct seq_file *m, loff_t *pos)
+{
+ struct lttng_probe_desc *probe_desc;
+ int iter = 0, i;
+
+ pthread_mutex_lock(&probe_mutex);
+ cds_list_for_each_entry(probe_desc, &probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ if (iter++ >= *pos)
+ return (void *) &probe_desc->event_desc[i];
+ }
+ }
+ /* End of list */
+ return NULL;
+}
+
+static
+void *tp_list_next(struct seq_file *m, void *p, loff_t *ppos)
+{
+ struct lttng_probe_desc *probe_desc;
+ int iter = 0, i;
+
+ (*ppos)++;
+ cds_list_for_each_entry(probe_desc, &probe_list, head) {
+ for (i = 0; i < probe_desc->nr_events; i++) {
+ if (iter++ >= *ppos)
+ return (void *) &probe_desc->event_desc[i];
+ }
+ }
+ /* End of list */
+ return NULL;
+}
+
+static
+void tp_list_stop(struct seq_file *m, void *p)
+{
+ pthread_mutex_unlock(&probe_mutex);
+}
+
+static
+int tp_list_show(struct seq_file *m, void *p)
+{
+ const struct lttng_event_desc *probe_desc = p;
+
+ /*
+ * Don't export lttng internal events (metadata).
+ */
+ if (!strncmp(probe_desc->name, "lttng_", sizeof("lttng_") - 1))
+ return 0;
+ seq_printf(m, "event { name = %s; };\n",
+ probe_desc->name);
+ return 0;
+}
+
+static
+const struct seq_operations lttng_tracepoint_list_seq_ops = {
+ .start = tp_list_start,
+ .next = tp_list_next,
+ .stop = tp_list_stop,
+ .show = tp_list_show,
+};
+
+static
+int lttng_tracepoint_list_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, <tng_tracepoint_list_seq_ops);
+}
+
+const struct file_operations lttng_tracepoint_list_fops = {
+ .open = lttng_tracepoint_list_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+#endif //0
--- /dev/null
+/*
+ * ltt-ring-buffer-client-discard.c
+ *
+ * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng lib ring buffer client (discard mode).
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include "ltt-tracer.h"
+
+#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_DISCARD
+#define RING_BUFFER_MODE_TEMPLATE_STRING "discard"
+#define RING_BUFFER_MODE_TEMPLATE_INIT \
+ ltt_ring_buffer_client_discard_init
+#define RING_BUFFER_MODE_TEMPLATE_EXIT \
+ ltt_ring_buffer_client_discard_exit
+#include "ltt-ring-buffer-client.h"
--- /dev/null
+/*
+ * ltt-ring-buffer-client-overwrite.c
+ *
+ * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng lib ring buffer client (overwrite mode).
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include "ltt-tracer.h"
+
+#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_OVERWRITE
+#define RING_BUFFER_MODE_TEMPLATE_STRING "overwrite"
+#define RING_BUFFER_MODE_TEMPLATE_INIT \
+ ltt_ring_buffer_client_overwrite_init
+#define RING_BUFFER_MODE_TEMPLATE_EXIT \
+ ltt_ring_buffer_client_overwrite_exit
+#include "ltt-ring-buffer-client.h"
--- /dev/null
+/*
+ * ltt-ring-buffer-client.h
+ *
+ * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng lib ring buffer client template.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <stdint.h>
+#include <lttng/ust-events.h>
+#include "lttng/bitfield.h"
+#include "lttng/clock.h"
+#include "ltt-tracer.h"
+#include "../libringbuffer/frontend_types.h"
+
+/*
+ * Keep the natural field alignment for _each field_ within this structure if
+ * you ever add/remove a field from this header. Packed attribute is not used
+ * because gcc generates poor code on at least powerpc and mips. Don't ever
+ * let gcc add padding between the structure elements.
+ */
+
+struct packet_header {
+ /* Trace packet header */
+ uint32_t magic; /*
+ * Trace magic number.
+ * contains endianness information.
+ */
+ uint8_t uuid[16];
+ uint32_t stream_id;
+
+ struct {
+ /* Stream packet context */
+ uint64_t timestamp_begin; /* Cycle count at subbuffer start */
+ uint64_t timestamp_end; /* Cycle count at subbuffer end */
+ uint32_t events_discarded; /*
+ * Events lost in this subbuffer since
+ * the beginning of the trace.
+ * (may overflow)
+ */
+ uint32_t content_size; /* Size of data in subbuffer */
+ uint32_t packet_size; /* Subbuffer size (include padding) */
+ uint32_t cpu_id; /* CPU id associated with stream */
+ uint8_t header_end; /* End of header */
+ } ctx;
+};
+
+
+static inline notrace u64 lib_ring_buffer_clock_read(struct channel *chan)
+{
+ return trace_clock_read64();
+}
+
+static inline
+size_t ctx_get_size(size_t offset, struct lttng_ctx *ctx)
+{
+ int i;
+ size_t orig_offset = offset;
+
+ if (caa_likely(!ctx))
+ return 0;
+ for (i = 0; i < ctx->nr_fields; i++)
+ offset += ctx->fields[i].get_size(offset);
+ return offset - orig_offset;
+}
+
+static inline
+void ctx_record(struct lttng_ust_lib_ring_buffer_ctx *bufctx,
+ struct ltt_channel *chan,
+ struct lttng_ctx *ctx)
+{
+ int i;
+
+ if (caa_likely(!ctx))
+ return;
+ for (i = 0; i < ctx->nr_fields; i++)
+ ctx->fields[i].record(&ctx->fields[i], bufctx, chan);
+}
+
+/*
+ * record_header_size - Calculate the header size and padding necessary.
+ * @config: ring buffer instance configuration
+ * @chan: channel
+ * @offset: offset in the write buffer
+ * @pre_header_padding: padding to add before the header (output)
+ * @ctx: reservation context
+ *
+ * Returns the event header size (including padding).
+ *
+ * The payload must itself determine its own alignment from the biggest type it
+ * contains.
+ */
+static __inline__
+unsigned char record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct channel *chan, size_t offset,
+ size_t *pre_header_padding,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ struct ltt_channel *ltt_chan = channel_get_private(chan);
+ struct ltt_event *event = ctx->priv;
+ size_t orig_offset = offset;
+ size_t padding;
+
+ switch (ltt_chan->header_type) {
+ case 1: /* compact */
+ padding = lib_ring_buffer_align(offset, lttng_alignof(uint32_t));
+ offset += padding;
+ if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
+ offset += sizeof(uint32_t); /* id and timestamp */
+ } else {
+ /* Minimum space taken by 5-bit id */
+ offset += sizeof(uint8_t);
+ /* Align extended struct on largest member */
+ offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
+ offset += sizeof(uint32_t); /* id */
+ offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
+ offset += sizeof(uint64_t); /* timestamp */
+ }
+ break;
+ case 2: /* large */
+ padding = lib_ring_buffer_align(offset, lttng_alignof(uint16_t));
+ offset += padding;
+ offset += sizeof(uint16_t);
+ if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
+ offset += lib_ring_buffer_align(offset, lttng_alignof(uint32_t));
+ offset += sizeof(uint32_t); /* timestamp */
+ } else {
+ /* Align extended struct on largest member */
+ offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
+ offset += sizeof(uint32_t); /* id */
+ offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
+ offset += sizeof(uint64_t); /* timestamp */
+ }
+ break;
+ default:
+ padding = 0;
+ WARN_ON_ONCE(1);
+ }
+ offset += ctx_get_size(offset, event->ctx);
+ offset += ctx_get_size(offset, ltt_chan->ctx);
+
+ *pre_header_padding = padding;
+ return offset - orig_offset;
+}
+
+#include "../libringbuffer/api.h"
+
+static
+void ltt_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ uint32_t event_id);
+
+/*
+ * ltt_write_event_header
+ *
+ * Writes the event header to the offset (already aligned on 32-bits).
+ *
+ * @config: ring buffer instance configuration
+ * @ctx: reservation context
+ * @event_id: event ID
+ */
+static __inline__
+void ltt_write_event_header(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ uint32_t event_id)
+{
+ struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
+ struct ltt_event *event = ctx->priv;
+
+ if (caa_unlikely(ctx->rflags))
+ goto slow_path;
+
+ switch (ltt_chan->header_type) {
+ case 1: /* compact */
+ {
+ uint32_t id_time = 0;
+
+ bt_bitfield_write(&id_time, uint32_t, 0, 5, event_id);
+ bt_bitfield_write(&id_time, uint32_t, 5, 27, ctx->tsc);
+ lib_ring_buffer_write(config, ctx, &id_time, sizeof(id_time));
+ break;
+ }
+ case 2: /* large */
+ {
+ uint32_t timestamp = (uint32_t) ctx->tsc;
+ uint16_t id = event_id;
+
+ lib_ring_buffer_write(config, ctx, &id, sizeof(id));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint32_t));
+ lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
+ break;
+ }
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ ctx_record(ctx, ltt_chan, ltt_chan->ctx);
+ ctx_record(ctx, ltt_chan, event->ctx);
+ lib_ring_buffer_align_ctx(ctx, ctx->largest_align);
+
+ return;
+
+slow_path:
+ ltt_write_event_header_slow(config, ctx, event_id);
+}
+
+static
+void ltt_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ uint32_t event_id)
+{
+ struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
+ struct ltt_event *event = ctx->priv;
+
+ switch (ltt_chan->header_type) {
+ case 1: /* compact */
+ if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
+ uint32_t id_time = 0;
+
+ bt_bitfield_write(&id_time, uint32_t, 0, 5, event_id);
+ bt_bitfield_write(&id_time, uint32_t, 5, 27, ctx->tsc);
+ lib_ring_buffer_write(config, ctx, &id_time, sizeof(id_time));
+ } else {
+ uint8_t id = 0;
+ uint64_t timestamp = ctx->tsc;
+
+ bt_bitfield_write(&id, uint8_t, 0, 5, 31);
+ lib_ring_buffer_write(config, ctx, &id, sizeof(id));
+ /* Align extended struct on largest member */
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
+ lib_ring_buffer_write(config, ctx, &event_id, sizeof(event_id));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
+ lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
+ }
+ break;
+ case 2: /* large */
+ {
+ if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
+ uint32_t timestamp = (uint32_t) ctx->tsc;
+ uint16_t id = event_id;
+
+ lib_ring_buffer_write(config, ctx, &id, sizeof(id));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint32_t));
+ lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
+ } else {
+ uint16_t id = 65535;
+ uint64_t timestamp = ctx->tsc;
+
+ lib_ring_buffer_write(config, ctx, &id, sizeof(id));
+ /* Align extended struct on largest member */
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
+ lib_ring_buffer_write(config, ctx, &event_id, sizeof(event_id));
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
+ lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
+ }
+ break;
+ }
+ default:
+ WARN_ON_ONCE(1);
+ }
+ ctx_record(ctx, ltt_chan, ltt_chan->ctx);
+ ctx_record(ctx, ltt_chan, event->ctx);
+ lib_ring_buffer_align_ctx(ctx, ctx->largest_align);
+}
+
+static const struct lttng_ust_lib_ring_buffer_config client_config;
+
+static u64 client_ring_buffer_clock_read(struct channel *chan)
+{
+ return lib_ring_buffer_clock_read(chan);
+}
+
+static
+size_t client_record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct channel *chan, size_t offset,
+ size_t *pre_header_padding,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ return record_header_size(config, chan, offset,
+ pre_header_padding, ctx);
+}
+
+/**
+ * client_packet_header_size - called on buffer-switch to a new sub-buffer
+ *
+ * Return header size without padding after the structure. Don't use packed
+ * structure because gcc generates inefficient code on some architectures
+ * (powerpc, mips..)
+ */
+static size_t client_packet_header_size(void)
+{
+ return offsetof(struct packet_header, ctx.header_end);
+}
+
+static void client_buffer_begin(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
+ unsigned int subbuf_idx,
+ struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan = shmp(handle, buf->backend.chan);
+ struct packet_header *header =
+ (struct packet_header *)
+ lib_ring_buffer_offset_address(&buf->backend,
+ subbuf_idx * chan->backend.subbuf_size,
+ handle);
+ struct ltt_channel *ltt_chan = channel_get_private(chan);
+ struct ltt_session *session = ltt_chan->session;
+
+ header->magic = CTF_MAGIC_NUMBER;
+ memcpy(header->uuid, session->uuid, sizeof(session->uuid));
+ header->stream_id = ltt_chan->id;
+ header->ctx.timestamp_begin = tsc;
+ header->ctx.timestamp_end = 0;
+ header->ctx.events_discarded = 0;
+ header->ctx.content_size = 0xFFFFFFFF; /* for debugging */
+ header->ctx.packet_size = 0xFFFFFFFF;
+ header->ctx.cpu_id = buf->backend.cpu;
+}
+
+/*
+ * offset is assumed to never be 0 here : never deliver a completely empty
+ * subbuffer. data_size is between 1 and subbuf_size.
+ */
+static void client_buffer_end(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
+ unsigned int subbuf_idx, unsigned long data_size,
+ struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan = shmp(handle, buf->backend.chan);
+ struct packet_header *header =
+ (struct packet_header *)
+ lib_ring_buffer_offset_address(&buf->backend,
+ subbuf_idx * chan->backend.subbuf_size,
+ handle);
+ unsigned long records_lost = 0;
+
+ header->ctx.timestamp_end = tsc;
+ header->ctx.content_size = data_size * CHAR_BIT; /* in bits */
+ header->ctx.packet_size = PAGE_ALIGN(data_size) * CHAR_BIT; /* in bits */
+ /*
+ * We do not care about the records lost count, because the metadata
+ * channel waits and retry.
+ */
+ (void) lib_ring_buffer_get_records_lost_full(&client_config, buf);
+ records_lost += lib_ring_buffer_get_records_lost_wrap(&client_config, buf);
+ records_lost += lib_ring_buffer_get_records_lost_big(&client_config, buf);
+ header->ctx.events_discarded = records_lost;
+}
+
+static int client_buffer_create(struct lttng_ust_lib_ring_buffer *buf, void *priv,
+ int cpu, const char *name, struct lttng_ust_shm_handle *handle)
+{
+ return 0;
+}
+
+static void client_buffer_finalize(struct lttng_ust_lib_ring_buffer *buf, void *priv, int cpu, struct lttng_ust_shm_handle *handle)
+{
+}
+
+static const struct lttng_ust_lib_ring_buffer_config client_config = {
+ .cb.ring_buffer_clock_read = client_ring_buffer_clock_read,
+ .cb.record_header_size = client_record_header_size,
+ .cb.subbuffer_header_size = client_packet_header_size,
+ .cb.buffer_begin = client_buffer_begin,
+ .cb.buffer_end = client_buffer_end,
+ .cb.buffer_create = client_buffer_create,
+ .cb.buffer_finalize = client_buffer_finalize,
+
+ .tsc_bits = 32,
+ .alloc = RING_BUFFER_ALLOC_PER_CPU,
+ .sync = RING_BUFFER_SYNC_GLOBAL,
+ .mode = RING_BUFFER_MODE_TEMPLATE,
+ .backend = RING_BUFFER_PAGE,
+ .output = RING_BUFFER_MMAP,
+ .oops = RING_BUFFER_OOPS_CONSISTENCY,
+ .ipi = RING_BUFFER_NO_IPI_BARRIER,
+ .wakeup = RING_BUFFER_WAKEUP_BY_WRITER,
+};
+
+static
+struct ltt_channel *_channel_create(const char *name,
+ struct ltt_channel *ltt_chan, void *buf_addr,
+ size_t subbuf_size, size_t num_subbuf,
+ unsigned int switch_timer_interval,
+ unsigned int read_timer_interval,
+ int *shm_fd, int *wait_fd,
+ uint64_t *memory_map_size)
+{
+ ltt_chan->handle = channel_create(&client_config, name, ltt_chan, buf_addr,
+ subbuf_size, num_subbuf, switch_timer_interval,
+ read_timer_interval, shm_fd, wait_fd,
+ memory_map_size);
+ if (!ltt_chan->handle)
+ return NULL;
+ ltt_chan->chan = shmp(ltt_chan->handle, ltt_chan->handle->chan);
+ return ltt_chan;
+}
+
+static
+void ltt_channel_destroy(struct ltt_channel *ltt_chan)
+{
+ channel_destroy(ltt_chan->chan, ltt_chan->handle, 0);
+}
+
+static
+struct lttng_ust_lib_ring_buffer *ltt_buffer_read_open(struct channel *chan,
+ struct lttng_ust_shm_handle *handle,
+ int *shm_fd, int *wait_fd,
+ uint64_t *memory_map_size)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ int cpu;
+
+ for_each_channel_cpu(cpu, chan) {
+ buf = channel_get_ring_buffer(&client_config, chan,
+ cpu, handle, shm_fd, wait_fd,
+ memory_map_size);
+ if (!lib_ring_buffer_open_read(buf, handle, 0))
+ return buf;
+ }
+ return NULL;
+}
+
+static
+void ltt_buffer_read_close(struct lttng_ust_lib_ring_buffer *buf,
+ struct lttng_ust_shm_handle *handle)
+{
+ lib_ring_buffer_release_read(buf, handle, 0);
+}
+
+static
+int ltt_event_reserve(struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ uint32_t event_id)
+{
+ struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
+ int ret, cpu;
+
+ cpu = lib_ring_buffer_get_cpu(&client_config);
+ if (cpu < 0)
+ return -EPERM;
+ ctx->cpu = cpu;
+
+ switch (ltt_chan->header_type) {
+ case 1: /* compact */
+ if (event_id > 30)
+ ctx->rflags |= LTT_RFLAG_EXTENDED;
+ break;
+ case 2: /* large */
+ if (event_id > 65534)
+ ctx->rflags |= LTT_RFLAG_EXTENDED;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
+
+ ret = lib_ring_buffer_reserve(&client_config, ctx);
+ if (ret)
+ goto put;
+ ltt_write_event_header(&client_config, ctx, event_id);
+ return 0;
+put:
+ lib_ring_buffer_put_cpu(&client_config);
+ return ret;
+}
+
+static
+void ltt_event_commit(struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ lib_ring_buffer_commit(&client_config, ctx);
+ lib_ring_buffer_put_cpu(&client_config);
+}
+
+static
+void ltt_event_write(struct lttng_ust_lib_ring_buffer_ctx *ctx, const void *src,
+ size_t len)
+{
+ lib_ring_buffer_write(&client_config, ctx, src, len);
+}
+
+#if 0
+static
+wait_queue_head_t *ltt_get_reader_wait_queue(struct channel *chan)
+{
+ return &chan->read_wait;
+}
+
+static
+wait_queue_head_t *ltt_get_hp_wait_queue(struct channel *chan)
+{
+ return &chan->hp_wait;
+}
+#endif //0
+
+static
+int ltt_is_finalized(struct channel *chan)
+{
+ return lib_ring_buffer_channel_is_finalized(chan);
+}
+
+static
+int ltt_is_disabled(struct channel *chan)
+{
+ return lib_ring_buffer_channel_is_disabled(chan);
+}
+
+static
+int ltt_flush_buffer(struct channel *chan, struct lttng_ust_shm_handle *handle)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ int cpu;
+
+ for_each_channel_cpu(cpu, chan) {
+ int shm_fd, wait_fd;
+ uint64_t memory_map_size;
+
+ buf = channel_get_ring_buffer(&client_config, chan,
+ cpu, handle, &shm_fd, &wait_fd,
+ &memory_map_size);
+ lib_ring_buffer_switch(&client_config, buf,
+ SWITCH_ACTIVE, handle);
+ }
+ return 0;
+}
+
+static struct ltt_transport ltt_relay_transport = {
+ .name = "relay-" RING_BUFFER_MODE_TEMPLATE_STRING "-mmap",
+ .ops = {
+ .channel_create = _channel_create,
+ .channel_destroy = ltt_channel_destroy,
+ .buffer_read_open = ltt_buffer_read_open,
+ .buffer_read_close = ltt_buffer_read_close,
+ .event_reserve = ltt_event_reserve,
+ .event_commit = ltt_event_commit,
+ .event_write = ltt_event_write,
+ .packet_avail_size = NULL, /* Would be racy anyway */
+ //.get_reader_wait_queue = ltt_get_reader_wait_queue,
+ //.get_hp_wait_queue = ltt_get_hp_wait_queue,
+ .is_finalized = ltt_is_finalized,
+ .is_disabled = ltt_is_disabled,
+ .flush_buffer = ltt_flush_buffer,
+ },
+};
+
+void RING_BUFFER_MODE_TEMPLATE_INIT(void)
+{
+ DBG("LTT : ltt ring buffer client init\n");
+ ltt_transport_register(<t_relay_transport);
+}
+
+void RING_BUFFER_MODE_TEMPLATE_EXIT(void)
+{
+ DBG("LTT : ltt ring buffer client exit\n");
+ ltt_transport_unregister(<t_relay_transport);
+}
--- /dev/null
+/*
+ * ltt-ring-buffer-metadata-client.c
+ *
+ * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng lib ring buffer metadta client.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include "ltt-tracer.h"
+
+#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_DISCARD
+#define RING_BUFFER_MODE_TEMPLATE_STRING "metadata"
+#define RING_BUFFER_MODE_TEMPLATE_INIT \
+ ltt_ring_buffer_metadata_client_init
+#define RING_BUFFER_MODE_TEMPLATE_EXIT \
+ ltt_ring_buffer_metadata_client_exit
+#include "ltt-ring-buffer-metadata-client.h"
--- /dev/null
+/*
+ * ltt-ring-buffer-client.h
+ *
+ * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng lib ring buffer client template.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <stdint.h>
+#include <lttng/ust-events.h>
+#include "lttng/bitfield.h"
+#include "ltt-tracer.h"
+#include "../libringbuffer/frontend_types.h"
+
+struct metadata_packet_header {
+ uint32_t magic; /* 0x75D11D57 */
+ uint8_t uuid[16]; /* Unique Universal Identifier */
+ uint32_t checksum; /* 0 if unused */
+ uint32_t content_size; /* in bits */
+ uint32_t packet_size; /* in bits */
+ uint8_t compression_scheme; /* 0 if unused */
+ uint8_t encryption_scheme; /* 0 if unused */
+ uint8_t checksum_scheme; /* 0 if unused */
+ uint8_t major; /* CTF spec major version number */
+ uint8_t minor; /* CTF spec minor version number */
+ uint8_t header_end[0];
+};
+
+struct metadata_record_header {
+ uint8_t header_end[0]; /* End of header */
+};
+
+static const struct lttng_ust_lib_ring_buffer_config client_config;
+
+static inline
+u64 lib_ring_buffer_clock_read(struct channel *chan)
+{
+ return 0;
+}
+
+static inline
+unsigned char record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct channel *chan, size_t offset,
+ size_t *pre_header_padding,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ return 0;
+}
+
+#include "../libringbuffer/api.h"
+
+static u64 client_ring_buffer_clock_read(struct channel *chan)
+{
+ return 0;
+}
+
+static
+size_t client_record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
+ struct channel *chan, size_t offset,
+ size_t *pre_header_padding,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ return 0;
+}
+
+/**
+ * client_packet_header_size - called on buffer-switch to a new sub-buffer
+ *
+ * Return header size without padding after the structure. Don't use packed
+ * structure because gcc generates inefficient code on some architectures
+ * (powerpc, mips..)
+ */
+static size_t client_packet_header_size(void)
+{
+ return offsetof(struct metadata_packet_header, header_end);
+}
+
+static void client_buffer_begin(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
+ unsigned int subbuf_idx,
+ struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan = shmp(handle, buf->backend.chan);
+ struct metadata_packet_header *header =
+ (struct metadata_packet_header *)
+ lib_ring_buffer_offset_address(&buf->backend,
+ subbuf_idx * chan->backend.subbuf_size,
+ handle);
+ struct ltt_channel *ltt_chan = channel_get_private(chan);
+ struct ltt_session *session = ltt_chan->session;
+
+ header->magic = TSDL_MAGIC_NUMBER;
+ memcpy(header->uuid, session->uuid, sizeof(session->uuid));
+ header->checksum = 0; /* 0 if unused */
+ header->content_size = 0xFFFFFFFF; /* in bits, for debugging */
+ header->packet_size = 0xFFFFFFFF; /* in bits, for debugging */
+ header->compression_scheme = 0; /* 0 if unused */
+ header->encryption_scheme = 0; /* 0 if unused */
+ header->checksum_scheme = 0; /* 0 if unused */
+ header->major = CTF_SPEC_MAJOR;
+ header->minor = CTF_SPEC_MINOR;
+
+}
+
+/*
+ * offset is assumed to never be 0 here : never deliver a completely empty
+ * subbuffer. data_size is between 1 and subbuf_size.
+ */
+static void client_buffer_end(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
+ unsigned int subbuf_idx, unsigned long data_size,
+ struct lttng_ust_shm_handle *handle)
+{
+ struct channel *chan = shmp(handle, buf->backend.chan);
+ struct metadata_packet_header *header =
+ (struct metadata_packet_header *)
+ lib_ring_buffer_offset_address(&buf->backend,
+ subbuf_idx * chan->backend.subbuf_size,
+ handle);
+ unsigned long records_lost = 0;
+
+ header->content_size = data_size * CHAR_BIT; /* in bits */
+ header->packet_size = PAGE_ALIGN(data_size) * CHAR_BIT; /* in bits */
+ records_lost += lib_ring_buffer_get_records_lost_full(&client_config, buf);
+ records_lost += lib_ring_buffer_get_records_lost_wrap(&client_config, buf);
+ records_lost += lib_ring_buffer_get_records_lost_big(&client_config, buf);
+ WARN_ON_ONCE(records_lost != 0);
+}
+
+static int client_buffer_create(struct lttng_ust_lib_ring_buffer *buf, void *priv,
+ int cpu, const char *name,
+ struct lttng_ust_shm_handle *handle)
+{
+ return 0;
+}
+
+static void client_buffer_finalize(struct lttng_ust_lib_ring_buffer *buf,
+ void *priv, int cpu,
+ struct lttng_ust_shm_handle *handle)
+{
+}
+
+static const struct lttng_ust_lib_ring_buffer_config client_config = {
+ .cb.ring_buffer_clock_read = client_ring_buffer_clock_read,
+ .cb.record_header_size = client_record_header_size,
+ .cb.subbuffer_header_size = client_packet_header_size,
+ .cb.buffer_begin = client_buffer_begin,
+ .cb.buffer_end = client_buffer_end,
+ .cb.buffer_create = client_buffer_create,
+ .cb.buffer_finalize = client_buffer_finalize,
+
+ .tsc_bits = 0,
+ .alloc = RING_BUFFER_ALLOC_GLOBAL,
+ .sync = RING_BUFFER_SYNC_GLOBAL,
+ .mode = RING_BUFFER_MODE_TEMPLATE,
+ .backend = RING_BUFFER_PAGE,
+ .output = RING_BUFFER_MMAP,
+ .oops = RING_BUFFER_OOPS_CONSISTENCY,
+ .ipi = RING_BUFFER_NO_IPI_BARRIER,
+ .wakeup = RING_BUFFER_WAKEUP_BY_WRITER,
+};
+
+static
+struct ltt_channel *_channel_create(const char *name,
+ struct ltt_channel *ltt_chan, void *buf_addr,
+ size_t subbuf_size, size_t num_subbuf,
+ unsigned int switch_timer_interval,
+ unsigned int read_timer_interval,
+ int *shm_fd, int *wait_fd,
+ uint64_t *memory_map_size)
+{
+ ltt_chan->handle = channel_create(&client_config, name, ltt_chan, buf_addr,
+ subbuf_size, num_subbuf, switch_timer_interval,
+ read_timer_interval, shm_fd, wait_fd,
+ memory_map_size);
+ if (!ltt_chan->handle)
+ return NULL;
+ ltt_chan->chan = shmp(ltt_chan->handle, ltt_chan->handle->chan);
+ return ltt_chan;
+}
+
+static
+void ltt_channel_destroy(struct ltt_channel *ltt_chan)
+{
+ channel_destroy(ltt_chan->chan, ltt_chan->handle, 0);
+}
+
+static
+struct lttng_ust_lib_ring_buffer *ltt_buffer_read_open(struct channel *chan,
+ struct lttng_ust_shm_handle *handle,
+ int *shm_fd, int *wait_fd,
+ uint64_t *memory_map_size)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ buf = channel_get_ring_buffer(&client_config, chan,
+ 0, handle, shm_fd, wait_fd, memory_map_size);
+ if (!lib_ring_buffer_open_read(buf, handle, 0))
+ return buf;
+ return NULL;
+}
+
+static
+void ltt_buffer_read_close(struct lttng_ust_lib_ring_buffer *buf,
+ struct lttng_ust_shm_handle *handle)
+{
+ lib_ring_buffer_release_read(buf, handle, 0);
+}
+
+static
+int ltt_event_reserve(struct lttng_ust_lib_ring_buffer_ctx *ctx, uint32_t event_id)
+{
+ return lib_ring_buffer_reserve(&client_config, ctx);
+}
+
+static
+void ltt_event_commit(struct lttng_ust_lib_ring_buffer_ctx *ctx)
+{
+ lib_ring_buffer_commit(&client_config, ctx);
+}
+
+static
+void ltt_event_write(struct lttng_ust_lib_ring_buffer_ctx *ctx, const void *src,
+ size_t len)
+{
+ lib_ring_buffer_write(&client_config, ctx, src, len);
+}
+
+static
+size_t ltt_packet_avail_size(struct channel *chan, struct lttng_ust_shm_handle *handle)
+
+{
+ unsigned long o_begin;
+ struct lttng_ust_lib_ring_buffer *buf;
+
+ buf = shmp(handle, chan->backend.buf[0].shmp); /* Only for global buffer ! */
+ o_begin = v_read(&client_config, &buf->offset);
+ if (subbuf_offset(o_begin, chan) != 0) {
+ return chan->backend.subbuf_size - subbuf_offset(o_begin, chan);
+ } else {
+ return chan->backend.subbuf_size - subbuf_offset(o_begin, chan)
+ - sizeof(struct metadata_packet_header);
+ }
+}
+
+#if 0
+static
+wait_queue_head_t *ltt_get_reader_wait_queue(struct channel *chan)
+{
+ return &chan->read_wait;
+}
+
+static
+wait_queue_head_t *ltt_get_hp_wait_queue(struct channel *chan)
+{
+ return &chan->hp_wait;
+}
+#endif //0
+
+static
+int ltt_is_finalized(struct channel *chan)
+{
+ return lib_ring_buffer_channel_is_finalized(chan);
+}
+
+static
+int ltt_is_disabled(struct channel *chan)
+{
+ return lib_ring_buffer_channel_is_disabled(chan);
+}
+
+static
+int ltt_flush_buffer(struct channel *chan, struct lttng_ust_shm_handle *handle)
+{
+ struct lttng_ust_lib_ring_buffer *buf;
+ int shm_fd, wait_fd;
+ uint64_t memory_map_size;
+
+ buf = channel_get_ring_buffer(&client_config, chan,
+ 0, handle, &shm_fd, &wait_fd,
+ &memory_map_size);
+ lib_ring_buffer_switch(&client_config, buf,
+ SWITCH_ACTIVE, handle);
+ return 0;
+}
+
+static struct ltt_transport ltt_relay_transport = {
+ .name = "relay-" RING_BUFFER_MODE_TEMPLATE_STRING "-mmap",
+ .ops = {
+ .channel_create = _channel_create,
+ .channel_destroy = ltt_channel_destroy,
+ .buffer_read_open = ltt_buffer_read_open,
+ .buffer_read_close = ltt_buffer_read_close,
+ .event_reserve = ltt_event_reserve,
+ .event_commit = ltt_event_commit,
+ .event_write = ltt_event_write,
+ .packet_avail_size = ltt_packet_avail_size,
+ //.get_reader_wait_queue = ltt_get_reader_wait_queue,
+ //.get_hp_wait_queue = ltt_get_hp_wait_queue,
+ .is_finalized = ltt_is_finalized,
+ .is_disabled = ltt_is_disabled,
+ .flush_buffer = ltt_flush_buffer,
+ },
+};
+
+void RING_BUFFER_MODE_TEMPLATE_INIT(void)
+{
+ DBG("LTT : ltt ring buffer client init\n");
+ ltt_transport_register(<t_relay_transport);
+}
+
+void RING_BUFFER_MODE_TEMPLATE_EXIT(void)
+{
+ DBG("LTT : ltt ring buffer client exit\n");
+ ltt_transport_unregister(<t_relay_transport);
+}
--- /dev/null
+#ifndef _LTT_TRACER_CORE_H
+#define _LTT_TRACER_CORE_H
+
+/*
+ * Copyright (C) 2005-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This contains the core definitions for the Linux Trace Toolkit.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1 of
+ * the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lttng/kcompat/kcompat.h>
+#include <lttng/core.h>
+#include <lttng/ust-tracer.h>
+#include <urcu/list.h>
+#include <lttng/usterr-signal-safe.h>
+#include "lttng/bug.h"
+
+#include <lttng/ringbuffer-config.h>
+
+struct ltt_session;
+struct ltt_channel;
+struct ltt_event;
+
+void ust_lock(void);
+void ust_unlock(void);
+
+#endif /* _LTT_TRACER_CORE_H */
--- /dev/null
+#ifndef _LTT_TRACER_H
+#define _LTT_TRACER_H
+
+/*
+ * Copyright (C) 2005-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This contains the definitions for the Linux Trace Toolkit tracer.
+ *
+ * Ported to userspace by Pierre-Marc Fournier.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1 of
+ * the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <lttng/core.h>
+#include <lttng/compat.h>
+#include <lttng/ust-events.h>
+#include "ltt-tracer-core.h"
+
+/* Number of bytes to log with a read/write event */
+#define LTT_LOG_RW_SIZE 32L
+#define LTT_MAX_SMALL_SIZE 0xFFFFU
+
+/* Tracer properties */
+#define CTF_MAGIC_NUMBER 0xC1FC1FC1
+#define TSDL_MAGIC_NUMBER 0x75D11D57
+
+/* CTF specification version followed */
+#define CTF_SPEC_MAJOR 1
+#define CTF_SPEC_MINOR 8
+
+/* Tracer major/minor versions */
+#define CTF_VERSION_MAJOR 0
+#define CTF_VERSION_MINOR 1
+
+/*
+ * Number of milliseconds to retry before failing metadata writes on buffer full
+ * condition. (10 seconds)
+ */
+#define LTTNG_METADATA_TIMEOUT_MSEC 10000
+
+#define LTT_RFLAG_EXTENDED RING_BUFFER_RFLAG_END
+#define LTT_RFLAG_END (LTT_RFLAG_EXTENDED << 1)
+
+#endif /* _LTT_TRACER_H */
--- /dev/null
+/*
+ * (C) Copyright 2009-2011 -
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST procname context.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <sys/prctl.h>
+#include <lttng/ust-events.h>
+#include <lttng/ust-tracer.h>
+#include <lttng/ringbuffer-config.h>
+#include <assert.h>
+
+#define PROCNAME_LEN 17 /* includes \0 */
+
+/*
+ * We cache the result to ensure we don't trigger a system call for
+ * each event.
+ * Upon exec, procname changes, but exec takes care of throwing away
+ * this cached version.
+ */
+static char cached_procname[17];
+
+static inline
+char *wrapper_getprocname(void)
+{
+ int ret;
+
+ if (caa_unlikely(!cached_procname[0])) {
+ ret = prctl(PR_GET_NAME, (unsigned long) cached_procname,
+ 0, 0, 0);
+ assert(!ret);
+ }
+ return cached_procname;
+}
+
+void lttng_context_procname_reset(void)
+{
+ cached_procname[0] = '\0';
+}
+
+static
+size_t procname_get_size(size_t offset)
+{
+ size_t size = 0;
+
+ size += PROCNAME_LEN;
+ return size;
+}
+
+static
+void procname_record(struct lttng_ctx_field *field,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct ltt_channel *chan)
+{
+ char *procname;
+
+ procname = wrapper_getprocname();
+ chan->ops->event_write(ctx, procname, PROCNAME_LEN);
+}
+
+int lttng_add_procname_to_ctx(struct lttng_ctx **ctx)
+{
+ struct lttng_ctx_field *field;
+
+ field = lttng_append_context(ctx);
+ if (!field)
+ return -ENOMEM;
+ if (lttng_find_context(*ctx, "procname")) {
+ lttng_remove_context_field(ctx, field);
+ return -EEXIST;
+ }
+ field->event_field.name = "procname";
+ field->event_field.type.atype = atype_array;
+ field->event_field.type.u.array.elem_type.atype = atype_integer;
+ field->event_field.type.u.array.elem_type.u.basic.integer.size = sizeof(char) * CHAR_BIT;
+ field->event_field.type.u.array.elem_type.u.basic.integer.alignment = lttng_alignof(char) * CHAR_BIT;
+ field->event_field.type.u.array.elem_type.u.basic.integer.signedness = lttng_is_signed_type(char);
+ field->event_field.type.u.array.elem_type.u.basic.integer.reverse_byte_order = 0;
+ field->event_field.type.u.array.elem_type.u.basic.integer.base = 10;
+ field->event_field.type.u.array.elem_type.u.basic.integer.encoding = lttng_encode_UTF8;
+ field->event_field.type.u.array.length = PROCNAME_LEN;
+ field->get_size = procname_get_size;
+ field->record = procname_record;
+ return 0;
+}
--- /dev/null
+/*
+ * (C) Copyright 2009-2011 -
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST pthread_id context.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <pthread.h>
+#include <lttng/ust-events.h>
+#include <lttng/ust-tracer.h>
+#include <lttng/ringbuffer-config.h>
+
+static
+size_t pthread_id_get_size(size_t offset)
+{
+ size_t size = 0;
+
+ size += lib_ring_buffer_align(offset, lttng_alignof(unsigned long));
+ size += sizeof(unsigned long);
+ return size;
+}
+
+static
+void pthread_id_record(struct lttng_ctx_field *field,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct ltt_channel *chan)
+{
+ unsigned long pthread_id;
+
+ pthread_id = (unsigned long) pthread_self();
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(pthread_id));
+ chan->ops->event_write(ctx, &pthread_id, sizeof(pthread_id));
+}
+
+int lttng_add_pthread_id_to_ctx(struct lttng_ctx **ctx)
+{
+ struct lttng_ctx_field *field;
+
+ field = lttng_append_context(ctx);
+ if (!field)
+ return -ENOMEM;
+ if (lttng_find_context(*ctx, "pthread_id")) {
+ lttng_remove_context_field(ctx, field);
+ return -EEXIST;
+ }
+ field->event_field.name = "pthread_id";
+ field->event_field.type.atype = atype_integer;
+ field->event_field.type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.alignment = lttng_alignof(unsigned long) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(unsigned long);
+ field->event_field.type.u.basic.integer.reverse_byte_order = 0;
+ field->event_field.type.u.basic.integer.base = 10;
+ field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
+ field->get_size = pthread_id_get_size;
+ field->record = pthread_id_record;
+ return 0;
+}
--- /dev/null
+/*
+ * (C) Copyright 2009-2011 -
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST vpid context.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <lttng/ust-events.h>
+#include <lttng/ust-tracer.h>
+#include <lttng/ringbuffer-config.h>
+
+#ifdef __linux__
+static inline
+pid_t wrapper_getpid(void)
+{
+ return getpid();
+}
+
+void lttng_context_vpid_reset(void)
+{
+}
+#else
+/*
+ * We cache the result to ensure we don't trigger a system call for
+ * each event.
+ */
+static pid_t cached_vpid;
+
+static inline
+pid_t wrapper_getpid(void)
+{
+ if (caa_unlikely(!cached_vpid))
+ cached_vpid = getpid();
+ return cached_vpid;
+}
+
+/*
+ * Upon fork or clone, the PID assigned to our thread is not the same as
+ * we kept in cache.
+ */
+void lttng_context_vpid_reset(void)
+{
+ cached_vpid = 0;
+}
+#endif
+
+static
+size_t vpid_get_size(size_t offset)
+{
+ size_t size = 0;
+
+ size += lib_ring_buffer_align(offset, lttng_alignof(pid_t));
+ size += sizeof(pid_t);
+ return size;
+}
+
+static
+void vpid_record(struct lttng_ctx_field *field,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct ltt_channel *chan)
+{
+ pid_t pid;
+
+ pid = wrapper_getpid();
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(pid));
+ chan->ops->event_write(ctx, &pid, sizeof(pid));
+}
+
+int lttng_add_vpid_to_ctx(struct lttng_ctx **ctx)
+{
+ struct lttng_ctx_field *field;
+
+ field = lttng_append_context(ctx);
+ if (!field)
+ return -ENOMEM;
+ if (lttng_find_context(*ctx, "vpid")) {
+ lttng_remove_context_field(ctx, field);
+ return -EEXIST;
+ }
+ field->event_field.name = "vpid";
+ field->event_field.type.atype = atype_integer;
+ field->event_field.type.u.basic.integer.size = sizeof(pid_t) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.alignment = lttng_alignof(pid_t) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(pid_t);
+ field->event_field.type.u.basic.integer.reverse_byte_order = 0;
+ field->event_field.type.u.basic.integer.base = 10;
+ field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
+ field->get_size = vpid_get_size;
+ field->record = vpid_record;
+ return 0;
+}
--- /dev/null
+/*
+ * (C) Copyright 2009-2011 -
+ * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST vtid context.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <lttng/ust-events.h>
+#include <lttng/ust-tracer.h>
+#include <lttng/ringbuffer-config.h>
+
+#ifdef __linux__
+#include <syscall.h>
+#endif
+
+#if defined(_syscall0)
+_syscall0(pid_t, gettid)
+#elif defined(__NR_gettid)
+static inline pid_t gettid(void)
+{
+ return syscall(__NR_gettid);
+}
+#else
+#warning "use pid as tid"
+static inline pid_t gettid(void)
+{
+ return getpid();
+}
+#endif
+
+/*
+ * We cache the result to ensure we don't trigger a system call for
+ * each event.
+ */
+static __thread pid_t cached_vtid;
+
+/*
+ * Upon fork or clone, the TID assigned to our thread is not the same as
+ * we kept in cache. Luckily, we are the only thread surviving in the
+ * child process, so we can simply clear our cached version.
+ */
+void lttng_context_vtid_reset(void)
+{
+ cached_vtid = 0;
+}
+
+static
+size_t vtid_get_size(size_t offset)
+{
+ size_t size = 0;
+
+ size += lib_ring_buffer_align(offset, lttng_alignof(pid_t));
+ size += sizeof(pid_t);
+ return size;
+}
+
+static
+void vtid_record(struct lttng_ctx_field *field,
+ struct lttng_ust_lib_ring_buffer_ctx *ctx,
+ struct ltt_channel *chan)
+{
+ if (caa_unlikely(!cached_vtid))
+ cached_vtid = gettid();
+ lib_ring_buffer_align_ctx(ctx, lttng_alignof(cached_vtid));
+ chan->ops->event_write(ctx, &cached_vtid, sizeof(cached_vtid));
+}
+
+int lttng_add_vtid_to_ctx(struct lttng_ctx **ctx)
+{
+ struct lttng_ctx_field *field;
+
+ field = lttng_append_context(ctx);
+ if (!field)
+ return -ENOMEM;
+ if (lttng_find_context(*ctx, "vtid")) {
+ lttng_remove_context_field(ctx, field);
+ return -EEXIST;
+ }
+ field->event_field.name = "vtid";
+ field->event_field.type.atype = atype_integer;
+ field->event_field.type.u.basic.integer.size = sizeof(pid_t) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.alignment = lttng_alignof(pid_t) * CHAR_BIT;
+ field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(pid_t);
+ field->event_field.type.u.basic.integer.reverse_byte_order = 0;
+ field->event_field.type.u.basic.integer.base = 10;
+ field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
+ field->get_size = vtid_get_size;
+ field->record = vtid_record;
+ return 0;
+}
--- /dev/null
+/*
+ * lttng-ust-abi.c
+ *
+ * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST ABI
+ *
+ * Mimic system calls for:
+ * - session creation, returns an object descriptor or failure.
+ * - channel creation, returns an object descriptor or failure.
+ * - Operates on a session object descriptor
+ * - Takes all channel options as parameters.
+ * - stream get, returns an object descriptor or failure.
+ * - Operates on a channel object descriptor.
+ * - stream notifier get, returns an object descriptor or failure.
+ * - Operates on a channel object descriptor.
+ * - event creation, returns an object descriptor or failure.
+ * - Operates on a channel object descriptor
+ * - Takes an event name as parameter
+ * - Takes an instrumentation source as parameter
+ * - e.g. tracepoints, dynamic_probes...
+ * - Takes instrumentation source specific arguments.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+#include <lttng/ust-abi.h>
+#include <urcu/compiler.h>
+#include <urcu/list.h>
+#include <lttng/ust-events.h>
+#include <lttng/usterr-signal-safe.h>
+#include "lttng/core.h"
+#include "ltt-tracer.h"
+
+/*
+ * Object descriptor table. Should be protected from concurrent access
+ * by the caller.
+ */
+
+struct lttng_ust_obj {
+ union {
+ struct {
+ void *private_data;
+ const struct lttng_ust_objd_ops *ops;
+ int f_count;
+ } s;
+ int freelist_next; /* offset freelist. end is -1. */
+ } u;
+};
+
+struct lttng_ust_objd_table {
+ struct lttng_ust_obj *array;
+ unsigned int len, allocated_len;
+ int freelist_head; /* offset freelist head. end is -1 */
+};
+
+static struct lttng_ust_objd_table objd_table = {
+ .freelist_head = -1,
+};
+
+static
+int objd_alloc(void *private_data, const struct lttng_ust_objd_ops *ops)
+{
+ struct lttng_ust_obj *obj;
+
+ if (objd_table.freelist_head != -1) {
+ obj = &objd_table.array[objd_table.freelist_head];
+ objd_table.freelist_head = obj->u.freelist_next;
+ goto end;
+ }
+
+ if (objd_table.len >= objd_table.allocated_len) {
+ unsigned int new_allocated_len, old_allocated_len;
+ struct lttng_ust_obj *new_table, *old_table;
+
+ old_allocated_len = objd_table.allocated_len;
+ old_table = objd_table.array;
+ if (!old_allocated_len)
+ new_allocated_len = 1;
+ else
+ new_allocated_len = old_allocated_len << 1;
+ new_table = zmalloc(sizeof(struct lttng_ust_obj) * new_allocated_len);
+ if (!new_table)
+ return -ENOMEM;
+ memcpy(new_table, old_table,
+ sizeof(struct lttng_ust_obj) * old_allocated_len);
+ free(old_table);
+ objd_table.array = new_table;
+ objd_table.allocated_len = new_allocated_len;
+ }
+ obj = &objd_table.array[objd_table.len];
+ objd_table.len++;
+end:
+ obj->u.s.private_data = private_data;
+ obj->u.s.ops = ops;
+ obj->u.s.f_count = 2; /* count == 1 : object is allocated */
+ /* count == 2 : allocated + hold ref */
+ return obj - objd_table.array;
+}
+
+static
+struct lttng_ust_obj *_objd_get(int id)
+{
+ if (id >= objd_table.len)
+ return NULL;
+ if (!objd_table.array[id].u.s.f_count)
+ return NULL;
+ return &objd_table.array[id];
+}
+
+static
+void *objd_private(int id)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+ assert(obj);
+ return obj->u.s.private_data;
+}
+
+static
+void objd_set_private(int id, void *private_data)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+ assert(obj);
+ obj->u.s.private_data = private_data;
+}
+
+const struct lttng_ust_objd_ops *objd_ops(int id)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+
+ if (!obj)
+ return NULL;
+ return obj->u.s.ops;
+}
+
+static
+void objd_free(int id)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+
+ assert(obj);
+ obj->u.freelist_next = objd_table.freelist_head;
+ objd_table.freelist_head = obj - objd_table.array;
+ assert(obj->u.s.f_count == 1);
+ obj->u.s.f_count = 0; /* deallocated */
+}
+
+static
+void objd_ref(int id)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+ obj->u.s.f_count++;
+}
+
+int lttng_ust_objd_unref(int id)
+{
+ struct lttng_ust_obj *obj = _objd_get(id);
+
+ if (!obj)
+ return -EINVAL;
+ if (obj->u.s.f_count == 1) {
+ ERR("Reference counting error\n");
+ return -EINVAL;
+ }
+ if ((--obj->u.s.f_count) == 1) {
+ const struct lttng_ust_objd_ops *ops = objd_ops(id);
+
+ if (ops->release)
+ ops->release(id);
+ objd_free(id);
+ }
+ return 0;
+}
+
+static
+void objd_table_destroy(void)
+{
+ int i;
+
+ for (i = 0; i < objd_table.allocated_len; i++)
+ (void) lttng_ust_objd_unref(i);
+ free(objd_table.array);
+ objd_table.array = NULL;
+ objd_table.len = 0;
+ objd_table.allocated_len = 0;
+ objd_table.freelist_head = -1;
+}
+
+/*
+ * This is LTTng's own personal way to create an ABI for sessiond.
+ * We send commands over a socket.
+ */
+
+static const struct lttng_ust_objd_ops lttng_ops;
+static const struct lttng_ust_objd_ops lttng_session_ops;
+static const struct lttng_ust_objd_ops lttng_channel_ops;
+static const struct lttng_ust_objd_ops lttng_metadata_ops;
+static const struct lttng_ust_objd_ops lttng_event_ops;
+static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops;
+
+enum channel_type {
+ PER_CPU_CHANNEL,
+ METADATA_CHANNEL,
+};
+
+int lttng_abi_create_root_handle(void)
+{
+ int root_handle;
+
+ root_handle = objd_alloc(NULL, <tng_ops);
+ return root_handle;
+}
+
+static
+int lttng_abi_create_session(void)
+{
+ struct ltt_session *session;
+ int session_objd, ret;
+
+ session = ltt_session_create();
+ if (!session)
+ return -ENOMEM;
+ session_objd = objd_alloc(session, <tng_session_ops);
+ if (session_objd < 0) {
+ ret = session_objd;
+ goto objd_error;
+ }
+ session->objd = session_objd;
+ return session_objd;
+
+objd_error:
+ ltt_session_destroy(session);
+ return ret;
+}
+
+#if 0
+static
+int lttng_abi_tracepoint_list(void)
+{
+ int list_objd, ret;
+
+ /* TODO: Create list private data */
+ list_objd = objd_alloc(NULL, <tng_tracepoint_list_ops);
+ if (list_objd < 0) {
+ ret = list_objd;
+ goto objd_error;
+ }
+
+ return list_objd;
+
+objd_error:
+ return ret;
+}
+#endif //0
+
+static
+long lttng_abi_tracer_version(int objd,
+ struct lttng_ust_tracer_version *v)
+{
+ v->version = LTTNG_UST_VERSION;
+ v->patchlevel = LTTNG_UST_PATCHLEVEL;
+ v->sublevel = LTTNG_UST_SUBLEVEL;
+ return 0;
+}
+
+static
+long lttng_abi_add_context(int objd,
+ struct lttng_ust_context *context_param,
+ struct lttng_ctx **ctx, struct ltt_session *session)
+{
+ if (session->been_active)
+ return -EPERM;
+
+ switch (context_param->ctx) {
+ case LTTNG_UST_CONTEXT_PTHREAD_ID:
+ return lttng_add_pthread_id_to_ctx(ctx);
+ case LTTNG_UST_CONTEXT_VTID:
+ return lttng_add_vtid_to_ctx(ctx);
+ case LTTNG_UST_CONTEXT_VPID:
+ return lttng_add_vpid_to_ctx(ctx);
+ case LTTNG_UST_CONTEXT_PROCNAME:
+ return lttng_add_procname_to_ctx(ctx);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * lttng_cmd - lttng control through socket commands
+ *
+ * @objd: the object descriptor
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This descriptor implements lttng commands:
+ * LTTNG_UST_SESSION
+ * Returns a LTTng trace session object descriptor
+ * LTTNG_UST_TRACER_VERSION
+ * Returns the LTTng kernel tracer version
+ * LTTNG_UST_TRACEPOINT_LIST
+ * Returns a file descriptor listing available tracepoints
+ * LTTNG_UST_WAIT_QUIESCENT
+ * Returns after all previously running probes have completed
+ *
+ * The returned session will be deleted when its file descriptor is closed.
+ */
+static
+long lttng_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case LTTNG_UST_SESSION:
+ return lttng_abi_create_session();
+ case LTTNG_UST_TRACER_VERSION:
+ return lttng_abi_tracer_version(objd,
+ (struct lttng_ust_tracer_version *) arg);
+ case LTTNG_UST_TRACEPOINT_LIST:
+ return -ENOSYS; //TODO
+ //return lttng_abi_tracepoint_list();
+ case LTTNG_UST_WAIT_QUIESCENT:
+ synchronize_trace();
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct lttng_ust_objd_ops lttng_ops = {
+ .cmd = lttng_cmd,
+};
+
+/*
+ * We tolerate no failure in this function (if one happens, we print a dmesg
+ * error, but cannot return any error, because the channel information is
+ * invariant.
+ */
+static
+void lttng_metadata_create_events(int channel_objd)
+{
+ struct ltt_channel *channel = objd_private(channel_objd);
+ static struct lttng_ust_event metadata_params = {
+ .instrumentation = LTTNG_UST_TRACEPOINT,
+ .name = "lttng_metadata",
+ };
+ struct ltt_event *event;
+ int ret;
+
+ /*
+ * We tolerate no failure path after event creation. It will stay
+ * invariant for the rest of the session.
+ */
+ event = ltt_event_create(channel, &metadata_params, NULL);
+ if (!event) {
+ ret = -EINVAL;
+ goto create_error;
+ }
+ return;
+
+create_error:
+ WARN_ON(1);
+ return; /* not allowed to return error */
+}
+
+int lttng_abi_create_channel(int session_objd,
+ struct lttng_ust_channel *chan_param,
+ enum channel_type channel_type)
+{
+ struct ltt_session *session = objd_private(session_objd);
+ const struct lttng_ust_objd_ops *ops;
+ const char *transport_name;
+ struct ltt_channel *chan;
+ int chan_objd;
+ int ret = 0;
+
+ chan_objd = objd_alloc(NULL, <tng_channel_ops);
+ if (chan_objd < 0) {
+ ret = chan_objd;
+ goto objd_error;
+ }
+ switch (channel_type) {
+ case PER_CPU_CHANNEL:
+ if (chan_param->output == LTTNG_UST_MMAP) {
+ transport_name = chan_param->overwrite ?
+ "relay-overwrite-mmap" : "relay-discard-mmap";
+ } else {
+ return -EINVAL;
+ }
+ ops = <tng_channel_ops;
+ break;
+ case METADATA_CHANNEL:
+ if (chan_param->output == LTTNG_UST_MMAP)
+ transport_name = "relay-metadata-mmap";
+ else
+ return -EINVAL;
+ ops = <tng_metadata_ops;
+ break;
+ default:
+ transport_name = "<unknown>";
+ break;
+ }
+ /*
+ * We tolerate no failure path after channel creation. It will stay
+ * invariant for the rest of the session.
+ */
+ chan = ltt_channel_create(session, transport_name, NULL,
+ chan_param->subbuf_size,
+ chan_param->num_subbuf,
+ chan_param->switch_timer_interval,
+ chan_param->read_timer_interval,
+ &chan_param->shm_fd,
+ &chan_param->wait_fd,
+ &chan_param->memory_map_size);
+ if (!chan) {
+ ret = -EINVAL;
+ goto chan_error;
+ }
+ objd_set_private(chan_objd, chan);
+ chan->objd = chan_objd;
+ if (channel_type == METADATA_CHANNEL) {
+ session->metadata = chan;
+ lttng_metadata_create_events(chan_objd);
+ }
+
+ /* The channel created holds a reference on the session */
+ objd_ref(session_objd);
+
+ return chan_objd;
+
+chan_error:
+ {
+ int err;
+
+ err = lttng_ust_objd_unref(chan_objd);
+ assert(!err);
+ }
+objd_error:
+ return ret;
+}
+
+/**
+ * lttng_session_cmd - lttng session object command
+ *
+ * @obj: the object
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This descriptor implements lttng commands:
+ * LTTNG_UST_CHANNEL
+ * Returns a LTTng channel object descriptor
+ * LTTNG_UST_ENABLE
+ * Enables tracing for a session (weak enable)
+ * LTTNG_UST_DISABLE
+ * Disables tracing for a session (strong disable)
+ * LTTNG_UST_METADATA
+ * Returns a LTTng metadata object descriptor
+ *
+ * The returned channel will be deleted when its file descriptor is closed.
+ */
+static
+long lttng_session_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ struct ltt_session *session = objd_private(objd);
+
+ switch (cmd) {
+ case LTTNG_UST_CHANNEL:
+ return lttng_abi_create_channel(objd,
+ (struct lttng_ust_channel *) arg,
+ PER_CPU_CHANNEL);
+ case LTTNG_UST_SESSION_START:
+ case LTTNG_UST_ENABLE:
+ return ltt_session_enable(session);
+ case LTTNG_UST_SESSION_STOP:
+ case LTTNG_UST_DISABLE:
+ return ltt_session_disable(session);
+ case LTTNG_UST_METADATA:
+ return lttng_abi_create_channel(objd,
+ (struct lttng_ust_channel *) arg,
+ METADATA_CHANNEL);
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Called when the last file reference is dropped.
+ *
+ * Big fat note: channels and events are invariant for the whole session after
+ * their creation. So this session destruction also destroys all channel and
+ * event structures specific to this session (they are not destroyed when their
+ * individual file is released).
+ */
+static
+int lttng_release_session(int objd)
+{
+ struct ltt_session *session = objd_private(objd);
+
+ if (session) {
+ ltt_session_destroy(session);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+}
+
+static const struct lttng_ust_objd_ops lttng_session_ops = {
+ .release = lttng_release_session,
+ .cmd = lttng_session_cmd,
+};
+
+struct stream_priv_data {
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ltt_channel *ltt_chan;
+};
+
+static
+int lttng_abi_open_stream(int channel_objd, struct lttng_ust_stream *info)
+{
+ struct ltt_channel *channel = objd_private(channel_objd);
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct stream_priv_data *priv;
+ int stream_objd, ret;
+
+ buf = channel->ops->buffer_read_open(channel->chan, channel->handle,
+ &info->shm_fd, &info->wait_fd, &info->memory_map_size);
+ if (!buf)
+ return -ENOENT;
+
+ priv = zmalloc(sizeof(*priv));
+ if (!priv) {
+ ret = -ENOMEM;
+ goto alloc_error;
+ }
+ priv->buf = buf;
+ priv->ltt_chan = channel;
+ stream_objd = objd_alloc(priv, &lib_ring_buffer_objd_ops);
+ if (stream_objd < 0) {
+ ret = stream_objd;
+ goto objd_error;
+ }
+ /* Hold a reference on the channel object descriptor */
+ objd_ref(channel_objd);
+ return stream_objd;
+
+objd_error:
+ free(priv);
+alloc_error:
+ channel->ops->buffer_read_close(buf, channel->handle);
+ return ret;
+}
+
+static
+int lttng_abi_create_event(int channel_objd,
+ struct lttng_ust_event *event_param)
+{
+ struct ltt_channel *channel = objd_private(channel_objd);
+ struct ltt_event *event;
+ int event_objd, ret;
+
+ event_param->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
+ event_objd = objd_alloc(NULL, <tng_event_ops);
+ if (event_objd < 0) {
+ ret = event_objd;
+ goto objd_error;
+ }
+ /*
+ * We tolerate no failure path after event creation. It will stay
+ * invariant for the rest of the session.
+ */
+ event = ltt_event_create(channel, event_param, NULL);
+ if (!event) {
+ ret = -EINVAL;
+ goto event_error;
+ }
+ objd_set_private(event_objd, event);
+ /* The event holds a reference on the channel */
+ objd_ref(channel_objd);
+ return event_objd;
+
+event_error:
+ {
+ int err;
+
+ err = lttng_ust_objd_unref(event_objd);
+ assert(!err);
+ }
+objd_error:
+ return ret;
+}
+
+/**
+ * lttng_channel_cmd - lttng control through object descriptors
+ *
+ * @objd: the object descriptor
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This object descriptor implements lttng commands:
+ * LTTNG_UST_STREAM
+ * Returns an event stream object descriptor or failure.
+ * (typically, one event stream records events from one CPU)
+ * LTTNG_UST_EVENT
+ * Returns an event object descriptor or failure.
+ * LTTNG_UST_CONTEXT
+ * Prepend a context field to each event in the channel
+ * LTTNG_UST_ENABLE
+ * Enable recording for events in this channel (weak enable)
+ * LTTNG_UST_DISABLE
+ * Disable recording for events in this channel (strong disable)
+ *
+ * Channel and event file descriptors also hold a reference on the session.
+ */
+static
+long lttng_channel_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ struct ltt_channel *channel = objd_private(objd);
+
+ switch (cmd) {
+ case LTTNG_UST_STREAM:
+ {
+ struct lttng_ust_stream *stream;
+
+ stream = (struct lttng_ust_stream *) arg;
+ /* stream used as output */
+ return lttng_abi_open_stream(objd, stream);
+ }
+ case LTTNG_UST_EVENT:
+ return lttng_abi_create_event(objd, (struct lttng_ust_event *) arg);
+ case LTTNG_UST_CONTEXT:
+ return lttng_abi_add_context(objd,
+ (struct lttng_ust_context *) arg,
+ &channel->ctx, channel->session);
+ case LTTNG_UST_ENABLE:
+ return ltt_channel_enable(channel);
+ case LTTNG_UST_DISABLE:
+ return ltt_channel_disable(channel);
+ case LTTNG_UST_FLUSH_BUFFER:
+ return channel->ops->flush_buffer(channel->chan, channel->handle);
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * lttng_metadata_cmd - lttng control through object descriptors
+ *
+ * @objd: the object descriptor
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This object descriptor implements lttng commands:
+ * LTTNG_UST_STREAM
+ * Returns an event stream file descriptor or failure.
+ *
+ * Channel and event file descriptors also hold a reference on the session.
+ */
+static
+long lttng_metadata_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ struct ltt_channel *channel = objd_private(objd);
+
+ switch (cmd) {
+ case LTTNG_UST_STREAM:
+ {
+ struct lttng_ust_stream *stream;
+
+ stream = (struct lttng_ust_stream *) arg;
+ /* stream used as output */
+ return lttng_abi_open_stream(objd, stream);
+ }
+ case LTTNG_UST_FLUSH_BUFFER:
+ return channel->ops->flush_buffer(channel->chan, channel->handle);
+ default:
+ return -EINVAL;
+ }
+}
+
+#if 0
+/**
+ * lttng_channel_poll - lttng stream addition/removal monitoring
+ *
+ * @file: the file
+ * @wait: poll table
+ */
+unsigned int lttng_channel_poll(struct file *file, poll_table *wait)
+{
+ struct ltt_channel *channel = file->private_data;
+ unsigned int mask = 0;
+
+ if (file->f_mode & FMODE_READ) {
+ poll_wait_set_exclusive(wait);
+ poll_wait(file, channel->ops->get_hp_wait_queue(channel->chan),
+ wait);
+
+ if (channel->ops->is_disabled(channel->chan))
+ return POLLERR;
+ if (channel->ops->is_finalized(channel->chan))
+ return POLLHUP;
+ if (channel->ops->buffer_has_read_closed_stream(channel->chan))
+ return POLLIN | POLLRDNORM;
+ return 0;
+ }
+ return mask;
+
+}
+#endif //0
+
+static
+int lttng_channel_release(int objd)
+{
+ struct ltt_channel *channel = objd_private(objd);
+
+ if (channel)
+ return lttng_ust_objd_unref(channel->session->objd);
+ return 0;
+}
+
+static const struct lttng_ust_objd_ops lttng_channel_ops = {
+ .release = lttng_channel_release,
+ //.poll = lttng_channel_poll,
+ .cmd = lttng_channel_cmd,
+};
+
+static const struct lttng_ust_objd_ops lttng_metadata_ops = {
+ .release = lttng_channel_release,
+ .cmd = lttng_metadata_cmd,
+};
+
+/**
+ * lttng_rb_cmd - lttng ring buffer control through object descriptors
+ *
+ * @objd: the object descriptor
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This object descriptor implements lttng commands:
+ * (None for now. Access is done directly though shm.)
+ */
+static
+long lttng_rb_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ default:
+ return -EINVAL;
+ }
+}
+
+static
+int lttng_rb_release(int objd)
+{
+ struct stream_priv_data *priv = objd_private(objd);
+ struct lttng_ust_lib_ring_buffer *buf;
+ struct ltt_channel *channel;
+
+ if (priv) {
+ buf = priv->buf;
+ channel = priv->ltt_chan;
+ free(priv);
+ channel->ops->buffer_read_close(buf, channel->handle);
+
+ return lttng_ust_objd_unref(channel->objd);
+ }
+ return 0;
+}
+
+static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops = {
+ .release = lttng_rb_release,
+ .cmd = lttng_rb_cmd,
+};
+
+/**
+ * lttng_event_cmd - lttng control through object descriptors
+ *
+ * @objd: the object descriptor
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This object descriptor implements lttng commands:
+ * LTTNG_UST_CONTEXT
+ * Prepend a context field to each record of this event
+ * LTTNG_UST_ENABLE
+ * Enable recording for this event (weak enable)
+ * LTTNG_UST_DISABLE
+ * Disable recording for this event (strong disable)
+ */
+static
+long lttng_event_cmd(int objd, unsigned int cmd, unsigned long arg)
+{
+ struct ltt_event *event = objd_private(objd);
+
+ switch (cmd) {
+ case LTTNG_UST_CONTEXT:
+ return lttng_abi_add_context(objd,
+ (struct lttng_ust_context *) arg,
+ &event->ctx, event->chan->session);
+ case LTTNG_UST_ENABLE:
+ return ltt_event_enable(event);
+ case LTTNG_UST_DISABLE:
+ return ltt_event_disable(event);
+ default:
+ return -EINVAL;
+ }
+}
+
+static
+int lttng_event_release(int objd)
+{
+ struct ltt_event *event = objd_private(objd);
+
+ if (event)
+ return lttng_ust_objd_unref(event->chan->objd);
+ return 0;
+}
+
+/* TODO: filter control ioctl */
+static const struct lttng_ust_objd_ops lttng_event_ops = {
+ .release = lttng_event_release,
+ .cmd = lttng_event_cmd,
+};
+
+void lttng_ust_abi_exit(void)
+{
+ objd_table_destroy();
+}
--- /dev/null
+/*
+ * lttng-ust-comm.c
+ *
+ * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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 _LGPL_SOURCE
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/prctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <time.h>
+#include <assert.h>
+#include <signal.h>
+#include <urcu/uatomic.h>
+#include <urcu/futex.h>
+
+#include <lttng/ust-comm.h>
+#include <lttng/ust-events.h>
+#include <lttng/usterr-signal-safe.h>
+#include <lttng/ust-abi.h>
+#include <lttng/tracepoint.h>
+#include <lttng/tracepoint-internal.h>
+#include <lttng/ust.h>
+#include "ltt-tracer-core.h"
+
+/*
+ * Has lttng ust comm constructor been called ?
+ */
+static int initialized;
+
+/*
+ * The ust_lock/ust_unlock lock is used as a communication thread mutex.
+ * Held when handling a command, also held by fork() to deal with
+ * removal of threads, and by exit path.
+ */
+
+/* Should the ust comm thread quit ? */
+static int lttng_ust_comm_should_quit;
+
+/*
+ * Wait for either of these before continuing to the main
+ * program:
+ * - the register_done message from sessiond daemon
+ * (will let the sessiond daemon enable sessions before main
+ * starts.)
+ * - sessiond daemon is not reachable.
+ * - timeout (ensuring applications are resilient to session
+ * daemon problems).
+ */
+static sem_t constructor_wait;
+/*
+ * Doing this for both the global and local sessiond.
+ */
+static int sem_count = { 2 };
+
+/*
+ * Info about socket and associated listener thread.
+ */
+struct sock_info {
+ const char *name;
+ pthread_t ust_listener; /* listener thread */
+ int root_handle;
+ int constructor_sem_posted;
+ int allowed;
+ int global;
+
+ char sock_path[PATH_MAX];
+ int socket;
+
+ char wait_shm_path[PATH_MAX];
+ char *wait_shm_mmap;
+};
+
+/* Socket from app (connect) to session daemon (listen) for communication */
+struct sock_info global_apps = {
+ .name = "global",
+ .global = 1,
+
+ .root_handle = -1,
+ .allowed = 1,
+
+ .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
+ .socket = -1,
+
+ .wait_shm_path = DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH,
+};
+
+/* TODO: allow global_apps_sock_path override */
+
+struct sock_info local_apps = {
+ .name = "local",
+ .global = 0,
+ .root_handle = -1,
+ .allowed = 0, /* Check setuid bit first */
+
+ .socket = -1,
+};
+
+static int wait_poll_fallback;
+
+extern void ltt_ring_buffer_client_overwrite_init(void);
+extern void ltt_ring_buffer_client_discard_init(void);
+extern void ltt_ring_buffer_metadata_client_init(void);
+extern void ltt_ring_buffer_client_overwrite_exit(void);
+extern void ltt_ring_buffer_client_discard_exit(void);
+extern void ltt_ring_buffer_metadata_client_exit(void);
+
+static
+int setup_local_apps(void)
+{
+ const char *home_dir;
+ uid_t uid;
+
+ uid = getuid();
+ /*
+ * Disallow per-user tracing for setuid binaries.
+ */
+ if (uid != geteuid()) {
+ local_apps.allowed = 0;
+ return 0;
+ } else {
+ local_apps.allowed = 1;
+ }
+ home_dir = (const char *) getenv("HOME");
+ if (!home_dir)
+ return -ENOENT;
+ snprintf(local_apps.sock_path, PATH_MAX,
+ DEFAULT_HOME_APPS_UNIX_SOCK, home_dir);
+ snprintf(local_apps.wait_shm_path, PATH_MAX,
+ DEFAULT_HOME_APPS_WAIT_SHM_PATH, uid);
+ return 0;
+}
+
+static
+int register_app_to_sessiond(int socket)
+{
+ ssize_t ret;
+ int prctl_ret;
+ struct {
+ uint32_t major;
+ uint32_t minor;
+ pid_t pid;
+ pid_t ppid;
+ uid_t uid;
+ gid_t gid;
+ char name[16]; /* process name */
+ } reg_msg;
+
+ reg_msg.major = LTTNG_UST_COMM_VERSION_MAJOR;
+ reg_msg.minor = LTTNG_UST_COMM_VERSION_MINOR;
+ reg_msg.pid = getpid();
+ reg_msg.ppid = getppid();
+ reg_msg.uid = getuid();
+ reg_msg.gid = getgid();
+ prctl_ret = prctl(PR_GET_NAME, (unsigned long) reg_msg.name, 0, 0, 0);
+ if (prctl_ret) {
+ ERR("Error executing prctl");
+ return -errno;
+ }
+
+ ret = ustcomm_send_unix_sock(socket, ®_msg, sizeof(reg_msg));
+ if (ret >= 0 && ret != sizeof(reg_msg))
+ return -EIO;
+ return ret;
+}
+
+static
+int send_reply(int sock, struct ustcomm_ust_reply *lur)
+{
+ ssize_t len;
+
+ len = ustcomm_send_unix_sock(sock, lur, sizeof(*lur));
+ switch (len) {
+ case sizeof(*lur):
+ DBG("message successfully sent");
+ return 0;
+ case -1:
+ if (errno == ECONNRESET) {
+ printf("remote end closed connection\n");
+ return 0;
+ }
+ return -1;
+ default:
+ printf("incorrect message size: %zd\n", len);
+ return -1;
+ }
+}
+
+static
+int handle_register_done(struct sock_info *sock_info)
+{
+ int ret;
+
+ if (sock_info->constructor_sem_posted)
+ return 0;
+ sock_info->constructor_sem_posted = 1;
+ if (uatomic_read(&sem_count) <= 0) {
+ return 0;
+ }
+ ret = uatomic_add_return(&sem_count, -1);
+ if (ret == 0) {
+ ret = sem_post(&constructor_wait);
+ assert(!ret);
+ }
+ return 0;
+}
+
+static
+int handle_message(struct sock_info *sock_info,
+ int sock, struct ustcomm_ust_msg *lum)
+{
+ int ret = 0;
+ const struct lttng_ust_objd_ops *ops;
+ struct ustcomm_ust_reply lur;
+ int shm_fd, wait_fd;
+
+ ust_lock();
+
+ memset(&lur, 0, sizeof(lur));
+
+ if (lttng_ust_comm_should_quit) {
+ ret = -EPERM;
+ goto end;
+ }
+
+ ops = objd_ops(lum->handle);
+ if (!ops) {
+ ret = -ENOENT;
+ goto end;
+ }
+
+ switch (lum->cmd) {
+ case LTTNG_UST_REGISTER_DONE:
+ if (lum->handle == LTTNG_UST_ROOT_HANDLE)
+ ret = handle_register_done(sock_info);
+ else
+ ret = -EINVAL;
+ break;
+ case LTTNG_UST_RELEASE:
+ if (lum->handle == LTTNG_UST_ROOT_HANDLE)
+ ret = -EPERM;
+ else
+ ret = lttng_ust_objd_unref(lum->handle);
+ break;
+ default:
+ if (ops->cmd)
+ ret = ops->cmd(lum->handle, lum->cmd,
+ (unsigned long) &lum->u);
+ else
+ ret = -ENOSYS;
+ break;
+ }
+
+end:
+ lur.handle = lum->handle;
+ lur.cmd = lum->cmd;
+ lur.ret_val = ret;
+ if (ret >= 0) {
+ lur.ret_code = USTCOMM_OK;
+ } else {
+ //lur.ret_code = USTCOMM_SESSION_FAIL;
+ lur.ret_code = ret;
+ }
+ switch (lum->cmd) {
+ case LTTNG_UST_STREAM:
+ /*
+ * Special-case reply to send stream info.
+ * Use lum.u output.
+ */
+ lur.u.stream.memory_map_size = lum->u.stream.memory_map_size;
+ shm_fd = lum->u.stream.shm_fd;
+ wait_fd = lum->u.stream.wait_fd;
+ break;
+ case LTTNG_UST_METADATA:
+ case LTTNG_UST_CHANNEL:
+ lur.u.channel.memory_map_size = lum->u.channel.memory_map_size;
+ shm_fd = lum->u.channel.shm_fd;
+ wait_fd = lum->u.channel.wait_fd;
+ break;
+ case LTTNG_UST_VERSION:
+ lur.u.version = lum->u.version;
+ break;
+ }
+ ret = send_reply(sock, &lur);
+ if (ret < 0) {
+ perror("error sending reply");
+ goto error;
+ }
+
+ if ((lum->cmd == LTTNG_UST_STREAM
+ || lum->cmd == LTTNG_UST_CHANNEL
+ || lum->cmd == LTTNG_UST_METADATA)
+ && lur.ret_code == USTCOMM_OK) {
+ /* we also need to send the file descriptors. */
+ ret = ustcomm_send_fds_unix_sock(sock,
+ &shm_fd, &shm_fd,
+ 1, sizeof(int));
+ if (ret < 0) {
+ perror("send shm_fd");
+ goto error;
+ }
+ ret = ustcomm_send_fds_unix_sock(sock,
+ &wait_fd, &wait_fd,
+ 1, sizeof(int));
+ if (ret < 0) {
+ perror("send wait_fd");
+ goto error;
+ }
+ }
+error:
+ ust_unlock();
+ return ret;
+}
+
+static
+void cleanup_sock_info(struct sock_info *sock_info)
+{
+ int ret;
+
+ if (sock_info->socket != -1) {
+ ret = close(sock_info->socket);
+ if (ret) {
+ ERR("Error closing apps socket");
+ }
+ sock_info->socket = -1;
+ }
+ if (sock_info->root_handle != -1) {
+ ret = lttng_ust_objd_unref(sock_info->root_handle);
+ if (ret) {
+ ERR("Error unref root handle");
+ }
+ sock_info->root_handle = -1;
+ }
+ sock_info->constructor_sem_posted = 0;
+ if (sock_info->wait_shm_mmap) {
+ ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE));
+ if (ret) {
+ ERR("Error unmapping wait shm");
+ }
+ sock_info->wait_shm_mmap = NULL;
+ }
+}
+
+/*
+ * 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(struct sock_info *sock_info, size_t mmap_size)
+{
+ int wait_shm_fd, ret;
+ pid_t pid;
+
+ /*
+ * Try to open read-only.
+ */
+ wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+ if (wait_shm_fd >= 0) {
+ goto end;
+ } else if (wait_shm_fd < 0 && errno != ENOENT) {
+ /*
+ * Real-only open did not work, and it's not because the
+ * entry was not present. It's a failure that prohibits
+ * using shm.
+ */
+ ERR("Error opening shm %s", sock_info->wait_shm_path);
+ goto end;
+ }
+ /*
+ * If the open failed because the file did not exist, try
+ * creating it ourself.
+ */
+ pid = fork();
+ if (pid > 0) {
+ int status;
+
+ /*
+ * Parent: wait for child to return, in which case the
+ * shared memory map will have been created.
+ */
+ pid = wait(&status);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ wait_shm_fd = -1;
+ goto end;
+ }
+ /*
+ * Try to open read-only again after creation.
+ */
+ wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
+ if (wait_shm_fd < 0) {
+ /*
+ * Real-only open did not work. It's a failure
+ * that prohibits using shm.
+ */
+ ERR("Error opening shm %s", sock_info->wait_shm_path);
+ goto end;
+ }
+ goto end;
+ } else if (pid == 0) {
+ int create_mode;
+
+ /* Child */
+ create_mode = S_IRUSR | S_IWUSR | S_IRGRP;
+ if (sock_info->global)
+ create_mode |= S_IROTH | S_IWGRP | S_IWOTH;
+ /*
+ * We're alone in a child process, so we can modify the
+ * process-wide umask.
+ */
+ umask(~create_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(sock_info->wait_shm_path,
+ O_RDWR | O_CREAT, create_mode);
+ if (wait_shm_fd >= 0) {
+ ret = ftruncate(wait_shm_fd, mmap_size);
+ if (ret) {
+ PERROR("ftruncate");
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+ }
+ /*
+ * For local shm, we need to have rw access to accept
+ * opening it: this means the local sessiond will be
+ * able to wake us up. For global shm, we open it even
+ * if rw access is not granted, because the root.root
+ * sessiond will be able to override all rights and wake
+ * us up.
+ */
+ if (!sock_info->global && errno != EACCES) {
+ ERR("Error opening shm %s", sock_info->wait_shm_path);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * The shm exists, but we cannot open it RW. Report
+ * success.
+ */
+ exit(EXIT_SUCCESS);
+ } else {
+ return -1;
+ }
+end:
+ if (wait_shm_fd >= 0 && !sock_info->global) {
+ struct stat statbuf;
+
+ /*
+ * Ensure that our user is the owner of the shm file for
+ * local shm. If we do not own the file, it means our
+ * sessiond will not have access to wake us up (there is
+ * probably a rogue process trying to fake our
+ * sessiond). Fallback to polling method in this case.
+ */
+ ret = fstat(wait_shm_fd, &statbuf);
+ if (ret) {
+ PERROR("fstat");
+ goto error_close;
+ }
+ if (statbuf.st_uid != getuid())
+ goto error_close;
+ }
+ return wait_shm_fd;
+
+error_close:
+ ret = close(wait_shm_fd);
+ if (ret) {
+ PERROR("Error closing fd");
+ }
+ return -1;
+}
+
+static
+char *get_map_shm(struct sock_info *sock_info)
+{
+ size_t mmap_size = sysconf(_SC_PAGE_SIZE);
+ int wait_shm_fd, ret;
+ char *wait_shm_mmap;
+
+ wait_shm_fd = get_wait_shm(sock_info, mmap_size);
+ if (wait_shm_fd < 0) {
+ goto error;
+ }
+ wait_shm_mmap = mmap(NULL, mmap_size, 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 sessiond). Fallback to poll mode.");
+ goto error;
+ }
+ return wait_shm_mmap;
+
+error:
+ return NULL;
+}
+
+static
+void wait_for_sessiond(struct sock_info *sock_info)
+{
+ int ret;
+
+ ust_lock();
+ if (lttng_ust_comm_should_quit) {
+ goto quit;
+ }
+ if (wait_poll_fallback) {
+ goto error;
+ }
+ if (!sock_info->wait_shm_mmap) {
+ sock_info->wait_shm_mmap = get_map_shm(sock_info);
+ if (!sock_info->wait_shm_mmap)
+ goto error;
+ }
+ ust_unlock();
+
+ DBG("Waiting for %s apps sessiond", sock_info->name);
+ /* Wait for futex wakeup */
+ if (uatomic_read((int32_t *) sock_info->wait_shm_mmap) == 0) {
+ ret = futex_async((int32_t *) sock_info->wait_shm_mmap,
+ FUTEX_WAIT, 0, NULL, NULL, 0);
+ if (ret < 0) {
+ if (errno == EFAULT) {
+ wait_poll_fallback = 1;
+ WARN(
+"Linux kernels 2.6.33 to 3.0 (with the exception of stable versions) "
+"do not support FUTEX_WAKE on read-only memory mappings correctly. "
+"Please upgrade your kernel "
+"(fix is commit 9ea71503a8ed9184d2d0b8ccc4d269d05f7940ae in Linux kernel "
+"mainline). LTTng-UST will use polling mode fallback.");
+ }
+ PERROR("futex");
+ }
+ }
+ return;
+
+quit:
+ ust_unlock();
+ return;
+
+error:
+ ust_unlock();
+ return;
+}
+
+/*
+ * This thread does not allocate any resource, except within
+ * handle_message, within mutex protection. This mutex protects against
+ * fork and exit.
+ * The other moment it allocates resources is at socket connexion, which
+ * is also protected by the mutex.
+ */
+static
+void *ust_listener_thread(void *arg)
+{
+ struct sock_info *sock_info = arg;
+ int sock, ret, prev_connect_failed = 0, has_waited = 0;
+
+ /* Restart trying to connect to the session daemon */
+restart:
+ if (prev_connect_failed) {
+ /* Wait for sessiond availability with pipe */
+ wait_for_sessiond(sock_info);
+ if (has_waited) {
+ has_waited = 0;
+ /*
+ * Sleep for 5 seconds before retrying after a
+ * sequence of failure / wait / failure. This
+ * deals with a killed or broken session daemon.
+ */
+ sleep(5);
+ }
+ has_waited = 1;
+ prev_connect_failed = 0;
+ }
+ ust_lock();
+
+ if (lttng_ust_comm_should_quit) {
+ ust_unlock();
+ goto quit;
+ }
+
+ if (sock_info->socket != -1) {
+ ret = close(sock_info->socket);
+ if (ret) {
+ ERR("Error closing %s apps socket", sock_info->name);
+ }
+ sock_info->socket = -1;
+ }
+
+ /* Register */
+ ret = ustcomm_connect_unix_sock(sock_info->sock_path);
+ if (ret < 0) {
+ ERR("Error connecting to %s apps socket", sock_info->name);
+ prev_connect_failed = 1;
+ /*
+ * If we cannot find the sessiond daemon, don't delay
+ * constructor execution.
+ */
+ ret = handle_register_done(sock_info);
+ assert(!ret);
+ ust_unlock();
+ goto restart;
+ }
+
+ sock_info->socket = sock = ret;
+
+ /*
+ * Create only one root handle per listener thread for the whole
+ * process lifetime.
+ */
+ if (sock_info->root_handle == -1) {
+ ret = lttng_abi_create_root_handle();
+ if (ret < 0) {
+ ERR("Error creating root handle");
+ ust_unlock();
+ goto quit;
+ }
+ sock_info->root_handle = ret;
+ }
+
+ ret = register_app_to_sessiond(sock);
+ if (ret < 0) {
+ ERR("Error registering to %s apps socket", sock_info->name);
+ prev_connect_failed = 1;
+ /*
+ * If we cannot register to the sessiond daemon, don't
+ * delay constructor execution.
+ */
+ ret = handle_register_done(sock_info);
+ assert(!ret);
+ ust_unlock();
+ goto restart;
+ }
+ ust_unlock();
+
+ for (;;) {
+ ssize_t len;
+ struct ustcomm_ust_msg lum;
+
+ len = ustcomm_recv_unix_sock(sock, &lum, sizeof(lum));
+ switch (len) {
+ case 0: /* orderly shutdown */
+ DBG("%s ltt-sessiond has performed an orderly shutdown\n", sock_info->name);
+ goto end;
+ case sizeof(lum):
+ DBG("message received\n");
+ ret = handle_message(sock_info, sock, &lum);
+ if (ret < 0) {
+ ERR("Error handling message for %s socket", sock_info->name);
+ }
+ continue;
+ case -1:
+ if (errno == ECONNRESET) {
+ ERR("%s remote end closed connection\n", sock_info->name);
+ goto end;
+ }
+ goto end;
+ default:
+ ERR("incorrect message size (%s socket): %zd\n", sock_info->name, len);
+ continue;
+ }
+
+ }
+end:
+ goto restart; /* try to reconnect */
+quit:
+ return NULL;
+}
+
+/*
+ * Return values: -1: don't wait. 0: wait forever. 1: timeout wait.
+ */
+static
+int get_timeout(struct timespec *constructor_timeout)
+{
+ long constructor_delay_ms = LTTNG_UST_DEFAULT_CONSTRUCTOR_TIMEOUT_MS;
+ char *str_delay;
+ int ret;
+
+ str_delay = getenv("LTTNG_UST_REGISTER_TIMEOUT");
+ if (str_delay) {
+ constructor_delay_ms = strtol(str_delay, NULL, 10);
+ }
+
+ switch (constructor_delay_ms) {
+ case -1:/* fall-through */
+ case 0:
+ return constructor_delay_ms;
+ default:
+ break;
+ }
+
+ /*
+ * If we are unable to find the current time, don't wait.
+ */
+ ret = clock_gettime(CLOCK_REALTIME, constructor_timeout);
+ if (ret) {
+ return -1;
+ }
+ constructor_timeout->tv_sec += constructor_delay_ms / 1000UL;
+ constructor_timeout->tv_nsec +=
+ (constructor_delay_ms % 1000UL) * 1000000UL;
+ if (constructor_timeout->tv_nsec >= 1000000000UL) {
+ constructor_timeout->tv_sec++;
+ constructor_timeout->tv_nsec -= 1000000000UL;
+ }
+ return 1;
+}
+
+/*
+ * sessiond monitoring thread: monitor presence of global and per-user
+ * sessiond by polling the application common named pipe.
+ */
+/* TODO */
+
+void __attribute__((constructor)) lttng_ust_init(void)
+{
+ struct timespec constructor_timeout;
+ int timeout_mode;
+ int ret;
+
+ if (uatomic_xchg(&initialized, 1) == 1)
+ return;
+
+ /*
+ * We want precise control over the order in which we construct
+ * our sub-libraries vs starting to receive commands from
+ * sessiond (otherwise leading to errors when trying to create
+ * sessiond before the init functions are completed).
+ */
+ init_usterr();
+ init_tracepoint();
+ ltt_ring_buffer_metadata_client_init();
+ ltt_ring_buffer_client_overwrite_init();
+ ltt_ring_buffer_client_discard_init();
+
+ timeout_mode = get_timeout(&constructor_timeout);
+
+ ret = sem_init(&constructor_wait, 0, 0);
+ assert(!ret);
+
+ ret = setup_local_apps();
+ if (ret) {
+ ERR("Error setting up to local apps");
+ }
+ ret = pthread_create(&local_apps.ust_listener, NULL,
+ ust_listener_thread, &local_apps);
+
+ if (local_apps.allowed) {
+ ret = pthread_create(&global_apps.ust_listener, NULL,
+ ust_listener_thread, &global_apps);
+ } else {
+ handle_register_done(&local_apps);
+ }
+
+ switch (timeout_mode) {
+ case 1: /* timeout wait */
+ do {
+ ret = sem_timedwait(&constructor_wait,
+ &constructor_timeout);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 && errno == ETIMEDOUT) {
+ ERR("Timed out waiting for ltt-sessiond");
+ } else {
+ assert(!ret);
+ }
+ break;
+ case -1:/* wait forever */
+ do {
+ ret = sem_wait(&constructor_wait);
+ } while (ret < 0 && errno == EINTR);
+ assert(!ret);
+ break;
+ case 0: /* no timeout */
+ break;
+ }
+}
+
+static
+void lttng_ust_cleanup(int exiting)
+{
+ cleanup_sock_info(&global_apps);
+ if (local_apps.allowed) {
+ cleanup_sock_info(&local_apps);
+ }
+ lttng_ust_abi_exit();
+ lttng_ust_events_exit();
+ ltt_ring_buffer_client_discard_exit();
+ ltt_ring_buffer_client_overwrite_exit();
+ ltt_ring_buffer_metadata_client_exit();
+ exit_tracepoint();
+ if (!exiting) {
+ /* Reinitialize values for fork */
+ sem_count = 2;
+ lttng_ust_comm_should_quit = 0;
+ initialized = 0;
+ }
+}
+
+void __attribute__((destructor)) lttng_ust_exit(void)
+{
+ int ret;
+
+ /*
+ * Using pthread_cancel here because:
+ * A) we don't want to hang application teardown.
+ * B) the thread is not allocating any resource.
+ */
+
+ /*
+ * Require the communication thread to quit. Synchronize with
+ * mutexes to ensure it is not in a mutex critical section when
+ * pthread_cancel is later called.
+ */
+ ust_lock();
+ lttng_ust_comm_should_quit = 1;
+ ust_unlock();
+
+ ret = pthread_cancel(global_apps.ust_listener);
+ if (ret) {
+ ERR("Error cancelling global ust listener thread");
+ }
+ if (local_apps.allowed) {
+ ret = pthread_cancel(local_apps.ust_listener);
+ if (ret) {
+ ERR("Error cancelling local ust listener thread");
+ }
+ }
+ lttng_ust_cleanup(1);
+}
+
+/*
+ * We exclude the worker threads across fork and clone (except
+ * CLONE_VM), because these system calls only keep the forking thread
+ * running in the child. Therefore, we don't want to call fork or clone
+ * in the middle of an tracepoint or ust tracing state modification.
+ * Holding this mutex protects these structures across fork and clone.
+ */
+void ust_before_fork(ust_fork_info_t *fork_info)
+{
+ /*
+ * Disable signals. This is to avoid that the child intervenes
+ * before it is properly setup for tracing. It is safer to
+ * disable all signals, because then we know we are not breaking
+ * anything by restoring the original mask.
+ */
+ sigset_t all_sigs;
+ int ret;
+
+ /* Disable signals */
+ sigfillset(&all_sigs);
+ ret = sigprocmask(SIG_BLOCK, &all_sigs, &fork_info->orig_sigs);
+ if (ret == -1) {
+ PERROR("sigprocmask");
+ }
+ ust_lock();
+ rcu_bp_before_fork();
+}
+
+static void ust_after_fork_common(ust_fork_info_t *fork_info)
+{
+ int ret;
+
+ DBG("process %d", getpid());
+ ust_unlock();
+ /* Restore signals */
+ ret = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL);
+ if (ret == -1) {
+ PERROR("sigprocmask");
+ }
+}
+
+void ust_after_fork_parent(ust_fork_info_t *fork_info)
+{
+ DBG("process %d", getpid());
+ rcu_bp_after_fork_parent();
+ /* Release mutexes and reenable signals */
+ ust_after_fork_common(fork_info);
+}
+
+/*
+ * After fork, in the child, we need to cleanup all the leftover state,
+ * except the worker thread which already magically disappeared thanks
+ * to the weird Linux fork semantics. After tyding up, we call
+ * lttng_ust_init() again to start over as a new PID.
+ *
+ * This is meant for forks() that have tracing in the child between the
+ * fork and following exec call (if there is any).
+ */
+void ust_after_fork_child(ust_fork_info_t *fork_info)
+{
+ DBG("process %d", getpid());
+ /* Release urcu mutexes */
+ rcu_bp_after_fork_child();
+ lttng_ust_cleanup(0);
+ lttng_context_vtid_reset();
+ /* Release mutexes and reenable signals */
+ ust_after_fork_common(fork_info);
+ lttng_ust_init();
+}
--- /dev/null
+/*
+ * probes/lttng-probe-ust.c
+ *
+ * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng UST core probes.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
+ */
+
+/*
+ * Create LTTng tracepoint probes.
+ */
+#define TRACEPOINT_CREATE_PROBES
+
+#include "lttng-probe-ust.h"
--- /dev/null
+#undef TRACEPOINT_SYSTEM
+#define TRACEPOINT_SYSTEM lttng_ust
+
+#if !defined(_TRACEPOINT_LTTNG_UST_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _TRACEPOINT_LTTNG_UST_H
+
+/*
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; version 2.1 of
+ * the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <lttng/tracepoint.h>
+
+TRACEPOINT_EVENT(lttng_metadata,
+
+ TP_PROTO(const char *str),
+
+ TP_ARGS(str),
+
+ /*
+ * Not exactly a string: more a sequence of bytes (dynamic
+ * array) without the length. This is a dummy anyway: we only
+ * use this declaration to generate an event metadata entry.
+ */
+ TP_FIELDS(
+ ctf_string(str, str)
+ )
+)
+
+#undef TRACEPOINT_INCLUDE_PATH
+#define TRACEPOINT_INCLUDE_PATH ./probes
+#undef TRACEPOINT_INCLUDE_FILE
+#define TRACEPOINT_INCLUDE_FILE lttng-probe-ust
+
+#endif /* _TRACEPOINT_LTTNG_UST_H */
+
+/* This part must be outside protection */
+#include <lttng/tracepoint-event.h>
--- /dev/null
+/*
+ * Copyright (C) 2008-2011 Mathieu Desnoyers
+ * Copyright (C) 2009 Pierre-Marc Fournier
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Ported to userspace by Pierre-Marc Fournier.
+ */
+
+#define _LGPL_SOURCE
+#include <errno.h>
+#include <lttng/tracepoint.h>
+#include <lttng/tracepoint-internal.h>
+#include <lttng/core.h>
+#include <lttng/kcompat/kcompat.h>
+#include <urcu-bp.h>
+#include <urcu/hlist.h>
+#include <urcu/uatomic.h>
+
+#include <lttng/usterr-signal-safe.h>
+#include "ltt-tracer-core.h"
+
+/* Set to 1 to enable tracepoint debug output */
+static const int tracepoint_debug;
+static int initialized;
+static void (*new_tracepoint_cb)(struct tracepoint *);
+
+/* libraries that contain tracepoints (struct tracepoint_lib) */
+static CDS_LIST_HEAD(libs);
+
+/*
+ * The UST lock protects the library tracepoints, the hash table, and
+ * the library list.
+ * All calls to the tracepoint API must be protected by the UST lock,
+ * excepts calls to tracepoint_register_lib and
+ * tracepoint_unregister_lib, which take the UST lock themselves.
+ */
+
+/*
+ * Tracepoint hash table, containing the active tracepoints.
+ * Protected by tracepoints_mutex.
+ */
+#define TRACEPOINT_HASH_BITS 6
+#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
+static struct cds_hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
+
+static CDS_LIST_HEAD(old_probes);
+static int need_update;
+
+/*
+ * Note about RCU :
+ * It is used to to delay the free of multiple probes array until a quiescent
+ * state is reached.
+ * Tracepoint entries modifications are protected by the tracepoints_mutex.
+ */
+struct tracepoint_entry {
+ struct cds_hlist_node hlist;
+ struct tracepoint_probe *probes;
+ int refcount; /* Number of times armed. 0 if disarmed. */
+ char name[0];
+};
+
+struct tp_probes {
+ union {
+ struct cds_list_head list;
+ } u;
+ struct tracepoint_probe probes[0];
+};
+
+static inline void *allocate_probes(int count)
+{
+ struct tp_probes *p = zmalloc(count * sizeof(struct tracepoint_probe)
+ + sizeof(struct tp_probes));
+ return p == NULL ? NULL : p->probes;
+}
+
+static inline void release_probes(void *old)
+{
+ if (old) {
+ struct tp_probes *tp_probes = _ust_container_of(old,
+ struct tp_probes, probes[0]);
+ synchronize_rcu();
+ free(tp_probes);
+ }
+}
+
+static void debug_print_probes(struct tracepoint_entry *entry)
+{
+ int i;
+
+ if (!tracepoint_debug || !entry->probes)
+ return;
+
+ for (i = 0; entry->probes[i].func; i++)
+ DBG("Probe %d : %p", i, entry->probes[i].func);
+}
+
+static void *
+tracepoint_entry_add_probe(struct tracepoint_entry *entry,
+ void *probe, void *data)
+{
+ int nr_probes = 0;
+ struct tracepoint_probe *old, *new;
+
+ WARN_ON(!probe);
+
+ debug_print_probes(entry);
+ old = entry->probes;
+ if (old) {
+ /* (N -> N+1), (N != 0, 1) probes */
+ for (nr_probes = 0; old[nr_probes].func; nr_probes++)
+ if (old[nr_probes].func == probe &&
+ old[nr_probes].data == data)
+ return ERR_PTR(-EEXIST);
+ }
+ /* + 2 : one for new probe, one for NULL func */
+ new = allocate_probes(nr_probes + 2);
+ if (new == NULL)
+ return ERR_PTR(-ENOMEM);
+ if (old)
+ memcpy(new, old, nr_probes * sizeof(struct tracepoint_probe));
+ new[nr_probes].func = probe;
+ new[nr_probes].data = data;
+ new[nr_probes + 1].func = NULL;
+ entry->refcount = nr_probes + 1;
+ entry->probes = new;
+ debug_print_probes(entry);
+ return old;
+}
+
+static void *
+tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe,
+ void *data)
+{
+ int nr_probes = 0, nr_del = 0, i;
+ struct tracepoint_probe *old, *new;
+
+ old = entry->probes;
+
+ if (!old)
+ return ERR_PTR(-ENOENT);
+
+ debug_print_probes(entry);
+ /* (N -> M), (N > 1, M >= 0) probes */
+ for (nr_probes = 0; old[nr_probes].func; nr_probes++) {
+ if (!probe ||
+ (old[nr_probes].func == probe &&
+ old[nr_probes].data == data))
+ nr_del++;
+ }
+
+ if (nr_probes - nr_del == 0) {
+ /* N -> 0, (N > 1) */
+ entry->probes = NULL;
+ entry->refcount = 0;
+ debug_print_probes(entry);
+ return old;
+ } else {
+ int j = 0;
+ /* N -> M, (N > 1, M > 0) */
+ /* + 1 for NULL */
+ new = allocate_probes(nr_probes - nr_del + 1);
+ if (new == NULL)
+ return ERR_PTR(-ENOMEM);
+ for (i = 0; old[i].func; i++)
+ if (probe &&
+ (old[i].func != probe || old[i].data != data))
+ new[j++] = old[i];
+ new[nr_probes - nr_del].func = NULL;
+ entry->refcount = nr_probes - nr_del;
+ entry->probes = new;
+ }
+ debug_print_probes(entry);
+ return old;
+}
+
+/*
+ * Get tracepoint if the tracepoint is present in the tracepoint hash table.
+ * Must be called with tracepoints_mutex held.
+ * Returns NULL if not present.
+ */
+static struct tracepoint_entry *get_tracepoint(const char *name)
+{
+ struct cds_hlist_head *head;
+ struct cds_hlist_node *node;
+ struct tracepoint_entry *e;
+ u32 hash = jhash(name, strlen(name), 0);
+
+ head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
+ cds_hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp(name, e->name))
+ return e;
+ }
+ return NULL;
+}
+
+/*
+ * Add the tracepoint to the tracepoint hash table. Must be called with
+ * tracepoints_mutex held.
+ */
+static struct tracepoint_entry *add_tracepoint(const char *name)
+{
+ struct cds_hlist_head *head;
+ struct cds_hlist_node *node;
+ struct tracepoint_entry *e;
+ size_t name_len = strlen(name) + 1;
+ u32 hash = jhash(name, name_len-1, 0);
+
+ head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
+ cds_hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp(name, e->name)) {
+ DBG("tracepoint %s busy", name);
+ return ERR_PTR(-EEXIST); /* Already there */
+ }
+ }
+ /*
+ * Using zmalloc here to allocate a variable length element. Could
+ * cause some memory fragmentation if overused.
+ */
+ e = zmalloc(sizeof(struct tracepoint_entry) + name_len);
+ if (!e)
+ return ERR_PTR(-ENOMEM);
+ memcpy(&e->name[0], name, name_len);
+ e->probes = NULL;
+ e->refcount = 0;
+ cds_hlist_add_head(&e->hlist, head);
+ return e;
+}
+
+/*
+ * Remove the tracepoint from the tracepoint hash table. Must be called with
+ * ust_lock held.
+ */
+static inline void remove_tracepoint(struct tracepoint_entry *e)
+{
+ cds_hlist_del(&e->hlist);
+ free(e);
+}
+
+/*
+ * Sets the probe callback corresponding to one tracepoint.
+ */
+static void set_tracepoint(struct tracepoint_entry **entry,
+ struct tracepoint *elem, int active)
+{
+ WARN_ON(strcmp((*entry)->name, elem->name) != 0);
+
+ /*
+ * rcu_assign_pointer has a cmm_smp_wmb() which makes sure that the new
+ * probe callbacks array is consistent before setting a pointer to it.
+ * This array is referenced by __DO_TRACE from
+ * include/linux/tracepoints.h. A matching cmm_smp_read_barrier_depends()
+ * is used.
+ */
+ rcu_assign_pointer(elem->probes, (*entry)->probes);
+ elem->state = active;
+}
+
+/*
+ * Disable a tracepoint and its probe callback.
+ * Note: only waiting an RCU period after setting elem->call to the empty
+ * function insures that the original callback is not used anymore. This insured
+ * by preempt_disable around the call site.
+ */
+static void disable_tracepoint(struct tracepoint *elem)
+{
+ elem->state = 0;
+ rcu_assign_pointer(elem->probes, NULL);
+}
+
+/**
+ * tracepoint_update_probe_range - Update a probe range
+ * @begin: beginning of the range
+ * @end: end of the range
+ *
+ * Updates the probe callback corresponding to a range of tracepoints.
+ */
+static
+void tracepoint_update_probe_range(struct tracepoint * const *begin,
+ struct tracepoint * const *end)
+{
+ struct tracepoint * const *iter;
+ struct tracepoint_entry *mark_entry;
+
+ for (iter = begin; iter < end; iter++) {
+ if (!*iter)
+ continue; /* skip dummy */
+ if (!(*iter)->name) {
+ disable_tracepoint(*iter);
+ continue;
+ }
+ mark_entry = get_tracepoint((*iter)->name);
+ if (mark_entry) {
+ set_tracepoint(&mark_entry, *iter,
+ !!mark_entry->refcount);
+ } else {
+ disable_tracepoint(*iter);
+ }
+ }
+}
+
+static void lib_update_tracepoints(void)
+{
+ struct tracepoint_lib *lib;
+
+ cds_list_for_each_entry(lib, &libs, list) {
+ tracepoint_update_probe_range(lib->tracepoints_start,
+ lib->tracepoints_start + lib->tracepoints_count);
+ }
+}
+
+/*
+ * Update probes, removing the faulty probes.
+ */
+static void tracepoint_update_probes(void)
+{
+ /* tracepoints registered from libraries and executable. */
+ lib_update_tracepoints();
+}
+
+static struct tracepoint_probe *
+tracepoint_add_probe(const char *name, void *probe, void *data)
+{
+ struct tracepoint_entry *entry;
+ struct tracepoint_probe *old;
+
+ entry = get_tracepoint(name);
+ if (!entry) {
+ entry = add_tracepoint(name);
+ if (IS_ERR(entry))
+ return (struct tracepoint_probe *)entry;
+ }
+ old = tracepoint_entry_add_probe(entry, probe, data);
+ if (IS_ERR(old) && !entry->refcount)
+ remove_tracepoint(entry);
+ return old;
+}
+
+/**
+ * __tracepoint_probe_register - Connect a probe to a tracepoint
+ * @name: tracepoint name
+ * @probe: probe handler
+ *
+ * Returns 0 if ok, error value on error.
+ * The probe address must at least be aligned on the architecture pointer size.
+ * Called with the UST lock held.
+ */
+int __tracepoint_probe_register(const char *name, void *probe, void *data)
+{
+ void *old;
+
+ old = tracepoint_add_probe(name, probe, data);
+ if (IS_ERR(old))
+ return PTR_ERR(old);
+
+ tracepoint_update_probes(); /* may update entry */
+ release_probes(old);
+ return 0;
+}
+
+static void *tracepoint_remove_probe(const char *name, void *probe, void *data)
+{
+ struct tracepoint_entry *entry;
+ void *old;
+
+ entry = get_tracepoint(name);
+ if (!entry)
+ return ERR_PTR(-ENOENT);
+ old = tracepoint_entry_remove_probe(entry, probe, data);
+ if (IS_ERR(old))
+ return old;
+ if (!entry->refcount)
+ remove_tracepoint(entry);
+ return old;
+}
+
+/**
+ * tracepoint_probe_unregister - Disconnect a probe from a tracepoint
+ * @name: tracepoint name
+ * @probe: probe function pointer
+ * @probe: probe data pointer
+ *
+ * Called with the UST lock held.
+ */
+int __tracepoint_probe_unregister(const char *name, void *probe, void *data)
+{
+ void *old;
+
+ old = tracepoint_remove_probe(name, probe, data);
+ if (IS_ERR(old))
+ return PTR_ERR(old);
+
+ tracepoint_update_probes(); /* may update entry */
+ release_probes(old);
+ return 0;
+}
+
+static void tracepoint_add_old_probes(void *old)
+{
+ need_update = 1;
+ if (old) {
+ struct tp_probes *tp_probes = _ust_container_of(old,
+ struct tp_probes, probes[0]);
+ cds_list_add(&tp_probes->u.list, &old_probes);
+ }
+}
+
+/**
+ * tracepoint_probe_register_noupdate - register a probe but not connect
+ * @name: tracepoint name
+ * @probe: probe handler
+ *
+ * caller must call tracepoint_probe_update_all()
+ * Called with the UST lock held.
+ */
+int tracepoint_probe_register_noupdate(const char *name, void *probe,
+ void *data)
+{
+ void *old;
+
+ old = tracepoint_add_probe(name, probe, data);
+ if (IS_ERR(old)) {
+ return PTR_ERR(old);
+ }
+ tracepoint_add_old_probes(old);
+ return 0;
+}
+
+/**
+ * tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
+ * @name: tracepoint name
+ * @probe: probe function pointer
+ *
+ * caller must call tracepoint_probe_update_all()
+ * Called with the UST lock held.
+ */
+int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
+ void *data)
+{
+ void *old;
+
+ old = tracepoint_remove_probe(name, probe, data);
+ if (IS_ERR(old)) {
+ return PTR_ERR(old);
+ }
+ tracepoint_add_old_probes(old);
+ return 0;
+}
+
+/**
+ * tracepoint_probe_update_all - update tracepoints
+ * Called with the UST lock held.
+ */
+void tracepoint_probe_update_all(void)
+{
+ CDS_LIST_HEAD(release_probes);
+ struct tp_probes *pos, *next;
+
+ if (!need_update) {
+ return;
+ }
+ if (!cds_list_empty(&old_probes))
+ cds_list_replace_init(&old_probes, &release_probes);
+ need_update = 0;
+
+ tracepoint_update_probes();
+ cds_list_for_each_entry_safe(pos, next, &release_probes, u.list) {
+ cds_list_del(&pos->u.list);
+ synchronize_rcu();
+ free(pos);
+ }
+}
+
+/*
+ * Returns 0 if current not found.
+ * Returns 1 if current found.
+ *
+ * Called with tracepoint mutex held
+ */
+int lib_get_iter_tracepoints(struct tracepoint_iter *iter)
+{
+ struct tracepoint_lib *iter_lib;
+ int found = 0;
+
+ cds_list_for_each_entry(iter_lib, &libs, list) {
+ if (iter_lib < iter->lib)
+ continue;
+ else if (iter_lib > iter->lib)
+ iter->tracepoint = NULL;
+ found = tracepoint_get_iter_range(&iter->tracepoint,
+ iter_lib->tracepoints_start,
+ iter_lib->tracepoints_start + iter_lib->tracepoints_count);
+ if (found) {
+ iter->lib = iter_lib;
+ break;
+ }
+ }
+ return found;
+}
+
+/**
+ * tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
+ * @tracepoint: current tracepoints (in), next tracepoint (out)
+ * @begin: beginning of the range
+ * @end: end of the range
+ *
+ * Returns whether a next tracepoint has been found (1) or not (0).
+ * Will return the first tracepoint in the range if the input tracepoint is
+ * NULL.
+ * Called with tracepoint mutex held.
+ */
+int tracepoint_get_iter_range(struct tracepoint * const **tracepoint,
+ struct tracepoint * const *begin, struct tracepoint * const *end)
+{
+ if (!*tracepoint && begin != end)
+ *tracepoint = begin;
+ while (*tracepoint >= begin && *tracepoint < end) {
+ if (!**tracepoint)
+ (*tracepoint)++; /* skip dummy */
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Called with tracepoint mutex held.
+ */
+static void tracepoint_get_iter(struct tracepoint_iter *iter)
+{
+ int found = 0;
+
+ /* tracepoints in libs. */
+ found = lib_get_iter_tracepoints(iter);
+ if (!found)
+ tracepoint_iter_reset(iter);
+}
+
+/*
+ * Called with UST lock held.
+ */
+void tracepoint_iter_start(struct tracepoint_iter *iter)
+{
+ tracepoint_get_iter(iter);
+}
+
+/*
+ * Called with UST lock held.
+ */
+void tracepoint_iter_next(struct tracepoint_iter *iter)
+{
+ iter->tracepoint++;
+ /*
+ * iter->tracepoint may be invalid because we blindly incremented it.
+ * Make sure it is valid by marshalling on the tracepoints, getting the
+ * tracepoints from following modules if necessary.
+ */
+ tracepoint_get_iter(iter);
+}
+
+/*
+ * Called with UST lock held.
+ */
+void tracepoint_iter_stop(struct tracepoint_iter *iter)
+{
+}
+
+void tracepoint_iter_reset(struct tracepoint_iter *iter)
+{
+ iter->tracepoint = NULL;
+}
+
+void tracepoint_set_new_tracepoint_cb(void (*cb)(struct tracepoint *))
+{
+ new_tracepoint_cb = cb;
+}
+
+static void new_tracepoints(struct tracepoint * const *start, struct tracepoint * const *end)
+{
+ if (new_tracepoint_cb) {
+ struct tracepoint * const *t;
+
+ for (t = start; t < end; t++) {
+ if (*t)
+ new_tracepoint_cb(*t);
+ }
+ }
+}
+
+int tracepoint_register_lib(struct tracepoint * const *tracepoints_start,
+ int tracepoints_count)
+{
+ struct tracepoint_lib *pl, *iter;
+
+ init_tracepoint();
+
+ pl = (struct tracepoint_lib *) zmalloc(sizeof(struct tracepoint_lib));
+
+ pl->tracepoints_start = tracepoints_start;
+ pl->tracepoints_count = tracepoints_count;
+
+ ust_lock();
+ /*
+ * We sort the libs by struct lib pointer address.
+ */
+ cds_list_for_each_entry_reverse(iter, &libs, list) {
+ BUG_ON(iter == pl); /* Should never be in the list twice */
+ if (iter < pl) {
+ /* We belong to the location right after iter. */
+ cds_list_add(&pl->list, &iter->list);
+ goto lib_added;
+ }
+ }
+ /* We should be added at the head of the list */
+ cds_list_add(&pl->list, &libs);
+lib_added:
+ new_tracepoints(tracepoints_start, tracepoints_start + tracepoints_count);
+
+ /* TODO: update just the loaded lib */
+ lib_update_tracepoints();
+ ust_unlock();
+
+ DBG("just registered a tracepoints section from %p and having %d tracepoints",
+ tracepoints_start, tracepoints_count);
+
+ return 0;
+}
+
+int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start)
+{
+ struct tracepoint_lib *lib;
+
+ ust_lock();
+ cds_list_for_each_entry(lib, &libs, list) {
+ if (lib->tracepoints_start == tracepoints_start) {
+ struct tracepoint_lib *lib2free = lib;
+ cds_list_del(&lib->list);
+ free(lib2free);
+ break;
+ }
+ }
+ ust_unlock();
+
+ return 0;
+}
+
+void init_tracepoint(void)
+{
+ if (uatomic_xchg(&initialized, 1) == 1)
+ return;
+ init_usterr();
+}
+
+void exit_tracepoint(void)
+{
+ initialized = 0;
+}
--- /dev/null
+/*
+ * ust-core.c
+ *
+ * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; 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
+ */
+
+#include <lttng/usterr-signal-safe.h>
+#include <stdlib.h>
+
+volatile enum ust_loglevel ust_loglevel;
+
+void init_usterr(void)
+{
+ char *ust_debug;
+
+ if (ust_loglevel == UST_LOGLEVEL_UNKNOWN) {
+ ust_debug = getenv("LTTNG_UST_DEBUG");
+ if (ust_debug)
+ ust_loglevel = UST_LOGLEVEL_DEBUG;
+ else
+ ust_loglevel = UST_LOGLEVEL_NORMAL;
+ }
+}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libust.la
-
-libust_la_SOURCES = \
- tracepoint.c \
- ltt-tracer.h \
- ltt-tracer-core.h \
- ltt-ring-buffer-client.h \
- ltt-ring-buffer-client-discard.c \
- ltt-ring-buffer-client-overwrite.c \
- ltt-ring-buffer-metadata-client.h \
- ltt-ring-buffer-metadata-client.c \
- ltt-events.c \
- ltt-probes.c \
- lttng-ust-abi.c \
- lttng-ust-comm.c \
- ust-core.c \
- probes/lttng-probe-ust.c \
- probes/lttng-probe-ust.h \
- lttng-context-vtid.c \
- lttng-context-vpid.c \
- lttng-context-pthread-id.c \
- lttng-context-procname.c \
- ltt-context.c
-
-libust_la_LDFLAGS = -no-undefined -version-info 0:0:0
-
-libust_la_LIBADD = \
- -lpthread \
- -lrt \
- -luuid \
- $(top_builddir)/snprintf/libustsnprintf.la \
- $(top_builddir)/libringbuffer/libringbuffer.la \
- $(top_builddir)/liblttng-ust-comm/liblttng-ust-comm.la
-
-libust_la_CFLAGS = -DUST_COMPONENT="libust" -fno-strict-aliasing
+++ /dev/null
-/*
- * ltt-context.c
- *
- * Copyright 2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST trace/channel/event context management.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <lttng/ust-events.h>
-#include <lttng/ust-tracer.h>
-#include <lttng/core.h>
-#include <string.h>
-#include <assert.h>
-
-int lttng_find_context(struct lttng_ctx *ctx, const char *name)
-{
- unsigned int i;
-
- for (i = 0; i < ctx->nr_fields; i++) {
- /* Skip allocated (but non-initialized) contexts */
- if (!ctx->fields[i].event_field.name)
- continue;
- if (!strcmp(ctx->fields[i].event_field.name, name))
- return 1;
- }
- return 0;
-}
-
-/*
- * Note: as we append context information, the pointer location may change.
- */
-struct lttng_ctx_field *lttng_append_context(struct lttng_ctx **ctx_p)
-{
- struct lttng_ctx_field *field;
- struct lttng_ctx *ctx;
-
- if (!*ctx_p) {
- *ctx_p = zmalloc(sizeof(struct lttng_ctx));
- if (!*ctx_p)
- return NULL;
- }
- ctx = *ctx_p;
- if (ctx->nr_fields + 1 > ctx->allocated_fields) {
- struct lttng_ctx_field *new_fields;
-
- ctx->allocated_fields = max_t(size_t, 1, 2 * ctx->allocated_fields);
- new_fields = zmalloc(ctx->allocated_fields * sizeof(struct lttng_ctx_field));
- if (!new_fields)
- return NULL;
- if (ctx->fields)
- memcpy(new_fields, ctx->fields, sizeof(*ctx->fields) * ctx->nr_fields);
- free(ctx->fields);
- ctx->fields = new_fields;
- }
- field = &ctx->fields[ctx->nr_fields];
- ctx->nr_fields++;
- return field;
-}
-
-/*
- * Remove last context field.
- */
-void lttng_remove_context_field(struct lttng_ctx **ctx_p,
- struct lttng_ctx_field *field)
-{
- struct lttng_ctx *ctx;
-
- ctx = *ctx_p;
- ctx->nr_fields--;
- assert(&ctx->fields[ctx->nr_fields] == field);
- memset(&ctx->fields[ctx->nr_fields], 0, sizeof(struct lttng_ctx_field));
-}
-
-void lttng_destroy_context(struct lttng_ctx *ctx)
-{
- int i;
-
- if (!ctx)
- return;
- for (i = 0; i < ctx->nr_fields; i++) {
- if (ctx->fields[i].destroy)
- ctx->fields[i].destroy(&ctx->fields[i]);
- }
- free(ctx->fields);
- free(ctx);
-}
+++ /dev/null
-/*
- * ltt-events.c
- *
- * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Holds LTTng per-session event registry.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <endian.h>
-#include <urcu/list.h>
-#include <urcu/hlist.h>
-#include <pthread.h>
-#include <urcu-bp.h>
-#include <urcu/compiler.h>
-#include <urcu/uatomic.h>
-#include <uuid/uuid.h>
-#include <lttng/tracepoint.h>
-#include <errno.h>
-#include <sys/shm.h>
-#include <sys/ipc.h>
-#include <lttng/ust-events.h>
-#include <lttng/usterr-signal-safe.h>
-#include "lttng/core.h"
-#include "ltt-tracer.h"
-#include "ltt-tracer-core.h"
-#include "lttng/wait.h"
-#include "../libringbuffer/shm.h"
-
-typedef u32 uint32_t;
-#include <lttng/kcompat/jhash.h>
-
-/*
- * The sessions mutex is the centralized mutex across UST tracing
- * control and probe registration. All operations within this file are
- * called by the communication thread, under ust_lock protection.
- */
-static DEFINE_MUTEX(sessions_mutex);
-
-void ust_lock(void)
-{
- pthread_mutex_lock(&sessions_mutex);
-}
-
-void ust_unlock(void)
-{
- pthread_mutex_unlock(&sessions_mutex);
-}
-
-static CDS_LIST_HEAD(sessions);
-static CDS_LIST_HEAD(ltt_transport_list);
-
-/*
- * Pending probes hash table, containing the registered ltt events for
- * which tracepoint probes are still missing. Protected by the sessions
- * mutex.
- */
-#define PENDING_PROBE_HASH_BITS 6
-#define PENDING_PROBE_HASH_SIZE (1 << PENDING_PROBE_HASH_BITS)
-static struct cds_hlist_head pending_probe_table[PENDING_PROBE_HASH_SIZE];
-
-struct ust_pending_probe {
- struct ltt_event *event;
- struct cds_hlist_node node;
- char name[];
-};
-
-static void _ltt_event_destroy(struct ltt_event *event);
-static void _ltt_channel_destroy(struct ltt_channel *chan);
-static int _ltt_event_unregister(struct ltt_event *event);
-static
-int _ltt_event_metadata_statedump(struct ltt_session *session,
- struct ltt_channel *chan,
- struct ltt_event *event);
-static
-int _ltt_session_metadata_statedump(struct ltt_session *session);
-
-/*
- * called at event creation if probe is missing.
- * called with session mutex held.
- */
-static
-int add_pending_probe(struct ltt_event *event, const char *name)
-{
- struct cds_hlist_head *head;
- struct ust_pending_probe *e;
- size_t name_len = strlen(name) + 1;
- u32 hash = jhash(name, name_len - 1, 0);
-
- head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
- e = zmalloc(sizeof(struct ust_pending_probe) + name_len);
- if (!e)
- return -ENOMEM;
- memcpy(&e->name[0], name, name_len);
- cds_hlist_add_head(&e->node, head);
- e->event = event;
- event->pending_probe = e;
- return 0;
-}
-
-/*
- * remove a pending probe. called when at event teardown and when an
- * event is fixed (probe is loaded).
- * called with session mutex held.
- */
-static
-void remove_pending_probe(struct ust_pending_probe *e)
-{
- if (!e)
- return;
- cds_hlist_del(&e->node);
- free(e);
-}
-
-/*
- * Called at library load: connect the probe on the events pending on
- * probe load.
- * called with session mutex held.
- */
-int pending_probe_fix_events(const struct lttng_event_desc *desc)
-{
- struct cds_hlist_head *head;
- struct cds_hlist_node *node, *p;
- struct ust_pending_probe *e;
- const char *name = desc->name;
- size_t name_len = strlen(name) + 1;
- u32 hash = jhash(name, name_len - 1, 0);
- int ret = 0;
-
- head = &pending_probe_table[hash & (PENDING_PROBE_HASH_SIZE - 1)];
- cds_hlist_for_each_entry_safe(e, node, p, head, node) {
- struct ltt_event *event;
- struct ltt_channel *chan;
-
- if (strcmp(name, e->name))
- continue;
- event = e->event;
- chan = event->chan;
- assert(!event->desc);
- event->desc = desc;
- event->pending_probe = NULL;
- remove_pending_probe(e);
- ret |= __tracepoint_probe_register(name,
- event->desc->probe_callback,
- event);
- if (ret)
- continue;
- event->id = chan->free_event_id++;
- ret |= _ltt_event_metadata_statedump(chan->session, chan,
- event);
- }
- return ret;
-}
-
-void synchronize_trace(void)
-{
- synchronize_rcu();
-}
-
-struct ltt_session *ltt_session_create(void)
-{
- struct ltt_session *session;
-
- session = zmalloc(sizeof(struct ltt_session));
- if (!session)
- return NULL;
- CDS_INIT_LIST_HEAD(&session->chan);
- CDS_INIT_LIST_HEAD(&session->events);
- uuid_generate(session->uuid);
- cds_list_add(&session->list, &sessions);
- return session;
-}
-
-void ltt_session_destroy(struct ltt_session *session)
-{
- struct ltt_channel *chan, *tmpchan;
- struct ltt_event *event, *tmpevent;
- int ret;
-
- CMM_ACCESS_ONCE(session->active) = 0;
- cds_list_for_each_entry(event, &session->events, list) {
- ret = _ltt_event_unregister(event);
- WARN_ON(ret);
- }
- synchronize_trace(); /* Wait for in-flight events to complete */
- cds_list_for_each_entry_safe(event, tmpevent, &session->events, list)
- _ltt_event_destroy(event);
- cds_list_for_each_entry_safe(chan, tmpchan, &session->chan, list)
- _ltt_channel_destroy(chan);
- cds_list_del(&session->list);
- free(session);
-}
-
-int ltt_session_enable(struct ltt_session *session)
-{
- int ret = 0;
- struct ltt_channel *chan;
-
- if (session->active) {
- ret = -EBUSY;
- goto end;
- }
-
- /*
- * Snapshot the number of events per channel to know the type of header
- * we need to use.
- */
- cds_list_for_each_entry(chan, &session->chan, list) {
- if (chan->header_type)
- continue; /* don't change it if session stop/restart */
- if (chan->free_event_id < 31)
- chan->header_type = 1; /* compact */
- else
- chan->header_type = 2; /* large */
- }
-
- CMM_ACCESS_ONCE(session->active) = 1;
- CMM_ACCESS_ONCE(session->been_active) = 1;
- ret = _ltt_session_metadata_statedump(session);
- if (ret)
- CMM_ACCESS_ONCE(session->active) = 0;
-end:
- return ret;
-}
-
-int ltt_session_disable(struct ltt_session *session)
-{
- int ret = 0;
-
- if (!session->active) {
- ret = -EBUSY;
- goto end;
- }
- CMM_ACCESS_ONCE(session->active) = 0;
-end:
- return ret;
-}
-
-int ltt_channel_enable(struct ltt_channel *channel)
-{
- int old;
-
- if (channel == channel->session->metadata)
- return -EPERM;
- old = uatomic_xchg(&channel->enabled, 1);
- if (old)
- return -EEXIST;
- return 0;
-}
-
-int ltt_channel_disable(struct ltt_channel *channel)
-{
- int old;
-
- if (channel == channel->session->metadata)
- return -EPERM;
- old = uatomic_xchg(&channel->enabled, 0);
- if (!old)
- return -EEXIST;
- return 0;
-}
-
-int ltt_event_enable(struct ltt_event *event)
-{
- int old;
-
- if (event->chan == event->chan->session->metadata)
- return -EPERM;
- old = uatomic_xchg(&event->enabled, 1);
- if (old)
- return -EEXIST;
- return 0;
-}
-
-int ltt_event_disable(struct ltt_event *event)
-{
- int old;
-
- if (event->chan == event->chan->session->metadata)
- return -EPERM;
- old = uatomic_xchg(&event->enabled, 0);
- if (!old)
- return -EEXIST;
- return 0;
-}
-
-static struct ltt_transport *ltt_transport_find(const char *name)
-{
- struct ltt_transport *transport;
-
- cds_list_for_each_entry(transport, <t_transport_list, node) {
- if (!strcmp(transport->name, name))
- return transport;
- }
- return NULL;
-}
-
-struct ltt_channel *ltt_channel_create(struct ltt_session *session,
- const char *transport_name,
- void *buf_addr,
- size_t subbuf_size, size_t num_subbuf,
- unsigned int switch_timer_interval,
- unsigned int read_timer_interval,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size)
-{
- struct ltt_channel *chan;
- struct ltt_transport *transport;
-
- if (session->been_active)
- goto active; /* Refuse to add channel to active session */
- transport = ltt_transport_find(transport_name);
- if (!transport) {
- DBG("LTTng transport %s not found\n",
- transport_name);
- goto notransport;
- }
- chan = zmalloc(sizeof(struct ltt_channel));
- if (!chan)
- goto nomem;
- chan->session = session;
- chan->id = session->free_chan_id++;
- /*
- * Note: the channel creation op already writes into the packet
- * headers. Therefore the "chan" information used as input
- * should be already accessible.
- */
- transport->ops.channel_create("[lttng]", chan, buf_addr,
- subbuf_size, num_subbuf, switch_timer_interval,
- read_timer_interval, shm_fd, wait_fd,
- memory_map_size);
- if (!chan->chan)
- goto create_error;
- chan->enabled = 1;
- chan->ops = &transport->ops;
- cds_list_add(&chan->list, &session->chan);
- return chan;
-
-create_error:
- free(chan);
-nomem:
-notransport:
-active:
- return NULL;
-}
-
-/*
- * Only used internally at session destruction.
- */
-static
-void _ltt_channel_destroy(struct ltt_channel *chan)
-{
- chan->ops->channel_destroy(chan);
- cds_list_del(&chan->list);
- lttng_destroy_context(chan->ctx);
- free(chan);
-}
-
-/*
- * Supports event creation while tracing session is active.
- */
-struct ltt_event *ltt_event_create(struct ltt_channel *chan,
- struct lttng_ust_event *event_param,
- void *filter)
-{
- struct ltt_event *event;
- int ret;
-
- if (chan->used_event_id == -1UL)
- goto full;
- /*
- * This is O(n^2) (for each event, the loop is called at event
- * creation). Might require a hash if we have lots of events.
- */
- cds_list_for_each_entry(event, &chan->session->events, list)
- if (event->desc && !strcmp(event->desc->name, event_param->name))
- goto exist;
- event = zmalloc(sizeof(struct ltt_event));
- if (!event)
- goto cache_error;
- event->chan = chan;
- event->filter = filter;
- /*
- * used_event_id counts the maximum number of event IDs that can
- * register if all probes register.
- */
- chan->used_event_id++;
- event->enabled = 1;
- event->instrumentation = event_param->instrumentation;
- /* Populate ltt_event structure before tracepoint registration. */
- cmm_smp_wmb();
- switch (event_param->instrumentation) {
- case LTTNG_UST_TRACEPOINT:
- event->desc = ltt_event_get(event_param->name);
- if (event->desc) {
- ret = __tracepoint_probe_register(event_param->name,
- event->desc->probe_callback,
- event);
- if (ret)
- goto register_error;
- event->id = chan->free_event_id++;
- } else {
- /*
- * If the probe is not present, event->desc stays NULL,
- * waiting for the probe to register, and the event->id
- * stays unallocated.
- */
- ret = add_pending_probe(event, event_param->name);
- if (ret)
- goto add_pending_error;
- }
- break;
- default:
- WARN_ON_ONCE(1);
- }
- if (event->desc) {
- ret = _ltt_event_metadata_statedump(chan->session, chan, event);
- if (ret)
- goto statedump_error;
- }
- cds_list_add(&event->list, &chan->session->events);
- return event;
-
-statedump_error:
- if (event->desc) {
- WARN_ON_ONCE(__tracepoint_probe_unregister(event_param->name,
- event->desc->probe_callback,
- event));
- ltt_event_put(event->desc);
- }
-add_pending_error:
-register_error:
- free(event);
-cache_error:
-exist:
-full:
- return NULL;
-}
-
-/*
- * Only used internally at session destruction.
- */
-int _ltt_event_unregister(struct ltt_event *event)
-{
- int ret = -EINVAL;
-
- switch (event->instrumentation) {
- case LTTNG_UST_TRACEPOINT:
- if (event->desc) {
- ret = __tracepoint_probe_unregister(event->desc->name,
- event->desc->probe_callback,
- event);
- if (ret)
- return ret;
- } else {
- remove_pending_probe(event->pending_probe);
- ret = 0;
- }
- break;
- default:
- WARN_ON_ONCE(1);
- }
- return ret;
-}
-
-/*
- * Only used internally at session destruction.
- */
-static
-void _ltt_event_destroy(struct ltt_event *event)
-{
- switch (event->instrumentation) {
- case LTTNG_UST_TRACEPOINT:
- if (event->desc) {
- ltt_event_put(event->desc);
- }
- break;
- default:
- WARN_ON_ONCE(1);
- }
- cds_list_del(&event->list);
- lttng_destroy_context(event->ctx);
- free(event);
-}
-
-/*
- * We have exclusive access to our metadata buffer (protected by the
- * ust_lock), so we can do racy operations such as looking for
- * remaining space left in packet and write, since mutual exclusion
- * protects us from concurrent writes.
- */
-int lttng_metadata_printf(struct ltt_session *session,
- const char *fmt, ...)
-{
- struct lttng_ust_lib_ring_buffer_ctx ctx;
- struct ltt_channel *chan = session->metadata;
- char *str = NULL;
- int ret = 0, waitret;
- size_t len, reserve_len, pos;
- va_list ap;
-
- WARN_ON_ONCE(!CMM_ACCESS_ONCE(session->active));
-
- va_start(ap, fmt);
- ret = vasprintf(&str, fmt, ap);
- va_end(ap);
- if (ret < 0)
- return -ENOMEM;
-
- len = strlen(str);
- pos = 0;
-
- for (pos = 0; pos < len; pos += reserve_len) {
- reserve_len = min_t(size_t,
- chan->ops->packet_avail_size(chan->chan, chan->handle),
- len - pos);
- lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len,
- sizeof(char), -1, chan->handle);
- /*
- * We don't care about metadata buffer's records lost
- * count, because we always retry here. Report error if
- * we need to bail out after timeout or being
- * interrupted.
- */
- waitret = wait_cond_interruptible_timeout(
- ({
- ret = chan->ops->event_reserve(&ctx, 0);
- ret != -ENOBUFS || !ret;
- }),
- LTTNG_METADATA_TIMEOUT_MSEC);
- if (waitret == -ETIMEDOUT || waitret == -EINTR || ret) {
- DBG("LTTng: Failure to write metadata to buffers (%s)\n",
- waitret == -EINTR ? "interrupted" :
- (ret == -ENOBUFS ? "timeout" : "I/O error"));
- if (waitret == -EINTR)
- ret = waitret;
- goto end;
- }
- chan->ops->event_write(&ctx, &str[pos], reserve_len);
- chan->ops->event_commit(&ctx);
- }
-end:
- free(str);
- return ret;
-}
-
-static
-int _ltt_field_statedump(struct ltt_session *session,
- const struct lttng_event_field *field)
-{
- int ret = 0;
-
- switch (field->type.atype) {
- case atype_integer:
- ret = lttng_metadata_printf(session,
- " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s;\n",
- field->type.u.basic.integer.size,
- field->type.u.basic.integer.alignment,
- field->type.u.basic.integer.signedness,
- (field->type.u.basic.integer.encoding == lttng_encode_none)
- ? "none"
- : (field->type.u.basic.integer.encoding == lttng_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- field->type.u.basic.integer.base,
-#if (BYTE_ORDER == BIG_ENDIAN)
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
-#else
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
-#endif
- field->name);
- break;
- case atype_float:
- ret = lttng_metadata_printf(session,
- " floating_point { exp_dig = %u; mant_dig = %u; align = %u;%s } _%s;\n",
- field->type.u.basic._float.exp_dig,
- field->type.u.basic._float.mant_dig,
- field->type.u.basic._float.alignment,
-#if (BYTE_ORDER == BIG_ENDIAN)
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
-#else
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
-#endif
- field->name);
- break;
- case atype_enum:
- ret = lttng_metadata_printf(session,
- " %s %s;\n",
- field->type.u.basic.enumeration.name,
- field->name);
- break;
- case atype_array:
- {
- const struct lttng_basic_type *elem_type;
-
- elem_type = &field->type.u.array.elem_type;
- ret = lttng_metadata_printf(session,
- " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n",
- elem_type->u.basic.integer.size,
- elem_type->u.basic.integer.alignment,
- elem_type->u.basic.integer.signedness,
- (elem_type->u.basic.integer.encoding == lttng_encode_none)
- ? "none"
- : (elem_type->u.basic.integer.encoding == lttng_encode_UTF8)
- ? "UTF8"
- : "ASCII",
- elem_type->u.basic.integer.base,
-#if (BYTE_ORDER == BIG_ENDIAN)
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
-#else
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
-#endif
- field->name, field->type.u.array.length);
- break;
- }
- case atype_sequence:
- {
- const struct lttng_basic_type *elem_type;
- const struct lttng_basic_type *length_type;
-
- elem_type = &field->type.u.sequence.elem_type;
- length_type = &field->type.u.sequence.length_type;
- ret = lttng_metadata_printf(session,
- " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } __%s_length;\n",
- length_type->u.basic.integer.size,
- (unsigned int) length_type->u.basic.integer.alignment,
- length_type->u.basic.integer.signedness,
- (length_type->u.basic.integer.encoding == lttng_encode_none)
- ? "none"
- : ((length_type->u.basic.integer.encoding == lttng_encode_UTF8)
- ? "UTF8"
- : "ASCII"),
- length_type->u.basic.integer.base,
-#if (BYTE_ORDER == BIG_ENDIAN)
- length_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
-#else
- length_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
-#endif
- field->name);
- if (ret)
- return ret;
-
- ret = lttng_metadata_printf(session,
- " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[ __%s_length ];\n",
- elem_type->u.basic.integer.size,
- (unsigned int) elem_type->u.basic.integer.alignment,
- elem_type->u.basic.integer.signedness,
- (elem_type->u.basic.integer.encoding == lttng_encode_none)
- ? "none"
- : ((elem_type->u.basic.integer.encoding == lttng_encode_UTF8)
- ? "UTF8"
- : "ASCII"),
- elem_type->u.basic.integer.base,
-#if (BYTE_ORDER == BIG_ENDIAN)
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
-#else
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
-#endif
- field->name,
- field->name);
- break;
- }
-
- case atype_string:
- /* Default encoding is UTF8 */
- ret = lttng_metadata_printf(session,
- " string%s _%s;\n",
- field->type.u.basic.string.encoding == lttng_encode_ASCII ?
- " { encoding = ASCII; }" : "",
- field->name);
- break;
- default:
- WARN_ON_ONCE(1);
- return -EINVAL;
- }
- return ret;
-}
-
-static
-int _ltt_context_metadata_statedump(struct ltt_session *session,
- struct lttng_ctx *ctx)
-{
- int ret = 0;
- int i;
-
- if (!ctx)
- return 0;
- for (i = 0; i < ctx->nr_fields; i++) {
- const struct lttng_ctx_field *field = &ctx->fields[i];
-
- ret = _ltt_field_statedump(session, &field->event_field);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static
-int _ltt_fields_metadata_statedump(struct ltt_session *session,
- struct ltt_event *event)
-{
- const struct lttng_event_desc *desc = event->desc;
- int ret = 0;
- int i;
-
- for (i = 0; i < desc->nr_fields; i++) {
- const struct lttng_event_field *field = &desc->fields[i];
-
- ret = _ltt_field_statedump(session, field);
- if (ret)
- return ret;
- }
- return ret;
-}
-
-static
-int _ltt_event_metadata_statedump(struct ltt_session *session,
- struct ltt_channel *chan,
- struct ltt_event *event)
-{
- int ret = 0;
-
- if (event->metadata_dumped || !CMM_ACCESS_ONCE(session->active))
- return 0;
- if (chan == session->metadata)
- return 0;
- /*
- * Don't print events for which probe load is pending.
- */
- if (!event->desc)
- return 0;
-
- ret = lttng_metadata_printf(session,
- "event {\n"
- " name = %s;\n"
- " id = %u;\n"
- " stream_id = %u;\n",
- event->desc->name,
- event->id,
- event->chan->id);
- if (ret)
- goto end;
-
- if (event->ctx) {
- ret = lttng_metadata_printf(session,
- " context := struct {\n");
- if (ret)
- goto end;
- }
- ret = _ltt_context_metadata_statedump(session, event->ctx);
- if (ret)
- goto end;
- if (event->ctx) {
- ret = lttng_metadata_printf(session,
- " };\n");
- if (ret)
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- " fields := struct {\n"
- );
- if (ret)
- goto end;
-
- ret = _ltt_fields_metadata_statedump(session, event);
- if (ret)
- goto end;
-
- /*
- * LTTng space reservation can only reserve multiples of the
- * byte size.
- */
- ret = lttng_metadata_printf(session,
- " };\n"
- "};\n\n");
- if (ret)
- goto end;
-
- event->metadata_dumped = 1;
-end:
- return ret;
-
-}
-
-static
-int _ltt_channel_metadata_statedump(struct ltt_session *session,
- struct ltt_channel *chan)
-{
- int ret = 0;
-
- if (chan->metadata_dumped || !CMM_ACCESS_ONCE(session->active))
- return 0;
- if (chan == session->metadata)
- return 0;
-
- WARN_ON_ONCE(!chan->header_type);
- ret = lttng_metadata_printf(session,
- "stream {\n"
- " id = %u;\n"
- " event.header := %s;\n"
- " packet.context := struct packet_context;\n",
- chan->id,
- chan->header_type == 1 ? "struct event_header_compact" :
- "struct event_header_large");
- if (ret)
- goto end;
-
- if (chan->ctx) {
- ret = lttng_metadata_printf(session,
- " event.context := struct {\n");
- if (ret)
- goto end;
- }
- ret = _ltt_context_metadata_statedump(session, chan->ctx);
- if (ret)
- goto end;
- if (chan->ctx) {
- ret = lttng_metadata_printf(session,
- " };\n");
- if (ret)
- goto end;
- }
-
- ret = lttng_metadata_printf(session,
- "};\n\n");
-
- chan->metadata_dumped = 1;
-end:
- return ret;
-}
-
-static
-int _ltt_stream_packet_context_declare(struct ltt_session *session)
-{
- return lttng_metadata_printf(session,
- "struct packet_context {\n"
- " uint64_t timestamp_begin;\n"
- " uint64_t timestamp_end;\n"
- " uint32_t events_discarded;\n"
- " uint32_t content_size;\n"
- " uint32_t packet_size;\n"
- " uint32_t cpu_id;\n"
- "};\n\n"
- );
-}
-
-/*
- * Compact header:
- * id: range: 0 - 30.
- * id 31 is reserved to indicate an extended header.
- *
- * Large header:
- * id: range: 0 - 65534.
- * id 65535 is reserved to indicate an extended header.
- */
-static
-int _ltt_event_header_declare(struct ltt_session *session)
-{
- return lttng_metadata_printf(session,
- "struct event_header_compact {\n"
- " enum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
- " variant <id> {\n"
- " struct {\n"
- " uint27_t timestamp;\n"
- " } compact;\n"
- " struct {\n"
- " uint32_t id;\n"
- " uint64_t timestamp;\n"
- " } extended;\n"
- " } v;\n"
- "} align(%u);\n"
- "\n"
- "struct event_header_large {\n"
- " enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;\n"
- " variant <id> {\n"
- " struct {\n"
- " uint32_t timestamp;\n"
- " } compact;\n"
- " struct {\n"
- " uint32_t id;\n"
- " uint64_t timestamp;\n"
- " } extended;\n"
- " } v;\n"
- "} align(%u);\n\n",
- lttng_alignof(uint32_t) * CHAR_BIT,
- lttng_alignof(uint16_t) * CHAR_BIT
- );
-}
-
-/*
- * Output metadata into this session's metadata buffers.
- */
-static
-int _ltt_session_metadata_statedump(struct ltt_session *session)
-{
- unsigned char *uuid_c = session->uuid;
- char uuid_s[37];
- struct ltt_channel *chan;
- struct ltt_event *event;
- int ret = 0;
-
- if (!CMM_ACCESS_ONCE(session->active))
- return 0;
- if (session->metadata_dumped)
- goto skip_session;
- if (!session->metadata) {
- DBG("LTTng: attempt to start tracing, but metadata channel is not found. Operation abort.\n");
- return -EPERM;
- }
-
- snprintf(uuid_s, sizeof(uuid_s),
- "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
- uuid_c[0], uuid_c[1], uuid_c[2], uuid_c[3],
- uuid_c[4], uuid_c[5], uuid_c[6], uuid_c[7],
- uuid_c[8], uuid_c[9], uuid_c[10], uuid_c[11],
- uuid_c[12], uuid_c[13], uuid_c[14], uuid_c[15]);
-
- ret = lttng_metadata_printf(session,
- "typealias integer { size = 8; align = %u; signed = false; } := uint8_t;\n"
- "typealias integer { size = 16; align = %u; signed = false; } := uint16_t;\n"
- "typealias integer { size = 32; align = %u; signed = false; } := uint32_t;\n"
- "typealias integer { size = 64; align = %u; signed = false; } := uint64_t;\n"
- "typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
- "typealias integer { size = 27; align = 1; signed = false; } := uint27_t;\n"
- "\n"
- "trace {\n"
- " major = %u;\n"
- " minor = %u;\n"
- " uuid = \"%s\";\n"
- " byte_order = %s;\n"
- " packet.header := struct {\n"
- " uint32_t magic;\n"
- " uint8_t uuid[16];\n"
- " uint32_t stream_id;\n"
- " };\n"
- "};\n\n",
- lttng_alignof(uint8_t) * CHAR_BIT,
- lttng_alignof(uint16_t) * CHAR_BIT,
- lttng_alignof(uint32_t) * CHAR_BIT,
- lttng_alignof(uint64_t) * CHAR_BIT,
- CTF_VERSION_MAJOR,
- CTF_VERSION_MINOR,
- uuid_s,
-#if (BYTE_ORDER == BIG_ENDIAN)
- "be"
-#else
- "le"
-#endif
- );
- if (ret)
- goto end;
-
- ret = _ltt_stream_packet_context_declare(session);
- if (ret)
- goto end;
-
- ret = _ltt_event_header_declare(session);
- if (ret)
- goto end;
-
-skip_session:
- cds_list_for_each_entry(chan, &session->chan, list) {
- ret = _ltt_channel_metadata_statedump(session, chan);
- if (ret)
- goto end;
- }
-
- cds_list_for_each_entry(event, &session->events, list) {
- ret = _ltt_event_metadata_statedump(session, event->chan, event);
- if (ret)
- goto end;
- }
- session->metadata_dumped = 1;
-end:
- return ret;
-}
-
-/**
- * ltt_transport_register - LTT transport registration
- * @transport: transport structure
- *
- * Registers a transport which can be used as output to extract the data out of
- * LTTng. Called with ust_lock held.
- */
-void ltt_transport_register(struct ltt_transport *transport)
-{
- cds_list_add_tail(&transport->node, <t_transport_list);
-}
-
-/**
- * ltt_transport_unregister - LTT transport unregistration
- * @transport: transport structure
- * Called with ust_lock held.
- */
-void ltt_transport_unregister(struct ltt_transport *transport)
-{
- cds_list_del(&transport->node);
-}
-
-void lttng_ust_events_exit(void)
-{
- struct ltt_session *session, *tmpsession;
-
- cds_list_for_each_entry_safe(session, tmpsession, &sessions, list)
- ltt_session_destroy(session);
-}
+++ /dev/null
-/*
- * ltt-probes.c
- *
- * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * Holds LTTng probes registry.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <string.h>
-#include <errno.h>
-#include <urcu/list.h>
-#include <lttng/core.h>
-#include <lttng/ust-events.h>
-
-#include "ltt-tracer-core.h"
-
-/*
- * probe list is protected by ust_lock()/ust_unlock().
- */
-static CDS_LIST_HEAD(probe_list);
-
-static
-const struct lttng_event_desc *find_event(const char *name)
-{
- struct lttng_probe_desc *probe_desc;
- int i;
-
- cds_list_for_each_entry(probe_desc, &probe_list, head) {
- for (i = 0; i < probe_desc->nr_events; i++) {
- if (!strcmp(probe_desc->event_desc[i].name, name))
- return &probe_desc->event_desc[i];
- }
- }
- return NULL;
-}
-
-int ltt_probe_register(struct lttng_probe_desc *desc)
-{
- int ret = 0;
- int i;
-
- ust_lock();
- /*
- * TODO: This is O(N^2). Turn into a hash table when probe registration
- * overhead becomes an issue.
- */
- for (i = 0; i < desc->nr_events; i++) {
- if (find_event(desc->event_desc[i].name)) {
- ret = -EEXIST;
- goto end;
- }
- }
- cds_list_add(&desc->head, &probe_list);
-
- /*
- * fix the events awaiting probe load.
- */
- for (i = 0; i < desc->nr_events; i++) {
- ret = pending_probe_fix_events(&desc->event_desc[i]);
- assert(!ret);
- }
-end:
- ust_unlock();
- return ret;
-}
-
-void ltt_probe_unregister(struct lttng_probe_desc *desc)
-{
- ust_lock();
- cds_list_del(&desc->head);
- ust_unlock();
-}
-
-/*
- * called with UST lock held.
- */
-const struct lttng_event_desc *ltt_event_get(const char *name)
-{
- const struct lttng_event_desc *event;
-
- event = find_event(name);
- if (!event)
- return NULL;
- return event;
-}
-
-void ltt_event_put(const struct lttng_event_desc *event)
-{
-}
-
-#if 0
-static
-void *tp_list_start(struct seq_file *m, loff_t *pos)
-{
- struct lttng_probe_desc *probe_desc;
- int iter = 0, i;
-
- pthread_mutex_lock(&probe_mutex);
- cds_list_for_each_entry(probe_desc, &probe_list, head) {
- for (i = 0; i < probe_desc->nr_events; i++) {
- if (iter++ >= *pos)
- return (void *) &probe_desc->event_desc[i];
- }
- }
- /* End of list */
- return NULL;
-}
-
-static
-void *tp_list_next(struct seq_file *m, void *p, loff_t *ppos)
-{
- struct lttng_probe_desc *probe_desc;
- int iter = 0, i;
-
- (*ppos)++;
- cds_list_for_each_entry(probe_desc, &probe_list, head) {
- for (i = 0; i < probe_desc->nr_events; i++) {
- if (iter++ >= *ppos)
- return (void *) &probe_desc->event_desc[i];
- }
- }
- /* End of list */
- return NULL;
-}
-
-static
-void tp_list_stop(struct seq_file *m, void *p)
-{
- pthread_mutex_unlock(&probe_mutex);
-}
-
-static
-int tp_list_show(struct seq_file *m, void *p)
-{
- const struct lttng_event_desc *probe_desc = p;
-
- /*
- * Don't export lttng internal events (metadata).
- */
- if (!strncmp(probe_desc->name, "lttng_", sizeof("lttng_") - 1))
- return 0;
- seq_printf(m, "event { name = %s; };\n",
- probe_desc->name);
- return 0;
-}
-
-static
-const struct seq_operations lttng_tracepoint_list_seq_ops = {
- .start = tp_list_start,
- .next = tp_list_next,
- .stop = tp_list_stop,
- .show = tp_list_show,
-};
-
-static
-int lttng_tracepoint_list_open(struct inode *inode, struct file *file)
-{
- return seq_open(file, <tng_tracepoint_list_seq_ops);
-}
-
-const struct file_operations lttng_tracepoint_list_fops = {
- .open = lttng_tracepoint_list_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = seq_release,
-};
-#endif //0
+++ /dev/null
-/*
- * ltt-ring-buffer-client-discard.c
- *
- * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng lib ring buffer client (discard mode).
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include "ltt-tracer.h"
-
-#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_DISCARD
-#define RING_BUFFER_MODE_TEMPLATE_STRING "discard"
-#define RING_BUFFER_MODE_TEMPLATE_INIT \
- ltt_ring_buffer_client_discard_init
-#define RING_BUFFER_MODE_TEMPLATE_EXIT \
- ltt_ring_buffer_client_discard_exit
-#include "ltt-ring-buffer-client.h"
+++ /dev/null
-/*
- * ltt-ring-buffer-client-overwrite.c
- *
- * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng lib ring buffer client (overwrite mode).
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include "ltt-tracer.h"
-
-#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_OVERWRITE
-#define RING_BUFFER_MODE_TEMPLATE_STRING "overwrite"
-#define RING_BUFFER_MODE_TEMPLATE_INIT \
- ltt_ring_buffer_client_overwrite_init
-#define RING_BUFFER_MODE_TEMPLATE_EXIT \
- ltt_ring_buffer_client_overwrite_exit
-#include "ltt-ring-buffer-client.h"
+++ /dev/null
-/*
- * ltt-ring-buffer-client.h
- *
- * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng lib ring buffer client template.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <stdint.h>
-#include <lttng/ust-events.h>
-#include "lttng/bitfield.h"
-#include "lttng/clock.h"
-#include "ltt-tracer.h"
-#include "../libringbuffer/frontend_types.h"
-
-/*
- * Keep the natural field alignment for _each field_ within this structure if
- * you ever add/remove a field from this header. Packed attribute is not used
- * because gcc generates poor code on at least powerpc and mips. Don't ever
- * let gcc add padding between the structure elements.
- */
-
-struct packet_header {
- /* Trace packet header */
- uint32_t magic; /*
- * Trace magic number.
- * contains endianness information.
- */
- uint8_t uuid[16];
- uint32_t stream_id;
-
- struct {
- /* Stream packet context */
- uint64_t timestamp_begin; /* Cycle count at subbuffer start */
- uint64_t timestamp_end; /* Cycle count at subbuffer end */
- uint32_t events_discarded; /*
- * Events lost in this subbuffer since
- * the beginning of the trace.
- * (may overflow)
- */
- uint32_t content_size; /* Size of data in subbuffer */
- uint32_t packet_size; /* Subbuffer size (include padding) */
- uint32_t cpu_id; /* CPU id associated with stream */
- uint8_t header_end; /* End of header */
- } ctx;
-};
-
-
-static inline notrace u64 lib_ring_buffer_clock_read(struct channel *chan)
-{
- return trace_clock_read64();
-}
-
-static inline
-size_t ctx_get_size(size_t offset, struct lttng_ctx *ctx)
-{
- int i;
- size_t orig_offset = offset;
-
- if (caa_likely(!ctx))
- return 0;
- for (i = 0; i < ctx->nr_fields; i++)
- offset += ctx->fields[i].get_size(offset);
- return offset - orig_offset;
-}
-
-static inline
-void ctx_record(struct lttng_ust_lib_ring_buffer_ctx *bufctx,
- struct ltt_channel *chan,
- struct lttng_ctx *ctx)
-{
- int i;
-
- if (caa_likely(!ctx))
- return;
- for (i = 0; i < ctx->nr_fields; i++)
- ctx->fields[i].record(&ctx->fields[i], bufctx, chan);
-}
-
-/*
- * record_header_size - Calculate the header size and padding necessary.
- * @config: ring buffer instance configuration
- * @chan: channel
- * @offset: offset in the write buffer
- * @pre_header_padding: padding to add before the header (output)
- * @ctx: reservation context
- *
- * Returns the event header size (including padding).
- *
- * The payload must itself determine its own alignment from the biggest type it
- * contains.
- */
-static __inline__
-unsigned char record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
- struct channel *chan, size_t offset,
- size_t *pre_header_padding,
- struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- struct ltt_channel *ltt_chan = channel_get_private(chan);
- struct ltt_event *event = ctx->priv;
- size_t orig_offset = offset;
- size_t padding;
-
- switch (ltt_chan->header_type) {
- case 1: /* compact */
- padding = lib_ring_buffer_align(offset, lttng_alignof(uint32_t));
- offset += padding;
- if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
- offset += sizeof(uint32_t); /* id and timestamp */
- } else {
- /* Minimum space taken by 5-bit id */
- offset += sizeof(uint8_t);
- /* Align extended struct on largest member */
- offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
- offset += sizeof(uint32_t); /* id */
- offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
- offset += sizeof(uint64_t); /* timestamp */
- }
- break;
- case 2: /* large */
- padding = lib_ring_buffer_align(offset, lttng_alignof(uint16_t));
- offset += padding;
- offset += sizeof(uint16_t);
- if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
- offset += lib_ring_buffer_align(offset, lttng_alignof(uint32_t));
- offset += sizeof(uint32_t); /* timestamp */
- } else {
- /* Align extended struct on largest member */
- offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
- offset += sizeof(uint32_t); /* id */
- offset += lib_ring_buffer_align(offset, lttng_alignof(uint64_t));
- offset += sizeof(uint64_t); /* timestamp */
- }
- break;
- default:
- padding = 0;
- WARN_ON_ONCE(1);
- }
- offset += ctx_get_size(offset, event->ctx);
- offset += ctx_get_size(offset, ltt_chan->ctx);
-
- *pre_header_padding = padding;
- return offset - orig_offset;
-}
-
-#include "../libringbuffer/api.h"
-
-static
-void ltt_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config *config,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- uint32_t event_id);
-
-/*
- * ltt_write_event_header
- *
- * Writes the event header to the offset (already aligned on 32-bits).
- *
- * @config: ring buffer instance configuration
- * @ctx: reservation context
- * @event_id: event ID
- */
-static __inline__
-void ltt_write_event_header(const struct lttng_ust_lib_ring_buffer_config *config,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- uint32_t event_id)
-{
- struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
- struct ltt_event *event = ctx->priv;
-
- if (caa_unlikely(ctx->rflags))
- goto slow_path;
-
- switch (ltt_chan->header_type) {
- case 1: /* compact */
- {
- uint32_t id_time = 0;
-
- bt_bitfield_write(&id_time, uint32_t, 0, 5, event_id);
- bt_bitfield_write(&id_time, uint32_t, 5, 27, ctx->tsc);
- lib_ring_buffer_write(config, ctx, &id_time, sizeof(id_time));
- break;
- }
- case 2: /* large */
- {
- uint32_t timestamp = (uint32_t) ctx->tsc;
- uint16_t id = event_id;
-
- lib_ring_buffer_write(config, ctx, &id, sizeof(id));
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint32_t));
- lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
- break;
- }
- default:
- WARN_ON_ONCE(1);
- }
-
- ctx_record(ctx, ltt_chan, ltt_chan->ctx);
- ctx_record(ctx, ltt_chan, event->ctx);
- lib_ring_buffer_align_ctx(ctx, ctx->largest_align);
-
- return;
-
-slow_path:
- ltt_write_event_header_slow(config, ctx, event_id);
-}
-
-static
-void ltt_write_event_header_slow(const struct lttng_ust_lib_ring_buffer_config *config,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- uint32_t event_id)
-{
- struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
- struct ltt_event *event = ctx->priv;
-
- switch (ltt_chan->header_type) {
- case 1: /* compact */
- if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
- uint32_t id_time = 0;
-
- bt_bitfield_write(&id_time, uint32_t, 0, 5, event_id);
- bt_bitfield_write(&id_time, uint32_t, 5, 27, ctx->tsc);
- lib_ring_buffer_write(config, ctx, &id_time, sizeof(id_time));
- } else {
- uint8_t id = 0;
- uint64_t timestamp = ctx->tsc;
-
- bt_bitfield_write(&id, uint8_t, 0, 5, 31);
- lib_ring_buffer_write(config, ctx, &id, sizeof(id));
- /* Align extended struct on largest member */
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
- lib_ring_buffer_write(config, ctx, &event_id, sizeof(event_id));
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
- lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
- }
- break;
- case 2: /* large */
- {
- if (!(ctx->rflags & (RING_BUFFER_RFLAG_FULL_TSC | LTT_RFLAG_EXTENDED))) {
- uint32_t timestamp = (uint32_t) ctx->tsc;
- uint16_t id = event_id;
-
- lib_ring_buffer_write(config, ctx, &id, sizeof(id));
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint32_t));
- lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
- } else {
- uint16_t id = 65535;
- uint64_t timestamp = ctx->tsc;
-
- lib_ring_buffer_write(config, ctx, &id, sizeof(id));
- /* Align extended struct on largest member */
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
- lib_ring_buffer_write(config, ctx, &event_id, sizeof(event_id));
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(uint64_t));
- lib_ring_buffer_write(config, ctx, ×tamp, sizeof(timestamp));
- }
- break;
- }
- default:
- WARN_ON_ONCE(1);
- }
- ctx_record(ctx, ltt_chan, ltt_chan->ctx);
- ctx_record(ctx, ltt_chan, event->ctx);
- lib_ring_buffer_align_ctx(ctx, ctx->largest_align);
-}
-
-static const struct lttng_ust_lib_ring_buffer_config client_config;
-
-static u64 client_ring_buffer_clock_read(struct channel *chan)
-{
- return lib_ring_buffer_clock_read(chan);
-}
-
-static
-size_t client_record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
- struct channel *chan, size_t offset,
- size_t *pre_header_padding,
- struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- return record_header_size(config, chan, offset,
- pre_header_padding, ctx);
-}
-
-/**
- * client_packet_header_size - called on buffer-switch to a new sub-buffer
- *
- * Return header size without padding after the structure. Don't use packed
- * structure because gcc generates inefficient code on some architectures
- * (powerpc, mips..)
- */
-static size_t client_packet_header_size(void)
-{
- return offsetof(struct packet_header, ctx.header_end);
-}
-
-static void client_buffer_begin(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
- unsigned int subbuf_idx,
- struct lttng_ust_shm_handle *handle)
-{
- struct channel *chan = shmp(handle, buf->backend.chan);
- struct packet_header *header =
- (struct packet_header *)
- lib_ring_buffer_offset_address(&buf->backend,
- subbuf_idx * chan->backend.subbuf_size,
- handle);
- struct ltt_channel *ltt_chan = channel_get_private(chan);
- struct ltt_session *session = ltt_chan->session;
-
- header->magic = CTF_MAGIC_NUMBER;
- memcpy(header->uuid, session->uuid, sizeof(session->uuid));
- header->stream_id = ltt_chan->id;
- header->ctx.timestamp_begin = tsc;
- header->ctx.timestamp_end = 0;
- header->ctx.events_discarded = 0;
- header->ctx.content_size = 0xFFFFFFFF; /* for debugging */
- header->ctx.packet_size = 0xFFFFFFFF;
- header->ctx.cpu_id = buf->backend.cpu;
-}
-
-/*
- * offset is assumed to never be 0 here : never deliver a completely empty
- * subbuffer. data_size is between 1 and subbuf_size.
- */
-static void client_buffer_end(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
- unsigned int subbuf_idx, unsigned long data_size,
- struct lttng_ust_shm_handle *handle)
-{
- struct channel *chan = shmp(handle, buf->backend.chan);
- struct packet_header *header =
- (struct packet_header *)
- lib_ring_buffer_offset_address(&buf->backend,
- subbuf_idx * chan->backend.subbuf_size,
- handle);
- unsigned long records_lost = 0;
-
- header->ctx.timestamp_end = tsc;
- header->ctx.content_size = data_size * CHAR_BIT; /* in bits */
- header->ctx.packet_size = PAGE_ALIGN(data_size) * CHAR_BIT; /* in bits */
- /*
- * We do not care about the records lost count, because the metadata
- * channel waits and retry.
- */
- (void) lib_ring_buffer_get_records_lost_full(&client_config, buf);
- records_lost += lib_ring_buffer_get_records_lost_wrap(&client_config, buf);
- records_lost += lib_ring_buffer_get_records_lost_big(&client_config, buf);
- header->ctx.events_discarded = records_lost;
-}
-
-static int client_buffer_create(struct lttng_ust_lib_ring_buffer *buf, void *priv,
- int cpu, const char *name, struct lttng_ust_shm_handle *handle)
-{
- return 0;
-}
-
-static void client_buffer_finalize(struct lttng_ust_lib_ring_buffer *buf, void *priv, int cpu, struct lttng_ust_shm_handle *handle)
-{
-}
-
-static const struct lttng_ust_lib_ring_buffer_config client_config = {
- .cb.ring_buffer_clock_read = client_ring_buffer_clock_read,
- .cb.record_header_size = client_record_header_size,
- .cb.subbuffer_header_size = client_packet_header_size,
- .cb.buffer_begin = client_buffer_begin,
- .cb.buffer_end = client_buffer_end,
- .cb.buffer_create = client_buffer_create,
- .cb.buffer_finalize = client_buffer_finalize,
-
- .tsc_bits = 32,
- .alloc = RING_BUFFER_ALLOC_PER_CPU,
- .sync = RING_BUFFER_SYNC_GLOBAL,
- .mode = RING_BUFFER_MODE_TEMPLATE,
- .backend = RING_BUFFER_PAGE,
- .output = RING_BUFFER_MMAP,
- .oops = RING_BUFFER_OOPS_CONSISTENCY,
- .ipi = RING_BUFFER_NO_IPI_BARRIER,
- .wakeup = RING_BUFFER_WAKEUP_BY_WRITER,
-};
-
-static
-struct ltt_channel *_channel_create(const char *name,
- struct ltt_channel *ltt_chan, void *buf_addr,
- size_t subbuf_size, size_t num_subbuf,
- unsigned int switch_timer_interval,
- unsigned int read_timer_interval,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size)
-{
- ltt_chan->handle = channel_create(&client_config, name, ltt_chan, buf_addr,
- subbuf_size, num_subbuf, switch_timer_interval,
- read_timer_interval, shm_fd, wait_fd,
- memory_map_size);
- if (!ltt_chan->handle)
- return NULL;
- ltt_chan->chan = shmp(ltt_chan->handle, ltt_chan->handle->chan);
- return ltt_chan;
-}
-
-static
-void ltt_channel_destroy(struct ltt_channel *ltt_chan)
-{
- channel_destroy(ltt_chan->chan, ltt_chan->handle, 0);
-}
-
-static
-struct lttng_ust_lib_ring_buffer *ltt_buffer_read_open(struct channel *chan,
- struct lttng_ust_shm_handle *handle,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size)
-{
- struct lttng_ust_lib_ring_buffer *buf;
- int cpu;
-
- for_each_channel_cpu(cpu, chan) {
- buf = channel_get_ring_buffer(&client_config, chan,
- cpu, handle, shm_fd, wait_fd,
- memory_map_size);
- if (!lib_ring_buffer_open_read(buf, handle, 0))
- return buf;
- }
- return NULL;
-}
-
-static
-void ltt_buffer_read_close(struct lttng_ust_lib_ring_buffer *buf,
- struct lttng_ust_shm_handle *handle)
-{
- lib_ring_buffer_release_read(buf, handle, 0);
-}
-
-static
-int ltt_event_reserve(struct lttng_ust_lib_ring_buffer_ctx *ctx,
- uint32_t event_id)
-{
- struct ltt_channel *ltt_chan = channel_get_private(ctx->chan);
- int ret, cpu;
-
- cpu = lib_ring_buffer_get_cpu(&client_config);
- if (cpu < 0)
- return -EPERM;
- ctx->cpu = cpu;
-
- switch (ltt_chan->header_type) {
- case 1: /* compact */
- if (event_id > 30)
- ctx->rflags |= LTT_RFLAG_EXTENDED;
- break;
- case 2: /* large */
- if (event_id > 65534)
- ctx->rflags |= LTT_RFLAG_EXTENDED;
- break;
- default:
- WARN_ON_ONCE(1);
- }
-
- ret = lib_ring_buffer_reserve(&client_config, ctx);
- if (ret)
- goto put;
- ltt_write_event_header(&client_config, ctx, event_id);
- return 0;
-put:
- lib_ring_buffer_put_cpu(&client_config);
- return ret;
-}
-
-static
-void ltt_event_commit(struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- lib_ring_buffer_commit(&client_config, ctx);
- lib_ring_buffer_put_cpu(&client_config);
-}
-
-static
-void ltt_event_write(struct lttng_ust_lib_ring_buffer_ctx *ctx, const void *src,
- size_t len)
-{
- lib_ring_buffer_write(&client_config, ctx, src, len);
-}
-
-#if 0
-static
-wait_queue_head_t *ltt_get_reader_wait_queue(struct channel *chan)
-{
- return &chan->read_wait;
-}
-
-static
-wait_queue_head_t *ltt_get_hp_wait_queue(struct channel *chan)
-{
- return &chan->hp_wait;
-}
-#endif //0
-
-static
-int ltt_is_finalized(struct channel *chan)
-{
- return lib_ring_buffer_channel_is_finalized(chan);
-}
-
-static
-int ltt_is_disabled(struct channel *chan)
-{
- return lib_ring_buffer_channel_is_disabled(chan);
-}
-
-static
-int ltt_flush_buffer(struct channel *chan, struct lttng_ust_shm_handle *handle)
-{
- struct lttng_ust_lib_ring_buffer *buf;
- int cpu;
-
- for_each_channel_cpu(cpu, chan) {
- int shm_fd, wait_fd;
- uint64_t memory_map_size;
-
- buf = channel_get_ring_buffer(&client_config, chan,
- cpu, handle, &shm_fd, &wait_fd,
- &memory_map_size);
- lib_ring_buffer_switch(&client_config, buf,
- SWITCH_ACTIVE, handle);
- }
- return 0;
-}
-
-static struct ltt_transport ltt_relay_transport = {
- .name = "relay-" RING_BUFFER_MODE_TEMPLATE_STRING "-mmap",
- .ops = {
- .channel_create = _channel_create,
- .channel_destroy = ltt_channel_destroy,
- .buffer_read_open = ltt_buffer_read_open,
- .buffer_read_close = ltt_buffer_read_close,
- .event_reserve = ltt_event_reserve,
- .event_commit = ltt_event_commit,
- .event_write = ltt_event_write,
- .packet_avail_size = NULL, /* Would be racy anyway */
- //.get_reader_wait_queue = ltt_get_reader_wait_queue,
- //.get_hp_wait_queue = ltt_get_hp_wait_queue,
- .is_finalized = ltt_is_finalized,
- .is_disabled = ltt_is_disabled,
- .flush_buffer = ltt_flush_buffer,
- },
-};
-
-void RING_BUFFER_MODE_TEMPLATE_INIT(void)
-{
- DBG("LTT : ltt ring buffer client init\n");
- ltt_transport_register(<t_relay_transport);
-}
-
-void RING_BUFFER_MODE_TEMPLATE_EXIT(void)
-{
- DBG("LTT : ltt ring buffer client exit\n");
- ltt_transport_unregister(<t_relay_transport);
-}
+++ /dev/null
-/*
- * ltt-ring-buffer-metadata-client.c
- *
- * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng lib ring buffer metadta client.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include "ltt-tracer.h"
-
-#define RING_BUFFER_MODE_TEMPLATE RING_BUFFER_DISCARD
-#define RING_BUFFER_MODE_TEMPLATE_STRING "metadata"
-#define RING_BUFFER_MODE_TEMPLATE_INIT \
- ltt_ring_buffer_metadata_client_init
-#define RING_BUFFER_MODE_TEMPLATE_EXIT \
- ltt_ring_buffer_metadata_client_exit
-#include "ltt-ring-buffer-metadata-client.h"
+++ /dev/null
-/*
- * ltt-ring-buffer-client.h
- *
- * Copyright (C) 2010 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng lib ring buffer client template.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <stdint.h>
-#include <lttng/ust-events.h>
-#include "lttng/bitfield.h"
-#include "ltt-tracer.h"
-#include "../libringbuffer/frontend_types.h"
-
-struct metadata_packet_header {
- uint32_t magic; /* 0x75D11D57 */
- uint8_t uuid[16]; /* Unique Universal Identifier */
- uint32_t checksum; /* 0 if unused */
- uint32_t content_size; /* in bits */
- uint32_t packet_size; /* in bits */
- uint8_t compression_scheme; /* 0 if unused */
- uint8_t encryption_scheme; /* 0 if unused */
- uint8_t checksum_scheme; /* 0 if unused */
- uint8_t major; /* CTF spec major version number */
- uint8_t minor; /* CTF spec minor version number */
- uint8_t header_end[0];
-};
-
-struct metadata_record_header {
- uint8_t header_end[0]; /* End of header */
-};
-
-static const struct lttng_ust_lib_ring_buffer_config client_config;
-
-static inline
-u64 lib_ring_buffer_clock_read(struct channel *chan)
-{
- return 0;
-}
-
-static inline
-unsigned char record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
- struct channel *chan, size_t offset,
- size_t *pre_header_padding,
- struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- return 0;
-}
-
-#include "../libringbuffer/api.h"
-
-static u64 client_ring_buffer_clock_read(struct channel *chan)
-{
- return 0;
-}
-
-static
-size_t client_record_header_size(const struct lttng_ust_lib_ring_buffer_config *config,
- struct channel *chan, size_t offset,
- size_t *pre_header_padding,
- struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- return 0;
-}
-
-/**
- * client_packet_header_size - called on buffer-switch to a new sub-buffer
- *
- * Return header size without padding after the structure. Don't use packed
- * structure because gcc generates inefficient code on some architectures
- * (powerpc, mips..)
- */
-static size_t client_packet_header_size(void)
-{
- return offsetof(struct metadata_packet_header, header_end);
-}
-
-static void client_buffer_begin(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
- unsigned int subbuf_idx,
- struct lttng_ust_shm_handle *handle)
-{
- struct channel *chan = shmp(handle, buf->backend.chan);
- struct metadata_packet_header *header =
- (struct metadata_packet_header *)
- lib_ring_buffer_offset_address(&buf->backend,
- subbuf_idx * chan->backend.subbuf_size,
- handle);
- struct ltt_channel *ltt_chan = channel_get_private(chan);
- struct ltt_session *session = ltt_chan->session;
-
- header->magic = TSDL_MAGIC_NUMBER;
- memcpy(header->uuid, session->uuid, sizeof(session->uuid));
- header->checksum = 0; /* 0 if unused */
- header->content_size = 0xFFFFFFFF; /* in bits, for debugging */
- header->packet_size = 0xFFFFFFFF; /* in bits, for debugging */
- header->compression_scheme = 0; /* 0 if unused */
- header->encryption_scheme = 0; /* 0 if unused */
- header->checksum_scheme = 0; /* 0 if unused */
- header->major = CTF_SPEC_MAJOR;
- header->minor = CTF_SPEC_MINOR;
-
-}
-
-/*
- * offset is assumed to never be 0 here : never deliver a completely empty
- * subbuffer. data_size is between 1 and subbuf_size.
- */
-static void client_buffer_end(struct lttng_ust_lib_ring_buffer *buf, u64 tsc,
- unsigned int subbuf_idx, unsigned long data_size,
- struct lttng_ust_shm_handle *handle)
-{
- struct channel *chan = shmp(handle, buf->backend.chan);
- struct metadata_packet_header *header =
- (struct metadata_packet_header *)
- lib_ring_buffer_offset_address(&buf->backend,
- subbuf_idx * chan->backend.subbuf_size,
- handle);
- unsigned long records_lost = 0;
-
- header->content_size = data_size * CHAR_BIT; /* in bits */
- header->packet_size = PAGE_ALIGN(data_size) * CHAR_BIT; /* in bits */
- records_lost += lib_ring_buffer_get_records_lost_full(&client_config, buf);
- records_lost += lib_ring_buffer_get_records_lost_wrap(&client_config, buf);
- records_lost += lib_ring_buffer_get_records_lost_big(&client_config, buf);
- WARN_ON_ONCE(records_lost != 0);
-}
-
-static int client_buffer_create(struct lttng_ust_lib_ring_buffer *buf, void *priv,
- int cpu, const char *name,
- struct lttng_ust_shm_handle *handle)
-{
- return 0;
-}
-
-static void client_buffer_finalize(struct lttng_ust_lib_ring_buffer *buf,
- void *priv, int cpu,
- struct lttng_ust_shm_handle *handle)
-{
-}
-
-static const struct lttng_ust_lib_ring_buffer_config client_config = {
- .cb.ring_buffer_clock_read = client_ring_buffer_clock_read,
- .cb.record_header_size = client_record_header_size,
- .cb.subbuffer_header_size = client_packet_header_size,
- .cb.buffer_begin = client_buffer_begin,
- .cb.buffer_end = client_buffer_end,
- .cb.buffer_create = client_buffer_create,
- .cb.buffer_finalize = client_buffer_finalize,
-
- .tsc_bits = 0,
- .alloc = RING_BUFFER_ALLOC_GLOBAL,
- .sync = RING_BUFFER_SYNC_GLOBAL,
- .mode = RING_BUFFER_MODE_TEMPLATE,
- .backend = RING_BUFFER_PAGE,
- .output = RING_BUFFER_MMAP,
- .oops = RING_BUFFER_OOPS_CONSISTENCY,
- .ipi = RING_BUFFER_NO_IPI_BARRIER,
- .wakeup = RING_BUFFER_WAKEUP_BY_WRITER,
-};
-
-static
-struct ltt_channel *_channel_create(const char *name,
- struct ltt_channel *ltt_chan, void *buf_addr,
- size_t subbuf_size, size_t num_subbuf,
- unsigned int switch_timer_interval,
- unsigned int read_timer_interval,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size)
-{
- ltt_chan->handle = channel_create(&client_config, name, ltt_chan, buf_addr,
- subbuf_size, num_subbuf, switch_timer_interval,
- read_timer_interval, shm_fd, wait_fd,
- memory_map_size);
- if (!ltt_chan->handle)
- return NULL;
- ltt_chan->chan = shmp(ltt_chan->handle, ltt_chan->handle->chan);
- return ltt_chan;
-}
-
-static
-void ltt_channel_destroy(struct ltt_channel *ltt_chan)
-{
- channel_destroy(ltt_chan->chan, ltt_chan->handle, 0);
-}
-
-static
-struct lttng_ust_lib_ring_buffer *ltt_buffer_read_open(struct channel *chan,
- struct lttng_ust_shm_handle *handle,
- int *shm_fd, int *wait_fd,
- uint64_t *memory_map_size)
-{
- struct lttng_ust_lib_ring_buffer *buf;
-
- buf = channel_get_ring_buffer(&client_config, chan,
- 0, handle, shm_fd, wait_fd, memory_map_size);
- if (!lib_ring_buffer_open_read(buf, handle, 0))
- return buf;
- return NULL;
-}
-
-static
-void ltt_buffer_read_close(struct lttng_ust_lib_ring_buffer *buf,
- struct lttng_ust_shm_handle *handle)
-{
- lib_ring_buffer_release_read(buf, handle, 0);
-}
-
-static
-int ltt_event_reserve(struct lttng_ust_lib_ring_buffer_ctx *ctx, uint32_t event_id)
-{
- return lib_ring_buffer_reserve(&client_config, ctx);
-}
-
-static
-void ltt_event_commit(struct lttng_ust_lib_ring_buffer_ctx *ctx)
-{
- lib_ring_buffer_commit(&client_config, ctx);
-}
-
-static
-void ltt_event_write(struct lttng_ust_lib_ring_buffer_ctx *ctx, const void *src,
- size_t len)
-{
- lib_ring_buffer_write(&client_config, ctx, src, len);
-}
-
-static
-size_t ltt_packet_avail_size(struct channel *chan, struct lttng_ust_shm_handle *handle)
-
-{
- unsigned long o_begin;
- struct lttng_ust_lib_ring_buffer *buf;
-
- buf = shmp(handle, chan->backend.buf[0].shmp); /* Only for global buffer ! */
- o_begin = v_read(&client_config, &buf->offset);
- if (subbuf_offset(o_begin, chan) != 0) {
- return chan->backend.subbuf_size - subbuf_offset(o_begin, chan);
- } else {
- return chan->backend.subbuf_size - subbuf_offset(o_begin, chan)
- - sizeof(struct metadata_packet_header);
- }
-}
-
-#if 0
-static
-wait_queue_head_t *ltt_get_reader_wait_queue(struct channel *chan)
-{
- return &chan->read_wait;
-}
-
-static
-wait_queue_head_t *ltt_get_hp_wait_queue(struct channel *chan)
-{
- return &chan->hp_wait;
-}
-#endif //0
-
-static
-int ltt_is_finalized(struct channel *chan)
-{
- return lib_ring_buffer_channel_is_finalized(chan);
-}
-
-static
-int ltt_is_disabled(struct channel *chan)
-{
- return lib_ring_buffer_channel_is_disabled(chan);
-}
-
-static
-int ltt_flush_buffer(struct channel *chan, struct lttng_ust_shm_handle *handle)
-{
- struct lttng_ust_lib_ring_buffer *buf;
- int shm_fd, wait_fd;
- uint64_t memory_map_size;
-
- buf = channel_get_ring_buffer(&client_config, chan,
- 0, handle, &shm_fd, &wait_fd,
- &memory_map_size);
- lib_ring_buffer_switch(&client_config, buf,
- SWITCH_ACTIVE, handle);
- return 0;
-}
-
-static struct ltt_transport ltt_relay_transport = {
- .name = "relay-" RING_BUFFER_MODE_TEMPLATE_STRING "-mmap",
- .ops = {
- .channel_create = _channel_create,
- .channel_destroy = ltt_channel_destroy,
- .buffer_read_open = ltt_buffer_read_open,
- .buffer_read_close = ltt_buffer_read_close,
- .event_reserve = ltt_event_reserve,
- .event_commit = ltt_event_commit,
- .event_write = ltt_event_write,
- .packet_avail_size = ltt_packet_avail_size,
- //.get_reader_wait_queue = ltt_get_reader_wait_queue,
- //.get_hp_wait_queue = ltt_get_hp_wait_queue,
- .is_finalized = ltt_is_finalized,
- .is_disabled = ltt_is_disabled,
- .flush_buffer = ltt_flush_buffer,
- },
-};
-
-void RING_BUFFER_MODE_TEMPLATE_INIT(void)
-{
- DBG("LTT : ltt ring buffer client init\n");
- ltt_transport_register(<t_relay_transport);
-}
-
-void RING_BUFFER_MODE_TEMPLATE_EXIT(void)
-{
- DBG("LTT : ltt ring buffer client exit\n");
- ltt_transport_unregister(<t_relay_transport);
-}
+++ /dev/null
-#ifndef _LTT_TRACER_CORE_H
-#define _LTT_TRACER_CORE_H
-
-/*
- * Copyright (C) 2005-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This contains the core definitions for the Linux Trace Toolkit.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1 of
- * the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <lttng/kcompat/kcompat.h>
-#include <lttng/core.h>
-#include <lttng/ust-tracer.h>
-#include <urcu/list.h>
-#include <lttng/usterr-signal-safe.h>
-#include "lttng/bug.h"
-
-#include <lttng/ringbuffer-config.h>
-
-struct ltt_session;
-struct ltt_channel;
-struct ltt_event;
-
-void ust_lock(void);
-void ust_unlock(void);
-
-#endif /* _LTT_TRACER_CORE_H */
+++ /dev/null
-#ifndef _LTT_TRACER_H
-#define _LTT_TRACER_H
-
-/*
- * Copyright (C) 2005-2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This contains the definitions for the Linux Trace Toolkit tracer.
- *
- * Ported to userspace by Pierre-Marc Fournier.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1 of
- * the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <stdarg.h>
-#include <stdint.h>
-#include <lttng/core.h>
-#include <lttng/compat.h>
-#include <lttng/ust-events.h>
-#include "ltt-tracer-core.h"
-
-/* Number of bytes to log with a read/write event */
-#define LTT_LOG_RW_SIZE 32L
-#define LTT_MAX_SMALL_SIZE 0xFFFFU
-
-/* Tracer properties */
-#define CTF_MAGIC_NUMBER 0xC1FC1FC1
-#define TSDL_MAGIC_NUMBER 0x75D11D57
-
-/* CTF specification version followed */
-#define CTF_SPEC_MAJOR 1
-#define CTF_SPEC_MINOR 8
-
-/* Tracer major/minor versions */
-#define CTF_VERSION_MAJOR 0
-#define CTF_VERSION_MINOR 1
-
-/*
- * Number of milliseconds to retry before failing metadata writes on buffer full
- * condition. (10 seconds)
- */
-#define LTTNG_METADATA_TIMEOUT_MSEC 10000
-
-#define LTT_RFLAG_EXTENDED RING_BUFFER_RFLAG_END
-#define LTT_RFLAG_END (LTT_RFLAG_EXTENDED << 1)
-
-#endif /* _LTT_TRACER_H */
+++ /dev/null
-/*
- * (C) Copyright 2009-2011 -
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST procname context.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <sys/prctl.h>
-#include <lttng/ust-events.h>
-#include <lttng/ust-tracer.h>
-#include <lttng/ringbuffer-config.h>
-#include <assert.h>
-
-#define PROCNAME_LEN 17 /* includes \0 */
-
-/*
- * We cache the result to ensure we don't trigger a system call for
- * each event.
- * Upon exec, procname changes, but exec takes care of throwing away
- * this cached version.
- */
-static char cached_procname[17];
-
-static inline
-char *wrapper_getprocname(void)
-{
- int ret;
-
- if (caa_unlikely(!cached_procname[0])) {
- ret = prctl(PR_GET_NAME, (unsigned long) cached_procname,
- 0, 0, 0);
- assert(!ret);
- }
- return cached_procname;
-}
-
-void lttng_context_procname_reset(void)
-{
- cached_procname[0] = '\0';
-}
-
-static
-size_t procname_get_size(size_t offset)
-{
- size_t size = 0;
-
- size += PROCNAME_LEN;
- return size;
-}
-
-static
-void procname_record(struct lttng_ctx_field *field,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- struct ltt_channel *chan)
-{
- char *procname;
-
- procname = wrapper_getprocname();
- chan->ops->event_write(ctx, procname, PROCNAME_LEN);
-}
-
-int lttng_add_procname_to_ctx(struct lttng_ctx **ctx)
-{
- struct lttng_ctx_field *field;
-
- field = lttng_append_context(ctx);
- if (!field)
- return -ENOMEM;
- if (lttng_find_context(*ctx, "procname")) {
- lttng_remove_context_field(ctx, field);
- return -EEXIST;
- }
- field->event_field.name = "procname";
- field->event_field.type.atype = atype_array;
- field->event_field.type.u.array.elem_type.atype = atype_integer;
- field->event_field.type.u.array.elem_type.u.basic.integer.size = sizeof(char) * CHAR_BIT;
- field->event_field.type.u.array.elem_type.u.basic.integer.alignment = lttng_alignof(char) * CHAR_BIT;
- field->event_field.type.u.array.elem_type.u.basic.integer.signedness = lttng_is_signed_type(char);
- field->event_field.type.u.array.elem_type.u.basic.integer.reverse_byte_order = 0;
- field->event_field.type.u.array.elem_type.u.basic.integer.base = 10;
- field->event_field.type.u.array.elem_type.u.basic.integer.encoding = lttng_encode_UTF8;
- field->event_field.type.u.array.length = PROCNAME_LEN;
- field->get_size = procname_get_size;
- field->record = procname_record;
- return 0;
-}
+++ /dev/null
-/*
- * (C) Copyright 2009-2011 -
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST pthread_id context.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <pthread.h>
-#include <lttng/ust-events.h>
-#include <lttng/ust-tracer.h>
-#include <lttng/ringbuffer-config.h>
-
-static
-size_t pthread_id_get_size(size_t offset)
-{
- size_t size = 0;
-
- size += lib_ring_buffer_align(offset, lttng_alignof(unsigned long));
- size += sizeof(unsigned long);
- return size;
-}
-
-static
-void pthread_id_record(struct lttng_ctx_field *field,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- struct ltt_channel *chan)
-{
- unsigned long pthread_id;
-
- pthread_id = (unsigned long) pthread_self();
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(pthread_id));
- chan->ops->event_write(ctx, &pthread_id, sizeof(pthread_id));
-}
-
-int lttng_add_pthread_id_to_ctx(struct lttng_ctx **ctx)
-{
- struct lttng_ctx_field *field;
-
- field = lttng_append_context(ctx);
- if (!field)
- return -ENOMEM;
- if (lttng_find_context(*ctx, "pthread_id")) {
- lttng_remove_context_field(ctx, field);
- return -EEXIST;
- }
- field->event_field.name = "pthread_id";
- field->event_field.type.atype = atype_integer;
- field->event_field.type.u.basic.integer.size = sizeof(unsigned long) * CHAR_BIT;
- field->event_field.type.u.basic.integer.alignment = lttng_alignof(unsigned long) * CHAR_BIT;
- field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(unsigned long);
- field->event_field.type.u.basic.integer.reverse_byte_order = 0;
- field->event_field.type.u.basic.integer.base = 10;
- field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
- field->get_size = pthread_id_get_size;
- field->record = pthread_id_record;
- return 0;
-}
+++ /dev/null
-/*
- * (C) Copyright 2009-2011 -
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST vpid context.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <lttng/ust-events.h>
-#include <lttng/ust-tracer.h>
-#include <lttng/ringbuffer-config.h>
-
-#ifdef __linux__
-static inline
-pid_t wrapper_getpid(void)
-{
- return getpid();
-}
-
-void lttng_context_vpid_reset(void)
-{
-}
-#else
-/*
- * We cache the result to ensure we don't trigger a system call for
- * each event.
- */
-static pid_t cached_vpid;
-
-static inline
-pid_t wrapper_getpid(void)
-{
- if (caa_unlikely(!cached_vpid))
- cached_vpid = getpid();
- return cached_vpid;
-}
-
-/*
- * Upon fork or clone, the PID assigned to our thread is not the same as
- * we kept in cache.
- */
-void lttng_context_vpid_reset(void)
-{
- cached_vpid = 0;
-}
-#endif
-
-static
-size_t vpid_get_size(size_t offset)
-{
- size_t size = 0;
-
- size += lib_ring_buffer_align(offset, lttng_alignof(pid_t));
- size += sizeof(pid_t);
- return size;
-}
-
-static
-void vpid_record(struct lttng_ctx_field *field,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- struct ltt_channel *chan)
-{
- pid_t pid;
-
- pid = wrapper_getpid();
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(pid));
- chan->ops->event_write(ctx, &pid, sizeof(pid));
-}
-
-int lttng_add_vpid_to_ctx(struct lttng_ctx **ctx)
-{
- struct lttng_ctx_field *field;
-
- field = lttng_append_context(ctx);
- if (!field)
- return -ENOMEM;
- if (lttng_find_context(*ctx, "vpid")) {
- lttng_remove_context_field(ctx, field);
- return -EEXIST;
- }
- field->event_field.name = "vpid";
- field->event_field.type.atype = atype_integer;
- field->event_field.type.u.basic.integer.size = sizeof(pid_t) * CHAR_BIT;
- field->event_field.type.u.basic.integer.alignment = lttng_alignof(pid_t) * CHAR_BIT;
- field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(pid_t);
- field->event_field.type.u.basic.integer.reverse_byte_order = 0;
- field->event_field.type.u.basic.integer.base = 10;
- field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
- field->get_size = vpid_get_size;
- field->record = vpid_record;
- return 0;
-}
+++ /dev/null
-/*
- * (C) Copyright 2009-2011 -
- * Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST vtid context.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <lttng/ust-events.h>
-#include <lttng/ust-tracer.h>
-#include <lttng/ringbuffer-config.h>
-
-#ifdef __linux__
-#include <syscall.h>
-#endif
-
-#if defined(_syscall0)
-_syscall0(pid_t, gettid)
-#elif defined(__NR_gettid)
-static inline pid_t gettid(void)
-{
- return syscall(__NR_gettid);
-}
-#else
-#warning "use pid as tid"
-static inline pid_t gettid(void)
-{
- return getpid();
-}
-#endif
-
-/*
- * We cache the result to ensure we don't trigger a system call for
- * each event.
- */
-static __thread pid_t cached_vtid;
-
-/*
- * Upon fork or clone, the TID assigned to our thread is not the same as
- * we kept in cache. Luckily, we are the only thread surviving in the
- * child process, so we can simply clear our cached version.
- */
-void lttng_context_vtid_reset(void)
-{
- cached_vtid = 0;
-}
-
-static
-size_t vtid_get_size(size_t offset)
-{
- size_t size = 0;
-
- size += lib_ring_buffer_align(offset, lttng_alignof(pid_t));
- size += sizeof(pid_t);
- return size;
-}
-
-static
-void vtid_record(struct lttng_ctx_field *field,
- struct lttng_ust_lib_ring_buffer_ctx *ctx,
- struct ltt_channel *chan)
-{
- if (caa_unlikely(!cached_vtid))
- cached_vtid = gettid();
- lib_ring_buffer_align_ctx(ctx, lttng_alignof(cached_vtid));
- chan->ops->event_write(ctx, &cached_vtid, sizeof(cached_vtid));
-}
-
-int lttng_add_vtid_to_ctx(struct lttng_ctx **ctx)
-{
- struct lttng_ctx_field *field;
-
- field = lttng_append_context(ctx);
- if (!field)
- return -ENOMEM;
- if (lttng_find_context(*ctx, "vtid")) {
- lttng_remove_context_field(ctx, field);
- return -EEXIST;
- }
- field->event_field.name = "vtid";
- field->event_field.type.atype = atype_integer;
- field->event_field.type.u.basic.integer.size = sizeof(pid_t) * CHAR_BIT;
- field->event_field.type.u.basic.integer.alignment = lttng_alignof(pid_t) * CHAR_BIT;
- field->event_field.type.u.basic.integer.signedness = lttng_is_signed_type(pid_t);
- field->event_field.type.u.basic.integer.reverse_byte_order = 0;
- field->event_field.type.u.basic.integer.base = 10;
- field->event_field.type.u.basic.integer.encoding = lttng_encode_none;
- field->get_size = vtid_get_size;
- field->record = vtid_record;
- return 0;
-}
+++ /dev/null
-/*
- * lttng-ust-abi.c
- *
- * Copyright 2010-2011 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST ABI
- *
- * Mimic system calls for:
- * - session creation, returns an object descriptor or failure.
- * - channel creation, returns an object descriptor or failure.
- * - Operates on a session object descriptor
- * - Takes all channel options as parameters.
- * - stream get, returns an object descriptor or failure.
- * - Operates on a channel object descriptor.
- * - stream notifier get, returns an object descriptor or failure.
- * - Operates on a channel object descriptor.
- * - event creation, returns an object descriptor or failure.
- * - Operates on a channel object descriptor
- * - Takes an event name as parameter
- * - Takes an instrumentation source as parameter
- * - e.g. tracepoints, dynamic_probes...
- * - Takes instrumentation source specific arguments.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-#include <lttng/ust-abi.h>
-#include <urcu/compiler.h>
-#include <urcu/list.h>
-#include <lttng/ust-events.h>
-#include <lttng/usterr-signal-safe.h>
-#include "lttng/core.h"
-#include "ltt-tracer.h"
-
-/*
- * Object descriptor table. Should be protected from concurrent access
- * by the caller.
- */
-
-struct lttng_ust_obj {
- union {
- struct {
- void *private_data;
- const struct lttng_ust_objd_ops *ops;
- int f_count;
- } s;
- int freelist_next; /* offset freelist. end is -1. */
- } u;
-};
-
-struct lttng_ust_objd_table {
- struct lttng_ust_obj *array;
- unsigned int len, allocated_len;
- int freelist_head; /* offset freelist head. end is -1 */
-};
-
-static struct lttng_ust_objd_table objd_table = {
- .freelist_head = -1,
-};
-
-static
-int objd_alloc(void *private_data, const struct lttng_ust_objd_ops *ops)
-{
- struct lttng_ust_obj *obj;
-
- if (objd_table.freelist_head != -1) {
- obj = &objd_table.array[objd_table.freelist_head];
- objd_table.freelist_head = obj->u.freelist_next;
- goto end;
- }
-
- if (objd_table.len >= objd_table.allocated_len) {
- unsigned int new_allocated_len, old_allocated_len;
- struct lttng_ust_obj *new_table, *old_table;
-
- old_allocated_len = objd_table.allocated_len;
- old_table = objd_table.array;
- if (!old_allocated_len)
- new_allocated_len = 1;
- else
- new_allocated_len = old_allocated_len << 1;
- new_table = zmalloc(sizeof(struct lttng_ust_obj) * new_allocated_len);
- if (!new_table)
- return -ENOMEM;
- memcpy(new_table, old_table,
- sizeof(struct lttng_ust_obj) * old_allocated_len);
- free(old_table);
- objd_table.array = new_table;
- objd_table.allocated_len = new_allocated_len;
- }
- obj = &objd_table.array[objd_table.len];
- objd_table.len++;
-end:
- obj->u.s.private_data = private_data;
- obj->u.s.ops = ops;
- obj->u.s.f_count = 2; /* count == 1 : object is allocated */
- /* count == 2 : allocated + hold ref */
- return obj - objd_table.array;
-}
-
-static
-struct lttng_ust_obj *_objd_get(int id)
-{
- if (id >= objd_table.len)
- return NULL;
- if (!objd_table.array[id].u.s.f_count)
- return NULL;
- return &objd_table.array[id];
-}
-
-static
-void *objd_private(int id)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
- assert(obj);
- return obj->u.s.private_data;
-}
-
-static
-void objd_set_private(int id, void *private_data)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
- assert(obj);
- obj->u.s.private_data = private_data;
-}
-
-const struct lttng_ust_objd_ops *objd_ops(int id)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
-
- if (!obj)
- return NULL;
- return obj->u.s.ops;
-}
-
-static
-void objd_free(int id)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
-
- assert(obj);
- obj->u.freelist_next = objd_table.freelist_head;
- objd_table.freelist_head = obj - objd_table.array;
- assert(obj->u.s.f_count == 1);
- obj->u.s.f_count = 0; /* deallocated */
-}
-
-static
-void objd_ref(int id)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
- obj->u.s.f_count++;
-}
-
-int lttng_ust_objd_unref(int id)
-{
- struct lttng_ust_obj *obj = _objd_get(id);
-
- if (!obj)
- return -EINVAL;
- if (obj->u.s.f_count == 1) {
- ERR("Reference counting error\n");
- return -EINVAL;
- }
- if ((--obj->u.s.f_count) == 1) {
- const struct lttng_ust_objd_ops *ops = objd_ops(id);
-
- if (ops->release)
- ops->release(id);
- objd_free(id);
- }
- return 0;
-}
-
-static
-void objd_table_destroy(void)
-{
- int i;
-
- for (i = 0; i < objd_table.allocated_len; i++)
- (void) lttng_ust_objd_unref(i);
- free(objd_table.array);
- objd_table.array = NULL;
- objd_table.len = 0;
- objd_table.allocated_len = 0;
- objd_table.freelist_head = -1;
-}
-
-/*
- * This is LTTng's own personal way to create an ABI for sessiond.
- * We send commands over a socket.
- */
-
-static const struct lttng_ust_objd_ops lttng_ops;
-static const struct lttng_ust_objd_ops lttng_session_ops;
-static const struct lttng_ust_objd_ops lttng_channel_ops;
-static const struct lttng_ust_objd_ops lttng_metadata_ops;
-static const struct lttng_ust_objd_ops lttng_event_ops;
-static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops;
-
-enum channel_type {
- PER_CPU_CHANNEL,
- METADATA_CHANNEL,
-};
-
-int lttng_abi_create_root_handle(void)
-{
- int root_handle;
-
- root_handle = objd_alloc(NULL, <tng_ops);
- return root_handle;
-}
-
-static
-int lttng_abi_create_session(void)
-{
- struct ltt_session *session;
- int session_objd, ret;
-
- session = ltt_session_create();
- if (!session)
- return -ENOMEM;
- session_objd = objd_alloc(session, <tng_session_ops);
- if (session_objd < 0) {
- ret = session_objd;
- goto objd_error;
- }
- session->objd = session_objd;
- return session_objd;
-
-objd_error:
- ltt_session_destroy(session);
- return ret;
-}
-
-#if 0
-static
-int lttng_abi_tracepoint_list(void)
-{
- int list_objd, ret;
-
- /* TODO: Create list private data */
- list_objd = objd_alloc(NULL, <tng_tracepoint_list_ops);
- if (list_objd < 0) {
- ret = list_objd;
- goto objd_error;
- }
-
- return list_objd;
-
-objd_error:
- return ret;
-}
-#endif //0
-
-static
-long lttng_abi_tracer_version(int objd,
- struct lttng_ust_tracer_version *v)
-{
- v->version = LTTNG_UST_VERSION;
- v->patchlevel = LTTNG_UST_PATCHLEVEL;
- v->sublevel = LTTNG_UST_SUBLEVEL;
- return 0;
-}
-
-static
-long lttng_abi_add_context(int objd,
- struct lttng_ust_context *context_param,
- struct lttng_ctx **ctx, struct ltt_session *session)
-{
- if (session->been_active)
- return -EPERM;
-
- switch (context_param->ctx) {
- case LTTNG_UST_CONTEXT_PTHREAD_ID:
- return lttng_add_pthread_id_to_ctx(ctx);
- case LTTNG_UST_CONTEXT_VTID:
- return lttng_add_vtid_to_ctx(ctx);
- case LTTNG_UST_CONTEXT_VPID:
- return lttng_add_vpid_to_ctx(ctx);
- case LTTNG_UST_CONTEXT_PROCNAME:
- return lttng_add_procname_to_ctx(ctx);
- default:
- return -EINVAL;
- }
-}
-
-/**
- * lttng_cmd - lttng control through socket commands
- *
- * @objd: the object descriptor
- * @cmd: the command
- * @arg: command arg
- *
- * This descriptor implements lttng commands:
- * LTTNG_UST_SESSION
- * Returns a LTTng trace session object descriptor
- * LTTNG_UST_TRACER_VERSION
- * Returns the LTTng kernel tracer version
- * LTTNG_UST_TRACEPOINT_LIST
- * Returns a file descriptor listing available tracepoints
- * LTTNG_UST_WAIT_QUIESCENT
- * Returns after all previously running probes have completed
- *
- * The returned session will be deleted when its file descriptor is closed.
- */
-static
-long lttng_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case LTTNG_UST_SESSION:
- return lttng_abi_create_session();
- case LTTNG_UST_TRACER_VERSION:
- return lttng_abi_tracer_version(objd,
- (struct lttng_ust_tracer_version *) arg);
- case LTTNG_UST_TRACEPOINT_LIST:
- return -ENOSYS; //TODO
- //return lttng_abi_tracepoint_list();
- case LTTNG_UST_WAIT_QUIESCENT:
- synchronize_trace();
- return 0;
- default:
- return -EINVAL;
- }
-}
-
-static const struct lttng_ust_objd_ops lttng_ops = {
- .cmd = lttng_cmd,
-};
-
-/*
- * We tolerate no failure in this function (if one happens, we print a dmesg
- * error, but cannot return any error, because the channel information is
- * invariant.
- */
-static
-void lttng_metadata_create_events(int channel_objd)
-{
- struct ltt_channel *channel = objd_private(channel_objd);
- static struct lttng_ust_event metadata_params = {
- .instrumentation = LTTNG_UST_TRACEPOINT,
- .name = "lttng_metadata",
- };
- struct ltt_event *event;
- int ret;
-
- /*
- * We tolerate no failure path after event creation. It will stay
- * invariant for the rest of the session.
- */
- event = ltt_event_create(channel, &metadata_params, NULL);
- if (!event) {
- ret = -EINVAL;
- goto create_error;
- }
- return;
-
-create_error:
- WARN_ON(1);
- return; /* not allowed to return error */
-}
-
-int lttng_abi_create_channel(int session_objd,
- struct lttng_ust_channel *chan_param,
- enum channel_type channel_type)
-{
- struct ltt_session *session = objd_private(session_objd);
- const struct lttng_ust_objd_ops *ops;
- const char *transport_name;
- struct ltt_channel *chan;
- int chan_objd;
- int ret = 0;
-
- chan_objd = objd_alloc(NULL, <tng_channel_ops);
- if (chan_objd < 0) {
- ret = chan_objd;
- goto objd_error;
- }
- switch (channel_type) {
- case PER_CPU_CHANNEL:
- if (chan_param->output == LTTNG_UST_MMAP) {
- transport_name = chan_param->overwrite ?
- "relay-overwrite-mmap" : "relay-discard-mmap";
- } else {
- return -EINVAL;
- }
- ops = <tng_channel_ops;
- break;
- case METADATA_CHANNEL:
- if (chan_param->output == LTTNG_UST_MMAP)
- transport_name = "relay-metadata-mmap";
- else
- return -EINVAL;
- ops = <tng_metadata_ops;
- break;
- default:
- transport_name = "<unknown>";
- break;
- }
- /*
- * We tolerate no failure path after channel creation. It will stay
- * invariant for the rest of the session.
- */
- chan = ltt_channel_create(session, transport_name, NULL,
- chan_param->subbuf_size,
- chan_param->num_subbuf,
- chan_param->switch_timer_interval,
- chan_param->read_timer_interval,
- &chan_param->shm_fd,
- &chan_param->wait_fd,
- &chan_param->memory_map_size);
- if (!chan) {
- ret = -EINVAL;
- goto chan_error;
- }
- objd_set_private(chan_objd, chan);
- chan->objd = chan_objd;
- if (channel_type == METADATA_CHANNEL) {
- session->metadata = chan;
- lttng_metadata_create_events(chan_objd);
- }
-
- /* The channel created holds a reference on the session */
- objd_ref(session_objd);
-
- return chan_objd;
-
-chan_error:
- {
- int err;
-
- err = lttng_ust_objd_unref(chan_objd);
- assert(!err);
- }
-objd_error:
- return ret;
-}
-
-/**
- * lttng_session_cmd - lttng session object command
- *
- * @obj: the object
- * @cmd: the command
- * @arg: command arg
- *
- * This descriptor implements lttng commands:
- * LTTNG_UST_CHANNEL
- * Returns a LTTng channel object descriptor
- * LTTNG_UST_ENABLE
- * Enables tracing for a session (weak enable)
- * LTTNG_UST_DISABLE
- * Disables tracing for a session (strong disable)
- * LTTNG_UST_METADATA
- * Returns a LTTng metadata object descriptor
- *
- * The returned channel will be deleted when its file descriptor is closed.
- */
-static
-long lttng_session_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- struct ltt_session *session = objd_private(objd);
-
- switch (cmd) {
- case LTTNG_UST_CHANNEL:
- return lttng_abi_create_channel(objd,
- (struct lttng_ust_channel *) arg,
- PER_CPU_CHANNEL);
- case LTTNG_UST_SESSION_START:
- case LTTNG_UST_ENABLE:
- return ltt_session_enable(session);
- case LTTNG_UST_SESSION_STOP:
- case LTTNG_UST_DISABLE:
- return ltt_session_disable(session);
- case LTTNG_UST_METADATA:
- return lttng_abi_create_channel(objd,
- (struct lttng_ust_channel *) arg,
- METADATA_CHANNEL);
- default:
- return -EINVAL;
- }
-}
-
-/*
- * Called when the last file reference is dropped.
- *
- * Big fat note: channels and events are invariant for the whole session after
- * their creation. So this session destruction also destroys all channel and
- * event structures specific to this session (they are not destroyed when their
- * individual file is released).
- */
-static
-int lttng_release_session(int objd)
-{
- struct ltt_session *session = objd_private(objd);
-
- if (session) {
- ltt_session_destroy(session);
- return 0;
- } else {
- return -EINVAL;
- }
-}
-
-static const struct lttng_ust_objd_ops lttng_session_ops = {
- .release = lttng_release_session,
- .cmd = lttng_session_cmd,
-};
-
-struct stream_priv_data {
- struct lttng_ust_lib_ring_buffer *buf;
- struct ltt_channel *ltt_chan;
-};
-
-static
-int lttng_abi_open_stream(int channel_objd, struct lttng_ust_stream *info)
-{
- struct ltt_channel *channel = objd_private(channel_objd);
- struct lttng_ust_lib_ring_buffer *buf;
- struct stream_priv_data *priv;
- int stream_objd, ret;
-
- buf = channel->ops->buffer_read_open(channel->chan, channel->handle,
- &info->shm_fd, &info->wait_fd, &info->memory_map_size);
- if (!buf)
- return -ENOENT;
-
- priv = zmalloc(sizeof(*priv));
- if (!priv) {
- ret = -ENOMEM;
- goto alloc_error;
- }
- priv->buf = buf;
- priv->ltt_chan = channel;
- stream_objd = objd_alloc(priv, &lib_ring_buffer_objd_ops);
- if (stream_objd < 0) {
- ret = stream_objd;
- goto objd_error;
- }
- /* Hold a reference on the channel object descriptor */
- objd_ref(channel_objd);
- return stream_objd;
-
-objd_error:
- free(priv);
-alloc_error:
- channel->ops->buffer_read_close(buf, channel->handle);
- return ret;
-}
-
-static
-int lttng_abi_create_event(int channel_objd,
- struct lttng_ust_event *event_param)
-{
- struct ltt_channel *channel = objd_private(channel_objd);
- struct ltt_event *event;
- int event_objd, ret;
-
- event_param->name[LTTNG_UST_SYM_NAME_LEN - 1] = '\0';
- event_objd = objd_alloc(NULL, <tng_event_ops);
- if (event_objd < 0) {
- ret = event_objd;
- goto objd_error;
- }
- /*
- * We tolerate no failure path after event creation. It will stay
- * invariant for the rest of the session.
- */
- event = ltt_event_create(channel, event_param, NULL);
- if (!event) {
- ret = -EINVAL;
- goto event_error;
- }
- objd_set_private(event_objd, event);
- /* The event holds a reference on the channel */
- objd_ref(channel_objd);
- return event_objd;
-
-event_error:
- {
- int err;
-
- err = lttng_ust_objd_unref(event_objd);
- assert(!err);
- }
-objd_error:
- return ret;
-}
-
-/**
- * lttng_channel_cmd - lttng control through object descriptors
- *
- * @objd: the object descriptor
- * @cmd: the command
- * @arg: command arg
- *
- * This object descriptor implements lttng commands:
- * LTTNG_UST_STREAM
- * Returns an event stream object descriptor or failure.
- * (typically, one event stream records events from one CPU)
- * LTTNG_UST_EVENT
- * Returns an event object descriptor or failure.
- * LTTNG_UST_CONTEXT
- * Prepend a context field to each event in the channel
- * LTTNG_UST_ENABLE
- * Enable recording for events in this channel (weak enable)
- * LTTNG_UST_DISABLE
- * Disable recording for events in this channel (strong disable)
- *
- * Channel and event file descriptors also hold a reference on the session.
- */
-static
-long lttng_channel_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- struct ltt_channel *channel = objd_private(objd);
-
- switch (cmd) {
- case LTTNG_UST_STREAM:
- {
- struct lttng_ust_stream *stream;
-
- stream = (struct lttng_ust_stream *) arg;
- /* stream used as output */
- return lttng_abi_open_stream(objd, stream);
- }
- case LTTNG_UST_EVENT:
- return lttng_abi_create_event(objd, (struct lttng_ust_event *) arg);
- case LTTNG_UST_CONTEXT:
- return lttng_abi_add_context(objd,
- (struct lttng_ust_context *) arg,
- &channel->ctx, channel->session);
- case LTTNG_UST_ENABLE:
- return ltt_channel_enable(channel);
- case LTTNG_UST_DISABLE:
- return ltt_channel_disable(channel);
- case LTTNG_UST_FLUSH_BUFFER:
- return channel->ops->flush_buffer(channel->chan, channel->handle);
- default:
- return -EINVAL;
- }
-}
-
-/**
- * lttng_metadata_cmd - lttng control through object descriptors
- *
- * @objd: the object descriptor
- * @cmd: the command
- * @arg: command arg
- *
- * This object descriptor implements lttng commands:
- * LTTNG_UST_STREAM
- * Returns an event stream file descriptor or failure.
- *
- * Channel and event file descriptors also hold a reference on the session.
- */
-static
-long lttng_metadata_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- struct ltt_channel *channel = objd_private(objd);
-
- switch (cmd) {
- case LTTNG_UST_STREAM:
- {
- struct lttng_ust_stream *stream;
-
- stream = (struct lttng_ust_stream *) arg;
- /* stream used as output */
- return lttng_abi_open_stream(objd, stream);
- }
- case LTTNG_UST_FLUSH_BUFFER:
- return channel->ops->flush_buffer(channel->chan, channel->handle);
- default:
- return -EINVAL;
- }
-}
-
-#if 0
-/**
- * lttng_channel_poll - lttng stream addition/removal monitoring
- *
- * @file: the file
- * @wait: poll table
- */
-unsigned int lttng_channel_poll(struct file *file, poll_table *wait)
-{
- struct ltt_channel *channel = file->private_data;
- unsigned int mask = 0;
-
- if (file->f_mode & FMODE_READ) {
- poll_wait_set_exclusive(wait);
- poll_wait(file, channel->ops->get_hp_wait_queue(channel->chan),
- wait);
-
- if (channel->ops->is_disabled(channel->chan))
- return POLLERR;
- if (channel->ops->is_finalized(channel->chan))
- return POLLHUP;
- if (channel->ops->buffer_has_read_closed_stream(channel->chan))
- return POLLIN | POLLRDNORM;
- return 0;
- }
- return mask;
-
-}
-#endif //0
-
-static
-int lttng_channel_release(int objd)
-{
- struct ltt_channel *channel = objd_private(objd);
-
- if (channel)
- return lttng_ust_objd_unref(channel->session->objd);
- return 0;
-}
-
-static const struct lttng_ust_objd_ops lttng_channel_ops = {
- .release = lttng_channel_release,
- //.poll = lttng_channel_poll,
- .cmd = lttng_channel_cmd,
-};
-
-static const struct lttng_ust_objd_ops lttng_metadata_ops = {
- .release = lttng_channel_release,
- .cmd = lttng_metadata_cmd,
-};
-
-/**
- * lttng_rb_cmd - lttng ring buffer control through object descriptors
- *
- * @objd: the object descriptor
- * @cmd: the command
- * @arg: command arg
- *
- * This object descriptor implements lttng commands:
- * (None for now. Access is done directly though shm.)
- */
-static
-long lttng_rb_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- default:
- return -EINVAL;
- }
-}
-
-static
-int lttng_rb_release(int objd)
-{
- struct stream_priv_data *priv = objd_private(objd);
- struct lttng_ust_lib_ring_buffer *buf;
- struct ltt_channel *channel;
-
- if (priv) {
- buf = priv->buf;
- channel = priv->ltt_chan;
- free(priv);
- channel->ops->buffer_read_close(buf, channel->handle);
-
- return lttng_ust_objd_unref(channel->objd);
- }
- return 0;
-}
-
-static const struct lttng_ust_objd_ops lib_ring_buffer_objd_ops = {
- .release = lttng_rb_release,
- .cmd = lttng_rb_cmd,
-};
-
-/**
- * lttng_event_cmd - lttng control through object descriptors
- *
- * @objd: the object descriptor
- * @cmd: the command
- * @arg: command arg
- *
- * This object descriptor implements lttng commands:
- * LTTNG_UST_CONTEXT
- * Prepend a context field to each record of this event
- * LTTNG_UST_ENABLE
- * Enable recording for this event (weak enable)
- * LTTNG_UST_DISABLE
- * Disable recording for this event (strong disable)
- */
-static
-long lttng_event_cmd(int objd, unsigned int cmd, unsigned long arg)
-{
- struct ltt_event *event = objd_private(objd);
-
- switch (cmd) {
- case LTTNG_UST_CONTEXT:
- return lttng_abi_add_context(objd,
- (struct lttng_ust_context *) arg,
- &event->ctx, event->chan->session);
- case LTTNG_UST_ENABLE:
- return ltt_event_enable(event);
- case LTTNG_UST_DISABLE:
- return ltt_event_disable(event);
- default:
- return -EINVAL;
- }
-}
-
-static
-int lttng_event_release(int objd)
-{
- struct ltt_event *event = objd_private(objd);
-
- if (event)
- return lttng_ust_objd_unref(event->chan->objd);
- return 0;
-}
-
-/* TODO: filter control ioctl */
-static const struct lttng_ust_objd_ops lttng_event_ops = {
- .release = lttng_event_release,
- .cmd = lttng_event_cmd,
-};
-
-void lttng_ust_abi_exit(void)
-{
- objd_table_destroy();
-}
+++ /dev/null
-/*
- * lttng-ust-comm.c
- *
- * Copyright (C) 2011 David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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 _LGPL_SOURCE
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/prctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <pthread.h>
-#include <semaphore.h>
-#include <time.h>
-#include <assert.h>
-#include <signal.h>
-#include <urcu/uatomic.h>
-#include <urcu/futex.h>
-
-#include <lttng/ust-comm.h>
-#include <lttng/ust-events.h>
-#include <lttng/usterr-signal-safe.h>
-#include <lttng/ust-abi.h>
-#include <lttng/tracepoint.h>
-#include <lttng/tracepoint-internal.h>
-#include <lttng/ust.h>
-#include "ltt-tracer-core.h"
-
-/*
- * Has lttng ust comm constructor been called ?
- */
-static int initialized;
-
-/*
- * The ust_lock/ust_unlock lock is used as a communication thread mutex.
- * Held when handling a command, also held by fork() to deal with
- * removal of threads, and by exit path.
- */
-
-/* Should the ust comm thread quit ? */
-static int lttng_ust_comm_should_quit;
-
-/*
- * Wait for either of these before continuing to the main
- * program:
- * - the register_done message from sessiond daemon
- * (will let the sessiond daemon enable sessions before main
- * starts.)
- * - sessiond daemon is not reachable.
- * - timeout (ensuring applications are resilient to session
- * daemon problems).
- */
-static sem_t constructor_wait;
-/*
- * Doing this for both the global and local sessiond.
- */
-static int sem_count = { 2 };
-
-/*
- * Info about socket and associated listener thread.
- */
-struct sock_info {
- const char *name;
- pthread_t ust_listener; /* listener thread */
- int root_handle;
- int constructor_sem_posted;
- int allowed;
- int global;
-
- char sock_path[PATH_MAX];
- int socket;
-
- char wait_shm_path[PATH_MAX];
- char *wait_shm_mmap;
-};
-
-/* Socket from app (connect) to session daemon (listen) for communication */
-struct sock_info global_apps = {
- .name = "global",
- .global = 1,
-
- .root_handle = -1,
- .allowed = 1,
-
- .sock_path = DEFAULT_GLOBAL_APPS_UNIX_SOCK,
- .socket = -1,
-
- .wait_shm_path = DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH,
-};
-
-/* TODO: allow global_apps_sock_path override */
-
-struct sock_info local_apps = {
- .name = "local",
- .global = 0,
- .root_handle = -1,
- .allowed = 0, /* Check setuid bit first */
-
- .socket = -1,
-};
-
-static int wait_poll_fallback;
-
-extern void ltt_ring_buffer_client_overwrite_init(void);
-extern void ltt_ring_buffer_client_discard_init(void);
-extern void ltt_ring_buffer_metadata_client_init(void);
-extern void ltt_ring_buffer_client_overwrite_exit(void);
-extern void ltt_ring_buffer_client_discard_exit(void);
-extern void ltt_ring_buffer_metadata_client_exit(void);
-
-static
-int setup_local_apps(void)
-{
- const char *home_dir;
- uid_t uid;
-
- uid = getuid();
- /*
- * Disallow per-user tracing for setuid binaries.
- */
- if (uid != geteuid()) {
- local_apps.allowed = 0;
- return 0;
- } else {
- local_apps.allowed = 1;
- }
- home_dir = (const char *) getenv("HOME");
- if (!home_dir)
- return -ENOENT;
- snprintf(local_apps.sock_path, PATH_MAX,
- DEFAULT_HOME_APPS_UNIX_SOCK, home_dir);
- snprintf(local_apps.wait_shm_path, PATH_MAX,
- DEFAULT_HOME_APPS_WAIT_SHM_PATH, uid);
- return 0;
-}
-
-static
-int register_app_to_sessiond(int socket)
-{
- ssize_t ret;
- int prctl_ret;
- struct {
- uint32_t major;
- uint32_t minor;
- pid_t pid;
- pid_t ppid;
- uid_t uid;
- gid_t gid;
- char name[16]; /* process name */
- } reg_msg;
-
- reg_msg.major = LTTNG_UST_COMM_VERSION_MAJOR;
- reg_msg.minor = LTTNG_UST_COMM_VERSION_MINOR;
- reg_msg.pid = getpid();
- reg_msg.ppid = getppid();
- reg_msg.uid = getuid();
- reg_msg.gid = getgid();
- prctl_ret = prctl(PR_GET_NAME, (unsigned long) reg_msg.name, 0, 0, 0);
- if (prctl_ret) {
- ERR("Error executing prctl");
- return -errno;
- }
-
- ret = ustcomm_send_unix_sock(socket, ®_msg, sizeof(reg_msg));
- if (ret >= 0 && ret != sizeof(reg_msg))
- return -EIO;
- return ret;
-}
-
-static
-int send_reply(int sock, struct ustcomm_ust_reply *lur)
-{
- ssize_t len;
-
- len = ustcomm_send_unix_sock(sock, lur, sizeof(*lur));
- switch (len) {
- case sizeof(*lur):
- DBG("message successfully sent");
- return 0;
- case -1:
- if (errno == ECONNRESET) {
- printf("remote end closed connection\n");
- return 0;
- }
- return -1;
- default:
- printf("incorrect message size: %zd\n", len);
- return -1;
- }
-}
-
-static
-int handle_register_done(struct sock_info *sock_info)
-{
- int ret;
-
- if (sock_info->constructor_sem_posted)
- return 0;
- sock_info->constructor_sem_posted = 1;
- if (uatomic_read(&sem_count) <= 0) {
- return 0;
- }
- ret = uatomic_add_return(&sem_count, -1);
- if (ret == 0) {
- ret = sem_post(&constructor_wait);
- assert(!ret);
- }
- return 0;
-}
-
-static
-int handle_message(struct sock_info *sock_info,
- int sock, struct ustcomm_ust_msg *lum)
-{
- int ret = 0;
- const struct lttng_ust_objd_ops *ops;
- struct ustcomm_ust_reply lur;
- int shm_fd, wait_fd;
-
- ust_lock();
-
- memset(&lur, 0, sizeof(lur));
-
- if (lttng_ust_comm_should_quit) {
- ret = -EPERM;
- goto end;
- }
-
- ops = objd_ops(lum->handle);
- if (!ops) {
- ret = -ENOENT;
- goto end;
- }
-
- switch (lum->cmd) {
- case LTTNG_UST_REGISTER_DONE:
- if (lum->handle == LTTNG_UST_ROOT_HANDLE)
- ret = handle_register_done(sock_info);
- else
- ret = -EINVAL;
- break;
- case LTTNG_UST_RELEASE:
- if (lum->handle == LTTNG_UST_ROOT_HANDLE)
- ret = -EPERM;
- else
- ret = lttng_ust_objd_unref(lum->handle);
- break;
- default:
- if (ops->cmd)
- ret = ops->cmd(lum->handle, lum->cmd,
- (unsigned long) &lum->u);
- else
- ret = -ENOSYS;
- break;
- }
-
-end:
- lur.handle = lum->handle;
- lur.cmd = lum->cmd;
- lur.ret_val = ret;
- if (ret >= 0) {
- lur.ret_code = USTCOMM_OK;
- } else {
- //lur.ret_code = USTCOMM_SESSION_FAIL;
- lur.ret_code = ret;
- }
- switch (lum->cmd) {
- case LTTNG_UST_STREAM:
- /*
- * Special-case reply to send stream info.
- * Use lum.u output.
- */
- lur.u.stream.memory_map_size = lum->u.stream.memory_map_size;
- shm_fd = lum->u.stream.shm_fd;
- wait_fd = lum->u.stream.wait_fd;
- break;
- case LTTNG_UST_METADATA:
- case LTTNG_UST_CHANNEL:
- lur.u.channel.memory_map_size = lum->u.channel.memory_map_size;
- shm_fd = lum->u.channel.shm_fd;
- wait_fd = lum->u.channel.wait_fd;
- break;
- case LTTNG_UST_VERSION:
- lur.u.version = lum->u.version;
- break;
- }
- ret = send_reply(sock, &lur);
- if (ret < 0) {
- perror("error sending reply");
- goto error;
- }
-
- if ((lum->cmd == LTTNG_UST_STREAM
- || lum->cmd == LTTNG_UST_CHANNEL
- || lum->cmd == LTTNG_UST_METADATA)
- && lur.ret_code == USTCOMM_OK) {
- /* we also need to send the file descriptors. */
- ret = ustcomm_send_fds_unix_sock(sock,
- &shm_fd, &shm_fd,
- 1, sizeof(int));
- if (ret < 0) {
- perror("send shm_fd");
- goto error;
- }
- ret = ustcomm_send_fds_unix_sock(sock,
- &wait_fd, &wait_fd,
- 1, sizeof(int));
- if (ret < 0) {
- perror("send wait_fd");
- goto error;
- }
- }
-error:
- ust_unlock();
- return ret;
-}
-
-static
-void cleanup_sock_info(struct sock_info *sock_info)
-{
- int ret;
-
- if (sock_info->socket != -1) {
- ret = close(sock_info->socket);
- if (ret) {
- ERR("Error closing apps socket");
- }
- sock_info->socket = -1;
- }
- if (sock_info->root_handle != -1) {
- ret = lttng_ust_objd_unref(sock_info->root_handle);
- if (ret) {
- ERR("Error unref root handle");
- }
- sock_info->root_handle = -1;
- }
- sock_info->constructor_sem_posted = 0;
- if (sock_info->wait_shm_mmap) {
- ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE));
- if (ret) {
- ERR("Error unmapping wait shm");
- }
- sock_info->wait_shm_mmap = NULL;
- }
-}
-
-/*
- * 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(struct sock_info *sock_info, size_t mmap_size)
-{
- int wait_shm_fd, ret;
- pid_t pid;
-
- /*
- * Try to open read-only.
- */
- wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
- if (wait_shm_fd >= 0) {
- goto end;
- } else if (wait_shm_fd < 0 && errno != ENOENT) {
- /*
- * Real-only open did not work, and it's not because the
- * entry was not present. It's a failure that prohibits
- * using shm.
- */
- ERR("Error opening shm %s", sock_info->wait_shm_path);
- goto end;
- }
- /*
- * If the open failed because the file did not exist, try
- * creating it ourself.
- */
- pid = fork();
- if (pid > 0) {
- int status;
-
- /*
- * Parent: wait for child to return, in which case the
- * shared memory map will have been created.
- */
- pid = wait(&status);
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- wait_shm_fd = -1;
- goto end;
- }
- /*
- * Try to open read-only again after creation.
- */
- wait_shm_fd = shm_open(sock_info->wait_shm_path, O_RDONLY, 0);
- if (wait_shm_fd < 0) {
- /*
- * Real-only open did not work. It's a failure
- * that prohibits using shm.
- */
- ERR("Error opening shm %s", sock_info->wait_shm_path);
- goto end;
- }
- goto end;
- } else if (pid == 0) {
- int create_mode;
-
- /* Child */
- create_mode = S_IRUSR | S_IWUSR | S_IRGRP;
- if (sock_info->global)
- create_mode |= S_IROTH | S_IWGRP | S_IWOTH;
- /*
- * We're alone in a child process, so we can modify the
- * process-wide umask.
- */
- umask(~create_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(sock_info->wait_shm_path,
- O_RDWR | O_CREAT, create_mode);
- if (wait_shm_fd >= 0) {
- ret = ftruncate(wait_shm_fd, mmap_size);
- if (ret) {
- PERROR("ftruncate");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
- /*
- * For local shm, we need to have rw access to accept
- * opening it: this means the local sessiond will be
- * able to wake us up. For global shm, we open it even
- * if rw access is not granted, because the root.root
- * sessiond will be able to override all rights and wake
- * us up.
- */
- if (!sock_info->global && errno != EACCES) {
- ERR("Error opening shm %s", sock_info->wait_shm_path);
- exit(EXIT_FAILURE);
- }
- /*
- * The shm exists, but we cannot open it RW. Report
- * success.
- */
- exit(EXIT_SUCCESS);
- } else {
- return -1;
- }
-end:
- if (wait_shm_fd >= 0 && !sock_info->global) {
- struct stat statbuf;
-
- /*
- * Ensure that our user is the owner of the shm file for
- * local shm. If we do not own the file, it means our
- * sessiond will not have access to wake us up (there is
- * probably a rogue process trying to fake our
- * sessiond). Fallback to polling method in this case.
- */
- ret = fstat(wait_shm_fd, &statbuf);
- if (ret) {
- PERROR("fstat");
- goto error_close;
- }
- if (statbuf.st_uid != getuid())
- goto error_close;
- }
- return wait_shm_fd;
-
-error_close:
- ret = close(wait_shm_fd);
- if (ret) {
- PERROR("Error closing fd");
- }
- return -1;
-}
-
-static
-char *get_map_shm(struct sock_info *sock_info)
-{
- size_t mmap_size = sysconf(_SC_PAGE_SIZE);
- int wait_shm_fd, ret;
- char *wait_shm_mmap;
-
- wait_shm_fd = get_wait_shm(sock_info, mmap_size);
- if (wait_shm_fd < 0) {
- goto error;
- }
- wait_shm_mmap = mmap(NULL, mmap_size, 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 sessiond). Fallback to poll mode.");
- goto error;
- }
- return wait_shm_mmap;
-
-error:
- return NULL;
-}
-
-static
-void wait_for_sessiond(struct sock_info *sock_info)
-{
- int ret;
-
- ust_lock();
- if (lttng_ust_comm_should_quit) {
- goto quit;
- }
- if (wait_poll_fallback) {
- goto error;
- }
- if (!sock_info->wait_shm_mmap) {
- sock_info->wait_shm_mmap = get_map_shm(sock_info);
- if (!sock_info->wait_shm_mmap)
- goto error;
- }
- ust_unlock();
-
- DBG("Waiting for %s apps sessiond", sock_info->name);
- /* Wait for futex wakeup */
- if (uatomic_read((int32_t *) sock_info->wait_shm_mmap) == 0) {
- ret = futex_async((int32_t *) sock_info->wait_shm_mmap,
- FUTEX_WAIT, 0, NULL, NULL, 0);
- if (ret < 0) {
- if (errno == EFAULT) {
- wait_poll_fallback = 1;
- WARN(
-"Linux kernels 2.6.33 to 3.0 (with the exception of stable versions) "
-"do not support FUTEX_WAKE on read-only memory mappings correctly. "
-"Please upgrade your kernel "
-"(fix is commit 9ea71503a8ed9184d2d0b8ccc4d269d05f7940ae in Linux kernel "
-"mainline). LTTng-UST will use polling mode fallback.");
- }
- PERROR("futex");
- }
- }
- return;
-
-quit:
- ust_unlock();
- return;
-
-error:
- ust_unlock();
- return;
-}
-
-/*
- * This thread does not allocate any resource, except within
- * handle_message, within mutex protection. This mutex protects against
- * fork and exit.
- * The other moment it allocates resources is at socket connexion, which
- * is also protected by the mutex.
- */
-static
-void *ust_listener_thread(void *arg)
-{
- struct sock_info *sock_info = arg;
- int sock, ret, prev_connect_failed = 0, has_waited = 0;
-
- /* Restart trying to connect to the session daemon */
-restart:
- if (prev_connect_failed) {
- /* Wait for sessiond availability with pipe */
- wait_for_sessiond(sock_info);
- if (has_waited) {
- has_waited = 0;
- /*
- * Sleep for 5 seconds before retrying after a
- * sequence of failure / wait / failure. This
- * deals with a killed or broken session daemon.
- */
- sleep(5);
- }
- has_waited = 1;
- prev_connect_failed = 0;
- }
- ust_lock();
-
- if (lttng_ust_comm_should_quit) {
- ust_unlock();
- goto quit;
- }
-
- if (sock_info->socket != -1) {
- ret = close(sock_info->socket);
- if (ret) {
- ERR("Error closing %s apps socket", sock_info->name);
- }
- sock_info->socket = -1;
- }
-
- /* Register */
- ret = ustcomm_connect_unix_sock(sock_info->sock_path);
- if (ret < 0) {
- ERR("Error connecting to %s apps socket", sock_info->name);
- prev_connect_failed = 1;
- /*
- * If we cannot find the sessiond daemon, don't delay
- * constructor execution.
- */
- ret = handle_register_done(sock_info);
- assert(!ret);
- ust_unlock();
- goto restart;
- }
-
- sock_info->socket = sock = ret;
-
- /*
- * Create only one root handle per listener thread for the whole
- * process lifetime.
- */
- if (sock_info->root_handle == -1) {
- ret = lttng_abi_create_root_handle();
- if (ret < 0) {
- ERR("Error creating root handle");
- ust_unlock();
- goto quit;
- }
- sock_info->root_handle = ret;
- }
-
- ret = register_app_to_sessiond(sock);
- if (ret < 0) {
- ERR("Error registering to %s apps socket", sock_info->name);
- prev_connect_failed = 1;
- /*
- * If we cannot register to the sessiond daemon, don't
- * delay constructor execution.
- */
- ret = handle_register_done(sock_info);
- assert(!ret);
- ust_unlock();
- goto restart;
- }
- ust_unlock();
-
- for (;;) {
- ssize_t len;
- struct ustcomm_ust_msg lum;
-
- len = ustcomm_recv_unix_sock(sock, &lum, sizeof(lum));
- switch (len) {
- case 0: /* orderly shutdown */
- DBG("%s ltt-sessiond has performed an orderly shutdown\n", sock_info->name);
- goto end;
- case sizeof(lum):
- DBG("message received\n");
- ret = handle_message(sock_info, sock, &lum);
- if (ret < 0) {
- ERR("Error handling message for %s socket", sock_info->name);
- }
- continue;
- case -1:
- if (errno == ECONNRESET) {
- ERR("%s remote end closed connection\n", sock_info->name);
- goto end;
- }
- goto end;
- default:
- ERR("incorrect message size (%s socket): %zd\n", sock_info->name, len);
- continue;
- }
-
- }
-end:
- goto restart; /* try to reconnect */
-quit:
- return NULL;
-}
-
-/*
- * Return values: -1: don't wait. 0: wait forever. 1: timeout wait.
- */
-static
-int get_timeout(struct timespec *constructor_timeout)
-{
- long constructor_delay_ms = LTTNG_UST_DEFAULT_CONSTRUCTOR_TIMEOUT_MS;
- char *str_delay;
- int ret;
-
- str_delay = getenv("UST_REGISTER_TIMEOUT");
- if (str_delay) {
- constructor_delay_ms = strtol(str_delay, NULL, 10);
- }
-
- switch (constructor_delay_ms) {
- case -1:/* fall-through */
- case 0:
- return constructor_delay_ms;
- default:
- break;
- }
-
- /*
- * If we are unable to find the current time, don't wait.
- */
- ret = clock_gettime(CLOCK_REALTIME, constructor_timeout);
- if (ret) {
- return -1;
- }
- constructor_timeout->tv_sec += constructor_delay_ms / 1000UL;
- constructor_timeout->tv_nsec +=
- (constructor_delay_ms % 1000UL) * 1000000UL;
- if (constructor_timeout->tv_nsec >= 1000000000UL) {
- constructor_timeout->tv_sec++;
- constructor_timeout->tv_nsec -= 1000000000UL;
- }
- return 1;
-}
-
-/*
- * sessiond monitoring thread: monitor presence of global and per-user
- * sessiond by polling the application common named pipe.
- */
-/* TODO */
-
-void __attribute__((constructor)) lttng_ust_init(void)
-{
- struct timespec constructor_timeout;
- int timeout_mode;
- int ret;
-
- if (uatomic_xchg(&initialized, 1) == 1)
- return;
-
- /*
- * We want precise control over the order in which we construct
- * our sub-libraries vs starting to receive commands from
- * sessiond (otherwise leading to errors when trying to create
- * sessiond before the init functions are completed).
- */
- init_usterr();
- init_tracepoint();
- ltt_ring_buffer_metadata_client_init();
- ltt_ring_buffer_client_overwrite_init();
- ltt_ring_buffer_client_discard_init();
-
- timeout_mode = get_timeout(&constructor_timeout);
-
- ret = sem_init(&constructor_wait, 0, 0);
- assert(!ret);
-
- ret = setup_local_apps();
- if (ret) {
- ERR("Error setting up to local apps");
- }
- ret = pthread_create(&local_apps.ust_listener, NULL,
- ust_listener_thread, &local_apps);
-
- if (local_apps.allowed) {
- ret = pthread_create(&global_apps.ust_listener, NULL,
- ust_listener_thread, &global_apps);
- } else {
- handle_register_done(&local_apps);
- }
-
- switch (timeout_mode) {
- case 1: /* timeout wait */
- do {
- ret = sem_timedwait(&constructor_wait,
- &constructor_timeout);
- } while (ret < 0 && errno == EINTR);
- if (ret < 0 && errno == ETIMEDOUT) {
- ERR("Timed out waiting for ltt-sessiond");
- } else {
- assert(!ret);
- }
- break;
- case -1:/* wait forever */
- do {
- ret = sem_wait(&constructor_wait);
- } while (ret < 0 && errno == EINTR);
- assert(!ret);
- break;
- case 0: /* no timeout */
- break;
- }
-}
-
-static
-void lttng_ust_cleanup(int exiting)
-{
- cleanup_sock_info(&global_apps);
- if (local_apps.allowed) {
- cleanup_sock_info(&local_apps);
- }
- lttng_ust_abi_exit();
- lttng_ust_events_exit();
- ltt_ring_buffer_client_discard_exit();
- ltt_ring_buffer_client_overwrite_exit();
- ltt_ring_buffer_metadata_client_exit();
- exit_tracepoint();
- if (!exiting) {
- /* Reinitialize values for fork */
- sem_count = 2;
- lttng_ust_comm_should_quit = 0;
- initialized = 0;
- }
-}
-
-void __attribute__((destructor)) lttng_ust_exit(void)
-{
- int ret;
-
- /*
- * Using pthread_cancel here because:
- * A) we don't want to hang application teardown.
- * B) the thread is not allocating any resource.
- */
-
- /*
- * Require the communication thread to quit. Synchronize with
- * mutexes to ensure it is not in a mutex critical section when
- * pthread_cancel is later called.
- */
- ust_lock();
- lttng_ust_comm_should_quit = 1;
- ust_unlock();
-
- ret = pthread_cancel(global_apps.ust_listener);
- if (ret) {
- ERR("Error cancelling global ust listener thread");
- }
- if (local_apps.allowed) {
- ret = pthread_cancel(local_apps.ust_listener);
- if (ret) {
- ERR("Error cancelling local ust listener thread");
- }
- }
- lttng_ust_cleanup(1);
-}
-
-/*
- * We exclude the worker threads across fork and clone (except
- * CLONE_VM), because these system calls only keep the forking thread
- * running in the child. Therefore, we don't want to call fork or clone
- * in the middle of an tracepoint or ust tracing state modification.
- * Holding this mutex protects these structures across fork and clone.
- */
-void ust_before_fork(ust_fork_info_t *fork_info)
-{
- /*
- * Disable signals. This is to avoid that the child intervenes
- * before it is properly setup for tracing. It is safer to
- * disable all signals, because then we know we are not breaking
- * anything by restoring the original mask.
- */
- sigset_t all_sigs;
- int ret;
-
- /* Disable signals */
- sigfillset(&all_sigs);
- ret = sigprocmask(SIG_BLOCK, &all_sigs, &fork_info->orig_sigs);
- if (ret == -1) {
- PERROR("sigprocmask");
- }
- ust_lock();
- rcu_bp_before_fork();
-}
-
-static void ust_after_fork_common(ust_fork_info_t *fork_info)
-{
- int ret;
-
- DBG("process %d", getpid());
- ust_unlock();
- /* Restore signals */
- ret = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL);
- if (ret == -1) {
- PERROR("sigprocmask");
- }
-}
-
-void ust_after_fork_parent(ust_fork_info_t *fork_info)
-{
- DBG("process %d", getpid());
- rcu_bp_after_fork_parent();
- /* Release mutexes and reenable signals */
- ust_after_fork_common(fork_info);
-}
-
-/*
- * After fork, in the child, we need to cleanup all the leftover state,
- * except the worker thread which already magically disappeared thanks
- * to the weird Linux fork semantics. After tyding up, we call
- * lttng_ust_init() again to start over as a new PID.
- *
- * This is meant for forks() that have tracing in the child between the
- * fork and following exec call (if there is any).
- */
-void ust_after_fork_child(ust_fork_info_t *fork_info)
-{
- DBG("process %d", getpid());
- /* Release urcu mutexes */
- rcu_bp_after_fork_child();
- lttng_ust_cleanup(0);
- lttng_context_vtid_reset();
- /* Release mutexes and reenable signals */
- ust_after_fork_common(fork_info);
- lttng_ust_init();
-}
+++ /dev/null
-/*
- * probes/lttng-probe-ust.c
- *
- * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * LTTng UST core probes.
- *
- * Dual LGPL v2.1/GPL v2 license.
- */
-
-/*
- * Create LTTng tracepoint probes.
- */
-#define TRACEPOINT_CREATE_PROBES
-
-#include "lttng-probe-ust.h"
+++ /dev/null
-#undef TRACEPOINT_SYSTEM
-#define TRACEPOINT_SYSTEM lttng_ust
-
-#if !defined(_TRACEPOINT_LTTNG_UST_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
-#define _TRACEPOINT_LTTNG_UST_H
-
-/*
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1 of
- * the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <lttng/tracepoint.h>
-
-TRACEPOINT_EVENT(lttng_metadata,
-
- TP_PROTO(const char *str),
-
- TP_ARGS(str),
-
- /*
- * Not exactly a string: more a sequence of bytes (dynamic
- * array) without the length. This is a dummy anyway: we only
- * use this declaration to generate an event metadata entry.
- */
- TP_FIELDS(
- ctf_string(str, str)
- )
-)
-
-#undef TRACEPOINT_INCLUDE_PATH
-#define TRACEPOINT_INCLUDE_PATH ./probes
-#undef TRACEPOINT_INCLUDE_FILE
-#define TRACEPOINT_INCLUDE_FILE lttng-probe-ust
-
-#endif /* _TRACEPOINT_LTTNG_UST_H */
-
-/* This part must be outside protection */
-#include <lttng/tracepoint-event.h>
+++ /dev/null
-/*
- * Copyright (C) 2008-2011 Mathieu Desnoyers
- * Copyright (C) 2009 Pierre-Marc Fournier
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Ported to userspace by Pierre-Marc Fournier.
- */
-
-#define _LGPL_SOURCE
-#include <errno.h>
-#include <lttng/tracepoint.h>
-#include <lttng/tracepoint-internal.h>
-#include <lttng/core.h>
-#include <lttng/kcompat/kcompat.h>
-#include <urcu-bp.h>
-#include <urcu/hlist.h>
-#include <urcu/uatomic.h>
-
-#include <lttng/usterr-signal-safe.h>
-#include "ltt-tracer-core.h"
-
-/* Set to 1 to enable tracepoint debug output */
-static const int tracepoint_debug;
-static int initialized;
-static void (*new_tracepoint_cb)(struct tracepoint *);
-
-/* libraries that contain tracepoints (struct tracepoint_lib) */
-static CDS_LIST_HEAD(libs);
-
-/*
- * The UST lock protects the library tracepoints, the hash table, and
- * the library list.
- * All calls to the tracepoint API must be protected by the UST lock,
- * excepts calls to tracepoint_register_lib and
- * tracepoint_unregister_lib, which take the UST lock themselves.
- */
-
-/*
- * Tracepoint hash table, containing the active tracepoints.
- * Protected by tracepoints_mutex.
- */
-#define TRACEPOINT_HASH_BITS 6
-#define TRACEPOINT_TABLE_SIZE (1 << TRACEPOINT_HASH_BITS)
-static struct cds_hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE];
-
-static CDS_LIST_HEAD(old_probes);
-static int need_update;
-
-/*
- * Note about RCU :
- * It is used to to delay the free of multiple probes array until a quiescent
- * state is reached.
- * Tracepoint entries modifications are protected by the tracepoints_mutex.
- */
-struct tracepoint_entry {
- struct cds_hlist_node hlist;
- struct tracepoint_probe *probes;
- int refcount; /* Number of times armed. 0 if disarmed. */
- char name[0];
-};
-
-struct tp_probes {
- union {
- struct cds_list_head list;
- } u;
- struct tracepoint_probe probes[0];
-};
-
-static inline void *allocate_probes(int count)
-{
- struct tp_probes *p = zmalloc(count * sizeof(struct tracepoint_probe)
- + sizeof(struct tp_probes));
- return p == NULL ? NULL : p->probes;
-}
-
-static inline void release_probes(void *old)
-{
- if (old) {
- struct tp_probes *tp_probes = _ust_container_of(old,
- struct tp_probes, probes[0]);
- synchronize_rcu();
- free(tp_probes);
- }
-}
-
-static void debug_print_probes(struct tracepoint_entry *entry)
-{
- int i;
-
- if (!tracepoint_debug || !entry->probes)
- return;
-
- for (i = 0; entry->probes[i].func; i++)
- DBG("Probe %d : %p", i, entry->probes[i].func);
-}
-
-static void *
-tracepoint_entry_add_probe(struct tracepoint_entry *entry,
- void *probe, void *data)
-{
- int nr_probes = 0;
- struct tracepoint_probe *old, *new;
-
- WARN_ON(!probe);
-
- debug_print_probes(entry);
- old = entry->probes;
- if (old) {
- /* (N -> N+1), (N != 0, 1) probes */
- for (nr_probes = 0; old[nr_probes].func; nr_probes++)
- if (old[nr_probes].func == probe &&
- old[nr_probes].data == data)
- return ERR_PTR(-EEXIST);
- }
- /* + 2 : one for new probe, one for NULL func */
- new = allocate_probes(nr_probes + 2);
- if (new == NULL)
- return ERR_PTR(-ENOMEM);
- if (old)
- memcpy(new, old, nr_probes * sizeof(struct tracepoint_probe));
- new[nr_probes].func = probe;
- new[nr_probes].data = data;
- new[nr_probes + 1].func = NULL;
- entry->refcount = nr_probes + 1;
- entry->probes = new;
- debug_print_probes(entry);
- return old;
-}
-
-static void *
-tracepoint_entry_remove_probe(struct tracepoint_entry *entry, void *probe,
- void *data)
-{
- int nr_probes = 0, nr_del = 0, i;
- struct tracepoint_probe *old, *new;
-
- old = entry->probes;
-
- if (!old)
- return ERR_PTR(-ENOENT);
-
- debug_print_probes(entry);
- /* (N -> M), (N > 1, M >= 0) probes */
- for (nr_probes = 0; old[nr_probes].func; nr_probes++) {
- if (!probe ||
- (old[nr_probes].func == probe &&
- old[nr_probes].data == data))
- nr_del++;
- }
-
- if (nr_probes - nr_del == 0) {
- /* N -> 0, (N > 1) */
- entry->probes = NULL;
- entry->refcount = 0;
- debug_print_probes(entry);
- return old;
- } else {
- int j = 0;
- /* N -> M, (N > 1, M > 0) */
- /* + 1 for NULL */
- new = allocate_probes(nr_probes - nr_del + 1);
- if (new == NULL)
- return ERR_PTR(-ENOMEM);
- for (i = 0; old[i].func; i++)
- if (probe &&
- (old[i].func != probe || old[i].data != data))
- new[j++] = old[i];
- new[nr_probes - nr_del].func = NULL;
- entry->refcount = nr_probes - nr_del;
- entry->probes = new;
- }
- debug_print_probes(entry);
- return old;
-}
-
-/*
- * Get tracepoint if the tracepoint is present in the tracepoint hash table.
- * Must be called with tracepoints_mutex held.
- * Returns NULL if not present.
- */
-static struct tracepoint_entry *get_tracepoint(const char *name)
-{
- struct cds_hlist_head *head;
- struct cds_hlist_node *node;
- struct tracepoint_entry *e;
- u32 hash = jhash(name, strlen(name), 0);
-
- head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
- cds_hlist_for_each_entry(e, node, head, hlist) {
- if (!strcmp(name, e->name))
- return e;
- }
- return NULL;
-}
-
-/*
- * Add the tracepoint to the tracepoint hash table. Must be called with
- * tracepoints_mutex held.
- */
-static struct tracepoint_entry *add_tracepoint(const char *name)
-{
- struct cds_hlist_head *head;
- struct cds_hlist_node *node;
- struct tracepoint_entry *e;
- size_t name_len = strlen(name) + 1;
- u32 hash = jhash(name, name_len-1, 0);
-
- head = &tracepoint_table[hash & (TRACEPOINT_TABLE_SIZE - 1)];
- cds_hlist_for_each_entry(e, node, head, hlist) {
- if (!strcmp(name, e->name)) {
- DBG("tracepoint %s busy", name);
- return ERR_PTR(-EEXIST); /* Already there */
- }
- }
- /*
- * Using zmalloc here to allocate a variable length element. Could
- * cause some memory fragmentation if overused.
- */
- e = zmalloc(sizeof(struct tracepoint_entry) + name_len);
- if (!e)
- return ERR_PTR(-ENOMEM);
- memcpy(&e->name[0], name, name_len);
- e->probes = NULL;
- e->refcount = 0;
- cds_hlist_add_head(&e->hlist, head);
- return e;
-}
-
-/*
- * Remove the tracepoint from the tracepoint hash table. Must be called with
- * ust_lock held.
- */
-static inline void remove_tracepoint(struct tracepoint_entry *e)
-{
- cds_hlist_del(&e->hlist);
- free(e);
-}
-
-/*
- * Sets the probe callback corresponding to one tracepoint.
- */
-static void set_tracepoint(struct tracepoint_entry **entry,
- struct tracepoint *elem, int active)
-{
- WARN_ON(strcmp((*entry)->name, elem->name) != 0);
-
- /*
- * rcu_assign_pointer has a cmm_smp_wmb() which makes sure that the new
- * probe callbacks array is consistent before setting a pointer to it.
- * This array is referenced by __DO_TRACE from
- * include/linux/tracepoints.h. A matching cmm_smp_read_barrier_depends()
- * is used.
- */
- rcu_assign_pointer(elem->probes, (*entry)->probes);
- elem->state = active;
-}
-
-/*
- * Disable a tracepoint and its probe callback.
- * Note: only waiting an RCU period after setting elem->call to the empty
- * function insures that the original callback is not used anymore. This insured
- * by preempt_disable around the call site.
- */
-static void disable_tracepoint(struct tracepoint *elem)
-{
- elem->state = 0;
- rcu_assign_pointer(elem->probes, NULL);
-}
-
-/**
- * tracepoint_update_probe_range - Update a probe range
- * @begin: beginning of the range
- * @end: end of the range
- *
- * Updates the probe callback corresponding to a range of tracepoints.
- */
-static
-void tracepoint_update_probe_range(struct tracepoint * const *begin,
- struct tracepoint * const *end)
-{
- struct tracepoint * const *iter;
- struct tracepoint_entry *mark_entry;
-
- for (iter = begin; iter < end; iter++) {
- if (!*iter)
- continue; /* skip dummy */
- if (!(*iter)->name) {
- disable_tracepoint(*iter);
- continue;
- }
- mark_entry = get_tracepoint((*iter)->name);
- if (mark_entry) {
- set_tracepoint(&mark_entry, *iter,
- !!mark_entry->refcount);
- } else {
- disable_tracepoint(*iter);
- }
- }
-}
-
-static void lib_update_tracepoints(void)
-{
- struct tracepoint_lib *lib;
-
- cds_list_for_each_entry(lib, &libs, list) {
- tracepoint_update_probe_range(lib->tracepoints_start,
- lib->tracepoints_start + lib->tracepoints_count);
- }
-}
-
-/*
- * Update probes, removing the faulty probes.
- */
-static void tracepoint_update_probes(void)
-{
- /* tracepoints registered from libraries and executable. */
- lib_update_tracepoints();
-}
-
-static struct tracepoint_probe *
-tracepoint_add_probe(const char *name, void *probe, void *data)
-{
- struct tracepoint_entry *entry;
- struct tracepoint_probe *old;
-
- entry = get_tracepoint(name);
- if (!entry) {
- entry = add_tracepoint(name);
- if (IS_ERR(entry))
- return (struct tracepoint_probe *)entry;
- }
- old = tracepoint_entry_add_probe(entry, probe, data);
- if (IS_ERR(old) && !entry->refcount)
- remove_tracepoint(entry);
- return old;
-}
-
-/**
- * __tracepoint_probe_register - Connect a probe to a tracepoint
- * @name: tracepoint name
- * @probe: probe handler
- *
- * Returns 0 if ok, error value on error.
- * The probe address must at least be aligned on the architecture pointer size.
- * Called with the UST lock held.
- */
-int __tracepoint_probe_register(const char *name, void *probe, void *data)
-{
- void *old;
-
- old = tracepoint_add_probe(name, probe, data);
- if (IS_ERR(old))
- return PTR_ERR(old);
-
- tracepoint_update_probes(); /* may update entry */
- release_probes(old);
- return 0;
-}
-
-static void *tracepoint_remove_probe(const char *name, void *probe, void *data)
-{
- struct tracepoint_entry *entry;
- void *old;
-
- entry = get_tracepoint(name);
- if (!entry)
- return ERR_PTR(-ENOENT);
- old = tracepoint_entry_remove_probe(entry, probe, data);
- if (IS_ERR(old))
- return old;
- if (!entry->refcount)
- remove_tracepoint(entry);
- return old;
-}
-
-/**
- * tracepoint_probe_unregister - Disconnect a probe from a tracepoint
- * @name: tracepoint name
- * @probe: probe function pointer
- * @probe: probe data pointer
- *
- * Called with the UST lock held.
- */
-int __tracepoint_probe_unregister(const char *name, void *probe, void *data)
-{
- void *old;
-
- old = tracepoint_remove_probe(name, probe, data);
- if (IS_ERR(old))
- return PTR_ERR(old);
-
- tracepoint_update_probes(); /* may update entry */
- release_probes(old);
- return 0;
-}
-
-static void tracepoint_add_old_probes(void *old)
-{
- need_update = 1;
- if (old) {
- struct tp_probes *tp_probes = _ust_container_of(old,
- struct tp_probes, probes[0]);
- cds_list_add(&tp_probes->u.list, &old_probes);
- }
-}
-
-/**
- * tracepoint_probe_register_noupdate - register a probe but not connect
- * @name: tracepoint name
- * @probe: probe handler
- *
- * caller must call tracepoint_probe_update_all()
- * Called with the UST lock held.
- */
-int tracepoint_probe_register_noupdate(const char *name, void *probe,
- void *data)
-{
- void *old;
-
- old = tracepoint_add_probe(name, probe, data);
- if (IS_ERR(old)) {
- return PTR_ERR(old);
- }
- tracepoint_add_old_probes(old);
- return 0;
-}
-
-/**
- * tracepoint_probe_unregister_noupdate - remove a probe but not disconnect
- * @name: tracepoint name
- * @probe: probe function pointer
- *
- * caller must call tracepoint_probe_update_all()
- * Called with the UST lock held.
- */
-int tracepoint_probe_unregister_noupdate(const char *name, void *probe,
- void *data)
-{
- void *old;
-
- old = tracepoint_remove_probe(name, probe, data);
- if (IS_ERR(old)) {
- return PTR_ERR(old);
- }
- tracepoint_add_old_probes(old);
- return 0;
-}
-
-/**
- * tracepoint_probe_update_all - update tracepoints
- * Called with the UST lock held.
- */
-void tracepoint_probe_update_all(void)
-{
- CDS_LIST_HEAD(release_probes);
- struct tp_probes *pos, *next;
-
- if (!need_update) {
- return;
- }
- if (!cds_list_empty(&old_probes))
- cds_list_replace_init(&old_probes, &release_probes);
- need_update = 0;
-
- tracepoint_update_probes();
- cds_list_for_each_entry_safe(pos, next, &release_probes, u.list) {
- cds_list_del(&pos->u.list);
- synchronize_rcu();
- free(pos);
- }
-}
-
-/*
- * Returns 0 if current not found.
- * Returns 1 if current found.
- *
- * Called with tracepoint mutex held
- */
-int lib_get_iter_tracepoints(struct tracepoint_iter *iter)
-{
- struct tracepoint_lib *iter_lib;
- int found = 0;
-
- cds_list_for_each_entry(iter_lib, &libs, list) {
- if (iter_lib < iter->lib)
- continue;
- else if (iter_lib > iter->lib)
- iter->tracepoint = NULL;
- found = tracepoint_get_iter_range(&iter->tracepoint,
- iter_lib->tracepoints_start,
- iter_lib->tracepoints_start + iter_lib->tracepoints_count);
- if (found) {
- iter->lib = iter_lib;
- break;
- }
- }
- return found;
-}
-
-/**
- * tracepoint_get_iter_range - Get a next tracepoint iterator given a range.
- * @tracepoint: current tracepoints (in), next tracepoint (out)
- * @begin: beginning of the range
- * @end: end of the range
- *
- * Returns whether a next tracepoint has been found (1) or not (0).
- * Will return the first tracepoint in the range if the input tracepoint is
- * NULL.
- * Called with tracepoint mutex held.
- */
-int tracepoint_get_iter_range(struct tracepoint * const **tracepoint,
- struct tracepoint * const *begin, struct tracepoint * const *end)
-{
- if (!*tracepoint && begin != end)
- *tracepoint = begin;
- while (*tracepoint >= begin && *tracepoint < end) {
- if (!**tracepoint)
- (*tracepoint)++; /* skip dummy */
- else
- return 1;
- }
- return 0;
-}
-
-/*
- * Called with tracepoint mutex held.
- */
-static void tracepoint_get_iter(struct tracepoint_iter *iter)
-{
- int found = 0;
-
- /* tracepoints in libs. */
- found = lib_get_iter_tracepoints(iter);
- if (!found)
- tracepoint_iter_reset(iter);
-}
-
-/*
- * Called with UST lock held.
- */
-void tracepoint_iter_start(struct tracepoint_iter *iter)
-{
- tracepoint_get_iter(iter);
-}
-
-/*
- * Called with UST lock held.
- */
-void tracepoint_iter_next(struct tracepoint_iter *iter)
-{
- iter->tracepoint++;
- /*
- * iter->tracepoint may be invalid because we blindly incremented it.
- * Make sure it is valid by marshalling on the tracepoints, getting the
- * tracepoints from following modules if necessary.
- */
- tracepoint_get_iter(iter);
-}
-
-/*
- * Called with UST lock held.
- */
-void tracepoint_iter_stop(struct tracepoint_iter *iter)
-{
-}
-
-void tracepoint_iter_reset(struct tracepoint_iter *iter)
-{
- iter->tracepoint = NULL;
-}
-
-void tracepoint_set_new_tracepoint_cb(void (*cb)(struct tracepoint *))
-{
- new_tracepoint_cb = cb;
-}
-
-static void new_tracepoints(struct tracepoint * const *start, struct tracepoint * const *end)
-{
- if (new_tracepoint_cb) {
- struct tracepoint * const *t;
-
- for (t = start; t < end; t++) {
- if (*t)
- new_tracepoint_cb(*t);
- }
- }
-}
-
-int tracepoint_register_lib(struct tracepoint * const *tracepoints_start,
- int tracepoints_count)
-{
- struct tracepoint_lib *pl, *iter;
-
- init_tracepoint();
-
- pl = (struct tracepoint_lib *) zmalloc(sizeof(struct tracepoint_lib));
-
- pl->tracepoints_start = tracepoints_start;
- pl->tracepoints_count = tracepoints_count;
-
- ust_lock();
- /*
- * We sort the libs by struct lib pointer address.
- */
- cds_list_for_each_entry_reverse(iter, &libs, list) {
- BUG_ON(iter == pl); /* Should never be in the list twice */
- if (iter < pl) {
- /* We belong to the location right after iter. */
- cds_list_add(&pl->list, &iter->list);
- goto lib_added;
- }
- }
- /* We should be added at the head of the list */
- cds_list_add(&pl->list, &libs);
-lib_added:
- new_tracepoints(tracepoints_start, tracepoints_start + tracepoints_count);
-
- /* TODO: update just the loaded lib */
- lib_update_tracepoints();
- ust_unlock();
-
- DBG("just registered a tracepoints section from %p and having %d tracepoints",
- tracepoints_start, tracepoints_count);
-
- return 0;
-}
-
-int tracepoint_unregister_lib(struct tracepoint * const *tracepoints_start)
-{
- struct tracepoint_lib *lib;
-
- ust_lock();
- cds_list_for_each_entry(lib, &libs, list) {
- if (lib->tracepoints_start == tracepoints_start) {
- struct tracepoint_lib *lib2free = lib;
- cds_list_del(&lib->list);
- free(lib2free);
- break;
- }
- }
- ust_unlock();
-
- return 0;
-}
-
-void init_tracepoint(void)
-{
- if (uatomic_xchg(&initialized, 1) == 1)
- return;
- init_usterr();
-}
-
-void exit_tracepoint(void)
-{
- initialized = 0;
-}
+++ /dev/null
-/*
- * ust-core.c
- *
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; 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
- */
-
-#include <lttng/usterr-signal-safe.h>
-#include <stdlib.h>
-
-volatile enum ust_loglevel ust_loglevel;
-
-void init_usterr(void)
-{
- char *ust_debug;
-
- if (ust_loglevel == UST_LOGLEVEL_UNKNOWN) {
- ust_debug = getenv("UST_DEBUG");
- if (ust_debug)
- ust_loglevel = UST_LOGLEVEL_DEBUG;
- else
- ust_loglevel = UST_LOGLEVEL_NORMAL;
- }
-}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/liblttng-ust-comm
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustctl.la
-
-libustctl_la_SOURCES = ustctl.c
-
-libustctl_la_LIBADD = $(top_builddir)/liblttng-ust-comm/liblttng-ust-comm.la \
- $(top_builddir)/libringbuffer/libringbuffer.la \
- $(top_builddir)/snprintf/libustsnprintf.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.
- */
-
-#include <string.h>
-#include <lttng/ust-ctl.h>
-#include <lttng/ust-abi.h>
-#include <lttng/usterr-signal-safe.h>
-#include <lttng/ust-comm.h>
-
-#include "../libringbuffer/backend.h"
-#include "../libringbuffer/frontend.h"
-
-volatile enum ust_loglevel ust_loglevel;
-
-static
-void init_object(struct lttng_ust_object_data *data)
-{
- data->handle = -1;
- data->shm_fd = -1;
- data->wait_fd = -1;
- data->memory_map_size = 0;
-}
-
-void release_object(int sock, struct lttng_ust_object_data *data)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- if (data->shm_fd >= 0)
- close(data->shm_fd);
- if (data->wait_fd >= 0)
- close(data->wait_fd);
- memset(&lum, 0, sizeof(lum));
- lum.handle = data->handle;
- lum.cmd = LTTNG_UST_RELEASE;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- assert(!ret);
- free(data);
-}
-
-/*
- * Send registration done packet to the application.
- */
-int ustctl_register_done(int sock)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- DBG("Sending register done command to %d", sock);
- memset(&lum, 0, sizeof(lum));
- lum.handle = LTTNG_UST_ROOT_HANDLE;
- lum.cmd = LTTNG_UST_REGISTER_DONE;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- if (lur.ret_code != USTCOMM_OK) {
- DBG("Return code: %s", ustcomm_get_readable_code(lur.ret_code));
- goto error;
- }
- return 0;
-
-error:
- return -1;
-}
-
-/*
- * returns session handle.
- */
-int ustctl_create_session(int sock)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret, session_handle;
-
- /* Create session */
- memset(&lum, 0, sizeof(lum));
- lum.handle = LTTNG_UST_ROOT_HANDLE;
- lum.cmd = LTTNG_UST_SESSION;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- session_handle = lur.ret_val;
- DBG("received session handle %u", session_handle);
- return session_handle;
-}
-
-/* open the metadata global channel */
-int ustctl_open_metadata(int sock, int session_handle,
- struct lttng_ust_channel_attr *chops,
- struct lttng_ust_object_data **_metadata_data)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- struct lttng_ust_object_data *metadata_data;
- int ret;
-
- metadata_data = malloc(sizeof(*metadata_data));
- if (!metadata_data)
- return -ENOMEM;
- init_object(metadata_data);
- /* Create metadata channel */
- memset(&lum, 0, sizeof(lum));
- lum.handle = session_handle;
- lum.cmd = LTTNG_UST_METADATA;
- lum.u.channel.overwrite = chops->overwrite;
- lum.u.channel.subbuf_size = chops->subbuf_size;
- lum.u.channel.num_subbuf = chops->num_subbuf;
- lum.u.channel.switch_timer_interval = chops->switch_timer_interval;
- lum.u.channel.read_timer_interval = chops->read_timer_interval;
- lum.u.channel.output = chops->output;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret) {
- free(metadata_data);
- return ret;
- }
- if (lur.ret_code != USTCOMM_OK) {
- free(metadata_data);
- return lur.ret_code;
- }
- metadata_data->handle = lur.ret_val;
- DBG("received metadata handle %u", metadata_data->handle);
- metadata_data->memory_map_size = lur.u.channel.memory_map_size;
- /* get shm fd */
- ret = ustcomm_recv_fd(sock);
- if (ret < 0)
- goto error;
- metadata_data->shm_fd = ret;
- /* get wait fd */
- ret = ustcomm_recv_fd(sock);
- if (ret < 0)
- goto error;
- metadata_data->wait_fd = ret;
- *_metadata_data = metadata_data;
- return 0;
-
-error:
- release_object(sock, metadata_data);
- return -EINVAL;
-}
-
-int ustctl_create_channel(int sock, int session_handle,
- struct lttng_ust_channel_attr *chops,
- struct lttng_ust_object_data **_channel_data)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- struct lttng_ust_object_data *channel_data;
- int ret;
-
- channel_data = malloc(sizeof(*channel_data));
- if (!channel_data)
- return -ENOMEM;
- init_object(channel_data);
- /* Create metadata channel */
- memset(&lum, 0, sizeof(lum));
- lum.handle = session_handle;
- lum.cmd = LTTNG_UST_CHANNEL;
- lum.u.channel.overwrite = chops->overwrite;
- lum.u.channel.subbuf_size = chops->subbuf_size;
- lum.u.channel.num_subbuf = chops->num_subbuf;
- lum.u.channel.switch_timer_interval = chops->switch_timer_interval;
- lum.u.channel.read_timer_interval = chops->read_timer_interval;
- lum.u.channel.output = chops->output;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret) {
- free(channel_data);
- return ret;
- }
- if (lur.ret_code != USTCOMM_OK) {
- free(channel_data);
- return lur.ret_code;
- }
- channel_data->handle = lur.ret_val;
- DBG("received channel handle %u", channel_data->handle);
- channel_data->memory_map_size = lur.u.channel.memory_map_size;
- /* get shm fd */
- ret = ustcomm_recv_fd(sock);
- if (ret < 0)
- goto error;
- channel_data->shm_fd = ret;
- /* get wait fd */
- ret = ustcomm_recv_fd(sock);
- if (ret < 0)
- goto error;
- channel_data->wait_fd = ret;
- *_channel_data = channel_data;
- return 0;
-
-error:
- release_object(sock, channel_data);
- return -EINVAL;
-}
-
-/*
- * Return -ENOENT if no more stream is available for creation.
- * Return 0 on success.
- * Return negative error value on error.
- */
-int ustctl_create_stream(int sock, struct lttng_ust_object_data *channel_data,
- struct lttng_ust_object_data **_stream_data)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- struct lttng_ust_object_data *stream_data;
- int ret, fd;
-
- stream_data = malloc(sizeof(*stream_data));
- if (!stream_data)
- return -ENOMEM;
- init_object(stream_data);
- memset(&lum, 0, sizeof(lum));
- lum.handle = channel_data->handle;
- lum.cmd = LTTNG_UST_STREAM;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret) {
- free(stream_data);
- return ret;
- }
- if (lur.ret_code != USTCOMM_OK) {
- free(stream_data);
- return lur.ret_code;
- }
-
- stream_data->handle = lur.ret_val;
- DBG("received stream handle %u", stream_data->handle);
- stream_data->memory_map_size = lur.u.stream.memory_map_size;
- /* get shm fd */
- fd = ustcomm_recv_fd(sock);
- if (fd < 0)
- goto error;
- stream_data->shm_fd = fd;
- /* get wait fd */
- fd = ustcomm_recv_fd(sock);
- if (fd < 0)
- goto error;
- stream_data->wait_fd = fd;
- *_stream_data = stream_data;
- return ret;
-
-error:
- release_object(sock, stream_data);
- return -EINVAL;
-}
-
-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)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- struct lttng_ust_object_data *event_data;
- int ret;
-
- event_data = malloc(sizeof(*event_data));
- if (!event_data)
- return -ENOMEM;
- init_object(event_data);
- memset(&lum, 0, sizeof(lum));
- lum.handle = channel_data->handle;
- lum.cmd = LTTNG_UST_EVENT;
- strncpy(lum.u.event.name, ev->name,
- LTTNG_UST_SYM_NAME_LEN);
- lum.u.event.instrumentation = ev->instrumentation;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret) {
- free(event_data);
- return ret;
- }
- event_data->handle = lur.ret_val;
- DBG("received event handle %u", event_data->handle);
- *_event_data = event_data;
- return 0;
-}
-
-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)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- struct lttng_ust_object_data *context_data;
- int ret;
-
- context_data = malloc(sizeof(*context_data));
- if (!context_data)
- return -ENOMEM;
- init_object(context_data);
- memset(&lum, 0, sizeof(lum));
- lum.handle = obj_data->handle;
- lum.cmd = LTTNG_UST_CONTEXT;
- lum.u.context.ctx = ctx->ctx;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret) {
- free(context_data);
- return ret;
- }
- context_data->handle = lur.ret_val;
- DBG("received context handle %u", context_data->handle);
- *_context_data = context_data;
- return ret;
-}
-
-/* Enable event, channel and session ioctl */
-int ustctl_enable(int sock, struct lttng_ust_object_data *object)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- memset(&lum, 0, sizeof(lum));
- lum.handle = object->handle;
- lum.cmd = LTTNG_UST_ENABLE;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- DBG("enabled handle %u", object->handle);
- return 0;
-}
-
-/* Disable event, channel and session ioctl */
-int ustctl_disable(int sock, struct lttng_ust_object_data *object)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- memset(&lum, 0, sizeof(lum));
- lum.handle = object->handle;
- lum.cmd = LTTNG_UST_DISABLE;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- DBG("disable handle %u", object->handle);
- return 0;
-}
-
-int ustctl_start_session(int sock, int handle)
-{
- struct lttng_ust_object_data obj;
-
- obj.handle = handle;
- return ustctl_enable(sock, &obj);
-}
-
-int ustctl_stop_session(int sock, int handle)
-{
- struct lttng_ust_object_data obj;
-
- obj.handle = handle;
- return ustctl_disable(sock, &obj);
-}
-
-
-int ustctl_tracepoint_list(int sock)
-{
- return -ENOSYS; /* not implemented */
-}
-
-int ustctl_tracer_version(int sock, struct lttng_ust_tracer_version *v)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- memset(&lum, 0, sizeof(lum));
- lum.handle = LTTNG_UST_ROOT_HANDLE;
- lum.cmd = LTTNG_UST_TRACER_VERSION;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- memcpy(v, &lur.u.version, sizeof(*v));
- DBG("received tracer version");
- return 0;
-}
-
-int ustctl_wait_quiescent(int sock)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
- int ret;
-
- memset(&lum, 0, sizeof(lum));
- lum.handle = LTTNG_UST_ROOT_HANDLE;
- lum.cmd = LTTNG_UST_WAIT_QUIESCENT;
- ret = ustcomm_send_app_cmd(sock, &lum, &lur);
- if (ret)
- return ret;
- DBG("waited for quiescent state");
- return 0;
-}
-
-int ustctl_flush_buffer(int sock, struct lttng_ust_object_data *channel_data)
-{
- struct ustcomm_ust_msg lum;
- struct ustcomm_ust_reply lur;
-
- memset(&lum, 0, sizeof(lum));
- lum.handle = channel_data->handle;
- lum.cmd = LTTNG_UST_FLUSH_BUFFER;
- return ustcomm_send_app_cmd(sock, &lum, &lur);
-}
-
-int ustctl_calibrate(int sock, struct lttng_ust_calibrate *calibrate)
-{
- return -ENOSYS;
-}
-
-/* Buffer operations */
-
-/* Map channel shm into process memory */
-struct lttng_ust_shm_handle *ustctl_map_channel(struct lttng_ust_object_data *chan_data)
-{
- struct lttng_ust_shm_handle *handle;
- struct channel *chan;
- size_t chan_size;
-
- handle = channel_handle_create(chan_data->shm_fd,
- chan_data->wait_fd,
- chan_data->memory_map_size);
- if (!handle) {
- ERR("create handle error");
- return NULL;
- }
- /*
- * Set to -1 because the lttng_ust_shm_handle destruction will take care
- * of closing shm_fd and wait_fd.
- */
- chan_data->shm_fd = -1;
- chan_data->wait_fd = -1;
-
- /*
- * TODO: add consistency checks to be resilient if the
- * application try to feed us with incoherent channel structure
- * values.
- */
- chan = shmp(handle, handle->chan);
- /* chan is object 0. This is hardcoded. */
- chan_size = handle->table->objects[0].allocated_len;
- handle->shadow_chan = malloc(chan_size);
- if (!handle->shadow_chan) {
- channel_destroy(chan, handle, 1);
- return NULL;
- }
- memcpy(handle->shadow_chan, chan, chan_size);
- return handle;
-}
-
-/* Add stream to channel shm and map its shm into process memory */
-int ustctl_add_stream(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_object_data *stream_data)
-{
- int ret;
-
- if (!stream_data->handle)
- return -ENOENT;
- /* map stream */
- ret = channel_handle_add_stream(handle,
- stream_data->shm_fd,
- stream_data->wait_fd,
- stream_data->memory_map_size);
- if (ret) {
- ERR("add stream error\n");
- return ret;
- }
- /*
- * Set to -1 because the lttng_ust_shm_handle destruction will take care
- * of closing shm_fd and wait_fd.
- */
- stream_data->shm_fd = -1;
- stream_data->wait_fd = -1;
- return 0;
-}
-
-void ustctl_unmap_channel(struct lttng_ust_shm_handle *handle)
-{
- struct channel *chan;
-
- chan = shmp(handle, handle->chan);
- channel_destroy(chan, handle, 1);
-}
-
-struct lttng_ust_lib_ring_buffer *ustctl_open_stream_read(struct lttng_ust_shm_handle *handle,
- int cpu)
-{
- struct channel *chan = handle->shadow_chan;
- int shm_fd, wait_fd;
- uint64_t memory_map_size;
- struct lttng_ust_lib_ring_buffer *buf;
- int ret;
-
- buf = channel_get_ring_buffer(&chan->backend.config,
- chan, cpu, handle, &shm_fd, &wait_fd, &memory_map_size);
- if (!buf)
- return NULL;
- ret = lib_ring_buffer_open_read(buf, handle, 1);
- if (ret)
- return NULL;
- return buf;
-}
-
-void ustctl_close_stream_read(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- lib_ring_buffer_release_read(buf, handle, 1);
-}
-
-/* For mmap mode, readable without "get" operation */
-
-void *ustctl_get_mmap_base(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- return shmp(handle, buf->backend.memory_map);
-}
-
-/* returns the length to mmap. */
-int ustctl_get_mmap_len(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf,
- unsigned long *len)
-{
- unsigned long mmap_buf_len;
- struct channel *chan = handle->shadow_chan;
-
- if (chan->backend.config.output != RING_BUFFER_MMAP)
- return -EINVAL;
- mmap_buf_len = chan->backend.buf_size;
- if (chan->backend.extra_reader_sb)
- mmap_buf_len += chan->backend.subbuf_size;
- if (mmap_buf_len > INT_MAX)
- return -EFBIG;
- *len = mmap_buf_len;
- return 0;
-}
-
-/* returns the maximum size for sub-buffers. */
-int ustctl_get_max_subbuf_size(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf,
- unsigned long *len)
-{
- struct channel *chan = handle->shadow_chan;
-
- *len = chan->backend.subbuf_size;
- return 0;
-}
-
-/*
- * 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 ustctl_get_mmap_read_offset(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *off)
-{
- struct channel *chan = handle->shadow_chan;
- unsigned long sb_bindex;
-
- if (chan->backend.config.output != RING_BUFFER_MMAP)
- return -EINVAL;
- sb_bindex = subbuffer_id_get_index(&chan->backend.config,
- buf->backend.buf_rsb.id);
- *off = shmp(handle, shmp_index(handle, buf->backend.array, sb_bindex)->shmp)->mmap_offset;
- return 0;
-}
-
-/* returns the size of the current sub-buffer, without padding (for mmap). */
-int ustctl_get_subbuf_size(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *len)
-{
- struct channel *chan = handle->shadow_chan;
-
- *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
- handle);
- return 0;
-}
-
-/* returns the size of the current sub-buffer, without padding (for mmap). */
-int ustctl_get_padded_subbuf_size(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *len)
-{
- struct channel *chan = handle->shadow_chan;
-
- *len = lib_ring_buffer_get_read_data_size(&chan->backend.config, buf,
- handle);
- *len = PAGE_ALIGN(*len);
- return 0;
-}
-
-/* Get exclusive read access to the next sub-buffer that can be read. */
-int ustctl_get_next_subbuf(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- return lib_ring_buffer_get_next_subbuf(buf, handle);
-}
-
-
-/* Release exclusive sub-buffer access, move consumer forward. */
-int ustctl_put_next_subbuf(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- lib_ring_buffer_put_next_subbuf(buf, handle);
- return 0;
-}
-
-/* snapshot */
-
-/* Get a snapshot of the current ring buffer producer and consumer positions */
-int ustctl_snapshot(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- return lib_ring_buffer_snapshot(buf, &buf->cons_snapshot,
- &buf->prod_snapshot, handle);
-}
-
-/* Get the consumer position (iteration start) */
-int ustctl_snapshot_get_consumed(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
-{
- *pos = buf->cons_snapshot;
- return 0;
-}
-
-/* Get the producer position (iteration end) */
-int ustctl_snapshot_get_produced(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
-{
- *pos = buf->prod_snapshot;
- return 0;
-}
-
-/* Get exclusive read access to the specified sub-buffer position */
-int ustctl_get_subbuf(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf, unsigned long *pos)
-{
- return lib_ring_buffer_get_subbuf(buf, *pos, handle);
-}
-
-/* Release exclusive sub-buffer access */
-int ustctl_put_subbuf(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- lib_ring_buffer_put_subbuf(buf, handle);
- return 0;
-}
-
-int ustctl_buffer_flush(struct lttng_ust_shm_handle *handle,
- struct lttng_ust_lib_ring_buffer *buf)
-{
- lib_ring_buffer_switch_slow(buf, SWITCH_ACTIVE, handle);
- return 0;
-}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustfork.la
-libustfork_la_SOURCES = ustfork.c
-libustfork_la_LIBADD = \
- -ldl \
- $(top_builddir)/libust/libust.la
-libustfork_CFLAGS = -DUST_COMPONENT=libustfork -fno-strict-aliasing
+++ /dev/null
-/*
- * Copyright (C) 2009 Pierre-Marc Fournier
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1 of
- * the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#define _GNU_SOURCE
-#include <dlfcn.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <signal.h>
-#include <sched.h>
-#include <stdarg.h>
-#include "usterr.h"
-
-#include <lttng/ust.h>
-
-struct user_desc;
-
-pid_t fork(void)
-{
- static pid_t (*plibc_func)(void) = NULL;
- ust_fork_info_t fork_info;
- pid_t retval;
-
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "fork");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"fork\" symbol\n");
- return -1;
- }
- }
-
- ust_before_fork(&fork_info);
- /* Do the real fork */
- retval = plibc_func();
- if (retval == 0) {
- /* child */
- ust_after_fork_child(&fork_info);
- } else {
- ust_after_fork_parent(&fork_info);
- }
- return retval;
-}
-
-struct ustfork_clone_info {
- int (*fn)(void *);
- void *arg;
- ust_fork_info_t fork_info;
-};
-
-static int clone_fn(void *arg)
-{
- struct ustfork_clone_info *info = (struct ustfork_clone_info *) arg;
-
- /* clone is now done and we are in child */
- ust_after_fork_child(&info->fork_info);
- return info->fn(info->arg);
-}
-
-int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
-{
- static int (*plibc_func)(int (*fn)(void *), void *child_stack,
- int flags, void *arg, pid_t *ptid,
- struct user_desc *tls, pid_t *ctid) = NULL;
- /* var args */
- pid_t *ptid;
- struct user_desc *tls;
- pid_t *ctid;
- /* end of var args */
- va_list ap;
- int retval;
-
- va_start(ap, arg);
- ptid = va_arg(ap, pid_t *);
- tls = va_arg(ap, struct user_desc *);
- ctid = va_arg(ap, pid_t *);
- va_end(ap);
-
- if (plibc_func == NULL) {
- plibc_func = dlsym(RTLD_NEXT, "clone");
- if (plibc_func == NULL) {
- fprintf(stderr, "libustfork: unable to find \"clone\" symbol.\n");
- return -1;
- }
- }
-
- if (flags & CLONE_VM) {
- /*
- * Creating a thread, no need to intervene, just pass on
- * the arguments.
- */
- retval = plibc_func(fn, child_stack, flags, arg, ptid,
- tls, ctid);
- } else {
- /* Creating a real process, we need to intervene. */
- struct ustfork_clone_info info = { fn: fn, arg: arg };
-
- ust_before_fork(&info.fork_info);
- retval = plibc_func(clone_fn, child_stack, flags, &info,
- ptid, tls, ctid);
- /* The child doesn't get here. */
- ust_after_fork_parent(&info.fork_info);
- }
- return retval;
-}
+++ /dev/null
-AM_CPPFLAGS = -I$(top_srcdir)/include
-AM_CFLAGS = -fno-strict-aliasing
-
-lib_LTLIBRARIES = libustinstr-malloc.la
-libustinstr_malloc_la_SOURCES = mallocwrap.c
-libustinstr_malloc_la_LIBADD = -ldl
-
-noinst_SCRIPTS = run
-EXTRA_DIST = run
+++ /dev/null
-libustinstr-malloc is used for instrumenting calls to malloc(3) in a program,
-without need for recompiling it.
-
-libustinstr-malloc defines a malloc() function that is instrumented with a
-marker. It also calls the libc malloc afterwards. When loaded with LD_PRELOAD,
-it replaces the libc malloc() function, in effect instrumenting all calls to
-malloc().
-
-See the "run" script for a usage example.
+++ /dev/null
-/*
- * Copyright (C) 2009 Pierre-Marc Fournier
- *
- * 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
- */
-
-#define _GNU_SOURCE
-#include <dlfcn.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <ust/marker.h>
-
-void *malloc(size_t size)
-{
- static void *(*plibc_malloc)(size_t size) = NULL;
- void *retval;
-
- if (plibc_malloc == NULL) {
- plibc_malloc = dlsym(RTLD_NEXT, "malloc");
- if (plibc_malloc == NULL) {
- fprintf(stderr, "mallocwrap: unable to find malloc\n");
- return NULL;
- }
- }
-
- retval = plibc_malloc(size);
-
- ust_marker(malloc, "size %d ptr %p", (int)size, retval);
-
- return retval;
-}
-
-void free(void *ptr)
-{
- static void *(*plibc_free)(void *ptr) = NULL;
-
- if (plibc_free == NULL) {
- plibc_free = dlsym(RTLD_NEXT, "free");
- if (plibc_free == NULL) {
- fprintf(stderr, "mallocwrap: unable to find free\n");
- return;
- }
- }
-
- ust_marker(free, "ptr %p", ptr);
-
- plibc_free(ptr);
-}
-
-UST_MARKER_LIB
+++ /dev/null
-#!/bin/sh
-
-LD_VERBOSE=1 LD_LIBRARY_PATH=.:../libust/.libs:../../liburcu LD_PRELOAD=liburcu.so:libust.so:.libs/libmallocwrap.so $1
+++ /dev/null
-UST.class
-UST.h
+++ /dev/null
-if BUILD_JNI_INTERFACE
-
-AM_CPPFLAGS = -I$(top_srcdir)/include
-
-lib_LTLIBRARIES = libustjava.la
-libustjava_la_SOURCES = UST.c UST.h ust_java.h
-dist_noinst_DATA = UST.java
-libustjava_la_LIBADD = -lc -L$(top_builddir)/libust/.libs -lust
-
-all: UST.class UST.h
-
-clean-local:
- rm -rf UST.h UST.class
-
-UST.class: UST.java
- javac -d "$(builddir)" "$(srcdir)/UST.java"
-
-UST.h: UST.class
- javah -jni UST
-
-endif
+++ /dev/null
-This directory contains a simple API for instrumenting java applications.
-
-Configuration examples to build this library:
-
-dependency: sun-java6-jdk
-./configure --with-java-jdk=/usr/lib/jvm/java-6-sun --with-jni-interface
-
-dependency: openjdk-6-jdk
-./configure --with-java-jdk=/usr/lib/jvm/java-6-openjdk --with-jni-interface
-
-dependency: gcj-4.4-jdk
-./configure --with-java-jdk=/usr/lib/jvm/java-gcj --with-jni-interface
+++ /dev/null
-#include <jni.h>
-
-#define TRACEPOINT_CREATE_PROBES
-#include "ust_java.h"
-
-JNIEXPORT void JNICALL Java_UST_ust_1java_1event (JNIEnv *env, jobject jobj,
- jstring ev_name, jstring args)
-{
- jboolean iscopy;
- const char *ev_name_cstr = (*env)->GetStringUTFChars(env, ev_name,
- &iscopy);
- const char *args_cstr = (*env)->GetStringUTFChars(env, args, &iscopy);
-
- tracepoint(ust_java_event, ev_name_cstr, args_cstr);
-}
+++ /dev/null
-import java.util.*;
-
-class UST {
- public static native void ust_java_event(String name, String arg);
- static {
- System.loadLibrary("ustjava");
- }
-}
-
+++ /dev/null
-#undef TRACEPOINT_SYSTEM
-#define TRACEPOINT_SYSTEM ust_java
-
-#if !defined(_TRACEPOINT_UST_JAVA_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
-#define _TRACEPOINT_UST_JAVA_H
-
-/*
- * Copyright (C) 2011 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; version 2.1 of
- * the License.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <ust/tracepoint.h>
-
-TRACEPOINT_EVENT(ust_java_event,
- TP_PROTO(const char *name, const char *args),
- TP_ARGS(name, args),
- TP_FIELDS(
- ctf_string(name, name)
- ctf_string(args, args)
- )
-)
-
-#endif /* _TRACEPOINT_UST_JAVA_H */
-
-#undef TRACEPOINT_INCLUDE_PATH
-#define TRACEPOINT_INCLUDE_PATH .
-#undef TRACEPOINT_INCLUDE_FILE
-#define TRACEPOINT_INCLUDE_FILE ust_java
-
-/* This part must be outside protection */
-#include <ust/tracepoint-event.h>
noinst_PROGRAMS = fork fork2
fork_SOURCES = fork.c ust_tests_fork.h
-fork_LDADD = $(top_builddir)/libust/libust.la $(top_builddir)/libustfork/libustfork.la
+fork_LDADD = $(top_builddir)/liblttng-ust/liblttng-ust.la \
+ $(top_builddir)/liblttng-ust-fork/liblttng-ust-fork.la
fork2_SOURCES = fork2.c
-fork2_LDADD = $(top_builddir)/libust/libust.la $(top_builddir)/libustfork/libustfork.la
+fork2_LDADD = $(top_builddir)/liblttng-ust/liblttng-ust.la \
+ $(top_builddir)/liblttng-ust-fork/liblttng-ust-fork.la
noinst_SCRIPTS = run
EXTRA_DIST = run
-AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/libust
+AM_CPPFLAGS = -I$(top_srcdir)/include
noinst_PROGRAMS = hello
hello_SOURCES = hello.c tp.c tp.h
-hello_LDADD = $(top_builddir)/libust/libust.la
+hello_LDADD = $(top_builddir)/liblttng-ust/liblttng-ust.la
noinst_SCRIPTS = run
EXTRA_DIST = run