mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-18 09:44:18 +08:00
9702970c7b
This reverts commit e306dfd06f
.
With this patch applied, we were the only architecture making this sort
of adjustment to the PC calculation in the unwinder. This causes
problems for ftrace, where the PC values are matched against the
contents of the stack frames in the callchain and fail to match any
records after the address adjustment.
Whilst there has been some effort to change ftrace to workaround this,
those patches are not yet ready for mainline and, since we're the odd
architecture in this regard, let's just step in line with other
architectures (like arch/arm/) for now.
Cc: <stable@vger.kernel.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
127 lines
2.9 KiB
C
127 lines
2.9 KiB
C
/*
|
|
* Stack tracing support
|
|
*
|
|
* Copyright (C) 2012 ARM Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/export.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/stacktrace.h>
|
|
|
|
#include <asm/stacktrace.h>
|
|
|
|
/*
|
|
* AArch64 PCS assigns the frame pointer to x29.
|
|
*
|
|
* A simple function prologue looks like this:
|
|
* sub sp, sp, #0x10
|
|
* stp x29, x30, [sp]
|
|
* mov x29, sp
|
|
*
|
|
* A simple function epilogue looks like this:
|
|
* mov sp, x29
|
|
* ldp x29, x30, [sp]
|
|
* add sp, sp, #0x10
|
|
*/
|
|
int notrace unwind_frame(struct stackframe *frame)
|
|
{
|
|
unsigned long high, low;
|
|
unsigned long fp = frame->fp;
|
|
|
|
low = frame->sp;
|
|
high = ALIGN(low, THREAD_SIZE);
|
|
|
|
if (fp < low || fp > high - 0x18 || fp & 0xf)
|
|
return -EINVAL;
|
|
|
|
frame->sp = fp + 0x10;
|
|
frame->fp = *(unsigned long *)(fp);
|
|
frame->pc = *(unsigned long *)(fp + 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void notrace walk_stackframe(struct stackframe *frame,
|
|
int (*fn)(struct stackframe *, void *), void *data)
|
|
{
|
|
while (1) {
|
|
int ret;
|
|
|
|
if (fn(frame, data))
|
|
break;
|
|
ret = unwind_frame(frame);
|
|
if (ret < 0)
|
|
break;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(walk_stackframe);
|
|
|
|
#ifdef CONFIG_STACKTRACE
|
|
struct stack_trace_data {
|
|
struct stack_trace *trace;
|
|
unsigned int no_sched_functions;
|
|
unsigned int skip;
|
|
};
|
|
|
|
static int save_trace(struct stackframe *frame, void *d)
|
|
{
|
|
struct stack_trace_data *data = d;
|
|
struct stack_trace *trace = data->trace;
|
|
unsigned long addr = frame->pc;
|
|
|
|
if (data->no_sched_functions && in_sched_functions(addr))
|
|
return 0;
|
|
if (data->skip) {
|
|
data->skip--;
|
|
return 0;
|
|
}
|
|
|
|
trace->entries[trace->nr_entries++] = addr;
|
|
|
|
return trace->nr_entries >= trace->max_entries;
|
|
}
|
|
|
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
|
{
|
|
struct stack_trace_data data;
|
|
struct stackframe frame;
|
|
|
|
data.trace = trace;
|
|
data.skip = trace->skip;
|
|
|
|
if (tsk != current) {
|
|
data.no_sched_functions = 1;
|
|
frame.fp = thread_saved_fp(tsk);
|
|
frame.sp = thread_saved_sp(tsk);
|
|
frame.pc = thread_saved_pc(tsk);
|
|
} else {
|
|
data.no_sched_functions = 0;
|
|
frame.fp = (unsigned long)__builtin_frame_address(0);
|
|
frame.sp = current_stack_pointer;
|
|
frame.pc = (unsigned long)save_stack_trace_tsk;
|
|
}
|
|
|
|
walk_stackframe(&frame, save_trace, &data);
|
|
if (trace->nr_entries < trace->max_entries)
|
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
|
}
|
|
|
|
void save_stack_trace(struct stack_trace *trace)
|
|
{
|
|
save_stack_trace_tsk(current, trace);
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
|
#endif
|