2010-04-14 04:55:45

by Steven J. Magnani

[permalink] [raw]
Subject: [PATCH 1/2] microblaze: add stack unwinder

Implement intelligent backtracing by searching for stack frame creation,
and emitting only return addresses. Use print_hex_dump() to display the
entire binary kernel stack.

Limitation: MMU kernels are not currently able to trace beyond a system trap
(interrupt, syscall, etc.). It is the intent of this patch to provide
infrastructure that can be extended to add this capability later.

Signed-off-by: Steven J. Magnani <[email protected]>
---
diff -uprN a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h
--- a/arch/microblaze/include/asm/exceptions.h 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/include/asm/exceptions.h 2010-04-12 22:21:01.000000000 -0500
@@ -14,6 +14,11 @@
#define _ASM_MICROBLAZE_EXCEPTIONS_H

#ifdef __KERNEL__
+
+#ifndef CONFIG_MMU
+#define EX_HANDLER_STACK_SIZ (4*19)
+#endif
+
#ifndef __ASSEMBLY__

/* Macros to enable and disable HW exceptions in the MSR */
diff -uprN a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h
--- a/arch/microblaze/include/asm/system.h 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/include/asm/system.h 2010-04-12 22:16:01.000000000 -0500
@@ -44,7 +44,6 @@ extern struct task_struct *_switch_to(st
#define smp_rmb() rmb()
#define smp_wmb() wmb()

-void show_trace(struct task_struct *task, unsigned long *stack);
void __bad_xchg(volatile void *ptr, int size);

static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
diff -uprN a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S
--- a/arch/microblaze/kernel/entry-nommu.S 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/entry-nommu.S 2010-04-12 22:16:01.000000000 -0500
@@ -586,3 +586,31 @@ sys_rt_sigsuspend_wrapper:
#include "syscall_table.S"

syscall_table_size=(.-sys_call_table)
+
+type_SYSCALL:
+ .ascii "SYSCALL\0"
+type_IRQ:
+ .ascii "IRQ\0"
+type_IRQ_PREEMPT:
+ .ascii "IRQ (PREEMPTED)\0"
+type_SYSCALL_PREEMPT:
+ .ascii " SYSCALL (PREEMPTED)\0"
+
+ /*
+ * Trap decoding for stack unwinder
+ * Tuples are (start addr, end addr, string)
+ * If return address lies on [start addr, end addr],
+ * unwinder displays 'string'
+ */
+
+ .align 4
+.global microblaze_trap_handlers
+microblaze_trap_handlers:
+ /* Exact matches come first */
+ .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL
+ .word ret_from_intr; .word ret_from_intr ; .word type_IRQ
+ /* Fuzzy matches go here */
+ .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT
+ .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT
+ /* End of table */
+ .word 0 ; .word 0 ; .word 0
diff -uprN a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
--- a/arch/microblaze/kernel/entry.S 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/entry.S 2010-04-12 22:16:01.000000000 -0500
@@ -1127,3 +1127,30 @@ ENTRY(_break)

syscall_table_size=(.-sys_call_table)

+type_SYSCALL:
+ .ascii "SYSCALL\0"
+type_IRQ:
+ .ascii "IRQ\0"
+type_IRQ_PREEMPT:
+ .ascii "IRQ (PREEMPTED)\0"
+type_SYSCALL_PREEMPT:
+ .ascii " SYSCALL (PREEMPTED)\0"
+
+ /*
+ * Trap decoding for stack unwinder
+ * Tuples are (start addr, end addr, string)
+ * If return address lies on [start addr, end addr],
+ * unwinder displays 'string'
+ */
+
+ .align 4
+.global microblaze_trap_handlers
+microblaze_trap_handlers:
+ /* Exact matches come first */
+ .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL
+ .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ
+ /* Fuzzy matches go here */
+ .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT
+ .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT
+ /* End of table */
+ .word 0 ; .word 0 ; .word 0
diff -uprN a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S
--- a/arch/microblaze/kernel/hw_exception_handler.S 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/hw_exception_handler.S 2010-04-12 22:21:15.000000000 -0500
@@ -78,9 +78,6 @@
#include <asm/asm-offsets.h>

/* Helpful Macros */
-#ifndef CONFIG_MMU
-#define EX_HANDLER_STACK_SIZ (4*19)
-#endif
#define NUM_TO_REG(num) r ## num

#ifdef CONFIG_MMU
@@ -988,6 +985,7 @@ ex_unaligned_fixup:
.end _unaligned_data_exception
#endif /* CONFIG_MMU */

+.global ex_handler_unhandled
ex_handler_unhandled:
/* FIXME add handle function for unhandled exception - dump register */
bri 0
diff -uprN a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
--- a/arch/microblaze/kernel/Makefile 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/Makefile 2010-04-12 22:16:01.000000000 -0500
@@ -17,7 +17,8 @@ extra-y := head.o vmlinux.lds
obj-y += dma.o exceptions.o \
hw_exception_handler.o init_task.o intc.o irq.o of_device.o \
of_platform.o process.o prom.o prom_parse.o ptrace.o \
- setup.o signal.o sys_microblaze.o timer.o traps.o reset.o
+ setup.o signal.o sys_microblaze.o timer.o traps.o reset.o \
+ unwind.o

obj-y += cpu/

diff -uprN a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c
--- a/arch/microblaze/kernel/stacktrace.c 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/stacktrace.c 2010-04-12 22:16:01.000000000 -0500
@@ -14,52 +14,18 @@
#include <linux/thread_info.h>
#include <linux/ptrace.h>
#include <linux/module.h>
+#include "unwind.h"

-/* FIXME initial support */
void save_stack_trace(struct stack_trace *trace)
{
- unsigned long *sp;
- unsigned long addr;
- asm("addik %0, r1, 0" : "=r" (sp));
-
- while (!kstack_end(sp)) {
- addr = *sp++;
- if (__kernel_text_address(addr)) {
- if (trace->skip > 0)
- trace->skip--;
- else
- trace->entries[trace->nr_entries++] = addr;
-
- if (trace->nr_entries >= trace->max_entries)
- break;
- }
- }
+ /* Exclude our helper functions from the trace*/
+ trace->skip += 2;
+ microblaze_unwind(NULL, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace);

void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
{
- unsigned int *sp;
- unsigned long addr;
-
- struct thread_info *ti = task_thread_info(tsk);
-
- if (tsk == current)
- asm("addik %0, r1, 0" : "=r" (sp));
- else
- sp = (unsigned int *)ti->cpu_context.r1;
-
- while (!kstack_end(sp)) {
- addr = *sp++;
- if (__kernel_text_address(addr)) {
- if (trace->skip > 0)
- trace->skip--;
- else
- trace->entries[trace->nr_entries++] = addr;
-
- if (trace->nr_entries >= trace->max_entries)
- break;
- }
- }
+ microblaze_unwind(tsk, trace);
}
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff -uprN a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c
--- a/arch/microblaze/kernel/traps.c 2010-04-09 21:52:36.000000000 -0500
+++ b/arch/microblaze/kernel/traps.c 2010-04-12 22:16:01.000000000 -0500
@@ -13,16 +13,19 @@
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/debug_locks.h>
+#include <linux/seq_file.h>

#include <asm/exceptions.h>
#include <asm/system.h>

+#include "unwind.h"
+
void trap_init(void)
{
__enable_hw_exceptions();
}

-static unsigned long kstack_depth_to_print = 24;
+static unsigned long kstack_depth_to_print; /* 0 == entire stack */

static int __init kstack_setup(char *s)
{
@@ -30,31 +33,42 @@ static int __init kstack_setup(char *s)
}
__setup("kstack=", kstack_setup);

-void show_trace(struct task_struct *task, unsigned long *stack)
+void show_stack(struct task_struct *task, unsigned long *sp)
{
- unsigned long addr;
+ unsigned long words_to_show;
+ __u32 fp = (__u32) sp;

- if (!stack)
- stack = (unsigned long *)&stack;
+ if (fp == 0) {
+ if (task)
+ fp = ((struct thread_info *)
+ (task->stack))->cpu_context.r1;
+ else {
+ /* Pick up caller of dump_stack() */
+ fp = (__u32)&sp - 8;
+ }
+ }

- printk(KERN_NOTICE "Call Trace: ");
-#ifdef CONFIG_KALLSYMS
- printk(KERN_NOTICE "\n");
-#endif
- while (!kstack_end(stack)) {
- addr = *stack++;
- /*
- * If the address is either in the text segment of the
- * kernel, or in the region which contains vmalloc'ed
- * memory, it *may* be the address of a calling
- * routine; if so, print it so that someone tracing
- * down the cause of the crash will be able to figure
- * out the call path that was taken.
- */
- if (kernel_text_address(addr))
- print_ip_sym(addr);
+ words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE-1))) >> 2;
+ if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print))
+ words_to_show = kstack_depth_to_print;
+
+ pr_info("Stack:\n");
+ if (fp & 0xF) {
+ unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2;
+ if (line1_words < words_to_show) {
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32,
+ 4, (void *)fp, line1_words << 2, 0);
+ fp += line1_words << 2;
+ words_to_show -= line1_words;
+ }
}
- printk(KERN_NOTICE "\n");
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp,
+ words_to_show << 2, 0);
+ printk(KERN_INFO "\n\n");
+
+ pr_info("Call Trace:\n");
+ microblaze_unwind(task, NULL);
+ pr_info("\n");

if (!task)
task = current;
@@ -62,34 +76,6 @@ void show_trace(struct task_struct *task
debug_show_held_locks(task);
}

