linux ops panic 解析和定位

一、oops输出解析

https://zhuanlan.zhihu.com/p/680156398

Oops 信息包含以下几部分内容:

  • 一段文本描述信息,比如类似“Unable to handle kernel NULL pointer dereference at virtual address 00000000”的信息,它说明了发生的是哪类错误。
  • Oops 信息的序号,比如是第 1 次、第 2 次等。这些信息与下面类似,中括号内的数据表示序号。Internal error: Oops: 805 [#1]
  • 内核中加载的模块名称,也可能没有,以下面字样开头。Modules linked in:
  • 发生错误的 CPU 的序号,对于单处理器的系统,序号为 0,比如:CPU: 0Not tainted (2.6.22.6 #36)
  • 发生错误时 CPU 的各个寄存器值。
  • 当前进程的名字及进程 ID,比如:Process swapper (pid: 1, stack limit = 0xc0480258)这并不是说发生错误的是这个进程,而是表示发生错误时,当前进程是它。错误可能发生在内核代码、驱动程序,也可能就是这个进程的错误。
  • 栈信息。
  • 出错指令附近的指令的机器码,比如(出错指令在小括号里)
Code: e24cb004 e24dd010 e59f34e0 e3a07000 (e5873000)

栈回溯信息,可以从中看出函数调用关系,形式如下:

Backtrace:
[<c001a6f4>] (s3c2410fb_probe+0x0/0x560) from [<c01bf4e8>] (platform_drv_
probe+0x20/0x24)
...


(1)__die()

arch/arm64/kernel/traps.c
 
static int __die(const char *str, int err, struct pt_regs *regs)
{
	static int die_counter;
	int ret;
 
	pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
		 str, err, ++die_counter);
 
	/* trap and error numbers are mostly meaningless on ARM */
	ret = notify_die(DIE_OOPS, str, regs, err, 0, SIGSEGV);
	if (ret == NOTIFY_STOP)
		return ret;
 
	print_modules();
	show_regs(regs);
 
	dump_kernel_instr(KERN_EMERG, regs);
 
	return ret;
}

打印 EMERG 的log,Internal error: oops.....;

  • notify_die() 会通知所有对 Oops 感兴趣的模块并进行callback;
  • print_modules() 打印模块状态不为 MODULE_STATE_UNFORMED 的模块信息;
  • show_regs() 打印PC、LR、SP 等寄存器的信息,同时打印调用堆栈信息;
  • dump_kernel_instr() 打印 pc指针和前4条指令;

这里不过多的剖析,感兴趣的可以查看下源码。这里需要注意的是 notify_die() 会通知所有的Oops 感兴趣的模块,模块会通过函数 register_die_notifier() 将callback 注册到全局结构体变量 die_chain 中(多个模块注册进来形成一个链表),然后在通过 notify_die() 函数去解析这个 die_chain,并分别调用callback:

 

panic_print 默认值为 0,可以通过 /proc/sys/kernel/panic_print 节点配置,当 panic 发生的时候,用户可以通过如下bit 位配置打印系统信息:

  • bit 0:打印所有的进程信息;
  • bit 1:打印系统内存信息;
  • bit 2:打印定时器信息;
  • bit 3:打印当 CONFIG_LOCKEDP 打开时的锁信息;
  • bit 4:打印所有 ftrace;
  • bit 5:打印串口所有信息;

三、linux内核异常常用分析方法

  1. 异常地址是否在0附近,确认是否是空指针解引用问题
  2. 异常地址是否在iomem映射区,确认是否是设备访问总线异常问题,如PCI异常导致的地址访问异常
  3. 异常地址是否在stack附近,如果相邻,要考虑是否被踩
  4. 比较delay reset/nmi watchdog等多种机制打印的栈信息,看看pc是否在动,确定是否是死锁
  5. 用SysRq判断是真死还是假死
  6. 通过反汇编获得发生异常的C代码段和函数,查找开源社区是否已有补丁修复

下面分别通过PowerPC和Mips64的2个异常例子详细讲解分析过程。

 

二、工具

1、objdump

嵌入式环境下  ${CROSS_COMPILE}objdump -dS <path/to/kernel-src/>/vmlinux > vmlinux.disas


//查看地址
#grep oops_tryv1 /proc/modules
oops_tryv1 24576 1 - Loading 0xffffffffc0223000 (OE+)
//使用地址
#objdump -dS --adjust-vma=0xffffffffc0223000 ./oops_tryv1.ko > oops_tryv1.disas

 

  • RIP的地址0x88
  • ffffffffc071b000 + 0x88 = ffffffffc071b088
  • 即可看到出问题的地方*(int *)val = 'x';

2、gdb

# gdb -q ./oops_tryv1.ko
Reading symbols from ./oops_tryv1.ko...
(gdb) list *try_oops_init+0x88
0x12f is in try_oops_init (/home/wy/misc/kernel/Linux-Kernel-Debugging/ch7/oops_tryv1/oops_tryv1.c:60).

 

3、addr2line       

addr2line -e </path/to/>vmlinux -p -f <faulting_kernel_address>

addr2line -e ./oops_tryv1.o -p -f 0x88

4、decodecode


# /kernel/linux-5.15/scripts/decodecode < dmesg_oops_buginworkq.txt

 

 

 

5、faddr2line

usage: faddr2line [--list] <object file> <func+offset>

#grep CONFIG_RANDOMIZE_BASE /boot/config-5.15.0-47-generic

CONFIG_RANDOMIZE_BASE=y
# <>/scripts/faddr2line ./oops_tryv1.ko try_oops_init+0x88
try_oops_init+0x88/0x100:
try_oops_init at <...>/oops_tryv1/oops_tryv1.c:58

 

posted @ 2024-04-02 14:46  skyycj  阅读(5)  评论(0编辑  收藏  举报