mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-23 16:09:57 +07:00
e18bcccd1a
Convert show_trace_log_lvl() to use the new unwinder. dump_trace() has been deprecated. show_trace_log_lvl() is special compared to other users of the unwinder. It's the only place where both reliable *and* unreliable addresses are needed. With frame pointers enabled, most callers of the unwinder don't want to know about unreliable addresses. But in this case, when we're dumping the stack to the console because something presumably went wrong, the unreliable addresses are useful: - They show stale data on the stack which can provide useful clues. - If something goes wrong with the unwinder, or if frame pointers are corrupt or missing, all the stack addresses still get shown. So in order to show all addresses on the stack, and at the same time figure out which addresses are reliable, we have to do the scanning and the unwinding in parallel. The scanning is done with the help of get_stack_info() to traverse the stacks. The unwinding is done separately by the new unwinder. In theory we could simplify show_trace_log_lvl() by instead pushing some of this logic into the unwind code. But then we would need some kind of "fake" frame logic in the unwinder which would add a lot of complexity and wouldn't be worth it in order to support only one user. Another benefit of this approach is that once we have a DWARF unwinder, we should be able to just plug it in with minimal impact to this code. Another change here is that callers of show_trace_log_lvl() don't need to provide the 'bp' argument. The unwinder already finds the relevant frame pointer by unwinding until it reaches the first frame after the provided stack pointer. Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Andy Lutomirski <luto@kernel.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Brian Gerst <brgerst@gmail.com> Cc: Byungchul Park <byungchul.park@lge.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Kees Cook <keescook@chromium.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Nilay Vaish <nilayvaish@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/703b5998604c712a1f801874b43f35d6dac52ede.1474045023.git.jpoimboe@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
155 lines
3.7 KiB
C
155 lines
3.7 KiB
C
/*
|
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
|
* Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
|
|
*/
|
|
|
|
#ifndef _ASM_X86_STACKTRACE_H
|
|
#define _ASM_X86_STACKTRACE_H
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <linux/ptrace.h>
|
|
#include <asm/switch_to.h>
|
|
|
|
enum stack_type {
|
|
STACK_TYPE_UNKNOWN,
|
|
STACK_TYPE_TASK,
|
|
STACK_TYPE_IRQ,
|
|
STACK_TYPE_SOFTIRQ,
|
|
STACK_TYPE_EXCEPTION,
|
|
STACK_TYPE_EXCEPTION_LAST = STACK_TYPE_EXCEPTION + N_EXCEPTION_STACKS-1,
|
|
};
|
|
|
|
struct stack_info {
|
|
enum stack_type type;
|
|
unsigned long *begin, *end, *next_sp;
|
|
};
|
|
|
|
bool in_task_stack(unsigned long *stack, struct task_struct *task,
|
|
struct stack_info *info);
|
|
|
|
int get_stack_info(unsigned long *stack, struct task_struct *task,
|
|
struct stack_info *info, unsigned long *visit_mask);
|
|
|
|
void stack_type_str(enum stack_type type, const char **begin,
|
|
const char **end);
|
|
|
|
static inline bool on_stack(struct stack_info *info, void *addr, size_t len)
|
|
{
|
|
void *begin = info->begin;
|
|
void *end = info->end;
|
|
|
|
return (info->type != STACK_TYPE_UNKNOWN &&
|
|
addr >= begin && addr < end &&
|
|
addr + len > begin && addr + len <= end);
|
|
}
|
|
|
|
extern int kstack_depth_to_print;
|
|
|
|
struct thread_info;
|
|
struct stacktrace_ops;
|
|
|
|
typedef unsigned long (*walk_stack_t)(struct task_struct *task,
|
|
unsigned long *stack,
|
|
unsigned long bp,
|
|
const struct stacktrace_ops *ops,
|
|
void *data,
|
|
struct stack_info *info,
|
|
int *graph);
|
|
|
|
extern unsigned long
|
|
print_context_stack(struct task_struct *task,
|
|
unsigned long *stack, unsigned long bp,
|
|
const struct stacktrace_ops *ops, void *data,
|
|
struct stack_info *info, int *graph);
|
|
|
|
extern unsigned long
|
|
print_context_stack_bp(struct task_struct *task,
|
|
unsigned long *stack, unsigned long bp,
|
|
const struct stacktrace_ops *ops, void *data,
|
|
struct stack_info *info, int *graph);
|
|
|
|
/* Generic stack tracer with callbacks */
|
|
|
|
struct stacktrace_ops {
|
|
int (*address)(void *data, unsigned long address, int reliable);
|
|
/* On negative return stop dumping */
|
|
int (*stack)(void *data, const char *name);
|
|
walk_stack_t walk_stack;
|
|
};
|
|
|
|
void dump_trace(struct task_struct *tsk, struct pt_regs *regs,
|
|
unsigned long *stack, unsigned long bp,
|
|
const struct stacktrace_ops *ops, void *data);
|
|
|
|
#ifdef CONFIG_X86_32
|
|
#define STACKSLOTS_PER_LINE 8
|
|
#else
|
|
#define STACKSLOTS_PER_LINE 4
|
|
#endif
|
|
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
static inline unsigned long *
|
|
get_frame_pointer(struct task_struct *task, struct pt_regs *regs)
|
|
{
|
|
if (regs)
|
|
return (unsigned long *)regs->bp;
|
|
|
|
if (task == current)
|
|
return __builtin_frame_address(0);
|
|
|
|
return (unsigned long *)((struct inactive_task_frame *)task->thread.sp)->bp;
|
|
}
|
|
#else
|
|
static inline unsigned long *
|
|
get_frame_pointer(struct task_struct *task, struct pt_regs *regs)
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif /* CONFIG_FRAME_POINTER */
|
|
|
|
static inline unsigned long *
|
|
get_stack_pointer(struct task_struct *task, struct pt_regs *regs)
|
|
{
|
|
if (regs)
|
|
return (unsigned long *)kernel_stack_pointer(regs);
|
|
|
|
if (task == current)
|
|
return __builtin_frame_address(0);
|
|
|
|
return (unsigned long *)task->thread.sp;
|
|
}
|
|
|
|
void show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
|
|
unsigned long *stack, char *log_lvl);
|
|
|
|
void show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
|
|
unsigned long *sp, char *log_lvl);
|
|
|
|
extern unsigned int code_bytes;
|
|
|
|
/* The form of the top of the frame on the stack */
|
|
struct stack_frame {
|
|
struct stack_frame *next_frame;
|
|
unsigned long return_address;
|
|
};
|
|
|
|
struct stack_frame_ia32 {
|
|
u32 next_frame;
|
|
u32 return_address;
|
|
};
|
|
|
|
static inline unsigned long caller_frame_pointer(void)
|
|
{
|
|
struct stack_frame *frame;
|
|
|
|
frame = __builtin_frame_address(0);
|
|
|
|
#ifdef CONFIG_FRAME_POINTER
|
|
frame = frame->next_frame;
|
|
#endif
|
|
|
|
return (unsigned long)frame;
|
|
}
|
|
|
|
#endif /* _ASM_X86_STACKTRACE_H */
|