X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=ltt-events.c;h=526fb823952219e74cbe1fcebe64da69b7a05865;hb=38d024aef3fc8a0dc8dbecf6fbd4b55dc1e5e49f;hp=a5ca1abaa798c83489fc604705a0ea966141ffa4;hpb=85a9ca7ff30ef892e5865c5541fd11f6c6de33a3;p=lttng-modules.git diff --git a/ltt-events.c b/ltt-events.c index a5ca1aba..526fb823 100644 --- a/ltt-events.c +++ b/ltt-events.c @@ -11,15 +11,29 @@ #include #include #include +#include +#include #include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ #include "ltt-events.h" +#include "ltt-tracer.h" static LIST_HEAD(sessions); static LIST_HEAD(ltt_transport_list); static DEFINE_MUTEX(sessions_mutex); static struct kmem_cache *event_cache; -static void synchronize_trace(void) +static void _ltt_event_destroy(struct ltt_event *event); +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); + + +static +void synchronize_trace(void) { synchronize_sched(); #ifdef CONFIG_PREEMPT_RT @@ -37,6 +51,7 @@ struct ltt_session *ltt_session_create(void) return NULL; INIT_LIST_HEAD(&session->chan); INIT_LIST_HEAD(&session->events); + uuid_le_gen(&session->uuid); list_add(&session->list, &sessions); mutex_unlock(&sessions_mutex); return session; @@ -67,14 +82,34 @@ void ltt_session_destroy(struct ltt_session *session) int ltt_session_start(struct ltt_session *session) { int ret = 0; + struct ltt_channel *chan; mutex_lock(&sessions_mutex); if (session->active) { ret = -EBUSY; goto end; } + + /* + * Snapshot the number of events per channel to know the type of header + * we need to use. + */ + 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 */ + } + ACCESS_ONCE(session->active) = 1; synchronize_trace(); /* Wait for in-flight events to complete */ + ret = _ltt_session_metadata_statedump(session); + if (ret) { + ACCESS_ONCE(session->active) = 0; + synchronize_trace(); /* Wait for in-flight events to complete */ + } end: mutex_unlock(&sessions_mutex); return ret; @@ -133,11 +168,12 @@ struct ltt_channel *ltt_channel_create(struct ltt_session *session, goto nomem; chan->session = session; init_waitqueue_head(&chan->notify_wait); - chan->chan = transport->ops.channel_create("[lttng]", session, buf_addr, + chan->chan = transport->ops.channel_create("[lttng]", chan, buf_addr, subbuf_size, num_subbuf, switch_timer_interval, read_timer_interval); if (!chan->chan) goto create_error; + chan->id = session->free_chan_id++; chan->ops = &transport->ops; list_add(&chan->list, &session->chan); mutex_unlock(&sessions_mutex); @@ -166,7 +202,7 @@ void _ltt_channel_destroy(struct ltt_channel *chan) * Supports event creation while tracing session is active. */ struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name, - enum instrum_type itype, + enum lttng_kernel_instrumentation instrumentation, const struct lttng_event_desc *event_desc, void *filter) { @@ -190,11 +226,11 @@ struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name, event->desc = event_desc; event->filter = filter; event->id = chan->free_event_id++; - event->itype = itype; + event->instrumentation = instrumentation; /* Populate ltt_event structure before tracepoint registration. */ smp_wmb(); - switch (itype) { - case INSTRUM_TRACEPOINTS: + switch (instrumentation) { + case LTTNG_KERNEL_TRACEPOINTS: ret = tracepoint_probe_register(name, event_desc->probe_callback, event); if (ret) @@ -203,10 +239,16 @@ struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name, default: WARN_ON_ONCE(1); } + ret = _ltt_event_metadata_statedump(chan->session, chan, event); + if (ret) + goto statedump_error; list_add(&event->list, &chan->session->events); mutex_unlock(&sessions_mutex); return event; +statedump_error: + WARN_ON_ONCE(tracepoint_probe_unregister(name, event_desc->probe_callback, + event)); register_error: kmem_cache_free(event_cache, event); cache_error: @@ -223,8 +265,8 @@ int _ltt_event_unregister(struct ltt_event *event) { int ret = -EINVAL; - switch (event->itype) { - case INSTRUM_TRACEPOINTS: + switch (event->instrumentation) { + case LTTNG_KERNEL_TRACEPOINTS: ret = tracepoint_probe_unregister(event->desc->name, event->desc->probe_callback, event); @@ -237,9 +279,23 @@ int _ltt_event_unregister(struct ltt_event *event) return ret; } +/* + * Used when an event FD is released. + */ +int ltt_event_unregister(struct ltt_event *event) +{ + int ret; + + mutex_lock(&sessions_mutex); + ret = ltt_event_unregister(event); + mutex_unlock(&sessions_mutex); + return ret; +} + /* * Only used internally at session destruction. */ +static void _ltt_event_destroy(struct ltt_event *event) { ltt_event_put(event->desc); @@ -247,6 +303,381 @@ void _ltt_event_destroy(struct ltt_event *event) kmem_cache_free(event_cache, event); } +/* + * We have exclusive access to our metadata buffer (protected by the + * sessions_mutex), 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 lib_ring_buffer_ctx ctx; + struct ltt_channel *chan = session->metadata; + char *str; + int ret = 0, waitret; + size_t len, reserve_len, pos; + va_list ap; + + WARN_ON_ONCE(!ACCESS_ONCE(session->active)); + + va_start(ap, fmt); + str = kvasprintf(GFP_KERNEL, fmt, ap); + va_end(ap); + if (!str) + 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), + len - pos); + lib_ring_buffer_ctx_init(&ctx, chan->chan, NULL, reserve_len, + sizeof(char), -1); + /* + * 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_event_interruptible_timeout(*chan->ops->get_reader_wait_queue(chan), + ({ + ret = chan->ops->event_reserve(&ctx); + ret != -ENOBUFS || !ret; + }), + msecs_to_jiffies(LTTNG_METADATA_TIMEOUT_MSEC)); + if (!waitret || waitret == -ERESTARTSYS || ret) { + printk(KERN_WARNING "LTTng: Failure to write metadata to buffers (%s)\n", + waitret == -ERESTARTSYS ? "interrupted" : + (ret == -ENOBUFS ? "timeout" : "I/O error")); + if (waitret == -ERESTARTSYS) + ret = waitret; + goto end; + } + chan->ops->event_write(&ctx, &str[pos], len); + chan->ops->event_commit(&ctx); + } +end: + kfree(str); + 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]; + + switch (field->type.atype) { + case atype_integer: + ret = lttng_metadata_printf(session, + " integer { size = %u; align = %u; signed = %u;%s } %s;\n", + field->type.u.basic.integer.size, + field->type.u.basic.integer.alignment, + field->type.u.basic.integer.signedness, +#ifdef __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;%s } %s[%u];\n", + elem_type->u.basic.integer.size, + elem_type->u.basic.integer.alignment, + elem_type->u.basic.integer.signedness, +#ifdef __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;%s } %s[ integer { size = %u; align = %u; signed = %u;%s } ];\n", + elem_type->u.basic.integer.size, + elem_type->u.basic.integer.alignment, + elem_type->u.basic.integer.signedness, +#ifdef __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, + length_type->u.basic.integer.size, + length_type->u.basic.integer.alignment, + length_type->u.basic.integer.signedness, +#ifdef __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 + ); + break; + } + + case atype_string: + 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_event_metadata_statedump(struct ltt_session *session, + struct ltt_channel *chan, + struct ltt_event *event) +{ + int ret = 0; + + if (event->metadata_dumped || !ACCESS_ONCE(session->active)) + return 0; + if (chan == session->metadata) + return 0; + + ret = lttng_metadata_printf(session, + "event {\n" + " name = %s;\n" + " id = %u;\n" + " stream_id = %u;\n" + " event.fields := struct {\n", + event->desc->name, + event->id, + event->chan->id); + 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 || !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" + "};\n\n", + chan->id, + chan->header_type == 1 ? "struct event_header_compact" : + "struct event_header_large"); + if (ret) + goto end; + + 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" + ); +} + +/* + * 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 {\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 {\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", + ltt_alignof(uint32_t) * CHAR_BIT, + ltt_alignof(uint16_t) * CHAR_BIT + ); +} + +/* + * Output metadata into this session's metadata buffers. + */ +static +int _ltt_session_metadata_statedump(struct ltt_session *session) +{ + char uuid_s[37]; + struct ltt_channel *chan; + struct ltt_event *event; + int ret = 0; + + if (!ACCESS_ONCE(session->active)) + return 0; + if (session->metadata_dumped) + goto skip_session; + if (!session->metadata) { + printk(KERN_WARNING "LTTng: attempt to start tracing, but metadata channel is not found. Operation abort.\n"); + return -EPERM; + } + + snprintf(uuid_s, sizeof(uuid_s), + "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x%x", + uuid_s[0], uuid_s[1], uuid_s[2], uuid_s[3], + uuid_s[4], uuid_s[5], uuid_s[6], uuid_s[7], + uuid_s[8], uuid_s[9], uuid_s[10], uuid_s[11], + uuid_s[12], uuid_s[13], uuid_s[14], uuid_s[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", + ltt_alignof(uint8_t) * CHAR_BIT, + ltt_alignof(uint32_t) * CHAR_BIT, + ltt_alignof(uint64_t) * CHAR_BIT, + CTF_VERSION_MAJOR, + CTF_VERSION_MINOR, + uuid_s, +#ifdef __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: + list_for_each_entry(chan, &session->chan, list) { + ret = _ltt_channel_metadata_statedump(session, chan); + if (ret) + goto end; + } + + list_for_each_entry(event, &session->events, list) { + ret = _ltt_event_metadata_statedump(session, chan, event); + if (ret) + goto end; + } + session->metadata_dumped = 1; +end: + return ret; +} + /** * ltt_transport_register - LTT transport registration * @transport: transport structure @@ -286,7 +717,6 @@ void ltt_transport_unregister(struct ltt_transport *transport) } EXPORT_SYMBOL_GPL(ltt_transport_unregister); - static int __init ltt_events_init(void) { int ret;