tracelevel module: Prioritize trace events
Alon Farchy [Wed, 6 Jul 2011 00:12:29 +0000 (17:12 -0700)]
This module lets subsystem authors prioritize ftrace events
by calling tracelevel_register(...). High priority traces
will be automatically enabled on boot. See tracelevel.h
for more details

Original-Change-Id: If03699e96c598bdcf93b9a9f73918ce7b0c750cb
Reviewed-on: http://git-master/r/40290
Reviewed-by: Alon Farchy <afarchy@nvidia.com>
Tested-by: Alon Farchy <afarchy@nvidia.com>
Reviewed-by: Daniel Willemsen <dwillemsen@nvidia.com>
Tested-by: Daniel Solomon <daniels@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>

Rebase-Id: R49f59f81d61907f66fdf892130a1b4dc6575d40e

Documentation/trace/tracelevel.txt [new file with mode: 0644]
include/linux/tracelevel.h [new file with mode: 0644]
kernel/trace/Kconfig
kernel/trace/Makefile
kernel/trace/tracelevel.c [new file with mode: 0644]

diff --git a/Documentation/trace/tracelevel.txt b/Documentation/trace/tracelevel.txt
new file mode 100644 (file)
index 0000000..b282dd2
--- /dev/null
@@ -0,0 +1,42 @@
+                        Tracelevel
+
+               Documentation by Alon Farchy
+
+1. Overview
+===========
+
+Tracelevel allows subsystem authors to add trace priorities to
+their tracing events. High priority traces will be enabled
+automatically at boot time.
+
+This module is configured with CONFIG_TRACELEVEL.
+
+2. Usage
+=========
+
+To give an event a priority, use the function tracelevel_register
+at any time.
+
+       tracelevel_register(my_event, level);
+
+my_event corresponds directly to the event name as defined in the
+event header file. Available levels are:
+
+       TRACELEVEL_ERR 3
+       TRACELEVEL_WARN 2
+       TRACELEVEL_INFO 1
+       TRACELEVEL_DEBUG 0
+
+Any event registered at boot time as TRACELEVEL_ERR will be enabled
+by default. The header also exposes the function tracelevel_set_level
+to change the trace level at runtime. Any trace event registered with the
+specified level or higher will be enabled with this call.
+
+A userspace handle to tracelevel_set_level is available via the module
+parameter 'level'. For example,
+
+       echo 1 > /sys/module/tracelevel/parameters/level
+
+Is logically equivalent to:
+
+       tracelevel_set_level(TRACELEVEL_INFO);
diff --git a/include/linux/tracelevel.h b/include/linux/tracelevel.h
new file mode 100644 (file)
index 0000000..ac3351c
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * include/linux/tracelevel.c
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _TRACELEVEL_H
+#define _TRACELEVEL_H
+
+/* tracelevel allows a subsystem author to add priorities to
+ * trace_events. For usage details, see tracelevel.txt.
+ */
+
+#define TRACELEVEL_ERR 3
+#define TRACELEVEL_WARN 2
+#define TRACELEVEL_INFO 1
+#define TRACELEVEL_DEBUG 0
+
+#define TRACELEVEL_MAX TRACELEVEL_ERR
+#define TRACELEVEL_DEFAULT TRACELEVEL_ERR
+
+int __tracelevel_register(char *name, unsigned int level);
+int tracelevel_set_level(int level);
+
+#define tracelevel_register(name, level)       \
+       __tracelevel_register(#name, level)
+
+#endif /* _TRACELEVEL_H */
index cd31345..997ef60 100644 (file)
@@ -487,6 +487,15 @@ config RING_BUFFER_BENCHMARK
 
          If unsure, say N.
 
+config TRACELEVEL
+       bool "Add capability to prioritize traces"
+       depends on EVENT_TRACING
+       help
+         This option allows subsystem programmers to add priorities to trace
+         events by calling to tracelevel_register. Traces of high priority
+         will automatically be enabled on kernel boot, and users can change
+         the the trace level in a kernel parameter.
+
 endif # FTRACE
 
 endif # TRACING_SUPPORT
index 5f39a07..7161323 100644 (file)
@@ -61,5 +61,6 @@ endif
 ifeq ($(CONFIG_TRACING),y)
 obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
 endif
+obj-$(CONFIG_TRACELEVEL) += tracelevel.o
 
 libftrace-y := ftrace.o
diff --git a/kernel/trace/tracelevel.c b/kernel/trace/tracelevel.c
new file mode 100644 (file)
index 0000000..9f8b8ee
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * kernel/trace/tracelevel.c
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/ftrace_event.h>
+#include <linux/list.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/tracelevel.h>
+#include <linux/vmalloc.h>
+
+#include "trace.h"
+
+#define TAG KERN_ERR "tracelevel: "
+
+struct tracelevel_record {
+       struct list_head list;
+       char *name;
+       int level;
+};
+
+static LIST_HEAD(tracelevel_list);
+
+static bool started;
+static unsigned int tracelevel_level = TRACELEVEL_DEFAULT;
+
+static DEFINE_MUTEX(tracelevel_record_lock);
+
+/* tracelevel_set_event sets a single event if set = 1, or
+ * clears an event if set = 0.
+ */
+static int tracelevel_set_event(struct tracelevel_record *evt, bool set)
+{
+       if (trace_set_clr_event(NULL, evt->name, set) < 0) {
+               printk(TAG "failed to set event %s\n", evt->name);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Registers an event. If possible, it also sets it.
+ * If not, we'll set it in tracelevel_init.
+ */
+int __tracelevel_register(char *name, unsigned int level)
+{
+       struct tracelevel_record *evt = (struct tracelevel_record *)
+               vmalloc(sizeof(struct tracelevel_record));
+       if (!evt) {
+               printk(TAG "failed to allocate tracelevel_record for %s\n",
+                       name);
+               return -ENOMEM;
+       }
+
+       evt->name = name;
+       evt->level = level;
+
+       mutex_lock(&tracelevel_record_lock);
+       list_add(&evt->list, &tracelevel_list);
+       mutex_unlock(&tracelevel_record_lock);
+
+       if (level >= tracelevel_level && started)
+               tracelevel_set_event(evt, 1);
+       return 0;
+}
+
+/* tracelevel_set_level sets the global level, clears events
+ * lower than that level, and enables events greater or equal.
+ */
+int tracelevel_set_level(int level)
+{
+       struct tracelevel_record *evt = NULL;
+
+       if (level < 0 || level > TRACELEVEL_MAX)
+               return -EINVAL;
+       tracelevel_level = level;
+
+       mutex_lock(&tracelevel_record_lock);
+       list_for_each_entry(evt, &tracelevel_list, list) {
+               if (evt->level >= level)
+                       tracelevel_set_event(evt, 1);
+               else
+                       tracelevel_set_event(evt, 0);
+       }
+       mutex_unlock(&tracelevel_record_lock);
+       return 0;
+}
+
+static int param_set_level(const char *val, const struct kernel_param *kp)
+{
+       int level, ret;
+       ret = strict_strtol(val, 0, &level);
+       if (ret < 0)
+               return ret;
+       return tracelevel_set_level(level);
+}
+
+static int param_get_level(char *buffer, const struct kernel_param *kp)
+{
+       return param_get_int(buffer, kp);
+}
+
+static struct kernel_param_ops tracelevel_level_ops = {
+       .set = param_set_level,
+       .get = param_get_level
+};
+
+module_param_cb(level, &tracelevel_level_ops, &tracelevel_level, 0644);
+
+/* Turn on the tracing that has been registered thus far. */
+static int __init tracelevel_init(void)
+{
+       int ret;
+       started = true;
+
+       /* Ring buffer is initialize to 1 page until the user sets a tracer.
+        * Since we're doing this manually, we need to ask for expanded buffer.
+        */
+       ret = tracing_update_buffers();
+       if (ret < 0)
+               return ret;
+
+       return tracelevel_set_level(tracelevel_level);
+}
+
+/* Tracing mechanism is set up during fs_initcall. */
+fs_initcall_sync(tracelevel_init);