-void show_stack(struct task_struct *task, unsigned long *sp)
-{
- unsigned long *stack;
- int i;
-
- if (sp == NULL) {
- if (task)
- sp = (unsigned long *) ((struct thread_info *)
- (task->stack))->cpu_context.r1;
- else
- sp = (unsigned long *)&sp;
- }
-
- stack = sp;
-
- printk(KERN_INFO "\nStack:\n ");
-
- for (i = 0; i < kstack_depth_to_print; i++) {
- if (kstack_end(sp))
- break;
- if (i && ((i % 8) == 0))
- printk("\n ");
- printk("%08lx ", *sp++);
- }
- printk("\n");
- show_trace(task, stack);
-}
-
void dump_stack(void)
{
show_stack(NULL, NULL);
@@ -129,3 +115,4 @@ int debug_trap(struct pt_regs *regs)
return -ENOSYS;
}
#endif
+
diff -uprN a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c
--- a/arch/microblaze/kernel/unwind.c 1969-12-31 18:00:00.000000000 -0600
+++ b/arch/microblaze/kernel/unwind.c 2010-04-12 22:16:01.000000000 -0500
@@ -0,0 +1,316 @@
+/*
+ * Backtrace support for Microblaze
+ *
+ * Copyright (C) 2010 Digital Design Corporation
+ *
+ * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is:
+ * Copyright (C) 2004 Paul Mundt
+ * Copyright (C) 2004 Richard Curnow
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+/* #define DEBUG 1 */
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <asm/sections.h>
+#include <asm/exceptions.h>
+#include "unwind.h"
+
+struct stack_trace;
+
+/*
+ * On Microblaze, finding the previous stack frame is a little tricky.
+ * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS,
+ * and even if it did, gcc (4.1.2) does not store the frame pointer at
+ * a consistent offset within each frame. To determine frame size, it is
+ * necessary to search for the assembly instruction that creates the frame
+ * and extract the size from it.
+ *
+ * Microblaze stores the stack pointer in r1, and creates a frame via
+ *
+ * addik r1, r1, -FRAME_SIZE
+ *
+ * A stack frame is usually not created in a leaf function.
+ *
+ */
+
+/**
+ * get_frame_size - Extract the stack adjustment from an
+ * "addik r1, r1, -adjust" instruction
+ * @instr : Microblaze instruction
+ *
+ * Return - Number of bytes the instruction reserves for a stack frame
+ */
+inline s16 get_frame_size(unsigned long instr)
+{
+ return -(s16)(instr & 0xFFFF);
+}
+
+/**
+ * find_frame_creation - Search backward to find the instruction that creates
+ * the stack frame (hopefully, for the same function the
+ * initial PC is in).
+ * @pc : Program counter at which to begin the search
+ *
+ * Return - PC at which stack frame creation occurs
+ * NULL if this cannot be found, i.e. a leaf function
+ */
+static unsigned long *find_frame_creation(unsigned long *pc)
+{
+ int i;
+
+ /* NOTE: Distance to search is arbitrary
+ * 250 works well for most things,
+ * 750 picks up things like tcp_recvmsg(),
+ * 1000 needed for fat_fill_super()
+ */
+ for (i = 0; i < 1000; i++, pc--) {
+ unsigned long instr;
+ s16 frame_size;
+
+ if (!kernel_text_address((unsigned long) pc))
+ return NULL;
+
+ instr = *pc;
+
+ /*
+ * rtsd of some sort,
+ * i.e., a return from a previous function?
+ */
+ if ((instr & 0xFFE00000) == 0xb6000000)
+ return NULL;
+
+ if ((instr & 0xFFFF0000) != 0x30210000) /* addik r1, r1 */
+ continue;
+
+ frame_size = get_frame_size(instr);
+ if ((frame_size < 8) || (frame_size & 3)) {
+ pr_debug(" Invalid frame size %d at 0x%p\n",
+ frame_size, pc);
+ return NULL;
+ }
+
+ pr_debug(" Found frame creation at 0x%p, size %d\n", pc,
+ frame_size);
+ return pc;
+ }
+
+ return NULL;
+}
+
+/**
+ * lookup_prev_stack_frame - Find the stack frame of the previous function.
+ * @fp : Frame (stack) pointer for current function
+ * @pc : Program counter within current function
+ * @leaf_return : r15 value within current function. If the current function
+ * is a leaf, this is the caller's return address.
+ * @pprev_fp : On exit, set to frame (stack) pointer for previous function
+ * @pprev_pc : On exit, set to current function caller's return address
+ *
+ * Return - 0 on success, -EINVAL if the previous frame cannot be found
+ */
+static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc,
+ unsigned long leaf_return,
+ unsigned long *pprev_fp,
+ unsigned long *pprev_pc)
+{
+ unsigned long *prologue = NULL;
+
+ /* _switch_to is a special leaf function */
+ if (pc != (unsigned long) &_switch_to)
+ prologue = find_frame_creation((unsigned long *)pc);
+
+ if (prologue) {
+ s16 frame_size = get_frame_size(*prologue);
+
+ *pprev_fp = fp + frame_size;
+ *pprev_pc = *(unsigned long *)fp;
+ } else {
+ if (!leaf_return)
+ return -EINVAL;
+ *pprev_pc = leaf_return;
+ *pprev_fp = fp;
+ }
+
+ /* NOTE: don't check kernel_text_address here, to allow display
+ * of userland return address
+ */
+ return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0;
+}
+
+static void microblaze_unwind_inner(struct task_struct *task,
+ unsigned long pc, unsigned long fp,
+ unsigned long leaf_return,
+ struct stack_trace *trace);
+
+/**
+ * unwind_trap - Unwind through a system trap, that stored previous state
+ * on the stack.
+ */
+#ifdef CONFIG_MMU
+static inline void unwind_trap(struct task_struct *task, unsigned long pc,
+ unsigned long fp, struct stack_trace *trace)
+{
+ /* To be implemented */
+}
+#else
+static inline void unwind_trap(struct task_struct *task, unsigned long pc,
+ unsigned long fp, struct stack_trace *trace)
+{
+ const struct pt_regs *regs = (const struct pt_regs *) fp;
+ microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace);
+}
+#endif
+
+/**
+ * microblaze_unwind_inner - Unwind the stack from the specified point
+ * @task : Task whose stack we are to unwind (may be NULL)
+ * @pc : Program counter from which we start unwinding
+ * @fp : Frame (stack) pointer from which we start unwinding
+ * @leaf_return : Value of r15 at pc. If the function is a leaf, this is
+ * the caller's return address.
+ * @trace : Where to store stack backtrace (PC values).
+ * NULL == print backtrace to kernel log
+ */
+void microblaze_unwind_inner(struct task_struct *task,
+ unsigned long pc, unsigned long fp,
+ unsigned long leaf_return,
+ struct stack_trace *trace)
+{
+ int ofs = 0;
+
+ pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp);
+ if (!pc || !fp || (pc & 3) || (fp & 3)) {
+ pr_debug(" Invalid state for unwind, aborting\n");
+ return;
+ }
+ for (; pc != 0;) {
+ unsigned long next_fp, next_pc = 0;
+ unsigned long return_to = pc + 2 * sizeof(unsigned long);
+ const struct trap_handler_info *handler =
+ &microblaze_trap_handlers;
+
+ /* Is previous function the HW exception handler? */
+ if ((return_to >= (unsigned long)&_hw_exception_handler)
+ &&(return_to < (unsigned long)&ex_handler_unhandled)) {
+ /*
+ * HW exception handler doesn't save all registers,
+ * so we open-code a special case of unwind_trap()
+ */
+#ifndef CONFIG_MMU
+ const struct pt_regs *regs =
+ (const struct pt_regs *) fp;
+#endif
+ pr_info("HW EXCEPTION\n");
+#ifndef CONFIG_MMU
+ microblaze_unwind_inner(task, regs->r17 - 4,
+ fp + EX_HANDLER_STACK_SIZ,
+ regs->r15, trace);
+#endif
+ return;
+ }
+
+ /* Is previous function a trap handler? */
+ for (; handler->start_addr; ++handler) {
+ if ((return_to >= handler->start_addr)
+ && (return_to <= handler->end_addr)) {
+ if (!trace)
+ pr_info("%s\n", handler->trap_name);
+ unwind_trap(task, pc, fp, trace);
+ return;
+ }
+ }
+ pc -= ofs;
+
+ if (trace) {
+#ifdef CONFIG_STACKTRACE
+ if (trace->skip > 0)
+ trace->skip--;
+ else
+ trace->entries[trace->nr_entries++] = pc;
+
+ if (trace->nr_entries >= trace->max_entries)
+ break;
+#endif
+ } else {
+ /* Have we reached userland? */
+ if (unlikely(pc == task_pt_regs(task)->pc)) {
+ pr_info("[<%p>] PID %lu [%s]\n",
+ (void *) pc,
+ (unsigned long) task->pid,
+ task->comm);
+ break;
+ } else
+ print_ip_sym(pc);
+ }
+
+ /* Stop when we reach anything not part of the kernel */
+ if (!kernel_text_address(pc))
+ break;
+
+ if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp,
+ &next_pc) == 0) {
+ ofs = sizeof(unsigned long);
+ pc = next_pc & ~3;
+ fp = next_fp;
+ leaf_return = 0;
+ } else {
+ pr_debug(" Failed to find previous stack frame\n");
+ break;
+ }
+
+ pr_debug(" Next PC=%p, next FP=%p\n",
+ (void *)next_pc, (void *)next_fp);
+ }
+}
+
+/**
+ * microblaze_unwind - Stack unwinder for Microblaze (external entry point)
+ * @task : Task whose stack we are to unwind (NULL == current)
+ * @trace : Where to store stack backtrace (PC values).
+ * NULL == print backtrace to kernel log
+ */
+void microblaze_unwind(struct task_struct *task, struct stack_trace *trace)
+{
+ if (task) {
+ if (task == current) {
+ const struct pt_regs *regs = task_pt_regs(task);
+ microblaze_unwind_inner(task, regs->pc, regs->r1,
+ regs->r15, trace);
+ } else {
+ struct thread_info *thread_info =
+ (struct thread_info *)(task->stack);
+ const struct cpu_context *cpu_context =
+ &thread_info->cpu_context;
+
+ microblaze_unwind_inner(task,
+ (unsigned long) &_switch_to,
+ cpu_context->r1,
+ cpu_context->r15, trace);
+ }
+ } else {
+ unsigned long pc, fp;
+
+ __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp));
+
+ __asm__ __volatile__ (
+ "brlid %0, 0f;"
+ "nop;"
+ "0:"
+ : "=r" (pc)
+ );
+
+ /* Since we are not a leaf function, use leaf_return = 0 */
+ microblaze_unwind_inner(current, pc, fp, 0, trace);
+ }
+}
+
diff -uprN a/arch/microblaze/kernel/unwind.h b/arch/microblaze/kernel/unwind.h
--- a/arch/microblaze/kernel/unwind.h 1969-12-31 18:00:00.000000000 -0600
+++ b/arch/microblaze/kernel/unwind.h 2010-04-12 22:16:01.000000000 -0500
@@ -0,0 +1,29 @@
+/*
+ * Backtrace support for Microblaze
+ *
+ * Copyright (C) 2010 Digital Design Corporation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef __MICROBLAZE_UNWIND_H
+#define __MICROBLAZE_UNWIND_H
+
+struct stack_trace;
+
+struct trap_handler_info {
+ unsigned long start_addr;
+ unsigned long end_addr;
+ const char *trap_name;
+};
+extern struct trap_handler_info microblaze_trap_handlers;
+
+extern const char _hw_exception_handler;
+extern const char ex_handler_unhandled;
+
+void microblaze_unwind(struct task_struct *task, struct stack_trace *trace);
+
+#endif /* __MICROBLAZE_UNWIND_H */
+


