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内核异常常用分析方法
- 异常地址是否在0附近,确认是否是空指针解引用问题
- 异常地址是否在iomem映射区,确认是否是设备访问总线异常问题,如PCI异常导致的地址访问异常
- 异常地址是否在stack附近,如果相邻,要考虑是否被踩
- 比较delay reset/nmi watchdog等多种机制打印的栈信息,看看pc是否在动,确定是否是死锁
- 用SysRq判断是真死还是假死
- 通过反汇编获得发生异常的C代码段和函数,查找开源社区是否已有补丁修复
下面分别通过PowerPC和Mips64的2个异常例子详细讲解分析过程。
嵌入式环境下 ${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';
# 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).
addr2line -e </path/to/>vmlinux -p -f <faulting_kernel_address>
addr2line -e ./oops_tryv1.o -p -f 0x88
# /kernel/linux-5.15/scripts/decodecode < dmesg_oops_buginworkq.txt
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
标签:ops,die,打印,信息,oops,linux,tryv1,bit,panic From: https://www.cnblogs.com/ycjstudy/p/18108861