From: Mathieu Desnoyers Date: Tue, 7 Dec 2010 22:22:20 +0000 (-0500) Subject: LTTng modules now builds again X-Git-Tag: v2.0-pre1~204 X-Git-Url: http://git.lttng.org/?p=lttng-modules.git;a=commitdiff_plain;h=1c25284c690cd38b71789c4024089d28de21caea LTTng modules now builds again Signed-off-by: Mathieu Desnoyers --- diff --git a/Makefile b/Makefile index 6e4da6c9..109b8060 100644 --- a/Makefile +++ b/Makefile @@ -6,14 +6,14 @@ ifneq ($(KERNELRELEASE),) ifneq ($(CONFIG_TRACEPOINTS),) obj-m += ltt-core.o -obj-m += ltt-debugfs-abi.o -obj-m += ltt-events.o obj-m += ltt-ring-buffer-client-discard.o obj-m += ltt-ring-buffer-client-overwrite.o obj-m += ltt-relay.o -ltt-relay-objs := ltt-event-header.o ltt-serialize.o ltt-type-serializer.o +ltt-relay-objs := ltt-events.o ltt-event-header.o ltt-debugfs-abi.o +#ltt-type-serializer.o +#ltt-serialize.o #obj-m += ltt-marker-control.o #obj-m += ltt-trace-control.o #ltt-ascii.o diff --git a/discard/ltt-ascii.c b/discard/ltt-ascii.c new file mode 100644 index 00000000..b020fedb --- /dev/null +++ b/discard/ltt-ascii.c @@ -0,0 +1,583 @@ +/* + * LTT ascii binary buffer to ascii converter. + * + * Copyright 2008 - 2009 Lai Jiangshan (laijs@cn.fujitsu.com) + * Copyright 2009 - Mathieu Desnoyers mathieu.desnoyers@polymtl.ca + * + * Dual LGPL v2.1/GPL v2 license. + */ + +/* + * TODO + * + * Move to new switch behavior: Wait for data for the duration of the + * timer interval + safety, if none is coming, consider that no activity occured + * in the buffer. + * + * Fix case when having a text file open and destroying trace. + * + * - Automate periodical switch: + * + * The debugfs file "switch_timer" receives a timer period as parameter + * (e.g. echo 100 > switch_timer) to activate the timer per channel. This can + * also be accessed through the internal API _before the trace session starts_. + * This timer will insure that we periodically have subbuffers to read, and + * therefore that the merge-sort does not wait endlessly for a subbuffer. + * + * - If a channel is switched and read without data, make sure it is still + * considered afterward (not removed from the queue). + * + * - Create a ascii/tracename/ALL file to merge-sort all active channels. + * - Create a ascii/tracename/README file to contain the text output legend. + * - Remove leading zeroes from timestamps. + * - Enhance pretty-printing to make sure all types used for addesses output in + * the form 0xAB00000000 (not decimal). This is true for %p and 0x%...X. + * - Hotplug support + */ + + + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ltt-tracer.h" +#include "ltt-relay.h" +#include "ltt-relay-lockless.h" + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , a...) +#endif + +struct dentry *ltt_ascii_dir_dentry; +EXPORT_SYMBOL_GPL(ltt_ascii_dir_dentry); + +struct ltt_relay_iter; + +struct ltt_relay_cpu_iter { + /* cpu buffer information */ + struct ltt_chanbuf *buf; + struct ltt_relay_iter *iter; + int sb_ref; /* holding a reference to a subbuffer */ + long read_sb_offset; /* offset of the subbuffer read */ + + /* current event information */ + struct ltt_subbuffer_header *header; + long hdr_offset; /* event header offset */ + long payload_offset; /* event payload offset */ + u64 tsc; /* full 64-bits timestamp value */ + u32 data_size; + u16 chID; /* channel ID, const */ + u16 eID; +}; + +struct ltt_relay_iter { + struct ltt_relay_cpu_iter iter_cpu[NR_CPUS]; + struct ltt_chan *chan; + loff_t pos; + int cpu; + int nr_refs; +}; + +/* + * offset of 0 in subbuffer means "subbuf size" (filled subbuffer). + */ +static int is_subbuffer_offset_end(struct ltt_relay_cpu_iter *citer, + long offset) +{ + struct ltt_chan *chan = container_of(citer->buf->a.chan, + struct ltt_chan, a); + long sub_offset = SUBBUF_OFFSET(offset - 1, chan) + 1; + + return (sub_offset <= citer->header->data_size); +} + +static u64 calculate_tsc(u64 pre_tsc, u64 read_tsc, unsigned int rflags) +{ + u64 new_tsc = read_tsc; + + if (rflags != LTT_RFLAG_ID_SIZE_TSC) { + BUG_ON(read_tsc >> LTT_TSC_BITS); + + new_tsc = (pre_tsc & ~LTT_TSC_MASK) + read_tsc; + if (read_tsc < (pre_tsc & LTT_TSC_MASK)) + new_tsc += 1UL << LTT_TSC_BITS; + } + + return new_tsc; +} + +/* + * calculate payload offset */ +static inline long calculate_payload_offset(long offset, u16 chID, u16 eID) +{ + const char *fmt; + + if (!ltt_get_alignment()) + return offset; + + fmt = marker_get_fmt_from_id(chID, eID); + BUG_ON(!fmt); + + return offset + ltt_fmt_largest_align(offset, fmt); +} + +static void update_new_event(struct ltt_relay_cpu_iter *citer, long hdr_offset) +{ + u64 read_tsc; + unsigned int rflags; + long tmp_offset; + + WARN_ON_ONCE(hdr_offset != citer->hdr_offset); + + tmp_offset = ltt_read_event_header(&citer->buf->a, hdr_offset, + &read_tsc, &citer->data_size, + &citer->eID, &rflags); + citer->payload_offset = calculate_payload_offset(tmp_offset, + citer->chID, + citer->eID); + + citer->tsc = calculate_tsc(citer->tsc, read_tsc, rflags); +} + +static void update_event_size(struct ltt_relay_cpu_iter *citer, long hdr_offset) +{ + char output[1]; + const char *fmt; + size_t data_size; + + if (citer->data_size != INT_MAX) + return; + + fmt = marker_get_fmt_from_id(citer->chID, citer->eID); + BUG_ON(!fmt); + ltt_serialize_printf(citer->buf, citer->payload_offset, + &data_size, output, 0, fmt); + citer->data_size = data_size; +} + +static void update_cpu_iter(struct ltt_relay_cpu_iter *citer, long hdr_offset) +{ + if (unlikely((!citer->sb_ref) + || is_subbuffer_offset_end(citer, hdr_offset))) { + citer->header = NULL; + return; + } + update_new_event(citer, hdr_offset); + update_event_size(citer, hdr_offset); +} + +/* + * returns 0 if we get a subbuffer reference. + * else, the buffer has not available data, try again later. + */ +static int subbuffer_start(struct ltt_relay_cpu_iter *citer, long *offset) +{ + int ret; + struct ltt_relay_iter *iter = citer->iter; + + ret = ltt_chanbuf_get_subbuf(citer->buf, offset); + if (!ret) { + citer->header = ltt_relay_read_offset_address(&citer->buf->a, + *offset); + citer->hdr_offset = (*offset) + ltt_sb_header_size(); + citer->tsc = citer->header->cycle_count_begin; + iter->nr_refs++; + citer->sb_ref = 1; + return 0; + } else { + if (ltt_chanbuf_is_finalized(citer->buf)) + return -ENODATA; + else + return -EAGAIN; + } +} + +static void subbuffer_stop(struct ltt_relay_cpu_iter *citer, + long offset) +{ + int ret; + struct ltt_relay_iter *iter = citer->iter; + + WARN_ON_ONCE(!citer->sb_ref); + ret = ltt_chanbuf_put_subbuf(citer->buf, offset); + WARN_ON_ONCE(ret); + citer->sb_ref = 0; + iter->nr_refs--; +} + +static void ltt_relay_advance_cpu_iter(struct ltt_relay_cpu_iter *citer) +{ + long old_offset = citer->payload_offset; + long new_offset = citer->payload_offset; + int ret; + + /* find that whether we read all data in this subbuffer */ + if (unlikely(is_subbuffer_offset_end(citer, + old_offset + citer->data_size))) { + DEBUGP(KERN_DEBUG "LTT ASCII stop cpu %d offset %lX\n", + citer->buf->a.cpu, citer->read_sb_offset); + subbuffer_stop(citer, citer->read_sb_offset); + for (;;) { + ret = subbuffer_start(citer, &citer->read_sb_offset); + DEBUGP(KERN_DEBUG + "LTT ASCII start cpu %d ret %d offset %lX\n", + citer->buf->a.cpu, ret, citer->read_sb_offset); + if (!ret || ret == -ENODATA) { + break; /* got data, or finalized */ + } else { /* -EAGAIN */ + if (signal_pending(current)) + break; + schedule_timeout_interruptible(1); + //TODO: check for no-data delay. take ref. break + } + } + } else { + new_offset += citer->data_size; + citer->hdr_offset = new_offset + ltt_align(new_offset, sizeof(struct ltt_event_header)); + DEBUGP(KERN_DEBUG + "LTT ASCII old_offset %lX new_offset %lX cpu %d\n", + old_offset, new_offset, citer->buf->a.cpu); + } + + update_cpu_iter(citer, citer->hdr_offset); +} + +static int cpu_iter_eof(struct ltt_relay_cpu_iter *citer) +{ + return !citer->sb_ref; +} + +static int ltt_relay_iter_eof(struct ltt_relay_iter *iter) +{ + return iter->nr_refs == 0; +} + +static void ltt_relay_advance_iter(struct ltt_relay_iter *iter) +{ + int i; + struct ltt_relay_cpu_iter *curr, *min = NULL; + iter->cpu = -1; + + /* + * find the event with the minimum tsc. + * TODO: use min-heep for 4096CPUS + */ + for_each_possible_cpu(i) { + curr = &iter->iter_cpu[i]; + + if (!curr->buf->a.allocated || !curr->header) + continue; + + if (cpu_iter_eof(curr)) + continue; + + if (!min || curr->tsc < min->tsc) { + min = curr; + iter->cpu = i; + } + } + + /* update cpu_iter for next ltt_relay_advance_iter() */ + if (min) + ltt_relay_advance_cpu_iter(min); +} + +static void *ascii_next(struct seq_file *m, void *v, loff_t *ppos) +{ + struct ltt_relay_iter *iter = m->private; + + WARN_ON_ONCE(!iter->nr_refs); + BUG_ON(v != iter); + + ltt_relay_advance_iter(iter); + return (ltt_relay_iter_eof(iter) || signal_pending(current)) + ? NULL : iter; +} + +static void *ascii_start(struct seq_file *m, loff_t *ppos) +{ + struct ltt_relay_iter *iter = m->private; + + ltt_relay_advance_iter(iter); + return (ltt_relay_iter_eof(iter) || signal_pending(current)) + ? NULL : iter; +} + +static void ascii_stop(struct seq_file *m, void *v) +{ +} + +static +int seq_serialize(struct seq_file *m, struct ltt_chanbuf *buf, + size_t buf_offset, const char *fmt, size_t *data_size) +{ + int len; + + if (m->count < m->size) { + len = ltt_serialize_printf(buf, buf_offset, data_size, + m->buf + m->count, + m->size - m->count, fmt); + if (m->count + len < m->size) { + m->count += len; + return 0; + } + } + + m->count = m->size; + return -1; +} + +static int ascii_show(struct seq_file *m, void *v) +{ + struct ltt_relay_iter *iter = v; + struct ltt_relay_cpu_iter *citer; + const char *name; + const char *fmt; + unsigned long long tsc; + size_t data_size; + + if (iter->cpu == -1) + return 0; + + citer = &iter->iter_cpu[iter->cpu]; + WARN_ON_ONCE(!citer->sb_ref); + /* + * Nothing to show, we are at the end of the last subbuffer currently + * having data. + */ + if (!citer->header) + return 0; + + tsc = citer->tsc; + name = marker_get_name_from_id(citer->chID, citer->eID); + fmt = marker_get_fmt_from_id(citer->chID, citer->eID); + + if (!name || !fmt) + return 0; + + seq_printf(m, "event:%16.16s: cpu:%2d time:%20.20llu ", + name, iter->cpu, tsc); + seq_serialize(m, citer->buf, citer->payload_offset, fmt, &data_size); + seq_puts(m, "\n"); + if (citer->data_size == INT_MAX) + citer->data_size = data_size; + + return 0; +} + +static struct seq_operations ascii_seq_ops = { + .start = ascii_start, + .next = ascii_next, + .stop = ascii_stop, + .show = ascii_show, +}; + +/* FIXME : cpu hotplug support */ +static int ltt_relay_iter_open_channel(struct ltt_relay_iter *iter, + struct ltt_chan *chan) +{ + int i, ret; + u16 chID = ltt_channels_get_index_from_name(chan->a.filename); + + /* we don't need lock relay_channels_mutex */ + for_each_possible_cpu(i) { + struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i]; + + citer->buf = per_cpu_ptr(chan->a.buf, i); + if (!citer->buf->a.allocated) + continue; + + citer->iter = iter; /* easy lazy parent info */ + citer->chID = chID; + + ret = ltt_chanbuf_open_read(citer->buf); + if (ret) { + /* Failed to open a percpu buffer, close everything. */ + citer->buf = NULL; + goto error; + } + + for (;;) { + ret = subbuffer_start(citer, + &citer->read_sb_offset); + DEBUGP(KERN_DEBUG + "LTT ASCII open start " + "cpu %d ret %d offset %lX\n", + citer->buf->a.cpu, ret, citer->read_sb_offset); + if (!ret || ret == -ENODATA) { + break; /* got data, or finalized */ + } else { /* -EAGAIN */ + if (signal_pending(current)) + break; + schedule_timeout_interruptible(1); + } + } + update_cpu_iter(citer, citer->hdr_offset); + } + if (!iter->nr_refs) { + ret = -ENODATA; + goto error; + } + + return 0; + +error: + for_each_possible_cpu(i) { + struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i]; + + if (!citer->buf) + break; + + if (citer->buf->a.allocated) + ltt_chanbuf_release_read(citer->buf); + } + return ret; +} + +/* FIXME : cpu hotplug support */ +static int ltt_relay_iter_release_channel(struct ltt_relay_iter *iter) +{ + int i; + + for_each_possible_cpu(i) { + struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i]; + + if (citer->sb_ref) { + WARN_ON_ONCE(!citer->buf->a.allocated); + DEBUGP(KERN_DEBUG + "LTT ASCII release stop cpu %d offset %lX\n", + citer->buf->a.cpu, citer->read_sb_offset); + subbuffer_stop(&iter->iter_cpu[i], + citer->read_sb_offset); + } + if (citer->buf->a.allocated) + ltt_chanbuf_release_read(citer->buf); + } + WARN_ON_ONCE(iter->nr_refs); + return 0; +} + +static int ltt_relay_ascii_open(struct inode *inode, struct file *file) +{ + int ret; + struct ltt_chan *chan = inode->i_private; + struct ltt_relay_iter *iter = kzalloc(sizeof(*iter), GFP_KERNEL); + if (!iter) + return -ENOMEM; + + iter->chan = chan; + ret = ltt_relay_iter_open_channel(iter, chan); + if (ret) + goto error_free_alloc; + + ret = seq_open(file, &ascii_seq_ops); + if (ret) + goto error_release_channel; + ((struct seq_file *)file->private_data)->private = iter; + return 0; + +error_release_channel: + ltt_relay_iter_release_channel(iter); +error_free_alloc: + kfree(iter); + return ret; +} + +static int ltt_relay_ascii_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct ltt_relay_iter *iter = seq->private; + + ltt_relay_iter_release_channel(iter); + kfree(iter); + return 0; +} + +static struct file_operations ltt_ascii_fops = +{ + .read = seq_read, + .open = ltt_relay_ascii_open, + .release = ltt_relay_ascii_release, + .llseek = no_llseek, + .owner = THIS_MODULE, +}; + +int ltt_ascii_create(struct ltt_chan *chan) +{ + struct dentry *dentry; + + dentry = debugfs_create_file(chan->a.filename, + S_IRUSR | S_IRGRP, + chan->a.trace->dentry.ascii_root, + chan, <t_ascii_fops); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + if (!dentry) + return -EEXIST; + + chan->a.ascii_dentry = dentry; + dentry->d_inode->i_private = chan; + return 0; +} +EXPORT_SYMBOL_GPL(ltt_ascii_create); + +void ltt_ascii_remove(struct ltt_chan *chan) +{ + struct dentry *dentry; + + dentry = dget(chan->a.ascii_dentry); + debugfs_remove(dentry); + /* TODO: wait / wakeup instead */ + /* + * Wait for every reference to the dentry to be gone, + * except us. + */ + while (atomic_read(&dentry->d_count) != 1) + msleep(100); + dput(dentry); +} +EXPORT_SYMBOL_GPL(ltt_ascii_remove); + +int ltt_ascii_create_dir(struct ltt_trace *new_trace) +{ + new_trace->dentry.ascii_root = debugfs_create_dir(new_trace->trace_name, + ltt_ascii_dir_dentry); + if (!new_trace->dentry.ascii_root) + return -EEXIST; + return 0; +} +EXPORT_SYMBOL_GPL(ltt_ascii_create_dir); + +void ltt_ascii_remove_dir(struct ltt_trace *trace) +{ + debugfs_remove(trace->dentry.ascii_root); +} +EXPORT_SYMBOL_GPL(ltt_ascii_remove_dir); + +__init int ltt_ascii_init(void) +{ + ltt_ascii_dir_dentry = debugfs_create_dir(LTT_ASCII, get_ltt_root()); + + return ltt_ascii_dir_dentry ? 0 : -EFAULT; +} + +__exit void ltt_ascii_exit(void) +{ + debugfs_remove(ltt_ascii_dir_dentry); + put_ltt_root(); +} + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Lai Jiangshan@FNST and Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Ascii Converter"); diff --git a/discard/ltt-channels.c b/discard/ltt-channels.c new file mode 100644 index 00000000..962c81a8 --- /dev/null +++ b/discard/ltt-channels.c @@ -0,0 +1,397 @@ +/* + * ltt/ltt-channels.c + * + * (C) Copyright 2008 - Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) + * + * LTTng channel management. + * + * Author: + * Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include "ltt-channels.h" + +/* + * ltt_channel_mutex may be nested inside the LTT trace mutex. + * ltt_channel_mutex mutex may be nested inside markers mutex. + */ +static DEFINE_MUTEX(ltt_channel_mutex); +static LIST_HEAD(ltt_channels); +/* + * Index of next channel in array. Makes sure that as long as a trace channel is + * allocated, no array index will be re-used when a channel is freed and then + * another channel is allocated. This index is cleared and the array indexeds + * get reassigned when the index_kref goes back to 0, which indicates that no + * more trace channels are allocated. + */ +static unsigned int free_index; +/* index_kref is protected by both ltt_channel_mutex and lock_markers */ +static struct kref index_kref; /* Keeps track of allocated trace channels */ + +static struct ltt_channel_setting *lookup_channel(const char *name) +{ + struct ltt_channel_setting *iter; + + list_for_each_entry(iter, <t_channels, list) + if (strcmp(name, iter->name) == 0) + return iter; + return NULL; +} + +/* + * Must be called when channel refcount falls to 0 _and_ also when the last + * trace is freed. This function is responsible for compacting the channel and + * event IDs when no users are active. + * + * Called with lock_markers() and channels mutex held. + */ +static void release_channel_setting(struct kref *kref) +{ + struct ltt_channel_setting *setting = container_of(kref, + struct ltt_channel_setting, kref); + struct ltt_channel_setting *iter; + + if (atomic_read(&index_kref.refcount) == 0 + && atomic_read(&setting->kref.refcount) == 0) { + list_del(&setting->list); + kfree(setting); + + free_index = 0; + list_for_each_entry(iter, <t_channels, list) { + iter->index = free_index++; + iter->free_event_id = 0; + } + } +} + +/* + * Perform channel index compaction when the last trace channel is freed. + * + * Called with lock_markers() and channels mutex held. + */ +static void release_trace_channel(struct kref *kref) +{ + struct ltt_channel_setting *iter, *n; + + list_for_each_entry_safe(iter, n, <t_channels, list) + release_channel_setting(&iter->kref); + if (atomic_read(&index_kref.refcount) == 0) + markers_compact_event_ids(); +} + +/* + * ltt_channel_trace_ref : Is there an existing trace session ? + * + * Must be called with lock_markers() held. + */ +int ltt_channels_trace_ref(void) +{ + return !!atomic_read(&index_kref.refcount); +} +EXPORT_SYMBOL_GPL(ltt_channels_trace_ref); + +/** + * ltt_channels_register - Register a trace channel. + * @name: channel name + * + * Uses refcounting. + */ +int ltt_channels_register(const char *name) +{ + struct ltt_channel_setting *setting; + int ret = 0; + + mutex_lock(<t_channel_mutex); + setting = lookup_channel(name); + if (setting) { + if (atomic_read(&setting->kref.refcount) == 0) + goto init_kref; + else { + kref_get(&setting->kref); + goto end; + } + } + setting = kzalloc(sizeof(*setting), GFP_KERNEL); + if (!setting) { + ret = -ENOMEM; + goto end; + } + list_add(&setting->list, <t_channels); + strncpy(setting->name, name, PATH_MAX-1); + setting->index = free_index++; +init_kref: + kref_init(&setting->kref); +end: + mutex_unlock(<t_channel_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_channels_register); + +/** + * ltt_channels_unregister - Unregister a trace channel. + * @name: channel name + * @compacting: performing compaction + * + * Must be called with markers mutex held. + */ +int ltt_channels_unregister(const char *name, int compacting) +{ + struct ltt_channel_setting *setting; + int ret = 0; + + if (!compacting) + mutex_lock(<t_channel_mutex); + setting = lookup_channel(name); + if (!setting || atomic_read(&setting->kref.refcount) == 0) { + ret = -ENOENT; + goto end; + } + kref_put(&setting->kref, release_channel_setting); + if (!compacting && atomic_read(&index_kref.refcount) == 0) + markers_compact_event_ids(); +end: + if (!compacting) + mutex_unlock(<t_channel_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_channels_unregister); + +/** + * ltt_channels_set_default - Set channel default behavior. + * @name: default channel name + * @sb_size: size of the subbuffers + * @n_sb: number of subbuffers + */ +int ltt_channels_set_default(const char *name, + unsigned int sb_size, + unsigned int n_sb) +{ + struct ltt_channel_setting *setting; + int ret = 0; + + mutex_lock(<t_channel_mutex); + setting = lookup_channel(name); + if (!setting || atomic_read(&setting->kref.refcount) == 0) { + ret = -ENOENT; + goto end; + } + setting->sb_size = sb_size; + setting->n_sb = n_sb; +end: + mutex_unlock(<t_channel_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_channels_set_default); + +/** + * ltt_channels_get_name_from_index - get channel name from channel index + * @index: channel index + * + * Allows to lookup the channel name given its index. Done to keep the name + * information outside of each trace channel instance. + */ +const char *ltt_channels_get_name_from_index(unsigned int index) +{ + struct ltt_channel_setting *iter; + + list_for_each_entry(iter, <t_channels, list) + if (iter->index == index && atomic_read(&iter->kref.refcount)) + return iter->name; + return NULL; +} +EXPORT_SYMBOL_GPL(ltt_channels_get_name_from_index); + +static struct ltt_channel_setting * +ltt_channels_get_setting_from_name(const char *name) +{ + struct ltt_channel_setting *iter; + + list_for_each_entry(iter, <t_channels, list) + if (!strcmp(iter->name, name) + && atomic_read(&iter->kref.refcount)) + return iter; + return NULL; +} + +/** + * ltt_channels_get_index_from_name - get channel index from channel name + * @name: channel name + * + * Allows to lookup the channel index given its name. Done to keep the name + * information outside of each trace channel instance. + * Returns -1 if not found. + */ +int ltt_channels_get_index_from_name(const char *name) +{ + struct ltt_channel_setting *setting; + + setting = ltt_channels_get_setting_from_name(name); + if (setting) + return setting->index; + else + return -1; +} +EXPORT_SYMBOL_GPL(ltt_channels_get_index_from_name); + +/** + * ltt_channels_trace_alloc - Allocate channel structures for a trace + * + * Use the current channel list to allocate the channels for a trace. + * Called with trace lock held. Does not perform the trace buffer allocation, + * because we must let the user overwrite specific channel sizes. + */ +int ltt_channels_trace_alloc(struct ltt_trace *trace, int overwrite) +{ + struct channel **chan = NULL; + struct ltt_channel_setting *chans, *iter; + int ret = 0; + + lock_markers(); + mutex_lock(<t_channel_mutex); + if (!free_index) + goto end; + if (!atomic_read(&index_kref.refcount)) + kref_init(&index_kref); + else + kref_get(&index_kref); + trace->nr_channels = free_index; + chan = kzalloc(sizeof(struct channel *) * free_index, GFP_KERNEL); + if (!chan) + goto end; + chans = kzalloc(sizeof(struct ltt_channel_setting) * free_index, + GFP_KERNEL); + if (!chan_settings) + goto free_chan; + list_for_each_entry(iter, <t_channels, list) { + if (!atomic_read(&iter->kref.refcount)) + continue; + chans[iter->index].sb_size = iter->sb_size; + chans[iter->index].n_sb = iter->n_sb; + chans[iter->index].overwrite = overwrite; + strncpy(chans[iter->index].filename, iter->name, + NAME_MAX - 1); + chans[iter->index].switch_timer_interval = 0; + chans[iter->index].read_timer_interval = LTT_READ_TIMER_INTERVAL; + } + trace->channels = chan; + trace->settings = chans; +end: + mutex_unlock(<t_channel_mutex); + unlock_markers(); + return ret; + +free_chan: + kfree(chan); + ret = -ENOMEM; + goto end; +} +EXPORT_SYMBOL_GPL(ltt_channels_trace_alloc); + +/** + * ltt_channels_trace_free - Free one trace's channels + * @channels: channels to free + * + * Called with trace lock held. The actual channel buffers must be freed before + * this function is called. + */ +void ltt_channels_trace_free(struct ltt_trace *trace) +{ + lock_markers(); + mutex_lock(<t_channel_mutex); + kfree(trace->settings); + kfree(trace->channels); + kref_put(&index_kref, release_trace_channel); + mutex_unlock(<t_channel_mutex); + unlock_markers(); + marker_update_probes(); +} +EXPORT_SYMBOL_GPL(ltt_channels_trace_free); + +/** + * ltt_channels_trace_set_timer - set switch timer + * @channel: channel + * @interval: interval of timer interrupt, in jiffies. 0 inhibits timer. + */ + +void ltt_channels_trace_set_timer(struct ltt_chan *chan, + unsigned long interval) +{ + chan->switch_timer_interval = interval; +} +EXPORT_SYMBOL_GPL(ltt_channels_trace_set_timer); + +/** + * _ltt_channels_get_event_id - get next event ID for a marker + * @channel: channel name + * @name: event name + * + * Returns a unique event ID (for this channel) or < 0 on error. + * Must be called with channels mutex held. + */ +int _ltt_channels_get_event_id(const char *channel, const char *name) +{ + struct ltt_channel_setting *setting; + int ret; + + setting = ltt_channels_get_setting_from_name(channel); + if (!setting) { + ret = -ENOENT; + goto end; + } + if (strcmp(channel, "metadata") == 0) { + if (strcmp(name, "core_marker_id") == 0) + ret = 0; + else if (strcmp(name, "core_marker_format") == 0) + ret = 1; + else + ret = -ENOENT; + goto end; + } + if (setting->free_event_id == EVENTS_PER_CHANNEL - 1) { + ret = -ENOSPC; + goto end; + } + ret = setting->free_event_id++; +end: + return ret; +} + +/** + * ltt_channels_get_event_id - get next event ID for a marker + * @channel: channel name + * @name: event name + * + * Returns a unique event ID (for this channel) or < 0 on error. + */ +int ltt_channels_get_event_id(const char *channel, const char *name) +{ + int ret; + + mutex_lock(<t_channel_mutex); + ret = _ltt_channels_get_event_id(channel, name); + mutex_unlock(<t_channel_mutex); + return ret; +} + +/** + * ltt_channels_reset_event_ids - reset event IDs at compaction + * + * Called with lock marker and channel mutex held. + */ +void _ltt_channels_reset_event_ids(void) +{ + struct ltt_channel_setting *iter; + + list_for_each_entry(iter, <t_channels, list) + iter->free_event_id = 0; +} + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Channel Management"); diff --git a/discard/ltt-channels.h b/discard/ltt-channels.h new file mode 100644 index 00000000..9eb604ba --- /dev/null +++ b/discard/ltt-channels.h @@ -0,0 +1,83 @@ +#ifndef _LTT_CHANNELS_H +#define _LTT_CHANNELS_H + +/* + * Copyright (C) 2008 Mathieu Desnoyers (mathieu.desnoyers@polymtl.ca) + * + * Dynamic tracer channel allocation. + + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include + +#define EVENTS_PER_CHANNEL 65536 + +#define LTT_READ_TIMER_INTERVAL 10000 /* us */ + +/* + * Forward declaration of locking-specific per-cpu buffer structure. + */ +struct ltt_trace; +struct ltt_serialize_closure; +struct ltt_probe_private_data; + +/* Serialization callback '%k' */ +typedef size_t (*ltt_serialize_cb)(struct ltt_chanbuf *buf, size_t buf_offset, + struct ltt_serialize_closure *closure, + void *serialize_private, + unsigned int stack_pos_ctx, + int *largest_align, + const char *fmt, va_list *args); + +struct ltt_probe_private_data { + struct ltt_trace *trace; /* + * Target trace, for metadata + * or statedump. + */ + ltt_serialize_cb serializer; /* + * Serialization function override. + */ + void *serialize_private; /* + * Private data for serialization + * functions. + */ +}; + +struct ltt_channel_setting { + unsigned int sb_size; + unsigned int n_sb; + int overwrite; + unsigned long switch_timer_interval; + unsigned long read_timer_interval; + struct kref kref; /* Number of references to structure content */ + struct list_head list; + unsigned int index; /* index of channel in trace channel array */ + u16 free_event_id; /* Next event ID to allocate */ + char name[PATH_MAX]; +}; + +int ltt_channels_register(const char *name); +int ltt_channels_unregister(const char *name, int compacting); +int ltt_channels_set_default(const char *name, + unsigned int subbuf_size, + unsigned int subbuf_cnt); +const char *ltt_channels_get_name_from_index(unsigned int index); +int ltt_channels_get_index_from_name(const char *name); +int ltt_channels_trace_ref(void); +struct ltt_chan *ltt_channels_trace_alloc(unsigned int *nr_channels, + int overwrite, int active); +void ltt_channels_trace_free(struct ltt_chan *channels, + unsigned int nr_channels); +void ltt_channels_trace_set_timer(struct ltt_channel_setting *chan, + unsigned long interval); + +int _ltt_channels_get_event_id(const char *channel, const char *name); +int ltt_channels_get_event_id(const char *channel, const char *name); +void _ltt_channels_reset_event_ids(void); + +#endif /* _LTT_CHANNELS_H */ diff --git a/discard/ltt-marker-control.c b/discard/ltt-marker-control.c new file mode 100644 index 00000000..2db5c4e9 --- /dev/null +++ b/discard/ltt-marker-control.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2007 Mathieu Desnoyers + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ltt-tracer.h" + +#define DEFAULT_CHANNEL "cpu" +#define DEFAULT_PROBE "default" + +LIST_HEAD(probes_list); + +/* + * Mutex protecting the probe slab cache. + * Nests inside the traces mutex. + */ +DEFINE_MUTEX(probes_mutex); + +struct ltt_available_probe default_probe = { + .name = "default", + .format = NULL, + .probe_func = ltt_vtrace, + .callbacks[0] = ltt_serialize_data, +}; + +static struct kmem_cache *markers_loaded_cachep; +static LIST_HEAD(markers_loaded_list); +/* + * List sorted by name strcmp order. + */ +static LIST_HEAD(probes_registered_list); + +static struct ltt_available_probe *get_probe_from_name(const char *pname) +{ + struct ltt_available_probe *iter; + int comparison, found = 0; + + if (!pname) + pname = DEFAULT_PROBE; + list_for_each_entry(iter, &probes_registered_list, node) { + comparison = strcmp(pname, iter->name); + if (!comparison) + found = 1; + if (comparison <= 0) + break; + } + if (found) + return iter; + else + return NULL; +} + +int ltt_probe_register(struct ltt_available_probe *pdata) +{ + int ret = 0; + int comparison; + struct ltt_available_probe *iter; + + mutex_lock(&probes_mutex); + list_for_each_entry_reverse(iter, &probes_registered_list, node) { + comparison = strcmp(pdata->name, iter->name); + if (!comparison) { + ret = -EBUSY; + goto end; + } else if (comparison > 0) { + /* We belong to the location right after iter. */ + list_add(&pdata->node, &iter->node); + goto end; + } + } + /* Should be added at the head of the list */ + list_add(&pdata->node, &probes_registered_list); +end: + mutex_unlock(&probes_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_probe_register); + +/* + * Called when a probe does not want to be called anymore. + */ +int ltt_probe_unregister(struct ltt_available_probe *pdata) +{ + int ret = 0; + struct ltt_active_marker *amark, *tmp; + + mutex_lock(&probes_mutex); + list_for_each_entry_safe(amark, tmp, &markers_loaded_list, node) { + if (amark->probe == pdata) { + ret = marker_probe_unregister_private_data( + pdata->probe_func, amark); + if (ret) + goto end; + list_del(&amark->node); + kmem_cache_free(markers_loaded_cachep, amark); + } + } + list_del(&pdata->node); +end: + mutex_unlock(&probes_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_probe_unregister); + +/* + * Connect marker "mname" to probe "pname". + * Only allow _only_ probe instance to be connected to a marker. + */ +int ltt_marker_connect(const char *channel, const char *mname, + const char *pname) +{ + int ret; + struct ltt_active_marker *pdata; + struct ltt_available_probe *probe; + + ltt_lock_traces(); + mutex_lock(&probes_mutex); + probe = get_probe_from_name(pname); + if (!probe) { + ret = -ENOENT; + goto end; + } + pdata = marker_get_private_data(channel, mname, probe->probe_func, 0); + if (pdata && !IS_ERR(pdata)) { + ret = -EEXIST; + goto end; + } + pdata = kmem_cache_zalloc(markers_loaded_cachep, GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto end; + } + pdata->probe = probe; + /* + * ID has priority over channel in case of conflict. + */ + ret = marker_probe_register(channel, mname, NULL, + probe->probe_func, pdata); + if (ret) + kmem_cache_free(markers_loaded_cachep, pdata); + else + list_add(&pdata->node, &markers_loaded_list); +end: + mutex_unlock(&probes_mutex); + ltt_unlock_traces(); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_marker_connect); + +/* + * Disconnect marker "mname", probe "pname". + */ +int ltt_marker_disconnect(const char *channel, const char *mname, + const char *pname) +{ + struct ltt_active_marker *pdata; + struct ltt_available_probe *probe; + int ret = 0; + + mutex_lock(&probes_mutex); + probe = get_probe_from_name(pname); + if (!probe) { + ret = -ENOENT; + goto end; + } + pdata = marker_get_private_data(channel, mname, probe->probe_func, 0); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto end; + } else if (!pdata) { + /* + * Not registered by us. + */ + ret = -EPERM; + goto end; + } + ret = marker_probe_unregister(channel, mname, probe->probe_func, pdata); + if (ret) + goto end; + else { + list_del(&pdata->node); + kmem_cache_free(markers_loaded_cachep, pdata); + } +end: + mutex_unlock(&probes_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_marker_disconnect); + +static void disconnect_all_markers(void) +{ + struct ltt_active_marker *pdata, *tmp; + + list_for_each_entry_safe(pdata, tmp, &markers_loaded_list, node) { + marker_probe_unregister_private_data(pdata->probe->probe_func, + pdata); + list_del(&pdata->node); + kmem_cache_free(markers_loaded_cachep, pdata); + } +} + +static int __init marker_control_init(void) +{ + int ret; + + markers_loaded_cachep = KMEM_CACHE(ltt_active_marker, 0); + + ret = ltt_probe_register(&default_probe); + BUG_ON(ret); + ret = ltt_marker_connect("metadata", "core_marker_format", + DEFAULT_PROBE); + BUG_ON(ret); + ret = ltt_marker_connect("metadata", "core_marker_id", DEFAULT_PROBE); + BUG_ON(ret); + + return 0; +} +module_init(marker_control_init); + +static void __exit marker_control_exit(void) +{ + int ret; + + ret = ltt_marker_disconnect("metadata", "core_marker_format", + DEFAULT_PROBE); + BUG_ON(ret); + ret = ltt_marker_disconnect("metadata", "core_marker_id", + DEFAULT_PROBE); + BUG_ON(ret); + ret = ltt_probe_unregister(&default_probe); + BUG_ON(ret); + disconnect_all_markers(); + kmem_cache_destroy(markers_loaded_cachep); + marker_synchronize_unregister(); +} +module_exit(marker_control_exit); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Marker Control"); diff --git a/discard/ltt-serialize.c b/discard/ltt-serialize.c new file mode 100644 index 00000000..50d7132c --- /dev/null +++ b/discard/ltt-serialize.c @@ -0,0 +1,968 @@ +/* + * LTTng serializing code. + * + * Copyright Mathieu Desnoyers, March 2007. + * + * Dual LGPL v2.1/GPL v2 license. + * + * See this discussion about weirdness about passing va_list and then va_list to + * functions. (related to array argument passing). va_list seems to be + * implemented as an array on x86_64, but not on i386... This is why we pass a + * va_list * to ltt_vtrace. + */ + +#include +#include +#include +#include + +#include "ltt-tracer.h" + +enum ltt_type { + LTT_TYPE_SIGNED_INT, + LTT_TYPE_UNSIGNED_INT, + LTT_TYPE_STRING, + LTT_TYPE_NONE, +}; + +#define LTT_ATTRIBUTE_NETWORK_BYTE_ORDER (1<<1) + +/* + * Stack used to keep track of string length at size calculation, passed to + * string copy to handle racy input string updates. + * Can be used by any context; this is ensured by putting the stack position + * back to its original position after using it. + */ +#define TRACER_STACK_LEN (PAGE_SIZE / sizeof(unsigned long)) +static DEFINE_PER_CPU(unsigned long [TRACER_STACK_LEN], + tracer_stack); +static DEFINE_PER_CPU(unsigned int, tracer_stack_pos); + +/* + * Inspired from vsnprintf + * + * The serialization format string supports the basic printf format strings. + * In addition, it defines new formats that can be used to serialize more + * complex/non portable data structures. + * + * Typical use: + * + * field_name %ctype + * field_name #tracetype %ctype + * field_name #tracetype %ctype1 %ctype2 ... + * + * A conversion is performed between format string types supported by GCC and + * the trace type requested. GCC type is used to perform type checking on format + * strings. Trace type is used to specify the exact binary representation + * in the trace. A mapping is done between one or more GCC types to one trace + * type. Sign extension, if required by the conversion, is performed following + * the trace type. + * + * If a gcc format is not declared with a trace format, the gcc format is + * also used as binary representation in the trace. + * + * Strings are supported with %s. + * A single tracetype (sequence) can take multiple c types as parameter. + * + * c types: + * + * see printf(3). + * + * Note: to write a uint32_t in a trace, the following expression is recommended + * si it can be portable: + * + * ("#4u%lu", (unsigned long)var) + * + * trace types: + * + * Serialization specific formats : + * + * Fixed size integers + * #1u writes uint8_t + * #2u writes uint16_t + * #4u writes uint32_t + * #8u writes uint64_t + * #1d writes int8_t + * #2d writes int16_t + * #4d writes int32_t + * #8d writes int64_t + * i.e.: + * #1u%lu #2u%lu #4d%lu #8d%lu #llu%hu #d%lu + * + * * Attributes: + * + * n: (for network byte order) + * #ntracetype%ctype + * is written in the trace in network byte order. + * + * i.e.: #bn4u%lu, #n%lu, #b%u + * + * TODO (eventually) + * Variable length sequence + * #a #tracetype1 #tracetype2 %array_ptr %elem_size %num_elems + * In the trace: + * #a specifies that this is a sequence + * #tracetype1 is the type of elements in the sequence + * #tracetype2 is the type of the element count + * GCC input: + * array_ptr is a pointer to an array that contains members of size + * elem_size. + * num_elems is the number of elements in the array. + * i.e.: #a #lu #lu %p %lu %u + * + * Callback + * #k callback (taken from the probe data) + * The following % arguments are exepected by the callback + * + * i.e.: #a #lu #lu #k %p + * + * Note: No conversion is done from floats to integers, nor from integers to + * floats between c types and trace types. float conversion from double to float + * or from float to double is also not supported. + * + * REMOVE + * %*b expects sizeof(data), data + * where sizeof(data) is 1, 2, 4 or 8 + * + * Fixed length struct, union or array. + * FIXME: unable to extract those sizes statically. + * %*r expects sizeof(*ptr), ptr + * %*.*r expects sizeof(*ptr), __alignof__(*ptr), ptr + * struct and unions removed. + * Fixed length array: + * [%p]#a[len #tracetype] + * i.e.: [%p]#a[12 #lu] + * + * Variable length sequence + * %*.*:*v expects sizeof(*ptr), __alignof__(*ptr), elem_num, ptr + * where elem_num is the number of elements in the sequence + */ +static inline +const char *parse_trace_type(const char *fmt, char *trace_size, + enum ltt_type *trace_type, + unsigned long *attributes) +{ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* parse attributes. */ +repeat: + switch (*fmt) { + case 'n': + *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; + ++fmt; + goto repeat; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z' || *fmt == 't' || + *fmt == 'S' || *fmt == '1' || *fmt == '2' || + *fmt == '4' || *fmt == 8) { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + switch (*fmt) { + case 'c': + *trace_type = LTT_TYPE_UNSIGNED_INT; + *trace_size = sizeof(unsigned char); + goto parse_end; + case 's': + *trace_type = LTT_TYPE_STRING; + goto parse_end; + case 'p': + *trace_type = LTT_TYPE_UNSIGNED_INT; + *trace_size = sizeof(void *); + goto parse_end; + case 'd': + case 'i': + *trace_type = LTT_TYPE_SIGNED_INT; + break; + case 'o': + case 'u': + case 'x': + case 'X': + *trace_type = LTT_TYPE_UNSIGNED_INT; + break; + default: + if (!*fmt) + --fmt; + goto parse_end; + } + switch (qualifier) { + case 'L': + *trace_size = sizeof(long long); + break; + case 'l': + *trace_size = sizeof(long); + break; + case 'Z': + case 'z': + *trace_size = sizeof(size_t); + break; + case 't': + *trace_size = sizeof(ptrdiff_t); + break; + case 'h': + *trace_size = sizeof(short); + break; + case '1': + *trace_size = sizeof(uint8_t); + break; + case '2': + *trace_size = sizeof(uint16_t); + break; + case '4': + *trace_size = sizeof(uint32_t); + break; + case '8': + *trace_size = sizeof(uint64_t); + break; + default: + *trace_size = sizeof(int); + } + +parse_end: + return fmt; +} + +/* + * Restrictions: + * Field width and precision are *not* supported. + * %n not supported. + */ +static inline +const char *parse_c_type(const char *fmt, char *c_size, enum ltt_type *c_type, + char *outfmt) +{ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + /* 't' added for ptrdiff_t */ + + /* process flags : ignore standard print formats for now. */ +repeat: + switch (*fmt) { + case '-': + case '+': + case ' ': + case '#': + case '0': + ++fmt; + goto repeat; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || + *fmt == 'Z' || *fmt == 'z' || *fmt == 't' || + *fmt == 'S') { + qualifier = *fmt; + ++fmt; + if (qualifier == 'l' && *fmt == 'l') { + qualifier = 'L'; + ++fmt; + } + } + + if (outfmt) { + if (qualifier != -1) + *outfmt++ = (char)qualifier; + *outfmt++ = *fmt; + *outfmt = 0; + } + + switch (*fmt) { + case 'c': + *c_type = LTT_TYPE_UNSIGNED_INT; + *c_size = sizeof(unsigned char); + goto parse_end; + case 's': + *c_type = LTT_TYPE_STRING; + goto parse_end; + case 'p': + *c_type = LTT_TYPE_UNSIGNED_INT; + *c_size = sizeof(void *); + goto parse_end; + case 'd': + case 'i': + *c_type = LTT_TYPE_SIGNED_INT; + break; + case 'o': + case 'u': + case 'x': + case 'X': + *c_type = LTT_TYPE_UNSIGNED_INT; + break; + default: + if (!*fmt) + --fmt; + goto parse_end; + } + switch (qualifier) { + case 'L': + *c_size = sizeof(long long); + break; + case 'l': + *c_size = sizeof(long); + break; + case 'Z': + case 'z': + *c_size = sizeof(size_t); + break; + case 't': + *c_size = sizeof(ptrdiff_t); + break; + case 'h': + *c_size = sizeof(short); + break; + default: + *c_size = sizeof(int); + } + +parse_end: + return fmt; +} + +static inline +size_t serialize_trace_data(struct ltt_chanbuf *buf, size_t buf_offset, + char trace_size, enum ltt_type trace_type, + char c_size, enum ltt_type c_type, + unsigned int *stack_pos_ctx, + int *largest_align, + va_list *args) +{ + union { + unsigned long v_ulong; + uint64_t v_uint64; + struct { + const char *s; + size_t len; + } v_string; + } tmp; + + /* + * Be careful about sign extension here. + * Sign extension is done with the destination (trace) type. + */ + switch (trace_type) { + case LTT_TYPE_SIGNED_INT: + switch (c_size) { + case 1: + tmp.v_ulong = (long)(int8_t)va_arg(*args, int); + break; + case 2: + tmp.v_ulong = (long)(int16_t)va_arg(*args, int); + break; + case 4: + tmp.v_ulong = (long)(int32_t)va_arg(*args, int); + break; + case 8: + tmp.v_uint64 = va_arg(*args, int64_t); + break; + default: + BUG(); + } + break; + case LTT_TYPE_UNSIGNED_INT: + switch (c_size) { + case 1: + tmp.v_ulong = (unsigned long)(uint8_t)va_arg(*args, unsigned int); + break; + case 2: + tmp.v_ulong = (unsigned long)(uint16_t)va_arg(*args, unsigned int); + break; + case 4: + tmp.v_ulong = (unsigned long)(uint32_t)va_arg(*args, unsigned int); + break; + case 8: + tmp.v_uint64 = va_arg(*args, uint64_t); + break; + default: + BUG(); + } + break; + case LTT_TYPE_STRING: + tmp.v_string.s = va_arg(*args, const char *); + if ((unsigned long)tmp.v_string.s < PAGE_SIZE) + tmp.v_string.s = ""; + if (!buf) { + /* + * Reserve tracer stack entry. + */ + __get_cpu_var(tracer_stack_pos)++; + WARN_ON_ONCE(__get_cpu_var(tracer_stack_pos) + > TRACER_STACK_LEN); + barrier(); + __get_cpu_var(tracer_stack)[*stack_pos_ctx] = + strlen(tmp.v_string.s) + 1; + } + tmp.v_string.len = __get_cpu_var(tracer_stack) + [(*stack_pos_ctx)++]; + if (buf) + ltt_relay_strncpy(&buf->a, buf->a.chan, buf_offset, + tmp.v_string.s, tmp.v_string.len); + buf_offset += tmp.v_string.len; + goto copydone; + default: + BUG(); + } + + /* + * If trace_size is lower or equal to 4 bytes, there is no sign + * extension to do because we are already encoded in a long. Therefore, + * we can combine signed and unsigned ops. 4 bytes float also works + * with this, because we do a simple copy of 4 bytes into 4 bytes + * without manipulation (and we do not support conversion from integers + * to floats). + * It is also the case if c_size is 8 bytes, which is the largest + * possible integer. + */ + if (ltt_get_alignment()) { + buf_offset += ltt_align(buf_offset, trace_size); + if (largest_align) + *largest_align = max_t(int, *largest_align, trace_size); + } + if (trace_size <= 4 || c_size == 8) { + if (buf) { + switch (trace_size) { + case 1: + if (c_size == 8) + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint8_t[]){ (uint8_t)tmp.v_uint64 }, + sizeof(uint8_t)); + else + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint8_t[]){ (uint8_t)tmp.v_ulong }, + sizeof(uint8_t)); + break; + case 2: + if (c_size == 8) + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint16_t[]){ (uint16_t)tmp.v_uint64 }, + sizeof(uint16_t)); + else + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint16_t[]){ (uint16_t)tmp.v_ulong }, + sizeof(uint16_t)); + break; + case 4: + if (c_size == 8) + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint32_t[]){ (uint32_t)tmp.v_uint64 }, + sizeof(uint32_t)); + else + ltt_relay_write(&buf->a, buf->a.chan, + buf_offset, + (uint32_t[]){ (uint32_t)tmp.v_ulong }, + sizeof(uint32_t)); + break; + case 8: + /* + * c_size cannot be other than 8 here because + * trace_size > 4. + */ + ltt_relay_write(&buf->a, buf->a.chan, buf_offset, + (uint64_t[]){ (uint64_t)tmp.v_uint64 }, + sizeof(uint64_t)); + break; + default: + BUG(); + } + } + buf_offset += trace_size; + goto copydone; + } else { + /* + * Perform sign extension. + */ + if (buf) { + switch (trace_type) { + case LTT_TYPE_SIGNED_INT: + ltt_relay_write(&buf->a, buf->a.chan, buf_offset, + (int64_t[]){ (int64_t)tmp.v_ulong }, + sizeof(int64_t)); + break; + case LTT_TYPE_UNSIGNED_INT: + ltt_relay_write(&buf->a, buf->a.chan, buf_offset, + (uint64_t[]){ (uint64_t)tmp.v_ulong }, + sizeof(uint64_t)); + break; + default: + BUG(); + } + } + buf_offset += trace_size; + goto copydone; + } + +copydone: + return buf_offset; +} + +notrace size_t +ltt_serialize_data(struct ltt_chanbuf *buf, size_t buf_offset, + struct ltt_serialize_closure *closure, + void *serialize_private, unsigned int stack_pos_ctx, + int *largest_align, const char *fmt, va_list *args) +{ + char trace_size = 0, c_size = 0; /* + * 0 (unset), 1, 2, 4, 8 bytes. + */ + enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; + unsigned long attributes = 0; + + for (; *fmt ; ++fmt) { + switch (*fmt) { + case '#': + /* tracetypes (#) */ + ++fmt; /* skip first '#' */ + if (*fmt == '#') /* Escaped ## */ + break; + attributes = 0; + fmt = parse_trace_type(fmt, &trace_size, &trace_type, + &attributes); + break; + case '%': + /* c types (%) */ + ++fmt; /* skip first '%' */ + if (*fmt == '%') /* Escaped %% */ + break; + fmt = parse_c_type(fmt, &c_size, &c_type, NULL); + /* + * Output c types if no trace types has been + * specified. + */ + if (!trace_size) + trace_size = c_size; + if (trace_type == LTT_TYPE_NONE) + trace_type = c_type; + if (c_type == LTT_TYPE_STRING) + trace_type = LTT_TYPE_STRING; + /* perform trace write */ + buf_offset = serialize_trace_data(buf, buf_offset, + trace_size, + trace_type, c_size, + c_type, + &stack_pos_ctx, + largest_align, + args); + trace_size = 0; + c_size = 0; + trace_type = LTT_TYPE_NONE; + c_size = LTT_TYPE_NONE; + attributes = 0; + break; + /* default is to skip the text, doing nothing */ + } + } + return buf_offset; +} +EXPORT_SYMBOL_GPL(ltt_serialize_data); + +static inline +uint64_t unserialize_base_type(struct ltt_chanbuf *buf, + size_t *ppos, char trace_size, + enum ltt_type trace_type) +{ + uint64_t tmp; + + *ppos += ltt_align(*ppos, trace_size); + ltt_relay_read(&buf->a, *ppos, &tmp, trace_size); + *ppos += trace_size; + + switch (trace_type) { + case LTT_TYPE_SIGNED_INT: + switch (trace_size) { + case 1: + return (uint64_t)*(int8_t *)&tmp; + case 2: + return (uint64_t)*(int16_t *)&tmp; + case 4: + return (uint64_t)*(int32_t *)&tmp; + case 8: + return tmp; + } + break; + case LTT_TYPE_UNSIGNED_INT: + switch (trace_size) { + case 1: + return (uint64_t)*(uint8_t *)&tmp; + case 2: + return (uint64_t)*(uint16_t *)&tmp; + case 4: + return (uint64_t)*(uint32_t *)&tmp; + case 8: + return tmp; + } + break; + default: + break; + } + + BUG(); + return 0; +} + +static +int serialize_printf_data(struct ltt_chanbuf *buf, size_t *ppos, + char trace_size, enum ltt_type trace_type, + char c_size, enum ltt_type c_type, char *output, + ssize_t outlen, const char *outfmt) +{ + u64 value; + outlen = outlen < 0 ? 0 : outlen; + + if (trace_type == LTT_TYPE_STRING) { + size_t len = ltt_relay_read_cstr(&buf->a, *ppos, output, + outlen); + *ppos += len + 1; + return len; + } + + value = unserialize_base_type(buf, ppos, trace_size, trace_type); + + if (c_size == 8) + return snprintf(output, outlen, outfmt, value); + else + return snprintf(output, outlen, outfmt, (unsigned int)value); +} + +/** + * ltt_serialize_printf - Format a string and place it in a buffer + * @buf: The ltt-relay buffer that store binary data + * @buf_offset: binary data's offset in @buf (should be masked to use as offset) + * @msg_size: return message's length + * @output: The buffer to place the result into + * @outlen: The size of the buffer, including the trailing '\0' + * @fmt: The format string to use + * + * The return value is the number of characters which would + * be generated for the given input, excluding the trailing + * '\0', as per ISO C99. If the return is greater than or equal to @outlen, + * the resulting string is truncated. + */ +size_t ltt_serialize_printf(struct ltt_chanbuf *buf, unsigned long buf_offset, + size_t *msg_size, char *output, size_t outlen, + const char *fmt) +{ + char trace_size = 0, c_size = 0; /* + * 0 (unset), 1, 2, 4, 8 bytes. + */ + enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; + unsigned long attributes = 0; + char outfmt[4] = "%"; + size_t outpos = 0; + size_t len; + size_t msgpos = buf_offset; + + for (; *fmt ; ++fmt) { + switch (*fmt) { + case '#': + /* tracetypes (#) */ + ++fmt; /* skip first '#' */ + if (*fmt == '#') { /* Escaped ## */ + if (outpos < outlen) + output[outpos] = '#'; + outpos++; + break; + } + attributes = 0; + fmt = parse_trace_type(fmt, &trace_size, &trace_type, + &attributes); + break; + case '%': + /* c types (%) */ + ++fmt; /* skip first '%' */ + if (*fmt == '%') { /* Escaped %% */ + if (outpos < outlen) + output[outpos] = '%'; + outpos++; + break; + } + fmt = parse_c_type(fmt, &c_size, &c_type, outfmt + 1); + /* + * Output c types if no trace types has been + * specified. + */ + if (!trace_size) + trace_size = c_size; + if (trace_type == LTT_TYPE_NONE) + trace_type = c_type; + if (c_type == LTT_TYPE_STRING) + trace_type = LTT_TYPE_STRING; + + /* perform trace printf */ + len = serialize_printf_data(buf, &msgpos, trace_size, + trace_type, c_size, c_type, + output + outpos, + outlen - outpos, outfmt); + outpos += len; + trace_size = 0; + c_size = 0; + trace_type = LTT_TYPE_NONE; + c_size = LTT_TYPE_NONE; + attributes = 0; + break; + default: + if (outpos < outlen) + output[outpos] = *fmt; + outpos++; + break; + } + } + if (msg_size) + *msg_size = (size_t)(msgpos - buf_offset); + /* + * Make sure we end output with terminating \0 when truncated. + */ + if (outpos >= outlen + 1) + output[outlen] = '\0'; + return outpos; +} +EXPORT_SYMBOL_GPL(ltt_serialize_printf); + +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + +unsigned int ltt_fmt_largest_align(size_t align_drift, const char *fmt) +{ + char trace_size = 0, c_size = 0; + enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; + unsigned long attributes = 0; + int largest_align = 1; + + for (; *fmt ; ++fmt) { + switch (*fmt) { + case '#': + /* tracetypes (#) */ + ++fmt; /* skip first '#' */ + if (*fmt == '#') /* Escaped ## */ + break; + attributes = 0; + fmt = parse_trace_type(fmt, &trace_size, &trace_type, + &attributes); + + largest_align = max_t(int, largest_align, trace_size); + if (largest_align >= ltt_get_alignment()) + goto exit; + break; + case '%': + /* c types (%) */ + ++fmt; /* skip first '%' */ + if (*fmt == '%') /* Escaped %% */ + break; + fmt = parse_c_type(fmt, &c_size, &c_type, NULL); + /* + * Output c types if no trace types has been + * specified. + */ + if (!trace_size) + trace_size = c_size; + if (trace_type == LTT_TYPE_NONE) + trace_type = c_type; + if (c_type == LTT_TYPE_STRING) + trace_type = LTT_TYPE_STRING; + + largest_align = max_t(int, largest_align, trace_size); + if (largest_align >= ltt_get_alignment()) + goto exit; + + trace_size = 0; + c_size = 0; + trace_type = LTT_TYPE_NONE; + c_size = LTT_TYPE_NONE; + break; + } + } + +exit: + largest_align = min_t(int, largest_align, ltt_get_alignment()); + return (largest_align - align_drift) & (largest_align - 1); +} +EXPORT_SYMBOL_GPL(ltt_fmt_largest_align); + +#endif + +/* + * Calculate data size + * Assume that the padding for alignment starts at a sizeof(void *) address. + */ +static notrace +size_t ltt_get_data_size(struct ltt_serialize_closure *closure, + void *serialize_private, unsigned int stack_pos_ctx, + int *largest_align, const char *fmt, va_list *args) +{ + ltt_serialize_cb cb = closure->callbacks[0]; + closure->cb_idx = 0; + return (size_t)cb(NULL, 0, closure, serialize_private, stack_pos_ctx, + largest_align, fmt, args); +} + +static notrace +void ltt_write_event_data(struct ltt_chanbuf *buf, size_t buf_offset, + struct ltt_serialize_closure *closure, + void *serialize_private, unsigned int stack_pos_ctx, + int largest_align, const char *fmt, va_list *args) +{ + ltt_serialize_cb cb = closure->callbacks[0]; + closure->cb_idx = 0; + buf_offset += ltt_align(buf_offset, largest_align); + cb(buf, buf_offset, closure, serialize_private, stack_pos_ctx, NULL, + fmt, args); +} + + +notrace +void ltt_vtrace(const struct marker *mdata, void *probe_data, void *call_data, + const char *fmt, va_list *args) +{ + int largest_align, ret; + struct ltt_active_marker *pdata; + uint16_t eID; + size_t data_size, slot_size; + unsigned int chan_index; + struct ltt_chanbuf *buf; + struct ltt_chan *chan; + struct ltt_trace *trace, *dest_trace = NULL; + uint64_t tsc; + long buf_offset; + va_list args_copy; + struct ltt_serialize_closure closure; + struct ltt_probe_private_data *private_data = call_data; + void *serialize_private = NULL; + int cpu; + unsigned int rflags; + unsigned int stack_pos_ctx; + + /* + * This test is useful for quickly exiting static tracing when no trace + * is active. We expect to have an active trace when we get here. + */ + if (unlikely(ltt_traces.num_active_traces == 0)) + return; + + rcu_read_lock_sched_notrace(); + cpu = smp_processor_id(); + __get_cpu_var(ltt_nesting)++; + stack_pos_ctx = __get_cpu_var(tracer_stack_pos); + /* + * asm volatile and "memory" clobber prevent the compiler from moving + * instructions out of the ltt nesting count. This is required to ensure + * that probe side-effects which can cause recursion (e.g. unforeseen + * traps, divisions by 0, ...) are triggered within the incremented + * nesting count section. + */ + barrier(); + pdata = (struct ltt_active_marker *)probe_data; + eID = mdata->event_id; + chan_index = mdata->channel_id; + closure.callbacks = pdata->probe->callbacks; + + if (unlikely(private_data)) { + dest_trace = private_data->trace; + if (private_data->serializer) + closure.callbacks = &private_data->serializer; + serialize_private = private_data->serialize_private; + } + + va_copy(args_copy, *args); + /* + * Assumes event payload to start on largest_align alignment. + */ + largest_align = 1; /* must be non-zero for ltt_align */ + data_size = ltt_get_data_size(&closure, serialize_private, + stack_pos_ctx, &largest_align, + fmt, &args_copy); + largest_align = min_t(int, largest_align, sizeof(void *)); + va_end(args_copy); + + /* Iterate on each trace */ + list_for_each_entry_rcu(trace, <t_traces.head, list) { + /* + * Expect the filter to filter out events. If we get here, + * we went through tracepoint activation as a first step. + */ + if (unlikely(dest_trace && trace != dest_trace)) + continue; + if (unlikely(!trace->active)) + continue; + if (unlikely(!ltt_run_filter(trace, eID))) + continue; +#ifdef LTT_DEBUG_EVENT_SIZE + rflags = LTT_RFLAG_ID_SIZE; +#else + if (unlikely(eID >= LTT_FREE_EVENTS)) + rflags = LTT_RFLAG_ID; + else + rflags = 0; +#endif + /* + * Skip channels added after trace creation. + */ + if (unlikely(chan_index >= trace->nr_channels)) + continue; + chan = &trace->channels[chan_index]; + if (!chan->active) + continue; + + /* reserve space : header and data */ + ret = ltt_reserve_slot(chan, trace, data_size, largest_align, + cpu, &buf, &slot_size, &buf_offset, + &tsc, &rflags); + if (unlikely(ret < 0)) + continue; /* buffer full */ + + va_copy(args_copy, *args); + /* Out-of-order write : header and data */ + buf_offset = ltt_write_event_header(&buf->a, &chan->a, + buf_offset, eID, data_size, + tsc, rflags); + ltt_write_event_data(buf, buf_offset, &closure, + serialize_private, stack_pos_ctx, + largest_align, fmt, &args_copy); + va_end(args_copy); + /* Out-of-order commit */ + ltt_commit_slot(buf, chan, buf_offset, data_size, slot_size); + } + /* + * asm volatile and "memory" clobber prevent the compiler from moving + * instructions out of the ltt nesting count. This is required to ensure + * that probe side-effects which can cause recursion (e.g. unforeseen + * traps, divisions by 0, ...) are triggered within the incremented + * nesting count section. + */ + barrier(); + __get_cpu_var(tracer_stack_pos) = stack_pos_ctx; + __get_cpu_var(ltt_nesting)--; + rcu_read_unlock_sched_notrace(); +} +EXPORT_SYMBOL_GPL(ltt_vtrace); + +notrace +void ltt_trace(const struct marker *mdata, void *probe_data, void *call_data, + const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ltt_vtrace(mdata, probe_data, call_data, fmt, &args); + va_end(args); +} +EXPORT_SYMBOL_GPL(ltt_trace); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Serializer"); diff --git a/discard/ltt-tracer.c b/discard/ltt-tracer.c new file mode 100644 index 00000000..5cdea932 --- /dev/null +++ b/discard/ltt-tracer.c @@ -0,0 +1,1112 @@ +/* + * ltt/ltt-tracer.c + * + * Copyright (c) 2005-2010 - Mathieu Desnoyers + * + * Tracing management internal kernel API. Trace buffer allocation/free, tracing + * start/stop. + * + * Author: + * Mathieu Desnoyers + * + * Inspired from LTT : + * Karim Yaghmour (karim@opersys.com) + * Tom Zanussi (zanussi@us.ibm.com) + * Bob Wisniewski (bob@watson.ibm.com) + * And from K42 : + * Bob Wisniewski (bob@watson.ibm.com) + * + * Changelog: + * 22/09/06, Move to the marker/probes mechanism. + * 19/10/05, Complete lockless mechanism. + * 27/05/05, Modular redesign and rewrite. + * + * Dual LGPL v2.1/GPL v2 license. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ltt-tracer.h" + +static void synchronize_trace(void) +{ + synchronize_sched(); +#ifdef CONFIG_PREEMPT_RT + synchronize_rcu(); +#endif +} + +static void async_wakeup(unsigned long data); + +static DEFINE_TIMER(ltt_async_wakeup_timer, async_wakeup, 0, 0); + +/* Default callbacks for modules */ +notrace +int ltt_filter_control_default(enum ltt_filter_control_msg msg, + struct ltt_trace *trace) +{ + return 0; +} + +int ltt_statedump_default(struct ltt_trace *trace) +{ + return 0; +} + +/* Callbacks for registered modules */ + +int (*ltt_filter_control_functor) + (enum ltt_filter_control_msg msg, struct ltt_trace *trace) = + ltt_filter_control_default; +struct module *ltt_filter_control_owner; + +/* These function pointers are protected by a trace activation check */ +struct module *ltt_run_filter_owner; +int (*ltt_statedump_functor)(struct ltt_trace *trace) = ltt_statedump_default; +struct module *ltt_statedump_owner; + +struct chan_info_struct { + const char *name; + unsigned int def_sb_size; + unsigned int def_n_sb; +} chan_infos[] = { + [LTT_CHANNEL_METADATA] = { + LTT_METADATA_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_FD_STATE] = { + LTT_FD_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_GLOBAL_STATE] = { + LTT_GLOBAL_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_IRQ_STATE] = { + LTT_IRQ_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_MODULE_STATE] = { + LTT_MODULE_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_NETIF_STATE] = { + LTT_NETIF_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_SOFTIRQ_STATE] = { + LTT_SOFTIRQ_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_SWAP_STATE] = { + LTT_SWAP_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_SYSCALL_STATE] = { + LTT_SYSCALL_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_TASK_STATE] = { + LTT_TASK_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_VM_STATE] = { + LTT_VM_STATE_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_MED, + LTT_DEFAULT_N_SUBBUFS_MED, + }, + [LTT_CHANNEL_FS] = { + LTT_FS_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_MED, + LTT_DEFAULT_N_SUBBUFS_MED, + }, + [LTT_CHANNEL_INPUT] = { + LTT_INPUT_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_IPC] = { + LTT_IPC_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_LOW, + LTT_DEFAULT_N_SUBBUFS_LOW, + }, + [LTT_CHANNEL_KERNEL] = { + LTT_KERNEL_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_HIGH, + LTT_DEFAULT_N_SUBBUFS_HIGH, + }, + [LTT_CHANNEL_MM] = { + LTT_MM_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_MED, + LTT_DEFAULT_N_SUBBUFS_MED, + }, + [LTT_CHANNEL_RCU] = { + LTT_RCU_CHANNEL, + LTT_DEFAULT_SUBBUF_SIZE_MED, + LTT_DEFAULT_N_SUBBUFS_MED, + }, + [LTT_CHANNEL_DEFAULT] = { + NULL, + LTT_DEFAULT_SUBBUF_SIZE_MED, + LTT_DEFAULT_N_SUBBUFS_MED, + }, +}; + +static enum ltt_channels get_channel_type_from_name(const char *name) +{ + int i; + + if (!name) + return LTT_CHANNEL_DEFAULT; + + for (i = 0; i < ARRAY_SIZE(chan_infos); i++) + if (chan_infos[i].name && !strcmp(name, chan_infos[i].name)) + return (enum ltt_channels)i; + + return LTT_CHANNEL_DEFAULT; +} + +/** + * ltt_module_register - LTT module registration + * @name: module type + * @function: callback to register + * @owner: module which owns the callback + * + * The module calling this registration function must ensure that no + * trap-inducing code will be executed by "function". E.g. vmalloc_sync_all() + * must be called between a vmalloc and the moment the memory is made visible to + * "function". This registration acts as a vmalloc_sync_all. Therefore, only if + * the module allocates virtual memory after its registration must it + * synchronize the TLBs. + */ +int ltt_module_register(enum ltt_module_function name, void *function, + struct module *owner) +{ + int ret = 0; + + /* + * Make sure no page fault can be triggered by the module about to be + * registered. We deal with this here so we don't have to call + * vmalloc_sync_all() in each module's init. + */ + vmalloc_sync_all(); + + switch (name) { + case LTT_FUNCTION_RUN_FILTER: + if (ltt_run_filter_owner != NULL) { + ret = -EEXIST; + goto end; + } + ltt_filter_register((ltt_run_filter_functor)function); + ltt_run_filter_owner = owner; + break; + case LTT_FUNCTION_FILTER_CONTROL: + if (ltt_filter_control_owner != NULL) { + ret = -EEXIST; + goto end; + } + ltt_filter_control_functor = + (int (*)(enum ltt_filter_control_msg, + struct ltt_trace *))function; + ltt_filter_control_owner = owner; + break; + case LTT_FUNCTION_STATEDUMP: + if (ltt_statedump_owner != NULL) { + ret = -EEXIST; + goto end; + } + ltt_statedump_functor = + (int (*)(struct ltt_trace *))function; + ltt_statedump_owner = owner; + break; + } +end: + return ret; +} +EXPORT_SYMBOL_GPL(ltt_module_register); + +/** + * ltt_module_unregister - LTT module unregistration + * @name: module type + */ +void ltt_module_unregister(enum ltt_module_function name) +{ + switch (name) { + case LTT_FUNCTION_RUN_FILTER: + ltt_filter_unregister(); + ltt_run_filter_owner = NULL; + /* Wait for preempt sections to finish */ + synchronize_trace(); + break; + case LTT_FUNCTION_FILTER_CONTROL: + ltt_filter_control_functor = ltt_filter_control_default; + ltt_filter_control_owner = NULL; + break; + case LTT_FUNCTION_STATEDUMP: + ltt_statedump_functor = ltt_statedump_default; + ltt_statedump_owner = NULL; + break; + } + +} +EXPORT_SYMBOL_GPL(ltt_module_unregister); + +static LIST_HEAD(ltt_transport_list); + +/** + * 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. The module calling this registration function must ensure that no + * trap-inducing code will be executed by the transport functions. E.g. + * vmalloc_sync_all() must be called between a vmalloc and the moment the memory + * is made visible to the transport function. This registration acts as a + * vmalloc_sync_all. Therefore, only if the module allocates virtual memory + * after its registration must it synchronize the TLBs. + */ +void ltt_transport_register(struct ltt_transport *transport) +{ + /* + * Make sure no page fault can be triggered by the module about to be + * registered. We deal with this here so we don't have to call + * vmalloc_sync_all() in each module's init. + */ + vmalloc_sync_all(); + + ltt_lock_traces(); + list_add_tail(&transport->node, <t_transport_list); + ltt_unlock_traces(); +} +EXPORT_SYMBOL_GPL(ltt_transport_register); + +/** + * ltt_transport_unregister - LTT transport unregistration + * @transport: transport structure + */ +void ltt_transport_unregister(struct ltt_transport *transport) +{ + ltt_lock_traces(); + list_del(&transport->node); + ltt_unlock_traces(); +} +EXPORT_SYMBOL_GPL(ltt_transport_unregister); + +static inline +int is_channel_overwrite(enum ltt_channels chan, enum trace_mode mode) +{ + switch (mode) { + case LTT_TRACE_NORMAL: + return 0; + case LTT_TRACE_FLIGHT: + switch (chan) { + case LTT_CHANNEL_METADATA: + return 0; + default: + return 1; + } + case LTT_TRACE_HYBRID: + switch (chan) { + case LTT_CHANNEL_KERNEL: + case LTT_CHANNEL_FS: + case LTT_CHANNEL_MM: + case LTT_CHANNEL_RCU: + case LTT_CHANNEL_IPC: + case LTT_CHANNEL_INPUT: + return 1; + default: + return 0; + } + default: + return 0; + } +} + +/** + * _ltt_trace_find - find a trace by given name. + * trace_name: trace name + * + * Returns a pointer to the trace structure, NULL if not found. + */ +static struct ltt_trace *_ltt_trace_find(const char *trace_name) +{ + struct ltt_trace *trace; + + list_for_each_entry(trace, <t_traces.head, list) + if (!strncmp(trace->trace_name, trace_name, NAME_MAX)) + return trace; + + return NULL; +} + +/* _ltt_trace_find_setup : + * find a trace in setup list by given name. + * + * Returns a pointer to the trace structure, NULL if not found. + */ +struct ltt_trace *_ltt_trace_find_setup(const char *trace_name) +{ + struct ltt_trace *trace; + + list_for_each_entry(trace, <t_traces.setup_head, list) + if (!strncmp(trace->trace_name, trace_name, NAME_MAX)) + return trace; + + return NULL; +} +EXPORT_SYMBOL_GPL(_ltt_trace_find_setup); + +/** + * ltt_release_trace - Release a LTT trace + * @kref : reference count on the trace + */ +void ltt_release_trace(struct kref *kref) +{ + struct ltt_trace *trace = container_of(kref, struct ltt_trace, kref); + + trace->ops->remove_dirs(trace); + module_put(trace->transport->owner); + ltt_channels_trace_free(trace); + kfree(trace); +} +EXPORT_SYMBOL_GPL(ltt_release_trace); + +static inline void prepare_chan_size_num(unsigned int *subbuf_size, + unsigned int *n_subbufs) +{ + /* Make sure the subbuffer size is larger than a page */ + *subbuf_size = max_t(unsigned int, *subbuf_size, PAGE_SIZE); + + /* round to next power of 2 */ + *subbuf_size = 1 << get_count_order(*subbuf_size); + *n_subbufs = 1 << get_count_order(*n_subbufs); + + /* Subbuf size and number must both be power of two */ + WARN_ON(hweight32(*subbuf_size) != 1); + WARN_ON(hweight32(*n_subbufs) != 1); +} + +int _ltt_trace_setup(const char *trace_name) +{ + int err = 0; + struct ltt_trace *new_trace = NULL; + int metadata_index; + unsigned int chan; + enum ltt_channels chantype; + + if (_ltt_trace_find_setup(trace_name)) { + printk(KERN_ERR "LTT : Trace name %s already used.\n", + trace_name); + err = -EEXIST; + goto traces_error; + } + + if (_ltt_trace_find(trace_name)) { + printk(KERN_ERR "LTT : Trace name %s already used.\n", + trace_name); + err = -EEXIST; + goto traces_error; + } + + new_trace = kzalloc(sizeof(struct ltt_trace), GFP_KERNEL); + if (!new_trace) { + printk(KERN_ERR + "LTT : Unable to allocate memory for trace %s\n", + trace_name); + err = -ENOMEM; + goto traces_error; + } + strncpy(new_trace->trace_name, trace_name, NAME_MAX); + if (ltt_channels_trace_alloc(&new_trace->nr_channels, 0)) { + printk(KERN_ERR + "LTT : Unable to allocate memory for chaninfo %s\n", + trace_name); + err = -ENOMEM; + goto trace_free; + } + + /* + * Force metadata channel to no overwrite. + */ + metadata_index = ltt_channels_get_index_from_name("metadata"); + WARN_ON(metadata_index < 0); + new_trace->settings[metadata_index].overwrite = 0; + + /* + * Set hardcoded tracer defaults for some channels + */ + for (chan = 0; chan < new_trace->nr_channels; chan++) { + chantype = get_channel_type_from_name( + ltt_channels_get_name_from_index(chan)); + new_trace->settings[chan].sb_size = + chan_infos[chantype].def_sb_size; + new_trace->settings[chan].n_sb = + chan_infos[chantype].def_n_sb; + } + + list_add(&new_trace->list, <t_traces.setup_head); + return 0; + +trace_free: + kfree(new_trace); +traces_error: + return err; +} +EXPORT_SYMBOL_GPL(_ltt_trace_setup); + + +int ltt_trace_setup(const char *trace_name) +{ + int ret; + ltt_lock_traces(); + ret = _ltt_trace_setup(trace_name); + ltt_unlock_traces(); + return ret; +} +EXPORT_SYMBOL_GPL(ltt_trace_setup); + +/* must be called from within a traces lock. */ +static void _ltt_trace_free(struct ltt_trace *trace) +{ + list_del(&trace->list); + kfree(trace); +} + +int ltt_trace_set_type(const char *trace_name, const char *trace_type) +{ + int err = 0; + struct ltt_trace *trace; + struct ltt_transport *tran_iter, *transport = NULL; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + list_for_each_entry(tran_iter, <t_transport_list, node) { + if (!strcmp(tran_iter->name, trace_type)) { + transport = tran_iter; + break; + } + } + if (!transport) { + printk(KERN_ERR "LTT : Transport %s is not present.\n", + trace_type); + err = -EINVAL; + goto traces_error; + } + + trace->transport = transport; + +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_set_type); + +int ltt_trace_set_channel_subbufsize(const char *trace_name, + const char *channel_name, + unsigned int size) +{ + int err = 0; + struct ltt_trace *trace; + int index; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + index = ltt_channels_get_index_from_name(channel_name); + if (index < 0) { + printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); + err = -ENOENT; + goto traces_error; + } + trace->settings[index].sb_size = size; + +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_set_channel_subbufsize); + +int ltt_trace_set_channel_subbufcount(const char *trace_name, + const char *channel_name, + unsigned int cnt) +{ + int err = 0; + struct ltt_trace *trace; + int index; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + index = ltt_channels_get_index_from_name(channel_name); + if (index < 0) { + printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); + err = -ENOENT; + goto traces_error; + } + trace->settings[index].n_sb = cnt; + +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_set_channel_subbufcount); + +int ltt_trace_set_channel_switch_timer(const char *trace_name, + const char *channel_name, + unsigned long interval) +{ + int err = 0; + struct ltt_trace *trace; + int index; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + index = ltt_channels_get_index_from_name(channel_name); + if (index < 0) { + printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); + err = -ENOENT; + goto traces_error; + } + ltt_channels_trace_set_timer(&trace->settings[index], interval); + +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_set_channel_switch_timer); + +int ltt_trace_set_channel_overwrite(const char *trace_name, + const char *channel_name, + unsigned int overwrite) +{ + int err = 0; + struct ltt_trace *trace; + int index; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + /* + * Always put the metadata channel in non-overwrite mode : + * This is a very low traffic channel and it can't afford to have its + * data overwritten : this data (marker info) is necessary to be + * able to read the trace. + */ + if (overwrite && !strcmp(channel_name, "metadata")) { + printk(KERN_ERR "LTT : Trying to set metadata channel to " + "overwrite mode\n"); + err = -EINVAL; + goto traces_error; + } + + index = ltt_channels_get_index_from_name(channel_name); + if (index < 0) { + printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); + err = -ENOENT; + goto traces_error; + } + + trace->settings[index].overwrite = overwrite; + +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_set_channel_overwrite); + +int ltt_trace_alloc(const char *trace_name) +{ + int err = 0; + struct ltt_trace *trace; + int sb_size, n_sb; + unsigned long flags; + int chan; + const char *channel_name; + + ltt_lock_traces(); + + trace = _ltt_trace_find_setup(trace_name); + if (!trace) { + printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); + err = -ENOENT; + goto traces_error; + } + + kref_init(&trace->kref); + init_waitqueue_head(&trace->kref_wq); + trace->active = 0; + get_trace_clock(); + trace->freq_scale = trace_clock_freq_scale(); + + if (!trace->transport) { + printk(KERN_ERR "LTT : Transport is not set.\n"); + err = -EINVAL; + goto transport_error; + } + if (!try_module_get(trace->transport->owner)) { + printk(KERN_ERR "LTT : Can't lock transport module.\n"); + err = -ENODEV; + goto transport_error; + } + trace->ops = &trace->transport->ops; + + err = trace->ops->create_dirs(trace); + if (err) { + printk(KERN_ERR "LTT : Can't create dir for trace %s.\n", + trace_name); + goto dirs_error; + } + + local_irq_save(flags); + trace->start_freq = trace_clock_frequency(); + trace->start_tsc = trace_clock_read64(); + do_gettimeofday(&trace->start_time); + local_irq_restore(flags); + + for (chan = 0; chan < trace->nr_channels; chan++) { + channel_name = ltt_channels_get_name_from_index(chan); + WARN_ON(!channel_name); + /* + * note: sb_size and n_sb will be overwritten with updated + * values by channel creation. + */ + sb_size = trace->settings[chan].sb_size; + n_sb = trace->settings[chan].n_sb; + prepare_chan_size_num(&sb_size, &n_sb); + trace->channels[chan] = ltt_create_channel(channel_name, + trace, NULL, sb_size, n_sb, + trace->settings[chan].overwrite, + trace->settings[chan].switch_timer_interval, + trace->settings[chan].read_timer_interval); + if (err != 0) { + printk(KERN_ERR "LTT : Can't create channel %s.\n", + channel_name); + goto create_channel_error; + } + } + + list_del(&trace->list); + if (list_empty(<t_traces.head)) + set_kernel_trace_flag_all_tasks(); + list_add_rcu(&trace->list, <t_traces.head); + synchronize_trace(); + + ltt_unlock_traces(); + + return 0; + +create_channel_error: + for (chan--; chan >= 0; chan--) + ltt_channel_destroy(trace->channels[chan]); + trace->ops->remove_dirs(trace); + +dirs_error: + module_put(trace->transport->owner); +transport_error: + put_trace_clock(); +traces_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_alloc); + +/* + * It is worked as a wrapper for current version of ltt_control.ko. + * We will make a new ltt_control based on debugfs, and control each channel's + * buffer. + */ +static +int ltt_trace_create(const char *trace_name, const char *trace_type, + enum trace_mode mode, + unsigned int subbuf_size_low, unsigned int n_subbufs_low, + unsigned int subbuf_size_med, unsigned int n_subbufs_med, + unsigned int subbuf_size_high, unsigned int n_subbufs_high) +{ + int err = 0; + + err = ltt_trace_setup(trace_name); + if (IS_ERR_VALUE(err)) + return err; + + err = ltt_trace_set_type(trace_name, trace_type); + if (IS_ERR_VALUE(err)) + return err; + + err = ltt_trace_alloc(trace_name); + if (IS_ERR_VALUE(err)) + return err; + + return err; +} + +/* Must be called while sure that trace is in the list. */ +static int _ltt_trace_destroy(struct ltt_trace *trace) +{ + int err = -EPERM; + + if (trace == NULL) { + err = -ENOENT; + goto traces_error; + } + if (trace->active) { + printk(KERN_ERR + "LTT : Can't destroy trace %s : tracer is active\n", + trace->trace_name); + err = -EBUSY; + goto active_error; + } + /* Everything went fine */ + list_del_rcu(&trace->list); + synchronize_trace(); + if (list_empty(<t_traces.head)) { + clear_kernel_trace_flag_all_tasks(); + } + return 0; + + /* error handling */ +active_error: +traces_error: + return err; +} + +/* Sleepable part of the destroy */ +static void __ltt_trace_destroy(struct ltt_trace *trace) +{ + int i; + + for (i = 0; i < trace->nr_channels; i++) + ltt_channel_destroy(trace->channels[i]); + kref_put(&trace->kref, ltt_release_trace); +} + +int ltt_trace_destroy(const char *trace_name) +{ + int err = 0; + struct ltt_trace *trace; + + ltt_lock_traces(); + + trace = _ltt_trace_find(trace_name); + if (trace) { + err = _ltt_trace_destroy(trace); + if (err) + goto error; + + __ltt_trace_destroy(trace); + ltt_unlock_traces(); + put_trace_clock(); + + return 0; + } + + trace = _ltt_trace_find_setup(trace_name); + if (trace) { + _ltt_trace_free(trace); + ltt_unlock_traces(); + return 0; + } + + err = -ENOENT; + + /* Error handling */ +error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_destroy); + +/* must be called from within a traces lock. */ +static int _ltt_trace_start(struct ltt_trace *trace) +{ + int err = 0; + + if (trace == NULL) { + err = -ENOENT; + goto traces_error; + } + if (trace->active) + printk(KERN_INFO "LTT : Tracing already active for trace %s\n", + trace->trace_name); + if (!try_module_get(ltt_run_filter_owner)) { + err = -ENODEV; + printk(KERN_ERR "LTT : Can't lock filter module.\n"); + goto get_ltt_run_filter_error; + } + trace->active = 1; + /* Read by trace points without protection : be careful */ + ltt_traces.num_active_traces++; + return err; + + /* error handling */ +get_ltt_run_filter_error: +traces_error: + return err; +} + +int ltt_trace_start(const char *trace_name) +{ + int err = 0; + struct ltt_trace *trace; + + ltt_lock_traces(); + + trace = _ltt_trace_find(trace_name); + err = _ltt_trace_start(trace); + if (err) + goto no_trace; + + ltt_unlock_traces(); + + /* + * Call the kernel state dump. + * Events will be mixed with real kernel events, it's ok. + * Notice that there is no protection on the trace : that's exactly + * why we iterate on the list and check for trace equality instead of + * directly using this trace handle inside the logging function. + */ + + ltt_dump_marker_state(trace); + + if (!try_module_get(ltt_statedump_owner)) { + err = -ENODEV; + printk(KERN_ERR + "LTT : Can't lock state dump module.\n"); + } else { + ltt_statedump_functor(trace); + module_put(ltt_statedump_owner); + } + + return err; + + /* Error handling */ +no_trace: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_start); + +/* must be called from within traces lock */ +static int _ltt_trace_stop(struct ltt_trace *trace) +{ + int err = -EPERM; + + if (trace == NULL) { + err = -ENOENT; + goto traces_error; + } + if (!trace->active) + printk(KERN_INFO "LTT : Tracing not active for trace %s\n", + trace->trace_name); + if (trace->active) { + trace->active = 0; + ltt_traces.num_active_traces--; + synchronize_trace(); /* Wait for each tracing to be finished */ + } + module_put(ltt_run_filter_owner); + /* Everything went fine */ + return 0; + + /* Error handling */ +traces_error: + return err; +} + +int ltt_trace_stop(const char *trace_name) +{ + int err = 0; + struct ltt_trace *trace; + + ltt_lock_traces(); + trace = _ltt_trace_find(trace_name); + err = _ltt_trace_stop(trace); + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_trace_stop); + +/** + * ltt_control - Trace control in-kernel API + * @msg: Action to perform + * @trace_name: Trace on which the action must be done + * @trace_type: Type of trace (normal, flight, hybrid) + * @args: Arguments specific to the action + */ +int ltt_control(enum ltt_control_msg msg, const char *trace_name, + const char *trace_type, union ltt_control_args args) +{ + int err = -EPERM; + + printk(KERN_ALERT "ltt_control : trace %s\n", trace_name); + switch (msg) { + case LTT_CONTROL_START: + printk(KERN_DEBUG "Start tracing %s\n", trace_name); + err = ltt_trace_start(trace_name); + break; + case LTT_CONTROL_STOP: + printk(KERN_DEBUG "Stop tracing %s\n", trace_name); + err = ltt_trace_stop(trace_name); + break; + case LTT_CONTROL_CREATE_TRACE: + printk(KERN_DEBUG "Creating trace %s\n", trace_name); + err = ltt_trace_create(trace_name, trace_type, + args.new_trace.mode, + args.new_trace.subbuf_size_low, + args.new_trace.n_subbufs_low, + args.new_trace.subbuf_size_med, + args.new_trace.n_subbufs_med, + args.new_trace.subbuf_size_high, + args.new_trace.n_subbufs_high); + break; + case LTT_CONTROL_DESTROY_TRACE: + printk(KERN_DEBUG "Destroying trace %s\n", trace_name); + err = ltt_trace_destroy(trace_name); + break; + } + return err; +} +EXPORT_SYMBOL_GPL(ltt_control); + +/** + * ltt_filter_control - Trace filter control in-kernel API + * @msg: Action to perform on the filter + * @trace_name: Trace on which the action must be done + */ +int ltt_filter_control(enum ltt_filter_control_msg msg, const char *trace_name) +{ + int err; + struct ltt_trace *trace; + + printk(KERN_DEBUG "ltt_filter_control : trace %s\n", trace_name); + ltt_lock_traces(); + trace = _ltt_trace_find(trace_name); + if (trace == NULL) { + printk(KERN_ALERT + "Trace does not exist. Cannot proxy control request\n"); + err = -ENOENT; + goto trace_error; + } + if (!try_module_get(ltt_filter_control_owner)) { + err = -ENODEV; + goto get_module_error; + } + switch (msg) { + case LTT_FILTER_DEFAULT_ACCEPT: + printk(KERN_DEBUG + "Proxy filter default accept %s\n", trace_name); + err = (*ltt_filter_control_functor)(msg, trace); + break; + case LTT_FILTER_DEFAULT_REJECT: + printk(KERN_DEBUG + "Proxy filter default reject %s\n", trace_name); + err = (*ltt_filter_control_functor)(msg, trace); + break; + default: + err = -EPERM; + } + module_put(ltt_filter_control_owner); + +get_module_error: +trace_error: + ltt_unlock_traces(); + return err; +} +EXPORT_SYMBOL_GPL(ltt_filter_control); + +int __init ltt_init(void) +{ + /* Make sure no page fault can be triggered by this module */ + vmalloc_sync_all(); + init_timer_deferrable(<t_async_wakeup_timer); + return 0; +} + +module_init(ltt_init) + +static void __exit ltt_exit(void) +{ + struct ltt_trace *trace; + struct list_head *pos, *n; + + ltt_lock_traces(); + /* Stop each trace, currently being read by RCU read-side */ + list_for_each_entry_rcu(trace, <t_traces.head, list) + _ltt_trace_stop(trace); + /* Wait for quiescent state. Readers have preemption disabled. */ + synchronize_trace(); + /* Safe iteration is now permitted. It does not have to be RCU-safe + * because no readers are left. */ + list_for_each_safe(pos, n, <t_traces.head) { + trace = container_of(pos, struct ltt_trace, list); + /* _ltt_trace_destroy does a synchronize_trace() */ + _ltt_trace_destroy(trace); + __ltt_trace_destroy(trace); + } + /* free traces in pre-alloc status */ + list_for_each_safe(pos, n, <t_traces.setup_head) { + trace = container_of(pos, struct ltt_trace, list); + _ltt_trace_free(trace); + } + + ltt_unlock_traces(); +} + +module_exit(ltt_exit) + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Tracer Kernel API"); diff --git a/discard/ltt-type-serializer.c b/discard/ltt-type-serializer.c new file mode 100644 index 00000000..ed589c73 --- /dev/null +++ b/discard/ltt-type-serializer.c @@ -0,0 +1,94 @@ +/** + * ltt-type-serializer.c + * + * LTTng specialized type serializer. + * + * Copyright Mathieu Desnoyers, 2008. + * + * Dual LGPL v2.1/GPL v2 license. + */ +#include + +#include "ltt-type-serializer.h" +#include "ltt-relay-lockless.h" + +notrace +void _ltt_specialized_trace(void *probe_data, + void *serialize_private, unsigned int data_size, + unsigned int largest_align) +{ + struct ltt_event *event = probe_data; + int ret; + uint16_t eID; + size_t slot_size; + struct ltt_chanbuf *buf; + struct ltt_channel *chan; + struct ltt_session *session; + uint64_t tsc; + long buf_offset; + int cpu; + unsigned int rflags; + + /* disabled from tracepoints rcu_read_lock_sched_notrace(); */ + cpu = smp_processor_id(); + __get_cpu_var(ltt_nesting)++; + /* + * asm volatile and "memory" clobber prevent the compiler from moving + * instructions out of the ltt nesting count. This is required to ensure + * that probe side-effects which can cause recursion (e.g. unforeseen + * traps, divisions by 0, ...) are triggered within the incremented + * nesting count section. + */ + barrier(); + eID = event->id; + chan = event->chan; + session = chan->session; + + if (unlikely(!session->active)) + goto skip; + if (unlikely(!ltt_run_filter(session, chan, event))) + goto skip; +#ifdef LTT_DEBUG_EVENT_SIZE + rflags = LTT_RFLAG_ID_SIZE; +#else + if (unlikely(eID >= LTT_FREE_EVENTS)) + rflags = LTT_RFLAG_ID; + else + rflags = 0; +#endif + /* reserve space : header and data */ + ret = ltt_reserve_slot(chan, trace, data_size, largest_align, + cpu, &buf, &slot_size, &buf_offset, &tsc, + &rflags); + if (unlikely(ret < 0)) + goto skip; /* buffer full */ + + /* Out-of-order write : header and data */ + buf_offset = ltt_write_event_header(&buf->a, &chan->a, + buf_offset, eID, data_size, + tsc, rflags); + if (data_size) { + buf_offset += ltt_align(buf_offset, largest_align); + ltt_relay_write(&buf->a, &chan->a, buf_offset, + serialize_private, data_size); + buf_offset += data_size; + } + /* Out-of-order commit */ + ltt_commit_slot(buf, chan, buf_offset, data_size, slot_size); +skip: + /* + * asm volatile and "memory" clobber prevent the compiler from moving + * instructions out of the ltt nesting count. This is required to ensure + * that probe side-effects which can cause recursion (e.g. unforeseen + * traps, divisions by 0, ...) are triggered within the incremented + * nesting count section. + */ + barrier(); + __get_cpu_var(ltt_nesting)--; + /* disabled from tracepoints rcu_read_unlock_sched_notrace(); */ +} +EXPORT_SYMBOL_GPL(_ltt_specialized_trace); + +MODULE_LICENSE("GPL and additional rights"); +MODULE_AUTHOR("Mathieu Desnoyers"); +MODULE_DESCRIPTION("LTT type serializer"); diff --git a/discard/ltt-type-serializer.h b/discard/ltt-type-serializer.h new file mode 100644 index 00000000..fb870c8f --- /dev/null +++ b/discard/ltt-type-serializer.h @@ -0,0 +1,186 @@ +#ifndef _LTT_TYPE_SERIALIZER_H +#define _LTT_TYPE_SERIALIZER_H + +#include /* For IFNAMSIZ */ + +#include "ltt-tracer.h" + +/* + * largest_align must be non-zero, equal to the minimum between the largest type + * and sizeof(void *). + */ +extern void _ltt_specialized_trace(void *probe_data, + void *serialize_private, unsigned int data_size, + unsigned int largest_align); + +/* + * Statically check that 0 < largest_align < sizeof(void *) to make sure it is + * dumb-proof. It will make sure 0 is changed into 1 and unsigned long long is + * changed into sizeof(void *) on 32-bit architectures. + */ +static inline void ltt_specialized_trace(void *probe_data, + void *serialize_private, unsigned int data_size, + unsigned int largest_align) +{ + largest_align = min_t(unsigned int, largest_align, sizeof(void *)); + largest_align = max_t(unsigned int, largest_align, 1); + _ltt_specialized_trace(probe_data, serialize_private, data_size, + largest_align); +} + +/* + * Type serializer definitions. + */ + +/* + * Return size of structure without end-of-structure padding. + */ +#define serialize_sizeof(type) offsetof(typeof(type), end_field) + +struct serialize_long_int { + unsigned long f1; + unsigned int f2; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_int_int_long { + unsigned int f1; + unsigned int f2; + unsigned long f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_int_int_short { + unsigned int f1; + unsigned int f2; + unsigned short f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_long { + unsigned long f1; + unsigned long f2; + unsigned long f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_int { + unsigned long f1; + unsigned long f2; + unsigned int f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_short_char { + unsigned long f1; + unsigned long f2; + unsigned short f3; + unsigned char f4; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_short { + unsigned long f1; + unsigned long f2; + unsigned short f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_short_char { + unsigned long f1; + unsigned short f2; + unsigned char f3; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_short { + unsigned long f1; + unsigned short f2; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_char { + unsigned long f1; + unsigned char f2; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_ifname { + unsigned long f1; + unsigned char f2[IFNAMSIZ]; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_sizet_int { + size_t f1; + unsigned int f2; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_sizet_int { + unsigned long f1; + unsigned long f2; + size_t f3; + unsigned int f4; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_long_long_sizet_int_int { + unsigned long f1; + unsigned long f2; + size_t f3; + unsigned int f4; + unsigned int f5; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_l4421224411111 { + unsigned long f1; + uint32_t f2; + uint32_t f3; + uint16_t f4; + uint8_t f5; + uint16_t f6; + uint16_t f7; + uint32_t f8; + uint32_t f9; + uint8_t f10; + uint8_t f11; + uint8_t f12; + uint8_t f13; + uint8_t f14; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_l214421224411111 { + unsigned long f1; + uint16_t f2; + uint8_t f3; + uint32_t f4; + uint32_t f5; + uint16_t f6; + uint8_t f7; + uint16_t f8; + uint16_t f9; + uint32_t f10; + uint32_t f11; + uint8_t f12; + uint8_t f13; + uint8_t f14; + uint8_t f15; + uint8_t f16; + uint8_t end_field[0]; +} RING_BUFFER_ALIGN_ATTR; + +struct serialize_l4412228 { + unsigned long f1; + uint32_t f2; + uint32_t f3; + uint8_t f4; + uint16_t f5; + uint16_t f6; + uint16_t f7; + uint64_t f8; + unsigned char end_field[0]; +} RING_BUFFER_ALIGN_ATTR; +#endif /* _LTT_TYPE_SERIALIZER_H */ diff --git a/ltt-core.c b/ltt-core.c index 1a0424e9..fc55bdff 100644 --- a/ltt-core.c +++ b/ltt-core.c @@ -14,79 +14,9 @@ #include "ltt-tracer-core.h" -/* Traces structures */ -struct ltt_traces ltt_traces = { - .setup_head = LIST_HEAD_INIT(ltt_traces.setup_head), - .head = LIST_HEAD_INIT(ltt_traces.head), -}; -EXPORT_SYMBOL(ltt_traces); - -/* Traces list writer locking */ -static DEFINE_MUTEX(ltt_traces_mutex); - -/* root dentry mutex */ -static DEFINE_MUTEX(ltt_root_mutex); -/* dentry of ltt's root dir */ -static struct dentry *ltt_root_dentry; -static struct kref ltt_root_kref = { - .refcount = ATOMIC_INIT(0), -}; - -static void ltt_root_release(struct kref *ref) -{ - debugfs_remove(ltt_root_dentry); - ltt_root_dentry = NULL; -} - -void put_ltt_root(void) -{ - mutex_lock(<t_root_mutex); - if (ltt_root_dentry) - kref_put(<t_root_kref, ltt_root_release); - mutex_unlock(<t_root_mutex); -} -EXPORT_SYMBOL_GPL(put_ltt_root); - -struct dentry *get_ltt_root(void) -{ - mutex_lock(<t_root_mutex); - if (!ltt_root_dentry) { - ltt_root_dentry = debugfs_create_dir(LTT_ROOT, NULL); - if (!ltt_root_dentry) { - printk(KERN_ERR "LTT : create ltt root dir failed\n"); - goto out; - } - kref_init(<t_root_kref); - goto out; - } - kref_get(<t_root_kref); -out: - mutex_unlock(<t_root_mutex); - return ltt_root_dentry; -} -EXPORT_SYMBOL_GPL(get_ltt_root); - -/* - * ltt_lock_traces/ltt_unlock_traces also disables cpu hotplug. - */ -void ltt_lock_traces(void) -{ - mutex_lock(<t_traces_mutex); - get_online_cpus(); -} -EXPORT_SYMBOL_GPL(ltt_lock_traces); - -void ltt_unlock_traces(void) -{ - put_online_cpus(); - mutex_unlock(<t_traces_mutex); -} -EXPORT_SYMBOL_GPL(ltt_unlock_traces); - -DEFINE_PER_CPU(unsigned int, ltt_nesting); -EXPORT_PER_CPU_SYMBOL(ltt_nesting); - -int ltt_run_filter_default(void *trace, uint16_t eID) +int ltt_run_filter_default(struct ltt_session *session, + struct ltt_channel *chan, + struct ltt_event *event) { return 1; } diff --git a/ltt-debugfs-abi.c b/ltt-debugfs-abi.c index a1023613..7fb73392 100644 --- a/ltt-debugfs-abi.c +++ b/ltt-debugfs-abi.c @@ -53,7 +53,7 @@ int lttng_abi_create_session(void) session = ltt_session_create(); if (!session) return -ENOMEM; - session_fd = get_unused_fd_flags(O_RDWR); + session_fd = get_unused_fd(); if (session_fd < 0) { ret = session_fd; goto fd_error; @@ -119,7 +119,7 @@ int lttng_abi_create_channel(struct file *session_file, if (copy_from_user(&chan_param, uchan_param, sizeof(chan_param))) return -EFAULT; - chan_fd = get_unused_fd_flags(O_RDWR); + chan_fd = get_unused_fd(); if (chan_fd < 0) { ret = chan_fd; goto fd_error; @@ -226,7 +226,7 @@ int lttng_abi_open_stream(struct file *channel_file) if (!buf) return -ENOENT; - stream_fd = get_unused_fd_flags(O_RDWR); + stream_fd = get_unused_fd(); if (stream_fd < 0) { ret = stream_fd; goto fd_error; @@ -271,7 +271,7 @@ int lttng_abi_create_event(struct file *channel_file, goto name_error; } event_name[PATH_MAX - 1] = '\0'; - event_fd = get_unused_fd_flags(O_RDWR); + event_fd = get_unused_fd(); if (event_fd < 0) { ret = event_fd; goto fd_error; @@ -401,7 +401,7 @@ static const struct file_operations lttng_event_fops = { .release = lttng_event_release, }; -static int __init ltt_debugfs_abi_init(void) +int __init ltt_debugfs_abi_init(void) { int ret = 0; @@ -416,15 +416,7 @@ error: return ret; } -module_init(ltt_debugfs_abi_init); - -static void __exit ltt_debugfs_abi_exit(void) +void __exit ltt_debugfs_abi_exit(void) { debugfs_remove(lttng_dentry); } - -module_exit(ltt_debugfs_abi_exit); - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation DebugFS ABI"); diff --git a/ltt-event-header.c b/ltt-event-header.c index 94e29cbc..d2739fcb 100644 --- a/ltt-event-header.c +++ b/ltt-event-header.c @@ -14,14 +14,14 @@ #include #include "ltt-tracer.h" -size_t ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, +void ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, struct lib_ring_buffer_ctx *ctx, u16 eID, u32 event_size) { struct event_header header; u16 small_size; - switch (rflags) { + switch (ctx->rflags) { case LTT_RFLAG_ID_SIZE_TSC: header.id_time = 29 << LTT_TSC_BITS; break; @@ -36,10 +36,10 @@ size_t ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, header.id_time = 0; } - header.id_time |= (u32)tsc & LTT_TSC_MASK; + header.id_time |= (u32)ctx->tsc & LTT_TSC_MASK; lib_ring_buffer_write(config, ctx, &header, sizeof(header)); - switch (rflags) { + switch (ctx->rflags) { case LTT_RFLAG_ID_SIZE_TSC: small_size = (u16)min_t(u32, event_size, LTT_MAX_SMALL_SIZE); lib_ring_buffer_write(config, ctx, &eID, sizeof(u16)); @@ -64,7 +64,3 @@ size_t ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, } } EXPORT_SYMBOL_GPL(ltt_write_event_header_slow); - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Event Header"); diff --git a/ltt-events.c b/ltt-events.c index 7c7cda65..f3f7ab78 100644 --- a/ltt-events.c +++ b/ltt-events.c @@ -263,10 +263,18 @@ EXPORT_SYMBOL_GPL(ltt_transport_unregister); static int __init ltt_events_init(void) { + int ret; + event_cache = KMEM_CACHE(ltt_event, 0); if (!event_cache) return -ENOMEM; + ret = ltt_debugfs_abi_init(); + if (ret) + goto error; return 0; +error: + kmem_cache_destroy(event_cache); + return ret; } module_init(ltt_events_init); @@ -275,6 +283,7 @@ static void __exit ltt_events_exit(void) { struct ltt_session *session, *tmpsession; + ltt_debugfs_abi_exit(); list_for_each_entry_safe(session, tmpsession, &sessions, list) ltt_session_destroy(session); kmem_cache_destroy(event_cache); diff --git a/ltt-events.h b/ltt-events.h index 6711341c..0d68e16a 100644 --- a/ltt-events.h +++ b/ltt-events.h @@ -14,6 +14,7 @@ struct ltt_channel; struct ltt_session; +struct lib_ring_buffer_ctx; /* * ltt_event structure is referred to by the tracing fast path. It must be @@ -38,7 +39,9 @@ struct ltt_channel_ops { unsigned int read_timer_interval); void (*channel_destroy)(struct channel *chan); struct lib_ring_buffer *(*buffer_read_open)(struct channel *chan); - struct lib_ring_buffer *(*buffer_read_close)(struct lib_ring_buffer *buf); + void (*buffer_read_close)(struct lib_ring_buffer *buf); + int (*event_reserve)(struct lib_ring_buffer_ctx *ctx); + void (*event_commit)(struct lib_ring_buffer_ctx *ctx); }; struct ltt_channel { @@ -88,4 +91,7 @@ int _ltt_event_destroy(struct ltt_event *event); void ltt_transport_register(struct ltt_transport *transport); void ltt_transport_unregister(struct ltt_transport *transport); +int ltt_debugfs_abi_init(void); +void ltt_debugfs_abi_exit(void); + #endif /* _LTT_EVENTS_H */ diff --git a/ltt-marker-control.c b/ltt-marker-control.c deleted file mode 100644 index 61424308..00000000 --- a/ltt-marker-control.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (C) 2007 Mathieu Desnoyers - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ltt-tracer.h" - -#define DEFAULT_CHANNEL "cpu" -#define DEFAULT_PROBE "default" - -LIST_HEAD(probes_list); - -/* - * Mutex protecting the probe slab cache. - * Nests inside the traces mutex. - */ -DEFINE_MUTEX(probes_mutex); - -struct ltt_available_probe default_probe = { - .name = "default", - .format = NULL, - .probe_func = ltt_vtrace, - .callbacks[0] = ltt_serialize_data, -}; - -static struct kmem_cache *markers_loaded_cachep; -static LIST_HEAD(markers_loaded_list); -/* - * List sorted by name strcmp order. - */ -static LIST_HEAD(probes_registered_list); - -static struct ltt_available_probe *get_probe_from_name(const char *pname) -{ - struct ltt_available_probe *iter; - int comparison, found = 0; - - if (!pname) - pname = DEFAULT_PROBE; - list_for_each_entry(iter, &probes_registered_list, node) { - comparison = strcmp(pname, iter->name); - if (!comparison) - found = 1; - if (comparison <= 0) - break; - } - if (found) - return iter; - else - return NULL; -} - -int ltt_probe_register(struct ltt_available_probe *pdata) -{ - int ret = 0; - int comparison; - struct ltt_available_probe *iter; - - mutex_lock(&probes_mutex); - list_for_each_entry_reverse(iter, &probes_registered_list, node) { - comparison = strcmp(pdata->name, iter->name); - if (!comparison) { - ret = -EBUSY; - goto end; - } else if (comparison > 0) { - /* We belong to the location right after iter. */ - list_add(&pdata->node, &iter->node); - goto end; - } - } - /* Should be added at the head of the list */ - list_add(&pdata->node, &probes_registered_list); -end: - mutex_unlock(&probes_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(ltt_probe_register); - -/* - * Called when a probe does not want to be called anymore. - */ -int ltt_probe_unregister(struct ltt_available_probe *pdata) -{ - int ret = 0; - struct ltt_active_marker *amark, *tmp; - - mutex_lock(&probes_mutex); - list_for_each_entry_safe(amark, tmp, &markers_loaded_list, node) { - if (amark->probe == pdata) { - ret = marker_probe_unregister_private_data( - pdata->probe_func, amark); - if (ret) - goto end; - list_del(&amark->node); - kmem_cache_free(markers_loaded_cachep, amark); - } - } - list_del(&pdata->node); -end: - mutex_unlock(&probes_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(ltt_probe_unregister); - -/* - * Connect marker "mname" to probe "pname". - * Only allow _only_ probe instance to be connected to a marker. - */ -int ltt_marker_connect(const char *channel, const char *mname, - const char *pname) - -{ - int ret; - struct ltt_active_marker *pdata; - struct ltt_available_probe *probe; - - ltt_lock_traces(); - mutex_lock(&probes_mutex); - probe = get_probe_from_name(pname); - if (!probe) { - ret = -ENOENT; - goto end; - } - pdata = marker_get_private_data(channel, mname, probe->probe_func, 0); - if (pdata && !IS_ERR(pdata)) { - ret = -EEXIST; - goto end; - } - pdata = kmem_cache_zalloc(markers_loaded_cachep, GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - goto end; - } - pdata->probe = probe; - /* - * ID has priority over channel in case of conflict. - */ - ret = marker_probe_register(channel, mname, NULL, - probe->probe_func, pdata); - if (ret) - kmem_cache_free(markers_loaded_cachep, pdata); - else - list_add(&pdata->node, &markers_loaded_list); -end: - mutex_unlock(&probes_mutex); - ltt_unlock_traces(); - return ret; -} -EXPORT_SYMBOL_GPL(ltt_marker_connect); - -/* - * Disconnect marker "mname", probe "pname". - */ -int ltt_marker_disconnect(const char *channel, const char *mname, - const char *pname) -{ - struct ltt_active_marker *pdata; - struct ltt_available_probe *probe; - int ret = 0; - - mutex_lock(&probes_mutex); - probe = get_probe_from_name(pname); - if (!probe) { - ret = -ENOENT; - goto end; - } - pdata = marker_get_private_data(channel, mname, probe->probe_func, 0); - if (IS_ERR(pdata)) { - ret = PTR_ERR(pdata); - goto end; - } else if (!pdata) { - /* - * Not registered by us. - */ - ret = -EPERM; - goto end; - } - ret = marker_probe_unregister(channel, mname, probe->probe_func, pdata); - if (ret) - goto end; - else { - list_del(&pdata->node); - kmem_cache_free(markers_loaded_cachep, pdata); - } -end: - mutex_unlock(&probes_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(ltt_marker_disconnect); - -static void disconnect_all_markers(void) -{ - struct ltt_active_marker *pdata, *tmp; - - list_for_each_entry_safe(pdata, tmp, &markers_loaded_list, node) { - marker_probe_unregister_private_data(pdata->probe->probe_func, - pdata); - list_del(&pdata->node); - kmem_cache_free(markers_loaded_cachep, pdata); - } -} - -static int __init marker_control_init(void) -{ - int ret; - - markers_loaded_cachep = KMEM_CACHE(ltt_active_marker, 0); - - ret = ltt_probe_register(&default_probe); - BUG_ON(ret); - ret = ltt_marker_connect("metadata", "core_marker_format", - DEFAULT_PROBE); - BUG_ON(ret); - ret = ltt_marker_connect("metadata", "core_marker_id", DEFAULT_PROBE); - BUG_ON(ret); - - return 0; -} -module_init(marker_control_init); - -static void __exit marker_control_exit(void) -{ - int ret; - - ret = ltt_marker_disconnect("metadata", "core_marker_format", - DEFAULT_PROBE); - BUG_ON(ret); - ret = ltt_marker_disconnect("metadata", "core_marker_id", - DEFAULT_PROBE); - BUG_ON(ret); - ret = ltt_probe_unregister(&default_probe); - BUG_ON(ret); - disconnect_all_markers(); - kmem_cache_destroy(markers_loaded_cachep); - marker_synchronize_unregister(); -} -module_exit(marker_control_exit); - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Linux Trace Toolkit Marker Control"); diff --git a/ltt-ring-buffer-client.h b/ltt-ring-buffer-client.h index 93c793ff..229c06aa 100644 --- a/ltt-ring-buffer-client.h +++ b/ltt-ring-buffer-client.h @@ -34,29 +34,29 @@ size_t client_record_header_size(const struct lib_ring_buffer_config *config, } /** - * client_subbuffer_header_size - called on buffer-switch to a new sub-buffer + * 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_subbuffer_header_size(void) +static size_t client_packet_header_size(void) { - return offsetof(struct subbuffer_header, header_end); + return offsetof(struct packet_header, header_end); } static void client_buffer_begin(struct lib_ring_buffer *buf, u64 tsc, unsigned int subbuf_idx) { struct channel *chan = buf->backend.chan; - struct subbuffer_header *header = - (struct subbuffer_header *) + struct packet_header *header = + (struct packet_header *) lib_ring_buffer_offset_address(&buf->backend, subbuf_idx * chan->backend.subbuf_size); - header->cycle_count_begin = tsc; - header->data_size = 0xFFFFFFFF; /* for debugging */ - write_trace_header(chan->backend.priv, header); + header->timestamp_begin = tsc; + header->content_size = 0xFFFFFFFF; /* for debugging */ + write_trace_header(&client_config, header); } /* @@ -67,25 +67,25 @@ static void client_buffer_end(struct lib_ring_buffer *buf, u64 tsc, unsigned int subbuf_idx, unsigned long data_size) { struct channel *chan = buf->backend.chan; - struct subbuffer_header *header = - (struct subbuffer_header *) + struct packet_header *header = + (struct packet_header *) lib_ring_buffer_offset_address(&buf->backend, subbuf_idx * chan->backend.subbuf_size); unsigned long records_lost = 0; - header->data_size = data_size; - header->subbuf_size = PAGE_ALIGN(data_size); - header->cycle_count_end = tsc; + header->content_size = data_size; + header->packet_size = PAGE_ALIGN(data_size); + header->timestamp_end = tsc; 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); header->events_lost = records_lost; - header->subbuf_corrupt = 0; /* deprecated */ } static int client_buffer_create(struct lib_ring_buffer *buf, void *priv, int cpu, const char *name) { + return 0; } static void client_buffer_finalize(struct lib_ring_buffer *buf, void *priv, int cpu) @@ -95,7 +95,7 @@ static void client_buffer_finalize(struct lib_ring_buffer *buf, void *priv, int static const struct 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_subbuffer_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, @@ -118,12 +118,13 @@ static const struct lib_ring_buffer_config client_config = { }; static -struct channel *ltt_channel_create(struct ltt_session *session, void *buf_addr, - size_t subbuf_size, size_t num_subbuf, - unsigned int switch_timer_interval, - unsigned int read_timer_interval) +struct channel *_channel_create(const char *name, + struct ltt_session *session, void *buf_addr, + size_t subbuf_size, size_t num_subbuf, + unsigned int switch_timer_interval, + unsigned int read_timer_interval) { - return channel_create(&client_config, "[lttng]", session, buf_addr, + return channel_create(&client_config, name, session, buf_addr, subbuf_size, num_subbuf, switch_timer_interval, read_timer_interval); } @@ -140,8 +141,8 @@ struct lib_ring_buffer *ltt_buffer_read_open(struct channel *chan) struct lib_ring_buffer *buf; int cpu; - for_each_channel_cpu(cpu, chan->chan) { - buf = channel_get_ring_buffer(&config_config, chan, cpu); + for_each_channel_cpu(cpu, chan) { + buf = channel_get_ring_buffer(&client_config, chan, cpu); if (!lib_ring_buffer_open_read(buf)) return buf; } @@ -149,19 +150,48 @@ struct lib_ring_buffer *ltt_buffer_read_open(struct channel *chan) } static -struct lib_ring_buffer *ltt_buffer_read_close(struct lib_ring_buffer *buf) +void ltt_buffer_read_close(struct lib_ring_buffer *buf) { lib_ring_buffer_release_read(buf); + +} + +int ltt_event_reserve(struct lib_ring_buffer_ctx *ctx) +{ + int ret, cpu; + + cpu = lib_ring_buffer_get_cpu(&client_config); + if (cpu < 0) + return -EPERM; + ctx->cpu = cpu; + + ret = lib_ring_buffer_reserve(&client_config, ctx); + if (ret) + goto put; + return ret; + +put: + lib_ring_buffer_put_cpu(&client_config); + return ret; } +void ltt_event_commit(struct lib_ring_buffer_ctx *ctx) +{ + lib_ring_buffer_commit(&client_config, ctx); + lib_ring_buffer_put_cpu(&client_config); +} + + static struct ltt_transport ltt_relay_transport = { .name = "relay-" RING_BUFFER_MODE_TEMPLATE_STRING, .owner = THIS_MODULE, .ops = { - .create_channel = ltt_channel_create, - .destroy_channel = ltt_channel_destroy, + .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, }, }; @@ -172,12 +202,16 @@ static int __init ltt_ring_buffer_client_init(void) return 0; } +module_init(ltt_ring_buffer_client_init); + static void __exit ltt_ring_buffer_client_exit(void) { printk(KERN_INFO "LTT : ltt ring buffer client exit\n"); ltt_transport_unregister(<t_relay_transport); } +module_exit(ltt_ring_buffer_client_exit); + MODULE_LICENSE("GPL and additional rights"); MODULE_AUTHOR("Mathieu Desnoyers"); MODULE_DESCRIPTION("LTTng ring buffer " RING_BUFFER_MODE_TEMPLATE_STRING diff --git a/ltt-serialize.c b/ltt-serialize.c deleted file mode 100644 index 1d5a5dfb..00000000 --- a/ltt-serialize.c +++ /dev/null @@ -1,969 +0,0 @@ -/* - * LTTng serializing code. - * - * Copyright Mathieu Desnoyers, March 2007. - * - * Dual LGPL v2.1/GPL v2 license. - * - * See this discussion about weirdness about passing va_list and then va_list to - * functions. (related to array argument passing). va_list seems to be - * implemented as an array on x86_64, but not on i386... This is why we pass a - * va_list * to ltt_vtrace. - */ - -#include -#include -#include -#include - -#include "ltt-tracer.h" -#include "ltt-relay-lockless.h" - -enum ltt_type { - LTT_TYPE_SIGNED_INT, - LTT_TYPE_UNSIGNED_INT, - LTT_TYPE_STRING, - LTT_TYPE_NONE, -}; - -#define LTT_ATTRIBUTE_NETWORK_BYTE_ORDER (1<<1) - -/* - * Stack used to keep track of string length at size calculation, passed to - * string copy to handle racy input string updates. - * Can be used by any context; this is ensured by putting the stack position - * back to its original position after using it. - */ -#define TRACER_STACK_LEN (PAGE_SIZE / sizeof(unsigned long)) -static DEFINE_PER_CPU(unsigned long [TRACER_STACK_LEN], - tracer_stack); -static DEFINE_PER_CPU(unsigned int, tracer_stack_pos); - -/* - * Inspired from vsnprintf - * - * The serialization format string supports the basic printf format strings. - * In addition, it defines new formats that can be used to serialize more - * complex/non portable data structures. - * - * Typical use: - * - * field_name %ctype - * field_name #tracetype %ctype - * field_name #tracetype %ctype1 %ctype2 ... - * - * A conversion is performed between format string types supported by GCC and - * the trace type requested. GCC type is used to perform type checking on format - * strings. Trace type is used to specify the exact binary representation - * in the trace. A mapping is done between one or more GCC types to one trace - * type. Sign extension, if required by the conversion, is performed following - * the trace type. - * - * If a gcc format is not declared with a trace format, the gcc format is - * also used as binary representation in the trace. - * - * Strings are supported with %s. - * A single tracetype (sequence) can take multiple c types as parameter. - * - * c types: - * - * see printf(3). - * - * Note: to write a uint32_t in a trace, the following expression is recommended - * si it can be portable: - * - * ("#4u%lu", (unsigned long)var) - * - * trace types: - * - * Serialization specific formats : - * - * Fixed size integers - * #1u writes uint8_t - * #2u writes uint16_t - * #4u writes uint32_t - * #8u writes uint64_t - * #1d writes int8_t - * #2d writes int16_t - * #4d writes int32_t - * #8d writes int64_t - * i.e.: - * #1u%lu #2u%lu #4d%lu #8d%lu #llu%hu #d%lu - * - * * Attributes: - * - * n: (for network byte order) - * #ntracetype%ctype - * is written in the trace in network byte order. - * - * i.e.: #bn4u%lu, #n%lu, #b%u - * - * TODO (eventually) - * Variable length sequence - * #a #tracetype1 #tracetype2 %array_ptr %elem_size %num_elems - * In the trace: - * #a specifies that this is a sequence - * #tracetype1 is the type of elements in the sequence - * #tracetype2 is the type of the element count - * GCC input: - * array_ptr is a pointer to an array that contains members of size - * elem_size. - * num_elems is the number of elements in the array. - * i.e.: #a #lu #lu %p %lu %u - * - * Callback - * #k callback (taken from the probe data) - * The following % arguments are exepected by the callback - * - * i.e.: #a #lu #lu #k %p - * - * Note: No conversion is done from floats to integers, nor from integers to - * floats between c types and trace types. float conversion from double to float - * or from float to double is also not supported. - * - * REMOVE - * %*b expects sizeof(data), data - * where sizeof(data) is 1, 2, 4 or 8 - * - * Fixed length struct, union or array. - * FIXME: unable to extract those sizes statically. - * %*r expects sizeof(*ptr), ptr - * %*.*r expects sizeof(*ptr), __alignof__(*ptr), ptr - * struct and unions removed. - * Fixed length array: - * [%p]#a[len #tracetype] - * i.e.: [%p]#a[12 #lu] - * - * Variable length sequence - * %*.*:*v expects sizeof(*ptr), __alignof__(*ptr), elem_num, ptr - * where elem_num is the number of elements in the sequence - */ -static inline -const char *parse_trace_type(const char *fmt, char *trace_size, - enum ltt_type *trace_type, - unsigned long *attributes) -{ - int qualifier; /* 'h', 'l', or 'L' for integer fields */ - /* 'z' support added 23/7/1999 S.H. */ - /* 'z' changed to 'Z' --davidm 1/25/99 */ - /* 't' added for ptrdiff_t */ - - /* parse attributes. */ -repeat: - switch (*fmt) { - case 'n': - *attributes |= LTT_ATTRIBUTE_NETWORK_BYTE_ORDER; - ++fmt; - goto repeat; - } - - /* get the conversion qualifier */ - qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || - *fmt == 'Z' || *fmt == 'z' || *fmt == 't' || - *fmt == 'S' || *fmt == '1' || *fmt == '2' || - *fmt == '4' || *fmt == 8) { - qualifier = *fmt; - ++fmt; - if (qualifier == 'l' && *fmt == 'l') { - qualifier = 'L'; - ++fmt; - } - } - - switch (*fmt) { - case 'c': - *trace_type = LTT_TYPE_UNSIGNED_INT; - *trace_size = sizeof(unsigned char); - goto parse_end; - case 's': - *trace_type = LTT_TYPE_STRING; - goto parse_end; - case 'p': - *trace_type = LTT_TYPE_UNSIGNED_INT; - *trace_size = sizeof(void *); - goto parse_end; - case 'd': - case 'i': - *trace_type = LTT_TYPE_SIGNED_INT; - break; - case 'o': - case 'u': - case 'x': - case 'X': - *trace_type = LTT_TYPE_UNSIGNED_INT; - break; - default: - if (!*fmt) - --fmt; - goto parse_end; - } - switch (qualifier) { - case 'L': - *trace_size = sizeof(long long); - break; - case 'l': - *trace_size = sizeof(long); - break; - case 'Z': - case 'z': - *trace_size = sizeof(size_t); - break; - case 't': - *trace_size = sizeof(ptrdiff_t); - break; - case 'h': - *trace_size = sizeof(short); - break; - case '1': - *trace_size = sizeof(uint8_t); - break; - case '2': - *trace_size = sizeof(uint16_t); - break; - case '4': - *trace_size = sizeof(uint32_t); - break; - case '8': - *trace_size = sizeof(uint64_t); - break; - default: - *trace_size = sizeof(int); - } - -parse_end: - return fmt; -} - -/* - * Restrictions: - * Field width and precision are *not* supported. - * %n not supported. - */ -static inline -const char *parse_c_type(const char *fmt, char *c_size, enum ltt_type *c_type, - char *outfmt) -{ - int qualifier; /* 'h', 'l', or 'L' for integer fields */ - /* 'z' support added 23/7/1999 S.H. */ - /* 'z' changed to 'Z' --davidm 1/25/99 */ - /* 't' added for ptrdiff_t */ - - /* process flags : ignore standard print formats for now. */ -repeat: - switch (*fmt) { - case '-': - case '+': - case ' ': - case '#': - case '0': - ++fmt; - goto repeat; - } - - /* get the conversion qualifier */ - qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || - *fmt == 'Z' || *fmt == 'z' || *fmt == 't' || - *fmt == 'S') { - qualifier = *fmt; - ++fmt; - if (qualifier == 'l' && *fmt == 'l') { - qualifier = 'L'; - ++fmt; - } - } - - if (outfmt) { - if (qualifier != -1) - *outfmt++ = (char)qualifier; - *outfmt++ = *fmt; - *outfmt = 0; - } - - switch (*fmt) { - case 'c': - *c_type = LTT_TYPE_UNSIGNED_INT; - *c_size = sizeof(unsigned char); - goto parse_end; - case 's': - *c_type = LTT_TYPE_STRING; - goto parse_end; - case 'p': - *c_type = LTT_TYPE_UNSIGNED_INT; - *c_size = sizeof(void *); - goto parse_end; - case 'd': - case 'i': - *c_type = LTT_TYPE_SIGNED_INT; - break; - case 'o': - case 'u': - case 'x': - case 'X': - *c_type = LTT_TYPE_UNSIGNED_INT; - break; - default: - if (!*fmt) - --fmt; - goto parse_end; - } - switch (qualifier) { - case 'L': - *c_size = sizeof(long long); - break; - case 'l': - *c_size = sizeof(long); - break; - case 'Z': - case 'z': - *c_size = sizeof(size_t); - break; - case 't': - *c_size = sizeof(ptrdiff_t); - break; - case 'h': - *c_size = sizeof(short); - break; - default: - *c_size = sizeof(int); - } - -parse_end: - return fmt; -} - -static inline -size_t serialize_trace_data(struct ltt_chanbuf *buf, size_t buf_offset, - char trace_size, enum ltt_type trace_type, - char c_size, enum ltt_type c_type, - unsigned int *stack_pos_ctx, - int *largest_align, - va_list *args) -{ - union { - unsigned long v_ulong; - uint64_t v_uint64; - struct { - const char *s; - size_t len; - } v_string; - } tmp; - - /* - * Be careful about sign extension here. - * Sign extension is done with the destination (trace) type. - */ - switch (trace_type) { - case LTT_TYPE_SIGNED_INT: - switch (c_size) { - case 1: - tmp.v_ulong = (long)(int8_t)va_arg(*args, int); - break; - case 2: - tmp.v_ulong = (long)(int16_t)va_arg(*args, int); - break; - case 4: - tmp.v_ulong = (long)(int32_t)va_arg(*args, int); - break; - case 8: - tmp.v_uint64 = va_arg(*args, int64_t); - break; - default: - BUG(); - } - break; - case LTT_TYPE_UNSIGNED_INT: - switch (c_size) { - case 1: - tmp.v_ulong = (unsigned long)(uint8_t)va_arg(*args, unsigned int); - break; - case 2: - tmp.v_ulong = (unsigned long)(uint16_t)va_arg(*args, unsigned int); - break; - case 4: - tmp.v_ulong = (unsigned long)(uint32_t)va_arg(*args, unsigned int); - break; - case 8: - tmp.v_uint64 = va_arg(*args, uint64_t); - break; - default: - BUG(); - } - break; - case LTT_TYPE_STRING: - tmp.v_string.s = va_arg(*args, const char *); - if ((unsigned long)tmp.v_string.s < PAGE_SIZE) - tmp.v_string.s = ""; - if (!buf) { - /* - * Reserve tracer stack entry. - */ - __get_cpu_var(tracer_stack_pos)++; - WARN_ON_ONCE(__get_cpu_var(tracer_stack_pos) - > TRACER_STACK_LEN); - barrier(); - __get_cpu_var(tracer_stack)[*stack_pos_ctx] = - strlen(tmp.v_string.s) + 1; - } - tmp.v_string.len = __get_cpu_var(tracer_stack) - [(*stack_pos_ctx)++]; - if (buf) - ltt_relay_strncpy(&buf->a, buf->a.chan, buf_offset, - tmp.v_string.s, tmp.v_string.len); - buf_offset += tmp.v_string.len; - goto copydone; - default: - BUG(); - } - - /* - * If trace_size is lower or equal to 4 bytes, there is no sign - * extension to do because we are already encoded in a long. Therefore, - * we can combine signed and unsigned ops. 4 bytes float also works - * with this, because we do a simple copy of 4 bytes into 4 bytes - * without manipulation (and we do not support conversion from integers - * to floats). - * It is also the case if c_size is 8 bytes, which is the largest - * possible integer. - */ - if (ltt_get_alignment()) { - buf_offset += ltt_align(buf_offset, trace_size); - if (largest_align) - *largest_align = max_t(int, *largest_align, trace_size); - } - if (trace_size <= 4 || c_size == 8) { - if (buf) { - switch (trace_size) { - case 1: - if (c_size == 8) - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint8_t[]){ (uint8_t)tmp.v_uint64 }, - sizeof(uint8_t)); - else - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint8_t[]){ (uint8_t)tmp.v_ulong }, - sizeof(uint8_t)); - break; - case 2: - if (c_size == 8) - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint16_t[]){ (uint16_t)tmp.v_uint64 }, - sizeof(uint16_t)); - else - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint16_t[]){ (uint16_t)tmp.v_ulong }, - sizeof(uint16_t)); - break; - case 4: - if (c_size == 8) - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint32_t[]){ (uint32_t)tmp.v_uint64 }, - sizeof(uint32_t)); - else - ltt_relay_write(&buf->a, buf->a.chan, - buf_offset, - (uint32_t[]){ (uint32_t)tmp.v_ulong }, - sizeof(uint32_t)); - break; - case 8: - /* - * c_size cannot be other than 8 here because - * trace_size > 4. - */ - ltt_relay_write(&buf->a, buf->a.chan, buf_offset, - (uint64_t[]){ (uint64_t)tmp.v_uint64 }, - sizeof(uint64_t)); - break; - default: - BUG(); - } - } - buf_offset += trace_size; - goto copydone; - } else { - /* - * Perform sign extension. - */ - if (buf) { - switch (trace_type) { - case LTT_TYPE_SIGNED_INT: - ltt_relay_write(&buf->a, buf->a.chan, buf_offset, - (int64_t[]){ (int64_t)tmp.v_ulong }, - sizeof(int64_t)); - break; - case LTT_TYPE_UNSIGNED_INT: - ltt_relay_write(&buf->a, buf->a.chan, buf_offset, - (uint64_t[]){ (uint64_t)tmp.v_ulong }, - sizeof(uint64_t)); - break; - default: - BUG(); - } - } - buf_offset += trace_size; - goto copydone; - } - -copydone: - return buf_offset; -} - -notrace size_t -ltt_serialize_data(struct ltt_chanbuf *buf, size_t buf_offset, - struct ltt_serialize_closure *closure, - void *serialize_private, unsigned int stack_pos_ctx, - int *largest_align, const char *fmt, va_list *args) -{ - char trace_size = 0, c_size = 0; /* - * 0 (unset), 1, 2, 4, 8 bytes. - */ - enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; - unsigned long attributes = 0; - - for (; *fmt ; ++fmt) { - switch (*fmt) { - case '#': - /* tracetypes (#) */ - ++fmt; /* skip first '#' */ - if (*fmt == '#') /* Escaped ## */ - break; - attributes = 0; - fmt = parse_trace_type(fmt, &trace_size, &trace_type, - &attributes); - break; - case '%': - /* c types (%) */ - ++fmt; /* skip first '%' */ - if (*fmt == '%') /* Escaped %% */ - break; - fmt = parse_c_type(fmt, &c_size, &c_type, NULL); - /* - * Output c types if no trace types has been - * specified. - */ - if (!trace_size) - trace_size = c_size; - if (trace_type == LTT_TYPE_NONE) - trace_type = c_type; - if (c_type == LTT_TYPE_STRING) - trace_type = LTT_TYPE_STRING; - /* perform trace write */ - buf_offset = serialize_trace_data(buf, buf_offset, - trace_size, - trace_type, c_size, - c_type, - &stack_pos_ctx, - largest_align, - args); - trace_size = 0; - c_size = 0; - trace_type = LTT_TYPE_NONE; - c_size = LTT_TYPE_NONE; - attributes = 0; - break; - /* default is to skip the text, doing nothing */ - } - } - return buf_offset; -} -EXPORT_SYMBOL_GPL(ltt_serialize_data); - -static inline -uint64_t unserialize_base_type(struct ltt_chanbuf *buf, - size_t *ppos, char trace_size, - enum ltt_type trace_type) -{ - uint64_t tmp; - - *ppos += ltt_align(*ppos, trace_size); - ltt_relay_read(&buf->a, *ppos, &tmp, trace_size); - *ppos += trace_size; - - switch (trace_type) { - case LTT_TYPE_SIGNED_INT: - switch (trace_size) { - case 1: - return (uint64_t)*(int8_t *)&tmp; - case 2: - return (uint64_t)*(int16_t *)&tmp; - case 4: - return (uint64_t)*(int32_t *)&tmp; - case 8: - return tmp; - } - break; - case LTT_TYPE_UNSIGNED_INT: - switch (trace_size) { - case 1: - return (uint64_t)*(uint8_t *)&tmp; - case 2: - return (uint64_t)*(uint16_t *)&tmp; - case 4: - return (uint64_t)*(uint32_t *)&tmp; - case 8: - return tmp; - } - break; - default: - break; - } - - BUG(); - return 0; -} - -static -int serialize_printf_data(struct ltt_chanbuf *buf, size_t *ppos, - char trace_size, enum ltt_type trace_type, - char c_size, enum ltt_type c_type, char *output, - ssize_t outlen, const char *outfmt) -{ - u64 value; - outlen = outlen < 0 ? 0 : outlen; - - if (trace_type == LTT_TYPE_STRING) { - size_t len = ltt_relay_read_cstr(&buf->a, *ppos, output, - outlen); - *ppos += len + 1; - return len; - } - - value = unserialize_base_type(buf, ppos, trace_size, trace_type); - - if (c_size == 8) - return snprintf(output, outlen, outfmt, value); - else - return snprintf(output, outlen, outfmt, (unsigned int)value); -} - -/** - * ltt_serialize_printf - Format a string and place it in a buffer - * @buf: The ltt-relay buffer that store binary data - * @buf_offset: binary data's offset in @buf (should be masked to use as offset) - * @msg_size: return message's length - * @output: The buffer to place the result into - * @outlen: The size of the buffer, including the trailing '\0' - * @fmt: The format string to use - * - * The return value is the number of characters which would - * be generated for the given input, excluding the trailing - * '\0', as per ISO C99. If the return is greater than or equal to @outlen, - * the resulting string is truncated. - */ -size_t ltt_serialize_printf(struct ltt_chanbuf *buf, unsigned long buf_offset, - size_t *msg_size, char *output, size_t outlen, - const char *fmt) -{ - char trace_size = 0, c_size = 0; /* - * 0 (unset), 1, 2, 4, 8 bytes. - */ - enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; - unsigned long attributes = 0; - char outfmt[4] = "%"; - size_t outpos = 0; - size_t len; - size_t msgpos = buf_offset; - - for (; *fmt ; ++fmt) { - switch (*fmt) { - case '#': - /* tracetypes (#) */ - ++fmt; /* skip first '#' */ - if (*fmt == '#') { /* Escaped ## */ - if (outpos < outlen) - output[outpos] = '#'; - outpos++; - break; - } - attributes = 0; - fmt = parse_trace_type(fmt, &trace_size, &trace_type, - &attributes); - break; - case '%': - /* c types (%) */ - ++fmt; /* skip first '%' */ - if (*fmt == '%') { /* Escaped %% */ - if (outpos < outlen) - output[outpos] = '%'; - outpos++; - break; - } - fmt = parse_c_type(fmt, &c_size, &c_type, outfmt + 1); - /* - * Output c types if no trace types has been - * specified. - */ - if (!trace_size) - trace_size = c_size; - if (trace_type == LTT_TYPE_NONE) - trace_type = c_type; - if (c_type == LTT_TYPE_STRING) - trace_type = LTT_TYPE_STRING; - - /* perform trace printf */ - len = serialize_printf_data(buf, &msgpos, trace_size, - trace_type, c_size, c_type, - output + outpos, - outlen - outpos, outfmt); - outpos += len; - trace_size = 0; - c_size = 0; - trace_type = LTT_TYPE_NONE; - c_size = LTT_TYPE_NONE; - attributes = 0; - break; - default: - if (outpos < outlen) - output[outpos] = *fmt; - outpos++; - break; - } - } - if (msg_size) - *msg_size = (size_t)(msgpos - buf_offset); - /* - * Make sure we end output with terminating \0 when truncated. - */ - if (outpos >= outlen + 1) - output[outlen] = '\0'; - return outpos; -} -EXPORT_SYMBOL_GPL(ltt_serialize_printf); - -#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS - -unsigned int ltt_fmt_largest_align(size_t align_drift, const char *fmt) -{ - char trace_size = 0, c_size = 0; - enum ltt_type trace_type = LTT_TYPE_NONE, c_type = LTT_TYPE_NONE; - unsigned long attributes = 0; - int largest_align = 1; - - for (; *fmt ; ++fmt) { - switch (*fmt) { - case '#': - /* tracetypes (#) */ - ++fmt; /* skip first '#' */ - if (*fmt == '#') /* Escaped ## */ - break; - attributes = 0; - fmt = parse_trace_type(fmt, &trace_size, &trace_type, - &attributes); - - largest_align = max_t(int, largest_align, trace_size); - if (largest_align >= ltt_get_alignment()) - goto exit; - break; - case '%': - /* c types (%) */ - ++fmt; /* skip first '%' */ - if (*fmt == '%') /* Escaped %% */ - break; - fmt = parse_c_type(fmt, &c_size, &c_type, NULL); - /* - * Output c types if no trace types has been - * specified. - */ - if (!trace_size) - trace_size = c_size; - if (trace_type == LTT_TYPE_NONE) - trace_type = c_type; - if (c_type == LTT_TYPE_STRING) - trace_type = LTT_TYPE_STRING; - - largest_align = max_t(int, largest_align, trace_size); - if (largest_align >= ltt_get_alignment()) - goto exit; - - trace_size = 0; - c_size = 0; - trace_type = LTT_TYPE_NONE; - c_size = LTT_TYPE_NONE; - break; - } - } - -exit: - largest_align = min_t(int, largest_align, ltt_get_alignment()); - return (largest_align - align_drift) & (largest_align - 1); -} -EXPORT_SYMBOL_GPL(ltt_fmt_largest_align); - -#endif - -/* - * Calculate data size - * Assume that the padding for alignment starts at a sizeof(void *) address. - */ -static notrace -size_t ltt_get_data_size(struct ltt_serialize_closure *closure, - void *serialize_private, unsigned int stack_pos_ctx, - int *largest_align, const char *fmt, va_list *args) -{ - ltt_serialize_cb cb = closure->callbacks[0]; - closure->cb_idx = 0; - return (size_t)cb(NULL, 0, closure, serialize_private, stack_pos_ctx, - largest_align, fmt, args); -} - -static notrace -void ltt_write_event_data(struct ltt_chanbuf *buf, size_t buf_offset, - struct ltt_serialize_closure *closure, - void *serialize_private, unsigned int stack_pos_ctx, - int largest_align, const char *fmt, va_list *args) -{ - ltt_serialize_cb cb = closure->callbacks[0]; - closure->cb_idx = 0; - buf_offset += ltt_align(buf_offset, largest_align); - cb(buf, buf_offset, closure, serialize_private, stack_pos_ctx, NULL, - fmt, args); -} - - -notrace -void ltt_vtrace(const struct marker *mdata, void *probe_data, void *call_data, - const char *fmt, va_list *args) -{ - int largest_align, ret; - struct ltt_active_marker *pdata; - uint16_t eID; - size_t data_size, slot_size; - unsigned int chan_index; - struct ltt_chanbuf *buf; - struct ltt_chan *chan; - struct ltt_trace *trace, *dest_trace = NULL; - uint64_t tsc; - long buf_offset; - va_list args_copy; - struct ltt_serialize_closure closure; - struct ltt_probe_private_data *private_data = call_data; - void *serialize_private = NULL; - int cpu; - unsigned int rflags; - unsigned int stack_pos_ctx; - - /* - * This test is useful for quickly exiting static tracing when no trace - * is active. We expect to have an active trace when we get here. - */ - if (unlikely(ltt_traces.num_active_traces == 0)) - return; - - rcu_read_lock_sched_notrace(); - cpu = smp_processor_id(); - __get_cpu_var(ltt_nesting)++; - stack_pos_ctx = __get_cpu_var(tracer_stack_pos); - /* - * asm volatile and "memory" clobber prevent the compiler from moving - * instructions out of the ltt nesting count. This is required to ensure - * that probe side-effects which can cause recursion (e.g. unforeseen - * traps, divisions by 0, ...) are triggered within the incremented - * nesting count section. - */ - barrier(); - pdata = (struct ltt_active_marker *)probe_data; - eID = mdata->event_id; - chan_index = mdata->channel_id; - closure.callbacks = pdata->probe->callbacks; - - if (unlikely(private_data)) { - dest_trace = private_data->trace; - if (private_data->serializer) - closure.callbacks = &private_data->serializer; - serialize_private = private_data->serialize_private; - } - - va_copy(args_copy, *args); - /* - * Assumes event payload to start on largest_align alignment. - */ - largest_align = 1; /* must be non-zero for ltt_align */ - data_size = ltt_get_data_size(&closure, serialize_private, - stack_pos_ctx, &largest_align, - fmt, &args_copy); - largest_align = min_t(int, largest_align, sizeof(void *)); - va_end(args_copy); - - /* Iterate on each trace */ - list_for_each_entry_rcu(trace, <t_traces.head, list) { - /* - * Expect the filter to filter out events. If we get here, - * we went through tracepoint activation as a first step. - */ - if (unlikely(dest_trace && trace != dest_trace)) - continue; - if (unlikely(!trace->active)) - continue; - if (unlikely(!ltt_run_filter(trace, eID))) - continue; -#ifdef LTT_DEBUG_EVENT_SIZE - rflags = LTT_RFLAG_ID_SIZE; -#else - if (unlikely(eID >= LTT_FREE_EVENTS)) - rflags = LTT_RFLAG_ID; - else - rflags = 0; -#endif - /* - * Skip channels added after trace creation. - */ - if (unlikely(chan_index >= trace->nr_channels)) - continue; - chan = &trace->channels[chan_index]; - if (!chan->active) - continue; - - /* reserve space : header and data */ - ret = ltt_reserve_slot(chan, trace, data_size, largest_align, - cpu, &buf, &slot_size, &buf_offset, - &tsc, &rflags); - if (unlikely(ret < 0)) - continue; /* buffer full */ - - va_copy(args_copy, *args); - /* Out-of-order write : header and data */ - buf_offset = ltt_write_event_header(&buf->a, &chan->a, - buf_offset, eID, data_size, - tsc, rflags); - ltt_write_event_data(buf, buf_offset, &closure, - serialize_private, stack_pos_ctx, - largest_align, fmt, &args_copy); - va_end(args_copy); - /* Out-of-order commit */ - ltt_commit_slot(buf, chan, buf_offset, data_size, slot_size); - } - /* - * asm volatile and "memory" clobber prevent the compiler from moving - * instructions out of the ltt nesting count. This is required to ensure - * that probe side-effects which can cause recursion (e.g. unforeseen - * traps, divisions by 0, ...) are triggered within the incremented - * nesting count section. - */ - barrier(); - __get_cpu_var(tracer_stack_pos) = stack_pos_ctx; - __get_cpu_var(ltt_nesting)--; - rcu_read_unlock_sched_notrace(); -} -EXPORT_SYMBOL_GPL(ltt_vtrace); - -notrace -void ltt_trace(const struct marker *mdata, void *probe_data, void *call_data, - const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - ltt_vtrace(mdata, probe_data, call_data, fmt, &args); - va_end(args); -} -EXPORT_SYMBOL_GPL(ltt_trace); - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Serializer"); diff --git a/ltt-tracer-core.h b/ltt-tracer-core.h index 1ac8c5b3..cb82658b 100644 --- a/ltt-tracer-core.h +++ b/ltt-tracer-core.h @@ -13,34 +13,13 @@ #include #include -/* ltt's root dir in debugfs */ -#define LTT_ROOT "ltt" +struct ltt_session; +struct ltt_channel; +struct ltt_event; -/* - * All modifications of ltt_traces must be done by ltt-tracer.c, while holding - * the semaphore. Only reading of this information can be done elsewhere, with - * the RCU mechanism : the preemption must be disabled while reading the - * list. - */ -struct ltt_traces { - struct list_head setup_head; /* Pre-allocated traces list */ - struct list_head head; /* Allocated Traces list */ - unsigned int num_active_traces; /* Number of active traces */ -} ____cacheline_aligned; - -extern struct ltt_traces ltt_traces; - -/* - * get dentry of ltt's root dir - */ -struct dentry *get_ltt_root(void); - -void put_ltt_root(void); - -/* Keep track of trap nesting inside LTT */ -DECLARE_PER_CPU(unsigned int, ltt_nesting); - -typedef int (*ltt_run_filter_functor)(void *trace, uint16_t eID); +typedef int (*ltt_run_filter_functor)(struct ltt_session *session, + struct ltt_channel *chan, + struct ltt_event *event); extern ltt_run_filter_functor ltt_run_filter; diff --git a/ltt-tracer.c b/ltt-tracer.c deleted file mode 100644 index 5cdea932..00000000 --- a/ltt-tracer.c +++ /dev/null @@ -1,1112 +0,0 @@ -/* - * ltt/ltt-tracer.c - * - * Copyright (c) 2005-2010 - Mathieu Desnoyers - * - * Tracing management internal kernel API. Trace buffer allocation/free, tracing - * start/stop. - * - * Author: - * Mathieu Desnoyers - * - * Inspired from LTT : - * Karim Yaghmour (karim@opersys.com) - * Tom Zanussi (zanussi@us.ibm.com) - * Bob Wisniewski (bob@watson.ibm.com) - * And from K42 : - * Bob Wisniewski (bob@watson.ibm.com) - * - * Changelog: - * 22/09/06, Move to the marker/probes mechanism. - * 19/10/05, Complete lockless mechanism. - * 27/05/05, Modular redesign and rewrite. - * - * Dual LGPL v2.1/GPL v2 license. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ltt-tracer.h" - -static void synchronize_trace(void) -{ - synchronize_sched(); -#ifdef CONFIG_PREEMPT_RT - synchronize_rcu(); -#endif -} - -static void async_wakeup(unsigned long data); - -static DEFINE_TIMER(ltt_async_wakeup_timer, async_wakeup, 0, 0); - -/* Default callbacks for modules */ -notrace -int ltt_filter_control_default(enum ltt_filter_control_msg msg, - struct ltt_trace *trace) -{ - return 0; -} - -int ltt_statedump_default(struct ltt_trace *trace) -{ - return 0; -} - -/* Callbacks for registered modules */ - -int (*ltt_filter_control_functor) - (enum ltt_filter_control_msg msg, struct ltt_trace *trace) = - ltt_filter_control_default; -struct module *ltt_filter_control_owner; - -/* These function pointers are protected by a trace activation check */ -struct module *ltt_run_filter_owner; -int (*ltt_statedump_functor)(struct ltt_trace *trace) = ltt_statedump_default; -struct module *ltt_statedump_owner; - -struct chan_info_struct { - const char *name; - unsigned int def_sb_size; - unsigned int def_n_sb; -} chan_infos[] = { - [LTT_CHANNEL_METADATA] = { - LTT_METADATA_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_FD_STATE] = { - LTT_FD_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_GLOBAL_STATE] = { - LTT_GLOBAL_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_IRQ_STATE] = { - LTT_IRQ_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_MODULE_STATE] = { - LTT_MODULE_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_NETIF_STATE] = { - LTT_NETIF_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_SOFTIRQ_STATE] = { - LTT_SOFTIRQ_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_SWAP_STATE] = { - LTT_SWAP_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_SYSCALL_STATE] = { - LTT_SYSCALL_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_TASK_STATE] = { - LTT_TASK_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_VM_STATE] = { - LTT_VM_STATE_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_MED, - LTT_DEFAULT_N_SUBBUFS_MED, - }, - [LTT_CHANNEL_FS] = { - LTT_FS_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_MED, - LTT_DEFAULT_N_SUBBUFS_MED, - }, - [LTT_CHANNEL_INPUT] = { - LTT_INPUT_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_IPC] = { - LTT_IPC_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_LOW, - LTT_DEFAULT_N_SUBBUFS_LOW, - }, - [LTT_CHANNEL_KERNEL] = { - LTT_KERNEL_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_HIGH, - LTT_DEFAULT_N_SUBBUFS_HIGH, - }, - [LTT_CHANNEL_MM] = { - LTT_MM_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_MED, - LTT_DEFAULT_N_SUBBUFS_MED, - }, - [LTT_CHANNEL_RCU] = { - LTT_RCU_CHANNEL, - LTT_DEFAULT_SUBBUF_SIZE_MED, - LTT_DEFAULT_N_SUBBUFS_MED, - }, - [LTT_CHANNEL_DEFAULT] = { - NULL, - LTT_DEFAULT_SUBBUF_SIZE_MED, - LTT_DEFAULT_N_SUBBUFS_MED, - }, -}; - -static enum ltt_channels get_channel_type_from_name(const char *name) -{ - int i; - - if (!name) - return LTT_CHANNEL_DEFAULT; - - for (i = 0; i < ARRAY_SIZE(chan_infos); i++) - if (chan_infos[i].name && !strcmp(name, chan_infos[i].name)) - return (enum ltt_channels)i; - - return LTT_CHANNEL_DEFAULT; -} - -/** - * ltt_module_register - LTT module registration - * @name: module type - * @function: callback to register - * @owner: module which owns the callback - * - * The module calling this registration function must ensure that no - * trap-inducing code will be executed by "function". E.g. vmalloc_sync_all() - * must be called between a vmalloc and the moment the memory is made visible to - * "function". This registration acts as a vmalloc_sync_all. Therefore, only if - * the module allocates virtual memory after its registration must it - * synchronize the TLBs. - */ -int ltt_module_register(enum ltt_module_function name, void *function, - struct module *owner) -{ - int ret = 0; - - /* - * Make sure no page fault can be triggered by the module about to be - * registered. We deal with this here so we don't have to call - * vmalloc_sync_all() in each module's init. - */ - vmalloc_sync_all(); - - switch (name) { - case LTT_FUNCTION_RUN_FILTER: - if (ltt_run_filter_owner != NULL) { - ret = -EEXIST; - goto end; - } - ltt_filter_register((ltt_run_filter_functor)function); - ltt_run_filter_owner = owner; - break; - case LTT_FUNCTION_FILTER_CONTROL: - if (ltt_filter_control_owner != NULL) { - ret = -EEXIST; - goto end; - } - ltt_filter_control_functor = - (int (*)(enum ltt_filter_control_msg, - struct ltt_trace *))function; - ltt_filter_control_owner = owner; - break; - case LTT_FUNCTION_STATEDUMP: - if (ltt_statedump_owner != NULL) { - ret = -EEXIST; - goto end; - } - ltt_statedump_functor = - (int (*)(struct ltt_trace *))function; - ltt_statedump_owner = owner; - break; - } -end: - return ret; -} -EXPORT_SYMBOL_GPL(ltt_module_register); - -/** - * ltt_module_unregister - LTT module unregistration - * @name: module type - */ -void ltt_module_unregister(enum ltt_module_function name) -{ - switch (name) { - case LTT_FUNCTION_RUN_FILTER: - ltt_filter_unregister(); - ltt_run_filter_owner = NULL; - /* Wait for preempt sections to finish */ - synchronize_trace(); - break; - case LTT_FUNCTION_FILTER_CONTROL: - ltt_filter_control_functor = ltt_filter_control_default; - ltt_filter_control_owner = NULL; - break; - case LTT_FUNCTION_STATEDUMP: - ltt_statedump_functor = ltt_statedump_default; - ltt_statedump_owner = NULL; - break; - } - -} -EXPORT_SYMBOL_GPL(ltt_module_unregister); - -static LIST_HEAD(ltt_transport_list); - -/** - * 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. The module calling this registration function must ensure that no - * trap-inducing code will be executed by the transport functions. E.g. - * vmalloc_sync_all() must be called between a vmalloc and the moment the memory - * is made visible to the transport function. This registration acts as a - * vmalloc_sync_all. Therefore, only if the module allocates virtual memory - * after its registration must it synchronize the TLBs. - */ -void ltt_transport_register(struct ltt_transport *transport) -{ - /* - * Make sure no page fault can be triggered by the module about to be - * registered. We deal with this here so we don't have to call - * vmalloc_sync_all() in each module's init. - */ - vmalloc_sync_all(); - - ltt_lock_traces(); - list_add_tail(&transport->node, <t_transport_list); - ltt_unlock_traces(); -} -EXPORT_SYMBOL_GPL(ltt_transport_register); - -/** - * ltt_transport_unregister - LTT transport unregistration - * @transport: transport structure - */ -void ltt_transport_unregister(struct ltt_transport *transport) -{ - ltt_lock_traces(); - list_del(&transport->node); - ltt_unlock_traces(); -} -EXPORT_SYMBOL_GPL(ltt_transport_unregister); - -static inline -int is_channel_overwrite(enum ltt_channels chan, enum trace_mode mode) -{ - switch (mode) { - case LTT_TRACE_NORMAL: - return 0; - case LTT_TRACE_FLIGHT: - switch (chan) { - case LTT_CHANNEL_METADATA: - return 0; - default: - return 1; - } - case LTT_TRACE_HYBRID: - switch (chan) { - case LTT_CHANNEL_KERNEL: - case LTT_CHANNEL_FS: - case LTT_CHANNEL_MM: - case LTT_CHANNEL_RCU: - case LTT_CHANNEL_IPC: - case LTT_CHANNEL_INPUT: - return 1; - default: - return 0; - } - default: - return 0; - } -} - -/** - * _ltt_trace_find - find a trace by given name. - * trace_name: trace name - * - * Returns a pointer to the trace structure, NULL if not found. - */ -static struct ltt_trace *_ltt_trace_find(const char *trace_name) -{ - struct ltt_trace *trace; - - list_for_each_entry(trace, <t_traces.head, list) - if (!strncmp(trace->trace_name, trace_name, NAME_MAX)) - return trace; - - return NULL; -} - -/* _ltt_trace_find_setup : - * find a trace in setup list by given name. - * - * Returns a pointer to the trace structure, NULL if not found. - */ -struct ltt_trace *_ltt_trace_find_setup(const char *trace_name) -{ - struct ltt_trace *trace; - - list_for_each_entry(trace, <t_traces.setup_head, list) - if (!strncmp(trace->trace_name, trace_name, NAME_MAX)) - return trace; - - return NULL; -} -EXPORT_SYMBOL_GPL(_ltt_trace_find_setup); - -/** - * ltt_release_trace - Release a LTT trace - * @kref : reference count on the trace - */ -void ltt_release_trace(struct kref *kref) -{ - struct ltt_trace *trace = container_of(kref, struct ltt_trace, kref); - - trace->ops->remove_dirs(trace); - module_put(trace->transport->owner); - ltt_channels_trace_free(trace); - kfree(trace); -} -EXPORT_SYMBOL_GPL(ltt_release_trace); - -static inline void prepare_chan_size_num(unsigned int *subbuf_size, - unsigned int *n_subbufs) -{ - /* Make sure the subbuffer size is larger than a page */ - *subbuf_size = max_t(unsigned int, *subbuf_size, PAGE_SIZE); - - /* round to next power of 2 */ - *subbuf_size = 1 << get_count_order(*subbuf_size); - *n_subbufs = 1 << get_count_order(*n_subbufs); - - /* Subbuf size and number must both be power of two */ - WARN_ON(hweight32(*subbuf_size) != 1); - WARN_ON(hweight32(*n_subbufs) != 1); -} - -int _ltt_trace_setup(const char *trace_name) -{ - int err = 0; - struct ltt_trace *new_trace = NULL; - int metadata_index; - unsigned int chan; - enum ltt_channels chantype; - - if (_ltt_trace_find_setup(trace_name)) { - printk(KERN_ERR "LTT : Trace name %s already used.\n", - trace_name); - err = -EEXIST; - goto traces_error; - } - - if (_ltt_trace_find(trace_name)) { - printk(KERN_ERR "LTT : Trace name %s already used.\n", - trace_name); - err = -EEXIST; - goto traces_error; - } - - new_trace = kzalloc(sizeof(struct ltt_trace), GFP_KERNEL); - if (!new_trace) { - printk(KERN_ERR - "LTT : Unable to allocate memory for trace %s\n", - trace_name); - err = -ENOMEM; - goto traces_error; - } - strncpy(new_trace->trace_name, trace_name, NAME_MAX); - if (ltt_channels_trace_alloc(&new_trace->nr_channels, 0)) { - printk(KERN_ERR - "LTT : Unable to allocate memory for chaninfo %s\n", - trace_name); - err = -ENOMEM; - goto trace_free; - } - - /* - * Force metadata channel to no overwrite. - */ - metadata_index = ltt_channels_get_index_from_name("metadata"); - WARN_ON(metadata_index < 0); - new_trace->settings[metadata_index].overwrite = 0; - - /* - * Set hardcoded tracer defaults for some channels - */ - for (chan = 0; chan < new_trace->nr_channels; chan++) { - chantype = get_channel_type_from_name( - ltt_channels_get_name_from_index(chan)); - new_trace->settings[chan].sb_size = - chan_infos[chantype].def_sb_size; - new_trace->settings[chan].n_sb = - chan_infos[chantype].def_n_sb; - } - - list_add(&new_trace->list, <t_traces.setup_head); - return 0; - -trace_free: - kfree(new_trace); -traces_error: - return err; -} -EXPORT_SYMBOL_GPL(_ltt_trace_setup); - - -int ltt_trace_setup(const char *trace_name) -{ - int ret; - ltt_lock_traces(); - ret = _ltt_trace_setup(trace_name); - ltt_unlock_traces(); - return ret; -} -EXPORT_SYMBOL_GPL(ltt_trace_setup); - -/* must be called from within a traces lock. */ -static void _ltt_trace_free(struct ltt_trace *trace) -{ - list_del(&trace->list); - kfree(trace); -} - -int ltt_trace_set_type(const char *trace_name, const char *trace_type) -{ - int err = 0; - struct ltt_trace *trace; - struct ltt_transport *tran_iter, *transport = NULL; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - list_for_each_entry(tran_iter, <t_transport_list, node) { - if (!strcmp(tran_iter->name, trace_type)) { - transport = tran_iter; - break; - } - } - if (!transport) { - printk(KERN_ERR "LTT : Transport %s is not present.\n", - trace_type); - err = -EINVAL; - goto traces_error; - } - - trace->transport = transport; - -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_set_type); - -int ltt_trace_set_channel_subbufsize(const char *trace_name, - const char *channel_name, - unsigned int size) -{ - int err = 0; - struct ltt_trace *trace; - int index; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - index = ltt_channels_get_index_from_name(channel_name); - if (index < 0) { - printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); - err = -ENOENT; - goto traces_error; - } - trace->settings[index].sb_size = size; - -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_set_channel_subbufsize); - -int ltt_trace_set_channel_subbufcount(const char *trace_name, - const char *channel_name, - unsigned int cnt) -{ - int err = 0; - struct ltt_trace *trace; - int index; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - index = ltt_channels_get_index_from_name(channel_name); - if (index < 0) { - printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); - err = -ENOENT; - goto traces_error; - } - trace->settings[index].n_sb = cnt; - -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_set_channel_subbufcount); - -int ltt_trace_set_channel_switch_timer(const char *trace_name, - const char *channel_name, - unsigned long interval) -{ - int err = 0; - struct ltt_trace *trace; - int index; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - index = ltt_channels_get_index_from_name(channel_name); - if (index < 0) { - printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); - err = -ENOENT; - goto traces_error; - } - ltt_channels_trace_set_timer(&trace->settings[index], interval); - -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_set_channel_switch_timer); - -int ltt_trace_set_channel_overwrite(const char *trace_name, - const char *channel_name, - unsigned int overwrite) -{ - int err = 0; - struct ltt_trace *trace; - int index; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - /* - * Always put the metadata channel in non-overwrite mode : - * This is a very low traffic channel and it can't afford to have its - * data overwritten : this data (marker info) is necessary to be - * able to read the trace. - */ - if (overwrite && !strcmp(channel_name, "metadata")) { - printk(KERN_ERR "LTT : Trying to set metadata channel to " - "overwrite mode\n"); - err = -EINVAL; - goto traces_error; - } - - index = ltt_channels_get_index_from_name(channel_name); - if (index < 0) { - printk(KERN_ERR "LTT : Channel %s not found\n", channel_name); - err = -ENOENT; - goto traces_error; - } - - trace->settings[index].overwrite = overwrite; - -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_set_channel_overwrite); - -int ltt_trace_alloc(const char *trace_name) -{ - int err = 0; - struct ltt_trace *trace; - int sb_size, n_sb; - unsigned long flags; - int chan; - const char *channel_name; - - ltt_lock_traces(); - - trace = _ltt_trace_find_setup(trace_name); - if (!trace) { - printk(KERN_ERR "LTT : Trace not found %s\n", trace_name); - err = -ENOENT; - goto traces_error; - } - - kref_init(&trace->kref); - init_waitqueue_head(&trace->kref_wq); - trace->active = 0; - get_trace_clock(); - trace->freq_scale = trace_clock_freq_scale(); - - if (!trace->transport) { - printk(KERN_ERR "LTT : Transport is not set.\n"); - err = -EINVAL; - goto transport_error; - } - if (!try_module_get(trace->transport->owner)) { - printk(KERN_ERR "LTT : Can't lock transport module.\n"); - err = -ENODEV; - goto transport_error; - } - trace->ops = &trace->transport->ops; - - err = trace->ops->create_dirs(trace); - if (err) { - printk(KERN_ERR "LTT : Can't create dir for trace %s.\n", - trace_name); - goto dirs_error; - } - - local_irq_save(flags); - trace->start_freq = trace_clock_frequency(); - trace->start_tsc = trace_clock_read64(); - do_gettimeofday(&trace->start_time); - local_irq_restore(flags); - - for (chan = 0; chan < trace->nr_channels; chan++) { - channel_name = ltt_channels_get_name_from_index(chan); - WARN_ON(!channel_name); - /* - * note: sb_size and n_sb will be overwritten with updated - * values by channel creation. - */ - sb_size = trace->settings[chan].sb_size; - n_sb = trace->settings[chan].n_sb; - prepare_chan_size_num(&sb_size, &n_sb); - trace->channels[chan] = ltt_create_channel(channel_name, - trace, NULL, sb_size, n_sb, - trace->settings[chan].overwrite, - trace->settings[chan].switch_timer_interval, - trace->settings[chan].read_timer_interval); - if (err != 0) { - printk(KERN_ERR "LTT : Can't create channel %s.\n", - channel_name); - goto create_channel_error; - } - } - - list_del(&trace->list); - if (list_empty(<t_traces.head)) - set_kernel_trace_flag_all_tasks(); - list_add_rcu(&trace->list, <t_traces.head); - synchronize_trace(); - - ltt_unlock_traces(); - - return 0; - -create_channel_error: - for (chan--; chan >= 0; chan--) - ltt_channel_destroy(trace->channels[chan]); - trace->ops->remove_dirs(trace); - -dirs_error: - module_put(trace->transport->owner); -transport_error: - put_trace_clock(); -traces_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_alloc); - -/* - * It is worked as a wrapper for current version of ltt_control.ko. - * We will make a new ltt_control based on debugfs, and control each channel's - * buffer. - */ -static -int ltt_trace_create(const char *trace_name, const char *trace_type, - enum trace_mode mode, - unsigned int subbuf_size_low, unsigned int n_subbufs_low, - unsigned int subbuf_size_med, unsigned int n_subbufs_med, - unsigned int subbuf_size_high, unsigned int n_subbufs_high) -{ - int err = 0; - - err = ltt_trace_setup(trace_name); - if (IS_ERR_VALUE(err)) - return err; - - err = ltt_trace_set_type(trace_name, trace_type); - if (IS_ERR_VALUE(err)) - return err; - - err = ltt_trace_alloc(trace_name); - if (IS_ERR_VALUE(err)) - return err; - - return err; -} - -/* Must be called while sure that trace is in the list. */ -static int _ltt_trace_destroy(struct ltt_trace *trace) -{ - int err = -EPERM; - - if (trace == NULL) { - err = -ENOENT; - goto traces_error; - } - if (trace->active) { - printk(KERN_ERR - "LTT : Can't destroy trace %s : tracer is active\n", - trace->trace_name); - err = -EBUSY; - goto active_error; - } - /* Everything went fine */ - list_del_rcu(&trace->list); - synchronize_trace(); - if (list_empty(<t_traces.head)) { - clear_kernel_trace_flag_all_tasks(); - } - return 0; - - /* error handling */ -active_error: -traces_error: - return err; -} - -/* Sleepable part of the destroy */ -static void __ltt_trace_destroy(struct ltt_trace *trace) -{ - int i; - - for (i = 0; i < trace->nr_channels; i++) - ltt_channel_destroy(trace->channels[i]); - kref_put(&trace->kref, ltt_release_trace); -} - -int ltt_trace_destroy(const char *trace_name) -{ - int err = 0; - struct ltt_trace *trace; - - ltt_lock_traces(); - - trace = _ltt_trace_find(trace_name); - if (trace) { - err = _ltt_trace_destroy(trace); - if (err) - goto error; - - __ltt_trace_destroy(trace); - ltt_unlock_traces(); - put_trace_clock(); - - return 0; - } - - trace = _ltt_trace_find_setup(trace_name); - if (trace) { - _ltt_trace_free(trace); - ltt_unlock_traces(); - return 0; - } - - err = -ENOENT; - - /* Error handling */ -error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_destroy); - -/* must be called from within a traces lock. */ -static int _ltt_trace_start(struct ltt_trace *trace) -{ - int err = 0; - - if (trace == NULL) { - err = -ENOENT; - goto traces_error; - } - if (trace->active) - printk(KERN_INFO "LTT : Tracing already active for trace %s\n", - trace->trace_name); - if (!try_module_get(ltt_run_filter_owner)) { - err = -ENODEV; - printk(KERN_ERR "LTT : Can't lock filter module.\n"); - goto get_ltt_run_filter_error; - } - trace->active = 1; - /* Read by trace points without protection : be careful */ - ltt_traces.num_active_traces++; - return err; - - /* error handling */ -get_ltt_run_filter_error: -traces_error: - return err; -} - -int ltt_trace_start(const char *trace_name) -{ - int err = 0; - struct ltt_trace *trace; - - ltt_lock_traces(); - - trace = _ltt_trace_find(trace_name); - err = _ltt_trace_start(trace); - if (err) - goto no_trace; - - ltt_unlock_traces(); - - /* - * Call the kernel state dump. - * Events will be mixed with real kernel events, it's ok. - * Notice that there is no protection on the trace : that's exactly - * why we iterate on the list and check for trace equality instead of - * directly using this trace handle inside the logging function. - */ - - ltt_dump_marker_state(trace); - - if (!try_module_get(ltt_statedump_owner)) { - err = -ENODEV; - printk(KERN_ERR - "LTT : Can't lock state dump module.\n"); - } else { - ltt_statedump_functor(trace); - module_put(ltt_statedump_owner); - } - - return err; - - /* Error handling */ -no_trace: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_start); - -/* must be called from within traces lock */ -static int _ltt_trace_stop(struct ltt_trace *trace) -{ - int err = -EPERM; - - if (trace == NULL) { - err = -ENOENT; - goto traces_error; - } - if (!trace->active) - printk(KERN_INFO "LTT : Tracing not active for trace %s\n", - trace->trace_name); - if (trace->active) { - trace->active = 0; - ltt_traces.num_active_traces--; - synchronize_trace(); /* Wait for each tracing to be finished */ - } - module_put(ltt_run_filter_owner); - /* Everything went fine */ - return 0; - - /* Error handling */ -traces_error: - return err; -} - -int ltt_trace_stop(const char *trace_name) -{ - int err = 0; - struct ltt_trace *trace; - - ltt_lock_traces(); - trace = _ltt_trace_find(trace_name); - err = _ltt_trace_stop(trace); - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_trace_stop); - -/** - * ltt_control - Trace control in-kernel API - * @msg: Action to perform - * @trace_name: Trace on which the action must be done - * @trace_type: Type of trace (normal, flight, hybrid) - * @args: Arguments specific to the action - */ -int ltt_control(enum ltt_control_msg msg, const char *trace_name, - const char *trace_type, union ltt_control_args args) -{ - int err = -EPERM; - - printk(KERN_ALERT "ltt_control : trace %s\n", trace_name); - switch (msg) { - case LTT_CONTROL_START: - printk(KERN_DEBUG "Start tracing %s\n", trace_name); - err = ltt_trace_start(trace_name); - break; - case LTT_CONTROL_STOP: - printk(KERN_DEBUG "Stop tracing %s\n", trace_name); - err = ltt_trace_stop(trace_name); - break; - case LTT_CONTROL_CREATE_TRACE: - printk(KERN_DEBUG "Creating trace %s\n", trace_name); - err = ltt_trace_create(trace_name, trace_type, - args.new_trace.mode, - args.new_trace.subbuf_size_low, - args.new_trace.n_subbufs_low, - args.new_trace.subbuf_size_med, - args.new_trace.n_subbufs_med, - args.new_trace.subbuf_size_high, - args.new_trace.n_subbufs_high); - break; - case LTT_CONTROL_DESTROY_TRACE: - printk(KERN_DEBUG "Destroying trace %s\n", trace_name); - err = ltt_trace_destroy(trace_name); - break; - } - return err; -} -EXPORT_SYMBOL_GPL(ltt_control); - -/** - * ltt_filter_control - Trace filter control in-kernel API - * @msg: Action to perform on the filter - * @trace_name: Trace on which the action must be done - */ -int ltt_filter_control(enum ltt_filter_control_msg msg, const char *trace_name) -{ - int err; - struct ltt_trace *trace; - - printk(KERN_DEBUG "ltt_filter_control : trace %s\n", trace_name); - ltt_lock_traces(); - trace = _ltt_trace_find(trace_name); - if (trace == NULL) { - printk(KERN_ALERT - "Trace does not exist. Cannot proxy control request\n"); - err = -ENOENT; - goto trace_error; - } - if (!try_module_get(ltt_filter_control_owner)) { - err = -ENODEV; - goto get_module_error; - } - switch (msg) { - case LTT_FILTER_DEFAULT_ACCEPT: - printk(KERN_DEBUG - "Proxy filter default accept %s\n", trace_name); - err = (*ltt_filter_control_functor)(msg, trace); - break; - case LTT_FILTER_DEFAULT_REJECT: - printk(KERN_DEBUG - "Proxy filter default reject %s\n", trace_name); - err = (*ltt_filter_control_functor)(msg, trace); - break; - default: - err = -EPERM; - } - module_put(ltt_filter_control_owner); - -get_module_error: -trace_error: - ltt_unlock_traces(); - return err; -} -EXPORT_SYMBOL_GPL(ltt_filter_control); - -int __init ltt_init(void) -{ - /* Make sure no page fault can be triggered by this module */ - vmalloc_sync_all(); - init_timer_deferrable(<t_async_wakeup_timer); - return 0; -} - -module_init(ltt_init) - -static void __exit ltt_exit(void) -{ - struct ltt_trace *trace; - struct list_head *pos, *n; - - ltt_lock_traces(); - /* Stop each trace, currently being read by RCU read-side */ - list_for_each_entry_rcu(trace, <t_traces.head, list) - _ltt_trace_stop(trace); - /* Wait for quiescent state. Readers have preemption disabled. */ - synchronize_trace(); - /* Safe iteration is now permitted. It does not have to be RCU-safe - * because no readers are left. */ - list_for_each_safe(pos, n, <t_traces.head) { - trace = container_of(pos, struct ltt_trace, list); - /* _ltt_trace_destroy does a synchronize_trace() */ - _ltt_trace_destroy(trace); - __ltt_trace_destroy(trace); - } - /* free traces in pre-alloc status */ - list_for_each_safe(pos, n, <t_traces.setup_head) { - trace = container_of(pos, struct ltt_trace, list); - _ltt_trace_free(trace); - } - - ltt_unlock_traces(); -} - -module_exit(ltt_exit) - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Tracer Kernel API"); diff --git a/ltt-tracer.h b/ltt-tracer.h index 6ed4fa94..3b5c3605 100644 --- a/ltt-tracer.h +++ b/ltt-tracer.h @@ -29,6 +29,7 @@ #include #include "ltt-tracer-core.h" +#include "ltt-events.h" /* Number of bytes to log with a read/write event */ #define LTT_LOG_RW_SIZE 32L @@ -193,13 +194,24 @@ struct event_header { * because gcc generates poor code on at least powerpc and mips. Don't ever * let gcc add padding between the structure elements. */ -struct subbuffer_header { - uint64_t cycle_count_begin; /* Cycle count at subbuffer start */ - uint64_t cycle_count_end; /* Cycle count at subbuffer end */ - uint32_t magic_number; /* +struct packet_header { + uint32_t magic; /* * Trace magic number. * contains endianness information. */ + uint8_t trace_uuid[16]; + uint32_t stream_id; + uint64_t timestamp_begin; /* Cycle count at subbuffer start */ + uint64_t timestamp_end; /* Cycle count at subbuffer end */ + uint32_t content_size; /* Size of data in subbuffer */ + uint32_t packet_size; /* Subbuffer size (include padding) */ + uint32_t events_lost; /* + * Events lost in this subbuffer since + * the beginning of the trace. + * (may overflow) + */ + /* TODO: move to metadata */ +#if 0 uint8_t major_version; uint8_t minor_version; uint8_t arch_size; /* Architecture pointer size */ @@ -211,18 +223,7 @@ struct subbuffer_header { * used all along the trace. */ uint32_t freq_scale; /* Frequency scaling (divisor) */ - uint32_t data_size; /* Size of data in subbuffer */ - uint32_t sb_size; /* Subbuffer size (include padding) */ - uint32_t events_lost; /* - * Events lost in this subbuffer since - * the beginning of the trace. - * (may overflow) - */ - uint32_t subbuf_corrupt; /* - * Corrupted (lost) subbuffers since - * the begginig of the trace. - * (may overflow) - */ +#endif //0 uint8_t header_end[0]; /* End of header */ }; @@ -301,9 +302,9 @@ unsigned char record_header_size(const struct lib_ring_buffer_config *config, #include extern -size_t ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, - struct lib_ring_buffer_ctx *ctx, - u16 eID, u32 event_size); +void ltt_write_event_header_slow(const struct lib_ring_buffer_config *config, + struct lib_ring_buffer_ctx *ctx, + u16 eID, u32 event_size); /* * ltt_write_event_header @@ -397,49 +398,10 @@ size_t ltt_read_event_header(struct ltt_chanbuf_alloc *bufa, long buf_offset, } #endif //0 -/* - * Control channels : - * control/metadata - * control/interrupts - * control/... - * - * cpu channel : - * cpu - */ -#define LTT_RELAY_ROOT "ltt" - -#define LTT_METADATA_CHANNEL "metadata_state" -#define LTT_FD_STATE_CHANNEL "fd_state" -#define LTT_GLOBAL_STATE_CHANNEL "global_state" -#define LTT_IRQ_STATE_CHANNEL "irq_state" -#define LTT_MODULE_STATE_CHANNEL "module_state" -#define LTT_NETIF_STATE_CHANNEL "netif_state" -#define LTT_SOFTIRQ_STATE_CHANNEL "softirq_state" -#define LTT_SWAP_STATE_CHANNEL "swap_state" -#define LTT_SYSCALL_STATE_CHANNEL "syscall_state" -#define LTT_TASK_STATE_CHANNEL "task_state" -#define LTT_VM_STATE_CHANNEL "vm_state" -#define LTT_FS_CHANNEL "fs" -#define LTT_INPUT_CHANNEL "input" -#define LTT_IPC_CHANNEL "ipc" -#define LTT_KERNEL_CHANNEL "kernel" -#define LTT_MM_CHANNEL "mm" -#define LTT_RCU_CHANNEL "rcu" - -#define LTT_FLIGHT_PREFIX "flight-" - -#define LTT_ASCII "ascii" - /* Tracer properties */ -#define LTT_DEFAULT_SUBBUF_SIZE_LOW 65536 -#define LTT_DEFAULT_N_SUBBUFS_LOW 2 -#define LTT_DEFAULT_SUBBUF_SIZE_MED 262144 -#define LTT_DEFAULT_N_SUBBUFS_MED 2 -#define LTT_DEFAULT_SUBBUF_SIZE_HIGH 1048576 -#define LTT_DEFAULT_N_SUBBUFS_HIGH 2 -#define LTT_TRACER_MAGIC_NUMBER 0x00D6B7ED -#define LTT_TRACER_VERSION_MAJOR 2 -#define LTT_TRACER_VERSION_MINOR 6 +#define CTF_MAGIC_NUMBER 0xC1FC1FC1 +#define LTT_TRACER_VERSION_MAJOR 3 +#define LTT_TRACER_VERSION_MINOR 0 /** * ltt_write_trace_header - Write trace header @@ -447,20 +409,21 @@ size_t ltt_read_event_header(struct ltt_chanbuf_alloc *bufa, long buf_offset, * @header: Memory address where the information must be written to */ static __inline__ -void ltt_write_trace_header(void *priv, - struct subbuffer_header *header) +void write_trace_header(const struct lib_ring_buffer_config *config, + struct packet_header *header) { - struct ltt_session *session = priv; - - header->magic_number = LTT_TRACER_MAGIC_NUMBER; + header->magic = CTF_MAGIC_NUMBER; +#if 0 + /* TODO: move start time to metadata */ header->major_version = LTT_TRACER_VERSION_MAJOR; header->minor_version = LTT_TRACER_VERSION_MINOR; header->arch_size = sizeof(void *); - header->alignment = lib_ring_buffer_get_alignment(); - header->start_time_sec = session->start_time.tv_sec; - header->start_time_usec = session->start_time.tv_usec; - header->start_freq = session->start_freq; - header->freq_scale = session->freq_scale; + header->alignment = lib_ring_buffer_get_alignment(config); + header->start_time_sec = ltt_chan->session->start_time.tv_sec; + header->start_time_usec = ltt_chan->session->start_time.tv_usec; + header->start_freq = ltt_chan->session->start_freq; + header->freq_scale = ltt_chan->session->freq_scale; +#endif //0 } /* @@ -485,35 +448,10 @@ extern void ltt_module_unregister(enum ltt_module_function name); /* Exported control function */ -enum ltt_control_msg { - LTT_CONTROL_START, - LTT_CONTROL_STOP, - LTT_CONTROL_CREATE_TRACE, - LTT_CONTROL_DESTROY_TRACE -}; - -union ltt_control_args { - struct { - enum trace_mode mode; - unsigned int subbuf_size_low; - unsigned int n_subbufs_low; - unsigned int subbuf_size_med; - unsigned int n_subbufs_med; - unsigned int subbuf_size_high; - unsigned int n_subbufs_high; - } new_trace; -}; - void ltt_core_register(int (*function)(u8, void *)); void ltt_core_unregister(void); -extern int ltt_marker_connect(const char *channel, const char *mname, - const char *pname); -extern int ltt_marker_disconnect(const char *channel, const char *mname, - const char *pname); -extern void ltt_dump_marker_state(struct ltt_trace *trace); - extern void ltt_statedump_register_kprobes_dump(void (*callback)(void *call_data)); extern diff --git a/ltt-type-serializer.c b/ltt-type-serializer.c deleted file mode 100644 index 7d06490c..00000000 --- a/ltt-type-serializer.c +++ /dev/null @@ -1,113 +0,0 @@ -/** - * ltt-type-serializer.c - * - * LTTng specialized type serializer. - * - * Copyright Mathieu Desnoyers, 2008. - * - * Dual LGPL v2.1/GPL v2 license. - */ -#include - -#include "ltt-type-serializer.h" -#include "ltt-relay-lockless.h" - -notrace -void _ltt_specialized_trace(const struct marker *mdata, void *probe_data, - void *serialize_private, unsigned int data_size, - unsigned int largest_align) -{ - int ret; - uint16_t eID; - size_t slot_size; - unsigned int chan_index; - struct ltt_chanbuf *buf; - struct ltt_chan *chan; - struct ltt_trace *trace; - uint64_t tsc; - long buf_offset; - int cpu; - unsigned int rflags; - - /* - * If we get here, it's probably because we have useful work to do. - */ - if (unlikely(ltt_traces.num_active_traces == 0)) - return; - - rcu_read_lock_sched_notrace(); - cpu = smp_processor_id(); - __get_cpu_var(ltt_nesting)++; - /* - * asm volatile and "memory" clobber prevent the compiler from moving - * instructions out of the ltt nesting count. This is required to ensure - * that probe side-effects which can cause recursion (e.g. unforeseen - * traps, divisions by 0, ...) are triggered within the incremented - * nesting count section. - */ - barrier(); - eID = mdata->event_id; - chan_index = mdata->channel_id; - - /* - * Iterate on each trace, typically small number of active traces, - * list iteration with prefetch is usually slower. - */ - __list_for_each_entry_rcu(trace, <t_traces.head, list) { - if (unlikely(!trace->active)) - continue; - if (unlikely(!ltt_run_filter(trace, eID))) - continue; -#ifdef LTT_DEBUG_EVENT_SIZE - rflags = LTT_RFLAG_ID_SIZE; -#else - if (unlikely(eID >= LTT_FREE_EVENTS)) - rflags = LTT_RFLAG_ID; - else - rflags = 0; -#endif - /* - * Skip channels added after trace creation. - */ - if (unlikely(chan_index >= trace->nr_channels)) - continue; - chan = &trace->channels[chan_index]; - if (!chan->active) - continue; - - /* reserve space : header and data */ - ret = ltt_reserve_slot(chan, trace, data_size, largest_align, - cpu, &buf, &slot_size, &buf_offset, &tsc, - &rflags); - if (unlikely(ret < 0)) - continue; /* buffer full */ - - /* Out-of-order write : header and data */ - buf_offset = ltt_write_event_header(&buf->a, &chan->a, - buf_offset, eID, data_size, - tsc, rflags); - if (data_size) { - buf_offset += ltt_align(buf_offset, largest_align); - ltt_relay_write(&buf->a, &chan->a, buf_offset, - serialize_private, data_size); - buf_offset += data_size; - } - /* Out-of-order commit */ - ltt_commit_slot(buf, chan, buf_offset, data_size, slot_size); - } - /* - * asm volatile and "memory" clobber prevent the compiler from moving - * instructions out of the ltt nesting count. This is required to ensure - * that probe side-effects which can cause recursion (e.g. unforeseen - * traps, divisions by 0, ...) are triggered within the incremented - * nesting count section. - */ - barrier(); - __get_cpu_var(ltt_nesting)--; - rcu_read_unlock_sched_notrace(); -} -EXPORT_SYMBOL_GPL(_ltt_specialized_trace); - -MODULE_LICENSE("GPL and additional rights"); -MODULE_AUTHOR("Mathieu Desnoyers"); -MODULE_DESCRIPTION("LTT type serializer"); diff --git a/ltt-type-serializer.h b/ltt-type-serializer.h deleted file mode 100644 index 49712c82..00000000 --- a/ltt-type-serializer.h +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef _LTT_TYPE_SERIALIZER_H -#define _LTT_TYPE_SERIALIZER_H - -#include /* For IFNAMSIZ */ - -#include "ltt-tracer.h" - -/* - * largest_align must be non-zero, equal to the minimum between the largest type - * and sizeof(void *). - */ -extern void _ltt_specialized_trace(const struct marker *mdata, void *probe_data, - void *serialize_private, unsigned int data_size, - unsigned int largest_align); - -/* - * Statically check that 0 < largest_align < sizeof(void *) to make sure it is - * dumb-proof. It will make sure 0 is changed into 1 and unsigned long long is - * changed into sizeof(void *) on 32-bit architectures. - */ -static inline void ltt_specialized_trace(const struct marker *mdata, - void *probe_data, - void *serialize_private, unsigned int data_size, - unsigned int largest_align) -{ - largest_align = min_t(unsigned int, largest_align, sizeof(void *)); - largest_align = max_t(unsigned int, largest_align, 1); - _ltt_specialized_trace(mdata, probe_data, serialize_private, data_size, - largest_align); -} - -/* - * Type serializer definitions. - */ - -/* - * Return size of structure without end-of-structure padding. - */ -#define serialize_sizeof(type) offsetof(typeof(type), end_field) - -struct serialize_long_int { - unsigned long f1; - unsigned int f2; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_int_int_long { - unsigned int f1; - unsigned int f2; - unsigned long f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_int_int_short { - unsigned int f1; - unsigned int f2; - unsigned short f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_long { - unsigned long f1; - unsigned long f2; - unsigned long f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_int { - unsigned long f1; - unsigned long f2; - unsigned int f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_short_char { - unsigned long f1; - unsigned long f2; - unsigned short f3; - unsigned char f4; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_short { - unsigned long f1; - unsigned long f2; - unsigned short f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_short_char { - unsigned long f1; - unsigned short f2; - unsigned char f3; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_short { - unsigned long f1; - unsigned short f2; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_char { - unsigned long f1; - unsigned char f2; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_ifname { - unsigned long f1; - unsigned char f2[IFNAMSIZ]; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_sizet_int { - size_t f1; - unsigned int f2; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_sizet_int { - unsigned long f1; - unsigned long f2; - size_t f3; - unsigned int f4; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_long_long_sizet_int_int { - unsigned long f1; - unsigned long f2; - size_t f3; - unsigned int f4; - unsigned int f5; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_l4421224411111 { - unsigned long f1; - uint32_t f2; - uint32_t f3; - uint16_t f4; - uint8_t f5; - uint16_t f6; - uint16_t f7; - uint32_t f8; - uint32_t f9; - uint8_t f10; - uint8_t f11; - uint8_t f12; - uint8_t f13; - uint8_t f14; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_l214421224411111 { - unsigned long f1; - uint16_t f2; - uint8_t f3; - uint32_t f4; - uint32_t f5; - uint16_t f6; - uint8_t f7; - uint16_t f8; - uint16_t f9; - uint32_t f10; - uint32_t f11; - uint8_t f12; - uint8_t f13; - uint8_t f14; - uint8_t f15; - uint8_t f16; - uint8_t end_field[0]; -} RING_BUFFER_ALIGN_ATTR; - -struct serialize_l4412228 { - unsigned long f1; - uint32_t f2; - uint32_t f3; - uint8_t f4; - uint16_t f5; - uint16_t f6; - uint16_t f7; - uint64_t f8; - unsigned char end_field[0]; -} RING_BUFFER_ALIGN_ATTR; -#endif /* _LTT_TYPE_SERIALIZER_H */