2010-04-14 06:34:50

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> Implement intelligent backtracing by searching for stack frame creation,
> and emitting only return addresses. Use print_hex_dump() to display the
> entire binary kernel stack.
>
> Limitation: MMU kernels are not currently able to trace beyond a system trap
> (interrupt, syscall, etc.). It is the intent of this patch to provide
> infrastructure that can be extended to add this capability later.
>
> Signed-off-by: Steven J. Magnani <[email protected]>
> ---
> diff -uprN a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h
> --- a/arch/microblaze/include/asm/exceptions.h 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/include/asm/exceptions.h 2010-04-12 22:21:01.000000000 -0500
> @@ -14,6 +14,11 @@
> #define _ASM_MICROBLAZE_EXCEPTIONS_H
>
> #ifdef __KERNEL__
> +
> +#ifndef CONFIG_MMU
> +#define EX_HANDLER_STACK_SIZ (4*19)
> +#endif
> +
> #ifndef __ASSEMBLY__
>
> /* Macros to enable and disable HW exceptions in the MSR */
> diff -uprN a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h
> --- a/arch/microblaze/include/asm/system.h 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/include/asm/system.h 2010-04-12 22:16:01.000000000 -0500
> @@ -44,7 +44,6 @@ extern struct task_struct *_switch_to(st
> #define smp_rmb() rmb()
> #define smp_wmb() wmb()
>
> -void show_trace(struct task_struct *task, unsigned long *stack);
> void __bad_xchg(volatile void *ptr, int size);
>
> static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
> diff -uprN a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S
> --- a/arch/microblaze/kernel/entry-nommu.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/entry-nommu.S 2010-04-12 22:16:01.000000000 -0500
> @@ -586,3 +586,31 @@ sys_rt_sigsuspend_wrapper:
> #include "syscall_table.S"
>
> syscall_table_size=(.-sys_call_table)
> +
> +type_SYSCALL:
> + .ascii "SYSCALL\0"
> +type_IRQ:
> + .ascii "IRQ\0"
> +type_IRQ_PREEMPT:
> + .ascii "IRQ (PREEMPTED)\0"
> +type_SYSCALL_PREEMPT:
> + .ascii " SYSCALL (PREEMPTED)\0"
> +
> + /*
> + * Trap decoding for stack unwinder
> + * Tuples are (start addr, end addr, string)
> + * If return address lies on [start addr, end addr],
> + * unwinder displays 'string'
> + */
> +
> + .align 4
> +.global microblaze_trap_handlers
> +microblaze_trap_handlers:
> + /* Exact matches come first */
> + .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL
> + .word ret_from_intr; .word ret_from_intr ; .word type_IRQ
> + /* Fuzzy matches go here */
> + .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT
> + .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT
> + /* End of table */
> + .word 0 ; .word 0 ; .word 0
> diff -uprN a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
> --- a/arch/microblaze/kernel/entry.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/entry.S 2010-04-12 22:16:01.000000000 -0500
> @@ -1127,3 +1127,30 @@ ENTRY(_break)
>
> syscall_table_size=(.-sys_call_table)
>
> +type_SYSCALL:
> + .ascii "SYSCALL\0"
> +type_IRQ:
> + .ascii "IRQ\0"
> +type_IRQ_PREEMPT:
> + .ascii "IRQ (PREEMPTED)\0"
> +type_SYSCALL_PREEMPT:
> + .ascii " SYSCALL (PREEMPTED)\0"
> +
> + /*
> + * Trap decoding for stack unwinder
> + * Tuples are (start addr, end addr, string)
> + * If return address lies on [start addr, end addr],
> + * unwinder displays 'string'
> + */
> +
> + .align 4
> +.global microblaze_trap_handlers
> +microblaze_trap_handlers:
> + /* Exact matches come first */
> + .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL
> + .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ
> + /* Fuzzy matches go here */
> + .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT
> + .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT
> + /* End of table */
> + .word 0 ; .word 0 ; .word 0
> diff -uprN a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S
> --- a/arch/microblaze/kernel/hw_exception_handler.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/hw_exception_handler.S 2010-04-12 22:21:15.000000000 -0500
> @@ -78,9 +78,6 @@
> #include <asm/asm-offsets.h>
>
> /* Helpful Macros */
> -#ifndef CONFIG_MMU
> -#define EX_HANDLER_STACK_SIZ (4*19)
> -#endif
> #define NUM_TO_REG(num) r ## num
>
> #ifdef CONFIG_MMU
> @@ -988,6 +985,7 @@ ex_unaligned_fixup:
> .end _unaligned_data_exception
> #endif /* CONFIG_MMU */
>
> +.global ex_handler_unhandled
> ex_handler_unhandled:
> /* FIXME add handle function for unhandled exception - dump register */
> bri 0
> diff -uprN a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
> --- a/arch/microblaze/kernel/Makefile 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/Makefile 2010-04-12 22:16:01.000000000 -0500
> @@ -17,7 +17,8 @@ extra-y := head.o vmlinux.lds
> obj-y += dma.o exceptions.o \
> hw_exception_handler.o init_task.o intc.o irq.o of_device.o \
> of_platform.o process.o prom.o prom_parse.o ptrace.o \
> - setup.o signal.o sys_microblaze.o timer.o traps.o reset.o
> + setup.o signal.o sys_microblaze.o timer.o traps.o reset.o \
> + unwind.o
>
> obj-y += cpu/
>
> diff -uprN a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c
> --- a/arch/microblaze/kernel/stacktrace.c 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/stacktrace.c 2010-04-12 22:16:01.000000000 -0500
> @@ -14,52 +14,18 @@
> #include <linux/thread_info.h>
> #include <linux/ptrace.h>
> #include <linux/module.h>
> +#include "unwind.h"
>
> -/* FIXME initial support */
> void save_stack_trace(struct stack_trace *trace)
> {
> - unsigned long *sp;
> - unsigned long addr;
> - asm("addik %0, r1, 0" : "=r" (sp));
> -
> - while (!kstack_end(sp)) {
> - addr = *sp++;
> - if (__kernel_text_address(addr)) {
> - if (trace->skip > 0)
> - trace->skip--;
> - else
> - trace->entries[trace->nr_entries++] = addr;
> -
> - if (trace->nr_entries >= trace->max_entries)
> - break;
> - }
> - }
> + /* Exclude our helper functions from the trace*/
> + trace->skip += 2;
> + microblaze_unwind(NULL, trace);
> }
> EXPORT_SYMBOL_GPL(save_stack_trace);
>
> void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
> {
> - unsigned int *sp;
> - unsigned long addr;
> -
> - struct thread_info *ti = task_thread_info(tsk);
> -
> - if (tsk == current)
> - asm("addik %0, r1, 0" : "=r" (sp));
> - else
> - sp = (unsigned int *)ti->cpu_context.r1;
> -
> - while (!kstack_end(sp)) {
> - addr = *sp++;
> - if (__kernel_text_address(addr)) {
> - if (trace->skip > 0)
> - trace->skip--;
> - else
> - trace->entries[trace->nr_entries++] = addr;
> -
> - if (trace->nr_entries >= trace->max_entries)
> - break;
> - }
> - }
> + microblaze_unwind(tsk, trace);
> }
> EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
> diff -uprN a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c
> --- a/arch/microblaze/kernel/traps.c 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/traps.c 2010-04-12 22:16:01.000000000 -0500
> @@ -13,16 +13,19 @@
> #include <linux/module.h>
> #include <linux/sched.h>
> #include <linux/debug_locks.h>
> +#include <linux/seq_file.h>
>
> #include <asm/exceptions.h>
> #include <asm/system.h>
>
> +#include "unwind.h"
> +
> void trap_init(void)
> {
> __enable_hw_exceptions();
> }
>
> -static unsigned long kstack_depth_to_print = 24;
> +static unsigned long kstack_depth_to_print; /* 0 == entire stack */
>
> static int __init kstack_setup(char *s)
> {
> @@ -30,31 +33,42 @@ static int __init kstack_setup(char *s)
> }
> __setup("kstack=", kstack_setup);
>
> -void show_trace(struct task_struct *task, unsigned long *stack)
> +void show_stack(struct task_struct *task, unsigned long *sp)
> {
> - unsigned long addr;
> + unsigned long words_to_show;
> + __u32 fp = (__u32) sp;
>
> - if (!stack)
> - stack = (unsigned long *)&stack;
> + if (fp == 0) {
> + if (task)
> + fp = ((struct thread_info *)
> + (task->stack))->cpu_context.r1;
> + else {
> + /* Pick up caller of dump_stack() */
> + fp = (__u32)&sp - 8;
> + }
> + }
>
> - printk(KERN_NOTICE "Call Trace: ");
> -#ifdef CONFIG_KALLSYMS
> - printk(KERN_NOTICE "\n");
> -#endif
> - while (!kstack_end(stack)) {
> - addr = *stack++;
> - /*
> - * If the address is either in the text segment of the
> - * kernel, or in the region which contains vmalloc'ed
> - * memory, it *may* be the address of a calling
> - * routine; if so, print it so that someone tracing
> - * down the cause of the crash will be able to figure
> - * out the call path that was taken.
> - */
> - if (kernel_text_address(addr))
> - print_ip_sym(addr);
> + words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE-1))) >> 2;
> + if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print))
> + words_to_show = kstack_depth_to_print;
> +
> + pr_info("Stack:\n");
> + if (fp & 0xF) {
> + unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2;
> + if (line1_words < words_to_show) {
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32,
> + 4, (void *)fp, line1_words << 2, 0);
> + fp += line1_words << 2;
> + words_to_show -= line1_words;
> + }
> }
> - printk(KERN_NOTICE "\n");
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp,
> + words_to_show << 2, 0);
> + printk(KERN_INFO "\n\n");
> +
> + pr_info("Call Trace:\n");
> + microblaze_unwind(task, NULL);
> + pr_info("\n");
>
> if (!task)
> task = current;
> @@ -62,34 +76,6 @@ void show_trace(struct task_struct *task
> debug_show_held_locks(task);
> }
>
> -void show_stack(struct task_struct *task, unsigned long *sp)
> -{
> - unsigned long *stack;
> - int i;
> -
> - if (sp == NULL) {
> - if (task)
> - sp = (unsigned long *) ((struct thread_info *)
> - (task->stack))->cpu_context.r1;
> - else
> - sp = (unsigned long *)&sp;
> - }
> -
> - stack = sp;
> -
> - printk(KERN_INFO "\nStack:\n ");
> -
> - for (i = 0; i < kstack_depth_to_print; i++) {
> - if (kstack_end(sp))
> - break;
> - if (i && ((i % 8) == 0))
> - printk("\n ");
> - printk("%08lx ", *sp++);
> - }
> - printk("\n");
> - show_trace(task, stack);
> -}
> -
> void dump_stack(void)
> {
> show_stack(NULL, NULL);
> @@ -129,3 +115,4 @@ int debug_trap(struct pt_regs *regs)
> return -ENOSYS;
> }
> #endif
> +
> diff -uprN a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c
> --- a/arch/microblaze/kernel/unwind.c 1969-12-31 18:00:00.000000000 -0600
> +++ b/arch/microblaze/kernel/unwind.c 2010-04-12 22:16:01.000000000 -0500
> @@ -0,0 +1,316 @@
> +/*
> + * Backtrace support for Microblaze
> + *
> + * Copyright (C) 2010 Digital Design Corporation
> + *
> + * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is:
> + * Copyright (C) 2004 Paul Mundt
> + * Copyright (C) 2004 Richard Curnow
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + */
> +
> +/* #define DEBUG 1 */
> +#include <linux/kallsyms.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/stacktrace.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <asm/sections.h>
> +#include <asm/exceptions.h>
> +#include "unwind.h"
> +
> +struct stack_trace;
> +
> +/*
> + * On Microblaze, finding the previous stack frame is a little tricky.
> + * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS,
> + * and even if it did, gcc (4.1.2) does not store the frame pointer at
> + * a consistent offset within each frame. To determine frame size, it is
> + * necessary to search for the assembly instruction that creates the frame
> + * and extract the size from it.
> + *
> + * Microblaze stores the stack pointer in r1, and creates a frame via
> + *
> + * addik r1, r1, -FRAME_SIZE
> + *
> + * A stack frame is usually not created in a leaf function.
> + *
> + */
> +
> +/**
> + * get_frame_size - Extract the stack adjustment from an
> + * "addik r1, r1, -adjust" instruction
> + * @instr : Microblaze instruction
> + *
> + * Return - Number of bytes the instruction reserves for a stack frame
> + */
> +inline s16 get_frame_size(unsigned long instr)
> +{
> + return -(s16)(instr & 0xFFFF);
> +}
> +
> +/**
> + * find_frame_creation - Search backward to find the instruction that creates
> + * the stack frame (hopefully, for the same function the
> + * initial PC is in).
> + * @pc : Program counter at which to begin the search
> + *
> + * Return - PC at which stack frame creation occurs
> + * NULL if this cannot be found, i.e. a leaf function
> + */
> +static unsigned long *find_frame_creation(unsigned long *pc)
> +{
> + int i;
> +
> + /* NOTE: Distance to search is arbitrary
> + * 250 works well for most things,
> + * 750 picks up things like tcp_recvmsg(),
> + * 1000 needed for fat_fill_super()
> + */
> + for (i = 0; i < 1000; i++, pc--) {
> + unsigned long instr;
> + s16 frame_size;
> +
> + if (!kernel_text_address((unsigned long) pc))
> + return NULL;
> +
> + instr = *pc;
> +
> + /*
> + * rtsd of some sort,
> + * i.e., a return from a previous function?
> + */
> + if ((instr & 0xFFE00000) == 0xb6000000)
> + return NULL;
> +
> + if ((instr & 0xFFFF0000) != 0x30210000) /* addik r1, r1 */
> + continue;
> +
> + frame_size = get_frame_size(instr);
> + if ((frame_size < 8) || (frame_size & 3)) {
> + pr_debug(" Invalid frame size %d at 0x%p\n",
> + frame_size, pc);
> + return NULL;
> + }
> +
> + pr_debug(" Found frame creation at 0x%p, size %d\n", pc,
> + frame_size);
> + return pc;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * lookup_prev_stack_frame - Find the stack frame of the previous function.
> + * @fp : Frame (stack) pointer for current function
> + * @pc : Program counter within current function
> + * @leaf_return : r15 value within current function. If the current function
> + * is a leaf, this is the caller's return address.
> + * @pprev_fp : On exit, set to frame (stack) pointer for previous function
> + * @pprev_pc : On exit, set to current function caller's return address
> + *
> + * Return - 0 on success, -EINVAL if the previous frame cannot be found
> + */
> +static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc,
> + unsigned long leaf_return,
> + unsigned long *pprev_fp,
> + unsigned long *pprev_pc)
> +{
> + unsigned long *prologue = NULL;
> +
> + /* _switch_to is a special leaf function */
> + if (pc != (unsigned long) &_switch_to)
> + prologue = find_frame_creation((unsigned long *)pc);
> +
> + if (prologue) {
> + s16 frame_size = get_frame_size(*prologue);
> +
> + *pprev_fp = fp + frame_size;
> + *pprev_pc = *(unsigned long *)fp;
> + } else {
> + if (!leaf_return)
> + return -EINVAL;
> + *pprev_pc = leaf_return;
> + *pprev_fp = fp;
> + }
> +
> + /* NOTE: don't check kernel_text_address here, to allow display
> + * of userland return address
> + */
> + return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0;
> +}
> +
> +static void microblaze_unwind_inner(struct task_struct *task,
> + unsigned long pc, unsigned long fp,
> + unsigned long leaf_return,
> + struct stack_trace *trace);
> +
> +/**
> + * unwind_trap - Unwind through a system trap, that stored previous state
> + * on the stack.
> + */
> +#ifdef CONFIG_MMU
> +static inline void unwind_trap(struct task_struct *task, unsigned long pc,
> + unsigned long fp, struct stack_trace *trace)
> +{
> + /* To be implemented */
> +}
> +#else
> +static inline void unwind_trap(struct task_struct *task, unsigned long pc,
> + unsigned long fp, struct stack_trace *trace)
> +{
> + const struct pt_regs *regs = (const struct pt_regs *) fp;
> + microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace);
> +}
> +#endif
> +
> +/**
> + * microblaze_unwind_inner - Unwind the stack from the specified point
> + * @task : Task whose stack we are to unwind (may be NULL)
> + * @pc : Program counter from which we start unwinding
> + * @fp : Frame (stack) pointer from which we start unwinding
> + * @leaf_return : Value of r15 at pc. If the function is a leaf, this is
> + * the caller's return address.
> + * @trace : Where to store stack backtrace (PC values).
> + * NULL == print backtrace to kernel log
> + */
> +void microblaze_unwind_inner(struct task_struct *task,
> + unsigned long pc, unsigned long fp,
> + unsigned long leaf_return,
> + struct stack_trace *trace)
> +{
> + int ofs = 0;
> +
> + pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp);
> + if (!pc || !fp || (pc & 3) || (fp & 3)) {
> + pr_debug(" Invalid state for unwind, aborting\n");
> + return;
> + }
> + for (; pc != 0;) {
> + unsigned long next_fp, next_pc = 0;
> + unsigned long return_to = pc + 2 * sizeof(unsigned long);
> + const struct trap_handler_info *handler =
> + &microblaze_trap_handlers;
> +
> + /* Is previous function the HW exception handler? */
> + if ((return_to >= (unsigned long)&_hw_exception_handler)
> + &&(return_to < (unsigned long)&ex_handler_unhandled)) {
> + /*
> + * HW exception handler doesn't save all registers,
> + * so we open-code a special case of unwind_trap()
> + */
> +#ifndef CONFIG_MMU
> + const struct pt_regs *regs =
> + (const struct pt_regs *) fp;
> +#endif
> + pr_info("HW EXCEPTION\n");
> +#ifndef CONFIG_MMU
> + microblaze_unwind_inner(task, regs->r17 - 4,
> + fp + EX_HANDLER_STACK_SIZ,
> + regs->r15, trace);
> +#endif
> + return;
> + }
> +
> + /* Is previous function a trap handler? */
> + for (; handler->start_addr; ++handler) {
> + if ((return_to >= handler->start_addr)
> + && (return_to <= handler->end_addr)) {
> + if (!trace)
> + pr_info("%s\n", handler->trap_name);
> + unwind_trap(task, pc, fp, trace);
> + return;
> + }
> + }
> + pc -= ofs;
> +
> + if (trace) {
> +#ifdef CONFIG_STACKTRACE
> + if (trace->skip > 0)
> + trace->skip--;
> + else
> + trace->entries[trace->nr_entries++] = pc;
> +
> + if (trace->nr_entries >= trace->max_entries)
> + break;
> +#endif
> + } else {
> + /* Have we reached userland? */
> + if (unlikely(pc == task_pt_regs(task)->pc)) {
> + pr_info("[<%p>] PID %lu [%s]\n",
> + (void *) pc,
> + (unsigned long) task->pid,
> + task->comm);
> + break;
> + } else
> + print_ip_sym(pc);
> + }
> +
> + /* Stop when we reach anything not part of the kernel */
> + if (!kernel_text_address(pc))
> + break;
> +
> + if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp,
> + &next_pc) == 0) {
> + ofs = sizeof(unsigned long);
> + pc = next_pc & ~3;
> + fp = next_fp;
> + leaf_return = 0;
> + } else {
> + pr_debug(" Failed to find previous stack frame\n");
> + break;
> + }
> +
> + pr_debug(" Next PC=%p, next FP=%p\n",
> + (void *)next_pc, (void *)next_fp);
> + }
> +}
> +
> +/**
> + * microblaze_unwind - Stack unwinder for Microblaze (external entry point)
> + * @task : Task whose stack we are to unwind (NULL == current)
> + * @trace : Where to store stack backtrace (PC values).
> + * NULL == print backtrace to kernel log
> + */
> +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace)
> +{
> + if (task) {
> + if (task == current) {
> + const struct pt_regs *regs = task_pt_regs(task);
> + microblaze_unwind_inner(task, regs->pc, regs->r1,
> + regs->r15, trace);
> + } else {
> + struct thread_info *thread_info =
> + (struct thread_info *)(task->stack);
> + const struct cpu_context *cpu_context =
> + &thread_info->cpu_context;
> +
> + microblaze_unwind_inner(task,
> + (unsigned long) &_switch_to,
> + cpu_context->r1,
> + cpu_context->r15, trace);
> + }
> + } else {
> + unsigned long pc, fp;
> +
> + __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp));
> +
> + __asm__ __volatile__ (
> + "brlid %0, 0f;"
> + "nop;"
> + "0:"
> + : "=r" (pc)
> + );
> +
> + /* Since we are not a leaf function, use leaf_return = 0 */
> + microblaze_unwind_inner(current, pc, fp, 0, trace);
> + }
> +}
> +
> diff -uprN a/arch/microblaze/kernel/unwind.h b/arch/microblaze/kernel/unwind.h
> --- a/arch/microblaze/kernel/unwind.h 1969-12-31 18:00:00.000000000 -0600
> +++ b/arch/microblaze/kernel/unwind.h 2010-04-12 22:16:01.000000000 -0500

