123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- /*
- * arch/xtensa/kernel/stacktrace.c
- *
- * 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.
- *
- * Copyright (C) 2001 - 2013 Tensilica Inc.
- */
- #include <linux/export.h>
- #include <linux/sched.h>
- #include <linux/stacktrace.h>
- #include <asm/stacktrace.h>
- #include <asm/traps.h>
- void walk_stackframe(unsigned long *sp,
- int (*fn)(struct stackframe *frame, void *data),
- void *data)
- {
- unsigned long a0, a1;
- unsigned long sp_end;
- a1 = (unsigned long)sp;
- sp_end = ALIGN(a1, THREAD_SIZE);
- spill_registers();
- while (a1 < sp_end) {
- struct stackframe frame;
- sp = (unsigned long *)a1;
- a0 = *(sp - 4);
- a1 = *(sp - 3);
- if (a1 <= (unsigned long)sp)
- break;
- frame.pc = MAKE_PC_FROM_RA(a0, a1);
- frame.sp = a1;
- if (fn(&frame, data))
- return;
- }
- }
- #ifdef CONFIG_STACKTRACE
- struct stack_trace_data {
- struct stack_trace *trace;
- unsigned skip;
- };
- static int stack_trace_cb(struct stackframe *frame, void *data)
- {
- struct stack_trace_data *trace_data = data;
- struct stack_trace *trace = trace_data->trace;
- if (trace_data->skip) {
- --trace_data->skip;
- return 0;
- }
- if (!kernel_text_address(frame->pc))
- return 0;
- trace->entries[trace->nr_entries++] = frame->pc;
- return trace->nr_entries >= trace->max_entries;
- }
- void save_stack_trace_tsk(struct task_struct *task, struct stack_trace *trace)
- {
- struct stack_trace_data trace_data = {
- .trace = trace,
- .skip = trace->skip,
- };
- walk_stackframe(stack_pointer(task), stack_trace_cb, &trace_data);
- }
- EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
- void save_stack_trace(struct stack_trace *trace)
- {
- save_stack_trace_tsk(current, trace);
- }
- EXPORT_SYMBOL_GPL(save_stack_trace);
- #endif
- #ifdef CONFIG_FRAME_POINTER
- struct return_addr_data {
- unsigned long addr;
- unsigned skip;
- };
- static int return_address_cb(struct stackframe *frame, void *data)
- {
- struct return_addr_data *r = data;
- if (r->skip) {
- --r->skip;
- return 0;
- }
- if (!kernel_text_address(frame->pc))
- return 0;
- r->addr = frame->pc;
- return 1;
- }
- unsigned long return_address(unsigned level)
- {
- struct return_addr_data r = {
- .skip = level + 1,
- };
- walk_stackframe(stack_pointer(NULL), return_address_cb, &r);
- return r.addr;
- }
- EXPORT_SYMBOL(return_address);
- #endif
|