forked from Minki/linux
4f9b514b76
For the current task, the kernel stack would only tell the last time the process was rescheduled, if ever. Use the current stack pointer for the current task. Otherwise, every once in a while, the stacktrace printed when reading /proc/self/stack would look like the process is running in userspace, while it's not, which some may consider as a bug. This is also consistent with some other architectures, like x86 and arm, at least. Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
79 lines
1.7 KiB
C
79 lines
1.7 KiB
C
/*
|
|
* Stack trace utility
|
|
*
|
|
* Copyright 2008 Christoph Hellwig, IBM Corp.
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/debug.h>
|
|
#include <linux/stacktrace.h>
|
|
#include <asm/ptrace.h>
|
|
#include <asm/processor.h>
|
|
|
|
/*
|
|
* Save stack-backtrace addresses into a stack_trace buffer.
|
|
*/
|
|
static void save_context_stack(struct stack_trace *trace, unsigned long sp,
|
|
struct task_struct *tsk, int savesched)
|
|
{
|
|
for (;;) {
|
|
unsigned long *stack = (unsigned long *) sp;
|
|
unsigned long newsp, ip;
|
|
|
|
if (!validate_sp(sp, tsk, STACK_FRAME_OVERHEAD))
|
|
return;
|
|
|
|
newsp = stack[0];
|
|
ip = stack[STACK_FRAME_LR_SAVE];
|
|
|
|
if (savesched || !in_sched_functions(ip)) {
|
|
if (!trace->skip)
|
|
trace->entries[trace->nr_entries++] = ip;
|
|
else
|
|
trace->skip--;
|
|
}
|
|
|
|
if (trace->nr_entries >= trace->max_entries)
|
|
return;
|
|
|
|
sp = newsp;
|
|
}
|
|
}
|
|
|
|
void save_stack_trace(struct stack_trace *trace)
|
|
{
|
|
unsigned long sp;
|
|
|
|
sp = current_stack_pointer();
|
|
|
|
save_context_stack(trace, sp, current, 1);
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
|
|
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
|
{
|
|
unsigned long sp;
|
|
|
|
if (tsk == current)
|
|
sp = current_stack_pointer();
|
|
else
|
|
sp = tsk->thread.ksp;
|
|
|
|
save_context_stack(trace, sp, tsk, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|
|
|
|
void
|
|
save_stack_trace_regs(struct pt_regs *regs, struct stack_trace *trace)
|
|
{
|
|
save_context_stack(trace, regs->gpr[1], current, 0);
|
|
}
|
|
EXPORT_SYMBOL_GPL(save_stack_trace_regs);
|