--- /dev/null
+/*
+ * ltt-debugfs-abi.c
+ *
+ * Copyright 2010 (c) - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * LTTng debugfs ABI
+ *
+ * Mimic system calls for:
+ * - session creation, returns a file descriptor or failure.
+ * - channel creation, returns a file descriptor or failure.
+ * - Takes a session file descriptor parameter
+ * - Takes all channel options as parameters.
+ * - event creation, returns a file descriptor or failure.
+ * - Takes an event name as parameter
+ * - Takes an instrumentation source as parameter
+ * - e.g. tracepoints, dynamic_probes...
+ * - Takes instrumentation source specific arguments.
+ */
+
+#include <linux/debugfs.h>
+
+/*
+ * This is LTTng's own personal way to create a system call as an external
+ * module. We use ioctl() on /sys/kernel/debug/lttng.
+ */
+
+static struct dentry *lttng_dentry;
+
+/*
+ * LTTng DebugFS ABI structures.
+ */
+
+struct lttng_channel {
+ int session; /* Session file descriptor */
+ int overwrite; /* 1: overwrite, 0: discard */
+ u64 subbuf_size;
+ u64 num_subbuf;
+ unsigned int switch_timer_interval;
+ unsigned int read_timer_interval;
+};
+
+struct lttng_event {
+ int channel; /* Channel file descriptor */
+ enum instrum_type itype;
+ char name[];
+};
+
+int lttng_abi_create_session(void)
+{
+ struct ltt_session *session;
+ struct file *session_file;
+ int session_fd;
+
+ session = ltt_session_create()
+ if (!session)
+ return -ENOMEM;
+ session_fd = get_unused_fd_flags(O_RDWR);
+ if (session_fd < 0) {
+ ret = session_fd;
+ goto fd_error;
+ }
+ session_file = anon_inode_getfile("[lttng_session]",
+ <tng_fops,
+ session, O_RDWR);
+ if (IS_ERR(session_file)) {
+ ret = PTR_ERR(session_file);
+ goto file_error;
+ }
+ return session_fd;
+
+file_error:
+ put_unused_fd(session_fd);
+fd_error:
+ ltt_session_destroy(session);
+ return ret;
+}
+
+int lttng_abi_create_channel(struct lttng_channel __user *uchan_param)
+{
+ struct ltt_channel *chan;
+ struct file *chan_file;
+ struct lttng_channel chan_param;
+ int chan_fd;
+
+ if (copy_from_user(&chan_param, ucham_param, sizeof(chan_param)))
+ return -EFAULT;
+ /* TODO: fetch session pointer from file descriptor */
+ chan = ltt_channel_create();
+ if (!chan)
+ return -ENOMEM;
+ chan_fd = get_unused_fd_flags(O_RDWR);
+ if (chan_fd < 0) {
+ ret = chan_fd;
+ goto fd_error;
+ }
+ chan_file = anon_inode_getfile("[lttng_channel]",
+ <tng_fops,
+ chan, O_RDWR);
+ if (IS_ERR(chan_file)) {
+ ret = PTR_ERR(chan_file);
+ goto file_error;
+ }
+ return chan_fd;
+
+file_error:
+ put_unused_fd(chan_fd);
+fd_error:
+ ltt_channel_destroy(chan);
+ return ret;
+}
+
+/**
+ * lttng_ioctl - lttng syscall through ioctl
+ *
+ * @filp: the file
+ * @cmd: the command
+ * @arg: command arg
+ *
+ * This ioctl implements lttng commands:
+ * LTTNG_SESSION
+ * Returns a LTTng trace session file descriptor
+ * LTTNG_CHANNEL
+ * Returns a LTTng channel file descriptor
+ * LTTNG_EVENT
+ * Returns a file descriptor or failure.
+ *
+ * The returned session will be deleted when its file descriptor is closed.
+ * Channel and event file descriptors also hold a reference on the session.
+ */
+long lttng_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ switch (cmd) {
+ case LTTNG_SESSION:
+ return lttng_abi_create_session();
+ case LTTNG_CHANNEL:
+ return lttng_abi_create_channel((struct lttng_channel __user *)arg);
+ case LTTNG_EVENT:
+ return lttng_abi_create_event((struct lttng_event __user *)arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+long lttng_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = filp->f_dentry->d_inode;
+
+ switch (cmd) {
+ case LTTNG_SESSION:
+ return lttng_abi_create_session();
+ case LTTNG_CHANNEL:
+ return lttng_abi_create_channel((struct lttng_channel __user *)arg);
+ case LTTNG_EVENT:
+ return lttng_abi_create_event((struct lttng_event __user *)arg);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+#endif
+
+const struct file_operations lttng_file_operations = {
+ .unlocked_ioctl = lttng_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lttng_compat_ioctl,
+#endif
+}
+
+static int __init ltt_debugfs_abi_init(void)
+{
+ int ret = 0;
+
+ lttng_dentry = debugfs_create_file("lttng", NULL);
+ if (IS_ERR(lttng_dentry) || !lttng_dentry)
+ printk(KERN_ERR "Error creating LTTng control file\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+error:
+ return ret;
+}
+
+static void __exit ltt_debugfs_abi_exit(void)
+{
+ debugfs_remote(lttng_dentry);
+}
#endif
}
-struct ltt_session *ltt_session_create(char *name)
+struct ltt_session *ltt_session_create(void)
{
struct ltt_session *session;
mutex_lock(&sessions_mutex);
- list_for_each_entry(session, &sessions, list)
- if (!strcmp(session->name, name))
- goto exist;
- session = kmalloc(sizeof(struct ltt_session) + strlen(name) + 1);
+ session = kmalloc(sizeof(struct ltt_session));
if (!session)
return NULL;
- strcpy(session->name, name);
INIT_LIST_HEAD(&session->chan);
list_add(&session->list, &sessions);
mutex_unlock(&sessions_mutex);
kfree(session);
}
-struct ltt_channel *ltt_channel_create(struct ltt_session *session, char *name,
+struct ltt_channel *ltt_channel_create(struct ltt_session *session,
int overwrite, void *buf_addr,
size_t subbuf_size, size_t num_subbuf,
unsigned int switch_timer_interval,
mutex_lock(&sessions_mutex);
if (session->active)
goto active; /* Refuse to add channel to active session */
- list_for_each_entry(chan, &session->chan, list)
- if (!strcmp(chan->name, name))
- goto exist;
- chan = kmalloc(sizeof(struct ltt_channel) + strlen(name) + 1, GFP_KERNEL);
+ chan = kmalloc(sizeof(struct ltt_channel), GFP_KERNEL);
if (!chan)
return NULL;
- strcpy(chan->name, name);
chan->session = session;
/* TODO: create rb channel */
* Supports event creation while tracing session is active.
*/
struct ltt_event *ltt_event_create(struct ltt_channel *chan, char *name,
- void *filter)
+ enum instrum_type itype,
+ void *probe, void *filter)
{
struct ltt_event *event;
int ret;
goto error;
strcpy(event->name, name);
event->chan = chan;
+ event->probe = probe;
event->filter = filter;
event->id = chan->free_event_id++;
+ event->itype = itype;
mutex_unlock(&sessions_mutex);
/* Populate ltt_event structure before tracepoint registration. */
smp_wmb();
+ switch (itype) {
+ case INSTRUM_TRACEPOINTS:
+ ret = tracepoint_probe_register(name, probe, event);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
return event;
error:
*/
int _ltt_event_destroy(struct ltt_event *event)
{
- /* TODO unregister from tracepoint */
+ switch (event->itype) {
+ case INSTRUM_TRACEPOINTS:
+ ret = tracepoint_probe_unregister(name, event->probe, event);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ }
kfree(event->name);
kmem_cache_free(event);
}
struct ltt_channel;
struct ltt_session;
+enum instrum_type itype {
+ INSTRUM_TRACEPOINTS,
+};
+
/*
* ltt_event structure is referred to by the tracing fast path. It must be
* kept small.
struct ltt_event {
unsigned int id;
struct ltt_channel *chan;
+ void *probe;
void *filter;
char *name;
+ enum instrum_type itype;
struct list_head list; /* Event list */
};
char name[PATH_MAX];
};
-struct ltt_session *ltt_session_create(char *name);
+struct ltt_session *ltt_session_create(void);
int ltt_session_destroy(struct ltt_session *session);
-struct ltt_channel *ltt_channel_create(struct ltt_session *session, char *name,
+struct ltt_channel *ltt_channel_create(struct ltt_session *session,
int overwrite, void *buf_addr,
size_t subbuf_size, size_t num_subbuf,
unsigned int switch_timer_interval,