This should go to arch/microblaze/include/asm.
I will test it later today.

Thanks,
Michal



> @@ -0,0 +1,29 @@
> +/*
> + * Backtrace support for Microblaze
> + *
> + * Copyright (C) 2010 Digital Design Corporation
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + */
> +
> +#ifndef __MICROBLAZE_UNWIND_H
> +#define __MICROBLAZE_UNWIND_H
> +
> +struct stack_trace;
> +
> +struct trap_handler_info {
> + unsigned long start_addr;
> + unsigned long end_addr;
> + const char *trap_name;
> +};
> +extern struct trap_handler_info microblaze_trap_handlers;
> +
> +extern const char _hw_exception_handler;
> +extern const char ex_handler_unhandled;
> +
> +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace);
> +
> +#endif /* __MICROBLAZE_UNWIND_H */
> +
>
>


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-14 16:45:41

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> Implement intelligent backtracing by searching for stack frame creation,
> and emitting only return addresses. Use print_hex_dump() to display the
> entire binary kernel stack.
>
> Limitation: MMU kernels are not currently able to trace beyond a system trap
> (interrupt, syscall, etc.). It is the intent of this patch to provide
> infrastructure that can be extended to add this capability later.
>
> Signed-off-by: Steven J. Magnani <[email protected]>
> ---
> diff -uprN a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h
> --- a/arch/microblaze/include/asm/exceptions.h 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/include/asm/exceptions.h 2010-04-12 22:21:01.000000000 -0500
> @@ -14,6 +14,11 @@
> #define _ASM_MICROBLAZE_EXCEPTIONS_H
>
> #ifdef __KERNEL__
> +
> +#ifndef CONFIG_MMU
> +#define EX_HANDLER_STACK_SIZ (4*19)
> +#endif
> +
> #ifndef __ASSEMBLY__
>
> /* Macros to enable and disable HW exceptions in the MSR */
> diff -uprN a/arch/microblaze/include/asm/system.h b/arch/microblaze/include/asm/system.h
> --- a/arch/microblaze/include/asm/system.h 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/include/asm/system.h 2010-04-12 22:16:01.000000000 -0500
> @@ -44,7 +44,6 @@ extern struct task_struct *_switch_to(st
> #define smp_rmb() rmb()
> #define smp_wmb() wmb()
>
> -void show_trace(struct task_struct *task, unsigned long *stack);
> void __bad_xchg(volatile void *ptr, int size);
>
> static inline unsigned long __xchg(unsigned long x, volatile void *ptr,
> diff -uprN a/arch/microblaze/kernel/entry-nommu.S b/arch/microblaze/kernel/entry-nommu.S
> --- a/arch/microblaze/kernel/entry-nommu.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/entry-nommu.S 2010-04-12 22:16:01.000000000 -0500
> @@ -586,3 +586,31 @@ sys_rt_sigsuspend_wrapper:
> #include "syscall_table.S"
>
> syscall_table_size=(.-sys_call_table)
> +
> +type_SYSCALL:
> + .ascii "SYSCALL\0"
> +type_IRQ:
> + .ascii "IRQ\0"
> +type_IRQ_PREEMPT:
> + .ascii "IRQ (PREEMPTED)\0"
> +type_SYSCALL_PREEMPT:
> + .ascii " SYSCALL (PREEMPTED)\0"
> +
> + /*
> + * Trap decoding for stack unwinder
> + * Tuples are (start addr, end addr, string)
> + * If return address lies on [start addr, end addr],
> + * unwinder displays 'string'
> + */
> +
> + .align 4
> +.global microblaze_trap_handlers
> +microblaze_trap_handlers:
> + /* Exact matches come first */
> + .word ret_to_user ; .word ret_to_user ; .word type_SYSCALL
> + .word ret_from_intr; .word ret_from_intr ; .word type_IRQ
> + /* Fuzzy matches go here */
> + .word ret_from_intr; .word no_intr_resched; .word type_IRQ_PREEMPT
> + .word work_pending ; .word no_work_pending; .word type_SYSCALL_PREEMPT
> + /* End of table */
> + .word 0 ; .word 0 ; .word 0
> diff -uprN a/arch/microblaze/kernel/entry.S b/arch/microblaze/kernel/entry.S
> --- a/arch/microblaze/kernel/entry.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/entry.S 2010-04-12 22:16:01.000000000 -0500
> @@ -1127,3 +1127,30 @@ ENTRY(_break)
>
> syscall_table_size=(.-sys_call_table)
>
> +type_SYSCALL:
> + .ascii "SYSCALL\0"
> +type_IRQ:
> + .ascii "IRQ\0"
> +type_IRQ_PREEMPT:
> + .ascii "IRQ (PREEMPTED)\0"
> +type_SYSCALL_PREEMPT:
> + .ascii " SYSCALL (PREEMPTED)\0"
> +
> + /*
> + * Trap decoding for stack unwinder
> + * Tuples are (start addr, end addr, string)
> + * If return address lies on [start addr, end addr],
> + * unwinder displays 'string'
> + */
> +
> + .align 4
> +.global microblaze_trap_handlers
> +microblaze_trap_handlers:
> + /* Exact matches come first */
> + .word ret_from_trap; .word ret_from_trap ; .word type_SYSCALL
> + .word ret_from_irq ; .word ret_from_irq ; .word type_IRQ
> + /* Fuzzy matches go here */
> + .word ret_from_irq ; .word no_intr_resched ; .word type_IRQ_PREEMPT
> + .word ret_from_trap; .word TRAP_return ; .word type_SYSCALL_PREEMPT
> + /* End of table */
> + .word 0 ; .word 0 ; .word 0
> diff -uprN a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S
> --- a/arch/microblaze/kernel/hw_exception_handler.S 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/hw_exception_handler.S 2010-04-12 22:21:15.000000000 -0500
> @@ -78,9 +78,6 @@
> #include <asm/asm-offsets.h>
>
> /* Helpful Macros */
> -#ifndef CONFIG_MMU
> -#define EX_HANDLER_STACK_SIZ (4*19)
> -#endif
> #define NUM_TO_REG(num) r ## num
>
> #ifdef CONFIG_MMU
> @@ -988,6 +985,7 @@ ex_unaligned_fixup:
> .end _unaligned_data_exception
> #endif /* CONFIG_MMU */
>
> +.global ex_handler_unhandled
> ex_handler_unhandled:
> /* FIXME add handle function for unhandled exception - dump register */
> bri 0
> diff -uprN a/arch/microblaze/kernel/Makefile b/arch/microblaze/kernel/Makefile
> --- a/arch/microblaze/kernel/Makefile 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/Makefile 2010-04-12 22:16:01.000000000 -0500
> @@ -17,7 +17,8 @@ extra-y := head.o vmlinux.lds
> obj-y += dma.o exceptions.o \
> hw_exception_handler.o init_task.o intc.o irq.o of_device.o \
> of_platform.o process.o prom.o prom_parse.o ptrace.o \
> - setup.o signal.o sys_microblaze.o timer.o traps.o reset.o
> + setup.o signal.o sys_microblaze.o timer.o traps.o reset.o \
> + unwind.o

It is not your fault but please sort this list - reset to front of the list.

>
> obj-y += cpu/
>
> diff -uprN a/arch/microblaze/kernel/stacktrace.c b/arch/microblaze/kernel/stacktrace.c
> --- a/arch/microblaze/kernel/stacktrace.c 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/stacktrace.c 2010-04-12 22:16:01.000000000 -0500
> @@ -14,52 +14,18 @@
> #include <linux/thread_info.h>
> #include <linux/ptrace.h>
> #include <linux/module.h>
> +#include "unwind.h"

asm/

>
> -/* FIXME initial support */
> void save_stack_trace(struct stack_trace *trace)
> {
> - unsigned long *sp;
> - unsigned long addr;
> - asm("addik %0, r1, 0" : "=r" (sp));
> -
> - while (!kstack_end(sp)) {
> - addr = *sp++;
> - if (__kernel_text_address(addr)) {
> - if (trace->skip > 0)
> - trace->skip--;
> - else
> - trace->entries[trace->nr_entries++] = addr;
> -
> - if (trace->nr_entries >= trace->max_entries)
> - break;
> - }
> - }
> + /* Exclude our helper functions from the trace*/
> + trace->skip += 2;
> + microblaze_unwind(NULL, trace);
> }
> EXPORT_SYMBOL_GPL(save_stack_trace);
>
> void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
> {
> - unsigned int *sp;
> - unsigned long addr;
> -
> - struct thread_info *ti = task_thread_info(tsk);
> -
> - if (tsk == current)
> - asm("addik %0, r1, 0" : "=r" (sp));
> - else
> - sp = (unsigned int *)ti->cpu_context.r1;
> -
> - while (!kstack_end(sp)) {
> - addr = *sp++;
> - if (__kernel_text_address(addr)) {
> - if (trace->skip > 0)
> - trace->skip--;
> - else
> - trace->entries[trace->nr_entries++] = addr;
> -
> - if (trace->nr_entries >= trace->max_entries)
> - break;
> - }
> - }
> + microblaze_unwind(tsk, trace);
> }
> EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
> diff -uprN a/arch/microblaze/kernel/traps.c b/arch/microblaze/kernel/traps.c
> --- a/arch/microblaze/kernel/traps.c 2010-04-09 21:52:36.000000000 -0500
> +++ b/arch/microblaze/kernel/traps.c 2010-04-12 22:16:01.000000000 -0500
> @@ -13,16 +13,19 @@
> #include <linux/module.h>
> #include <linux/sched.h>
> #include <linux/debug_locks.h>
> +#include <linux/seq_file.h>
>
> #include <asm/exceptions.h>
> #include <asm/system.h>
>
> +#include "unwind.h"
> +
> void trap_init(void)
> {
> __enable_hw_exceptions();
> }
>
> -static unsigned long kstack_depth_to_print = 24;
> +static unsigned long kstack_depth_to_print; /* 0 == entire stack */
>
> static int __init kstack_setup(char *s)
> {
> @@ -30,31 +33,42 @@ static int __init kstack_setup(char *s)
> }
> __setup("kstack=", kstack_setup);
>
> -void show_trace(struct task_struct *task, unsigned long *stack)
> +void show_stack(struct task_struct *task, unsigned long *sp)
> {
> - unsigned long addr;
> + unsigned long words_to_show;
> + __u32 fp = (__u32) sp;


include/asm-generic/int-ll64.h - just u32 is enough. The same above too.

/*
* __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
* header files exported to user space
*/

>
> - if (!stack)
> - stack = (unsigned long *)&stack;
> + if (fp == 0) {
> + if (task)
> + fp = ((struct thread_info *)
> + (task->stack))->cpu_context.r1;
> + else {
> + /* Pick up caller of dump_stack() */
> + fp = (__u32)&sp - 8;
> + }
> + }

just coding style.

if (fp == 0)
if (task)
fp = ((struct thread_info *)
(task->stack))->cpu_context.r1;
else
/* Pick up caller of dump_stack() */
fp = (__u32)&sp - 8;




>
> - printk(KERN_NOTICE "Call Trace: ");
> -#ifdef CONFIG_KALLSYMS
> - printk(KERN_NOTICE "\n");
> -#endif
> - while (!kstack_end(stack)) {
> - addr = *stack++;
> - /*
> - * If the address is either in the text segment of the
> - * kernel, or in the region which contains vmalloc'ed
> - * memory, it *may* be the address of a calling
> - * routine; if so, print it so that someone tracing
> - * down the cause of the crash will be able to figure
> - * out the call path that was taken.
> - */
> - if (kernel_text_address(addr))
> - print_ip_sym(addr);
> + words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE-1))) >> 2;

words_to_show = (THREAD_SIZE - (fp & (THREAD_SIZE - 1))) >> 2;

> + if (kstack_depth_to_print && (words_to_show > kstack_depth_to_print))
> + words_to_show = kstack_depth_to_print;
> +
> + pr_info("Stack:\n");
> + if (fp & 0xF) {
> + unsigned long line1_words = (0x10 - (fp & 0xF)) >> 2;

these constants need some comments.

> + if (line1_words < words_to_show) {
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32,
> + 4, (void *)fp, line1_words << 2, 0);
> + fp += line1_words << 2;
> + words_to_show -= line1_words;
> + }
> }
> - printk(KERN_NOTICE "\n");
> + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS, 32, 4, (void *)fp,
> + words_to_show << 2, 0);
> + printk(KERN_INFO "\n\n");
> +
> + pr_info("Call Trace:\n");
> + microblaze_unwind(task, NULL);
> + pr_info("\n");
>
> if (!task)
> task = current;
> @@ -62,34 +76,6 @@ void show_trace(struct task_struct *task
> debug_show_held_locks(task);
> }
>
> -void show_stack(struct task_struct *task, unsigned long *sp)
> -{
> - unsigned long *stack;
> - int i;
> -
> - if (sp == NULL) {
> - if (task)
> - sp = (unsigned long *) ((struct thread_info *)
> - (task->stack))->cpu_context.r1;
> - else
> - sp = (unsigned long *)&sp;
> - }
> -
> - stack = sp;
> -
> - printk(KERN_INFO "\nStack:\n ");
> -
> - for (i = 0; i < kstack_depth_to_print; i++) {
> - if (kstack_end(sp))
> - break;
> - if (i && ((i % 8) == 0))
> - printk("\n ");
> - printk("%08lx ", *sp++);
> - }
> - printk("\n");
> - show_trace(task, stack);
> -}
> -
> void dump_stack(void)
> {
> show_stack(NULL, NULL);
> @@ -129,3 +115,4 @@ int debug_trap(struct pt_regs *regs)
> return -ENOSYS;
> }
> #endif
> +

