* Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
* Holds LTTng per-session event registry.
+ *
+ * Dual LGPL v2.1/GPL v2 license.
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <linux/uuid.h>
+#include "wrapper/uuid.h"
#include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */
#include "ltt-events.h"
#include "ltt-tracer.h"
static
int _ltt_session_metadata_statedump(struct ltt_session *session);
-
-static
void synchronize_trace(void)
{
synchronize_sched();
mutex_lock(&sessions_mutex);
ACCESS_ONCE(session->active) = 0;
+ list_for_each_entry(chan, &session->chan, list) {
+ ret = lttng_syscalls_unregister(chan);
+ WARN_ON(ret);
+ }
list_for_each_entry(event, &session->events, list) {
ret = _ltt_event_unregister(event);
WARN_ON(ret);
kfree(session);
}
-int ltt_session_start(struct ltt_session *session)
+int ltt_session_enable(struct ltt_session *session)
{
int ret = 0;
struct ltt_channel *chan;
}
ACCESS_ONCE(session->active) = 1;
- synchronize_trace(); /* Wait for in-flight events to complete */
+ ACCESS_ONCE(session->been_active) = 1;
ret = _ltt_session_metadata_statedump(session);
- if (ret) {
+ if (ret)
ACCESS_ONCE(session->active) = 0;
- synchronize_trace(); /* Wait for in-flight events to complete */
- }
end:
mutex_unlock(&sessions_mutex);
return ret;
}
-int ltt_session_stop(struct ltt_session *session)
+int ltt_session_disable(struct ltt_session *session)
{
int ret = 0;
goto end;
}
ACCESS_ONCE(session->active) = 0;
- synchronize_trace(); /* Wait for in-flight events to complete */
end:
mutex_unlock(&sessions_mutex);
return ret;
}
+int ltt_channel_enable(struct ltt_channel *channel)
+{
+ int old;
+
+ if (channel == channel->session->metadata)
+ return -EPERM;
+ old = 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 = 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 = 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 = xchg(&event->enabled, 0);
+ if (!old)
+ return -EEXIST;
+ return 0;
+}
+
static struct ltt_transport *ltt_transport_find(const char *name)
{
struct ltt_transport *transport;
unsigned int read_timer_interval)
{
struct ltt_channel *chan;
- struct ltt_transport *transport;
+ struct ltt_transport *transport = NULL;
mutex_lock(&sessions_mutex);
- if (session->active) {
- printk(KERN_WARNING "LTTng refusing to add channel to active session\n");
+ if (session->been_active)
goto active; /* Refuse to add channel to active session */
- }
transport = ltt_transport_find(transport_name);
if (!transport) {
printk(KERN_WARNING "LTTng transport %s not found\n",
transport_name);
goto notransport;
}
+ if (!try_module_get(transport->owner)) {
+ printk(KERN_WARNING "LTT : Can't lock transport module.\n");
+ goto notransport;
+ }
chan = kzalloc(sizeof(struct ltt_channel), GFP_KERNEL);
if (!chan)
goto nomem;
chan->session = session;
- init_waitqueue_head(&chan->notify_wait);
chan->id = session->free_chan_id++;
/*
* Note: the channel creation op already writes into the packet
read_timer_interval);
if (!chan->chan)
goto create_error;
+ chan->enabled = 1;
chan->ops = &transport->ops;
+ chan->transport = transport;
list_add(&chan->list, &session->chan);
mutex_unlock(&sessions_mutex);
return chan;
create_error:
kfree(chan);
nomem:
+ if (transport)
+ module_put(transport->owner);
notransport:
active:
mutex_unlock(&sessions_mutex);
void _ltt_channel_destroy(struct ltt_channel *chan)
{
chan->ops->channel_destroy(chan->chan);
+ module_put(chan->transport->owner);
list_del(&chan->list);
+ lttng_destroy_context(chan->ctx);
kfree(chan);
}
*/
struct ltt_event *ltt_event_create(struct ltt_channel *chan,
struct lttng_kernel_event *event_param,
- void *filter)
+ void *filter,
+ const struct lttng_event_desc *internal_desc)
{
struct ltt_event *event;
int ret;
event->chan = chan;
event->filter = filter;
event->id = chan->free_event_id++;
+ event->enabled = 1;
event->instrumentation = event_param->instrumentation;
/* Populate ltt_event structure before tracepoint registration. */
smp_wmb();
ret = try_module_get(event->desc->owner);
WARN_ON_ONCE(!ret);
break;
+ case LTTNG_KERNEL_KRETPROBE:
+ {
+ struct ltt_event *event_return;
+
+ /* kretprobe defines 2 events */
+ event_return =
+ kmem_cache_zalloc(event_cache, GFP_KERNEL);
+ if (!event_return)
+ goto register_error;
+ event_return->chan = chan;
+ event_return->filter = filter;
+ event_return->id = chan->free_event_id++;
+ event_return->enabled = 1;
+ event_return->instrumentation = event_param->instrumentation;
+ /*
+ * Populate ltt_event structure before kretprobe registration.
+ */
+ smp_wmb();
+ ret = lttng_kretprobes_register(event_param->name,
+ event_param->u.kretprobe.symbol_name,
+ event_param->u.kretprobe.offset,
+ event_param->u.kretprobe.addr,
+ event, event_return);
+ if (ret) {
+ kmem_cache_free(event_cache, event_return);
+ goto register_error;
+ }
+ /* Take 2 refs on the module: one per event. */
+ ret = try_module_get(event->desc->owner);
+ WARN_ON_ONCE(!ret);
+ ret = try_module_get(event->desc->owner);
+ WARN_ON_ONCE(!ret);
+ ret = _ltt_event_metadata_statedump(chan->session, chan,
+ event_return);
+ if (ret) {
+ kmem_cache_free(event_cache, event_return);
+ module_put(event->desc->owner);
+ module_put(event->desc->owner);
+ goto statedump_error;
+ }
+ list_add(&event_return->list, &chan->session->events);
+ break;
+ }
case LTTNG_KERNEL_FUNCTION:
ret = lttng_ftrace_register(event_param->name,
event_param->u.ftrace.symbol_name,
ret = try_module_get(event->desc->owner);
WARN_ON_ONCE(!ret);
break;
+ case LTTNG_KERNEL_NOOP:
+ event->desc = internal_desc;
+ if (!event->desc)
+ goto register_error;
+ break;
default:
WARN_ON_ONCE(1);
}
return event;
statedump_error:
- WARN_ON_ONCE(tracepoint_probe_unregister(event_param->name,
- event->desc->probe_callback,
- event));
- ltt_event_put(event->desc);
+ /* If a statedump error occurs, events will not be readable. */
register_error:
kmem_cache_free(event_cache, event);
cache_error:
lttng_kprobes_unregister(event);
ret = 0;
break;
+ case LTTNG_KERNEL_KRETPROBE:
+ lttng_kretprobes_unregister(event);
+ ret = 0;
+ break;
case LTTNG_KERNEL_FUNCTION:
lttng_ftrace_unregister(event);
ret = 0;
break;
+ case LTTNG_KERNEL_NOOP:
+ ret = 0;
+ break;
default:
WARN_ON_ONCE(1);
}
module_put(event->desc->owner);
lttng_kprobes_destroy_private(event);
break;
+ case LTTNG_KERNEL_KRETPROBE:
+ module_put(event->desc->owner);
+ lttng_kretprobes_destroy_private(event);
+ break;
case LTTNG_KERNEL_FUNCTION:
module_put(event->desc->owner);
lttng_ftrace_destroy_private(event);
break;
+ case LTTNG_KERNEL_NOOP:
+ break;
default:
WARN_ON_ONCE(1);
}
list_del(&event->list);
+ lttng_destroy_context(event->ctx);
kmem_cache_free(event_cache, event);
}
* we need to bail out after timeout or being
* interrupted.
*/
- waitret = wait_event_interruptible_timeout(*chan->ops->get_reader_wait_queue(chan),
+ waitret = wait_event_interruptible_timeout(*chan->ops->get_writer_buf_wait_queue(chan->chan, -1),
({
ret = chan->ops->event_reserve(&ctx, 0);
ret != -ENOBUFS || !ret;
ret = waitret;
goto end;
}
- chan->ops->event_write(&ctx, &str[pos], len);
+ chan->ops->event_write(&ctx, &str[pos], reserve_len);
chan->ops->event_commit(&ctx);
}
end:
}
static
-int _ltt_fields_metadata_statedump(struct ltt_session *session,
- struct ltt_event *event)
+int _ltt_field_statedump(struct ltt_session *session,
+ const struct lttng_event_field *field)
{
- 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];
- 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,
+ 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,
#ifdef __BIG_ENDIAN
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+ field->type.u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
#else
- field->type.u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+ 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,
+ 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,
#ifdef __BIG_ENDIAN
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
#else
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+ 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",
- " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } %s[ __%s_length ];\n",
- length_type->u.basic.integer.size,
- 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,
+ 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,
#ifdef __BIG_ENDIAN
- length_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+ length_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
#else
- length_type->u.basic.integer.reverse_byte_order
-? " byte_order = be;" : "",
+ length_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
#endif
- field->name,
- 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,
+ 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,
#ifdef __BIG_ENDIAN
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = le;" : "",
#else
- elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
+ elem_type->u.basic.integer.reverse_byte_order ? " byte_order = be;" : "",
#endif
- field->name,
- field->name
- );
- break;
- }
+ 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;
- }
+ 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;
}
"event {\n"
" name = %s;\n"
" id = %u;\n"
- " stream_id = %u;\n"
- " fields := struct {\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;
"stream {\n"
" id = %u;\n"
" event.header := %s;\n"
- " packet.context := struct packet_context;\n"
- "};\n\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;