X-Git-Url: http://git.liburcu.org/?p=lttng-modules.git;a=blobdiff_plain;f=lttng-events.c;h=4588153bf2d76a3f1fe8190a9119b69285ca0b6c;hp=7eec04c0f269682a1aed08bbc501a9a992a127ea;hb=436f640717bab63240d9a624846d947e150a2cee;hpb=8c6e7f13c778701dec2d6549f1e2ca98970907a0 diff --git a/lttng-events.c b/lttng-events.c index 7eec04c0..4588153b 100644 --- a/lttng-events.c +++ b/lttng-events.c @@ -1,23 +1,10 @@ -/* +/* SPDX-License-Identifier: (GPL-2.0 or LGPL-2.1) + * * lttng-events.c * * Holds LTTng per-session event registry. * * Copyright (C) 2010-2012 Mathieu Desnoyers - * - * 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 */ /* @@ -36,19 +23,26 @@ #include #include #include -#include "wrapper/file.h" +#include #include #include - -#include "wrapper/uuid.h" -#include "wrapper/vmalloc.h" /* for wrapper_vmalloc_sync_all() */ -#include "wrapper/random.h" -#include "wrapper/tracepoint.h" -#include "wrapper/list.h" -#include "lttng-kernel-version.h" -#include "lttng-events.h" -#include "lttng-tracer.h" -#include "lttng-abi-old.h" +#include +#include +#include + +#include /* for wrapper_vmalloc_sync_all() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #define METADATA_CACHE_DEFAULT_SIZE 4096 @@ -75,10 +69,19 @@ static int _lttng_session_metadata_statedump(struct lttng_session *session); static void _lttng_metadata_channel_hangup(struct lttng_metadata_stream *stream); +static +int _lttng_field_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting); void synchronize_trace(void) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5,1,0)) + synchronize_rcu(); +#else synchronize_sched(); +#endif + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) #ifdef CONFIG_PREEMPT_RT_FULL synchronize_rcu(); @@ -121,7 +124,7 @@ struct lttng_session *lttng_session_create(void) int i; mutex_lock(&sessions_mutex); - session = kzalloc(sizeof(struct lttng_session), GFP_KERNEL); + session = lttng_kvzalloc(sizeof(struct lttng_session), GFP_KERNEL); if (!session) goto err; INIT_LIST_HEAD(&session->chan); @@ -132,12 +135,12 @@ struct lttng_session *lttng_session_create(void) GFP_KERNEL); if (!metadata_cache) goto err_free_session; - metadata_cache->data = kzalloc(METADATA_CACHE_DEFAULT_SIZE, - GFP_KERNEL); + metadata_cache->data = vzalloc(METADATA_CACHE_DEFAULT_SIZE); if (!metadata_cache->data) goto err_free_cache; metadata_cache->cache_alloc = METADATA_CACHE_DEFAULT_SIZE; kref_init(&metadata_cache->refcount); + mutex_init(&metadata_cache->lock); session->metadata_cache = metadata_cache; INIT_LIST_HEAD(&metadata_cache->metadata_stream); memcpy(&metadata_cache->uuid, &session->uuid, @@ -152,7 +155,7 @@ struct lttng_session *lttng_session_create(void) err_free_cache: kfree(metadata_cache); err_free_session: - kfree(session); + lttng_kvfree(session); err: mutex_unlock(&sessions_mutex); return NULL; @@ -162,7 +165,7 @@ void metadata_cache_destroy(struct kref *kref) { struct lttng_metadata_cache *cache = container_of(kref, struct lttng_metadata_cache, refcount); - kfree(cache->data); + vfree(cache->data); kfree(cache); } @@ -175,7 +178,7 @@ void lttng_session_destroy(struct lttng_session *session) int ret; mutex_lock(&sessions_mutex); - ACCESS_ONCE(session->active) = 0; + WRITE_ONCE(session->active, 0); list_for_each_entry(chan, &session->chan, list) { ret = lttng_syscalls_unregister(chan); WARN_ON(ret); @@ -201,7 +204,17 @@ void lttng_session_destroy(struct lttng_session *session) kref_put(&session->metadata_cache->refcount, metadata_cache_destroy); list_del(&session->list); mutex_unlock(&sessions_mutex); - kfree(session); + lttng_kvfree(session); +} + +int lttng_session_statedump(struct lttng_session *session) +{ + int ret; + + mutex_lock(&sessions_mutex); + ret = lttng_statedump_start(session); + mutex_unlock(&sessions_mutex); + return ret; } int lttng_session_enable(struct lttng_session *session) @@ -218,6 +231,9 @@ int lttng_session_enable(struct lttng_session *session) /* Set transient enabler state to "enabled" */ session->tstate = 1; + /* We need to sync enablers with session before activation. */ + lttng_session_sync_enablers(session); + /* * Snapshot the number of events per channel to know the type of header * we need to use. @@ -231,19 +247,22 @@ int lttng_session_enable(struct lttng_session *session) chan->header_type = 2; /* large */ } - /* We need to sync enablers with session before activation. */ - lttng_session_sync_enablers(session); + /* Clear each stream's quiescent state. */ + list_for_each_entry(chan, &session->chan, list) { + if (chan->channel_type != METADATA_CHANNEL) + lib_ring_buffer_clear_quiescent_channel(chan->chan); + } - ACCESS_ONCE(session->active) = 1; - ACCESS_ONCE(session->been_active) = 1; + WRITE_ONCE(session->active, 1); + WRITE_ONCE(session->been_active, 1); ret = _lttng_session_metadata_statedump(session); if (ret) { - ACCESS_ONCE(session->active) = 0; + WRITE_ONCE(session->active, 0); goto end; } ret = lttng_statedump_start(session); if (ret) - ACCESS_ONCE(session->active) = 0; + WRITE_ONCE(session->active, 0); end: mutex_unlock(&sessions_mutex); return ret; @@ -252,17 +271,64 @@ end: int lttng_session_disable(struct lttng_session *session) { int ret = 0; + struct lttng_channel *chan; mutex_lock(&sessions_mutex); if (!session->active) { ret = -EBUSY; goto end; } - ACCESS_ONCE(session->active) = 0; + WRITE_ONCE(session->active, 0); /* Set transient enabler state to "disabled" */ session->tstate = 0; lttng_session_sync_enablers(session); + + /* Set each stream's quiescent state. */ + list_for_each_entry(chan, &session->chan, list) { + if (chan->channel_type != METADATA_CHANNEL) + lib_ring_buffer_set_quiescent_channel(chan->chan); + } +end: + mutex_unlock(&sessions_mutex); + return ret; +} + +int lttng_session_metadata_regenerate(struct lttng_session *session) +{ + int ret = 0; + struct lttng_channel *chan; + struct lttng_event *event; + struct lttng_metadata_cache *cache = session->metadata_cache; + struct lttng_metadata_stream *stream; + + mutex_lock(&sessions_mutex); + if (!session->active) { + ret = -EBUSY; + goto end; + } + + mutex_lock(&cache->lock); + memset(cache->data, 0, cache->cache_alloc); + cache->metadata_written = 0; + cache->version++; + list_for_each_entry(stream, &session->metadata_cache->metadata_stream, list) { + stream->metadata_out = 0; + stream->metadata_in = 0; + } + mutex_unlock(&cache->lock); + + session->metadata_dumped = 0; + list_for_each_entry(chan, &session->chan, list) { + chan->metadata_dumped = 0; + } + + list_for_each_entry(event, &session->events, list) { + event->metadata_dumped = 0; + } + + ret = _lttng_session_metadata_statedump(session); + end: mutex_unlock(&sessions_mutex); return ret; @@ -285,7 +351,7 @@ int lttng_channel_enable(struct lttng_channel *channel) channel->tstate = 1; lttng_session_sync_enablers(channel->session); /* Set atomically the state to "enabled" */ - ACCESS_ONCE(channel->enabled) = 1; + WRITE_ONCE(channel->enabled, 1); end: mutex_unlock(&sessions_mutex); return ret; @@ -305,7 +371,7 @@ int lttng_channel_disable(struct lttng_channel *channel) goto end; } /* Set atomically the state to "disabled" */ - ACCESS_ONCE(channel->enabled) = 0; + WRITE_ONCE(channel->enabled, 0); /* Set transient enabler state to "enabled" */ channel->tstate = 0; lttng_session_sync_enablers(channel->session); @@ -327,8 +393,24 @@ int lttng_event_enable(struct lttng_event *event) ret = -EEXIST; goto end; } - ACCESS_ONCE(event->enabled) = 1; - lttng_session_sync_enablers(event->chan->session); + switch (event->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + ret = -EINVAL; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_NOOP: + WRITE_ONCE(event->enabled, 1); + break; + case LTTNG_KERNEL_KRETPROBE: + ret = lttng_kretprobes_event_enable_state(event, 1); + break; + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } end: mutex_unlock(&sessions_mutex); return ret; @@ -347,8 +429,24 @@ int lttng_event_disable(struct lttng_event *event) ret = -EEXIST; goto end; } - ACCESS_ONCE(event->enabled) = 0; - lttng_session_sync_enablers(event->chan->session); + switch (event->instrumentation) { + case LTTNG_KERNEL_TRACEPOINT: + case LTTNG_KERNEL_SYSCALL: + ret = -EINVAL; + break; + case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_FUNCTION: + case LTTNG_KERNEL_UPROBE: + case LTTNG_KERNEL_NOOP: + WRITE_ONCE(event->enabled, 0); + break; + case LTTNG_KERNEL_KRETPROBE: + ret = lttng_kretprobes_event_enable_state(event, 0); + break; + default: + WARN_ON_ONCE(1); + ret = -EINVAL; + } end: mutex_unlock(&sessions_mutex); return ret; @@ -485,6 +583,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, event_name = event_desc->name; break; case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: case LTTNG_KERNEL_KRETPROBE: case LTTNG_KERNEL_FUNCTION: case LTTNG_KERNEL_NOOP: @@ -536,7 +635,11 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, smp_wmb(); break; case LTTNG_KERNEL_KPROBE: - event->enabled = 1; + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + event->enabled = 0; event->registered = 1; /* * Populate lttng_event structure before event @@ -560,7 +663,11 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, struct lttng_event *event_return; /* kretprobe defines 2 events */ - event->enabled = 1; + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + event->enabled = 0; event->registered = 1; event_return = kmem_cache_zalloc(event_cache, GFP_KERNEL); @@ -571,7 +678,7 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, event_return->chan = chan; event_return->filter = filter; event_return->id = chan->free_event_id++; - event_return->enabled = 1; + event_return->enabled = 0; event_return->registered = 1; event_return->instrumentation = itype; /* @@ -606,7 +713,11 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, break; } case LTTNG_KERNEL_FUNCTION: - event->enabled = 1; + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + event->enabled = 0; event->registered = 1; /* * Populate lttng_event structure before event @@ -624,7 +735,11 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, break; case LTTNG_KERNEL_NOOP: case LTTNG_KERNEL_SYSCALL: - event->enabled = 1; + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + event->enabled = 0; event->registered = 0; event->desc = event_desc; if (!event->desc) { @@ -632,6 +747,28 @@ struct lttng_event *_lttng_event_create(struct lttng_channel *chan, goto register_error; } break; + case LTTNG_KERNEL_UPROBE: + /* + * Needs to be explicitly enabled after creation, since + * we may want to apply filters. + */ + event->enabled = 0; + event->registered = 1; + + /* + * Populate lttng_event structure before event + * registration. + */ + smp_wmb(); + + ret = lttng_uprobes_register(event_param->name, + event_param->u.uprobe.fd, + event); + if (ret) + goto register_error; + ret = try_module_get(event->desc->owner); + WARN_ON_ONCE(!ret); + break; default: WARN_ON_ONCE(1); ret = -EINVAL; @@ -694,6 +831,7 @@ void register_event(struct lttng_event *event) desc->name); break; case LTTNG_KERNEL_KPROBE: + case LTTNG_KERNEL_UPROBE: case LTTNG_KERNEL_KRETPROBE: case LTTNG_KERNEL_FUNCTION: case LTTNG_KERNEL_NOOP: @@ -743,6 +881,10 @@ int _lttng_event_unregister(struct lttng_event *event) case LTTNG_KERNEL_NOOP: ret = 0; break; + case LTTNG_KERNEL_UPROBE: + lttng_uprobes_unregister(event); + ret = 0; + break; default: WARN_ON_ONCE(1); } @@ -776,6 +918,10 @@ void _lttng_event_destroy(struct lttng_event *event) case LTTNG_KERNEL_NOOP: case LTTNG_KERNEL_SYSCALL: break; + case LTTNG_KERNEL_UPROBE: + module_put(event->desc->owner); + lttng_uprobes_destroy_private(event); + break; default: WARN_ON_ONCE(1); } @@ -995,17 +1141,22 @@ int lttng_session_list_tracker_pids(struct lttng_session *session) ret = PTR_ERR(tracker_pids_list_file); goto file_error; } + if (!atomic_long_add_unless(&session->file->f_count, 1, LONG_MAX)) { + ret = -EOVERFLOW; + goto refcount_error; + } ret = lttng_tracker_pids_list_fops.open(NULL, tracker_pids_list_file); if (ret < 0) goto open_error; m = tracker_pids_list_file->private_data; m->private = session; fd_install(file_fd, tracker_pids_list_file); - atomic_long_inc(&session->file->f_count); return file_fd; open_error: + atomic_long_dec(&session->file->f_count); +refcount_error: fput(tracker_pids_list_file); file_error: put_unused_fd(file_fd); @@ -1017,11 +1168,11 @@ fd_error: * Enabler management. */ static -int lttng_match_enabler_wildcard(const char *desc_name, - const char *name) +int lttng_match_enabler_star_glob(const char *desc_name, + const char *pattern) { - /* Compare excluding final '*' */ - if (strncmp(desc_name, name, strlen(name) - 1)) + if (!strutils_star_glob_match(pattern, LTTNG_SIZE_MAX, + desc_name, LTTNG_SIZE_MAX)) return 0; return 1; } @@ -1066,8 +1217,8 @@ int lttng_desc_match_enabler(const struct lttng_event_desc *desc, return -EINVAL; } switch (enabler->type) { - case LTTNG_ENABLER_WILDCARD: - return lttng_match_enabler_wildcard(desc_name, enabler_name); + case LTTNG_ENABLER_STAR_GLOB: + return lttng_match_enabler_star_glob(desc_name, enabler_name); case LTTNG_ENABLER_NAME: return lttng_match_enabler_name(desc_name, enabler_name); default: @@ -1320,6 +1471,18 @@ error_free: return ret; } +int lttng_event_add_callsite(struct lttng_event *event, + struct lttng_kernel_event_callsite __user *callsite) +{ + + switch (event->instrumentation) { + case LTTNG_KERNEL_UPROBE: + return lttng_uprobes_add_callsite(event, callsite); + default: + return -EINVAL; + } +} + int lttng_enabler_attach_context(struct lttng_enabler *enabler, struct lttng_kernel_context *context_param) { @@ -1390,7 +1553,7 @@ void lttng_session_sync_enablers(struct lttng_session *session) */ enabled = enabled && session->tstate && event->chan->tstate; - ACCESS_ONCE(event->enabled) = enabled; + WRITE_ONCE(event->enabled, enabled); /* * Sync tracepoint registration with event enabled * state. @@ -1439,10 +1602,12 @@ void lttng_session_lazy_sync_enablers(struct lttng_session *session) /* * Serialize at most one packet worth of metadata into a metadata * channel. - * 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. + * We grab the metadata cache mutex to get exclusive access to our metadata + * buffer and to the metadata cache. Exclusive access to the metadata buffer + * allows us to do racy operations such as looking for remaining space left in + * packet and write, since mutual exclusion protects us from concurrent writes. + * Mutual exclusion on the metadata cache allow us to read the cache content + * without racing against reallocation of the cache by updates. * Returns the number of bytes written in the channel, 0 if no data * was written and a negative value on error. */ @@ -1454,17 +1619,23 @@ int lttng_metadata_output_channel(struct lttng_metadata_stream *stream, size_t len, reserve_len; /* - * Ensure we support mutiple get_next / put sequences followed - * by put_next. The metadata stream lock internally protects - * reading the metadata cache. It can indeed be read - * concurrently by "get_next_subbuf" and "flush" operations on - * the buffer invoked by different processes. + * Ensure we support mutiple get_next / put sequences followed by + * put_next. The metadata cache lock protects reading the metadata + * cache. It can indeed be read concurrently by "get_next_subbuf" and + * "flush" operations on the buffer invoked by different processes. + * Moreover, since the metadata cache memory can be reallocated, we + * need to have exclusive access against updates even though we only + * read it. */ - mutex_lock(&stream->lock); + mutex_lock(&stream->metadata_cache->lock); WARN_ON(stream->metadata_in < stream->metadata_out); if (stream->metadata_in != stream->metadata_out) goto end; + /* Metadata regenerated, change the version. */ + if (stream->metadata_cache->version != stream->version) + stream->version = stream->metadata_cache->version; + len = stream->metadata_cache->metadata_written - stream->metadata_in; if (!len) @@ -1490,13 +1661,15 @@ int lttng_metadata_output_channel(struct lttng_metadata_stream *stream, ret = reserve_len; end: - mutex_unlock(&stream->lock); + mutex_unlock(&stream->metadata_cache->lock); return ret; } /* * Write the metadata to the metadata cache. * Must be called with sessions_mutex held. + * The metadata cache lock protects us from concurrent read access from + * thread outputting metadata content to ring buffer. */ int lttng_metadata_printf(struct lttng_session *session, const char *fmt, ...) @@ -1506,7 +1679,7 @@ int lttng_metadata_printf(struct lttng_session *session, va_list ap; struct lttng_metadata_stream *stream; - WARN_ON_ONCE(!ACCESS_ONCE(session->active)); + WARN_ON_ONCE(!READ_ONCE(session->active)); va_start(ap, fmt); str = kvasprintf(GFP_KERNEL, fmt, ap); @@ -1515,6 +1688,7 @@ int lttng_metadata_printf(struct lttng_session *session, return -ENOMEM; len = strlen(str); + mutex_lock(&session->metadata_cache->lock); if (session->metadata_cache->metadata_written + len > session->metadata_cache->cache_alloc) { char *tmp_cache_realloc; @@ -1523,10 +1697,16 @@ int lttng_metadata_printf(struct lttng_session *session, tmp_cache_alloc_size = max_t(unsigned int, session->metadata_cache->cache_alloc + len, session->metadata_cache->cache_alloc << 1); - tmp_cache_realloc = krealloc(session->metadata_cache->data, - tmp_cache_alloc_size, GFP_KERNEL); + tmp_cache_realloc = vzalloc(tmp_cache_alloc_size); if (!tmp_cache_realloc) goto err; + if (session->metadata_cache->data) { + memcpy(tmp_cache_realloc, + session->metadata_cache->data, + session->metadata_cache->cache_alloc); + vfree(session->metadata_cache->data); + } + session->metadata_cache->cache_alloc = tmp_cache_alloc_size; session->metadata_cache->data = tmp_cache_realloc; } @@ -1534,6 +1714,7 @@ int lttng_metadata_printf(struct lttng_session *session, session->metadata_cache->metadata_written, str, len); session->metadata_cache->metadata_written += len; + mutex_unlock(&session->metadata_cache->lock); kfree(str); list_for_each_entry(stream, &session->metadata_cache->metadata_stream, list) @@ -1542,23 +1723,349 @@ int lttng_metadata_printf(struct lttng_session *session, return 0; err: + mutex_unlock(&session->metadata_cache->lock); kfree(str); return -ENOMEM; } +static +int print_tabs(struct lttng_session *session, size_t nesting) +{ + size_t i; + + for (i = 0; i < nesting; i++) { + int ret; + + ret = lttng_metadata_printf(session, " "); + if (ret) { + return ret; + } + } + return 0; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_struct_type_statedump(struct lttng_session *session, + const struct lttng_type *type, + size_t nesting) +{ + int ret; + uint32_t i, nr_fields; + + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "struct {\n"); + if (ret) + return ret; + nr_fields = type->u._struct.nr_fields; + for (i = 0; i < nr_fields; i++) { + const struct lttng_event_field *iter_field; + + iter_field = &type->u._struct.fields[i]; + ret = _lttng_field_statedump(session, iter_field, nesting + 1); + if (ret) + return ret; + } + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "}"); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_struct_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting) +{ + int ret; + + ret = _lttng_struct_type_statedump(session, + &field->type, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "_%s;\n", + field->name); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_variant_type_statedump(struct lttng_session *session, + const struct lttng_type *type, + size_t nesting) +{ + int ret; + uint32_t i, nr_choices; + + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "variant <_%s> {\n", + type->u.variant.tag_name); + if (ret) + return ret; + nr_choices = type->u.variant.nr_choices; + for (i = 0; i < nr_choices; i++) { + const struct lttng_event_field *iter_field; + + iter_field = &type->u.variant.choices[i]; + ret = _lttng_field_statedump(session, iter_field, nesting + 1); + if (ret) + return ret; + } + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "}"); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_variant_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting) +{ + int ret; + + ret = _lttng_variant_type_statedump(session, + &field->type, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "_%s;\n", + field->name); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_array_compound_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting) +{ + int ret; + const struct lttng_type *elem_type; + + /* Only array of structures and variants are currently supported. */ + elem_type = field->type.u.array_compound.elem_type; + switch (elem_type->atype) { + case atype_struct: + ret = _lttng_struct_type_statedump(session, elem_type, nesting); + if (ret) + return ret; + break; + case atype_variant: + ret = _lttng_variant_type_statedump(session, elem_type, nesting); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + ret = lttng_metadata_printf(session, + " _%s[%u];\n", + field->name, + field->type.u.array_compound.length); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_sequence_compound_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting) +{ + int ret; + const char *length_name; + const struct lttng_type *elem_type; + + length_name = field->type.u.sequence_compound.length_name; + + /* Only array of structures and variants are currently supported. */ + elem_type = field->type.u.sequence_compound.elem_type; + switch (elem_type->atype) { + case atype_struct: + ret = _lttng_struct_type_statedump(session, elem_type, nesting); + if (ret) + return ret; + break; + case atype_variant: + ret = _lttng_variant_type_statedump(session, elem_type, nesting); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + ret = lttng_metadata_printf(session, + " _%s[ _%s ];\n", + field->name, + length_name); + return ret; +} + +/* + * Must be called with sessions_mutex held. + */ +static +int _lttng_enum_statedump(struct lttng_session *session, + const struct lttng_event_field *field, + size_t nesting) +{ + const struct lttng_enum_desc *enum_desc; + const struct lttng_integer_type *container_type; + int ret; + unsigned int i, nr_entries; + + enum_desc = field->type.u.basic.enumeration.desc; + container_type = &field->type.u.basic.enumeration.container_type; + nr_entries = enum_desc->nr_entries; + + ret = print_tabs(session, nesting); + if (ret) + goto end; + ret = lttng_metadata_printf(session, + "enum : integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } {\n", + container_type->size, + container_type->alignment, + container_type->signedness, + (container_type->encoding == lttng_encode_none) + ? "none" + : (container_type->encoding == lttng_encode_UTF8) + ? "UTF8" + : "ASCII", + container_type->base, +#if __BYTE_ORDER == __BIG_ENDIAN + container_type->reverse_byte_order ? " byte_order = le;" : "" +#else + container_type->reverse_byte_order ? " byte_order = be;" : "" +#endif + ); + if (ret) + goto end; + /* Dump all entries */ + for (i = 0; i < nr_entries; i++) { + const struct lttng_enum_entry *entry = &enum_desc->entries[i]; + int j, len; + + ret = print_tabs(session, nesting + 1); + if (ret) + goto end; + ret = lttng_metadata_printf(session, + "\""); + if (ret) + goto end; + len = strlen(entry->string); + /* Escape the character '"' */ + for (j = 0; j < len; j++) { + char c = entry->string[j]; + + switch (c) { + case '"': + ret = lttng_metadata_printf(session, + "\\\""); + break; + case '\\': + ret = lttng_metadata_printf(session, + "\\\\"); + break; + default: + ret = lttng_metadata_printf(session, + "%c", c); + break; + } + if (ret) + goto end; + } + ret = lttng_metadata_printf(session, "\""); + if (ret) + goto end; + + if (entry->options.is_auto) { + ret = lttng_metadata_printf(session, ",\n"); + if (ret) + goto end; + } else { + ret = lttng_metadata_printf(session, + " = "); + if (ret) + goto end; + if (entry->start.signedness) + ret = lttng_metadata_printf(session, + "%lld", (long long) entry->start.value); + else + ret = lttng_metadata_printf(session, + "%llu", entry->start.value); + if (ret) + goto end; + if (entry->start.signedness == entry->end.signedness && + entry->start.value + == entry->end.value) { + ret = lttng_metadata_printf(session, + ",\n"); + } else { + if (entry->end.signedness) { + ret = lttng_metadata_printf(session, + " ... %lld,\n", + (long long) entry->end.value); + } else { + ret = lttng_metadata_printf(session, + " ... %llu,\n", + entry->end.value); + } + } + if (ret) + goto end; + } + } + ret = print_tabs(session, nesting); + if (ret) + goto end; + ret = lttng_metadata_printf(session, "} _%s;\n", + field->name); +end: + return ret; +} + /* * Must be called with sessions_mutex held. */ static int _lttng_field_statedump(struct lttng_session *session, - const struct lttng_event_field *field) + const struct lttng_event_field *field, + size_t nesting) { int ret = 0; switch (field->type.atype) { case atype_integer: + ret = print_tabs(session, nesting); + if (ret) + return ret; ret = lttng_metadata_printf(session, - " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s;\n", + "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, @@ -1568,7 +2075,7 @@ int _lttng_field_statedump(struct lttng_session *session, ? "UTF8" : "ASCII", field->type.u.basic.integer.base, -#ifdef __BIG_ENDIAN +#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;" : "", @@ -1576,18 +2083,30 @@ int _lttng_field_statedump(struct lttng_session *session, field->name); break; case atype_enum: - ret = lttng_metadata_printf(session, - " %s _%s;\n", - field->type.u.basic.enumeration.name, - field->name); + ret = _lttng_enum_statedump(session, field, nesting); break; case atype_array: + case atype_array_bitfield: { const struct lttng_basic_type *elem_type; elem_type = &field->type.u.array.elem_type; + if (field->type.u.array.elem_alignment) { + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "struct { } align(%u) _%s_padding;\n", + field->type.u.array.elem_alignment * CHAR_BIT, + field->name); + if (ret) + return ret; + } + ret = print_tabs(session, nesting); + if (ret) + return ret; ret = lttng_metadata_printf(session, - " integer { size = %u; align = %u; signed = %u; encoding = %s; base = %u;%s } _%s[%u];\n", + "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, @@ -1597,7 +2116,7 @@ int _lttng_field_statedump(struct lttng_session *session, ? "UTF8" : "ASCII", elem_type->u.basic.integer.base, -#ifdef __BIG_ENDIAN +#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;" : "", @@ -1606,14 +2125,18 @@ int _lttng_field_statedump(struct lttng_session *session, break; } case atype_sequence: + case atype_sequence_bitfield: { 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 = print_tabs(session, nesting); + if (ret) + return ret; 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_length;\n", length_type->u.basic.integer.size, (unsigned int) length_type->u.basic.integer.alignment, length_type->u.basic.integer.signedness, @@ -1623,7 +2146,7 @@ int _lttng_field_statedump(struct lttng_session *session, ? "UTF8" : "ASCII"), length_type->u.basic.integer.base, -#ifdef __BIG_ENDIAN +#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;" : "", @@ -1632,8 +2155,22 @@ int _lttng_field_statedump(struct lttng_session *session, if (ret) return ret; + if (field->type.u.sequence.elem_alignment) { + ret = print_tabs(session, nesting); + if (ret) + return ret; + ret = lttng_metadata_printf(session, + "struct { } align(%u) _%s_padding;\n", + field->type.u.sequence.elem_alignment * CHAR_BIT, + field->name); + if (ret) + return ret; + } + ret = print_tabs(session, nesting); + 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", + "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, @@ -1643,7 +2180,7 @@ int _lttng_field_statedump(struct lttng_session *session, ? "UTF8" : "ASCII"), elem_type->u.basic.integer.base, -#ifdef __BIG_ENDIAN +#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;" : "", @@ -1655,12 +2192,28 @@ int _lttng_field_statedump(struct lttng_session *session, case atype_string: /* Default encoding is UTF8 */ + ret = print_tabs(session, nesting); + if (ret) + return ret; ret = lttng_metadata_printf(session, - " string%s _%s;\n", + "string%s _%s;\n", field->type.u.basic.string.encoding == lttng_encode_ASCII ? " { encoding = ASCII; }" : "", field->name); break; + case atype_struct: + ret = _lttng_struct_statedump(session, field, nesting); + break; + case atype_array_compound: + ret = _lttng_array_compound_statedump(session, field, nesting); + break; + case atype_sequence_compound: + ret = _lttng_sequence_compound_statedump(session, field, nesting); + break; + case atype_variant: + ret = _lttng_variant_statedump(session, field, nesting); + break; + default: WARN_ON_ONCE(1); return -EINVAL; @@ -1680,7 +2233,7 @@ int _lttng_context_metadata_statedump(struct lttng_session *session, for (i = 0; i < ctx->nr_fields; i++) { const struct lttng_ctx_field *field = &ctx->fields[i]; - ret = _lttng_field_statedump(session, &field->event_field); + ret = _lttng_field_statedump(session, &field->event_field, 2); if (ret) return ret; } @@ -1698,7 +2251,7 @@ int _lttng_fields_metadata_statedump(struct lttng_session *session, for (i = 0; i < desc->nr_fields; i++) { const struct lttng_event_field *field = &desc->fields[i]; - ret = _lttng_field_statedump(session, field); + ret = _lttng_field_statedump(session, field, 2); if (ret) return ret; } @@ -1715,7 +2268,7 @@ int _lttng_event_metadata_statedump(struct lttng_session *session, { int ret = 0; - if (event->metadata_dumped || !ACCESS_ONCE(session->active)) + if (event->metadata_dumped || !READ_ONCE(session->active)) return 0; if (chan->channel_type == METADATA_CHANNEL) return 0; @@ -1782,7 +2335,7 @@ int _lttng_channel_metadata_statedump(struct lttng_session *session, { int ret = 0; - if (chan->metadata_dumped || !ACCESS_ONCE(session->active)) + if (chan->metadata_dumped || !READ_ONCE(session->active)) return 0; if (chan->channel_type == METADATA_CHANNEL) @@ -1836,6 +2389,7 @@ int _lttng_stream_packet_context_declare(struct lttng_session *session) " uint64_clock_monotonic_t timestamp_end;\n" " uint64_t content_size;\n" " uint64_t packet_size;\n" + " uint64_t packet_seq_num;\n" " unsigned long events_discarded;\n" " uint32_t cpu_id;\n" "};\n\n" @@ -1892,28 +2446,94 @@ int _lttng_event_header_declare(struct lttng_session *session) * taken at start of trace. * Yes, this is only an approximation. Yes, we can (and will) do better * in future versions. + * This function may return a negative offset. It may happen if the + * system sets the REALTIME clock to 0 after boot. */ static -uint64_t measure_clock_offset(void) +int64_t measure_clock_offset(void) { - uint64_t offset, monotonic[2], realtime; + uint64_t monotonic_avg, monotonic[2], realtime; + uint64_t tcf = trace_clock_freq(); + int64_t offset; struct timespec rts = { 0, 0 }; unsigned long flags; /* Disable interrupts to increase correlation precision. */ local_irq_save(flags); monotonic[0] = trace_clock_read64(); - getnstimeofday(&rts); + getnstimeofday(&rts); monotonic[1] = trace_clock_read64(); local_irq_restore(flags); - offset = (monotonic[0] + monotonic[1]) >> 1; - realtime = (uint64_t) rts.tv_sec * NSEC_PER_SEC; - realtime += rts.tv_nsec; - offset = realtime - offset; + monotonic_avg = (monotonic[0] + monotonic[1]) >> 1; + realtime = (uint64_t) rts.tv_sec * tcf; + if (tcf == NSEC_PER_SEC) { + realtime += rts.tv_nsec; + } else { + uint64_t n = rts.tv_nsec * tcf; + + do_div(n, NSEC_PER_SEC); + realtime += n; + } + offset = (int64_t) realtime - monotonic_avg; return offset; } +static +int print_escaped_ctf_string(struct lttng_session *session, const char *string) +{ + int ret; + size_t i; + char cur; + + i = 0; + cur = string[i]; + while (cur != '\0') { + switch (cur) { + case '\n': + ret = lttng_metadata_printf(session, "%s", "\\n"); + break; + case '\\': + case '"': + ret = lttng_metadata_printf(session, "%c", '\\'); + if (ret) + goto error; + /* We still print the current char */ + /* Fallthrough */ + default: + ret = lttng_metadata_printf(session, "%c", cur); + break; + } + + if (ret) + goto error; + + cur = string[++i]; + } +error: + return ret; +} + +static +int print_metadata_escaped_field(struct lttng_session *session, const char *field, + const char *field_value) +{ + int ret; + + ret = lttng_metadata_printf(session, " %s = \"", field); + if (ret) + goto error; + + ret = print_escaped_ctf_string(session, field_value); + if (ret) + goto error; + + ret = lttng_metadata_printf(session, "\";\n"); + +error: + return ret; +} + /* * Output metadata into this session's metadata buffers. * Must be called with sessions_mutex held. @@ -1923,11 +2543,12 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) { unsigned char *uuid_c = session->uuid.b; unsigned char uuid_s[37], clock_uuid_s[BOOT_ID_LEN]; + const char *product_uuid; struct lttng_channel *chan; struct lttng_event *event; int ret = 0; - if (!ACCESS_ONCE(session->active)) + if (!READ_ONCE(session->active)) return 0; if (session->metadata_dumped) goto skip_session; @@ -1957,6 +2578,7 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) " uint32_t magic;\n" " uint8_t uuid[16];\n" " uint32_t stream_id;\n" + " uint64_t stream_instance_id;\n" " };\n" "};\n\n", lttng_alignof(uint8_t) * CHAR_BIT, @@ -1968,7 +2590,7 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) CTF_SPEC_MAJOR, CTF_SPEC_MINOR, uuid_s, -#ifdef __BIG_ENDIAN +#if __BYTE_ORDER == __BIG_ENDIAN "be" #else "le" @@ -1988,7 +2610,7 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) " tracer_major = %d;\n" " tracer_minor = %d;\n" " tracer_patchlevel = %d;\n" - "};\n\n", + " trace_buffering_scheme = \"global\";\n", current->nsproxy->uts_ns->name.nodename, utsname()->sysname, utsname()->release, @@ -2000,10 +2622,34 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) if (ret) goto end; + ret = print_metadata_escaped_field(session, "trace_name", session->name); + if (ret) + goto end; + ret = print_metadata_escaped_field(session, "trace_creation_datetime", + session->creation_time); + if (ret) + goto end; + + /* Add the product UUID to the 'env' section */ + product_uuid = dmi_get_system_info(DMI_PRODUCT_UUID); + if (product_uuid) { + ret = lttng_metadata_printf(session, + " product_uuid = \"%s\";\n", + product_uuid + ); + if (ret) + goto end; + } + + /* Close the 'env' section */ + ret = lttng_metadata_printf(session, "};\n\n"); + if (ret) + goto end; + ret = lttng_metadata_printf(session, "clock {\n" - " name = %s;\n", - "monotonic" + " name = \"%s\";\n", + trace_clock_name() ); if (ret) goto end; @@ -2018,13 +2664,14 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) } ret = lttng_metadata_printf(session, - " description = \"Monotonic Clock\";\n" + " description = \"%s\";\n" " freq = %llu; /* Frequency, in Hz */\n" " /* clock value offset from Epoch is: offset * (1/freq) */\n" - " offset = %llu;\n" + " offset = %lld;\n" "};\n\n", + trace_clock_description(), (unsigned long long) trace_clock_freq(), - (unsigned long long) measure_clock_offset() + (long long) measure_clock_offset() ); if (ret) goto end; @@ -2032,20 +2679,23 @@ int _lttng_session_metadata_statedump(struct lttng_session *session) ret = lttng_metadata_printf(session, "typealias integer {\n" " size = 27; align = 1; signed = false;\n" - " map = clock.monotonic.value;\n" + " map = clock.%s.value;\n" "} := uint27_clock_monotonic_t;\n" "\n" "typealias integer {\n" " size = 32; align = %u; signed = false;\n" - " map = clock.monotonic.value;\n" + " map = clock.%s.value;\n" "} := uint32_clock_monotonic_t;\n" "\n" "typealias integer {\n" " size = 64; align = %u; signed = false;\n" - " map = clock.monotonic.value;\n" + " map = clock.%s.value;\n" "} := uint64_clock_monotonic_t;\n\n", + trace_clock_name(), lttng_alignof(uint32_t) * CHAR_BIT, - lttng_alignof(uint64_t) * CHAR_BIT + trace_clock_name(), + lttng_alignof(uint64_t) * CHAR_BIT, + trace_clock_name() ); if (ret) goto end; @@ -2114,6 +2764,133 @@ void lttng_transport_unregister(struct lttng_transport *transport) } EXPORT_SYMBOL_GPL(lttng_transport_unregister); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) + +enum cpuhp_state lttng_hp_prepare; +enum cpuhp_state lttng_hp_online; + +static int lttng_hotplug_prepare(unsigned int cpu, struct hlist_node *node) +{ + struct lttng_cpuhp_node *lttng_node; + + lttng_node = container_of(node, struct lttng_cpuhp_node, node); + switch (lttng_node->component) { + case LTTNG_RING_BUFFER_FRONTEND: + return 0; + case LTTNG_RING_BUFFER_BACKEND: + return lttng_cpuhp_rb_backend_prepare(cpu, lttng_node); + case LTTNG_RING_BUFFER_ITER: + return 0; + case LTTNG_CONTEXT_PERF_COUNTERS: + return 0; + default: + return -EINVAL; + } +} + +static int lttng_hotplug_dead(unsigned int cpu, struct hlist_node *node) +{ + struct lttng_cpuhp_node *lttng_node; + + lttng_node = container_of(node, struct lttng_cpuhp_node, node); + switch (lttng_node->component) { + case LTTNG_RING_BUFFER_FRONTEND: + return lttng_cpuhp_rb_frontend_dead(cpu, lttng_node); + case LTTNG_RING_BUFFER_BACKEND: + return 0; + case LTTNG_RING_BUFFER_ITER: + return 0; + case LTTNG_CONTEXT_PERF_COUNTERS: + return lttng_cpuhp_perf_counter_dead(cpu, lttng_node); + default: + return -EINVAL; + } +} + +static int lttng_hotplug_online(unsigned int cpu, struct hlist_node *node) +{ + struct lttng_cpuhp_node *lttng_node; + + lttng_node = container_of(node, struct lttng_cpuhp_node, node); + switch (lttng_node->component) { + case LTTNG_RING_BUFFER_FRONTEND: + return lttng_cpuhp_rb_frontend_online(cpu, lttng_node); + case LTTNG_RING_BUFFER_BACKEND: + return 0; + case LTTNG_RING_BUFFER_ITER: + return lttng_cpuhp_rb_iter_online(cpu, lttng_node); + case LTTNG_CONTEXT_PERF_COUNTERS: + return lttng_cpuhp_perf_counter_online(cpu, lttng_node); + default: + return -EINVAL; + } +} + +static int lttng_hotplug_offline(unsigned int cpu, struct hlist_node *node) +{ + struct lttng_cpuhp_node *lttng_node; + + lttng_node = container_of(node, struct lttng_cpuhp_node, node); + switch (lttng_node->component) { + case LTTNG_RING_BUFFER_FRONTEND: + return lttng_cpuhp_rb_frontend_offline(cpu, lttng_node); + case LTTNG_RING_BUFFER_BACKEND: + return 0; + case LTTNG_RING_BUFFER_ITER: + return 0; + case LTTNG_CONTEXT_PERF_COUNTERS: + return 0; + default: + return -EINVAL; + } +} + +static int __init lttng_init_cpu_hotplug(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_BP_PREPARE_DYN, "lttng:prepare", + lttng_hotplug_prepare, + lttng_hotplug_dead); + if (ret < 0) { + return ret; + } + lttng_hp_prepare = ret; + lttng_rb_set_hp_prepare(ret); + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "lttng:online", + lttng_hotplug_online, + lttng_hotplug_offline); + if (ret < 0) { + cpuhp_remove_multi_state(lttng_hp_prepare); + lttng_hp_prepare = 0; + return ret; + } + lttng_hp_online = ret; + lttng_rb_set_hp_online(ret); + + return 0; +} + +static void __exit lttng_exit_cpu_hotplug(void) +{ + lttng_rb_set_hp_online(0); + cpuhp_remove_multi_state(lttng_hp_online); + lttng_rb_set_hp_prepare(0); + cpuhp_remove_multi_state(lttng_hp_prepare); +} + +#else /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) */ +static int lttng_init_cpu_hotplug(void) +{ + return 0; +} +static void lttng_exit_cpu_hotplug(void) +{ +} +#endif /* #else #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,10,0)) */ + + static int __init lttng_events_init(void) { int ret; @@ -2122,6 +2899,12 @@ static int __init lttng_events_init(void) if (ret) return ret; ret = wrapper_get_pfnblock_flags_mask_init(); + if (ret) + return ret; + ret = wrapper_get_pageblock_flags_mask_init(); + if (ret) + return ret; + ret = lttng_probes_init(); if (ret) return ret; ret = lttng_context_init(); @@ -2141,8 +2924,29 @@ static int __init lttng_events_init(void) ret = lttng_logger_init(); if (ret) goto error_logger; + ret = lttng_init_cpu_hotplug(); + if (ret) + goto error_hotplug; + printk(KERN_NOTICE "LTTng: Loaded modules v%s.%s.%s%s (%s)%s%s\n", + __stringify(LTTNG_MODULES_MAJOR_VERSION), + __stringify(LTTNG_MODULES_MINOR_VERSION), + __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION), + LTTNG_MODULES_EXTRAVERSION, + LTTNG_VERSION_NAME, +#ifdef LTTNG_EXTRA_VERSION_GIT + LTTNG_EXTRA_VERSION_GIT[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_GIT, +#else + "", +#endif +#ifdef LTTNG_EXTRA_VERSION_NAME + LTTNG_EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_NAME); +#else + ""); +#endif return 0; +error_hotplug: + lttng_logger_exit(); error_logger: lttng_abi_exit(); error_abi: @@ -2151,6 +2955,22 @@ error_kmem: lttng_tracepoint_exit(); error_tp: lttng_context_exit(); + printk(KERN_NOTICE "LTTng: Failed to load modules v%s.%s.%s%s (%s)%s%s\n", + __stringify(LTTNG_MODULES_MAJOR_VERSION), + __stringify(LTTNG_MODULES_MINOR_VERSION), + __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION), + LTTNG_MODULES_EXTRAVERSION, + LTTNG_VERSION_NAME, +#ifdef LTTNG_EXTRA_VERSION_GIT + LTTNG_EXTRA_VERSION_GIT[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_GIT, +#else + "", +#endif +#ifdef LTTNG_EXTRA_VERSION_NAME + LTTNG_EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_NAME); +#else + ""); +#endif return ret; } @@ -2160,6 +2980,7 @@ static void __exit lttng_events_exit(void) { struct lttng_session *session, *tmpsession; + lttng_exit_cpu_hotplug(); lttng_logger_exit(); lttng_abi_exit(); list_for_each_entry_safe(session, tmpsession, &sessions, list) @@ -2167,13 +2988,36 @@ static void __exit lttng_events_exit(void) kmem_cache_destroy(event_cache); lttng_tracepoint_exit(); lttng_context_exit(); + printk(KERN_NOTICE "LTTng: Unloaded modules v%s.%s.%s%s (%s)%s%s\n", + __stringify(LTTNG_MODULES_MAJOR_VERSION), + __stringify(LTTNG_MODULES_MINOR_VERSION), + __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION), + LTTNG_MODULES_EXTRAVERSION, + LTTNG_VERSION_NAME, +#ifdef LTTNG_EXTRA_VERSION_GIT + LTTNG_EXTRA_VERSION_GIT[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_GIT, +#else + "", +#endif +#ifdef LTTNG_EXTRA_VERSION_NAME + LTTNG_EXTRA_VERSION_NAME[0] == '\0' ? "" : " - " LTTNG_EXTRA_VERSION_NAME); +#else + ""); +#endif } module_exit(lttng_events_exit); +#include "extra_version/patches.i" +#ifdef LTTNG_EXTRA_VERSION_GIT +MODULE_INFO(extra_version_git, LTTNG_EXTRA_VERSION_GIT); +#endif +#ifdef LTTNG_EXTRA_VERSION_NAME +MODULE_INFO(extra_version_name, LTTNG_EXTRA_VERSION_NAME); +#endif MODULE_LICENSE("GPL and additional rights"); MODULE_AUTHOR("Mathieu Desnoyers "); -MODULE_DESCRIPTION("LTTng Events"); +MODULE_DESCRIPTION("LTTng tracer"); MODULE_VERSION(__stringify(LTTNG_MODULES_MAJOR_VERSION) "." __stringify(LTTNG_MODULES_MINOR_VERSION) "." __stringify(LTTNG_MODULES_PATCHLEVEL_VERSION)