[ltt-dev] [PATCH v5 5/5] Add ltt-trace-control module
Zhaolei
zhaolei at cn.fujitsu.com
Thu Nov 13 21:26:10 EST 2008
ltt-trace-control is used to control ltt's trace.
It will replace netlink-based ltt-control.
Signed-off-by: Zhao Lei <zhaolei at cn.fujitsu.com>
---
ltt/Kconfig | 8 +
ltt/Makefile | 1 +
ltt/ltt-trace-control.c | 708 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 717 insertions(+), 0 deletions(-)
create mode 100644 ltt/ltt-trace-control.c
diff --git a/ltt/Kconfig b/ltt/Kconfig
index c63734d..341f178 100644
--- a/ltt/Kconfig
+++ b/ltt/Kconfig
@@ -150,6 +150,14 @@ config LTT_NETLINK_CONTROL
If you enable this option, the Linux Trace Toolkit Netlink Controller
will be either built in the kernel or as module.
+config LTT_TRACE_CONTROL
+ tristate "Linux Trace Toolkit Trace Controller"
+ depends on LTT_TRACER
+ default m
+ help
+ If you enable this option, the debugfs-based Linux Trace Toolkit Trace
+ Controller will be either built in the kernel or as module.
+
config LTT_STATEDUMP
tristate "Linux Trace Toolkit State Dump"
depends on LTT_TRACER
diff --git a/ltt/Makefile b/ltt/Makefile
index afcee2e..fcd1a12 100644
--- a/ltt/Makefile
+++ b/ltt/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_LTT_RELAY) += ltt-relay.o
obj-$(CONFIG_LTT_RELAY_LOCKED) += ltt-relay-locked.o
obj-$(CONFIG_LTT_RELAY_ALLOC) += ltt-relay-alloc.o
obj-$(CONFIG_LTT_NETLINK_CONTROL) += ltt-control.o
+obj-$(CONFIG_LTT_TRACE_CONTROL) += ltt-trace-control.o
obj-$(CONFIG_LTT_SERIALIZE) += ltt-serialize.o
obj-$(CONFIG_LTT_STATEDUMP) += ltt-statedump.o
obj-$(CONFIG_LTT_MARKER_CONTROL) += ltt-marker-control.o
diff --git a/ltt/ltt-trace-control.c b/ltt/ltt-trace-control.c
new file mode 100644
index 0000000..70182df
--- /dev/null
+++ b/ltt/ltt-trace-control.c
@@ -0,0 +1,708 @@
+/*
+ * LTT trace control module over debugfs.
+ *
+ * Copyright 2008 -
+ * Zhaolei <zhaolei at cn.fujitsu.com>
+ */
+
+/*
+ * Todo:
+ * Impl read operations for control file to read attributes
+ * Create a README file in ltt control dir, for display help info
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/ltt-tracer.h>
+
+#define LTT_CONTROL_DIR "control"
+#define LTT_SETUP_TRACE_FILE "setup_trace"
+#define LTT_DESTROY_TRACE_FILE "destroy_trace"
+
+#define LTT_WRITE_MAXLEN (128)
+
+struct dentry *ltt_control_dir, *ltt_setup_trace_file, *ltt_destroy_trace_file;
+
+static DEFINE_MUTEX(control_lock);
+
+/*
+ * lookup a file/dir in parent dir.
+ * only designed to work well for debugfs.
+ * (although it maybe ok for other fs)
+ *
+ * return:
+ * file/dir's dentry on success
+ * NULL on failure
+ */
+static struct dentry *dir_lookup(struct dentry *parent, const char *name)
+{
+ struct qstr q;
+ struct dentry *d;
+
+ q.name = name;
+ q.len = strlen(name);
+ q.hash = full_name_hash(q.name, q.len);
+
+ d = d_lookup(parent, &q);
+ if (d)
+ dput(d);
+
+ return d;
+}
+
+
+static ssize_t alloc_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char cmd[NAME_MAX];
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if ((cmd[0] != 'Y' && cmd[0] != 'y' && cmd[0] != '1') || cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ err = ltt_trace_alloc(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "alloc_write: ltt_trace_alloc failed: %d\n",
+ err);
+ goto err_alloc_trace;
+ }
+
+ return count;
+
+err_alloc_trace:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_alloc_operations = {
+ .write = alloc_write,
+};
+
+
+static ssize_t enabled_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char cmd[NAME_MAX];
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if (cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ switch (cmd[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ err = ltt_trace_start(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "enabled_write: ltt_trace_start failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_start_trace;
+ }
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ err = ltt_trace_stop(file->f_dentry->d_parent->d_name.name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "enabled_write: ltt_trace_stop failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_stop_trace;
+ }
+ break;
+ default:
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ return count;
+
+err_stop_trace:
+err_start_trace:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_enabled_operations = {
+ .write = enabled_write,
+};
+
+
+static ssize_t trans_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char trans_name[NAME_MAX];
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trans_name) != 1) {
+ err = -EPERM;
+ goto err_get_transname;
+ }
+
+ err = ltt_trace_set_type(file->f_dentry->d_parent->d_name.name,
+ trans_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "trans_write: ltt_trace_set_type failed: %d\n",
+ err);
+ goto err_set_trans;
+ }
+
+ return count;
+
+err_set_trans:
+err_get_transname:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_trans_operations = {
+ .write = trans_write,
+};
+
+
+static ssize_t subbuf_num_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ unsigned int num;
+ const char *channel_name;
+ const char *trace_name;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%u", &num) != 1) {
+ err = -EPERM;
+ goto err_get_number;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ err = ltt_trace_set_channel_subbufcount(trace_name, channel_name, num);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "subbuf_num_write: "
+ "ltt_trace_set_channel_subbufcount failed: %d\n", err);
+ goto err_set_subbufcount;
+ }
+
+ return count;
+
+err_set_subbufcount:
+err_get_number:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_subbuf_num_operations = {
+ .write = subbuf_num_write,
+};
+
+
+static ssize_t subbuf_size_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ unsigned int num;
+ const char *channel_name;
+ const char *trace_name;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%u", &num) != 1) {
+ err = -EPERM;
+ goto err_get_number;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ err = ltt_trace_set_channel_subbufsize(trace_name, channel_name, num);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "subbuf_size_write: "
+ "ltt_trace_set_channel_subbufsize failed: %d\n", err);
+ goto err_set_subbufsize;
+ }
+
+ return count;
+
+err_set_subbufsize:
+err_get_number:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_subbuf_size_operations = {
+ .write = subbuf_size_write,
+};
+
+
+static ssize_t overwrite_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char cmd[NAME_MAX];
+ const char *channel_name;
+ const char *trace_name;
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", cmd) != 1) {
+ err = -EPERM;
+ goto err_get_cmd;
+ }
+
+ if (cmd[1]) {
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ channel_name = file->f_dentry->d_parent->d_name.name;
+ trace_name = file->f_dentry->d_parent->d_parent->d_parent->d_name.name;
+
+ switch (cmd[0]) {
+ case 'Y':
+ case 'y':
+ case '1':
+ err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
+ 1);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "subbuf_size_write: "
+ "ltt_trace_set_channel_subbufsize failed: %d\n", err);
+ goto err_set_subbufsize;
+ }
+ break;
+ case 'N':
+ case 'n':
+ case '0':
+ err = ltt_trace_set_channel_overwrite(trace_name, channel_name,
+ 0);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "subbuf_size_write: "
+ "ltt_trace_set_channel_subbufsize failed: %d\n", err);
+ goto err_set_subbufsize;
+ }
+ break;
+ default:
+ err = -EPERM;
+ goto err_bad_cmd;
+ }
+
+ return count;
+
+err_set_subbufsize:
+err_bad_cmd:
+err_get_cmd:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_overwrite_operations = {
+ .write = overwrite_write,
+};
+
+
+static const char *channels[] = {
+ "cpu",
+ "processes",
+ "interrupts",
+ "network",
+ "modules",
+ "metadata"
+};
+static int _create_trace_control_dir(const char *trace_name)
+{
+ int err;
+ struct dentry *trace_root, *channel_root;
+ struct dentry *tmp_den;
+ int i;
+
+ /*
+ * for create channels' dir.
+ * Todo:
+ * get channel_names from exported lttng variable.
+ */
+
+
+ /* debugfs/control/trace_name */
+ trace_root = debugfs_create_dir(trace_name, ltt_control_dir);
+ if (IS_ERR(trace_root) || !trace_root) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create control root dir of %s failed\n", trace_name);
+ err = -ENOMEM;
+ goto err_create_trace_root;
+ }
+
+ /* debugfs/control/trace_name/alloc */
+ tmp_den = debugfs_create_file("alloc", S_IWUSR, trace_root, NULL,
+ <t_alloc_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of alloc failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/trans */
+ tmp_den = debugfs_create_file("trans", S_IWUSR, trace_root, NULL,
+ <t_trans_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of trans failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/enabled */
+ tmp_den = debugfs_create_file("enabled", S_IWUSR, trace_root, NULL,
+ <t_enabled_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create file of enabled failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /* debugfs/control/trace_name/channel/ */
+ channel_root = debugfs_create_dir("channel", trace_root);
+ if (IS_ERR(channel_root) || !channel_root) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create dir of channel failed\n");
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ /*
+ * Create dir and files in debugfs/ltt/control/trace_name/channel/
+ * Following things(without <>) will be created:
+ * `-- <control>
+ * `-- <trace_name>
+ * `-- <channel>
+ * |-- interrupts
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | `-- subbuf_size
+ * |-- metadata
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | `-- subbuf_size
+ * |-- modules
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | `-- subbuf_size
+ * |-- network
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | `-- subbuf_size
+ * |-- processes
+ * | |-- overwrite
+ * | |-- subbuf_num
+ * | `-- subbuf_size
+ * `-- cpu
+ * |-- overwrite
+ * |-- subbuf_num
+ * `-- subbuf_size
+ */
+
+ for (i = 0; i < sizeof(channels)/sizeof(channels[0]); i++) {
+ struct dentry *channel_den;
+
+ channel_den = debugfs_create_dir(channels[i], channel_root);
+ if (IS_ERR(channel_den) || !channel_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create channel dir of %s failed\n",
+ channels[i]);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("subbuf_num", S_IWUSR,
+ channel_den, NULL, <t_subbuf_num_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create subbuf_num in %s failed\n",
+ channels[i]);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("subbuf_size", S_IWUSR,
+ channel_den, NULL, <t_subbuf_size_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create subbuf_size in %s failed\n",
+ channels[i]);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+
+ tmp_den = debugfs_create_file("overwrite", S_IWUSR, channel_den,
+ NULL, <t_overwrite_operations);
+ if (IS_ERR(tmp_den) || !tmp_den) {
+ printk(KERN_ERR "_create_trace_control_dir: "
+ "create overwrite in %s failed\n", channels[i]);
+ err = -ENOMEM;
+ goto err_create_subdir;
+ }
+ }
+
+ return 0;
+
+err_create_subdir:
+ debugfs_remove_recursive(trace_root);
+err_create_trace_root:
+ return err;
+}
+
+static ssize_t setup_trace_write(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char trace_name[NAME_MAX];
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trace_name) != 1) {
+ err = -EPERM;
+ goto err_get_tracename;
+ }
+
+ mutex_lock(&control_lock);
+
+ err = ltt_trace_setup(trace_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "setup_trace_write: ltt_trace_setup failed: %d\n", err);
+ goto err_setup_trace;
+ }
+
+ err = _create_trace_control_dir(trace_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR "setup_trace_write: "
+ "_create_trace_control_dir failed: %d\n", err);
+ goto err_create_trace_control_dir;
+ }
+
+ mutex_unlock(&control_lock);
+
+ return count;
+
+err_create_trace_control_dir:
+ ltt_trace_destroy(trace_name);
+err_setup_trace:
+ mutex_unlock(&control_lock);
+err_get_tracename:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_setup_trace_operations = {
+ .write = setup_trace_write,
+};
+
+
+static ssize_t destroy_trace_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ int err = 0;
+ char buf[NAME_MAX];
+ int buf_size;
+ char trace_name[NAME_MAX];
+ struct dentry *trace_den;
+
+
+ buf_size = min(count, sizeof(buf) - 1);
+ err = copy_from_user(buf, user_buf, buf_size);
+ if (err)
+ goto err_copy_from_user;
+ buf[buf_size] = 0;
+
+ if (sscanf(buf, "%s", trace_name) != 1) {
+ err = -EPERM;
+ goto err_get_tracename;
+ }
+
+ mutex_lock(&control_lock);
+
+ err = ltt_trace_destroy(trace_name);
+ if (IS_ERR_VALUE(err)) {
+ printk(KERN_ERR
+ "destroy_trace_write: ltt_trace_destroy failed: %d\n",
+ err);
+ err = -EPERM;
+ goto err_destroy_trace;
+ }
+
+ trace_den = dir_lookup(ltt_control_dir, trace_name);
+ if (!trace_den) {
+ printk(KERN_ERR
+ "destroy_trace_write: lookup for %s's dentry failed\n",
+ trace_name);
+ err = -ENOENT;
+ goto err_get_dentry;
+ }
+
+ debugfs_remove_recursive(trace_den);
+
+ mutex_unlock(&control_lock);
+
+ return count;
+
+err_get_dentry:
+err_destroy_trace:
+ mutex_unlock(&control_lock);
+err_get_tracename:
+err_copy_from_user:
+ return err;
+}
+
+static struct file_operations ltt_destroy_trace_operations = {
+ .write = destroy_trace_write,
+};
+
+
+static int __init ltt_trace_control_init(void)
+{
+ int err = 0;
+ struct dentry *ltt_root_dentry;
+
+ ltt_root_dentry = get_ltt_root();
+ if (!ltt_root_dentry) {
+ err = -ENOENT;
+ goto err_no_root;
+ }
+
+ ltt_control_dir = debugfs_create_dir(LTT_CONTROL_DIR, ltt_root_dentry);
+ if (IS_ERR(ltt_control_dir) || !ltt_control_dir) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create dir of %s failed\n",
+ LTT_CONTROL_DIR);
+ err = -ENOMEM;
+ goto err_create_control_dir;
+ }
+
+ ltt_setup_trace_file = debugfs_create_file(LTT_SETUP_TRACE_FILE,
+ S_IWUSR, ltt_root_dentry, NULL, <t_setup_trace_operations);
+ if (IS_ERR(ltt_setup_trace_file) || !ltt_setup_trace_file) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create file of %s failed\n",
+ LTT_SETUP_TRACE_FILE);
+ err = -ENOMEM;
+ goto err_create_setup_trace_file;
+ }
+
+ ltt_destroy_trace_file = debugfs_create_file(LTT_DESTROY_TRACE_FILE,
+ S_IWUSR, ltt_root_dentry, NULL, <t_destroy_trace_operations);
+ if (IS_ERR(ltt_destroy_trace_file) || !ltt_destroy_trace_file) {
+ printk(KERN_ERR
+ "ltt_channel_control_init: create file of %s failed\n",
+ LTT_DESTROY_TRACE_FILE);
+ err = -ENOMEM;
+ goto err_create_destroy_trace_file;
+ }
+
+ return 0;
+
+err_create_destroy_trace_file:
+ debugfs_remove(ltt_setup_trace_file);
+err_create_setup_trace_file:
+ debugfs_remove(ltt_control_dir);
+err_create_control_dir:
+err_no_root:
+ return err;
+}
+
+static void __exit ltt_trace_control_exit(void)
+{
+ struct dentry *trace_dir;
+
+ /* destory all traces */
+ list_for_each_entry(trace_dir, <t_control_dir->d_subdirs,
+ d_u.d_child) {
+ ltt_trace_stop(trace_dir->d_name.name);
+ ltt_trace_destroy(trace_dir->d_name.name);
+ }
+
+ /* clean dirs in debugfs */
+ debugfs_remove(ltt_setup_trace_file);
+ debugfs_remove(ltt_destroy_trace_file);
+ debugfs_remove_recursive(ltt_control_dir);
+}
+
+module_init(ltt_trace_control_init);
+module_exit(ltt_trace_control_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhao Lei <zhaolei at cn.fujitsu.com>");
+MODULE_DESCRIPTION("Linux Trace Toolkit Trace Controller");
+
--
1.5.5.3
More information about the lttng-dev
mailing list