remove this white line

I will continue on it tomorrow.

Michal


> diff -uprN a/arch/microblaze/kernel/unwind.c b/arch/microblaze/kernel/unwind.c
> --- a/arch/microblaze/kernel/unwind.c 1969-12-31 18:00:00.000000000 -0600
> +++ b/arch/microblaze/kernel/unwind.c 2010-04-12 22:16:01.000000000 -0500
> @@ -0,0 +1,316 @@
> +/*
> + * Backtrace support for Microblaze
> + *
> + * Copyright (C) 2010 Digital Design Corporation
> + *
> + * Based on arch/sh/kernel/cpu/sh5/unwind.c code which is:
> + * Copyright (C) 2004 Paul Mundt
> + * Copyright (C) 2004 Richard Curnow
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + */
> +
> +/* #define DEBUG 1 */
> +#include <linux/kallsyms.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/stacktrace.h>
> +#include <linux/types.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <asm/sections.h>
> +#include <asm/exceptions.h>
> +#include "unwind.h"
> +
> +struct stack_trace;
> +
> +/*
> + * On Microblaze, finding the previous stack frame is a little tricky.
> + * At this writing (3/2010), Microblaze does not support CONFIG_FRAME_POINTERS,
> + * and even if it did, gcc (4.1.2) does not store the frame pointer at
> + * a consistent offset within each frame. To determine frame size, it is
> + * necessary to search for the assembly instruction that creates the frame
> + * and extract the size from it.
> + *
> + * Microblaze stores the stack pointer in r1, and creates a frame via
> + *
> + * addik r1, r1, -FRAME_SIZE
> + *
> + * A stack frame is usually not created in a leaf function.
> + *
> + */
> +
> +/**
> + * get_frame_size - Extract the stack adjustment from an
> + * "addik r1, r1, -adjust" instruction
> + * @instr : Microblaze instruction
> + *
> + * Return - Number of bytes the instruction reserves for a stack frame
> + */
> +inline s16 get_frame_size(unsigned long instr)
> +{
> + return -(s16)(instr & 0xFFFF);
> +}
> +
> +/**
> + * find_frame_creation - Search backward to find the instruction that creates
> + * the stack frame (hopefully, for the same function the
> + * initial PC is in).
> + * @pc : Program counter at which to begin the search
> + *
> + * Return - PC at which stack frame creation occurs
> + * NULL if this cannot be found, i.e. a leaf function
> + */
> +static unsigned long *find_frame_creation(unsigned long *pc)
> +{
> + int i;
> +
> + /* NOTE: Distance to search is arbitrary
> + * 250 works well for most things,
> + * 750 picks up things like tcp_recvmsg(),
> + * 1000 needed for fat_fill_super()
> + */
> + for (i = 0; i < 1000; i++, pc--) {
> + unsigned long instr;
> + s16 frame_size;
> +
> + if (!kernel_text_address((unsigned long) pc))
> + return NULL;
> +
> + instr = *pc;
> +
> + /*
> + * rtsd of some sort,
> + * i.e., a return from a previous function?
> + */
> + if ((instr & 0xFFE00000) == 0xb6000000)
> + return NULL;
> +
> + if ((instr & 0xFFFF0000) != 0x30210000) /* addik r1, r1 */
> + continue;
> +
> + frame_size = get_frame_size(instr);
> + if ((frame_size < 8) || (frame_size & 3)) {
> + pr_debug(" Invalid frame size %d at 0x%p\n",
> + frame_size, pc);
> + return NULL;
> + }
> +
> + pr_debug(" Found frame creation at 0x%p, size %d\n", pc,
> + frame_size);
> + return pc;
> + }
> +
> + return NULL;
> +}
> +
> +/**
> + * lookup_prev_stack_frame - Find the stack frame of the previous function.
> + * @fp : Frame (stack) pointer for current function
> + * @pc : Program counter within current function
> + * @leaf_return : r15 value within current function. If the current function
> + * is a leaf, this is the caller's return address.
> + * @pprev_fp : On exit, set to frame (stack) pointer for previous function
> + * @pprev_pc : On exit, set to current function caller's return address
> + *
> + * Return - 0 on success, -EINVAL if the previous frame cannot be found
> + */
> +static int lookup_prev_stack_frame(unsigned long fp, unsigned long pc,
> + unsigned long leaf_return,
> + unsigned long *pprev_fp,
> + unsigned long *pprev_pc)
> +{
> + unsigned long *prologue = NULL;
> +
> + /* _switch_to is a special leaf function */
> + if (pc != (unsigned long) &_switch_to)
> + prologue = find_frame_creation((unsigned long *)pc);
> +
> + if (prologue) {
> + s16 frame_size = get_frame_size(*prologue);
> +
> + *pprev_fp = fp + frame_size;
> + *pprev_pc = *(unsigned long *)fp;
> + } else {
> + if (!leaf_return)
> + return -EINVAL;
> + *pprev_pc = leaf_return;
> + *pprev_fp = fp;
> + }
> +
> + /* NOTE: don't check kernel_text_address here, to allow display
> + * of userland return address
> + */
> + return (!*pprev_pc || (*pprev_pc & 3)) ? -EINVAL : 0;
> +}
> +
> +static void microblaze_unwind_inner(struct task_struct *task,
> + unsigned long pc, unsigned long fp,
> + unsigned long leaf_return,
> + struct stack_trace *trace);
> +
> +/**
> + * unwind_trap - Unwind through a system trap, that stored previous state
> + * on the stack.
> + */
> +#ifdef CONFIG_MMU
> +static inline void unwind_trap(struct task_struct *task, unsigned long pc,
> + unsigned long fp, struct stack_trace *trace)
> +{
> + /* To be implemented */
> +}
> +#else
> +static inline void unwind_trap(struct task_struct *task, unsigned long pc,
> + unsigned long fp, struct stack_trace *trace)
> +{
> + const struct pt_regs *regs = (const struct pt_regs *) fp;
> + microblaze_unwind_inner(task, regs->pc, regs->r1, regs->r15, trace);
> +}
> +#endif
> +
> +/**
> + * microblaze_unwind_inner - Unwind the stack from the specified point
> + * @task : Task whose stack we are to unwind (may be NULL)
> + * @pc : Program counter from which we start unwinding
> + * @fp : Frame (stack) pointer from which we start unwinding
> + * @leaf_return : Value of r15 at pc. If the function is a leaf, this is
> + * the caller's return address.
> + * @trace : Where to store stack backtrace (PC values).
> + * NULL == print backtrace to kernel log
> + */
> +void microblaze_unwind_inner(struct task_struct *task,
> + unsigned long pc, unsigned long fp,
> + unsigned long leaf_return,
> + struct stack_trace *trace)
> +{
> + int ofs = 0;
> +
> + pr_debug(" Unwinding with PC=%p, FP=%p\n", (void *)pc, (void *)fp);
> + if (!pc || !fp || (pc & 3) || (fp & 3)) {
> + pr_debug(" Invalid state for unwind, aborting\n");
> + return;
> + }
> + for (; pc != 0;) {
> + unsigned long next_fp, next_pc = 0;
> + unsigned long return_to = pc + 2 * sizeof(unsigned long);
> + const struct trap_handler_info *handler =
> + &microblaze_trap_handlers;
> +
> + /* Is previous function the HW exception handler? */
> + if ((return_to >= (unsigned long)&_hw_exception_handler)
> + &&(return_to < (unsigned long)&ex_handler_unhandled)) {
> + /*
> + * HW exception handler doesn't save all registers,
> + * so we open-code a special case of unwind_trap()
> + */
> +#ifndef CONFIG_MMU
> + const struct pt_regs *regs =
> + (const struct pt_regs *) fp;
> +#endif
> + pr_info("HW EXCEPTION\n");
> +#ifndef CONFIG_MMU
> + microblaze_unwind_inner(task, regs->r17 - 4,
> + fp + EX_HANDLER_STACK_SIZ,
> + regs->r15, trace);
> +#endif
> + return;
> + }
> +
> + /* Is previous function a trap handler? */
> + for (; handler->start_addr; ++handler) {
> + if ((return_to >= handler->start_addr)
> + && (return_to <= handler->end_addr)) {
> + if (!trace)
> + pr_info("%s\n", handler->trap_name);
> + unwind_trap(task, pc, fp, trace);
> + return;
> + }
> + }
> + pc -= ofs;
> +
> + if (trace) {
> +#ifdef CONFIG_STACKTRACE
> + if (trace->skip > 0)
> + trace->skip--;
> + else
> + trace->entries[trace->nr_entries++] = pc;
> +
> + if (trace->nr_entries >= trace->max_entries)
> + break;
> +#endif
> + } else {
> + /* Have we reached userland? */
> + if (unlikely(pc == task_pt_regs(task)->pc)) {
> + pr_info("[<%p>] PID %lu [%s]\n",
> + (void *) pc,
> + (unsigned long) task->pid,
> + task->comm);
> + break;
> + } else
> + print_ip_sym(pc);
> + }
> +
> + /* Stop when we reach anything not part of the kernel */
> + if (!kernel_text_address(pc))
> + break;
> +
> + if (lookup_prev_stack_frame(fp, pc, leaf_return, &next_fp,
> + &next_pc) == 0) {
> + ofs = sizeof(unsigned long);
> + pc = next_pc & ~3;
> + fp = next_fp;
> + leaf_return = 0;
> + } else {
> + pr_debug(" Failed to find previous stack frame\n");
> + break;
> + }
> +
> + pr_debug(" Next PC=%p, next FP=%p\n",
> + (void *)next_pc, (void *)next_fp);
> + }
> +}
> +
> +/**
> + * microblaze_unwind - Stack unwinder for Microblaze (external entry point)
> + * @task : Task whose stack we are to unwind (NULL == current)
> + * @trace : Where to store stack backtrace (PC values).
> + * NULL == print backtrace to kernel log
> + */
> +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace)
> +{
> + if (task) {
> + if (task == current) {
> + const struct pt_regs *regs = task_pt_regs(task);
> + microblaze_unwind_inner(task, regs->pc, regs->r1,
> + regs->r15, trace);
> + } else {
> + struct thread_info *thread_info =
> + (struct thread_info *)(task->stack);
> + const struct cpu_context *cpu_context =
> + &thread_info->cpu_context;
> +
> + microblaze_unwind_inner(task,
> + (unsigned long) &_switch_to,
> + cpu_context->r1,
> + cpu_context->r15, trace);
> + }
> + } else {
> + unsigned long pc, fp;
> +
> + __asm__ __volatile__ ("or %0, r1, r0" : "=r" (fp));
> +
> + __asm__ __volatile__ (
> + "brlid %0, 0f;"
> + "nop;"
> + "0:"
> + : "=r" (pc)
> + );
> +
> + /* Since we are not a leaf function, use leaf_return = 0 */
> + microblaze_unwind_inner(current, pc, fp, 0, trace);
> + }
> +}
> +
> diff -uprN a/arch/microblaze/kernel/unwind.h b/arch/microblaze/kernel/unwind.h
> --- a/arch/microblaze/kernel/unwind.h 1969-12-31 18:00:00.000000000 -0600
> +++ b/arch/microblaze/kernel/unwind.h 2010-04-12 22:16:01.000000000 -0500
> @@ -0,0 +1,29 @@
> +/*
> + * Backtrace support for Microblaze
> + *
> + * Copyright (C) 2010 Digital Design Corporation
> + *
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License. See the file "COPYING" in the main directory of this archive
> + * for more details.
> + */
> +
> +#ifndef __MICROBLAZE_UNWIND_H
> +#define __MICROBLAZE_UNWIND_H
> +
> +struct stack_trace;
> +
> +struct trap_handler_info {
> + unsigned long start_addr;
> + unsigned long end_addr;
> + const char *trap_name;
> +};
> +extern struct trap_handler_info microblaze_trap_handlers;
> +
> +extern const char _hw_exception_handler;
> +extern const char ex_handler_unhandled;
> +
> +void microblaze_unwind(struct task_struct *task, struct stack_trace *trace);
> +
> +#endif /* __MICROBLAZE_UNWIND_H */
> +
>
>


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-16 08:33:01

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> Implement intelligent backtracing by searching for stack frame creation,
> and emitting only return addresses. Use print_hex_dump() to display the
> entire binary kernel stack.
>
> Limitation: MMU kernels are not currently able to trace beyond a system trap
> (interrupt, syscall, etc.). It is the intent of this patch to provide
> infrastructure that can be extended to add this capability later.
>
> Signed-off-by: Steven J. Magnani <[email protected]>

