方法一:可以通过/proc/线程ID/stack来获取指定线程当前的调用栈,但是该方案存在限制:可能不能准确获取出问题的时候该线程的调用栈
方法二:通过内核接口save_stack_trace_tsk和%pS,在程序检测到异常的时候打印出对应的调用栈用于问题定位,具体的方法(从内核/proc文件系统处理打印调用栈的proc_pid_stack中简单修改而来):
//TODO: enable CONFIG_STACKTRACE
#include <linux/stacktrace.h>
#define MAX_STACK_TRACE_DEPTH 64
static int _kprink_stack(struct task_struct *task) { struct stack_trace trace; unsigned long *entries; int err; entries = kmalloc(MAX_STACK_TRACE_DEPTH * sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; trace.nr_entries = 0;/*调用返回后,为记录的调用栈中的有效符号的个数*/ trace.max_entries = MAX_STACK_TRACE_DEPTH;/*传入的trace.entries的大小,save_stack_trace_tsk最多保持该数量的调用栈符号*/ trace.entries = entries;/*返回的符号地址保存在这里*/ trace.skip = 0;/*从调用栈的顶开始,忽略的调用栈符号数量*/ //err = lock_trace(task); if (!err) {
unsigned int i; save_stack_trace_tsk(task, &trace); for (i = 0; i < trace.nr_entries; i++) { pr_notice("[<%pK>] %pS ", (void *)entries[i], (void *)entries[i]); } //unlock_trace(task); } kfree(entries); return err; }
说明:其中的%pS用于打印内核符号,内核的相关处理见printk--->vprintk--->vscnprintf-->vsnprintf-->symbol_string的处理
————————————————
版权声明:本文为CSDN博主「phenix_lord」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/phenix_lord/article/details/41624229
方法三:使用内核提供的API可以直接打印指定进程的stack信息, 注意入参,regs可以赋值为 NULL,如果tsk为NULL则dump当前进程,如果不为NULL,则打印指定进程
void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) { struct stackframe frame; int skip = 0; pr_debug("%s(regs = %p tsk = %p) ", __func__, regs, tsk); if (regs) { if (user_mode(regs)) return; skip = 1; } if (!tsk) tsk = current; if (!try_get_task_stack(tsk)) return; if (tsk == current) { frame.fp = (unsigned long)__builtin_frame_address(0); frame.pc = (unsigned long)dump_backtrace; } else { /* * task blocked in __switch_to */ frame.fp = thread_saved_fp(tsk); frame.pc = thread_saved_pc(tsk); } #ifdef CONFIG_FUNCTION_GRAPH_TRACER frame.graph = tsk->curr_ret_stack; #endif printk("Call trace: "); do { /* skip until specified stack frame */ if (!skip) { dump_backtrace_entry(frame.pc); } else if (frame.fp == regs->regs[29]) { skip = 0; /* * Mostly, this is the case where this function is * called in panic/abort. As exception handler's * stack frame does not contain the corresponding pc * at which an exception has taken place, use regs->pc * instead. */ dump_backtrace_entry(regs->pc); } } while (!unwind_frame(tsk, &frame)); put_task_stack(tsk); }
EXPORT_SYMBOL(dump_backtrace)
https://elixir.bootlin.com/linux/v4.19.180/source/arch/arm64/kernel/traps.c#L101