ftrace: add quick function trace stop
authorSteven Rostedt <srostedt@redhat.com>
Wed, 5 Nov 2008 21:05:44 +0000 (16:05 -0500)
committerIngo Molnar <mingo@elte.hu>
Thu, 6 Nov 2008 06:50:51 +0000 (07:50 +0100)
Impact: quick start and stop of function tracer

This patch adds a way to disable the function tracer quickly without
the need to run kstop_machine. It adds a new variable called
function_trace_stop which will stop the calls to functions from mcount
when set.  This is just an on/off switch and does not handle recursion
like preempt_disable().

It's main purpose is to help other tracers/debuggers start and stop tracing
fuctions without the need to call kstop_machine.

The config option HAVE_FUNCTION_TRACE_MCOUNT_TEST is added for archs
that implement the testing of the function_trace_stop in the mcount
arch dependent code. Otherwise, the test is done in the C code.

x86 is the only arch at the moment that supports this.

Signed-off-by: Steven Rostedt <srostedt@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
arch/x86/Kconfig
arch/x86/kernel/entry_32.S
arch/x86/kernel/entry_64.S
include/linux/ftrace.h
kernel/trace/Kconfig
kernel/trace/ftrace.c

index 6f20718d3156b27badfe7774f09bb89f8bfe34f7..d09e812c62237c8d4d21027eb7bb880967f3438c 100644 (file)
@@ -29,6 +29,7 @@ config X86
        select HAVE_FTRACE_MCOUNT_RECORD
        select HAVE_DYNAMIC_FTRACE
        select HAVE_FUNCTION_TRACER
+       select HAVE_FUNCTION_TRACE_MCOUNT_TEST
        select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
        select HAVE_ARCH_KGDB if !X86_VOYAGER
        select HAVE_ARCH_TRACEHOOK
index 28b597ef9ca16b7992c333f10eae252ea695475c..9134de814c9751e42f7f7a06510c1f29133cabe1 100644 (file)
@@ -1157,6 +1157,9 @@ ENTRY(mcount)
 END(mcount)
 
 ENTRY(ftrace_caller)
+       cmpl $0, function_trace_stop
+       jne  ftrace_stub
+
        pushl %eax
        pushl %ecx
        pushl %edx
@@ -1180,6 +1183,9 @@ END(ftrace_caller)
 #else /* ! CONFIG_DYNAMIC_FTRACE */
 
 ENTRY(mcount)
+       cmpl $0, function_trace_stop
+       jne  ftrace_stub
+
        cmpl $ftrace_stub, ftrace_trace_function
        jnz trace
 .globl ftrace_stub
index b86f332c96a66596f0fe076d7c0eda78ea05dbe5..08aa6b10933cd74232a3f406f8e6f706a294c245 100644 (file)
@@ -68,6 +68,8 @@ ENTRY(mcount)
 END(mcount)
 
 ENTRY(ftrace_caller)
+       cmpl $0, function_trace_stop
+       jne  ftrace_stub
 
        /* taken from glibc */
        subq $0x38, %rsp
@@ -103,6 +105,9 @@ END(ftrace_caller)
 
 #else /* ! CONFIG_DYNAMIC_FTRACE */
 ENTRY(mcount)
+       cmpl $0, function_trace_stop
+       jne  ftrace_stub
+
        cmpq $ftrace_stub, ftrace_trace_function
        jnz trace
 .globl ftrace_stub
index 4642959e5bda53b33061b37313b9cafd4c1aaa70..794ab907dbfea5520adc19fdf177d8178edbf4c8 100644 (file)
@@ -23,6 +23,34 @@ struct ftrace_ops {
        struct ftrace_ops *next;
 };
 