First of all it is step to good direction.

Please apply this patch and do the same testing as I did.

diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
index a1721a3..76f6587 100644
--- a/arch/microblaze/kernel/reset.c
+++ b/arch/microblaze/kernel/reset.c
@@ -112,8 +112,8 @@ void of_platform_reset_gpio_probe(void)
void machine_restart(char *cmd)
{
printk(KERN_NOTICE "Machine restart...\n");
+ machine_shutdown();
gpio_system_reset();
- dump_stack();
while (1)
;
}
@@ -121,6 +121,7 @@ void machine_restart(char *cmd)
void machine_shutdown(void)
{
printk(KERN_NOTICE "Machine shutdown...\n");
+ dump_stack();
while (1)
;
}


My expectation is that if is called machine_restart then stack will show

kernel_restart
machine_restart
machine_shutdown
microblaze_unwind

but I can see just latest function. Look at logs below + there are some
my comments.

early_printk_console is enabled at 0x83e00000
Ramdisk addr 0x00000000, FDT at 0x4a000000
Linux version 2.6.34-rc4-00053-gbc6ce8a-dirty ([email protected]) (gcc
version 4.1.2) #42 Fri Apr 16 10:01:05 CEST 2010
setup_cpuinfo: initialising
setup_cpuinfo: Using full CPU PVR support
cache: wt_msr_noirq
setup_memory: Main mem: 0x48000000-0x4a800000, size 0x02800000
setup_memory: kernel addr=0x48000000-0x48358000 size=0x00358000
setup_memory: Main mem: 0x48000000-0x4a800000, size 0x02800000, klimit
0x48358000
setup_memory: max_mapnr: 0x2800
setup_memory: min_low_pfn: 0x48000
setup_memory: max_low_pfn: 0x4a800
reserved 0 - 0x48000000-0x00358500
reserved 1 - 0x4a7fddc0-0x00002240
On node 0 totalpages: 10240
free_area_init_node: node 0, pgdat 4824aae0, node_mem_map 48359000
Normal zone: 80 pages used for memmap
Normal zone: 10160 pages, LIFO batch:0
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 10160
Kernel command line: console=ttyUL0,115200
PID hash table entries: 256 (order: -2, 1024 bytes)
Dentry cache hash table entries: 8192 (order: 3, 32768 bytes)
Inode-cache hash table entries: 4096 (order: 2, 16384 bytes)
Memory: 37152k/40960k available
Hierarchical RCU implementation.
NR_IRQS:32
xlnx,xps-intc-1.00.a #0 at 0x81800000, num_irq=7, edge=0x53
xlnx,xps-timer-1.00.a #0 at 0x83c00000, irq=4
microblaze_timer_set_mode: shutdown
microblaze_timer_set_mode: periodic
Calibrating delay loop... 44.64 BogoMIPS (lpj=223232)
Mount-cache hash table entries: 512
NET: Registered protocol family 16
Switching to clocksource microblaze_clocksource
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 2048 (order: 2, 16384 bytes)
TCP bind hash table entries: 2048 (order: 1, 8192 bytes)
TCP: Hash tables configured (established 2048 bind 2048)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Skipping unavailable RESET gpio -2 (reset)
GPIO pin is already allocated
JFFS2 version 2.2. (NAND) (SUMMARY) ? 2001-2006 Red Hat, Inc.
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
83e00000.serial: ttyUL0 at MMIO 0x83e00003 (irq = 6) is a uartlite
console [ttyUL0] enabled
86000000.flash: Found 1 x16 devices at 0x0 in 16-bit bank
Intel/Sharp Extended Query Table at 0x010A
Intel/Sharp Extended Query Table at 0x010A
Intel/Sharp Extended Query Table at 0x010A
Intel/Sharp Extended Query Table at 0x010A
Intel/Sharp Extended Query Table at 0x010A
Using buffer write method
Using auto-unlock on power-up/resume
cfi_cmdset_0001: Erase suspend on write enabled
erase region 0: offset=0x0,size=0x20000,blocks=255
erase region 1: offset=0x1fe0000,size=0x8000,blocks=4
RedBoot partition parsing not available
Device Tree Probing 'ethernet'
TXCSUM 1
RXCSUM 1
xilinx_lltemac 82a80000.ethernet: MAC address is now 0: a:35: 0: 0: 0
xilinx_lltemac 82a80000.ethernet: XLlTemac: using DMA mode.
XLlTemac: Dma base address: phy: 0x84600080, virt: 0x84600080
XLlTemac: buffer descriptor size: 32768 (0x8000)
XLlTemac: Allocating DMA descriptors with kmalloc
XLlTemac: (buffer_descriptor_init) phy: 0x4a060000, virt: 0x4a060000,
size: 0x8000
XTemac: PHY detected at address 7.
xilinx_lltemac 82a80000.ethernet: eth0: Xilinx TEMAC at 0x82A80000
mapped to 0x82A80000, irq=5
TCP cubic registered
NET: Registered protocol family 17
Freeing unused kernel memory: 988k freed
Bad inittab entry at line 3
Mounting proc:
Mounting var:
Populating /var:
Running local start scripts.
Mounting sysfs:
Setting hostname:
Setting up interface lo:
Setting up interface eth0:
eth0: XLlTemac: Options: 0x3fa
eth0: XLlTemac: allocating interrupt 2 for dma mode tx.
eth0: XLlTemac: allocating interrupt 3 for dma mode rx.
eth0: XLlTemac: speed set to 100Mb/s
eth0: XLlTemac: Send Threshold = 24, Receive Threshold = 4
eth0: XLlTemac: Send Wait bound = 254, Receive Wait bound = 254
Starting syslogd:
Starting httpd:
Mounting nfs server

uclinux login: root
Password:
/root #
/root #
/root # reboot
The system is going down NOW!
Sent SIGTERM to all processes
TERM
Sent SIGKILL to all processes
Requesting system rRestarting system.
Machine restart...
Machine shutdown...
Stack:
4a059dc8: --48004aec-- 00000008
4a059dd0: 00000000 00000001 0000006d 49734fc8 4a059dec --48004b14--
481da68c 4823f318
4a059df0: ffffffff 00001019 00003fff 483459cc --4801f36c-- 481da6a4
4823f318 ffffffff
4a059e10: 00001003 00003fff 483459cc 4801ffa4 481e0f18 00000000 000005a2
00000000
4a059e30: 00000000 00000000 28121969 48006a28 00000000 00000000 00000000
00000000
4a059e50: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059e70: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059e90: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059eb0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059ed0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059ef0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
4a059f10: 00000000 00000000 00000000 48009e8c 00000000 00000000 00000000
00000000
4a059f30: 00000000 00000000 4a059f44 00000000 00000000 480069dc 00000000
00000000
4a059f50: 00000000 00000000 00000000 fee1dead 01234567 ffffffff 00000000
49734a74
4a059f70: 00000000 00000000 0000007d fee1dead 28121969 01234567 00000008
497160b0
4a059f90: 00000000 28121969 00000058 00000000 4960c998 496eef5c 00000000
00000000
4a059fb0: 7fffff82 00000000 00000000 00000000 fee1dead 01234567 ffffffff
00000001
4a059fd0: 00000001 0000006d 49734fc8 49734fc8 00000000 4a3b9930 4960c99c
000005a0
4a059ff0: 00000000 00000000 000005a0 00000000


Call Trace:
[<48004fd0>] microblaze_unwind+0x74/0x8c


I identify some values (between -- -- in log above) which are correct
traces but the code doesn't show them. That's why I think that something
is wrong there.

Thanks,
Michal



Interesting is that my memory dump has some weird values. Read via XMD.
(It could be any cache issue)

XMD% mrd 0x4a059dc8 100
4A059DC8: 4A3B9930
4A059DCC: 48004AF4 - machine_shutdown - bad number (should be 48004aec
as you have in your log - and one line above also)
4A059DD0: 000005A0
4A059DD4: 00000000
4A059DD8: 00000000
4A059DDC: 000005A0
4A059DE0: 00000001
4A059DE4: 48004B14 - machine restart
4A059DE8: 481DA68C
4A059DEC: 4823F318
4A059DF0: FFFFFFFF
4A059DF4: 00001019
4A059DF8: 00003FFF
4A059DFC: 483459CC
4A059E00: 4801F36C - kernel restart
4A059E04: 481DA6A4
4A059E08: 4823F318
4A059E0C: FFFFFFFF
4A059E10: 00001003
4A059E14: 00003FFF
4A059E18: 483459CC
4A059E1C: 4801FFA4


Here is objdump

48004acc <machine_shutdown>:
48004acc: b000481d imm 18461
48004ad0: 30a0a68c addik r5, r0, -22900
48004ad4: 3021ffe4 addik r1, r1, -28
48004ad8: f9e10000 swi r15, r1, 0
48004adc: b0000000 imm 0
48004ae0: b9f4a98c brlid r15, -22132 // 4800f46c <printk>
48004ae4: 80000000 or r0, r0, r0
48004ae8: b000ffff imm -1
48004aec: b9f4ff80 brlid r15, -128 // 48004a6c <dump_stack>
48004af0: 80000000 or r0, r0, r0
48004af4: b8000000 bri 0 // 48004af4

48004af8 <machine_restart>:
48004af8: b000481d imm 18461
48004afc: 30a0a6a4 addik r5, r0, -22876
48004b00: 3021ffe4 addik r1, r1, -28
48004b04: f9e10000 swi r15, r1, 0
48004b08: b0000000 imm 0
48004b0c: b9f4a960 brlid r15, -22176 // 4800f46c <printk>
48004b10: 80000000 or r0, r0, r0
48004b14: b9f4ffb8 brlid r15, -72 // 48004acc <machine_shutdown>
48004b18: 80000000 or r0, r0, r0
48004b1c: b0004834 imm 18484
48004b20: e8c053a4 lwi r6, r0, 21412 // 483453a4 <reset_val>
48004b24: b0004834 imm 18484
48004b28: e8a053a0 lwi r5, r0, 21408 // 483453a0 <handle>
48004b2c: b000000e imm 14
48004b30: b9f4f0c8 brlid r15, -3896 // 480f3bf8 <__gpio_set_value>
48004b34: 34c60001 rsubik r6, r6, 1
48004b38: b8000000 bri 0 // 48004b38


4801f330 <kernel_restart>:
4801f330: 3021ffe0 addik r1, r1, -32
4801f334: fa61001c swi r19, r1, 28
4801f338: f9e10000 swi r15, r1, 0
4801f33c: b9f4ff5c brlid r15, -164 // 4801f298 <kernel_restart_prepare>
4801f340: 12650000 addk r19, r5, r0
4801f344: b000481e imm 18462
4801f348: 30a00f18 addik r5, r0, 3864
4801f34c: be130038 beqid r19, 56 // 4801f384
4801f350: 10d30000 addk r6, r19, r0
4801f354: b000481e imm 18462
4801f358: 30a00f30 addik r5, r0, 3888
4801f35c: b000ffff imm -1
4801f360: b9f4010c brlid r15, 268 // 4800f46c <printk>
4801f364: 80000000 or r0, r0, r0
4801f368: b000fffe imm -2
4801f36c: b9f4578c brlid r15, 22412 // 48004af8 <machine_restart>
4801f370: 10b30000 addk r5, r19, r0
4801f374: e9e10000 lwi r15, r1, 0
4801f378: ea61001c lwi r19, r1, 28
4801f37c: b60f0008 rtsd r15, 8
4801f380: 30210020 addik r1, r1, 32
4801f384: b000ffff imm -1
4801f388: b9f400e4 brlid r15, 228 // 4800f46c <printk>
4801f38c: 80000000 or r0, r0, r0
4801f390: b800ffd8 bri -40 // 4801f368


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-16 14:33:38

by Steven J. Magnani

[permalink] [raw]
Subject: Re: [microblaze-uclinux] Re: [PATCH 1/2] microblaze: add stack unwinder

On Fri, 2010-04-16 at 10:32 +0200, Michal Simek wrote:
> Please apply this patch and do the same testing as I did.
>
> diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
> index a1721a3..76f6587 100644
> --- a/arch/microblaze/kernel/reset.c
> +++ b/arch/microblaze/kernel/reset.c
> @@ -112,8 +112,8 @@ void of_platform_reset_gpio_probe(void)
> void machine_restart(char *cmd)
> {
> printk(KERN_NOTICE "Machine restart...\n");
> + machine_shutdown();
> gpio_system_reset();
> - dump_stack();
> while (1)
> ;
> }
> @@ -121,6 +121,7 @@ void machine_restart(char *cmd)
> void machine_shutdown(void)
> {
> printk(KERN_NOTICE "Machine shutdown...\n");
> + dump_stack();
> while (1)
> ;
> }

I get this:

Call Trace:
[<20003008>] microblaze_unwind+0x68/0x94
[<20002c24>] show_stack+0x134/0x174
[<20002c80>] dump_stack+0x1c/0x3c
[<20001d84>] machine_shutdown+0x30/0x40
[<20001dc0>] machine_restart+0x2c/0x48
[<2002a540>] kernel_restart+0x5c/0x80
[<2002a6b8>] sys_reboot+0x11c/0x218
SYSCALL
[<2d5c0248>] PID 118 [reboot]

Is your problem on a MMU or noMMU system?

