]> Pileus Git - ~andy/linux/blobdiff - kernel/trace/trace.c
tracing: Add skip argument to trace_dump_stack()
[~andy/linux] / kernel / trace / trace.c
index 906049c0af9000fd75603f10d3fe801c3234db91..8aa53213201ff985e766b30541b243d9d93ead53 100644 (file)
@@ -1657,8 +1657,9 @@ void __trace_stack(struct trace_array *tr, unsigned long flags, int skip,
 
 /**
  * trace_dump_stack - record a stack back trace in the trace buffer
+ * @skip: Number of functions to skip (helper handlers)
  */
-void trace_dump_stack(void)
+void trace_dump_stack(int skip)
 {
        unsigned long flags;
 
@@ -1667,9 +1668,13 @@ void trace_dump_stack(void)
 
        local_save_flags(flags);
 
-       /* skipping 3 traces, seems to get us at the caller of this function */
-       __ftrace_trace_stack(global_trace.trace_buffer.buffer, flags, 3,
-                            preempt_count(), NULL);
+       /*
+        * Skip 3 more, seems to get us at the caller of
+        * this function.
+        */
+       skip += 3;
+       __ftrace_trace_stack(global_trace.trace_buffer.buffer,
+                            flags, skip, preempt_count(), NULL);
 }
 
 static DEFINE_PER_CPU(int, user_stack_count);
@@ -5086,7 +5091,114 @@ static const struct file_operations tracing_dyn_info_fops = {
        .read           = tracing_read_dyn_info,
        .llseek         = generic_file_llseek,
 };
-#endif
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+#if defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE)
+static void
+ftrace_snapshot(unsigned long ip, unsigned long parent_ip, void **data)
+{
+       tracing_snapshot();
+}
+
+static void
+ftrace_count_snapshot(unsigned long ip, unsigned long parent_ip, void **data)
+{
+       unsigned long *count = (long *)data;
+
+       if (!*count)
+               return;
+
+       if (*count != -1)
+               (*count)--;
+
+       tracing_snapshot();
+}
+
+static int
+ftrace_snapshot_print(struct seq_file *m, unsigned long ip,
+                     struct ftrace_probe_ops *ops, void *data)
+{
+       long count = (long)data;
+
+       seq_printf(m, "%ps:", (void *)ip);
+
+       seq_printf(m, "snapshot");
+
+       if (count == -1)
+               seq_printf(m, ":unlimited\n");
+       else
+               seq_printf(m, ":count=%ld\n", count);
+
+       return 0;
+}
+
+static struct ftrace_probe_ops snapshot_probe_ops = {
+       .func                   = ftrace_snapshot,
+       .print                  = ftrace_snapshot_print,
+};
+
+static struct ftrace_probe_ops snapshot_count_probe_ops = {
+       .func                   = ftrace_count_snapshot,
+       .print                  = ftrace_snapshot_print,
+};
+
+static int
+ftrace_trace_snapshot_callback(struct ftrace_hash *hash,
+                              char *glob, char *cmd, char *param, int enable)
+{
+       struct ftrace_probe_ops *ops;
+       void *count = (void *)-1;
+       char *number;
+       int ret;
+
+       /* hash funcs only work with set_ftrace_filter */
+       if (!enable)
+               return -EINVAL;
+
+       ops = param ? &snapshot_count_probe_ops :  &snapshot_probe_ops;
+
+       if (glob[0] == '!') {
+               unregister_ftrace_function_probe_func(glob+1, ops);
+               return 0;
+       }
+
+       if (!param)
+               goto out_reg;
+
+       number = strsep(&param, ":");
+
+       if (!strlen(number))
+               goto out_reg;
+
+       /*
+        * We use the callback data field (which is a pointer)
+        * as our counter.
+        */
+       ret = kstrtoul(number, 0, (unsigned long *)&count);
+       if (ret)
+               return ret;
+
+ out_reg:
+       ret = register_ftrace_function_probe(glob, ops, count);
+
+       if (ret >= 0)
+               alloc_snapshot(&global_trace);
+
+       return ret < 0 ? ret : 0;
+}
+
+static struct ftrace_func_command ftrace_snapshot_cmd = {
+       .name                   = "snapshot",
+       .func                   = ftrace_trace_snapshot_callback,
+};
+
+static int register_snapshot_cmd(void)
+{
+       return register_ftrace_command(&ftrace_snapshot_cmd);
+}
+#else
+static inline int register_snapshot_cmd(void) { return 0; }
+#endif /* defined(CONFIG_TRACER_SNAPSHOT) && defined(CONFIG_DYNAMIC_FTRACE) */
 
 struct dentry *tracing_init_dentry_tr(struct trace_array *tr)
 {
@@ -6076,6 +6188,8 @@ __init static int tracer_alloc_buffers(void)
                trace_set_options(&global_trace, option);
        }
 
+       register_snapshot_cmd();
+
        return 0;
 
 out_free_cpumask: