mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-27 22:53:55 +08:00
c9cf4dbb4d
arch/x86/include/asm/stacktrace.h and arch/x86/kernel/dumpstack.h declare headers of objects that deal with the same topic. Actually most of the files that include stacktrace.h also include dumpstack.h Although dumpstack.h seems more reserved for internals of stack traces, those are quite often needed to define specialized stack trace operations. And perf event arch headers are going to need access to such low level operations anyway. So don't continue to bother with dumpstack.h as it's not anymore about isolated deep internals. v2: fix struct stack_frame definition conflict in sysprof Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Soeren Sandmann <sandmann@daimi.au.dk>
160 lines
3.7 KiB
C
160 lines
3.7 KiB
C
/*
|
|
* Stack trace management functions
|
|
*
|
|
* Copyright (C) 2006-2009 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
|
|
*/
|
|
#include <linux/sched.h>
|
|
#include <linux/stacktrace.h>
|
|
#include <linux/module.h>
|
|
#include <linux/uaccess.h>
|
|
#include <asm/stacktrace.h>
|
|
|
|
static void save_stack_warning(void *data, char *msg)
|
|
{
|
|
}
|
|
|
|
static void
|
|
save_stack_warning_symbol(void *data, char *msg, unsigned long symbol)
|
|
{
|
|
}
|
|
|
|
static int save_stack_stack(void *data, char *name)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void save_stack_address(void *data, unsigned long addr, int reliable)
|
|
{
|
|
struct stack_trace *trace = data;
|
|
if (!reliable)
|
|
return;
|
|
if (trace->skip > 0) {
|
|
trace->skip--;
|
|
return;
|
|
}
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
}
|
|
|
|
static void
|
|
save_stack_address_nosched(void *data, unsigned long addr, int reliable)
|
|
{
|
|
struct stack_trace *trace = (struct stack_trace *)data;
|
|
if (!reliable)
|
|
return;
|
|
if (in_sched_functions(addr))
|
|
return;
|
|
if (trace->skip > 0) {
|
|
trace->skip--;
|
|
return;
|
|
}
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
}
|
|
|
|
static const struct stacktrace_ops save_stack_ops = {
|
|
.warning = save_stack_warning,
|
|
.warning_symbol = save_stack_warning_symbol,
|
|
.stack = save_stack_stack,
|
|
.address = save_stack_address,
|
|
.walk_stack = print_context_stack,
|
|
};
|
|
|
|
static const struct stacktrace_ops save_stack_ops_nosched = {
|
|
.warning = save_stack_warning,
|
|
.warning_symbol = save_stack_warning_symbol,
|
|
.stack = save_stack_stack,
|
|
.address = save_stack_address_nosched,
|
|
.walk_stack = print_context_stack,
|
|
};
|
|
|
|
/*
|
|
* Save stack-backtrace addresses into a stack_trace buffer.
|
|
*/
|
|
void save_stack_trace(struct stack_trace *trace)
|
|
{
|
|
dump_trace(current, NULL, NULL, 0, &save_stack_ops, trace);
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
|
|
|
void save_stack_trace_bp(struct stack_trace *trace, unsigned long bp)
|
|
{
|
|
dump_trace(current, NULL, NULL, bp, &save_stack_ops, trace);
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
|
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
|
{
|
|
dump_trace(tsk, NULL, NULL, 0, &save_stack_ops_nosched, trace);
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|
|
|
|
/* Userspace stacktrace - based on kernel/trace/trace_sysprof.c */
|
|
|
|
struct stack_frame_user {
|
|
const void __user *next_fp;
|
|
unsigned long ret_addr;
|
|
};
|
|
|
|
static int
|
|
copy_stack_frame(const void __user *fp, struct stack_frame_user *frame)
|
|
{
|
|
int ret;
|
|
|
|
if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
|
|
return 0;
|
|
|
|
ret = 1;
|
|
pagefault_disable();
|
|
if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
|
|
ret = 0;
|
|
pagefault_enable();
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline void __save_stack_trace_user(struct stack_trace *trace)
|
|
{
|
|
const struct pt_regs *regs = task_pt_regs(current);
|
|
const void __user *fp = (const void __user *)regs->bp;
|
|
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = regs->ip;
|
|
|
|
while (trace->nr_entries < trace->max_entries) {
|
|
struct stack_frame_user frame;
|
|
|
|
frame.next_fp = NULL;
|
|
frame.ret_addr = 0;
|
|
if (!copy_stack_frame(fp, &frame))
|
|
break;
|
|
if ((unsigned long)fp < regs->sp)
|
|
break;
|
|
if (frame.ret_addr) {
|
|
trace->entries[trace->nr_entries++] =
|
|
frame.ret_addr;
|
|
}
|
|
if (fp == frame.next_fp)
|
|
break;
|
|
fp = frame.next_fp;
|
|
}
|
|
}
|
|
|
|
void save_stack_trace_user(struct stack_trace *trace)
|
|
{
|
|
/*
|
|
* Trace user stack if we are not a kernel thread
|
|
*/
|
|
if (current->mm) {
|
|
__save_stack_trace_user(trace);
|
|
}
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
|