If the unwinder stops early, it found a return address outside the
kernel text, or couldn't find the addik that creates a stack frame - but
there's not enough information here to tell what happened. In your case
the backtrace goes off the rails before getting to the frame for
dump_stack(), which is where the stack dump begins. We'd need to see the
21 words preceding the dump (show_stack's frame is 13 words, and
microblaze_unwind's is 8) to know why.

Can you uncomment the #define DEBUG in unwind.c and try it again?

Thanks,
------------------------------------------------------------------------
Steven J. Magnani "I claim this network for MARS!
http://www.digidescorp.com Earthling, return my space modulator!"

#include <standard.disclaimer>

2010-04-16 15:03:13

by Michal Simek

[permalink] [raw]
Subject: Re: [microblaze-uclinux] Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> On Fri, 2010-04-16 at 10:32 +0200, Michal Simek wrote:
>> Please apply this patch and do the same testing as I did.
>>
>> diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
>> index a1721a3..76f6587 100644
>> --- a/arch/microblaze/kernel/reset.c
>> +++ b/arch/microblaze/kernel/reset.c
>> @@ -112,8 +112,8 @@ void of_platform_reset_gpio_probe(void)
>> void machine_restart(char *cmd)
>> {
>> printk(KERN_NOTICE "Machine restart...\n");
>> + machine_shutdown();
>> gpio_system_reset();
>> - dump_stack();
>> while (1)
>> ;
>> }
>> @@ -121,6 +121,7 @@ void machine_restart(char *cmd)
>> void machine_shutdown(void)
>> {
>> printk(KERN_NOTICE "Machine shutdown...\n");
>> + dump_stack();
>> while (1)
>> ;
>> }
>
> I get this:
>
> Call Trace:
> [<20003008>] microblaze_unwind+0x68/0x94
> [<20002c24>] show_stack+0x134/0x174
> [<20002c80>] dump_stack+0x1c/0x3c
> [<20001d84>] machine_shutdown+0x30/0x40
> [<20001dc0>] machine_restart+0x2c/0x48
> [<2002a540>] kernel_restart+0x5c/0x80
> [<2002a6b8>] sys_reboot+0x11c/0x218
> SYSCALL
> [<2d5c0248>] PID 118 [reboot]
>
> Is your problem on a MMU or noMMU system?

Interesting. It was noMMU system.

>
> If the unwinder stops early, it found a return address outside the
> kernel text, or couldn't find the addik that creates a stack frame - but
> there's not enough information here to tell what happened. In your case
> the backtrace goes off the rails before getting to the frame for
> dump_stack(), which is where the stack dump begins. We'd need to see the
> 21 words preceding the dump (show_stack's frame is 13 words, and
> microblaze_unwind's is 8) to know why.
>
> Can you uncomment the #define DEBUG in unwind.c and try it again?

will do next week and let you know.

Thanks,
Michal


>
> Thanks,
> ------------------------------------------------------------------------
> Steven J. Magnani "I claim this network for MARS!
> http://www.digidescorp.com Earthling, return my space modulator!"
>
> #include <standard.disclaimer>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-26 15:35:02

by Michal Simek

[permalink] [raw]
Subject: Re: [microblaze-uclinux] Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> On Fri, 2010-04-16 at 10:32 +0200, Michal Simek wrote:
>> Please apply this patch and do the same testing as I did.
>>
>> diff --git a/arch/microblaze/kernel/reset.c b/arch/microblaze/kernel/reset.c
>> index a1721a3..76f6587 100644
>> --- a/arch/microblaze/kernel/reset.c
>> +++ b/arch/microblaze/kernel/reset.c
>> @@ -112,8 +112,8 @@ void of_platform_reset_gpio_probe(void)
>> void machine_restart(char *cmd)
>> {
>> printk(KERN_NOTICE "Machine restart...\n");
>> + machine_shutdown();
>> gpio_system_reset();
>> - dump_stack();
>> while (1)
>> ;
>> }
>> @@ -121,6 +121,7 @@ void machine_restart(char *cmd)
>> void machine_shutdown(void)
>> {
>> printk(KERN_NOTICE "Machine shutdown...\n");
>> + dump_stack();
>> while (1)
>> ;
>> }
>
> I get this:
>
> Call Trace:
> [<20003008>] microblaze_unwind+0x68/0x94
> [<20002c24>] show_stack+0x134/0x174
> [<20002c80>] dump_stack+0x1c/0x3c
> [<20001d84>] machine_shutdown+0x30/0x40
> [<20001dc0>] machine_restart+0x2c/0x48
> [<2002a540>] kernel_restart+0x5c/0x80
> [<2002a6b8>] sys_reboot+0x11c/0x218
> SYSCALL
> [<2d5c0248>] PID 118 [reboot]
>
> Is your problem on a MMU or noMMU system?
>
> If the unwinder stops early, it found a return address outside the
> kernel text, or couldn't find the addik that creates a stack frame - but
> there's not enough information here to tell what happened. In your case
> the backtrace goes off the rails before getting to the frame for
> dump_stack(), which is where the stack dump begins. We'd need to see the
> 21 words preceding the dump (show_stack's frame is 13 words, and
> microblaze_unwind's is 8) to know why.
>
> Can you uncomment the #define DEBUG in unwind.c and try it again?

I finally looked at it and here is what I found.

# ./reboot
Restarting system.
Machine restart...
Machine shutdown...
Stack:
4f3ffdc8: 48004aec 00000008
4f3ffdd0: 00000000 00000001 4f3e0150 00000001 4f3ffdec 48004b14 481dc68c
48241318
4f3ffdf0: ffffffff 00001099 00003fff 482f49cc 4801f518 481dc6a4 48241318
ffffffff
4f3ffe10: 00001083 00003fff 482f49cc 48020150 481e3048 00000000 000001a2
00000000
4f3ffe30: 00000000 00000000 28121969 48006b38 48240dc0 481d549c 481d53a8
481d549c
4f3ffe50: 00aa4812 481d523c 0000c350 00000035 48029618 4f3ffe70 000001a2
4f96c04c
4f3ffe70: 481d6390 0000c350 4f3fff58 4f3ffee0 481d639c 481d6370 00000009
4f3ffee0
4f3ffe90: 00000000 00000000 00000000 4f3ffee0 48029df4 00000001 4f3e0150
00000001
4f3ffeb0: 00000001 00000001 00000000 00000001 00000001 48029f34 4801b2a8
4800c5e0
4f3ffed0: 00000020 00000000 0000c350 00000000 00000001 00000000 00000000
00000035
4f3ffef0: 00ab0b62 00000035 00aa4812 480294e0 482425c0 00000000 00000000
00000000
4f3fff10: 0000c350 00000001 0000c350 4f3ead40 4f3fff58 00000001 00000000
00000000
4f3fff30: 00000001 4f3e0150 00000001 48006b38 00000000 4f3eaf68 00000000
00010000
4f3fff50: 00000000 00000000 00000000 fee1dead 01234567 00000000 00000000
4f3eaebc
4f3fff70: 00000000 00000000 00000000 fee1dead 28121969 01234567 00000008
00000000
4f3fff90: 00000000 28121969 00000058 00000000 4f3e0320 4f3e0274 00000000
00000000
4f3fffb0: 7fffff82 00000000 00000000 00000000 fee1dead 01234567 00000000
00000000
4f3fffd0: 00000001 4f3e0150 00000001 00000000 00000000 4f96c04c 4f3e0324
000001a0
4f3ffff0: 00000000 00000000 000001a0 00000000


Call Trace:
Unwinding with PC=480050d4, FP=4f3ffd74
[<480050d4>] microblaze_unwind+0xa8/0xc4
pc 0x480050ac instr 0x30210024
Invalid frame size -36 at 0x480050ac
Failed to find previous stack frame

Below is the dump - then you can see that 36 is correct.
That could be due to different toolchain behavior.

Thanks,
Michal


4800502c <microblaze_unwind>:
4800502c: b000482f imm 18479
48005030: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
48005034: 3021ffdc addik r1, r1, -36
48005038: fa61001c swi r19, r1, 28
4800503c: fac10020 swi r22, r1, 32
48005040: f9e10000 swi r15, r1, 0
48005044: e8830020 lwi r4, r3, 32
48005048: 12650000 addk r19, r5, r0
4800504c: 99fc2000 brald r15, r4
48005050: 12c60000 addk r22, r6, r0
48005054: b000482f imm 18479
48005058: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
4800505c: e8830008 lwi r4, r3, 8
48005060: 99fc2000 brald r15, r4
48005064: 80000000 or r0, r0, r0
48005068: be130068 beqid r19, 104 // 480050d0
4800506c: 10b30000 addk r5, r19, r0
48005070: b0004800 imm 18432
48005074: 30c069e0 addik r6, r0, 27104 // 480069e0
<_switch_to>
48005078: 165f9800 rsubk r18, r31, r19
4800507c: be120034 beqid r18, 52 // 480050b0
48005080: 11360000 addk r9, r22, r0
48005084: e8730004 lwi r3, r19, 4
48005088: 10b30000 addk r5, r19, r0
4800508c: e903004c lwi r8, r3, 76
48005090: e8e3003c lwi r7, r3, 60
48005094: b9f4fc7c brlid r15, -900 // 48004d10
<microblaze_unwind_inner>
48005098: 11360000 addk r9, r22, r0
4800509c: e9e10000 lwi r15, r1, 0
480050a0: ea61001c lwi r19, r1, 28
480050a4: eac10020 lwi r22, r1, 32
480050a8: b60f0008 rtsd r15, 8
480050ac: 30210024 addik r1, r1, 36
480050b0: e8730004 lwi r3, r19, 4
480050b4: 30631f68 addik r3, r3, 8040
480050b8: e903003c lwi r8, r3, 60
480050bc: e8c30080 lwi r6, r3, 128
480050c0: e8e30004 lwi r7, r3, 4
480050c4: b9f4fc4c brlid r15, -948 // 48004d10
<microblaze_unwind_inner>
480050c8: 80000000 or r0, r0, r0
480050cc: b800ffd0 bri -48 // 4800509c
480050d0: 80e10000 or r7, r1, r0
480050d4: b8d40008 brlid r6, 8
480050d8: 80000000 or r0, r0, r0
480050dc: 11130000 addk r8, r19, r0
480050e0: 11360000 addk r9, r22, r0
480050e4: b9f4fc2c brlid r15, -980 // 48004d10
<microblaze_unwind_inner>
480050e8: 10bf0000 addk r5, r31, r0
480050ec: b800ffb0 bri -80 // 4800509c






>
> Thanks,
> ------------------------------------------------------------------------
> Steven J. Magnani "I claim this network for MARS!
> http://www.digidescorp.com Earthling, return my space modulator!"
>
> #include <standard.disclaimer>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-27 03:51:27

by Steven J. Magnani

[permalink] [raw]
Subject: Re: [PATCH 1/2] microblaze: add stack unwinder

On Wed, 2010-04-14 at 18:45 +0200, Michal Simek wrote:
> > diff -uprN a/arch/microblaze/kernel/traps.c
> b/arch/microblaze/kernel/traps.c
> > --- a/arch/microblaze/kernel/traps.c 2010-04-09 21:52:36.000000000
> > +++ b/arch/microblaze/kernel/traps.c 2010-04-12 22:16:01.000000000
[snip]
> >
> > - if (!stack)
> > - stack = (unsigned long *)&stack;
> > + if (fp == 0) {
> > + if (task)
> > + fp = ((struct thread_info *)
> > + (task->stack))->cpu_context.r1;
> > + else {
> > + /* Pick up caller of dump_stack() */
> > + fp = (__u32)&sp - 8;
> > + }
> > + }
>
> just coding style.
>
> if (fp == 0)
> if (task)
> fp = ((struct thread_info *)
> (task->stack))->cpu_context.r1;
> else
> /* Pick up caller of dump_stack() */
> fp = (__u32)&sp - 8;

Do you feel strongly about this? I try to always use braces on if/else
clauses that have more than one line. I've found that the extra
characters are well worth the savings in debugging time when someone
tries to extend the clause and forgets to add the braces.

Steve

2010-04-27 03:51:49

by Steven J. Magnani

[permalink] [raw]
Subject: Re: [microblaze-uclinux] Re: [PATCH 1/2] microblaze: add stack unwinder

On Mon, 2010-04-26 at 17:34 +0200, Michal Simek wrote:

> I finally looked at it and here is what I found.
>
> # ./reboot
> Restarting system.
> Machine restart...
> Machine shutdown...
> Stack:
> 4f3ffdc8: 48004aec 00000008
> 4f3ffdd0: 00000000 00000001 4f3e0150 00000001 4f3ffdec 48004b14 481dc68c
> 48241318
> 4f3ffdf0: ffffffff 00001099 00003fff 482f49cc 4801f518 481dc6a4 48241318
> ffffffff
> 4f3ffe10: 00001083 00003fff 482f49cc 48020150 481e3048 00000000 000001a2
> 00000000
> 4f3ffe30: 00000000 00000000 28121969 48006b38 48240dc0 481d549c 481d53a8
> 481d549c
> 4f3ffe50: 00aa4812 481d523c 0000c350 00000035 48029618 4f3ffe70 000001a2
> 4f96c04c
> 4f3ffe70: 481d6390 0000c350 4f3fff58 4f3ffee0 481d639c 481d6370 00000009
> 4f3ffee0
> 4f3ffe90: 00000000 00000000 00000000 4f3ffee0 48029df4 00000001 4f3e0150
> 00000001
> 4f3ffeb0: 00000001 00000001 00000000 00000001 00000001 48029f34 4801b2a8
> 4800c5e0
> 4f3ffed0: 00000020 00000000 0000c350 00000000 00000001 00000000 00000000
> 00000035
> 4f3ffef0: 00ab0b62 00000035 00aa4812 480294e0 482425c0 00000000 00000000
> 00000000
> 4f3fff10: 0000c350 00000001 0000c350 4f3ead40 4f3fff58 00000001 00000000
> 00000000
> 4f3fff30: 00000001 4f3e0150 00000001 48006b38 00000000 4f3eaf68 00000000
> 00010000
> 4f3fff50: 00000000 00000000 00000000 fee1dead 01234567 00000000 00000000
> 4f3eaebc
> 4f3fff70: 00000000 00000000 00000000 fee1dead 28121969 01234567 00000008
> 00000000
> 4f3fff90: 00000000 28121969 00000058 00000000 4f3e0320 4f3e0274 00000000
> 00000000
> 4f3fffb0: 7fffff82 00000000 00000000 00000000 fee1dead 01234567 00000000
> 00000000
> 4f3fffd0: 00000001 4f3e0150 00000001 00000000 00000000 4f96c04c 4f3e0324
> 000001a0
> 4f3ffff0: 00000000 00000000 000001a0 00000000
>
>
> Call Trace:
> Unwinding with PC=480050d4, FP=4f3ffd74
> [<480050d4>] microblaze_unwind+0xa8/0xc4
> pc 0x480050ac instr 0x30210024
> Invalid frame size -36 at 0x480050ac
> Failed to find previous stack frame
>
> Below is the dump - then you can see that 36 is correct.
> That could be due to different toolchain behavior.
>
> Thanks,
> Michal
>
>
> 4800502c <microblaze_unwind>:
> 4800502c: b000482f imm 18479
> 48005030: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
> 48005034: 3021ffdc addik r1, r1, -36
> 48005038: fa61001c swi r19, r1, 28
> 4800503c: fac10020 swi r22, r1, 32
> 48005040: f9e10000 swi r15, r1, 0
> 48005044: e8830020 lwi r4, r3, 32
> 48005048: 12650000 addk r19, r5, r0
> 4800504c: 99fc2000 brald r15, r4
> 48005050: 12c60000 addk r22, r6, r0
> 48005054: b000482f imm 18479
> 48005058: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
> 4800505c: e8830008 lwi r4, r3, 8
> 48005060: 99fc2000 brald r15, r4
> 48005064: 80000000 or r0, r0, r0
> 48005068: be130068 beqid r19, 104 // 480050d0
> 4800506c: 10b30000 addk r5, r19, r0
> 48005070: b0004800 imm 18432
> 48005074: 30c069e0 addik r6, r0, 27104 // 480069e0
> <_switch_to>
> 48005078: 165f9800 rsubk r18, r31, r19
> 4800507c: be120034 beqid r18, 52 // 480050b0
> 48005080: 11360000 addk r9, r22, r0
> 48005084: e8730004 lwi r3, r19, 4
> 48005088: 10b30000 addk r5, r19, r0
> 4800508c: e903004c lwi r8, r3, 76
> 48005090: e8e3003c lwi r7, r3, 60
> 48005094: b9f4fc7c brlid r15, -900 // 48004d10
> <microblaze_unwind_inner>
> 48005098: 11360000 addk r9, r22, r0
> 4800509c: e9e10000 lwi r15, r1, 0
> 480050a0: ea61001c lwi r19, r1, 28
> 480050a4: eac10020 lwi r22, r1, 32
> 480050a8: b60f0008 rtsd r15, 8
> 480050ac: 30210024 addik r1, r1, 36
> 480050b0: e8730004 lwi r3, r19, 4
> 480050b4: 30631f68 addik r3, r3, 8040
> 480050b8: e903003c lwi r8, r3, 60
> 480050bc: e8c30080 lwi r6, r3, 128
> 480050c0: e8e30004 lwi r7, r3, 4
> 480050c4: b9f4fc4c brlid r15, -948 // 48004d10
> <microblaze_unwind_inner>
> 480050c8: 80000000 or r0, r0, r0
> 480050cc: b800ffd0 bri -48 // 4800509c
> 480050d0: 80e10000 or r7, r1, r0
> 480050d4: b8d40008 brlid r6, 8
> 480050d8: 80000000 or r0, r0, r0
> 480050dc: 11130000 addk r8, r19, r0
> 480050e0: 11360000 addk r9, r22, r0
> 480050e4: b9f4fc2c brlid r15, -980 // 48004d10
> <microblaze_unwind_inner>
> 480050e8: 10bf0000 addk r5, r31, r0
> 480050ec: b800ffb0 bri -80 // 4800509c
>

Wow. Your compiler generates code very different from mine. (What's its
pedigree?) With mine, the rtsd and "addik r1, r1, +foo" instructions are
always at the end of each function. So, I had find_frame_creation()
treat these as crossing into a different function, and give up. I will
remove those checks when I respin the patch - they're not needed with my
compiler, and with yours they prevent the backtrace from completing
properly.

Thanks for testing.

------------------------------------------------------------------------
Steven J. Magnani "I claim this network for MARS!
http://www.digidescorp.com Earthling, return my space modulator!"

#include <standard.disclaimer>

2010-04-27 07:55:09

by Michal Simek

[permalink] [raw]
Subject: Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> On Wed, 2010-04-14 at 18:45 +0200, Michal Simek wrote:
>>> diff -uprN a/arch/microblaze/kernel/traps.c
>> b/arch/microblaze/kernel/traps.c
>>> --- a/arch/microblaze/kernel/traps.c 2010-04-09 21:52:36.000000000
>>> +++ b/arch/microblaze/kernel/traps.c 2010-04-12 22:16:01.000000000
> [snip]
>>>
>>> - if (!stack)
>>> - stack = (unsigned long *)&stack;
>>> + if (fp == 0) {
>>> + if (task)
>>> + fp = ((struct thread_info *)
>>> + (task->stack))->cpu_context.r1;
>>> + else {
>>> + /* Pick up caller of dump_stack() */
>>> + fp = (__u32)&sp - 8;
>>> + }
>>> + }
>> just coding style.
>>
>> if (fp == 0)
>> if (task)
>> fp = ((struct thread_info *)
>> (task->stack))->cpu_context.r1;
>> else
>> /* Pick up caller of dump_stack() */
>> fp = (__u32)&sp - 8;
>
> Do you feel strongly about this? I try to always use braces on if/else
> clauses that have more than one line. I've found that the extra
> characters are well worth the savings in debugging time when someone
> tries to extend the clause and forgets to add the braces.

It will work. I like brackets too. I agree that can save a lot of time
with debugging.

I just didn't like that inconsistency in if (task) part.

If is if (task) { ... } else {...} then I am ok with it if
checkpatch.pl doesn't report it as warning.


Michal



--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian

2010-04-27 07:58:09

by Michal Simek

[permalink] [raw]
Subject: Re: [microblaze-uclinux] Re: [PATCH 1/2] microblaze: add stack unwinder

Steven J. Magnani wrote:
> On Mon, 2010-04-26 at 17:34 +0200, Michal Simek wrote:
>
>> I finally looked at it and here is what I found.
>>
>> # ./reboot
>> Restarting system.
>> Machine restart...
>> Machine shutdown...
>> Stack:
>> 4f3ffdc8: 48004aec 00000008
>> 4f3ffdd0: 00000000 00000001 4f3e0150 00000001 4f3ffdec 48004b14 481dc68c
>> 48241318
>> 4f3ffdf0: ffffffff 00001099 00003fff 482f49cc 4801f518 481dc6a4 48241318
>> ffffffff
>> 4f3ffe10: 00001083 00003fff 482f49cc 48020150 481e3048 00000000 000001a2
>> 00000000
>> 4f3ffe30: 00000000 00000000 28121969 48006b38 48240dc0 481d549c 481d53a8
>> 481d549c
>> 4f3ffe50: 00aa4812 481d523c 0000c350 00000035 48029618 4f3ffe70 000001a2
>> 4f96c04c
>> 4f3ffe70: 481d6390 0000c350 4f3fff58 4f3ffee0 481d639c 481d6370 00000009
>> 4f3ffee0
>> 4f3ffe90: 00000000 00000000 00000000 4f3ffee0 48029df4 00000001 4f3e0150
>> 00000001
>> 4f3ffeb0: 00000001 00000001 00000000 00000001 00000001 48029f34 4801b2a8
>> 4800c5e0
>> 4f3ffed0: 00000020 00000000 0000c350 00000000 00000001 00000000 00000000
>> 00000035
>> 4f3ffef0: 00ab0b62 00000035 00aa4812 480294e0 482425c0 00000000 00000000
>> 00000000
>> 4f3fff10: 0000c350 00000001 0000c350 4f3ead40 4f3fff58 00000001 00000000
>> 00000000
>> 4f3fff30: 00000001 4f3e0150 00000001 48006b38 00000000 4f3eaf68 00000000
>> 00010000
>> 4f3fff50: 00000000 00000000 00000000 fee1dead 01234567 00000000 00000000
>> 4f3eaebc
>> 4f3fff70: 00000000 00000000 00000000 fee1dead 28121969 01234567 00000008
>> 00000000
>> 4f3fff90: 00000000 28121969 00000058 00000000 4f3e0320 4f3e0274 00000000
>> 00000000
>> 4f3fffb0: 7fffff82 00000000 00000000 00000000 fee1dead 01234567 00000000
>> 00000000
>> 4f3fffd0: 00000001 4f3e0150 00000001 00000000 00000000 4f96c04c 4f3e0324
>> 000001a0
>> 4f3ffff0: 00000000 00000000 000001a0 00000000
>>
>>
>> Call Trace:
>> Unwinding with PC=480050d4, FP=4f3ffd74
>> [<480050d4>] microblaze_unwind+0xa8/0xc4
>> pc 0x480050ac instr 0x30210024
>> Invalid frame size -36 at 0x480050ac
>> Failed to find previous stack frame
>>
>> Below is the dump - then you can see that 36 is correct.
>> That could be due to different toolchain behavior.
>>
>> Thanks,
>> Michal
>>
>>
>> 4800502c <microblaze_unwind>:
>> 4800502c: b000482f imm 18479
>> 48005030: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
>> 48005034: 3021ffdc addik r1, r1, -36
>> 48005038: fa61001c swi r19, r1, 28
>> 4800503c: fac10020 swi r22, r1, 32
>> 48005040: f9e10000 swi r15, r1, 0
>> 48005044: e8830020 lwi r4, r3, 32
>> 48005048: 12650000 addk r19, r5, r0
>> 4800504c: 99fc2000 brald r15, r4
>> 48005050: 12c60000 addk r22, r6, r0
>> 48005054: b000482f imm 18479
>> 48005058: e86043a8 lwi r3, r0, 17320 // 482f43a8 <mbc>
>> 4800505c: e8830008 lwi r4, r3, 8
>> 48005060: 99fc2000 brald r15, r4
>> 48005064: 80000000 or r0, r0, r0
>> 48005068: be130068 beqid r19, 104 // 480050d0
>> 4800506c: 10b30000 addk r5, r19, r0
>> 48005070: b0004800 imm 18432
>> 48005074: 30c069e0 addik r6, r0, 27104 // 480069e0
>> <_switch_to>
>> 48005078: 165f9800 rsubk r18, r31, r19
>> 4800507c: be120034 beqid r18, 52 // 480050b0
>> 48005080: 11360000 addk r9, r22, r0
>> 48005084: e8730004 lwi r3, r19, 4
>> 48005088: 10b30000 addk r5, r19, r0
>> 4800508c: e903004c lwi r8, r3, 76
>> 48005090: e8e3003c lwi r7, r3, 60
>> 48005094: b9f4fc7c brlid r15, -900 // 48004d10
>> <microblaze_unwind_inner>
>> 48005098: 11360000 addk r9, r22, r0
>> 4800509c: e9e10000 lwi r15, r1, 0
>> 480050a0: ea61001c lwi r19, r1, 28
>> 480050a4: eac10020 lwi r22, r1, 32
>> 480050a8: b60f0008 rtsd r15, 8
>> 480050ac: 30210024 addik r1, r1, 36
>> 480050b0: e8730004 lwi r3, r19, 4
>> 480050b4: 30631f68 addik r3, r3, 8040
>> 480050b8: e903003c lwi r8, r3, 60
>> 480050bc: e8c30080 lwi r6, r3, 128
>> 480050c0: e8e30004 lwi r7, r3, 4
>> 480050c4: b9f4fc4c brlid r15, -948 // 48004d10
>> <microblaze_unwind_inner>
>> 480050c8: 80000000 or r0, r0, r0
>> 480050cc: b800ffd0 bri -48 // 4800509c
>> 480050d0: 80e10000 or r7, r1, r0
>> 480050d4: b8d40008 brlid r6, 8
>> 480050d8: 80000000 or r0, r0, r0
>> 480050dc: 11130000 addk r8, r19, r0
>> 480050e0: 11360000 addk r9, r22, r0
>> 480050e4: b9f4fc2c brlid r15, -980 // 48004d10
>> <microblaze_unwind_inner>
>> 480050e8: 10bf0000 addk r5, r31, r0
>> 480050ec: b800ffb0 bri -80 // 4800509c
>>
>
> Wow. Your compiler generates code very different from mine. (What's its
> pedigree?) With mine, the rtsd and "addik r1, r1, +foo" instructions are
> always at the end of each function. So, I had find_frame_creation()
> treat these as crossing into a different function, and give up. I will
> remove those checks when I respin the patch - they're not needed with my
> compiler, and with yours they prevent the backtrace from completing
> properly.

It is gcc 4.1.2. It is not the first time when I see this construction.

>
> Thanks for testing.

No problem. Looking forward to v2.

Thanks,
Michal

>
> ------------------------------------------------------------------------
> Steven J. Magnani "I claim this network for MARS!
> http://www.digidescorp.com Earthling, return my space modulator!"
>
> #include <standard.disclaimer>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/


--
Michal Simek, Ing. (M.Eng)
w: http://www.monstr.eu p: +42-0-721842854
Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/
Microblaze U-BOOT custodian