|
@@ -0,0 +1,128 @@
|
|
|
+/*
|
|
|
+ * Copyright (C) 1991, 1992 Linus Torvalds
|
|
|
+ * Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
|
|
|
+ * Copyright (C) 2009 Matt Fleming
|
|
|
+ */
|
|
|
+#include <linux/kallsyms.h>
|
|
|
+#include <linux/ftrace.h>
|
|
|
+#include <linux/debug_locks.h>
|
|
|
+
|
|
|
+#include <asm/stacktrace.h>
|
|
|
+
|
|
|
+void printk_address(unsigned long address, int reliable)
|
|
|
+{
|
|
|
+ printk(" [<%p>] %s%pS\n", (void *) address,
|
|
|
+ reliable ? "" : "? ", (void *) address);
|
|
|
+}
|
|
|
+
|
|
|
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
|
|
+static void
|
|
|
+print_ftrace_graph_addr(unsigned long addr, void *data,
|
|
|
+ const struct stacktrace_ops *ops,
|
|
|
+ struct thread_info *tinfo, int *graph)
|
|
|
+{
|
|
|
+ struct task_struct *task = tinfo->task;
|
|
|
+ unsigned long ret_addr;
|
|
|
+ int index = task->curr_ret_stack;
|
|
|
+
|
|
|
+ if (addr != (unsigned long)return_to_handler)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!task->ret_stack || index < *graph)
|
|
|
+ return;
|
|
|
+
|
|
|
+ index -= *graph;
|
|
|
+ ret_addr = task->ret_stack[index].ret;
|
|
|
+
|
|
|
+ ops->address(data, ret_addr, 1);
|
|
|
+
|
|
|
+ (*graph)++;
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline void
|
|
|
+print_ftrace_graph_addr(unsigned long addr, void *data,
|
|
|
+ const struct stacktrace_ops *ops,
|
|
|
+ struct thread_info *tinfo, int *graph)
|
|
|
+{ }
|
|
|
+#endif
|
|
|
+
|
|
|
+/*
|
|
|
+ * Unwind the call stack and pass information to the stacktrace_ops
|
|
|
+ * functions.
|
|
|
+ */
|
|
|
+void dump_trace(struct task_struct *task, struct pt_regs *regs,
|
|
|
+ unsigned long *sp, const struct stacktrace_ops *ops,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct thread_info *context;
|
|
|
+ int graph = 0;
|
|
|
+
|
|
|
+ context = (struct thread_info *)
|
|
|
+ ((unsigned long)sp & (~(THREAD_SIZE - 1)));
|
|
|
+
|
|
|
+ while (!kstack_end(sp)) {
|
|
|
+ unsigned long addr = *sp++;
|
|
|
+
|
|
|
+ if (__kernel_text_address(addr)) {
|
|
|
+ ops->address(data, addr, 0);
|
|
|
+
|
|
|
+ print_ftrace_graph_addr(addr, data, ops,
|
|
|
+ context, &graph);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(dump_trace);
|
|
|
+
|
|
|
+
|
|
|
+static void
|
|
|
+print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
|
|
|
+{
|
|
|
+ printk(data);
|
|
|
+ print_symbol(msg, symbol);
|
|
|
+ printk("\n");
|
|
|
+}
|
|
|
+
|
|
|
+static void print_trace_warning(void *data, char *msg)
|
|
|
+{
|
|
|
+ printk("%s%s\n", (char *)data, msg);
|
|
|
+}
|
|
|
+
|
|
|
+static int print_trace_stack(void *data, char *name)
|
|
|
+{
|
|
|
+ printk("%s <%s> ", (char *)data, name);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Print one address/symbol entries per line.
|
|
|
+ */
|
|
|
+static void print_trace_address(void *data, unsigned long addr, int reliable)
|
|
|
+{
|
|
|
+ printk(data);
|
|
|
+ printk_address(addr, reliable);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct stacktrace_ops print_trace_ops = {
|
|
|
+ .warning = print_trace_warning,
|
|
|
+ .warning_symbol = print_trace_warning_symbol,
|
|
|
+ .stack = print_trace_stack,
|
|
|
+ .address = print_trace_address,
|
|
|
+};
|
|
|
+
|
|
|
+void show_trace(struct task_struct *tsk, unsigned long *sp,
|
|
|
+ struct pt_regs *regs)
|
|
|
+{
|
|
|
+ if (regs && user_mode(regs))
|
|
|
+ return;
|
|
|
+
|
|
|
+ printk("\nCall trace:\n");
|
|
|
+
|
|
|
+ dump_trace(tsk, regs, sp, &print_trace_ops, "");
|
|
|
+
|
|
|
+ printk("\n");
|
|
|
+
|
|
|
+ if (!tsk)
|
|
|
+ tsk = current;
|
|
|
+
|
|
|
+ debug_show_held_locks(tsk);
|
|
|
+}
|