mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
openrisc: support framepointers and STACKTRACE_SUPPORT
For lockdep support a reliable stack trace mechanism is needed. This patch adds support in OpenRISC for the stacktrace framework, implemented by a simple unwinder api. The unwinder api supports both framepointer and basic stack tracing. The unwinder is now used to replace the stack_dump() implementation as well. The new traces are inline with other architectures trace format: Call trace: [<c0004448>] show_stack+0x3c/0x58 [<c031c940>] dump_stack+0xa8/0xe4 [<c0008104>] __cpu_up+0x64/0x130 [<c000d268>] bringup_cpu+0x3c/0x178 [<c000d038>] cpuhp_invoke_callback+0xa8/0x1fc [<c000d680>] cpuhp_up_callbacks+0x44/0x14c [<c000e400>] cpu_up+0x14c/0x1bc [<c041da60>] smp_init+0x104/0x15c [<c033843c>] ? kernel_init+0x0/0x140 [<c0415e04>] kernel_init_freeable+0xbc/0x25c [<c033843c>] ? kernel_init+0x0/0x140 [<c0338458>] kernel_init+0x1c/0x140 [<c003a174>] ? schedule_tail+0x18/0xa0 [<c0006b80>] ret_from_fork+0x1c/0x9c Signed-off-by: Stafford Horne <shorne@gmail.com>
This commit is contained in:
parent
306e5e50a3
commit
eecac38b04
@ -33,6 +33,7 @@ config OPENRISC
|
|||||||
select ARCH_USE_QUEUED_SPINLOCKS
|
select ARCH_USE_QUEUED_SPINLOCKS
|
||||||
select ARCH_USE_QUEUED_RWLOCKS
|
select ARCH_USE_QUEUED_RWLOCKS
|
||||||
select OMPIC if SMP
|
select OMPIC if SMP
|
||||||
|
select ARCH_WANT_FRAME_POINTERS
|
||||||
|
|
||||||
config CPU_BIG_ENDIAN
|
config CPU_BIG_ENDIAN
|
||||||
def_bool y
|
def_bool y
|
||||||
@ -60,6 +61,9 @@ config TRACE_IRQFLAGS_SUPPORT
|
|||||||
config GENERIC_CSUM
|
config GENERIC_CSUM
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
||||||
|
config STACKTRACE_SUPPORT
|
||||||
|
def_bool y
|
||||||
|
|
||||||
source "init/Kconfig"
|
source "init/Kconfig"
|
||||||
|
|
||||||
source "kernel/Kconfig.freezer"
|
source "kernel/Kconfig.freezer"
|
||||||
|
20
arch/openrisc/include/asm/unwinder.h
Normal file
20
arch/openrisc/include/asm/unwinder.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* OpenRISC unwinder.h
|
||||||
|
*
|
||||||
|
* Architecture API for unwinding stacks.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public License
|
||||||
|
* version 2. This program is licensed "as is" without any warranty of any
|
||||||
|
* kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ASM_OPENRISC_UNWINDER_H
|
||||||
|
#define __ASM_OPENRISC_UNWINDER_H
|
||||||
|
|
||||||
|
void unwind_stack(void *data, unsigned long *stack,
|
||||||
|
void (*trace)(void *data, unsigned long addr,
|
||||||
|
int reliable));
|
||||||
|
|
||||||
|
#endif /* __ASM_OPENRISC_UNWINDER_H */
|
@ -6,9 +6,10 @@ extra-y := head.o vmlinux.lds
|
|||||||
|
|
||||||
obj-y := setup.o or32_ksyms.o process.o dma.o \
|
obj-y := setup.o or32_ksyms.o process.o dma.o \
|
||||||
traps.o time.o irq.o entry.o ptrace.o signal.o \
|
traps.o time.o irq.o entry.o ptrace.o signal.o \
|
||||||
sys_call_table.o
|
sys_call_table.o unwinder.o
|
||||||
|
|
||||||
obj-$(CONFIG_SMP) += smp.o
|
obj-$(CONFIG_SMP) += smp.o
|
||||||
|
obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||||
obj-$(CONFIG_MODULES) += module.o
|
obj-$(CONFIG_MODULES) += module.o
|
||||||
obj-$(CONFIG_OF) += prom.o
|
obj-$(CONFIG_OF) += prom.o
|
||||||
|
|
||||||
|
86
arch/openrisc/kernel/stacktrace.c
Normal file
86
arch/openrisc/kernel/stacktrace.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Stack trace utility for OpenRISC
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public License
|
||||||
|
* version 2. This program is licensed "as is" without any warranty of any
|
||||||
|
* kind, whether express or implied.
|
||||||
|
*
|
||||||
|
* Losely based on work from sh and powerpc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/sched/debug.h>
|
||||||
|
#include <linux/stacktrace.h>
|
||||||
|
|
||||||
|
#include <asm/processor.h>
|
||||||
|
#include <asm/unwinder.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save stack-backtrace addresses into a stack_trace buffer.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_stack_trace(struct stack_trace *trace)
|
||||||
|
{
|
||||||
|
unwind_stack(trace, (unsigned long *) &trace, save_stack_address);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||||
|
{
|
||||||
|
unsigned long *sp = NULL;
|
||||||
|
|
||||||
|
if (tsk == current)
|
||||||
|
sp = (unsigned long *) &sp;
|
||||||
|
else
|
||||||
|
sp = (unsigned long *) KSTK_ESP(tsk);
|
||||||
|
|
||||||
|
unwind_stack(trace, sp, save_stack_address_nosched);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|
||||||
|
|
||||||
|
void
|
||||||
|
save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
|
||||||
|
{
|
||||||
|
unwind_stack(trace, (unsigned long *) regs->sp,
|
||||||
|
save_stack_address_nosched);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(save_stack_trace_regs);
|
@ -38,6 +38,7 @@
|
|||||||
#include <asm/segment.h>
|
#include <asm/segment.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
|
#include <asm/unwinder.h>
|
||||||
|
|
||||||
extern char _etext, _stext;
|
extern char _etext, _stext;
|
||||||
|
|
||||||
@ -45,61 +46,20 @@ int kstack_depth_to_print = 0x180;
|
|||||||
int lwa_flag;
|
int lwa_flag;
|
||||||
unsigned long __user *lwa_addr;
|
unsigned long __user *lwa_addr;
|
||||||
|
|
||||||
static inline int valid_stack_ptr(struct thread_info *tinfo, void *p)
|
void print_trace(void *data, unsigned long addr, int reliable)
|
||||||
{
|
{
|
||||||
return p > (void *)tinfo && p < (void *)tinfo + THREAD_SIZE - 3;
|
pr_emerg("[<%p>] %s%pS\n", (void *) addr, reliable ? "" : "? ",
|
||||||
}
|
(void *) addr);
|
||||||
|
|
||||||
void show_trace(struct task_struct *task, unsigned long *stack)
|
|
||||||
{
|
|
||||||
struct thread_info *context;
|
|
||||||
unsigned long addr;
|
|
||||||
|
|
||||||
context = (struct thread_info *)
|
|
||||||
((unsigned long)stack & (~(THREAD_SIZE - 1)));
|
|
||||||
|
|
||||||
while (valid_stack_ptr(context, stack)) {
|
|
||||||
addr = *stack++;
|
|
||||||
if (__kernel_text_address(addr)) {
|
|
||||||
printk(" [<%08lx>]", addr);
|
|
||||||
print_symbol(" %s", addr);
|
|
||||||
printk("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printk(" =======================\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* displays a short stack trace */
|
/* displays a short stack trace */
|
||||||
void show_stack(struct task_struct *task, unsigned long *esp)
|
void show_stack(struct task_struct *task, unsigned long *esp)
|
||||||
{
|
{
|
||||||
unsigned long addr, *stack;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (esp == NULL)
|
if (esp == NULL)
|
||||||
esp = (unsigned long *)&esp;
|
esp = (unsigned long *)&esp;
|
||||||
|
|
||||||
stack = esp;
|
pr_emerg("Call trace:\n");
|
||||||
|
unwind_stack(NULL, esp, print_trace);
|
||||||
printk("Stack dump [0x%08lx]:\n", (unsigned long)esp);
|
|
||||||
for (i = 0; i < kstack_depth_to_print; i++) {
|
|
||||||
if (kstack_end(stack))
|
|
||||||
break;
|
|
||||||
if (__get_user(addr, stack)) {
|
|
||||||
/* This message matches "failing address" marked
|
|
||||||
s390 in ksymoops, so lines containing it will
|
|
||||||
not be filtered out by ksymoops. */
|
|
||||||
printk("Failing address 0x%lx\n", (unsigned long)stack);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
stack++;
|
|
||||||
|
|
||||||
printk("sp + %02d: 0x%08lx\n", i * 4, addr);
|
|
||||||
}
|
|
||||||
printk("\n");
|
|
||||||
|
|
||||||
show_trace(task, esp);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_trace_task(struct task_struct *tsk)
|
void show_trace_task(struct task_struct *tsk)
|
||||||
@ -115,7 +75,7 @@ void show_registers(struct pt_regs *regs)
|
|||||||
int in_kernel = 1;
|
int in_kernel = 1;
|
||||||
unsigned long esp;
|
unsigned long esp;
|
||||||
|
|
||||||
esp = (unsigned long)(®s->sp);
|
esp = (unsigned long)(regs->sp);
|
||||||
if (user_mode(regs))
|
if (user_mode(regs))
|
||||||
in_kernel = 0;
|
in_kernel = 0;
|
||||||
|
|
||||||
|
105
arch/openrisc/kernel/unwinder.c
Normal file
105
arch/openrisc/kernel/unwinder.c
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* OpenRISC unwinder.c
|
||||||
|
*
|
||||||
|
* Reusable arch specific api for unwinding stacks.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Stafford Horne <shorne@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is licensed under the terms of the GNU General Public License
|
||||||
|
* version 2. This program is licensed "as is" without any warranty of any
|
||||||
|
* kind, whether express or implied.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/sched/task_stack.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
#include <asm/unwinder.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_FRAME_POINTER
|
||||||
|
struct or1k_frameinfo {
|
||||||
|
unsigned long *fp;
|
||||||
|
unsigned long ra;
|
||||||
|
unsigned long top;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify a frameinfo structure. The return address should be a valid text
|
||||||
|
* address. The frame pointer may be null if its the last frame, otherwise
|
||||||
|
* the frame pointer should point to a location in the stack after the the
|
||||||
|
* top of the next frame up.
|
||||||
|
*/
|
||||||
|
static inline int or1k_frameinfo_valid(struct or1k_frameinfo *frameinfo)
|
||||||
|
{
|
||||||
|
return (frameinfo->fp == NULL ||
|
||||||
|
(!kstack_end(frameinfo->fp) &&
|
||||||
|
frameinfo->fp > &frameinfo->top)) &&
|
||||||
|
__kernel_text_address(frameinfo->ra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a stack trace doing scanning which is frame pointer aware. We can
|
||||||
|
* get reliable stack traces by matching the previously found frame
|
||||||
|
* pointer with the top of the stack address every time we find a valid
|
||||||
|
* or1k_frameinfo.
|
||||||
|
*
|
||||||
|
* Ideally the stack parameter will be passed as FP, but it can not be
|
||||||
|
* guaranteed. Therefore we scan each address looking for the first sign
|
||||||
|
* of a return address.
|
||||||
|
*
|
||||||
|
* The OpenRISC stack frame looks something like the following. The
|
||||||
|
* location SP is held in r1 and location FP is held in r2 when frame pointers
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* SP -> (top of stack)
|
||||||
|
* - (callee saved registers)
|
||||||
|
* - (local variables)
|
||||||
|
* FP-8 -> previous FP \
|
||||||
|
* FP-4 -> return address |- or1k_frameinfo
|
||||||
|
* FP -> (previous top of stack) /
|
||||||
|
*/
|
||||||
|
void unwind_stack(void *data, unsigned long *stack,
|
||||||
|
void (*trace)(void *data, unsigned long addr, int reliable))
|
||||||
|
{
|
||||||
|
unsigned long *next_fp = NULL;
|
||||||
|
struct or1k_frameinfo *frameinfo = NULL;
|
||||||
|
int reliable = 0;
|
||||||
|
|
||||||
|
while (!kstack_end(stack)) {
|
||||||
|
frameinfo = container_of(stack,
|
||||||
|
struct or1k_frameinfo,
|
||||||
|
top);
|
||||||
|
|
||||||
|
if (__kernel_text_address(frameinfo->ra)) {
|
||||||
|
if (or1k_frameinfo_valid(frameinfo) &&
|
||||||
|
(next_fp == NULL ||
|
||||||
|
next_fp == &frameinfo->top)) {
|
||||||
|
reliable = 1;
|
||||||
|
next_fp = frameinfo->fp;
|
||||||
|
} else
|
||||||
|
reliable = 0;
|
||||||
|
|
||||||
|
trace(data, frameinfo->ra, reliable);
|
||||||
|
}
|
||||||
|
stack++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* CONFIG_FRAME_POINTER */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a stack trace by doing a simple scan treating all text addresses
|
||||||
|
* as return addresses.
|
||||||
|
*/
|
||||||
|
void unwind_stack(void *data, unsigned long *stack,
|
||||||
|
void (*trace)(void *data, unsigned long addr, int reliable))
|
||||||
|
{
|
||||||
|
unsigned long addr;
|
||||||
|
|
||||||
|
while (!kstack_end(stack)) {
|
||||||
|
addr = *stack++;
|
||||||
|
if (__kernel_text_address(addr))
|
||||||
|
trace(data, addr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_FRAME_POINTER */
|
||||||
|
|
Loading…
Reference in New Issue
Block a user