+extern int function_trace_stop;
+
+/**
+ * ftrace_stop - stop function tracer.
+ *
+ * A quick way to stop the function tracer. Note this an on off switch,
+ * it is not something that is recursive like preempt_disable.
+ * This does not disable the calling of mcount, it only stops the
+ * calling of functions from mcount.
+ */
+static inline void ftrace_stop(void)
+{
+       function_trace_stop = 1;
+}
+
+/**
+ * ftrace_start - start the function tracer.
+ *
+ * This function is the inverse of ftrace_stop. This does not enable
+ * the function tracing if the function tracer is disabled. This only
+ * sets the function tracer flag to continue calling the functions
+ * from mcount.
+ */
+static inline void ftrace_start(void)
+{
+       function_trace_stop = 0;
+}
+
 /*
  * The ftrace_ops must be a static and should also
  * be read_mostly.  These functions do modify read_mostly variables
@@ -41,6 +69,8 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1);
 # define unregister_ftrace_function(ops) do { } while (0)
 # define clear_ftrace_function(ops) do { } while (0)
 static inline void ftrace_kill(void) { }
+static inline void ftrace_stop(void) { }
+static inline void ftrace_start(void) { }
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_DYNAMIC_FTRACE
index 33dbefd471e88f9571f299f92b433188dd6697de..fc4febc3334a5c7f013985cf2db13b7ba3db4387 100644 (file)
@@ -9,6 +9,13 @@ config NOP_TRACER
 config HAVE_FUNCTION_TRACER
        bool
 
+config HAVE_FUNCTION_TRACE_MCOUNT_TEST
+       bool
+       help
+        This gets selected when the arch tests the function_trace_stop
+        variable at the mcount call site. Otherwise, this variable
+        is tested by the called function.
+
 config HAVE_DYNAMIC_FTRACE
        bool
 
index 4a39d24568c82ffddbcbfed2a6dcb5d2b9eb7f22..896c71f0f4c4fc745b6bd447ca4f0ddee93d6dd7 100644 (file)
@@ -47,6 +47,9 @@
 int ftrace_enabled __read_mostly;
 static int last_ftrace_enabled;
 
+/* Quick disabling of function tracer. */
+int function_trace_stop;
+
 /*
  * ftrace_disabled is set when an anomaly is discovered.
  * ftrace_disabled is much stronger than ftrace_enabled.
@@ -63,6 +66,7 @@ static struct ftrace_ops ftrace_list_end __read_mostly =
 
 static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end;
 ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
+ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
 
 static void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
 {
@@ -88,8 +92,23 @@ static void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
 void clear_ftrace_function(void)
 {
        ftrace_trace_function = ftrace_stub;
+       __ftrace_trace_function = ftrace_stub;
 }
 
+#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
+/*
+ * For those archs that do not test ftrace_trace_stop in their
+ * mcount call site, we need to do it from C.
+ */
+static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip)
+{
+       if (function_trace_stop)
+               return;
+
+       __ftrace_trace_function(ip, parent_ip);
+}
+#endif
+
 static int __register_ftrace_function(struct ftrace_ops *ops)
 {
        /* should not be called from interrupt context */
@@ -110,10 +129,18 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
                 * For one func, simply call it directly.
                 * For more than one func, call the chain.
                 */
+#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
                if (ops->next == &ftrace_list_end)
                        ftrace_trace_function = ops->func;
                else
                        ftrace_trace_function = ftrace_list_func;
+#else
+               if (ops->next == &ftrace_list_end)
+                       __ftrace_trace_function = ops->func;
+               else
+                       __ftrace_trace_function = ftrace_list_func;
+               ftrace_trace_function = ftrace_test_stop_func;
+#endif
        }
 
        spin_unlock(&ftrace_lock);
@@ -526,7 +553,7 @@ static void ftrace_run_update_code(int command)
 }
 
 static ftrace_func_t saved_ftrace_func;
-static int ftrace_start;
+static int ftrace_start_up;
 static DEFINE_MUTEX(ftrace_start_lock);
 
 static void ftrace_startup(void)
@@ -537,8 +564,8 @@ static void ftrace_startup(void)
                return;
 
        mutex_lock(&ftrace_start_lock);
-       ftrace_start++;
-       if (ftrace_start == 1)
+       ftrace_start_up++;
+       if (ftrace_start_up == 1)
                command |= FTRACE_ENABLE_CALLS;
 
        if (saved_ftrace_func != ftrace_trace_function) {
@@ -562,8 +589,8 @@ static void ftrace_shutdown(void)
                return;
 
        mutex_lock(&ftrace_start_lock);
-       ftrace_start--;
-       if (!ftrace_start)
+       ftrace_start_up--;
+       if (!ftrace_start_up)
                command |= FTRACE_DISABLE_CALLS;
 
        if (saved_ftrace_func != ftrace_trace_function) {
@@ -589,8 +616,8 @@ static void ftrace_startup_sysctl(void)
        mutex_lock(&ftrace_start_lock);
        /* Force update next time */
        saved_ftrace_func = NULL;
-       /* ftrace_start is true if we want ftrace running */
-       if (ftrace_start)
+       /* ftrace_start_up is true if we want ftrace running */
+       if (ftrace_start_up)
                command |= FTRACE_ENABLE_CALLS;
 
        ftrace_run_update_code(command);
@@ -605,8 +632,8 @@ static void ftrace_shutdown_sysctl(void)
                return;
 
        mutex_lock(&ftrace_start_lock);
-       /* ftrace_start is true if ftrace is running */
-       if (ftrace_start)
+       /* ftrace_start_up is true if ftrace is running */
+       if (ftrace_start_up)
                command |= FTRACE_DISABLE_CALLS;
 
        ftrace_run_update_code(command);
@@ -1186,7 +1213,7 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable)
 
        mutex_lock(&ftrace_sysctl_lock);
        mutex_lock(&ftrace_start_lock);
-       if (iter->filtered && ftrace_start && ftrace_enabled)
+       if (iter->filtered && ftrace_start_up && ftrace_enabled)
                ftrace_run_update_code(FTRACE_ENABLE_CALLS);
        mutex_unlock(&ftrace_start_lock);
        mutex_unlock(&ftrace_sysctl_lock);