linux panic 记录文件
1 设计1.1 技术使用kdump(kexec)引导第二个内核启动,在第二个内核中捕捉panic信息,同时将系统内核的内存镜像存储到/proc/vmcore文件中,由于此文件与内存大小一致,故不能直接使用此文件。使用kexec 的一个工具vmcore-dmesg,可以将/proc/vmcore转换为dmesg的信息,然后存储到文件中即可。1.2 步骤1.2.1
1 设计
1.1 技术
使用kdump(kexec)引导第二个内核启动,在第二个内核中捕捉panic信息,同时将系统内核的内存镜像存储到/proc/vmcore文件中,由于此文件与内存大小一致,故不能直接使用此文件。使用kexec 的一个工具vmcore-dmesg,可以将/proc/vmcore转换为dmesg的信息,然后存储到文件中即可。
1.2 步骤
1.2.1 制作小内核
制作一个小内核,即crash kernel,专门捕捉panic信息,必须足够小,如此可以少占用内存。
根据内核文件kdump.txt,编译小内核时,编译参数选中HIGHMEM、CRASH_DUMP、PROC_VMCORE、RELOCABLE,详细参考内核文档。
小内核使用自己的config文件编译,目前小内核加上文件系统总共2.1M(已经包含了网络和部分网卡驱动)。
1.2.2 修改大内核
大内核,即系统内核,增加KEXEC编译选项,注意openwrt中不仅仅选中内核参数,还需要选中openwrt的“Global build settings”的KEXEC编译参数,之前为此搞了近一个星期。
1.2.3 修改grub
引导大内核时,需要指定参数crashkernel,由于小内核足够小,目前仅仅预留32M。
小内核启动后,只能使用crashkernel指定的内存区域,大内核的内存内容不会被修改。
1.2.4 启动脚本
大内核启动脚本,启动时调用命令kexec -p PATH_TO_CRASH_KERNEL,如此就装载了panic时的小内核,目前不需要传递任何参数。
小内核启动脚本,启动时调用命令vmcore-dmesg /proc/vmcore 输出panic日志到文件,然后系统重启。
大内核启动参数中有BOOT_IMAGE,故系统启动时使用这个标志区分大小内核。
2 尝试
在实现此功能的过程中,作了很多尝试,之前不知道有vmcore-dmesg这个工具,如此大的vmcore文件在系统中无法使用,故刚开始否定了kdump的方案。
2.1 ramoops
内核已经支持,但在使用时发现重启后无法获得panic信息。
后来模仿ramoops,自己设计了一个内核模块,预留一块内存,使用kmsg_dump_register获得panic时的字符串信息,然后将这些信息写入预留的内存区域,同样在系统启动后这块区域的内容消失。
2.2 pstore
内核已经有这方面的考虑,pstore,即persistent store,顾名思义,即可以保存的存储空间,试验时发现系统重启后无法保存写入的消息。
后来仔细查看内核文档,发现pstore机制需要硬件支持,x86上有APEI,在其他平台上也有相应的硬件支持,但我们目前的设备上不支持。
2.3 makedumpfile
此程序可以对vmcore作压缩动作,而且可以直接输出dmesg字符串信息,但在编译时发现依赖的库太多,故作罢。
3 原理
此章节简单描述kexec、kdump、vmcore-dmesg的原理。
3.1 kexec
这个东西网上的资料很多,目前有两种工作方式。
1. kexec-l
需要使用kexec –e命令手动启动小内核, 这种方式非常适合内核切换调试。
2. kexec-p
在系统panic时,自动进入小内核
这种方式必须设定crashkernel,小内核只会使用这个指定的内存空间,因此不会污染大内核的内存数据。
3.2 ELF
大内核的内存数据存储为elf文件格式,其elf文件头被当作内核启动参数传递到小内核。
此elf格式为CORE,可以看作是可执行文件(Execution View),有若干个programheader,由于我们只关注dmesg的信息,因此只需要关注PT_NOTE类型的program header。
图1:ELF格式
3.2.1 PT_NOTE
PT_NOTE类型的program header,是由一个固定的头部加多个NOTE结点组成的,每个NOTE结点结构如下(Elf32_word为4字节长度):
typedef structelf32_note {
Elf32_Word n_namesz; /* Name size */
Elf32_Word n_descsz; /* Content size */
Elf32_Word n_type; /* Content type */
} Elf32_Nhdr;
NOTE结构后面直接是字符串形式的name和desc,注意4字节对齐。
对于vmcore信息来说,name固定为VMCOREINFO,desc为内核写入的字符串。
3.3 log
内核的log信息使用一个类似“字符串环”结构,使用4个全局变量:
log_buf,起始指针
log_end,下一个写入内容的指针
log_buf_len,字符串环总长,默认为128K
logged_chars,未读取内容的长度
3.4 dmesg
这就是我们最关注的panic对应的dmesg内容了。
3.4.1 生成
3.4.1.1 系统初始化
crash_save_vmcoreinfo_init函数初始化时调用,它会初始化vmcoreinfo_data内容,此内容就是ELF文件中的PT_NOTE结构的desc信息,这些内容在初始化时就可以得到。
注意“字符串环”,系统初始化时那4个全局变量的地址也被存储到ELF文件中去了,因此后续panic时对这个全局变量指向内容的修改,均可以通过此“字符串环”获得。
3.4.1.2 系统panic
crash_kexec函数会被执行,其中调用crash_save_vmcoreinfo函数,将CRASHTIME字符串内容数据写入“字符串环”,然后加载小内核并启动小内核。
3.4.2 获取
小内核启动后,会在vmcore.c中将ELF内存映射到/proc/vmcore文件,供应用层访问。此过程比较简单,主要是根据内核启动参数是否带有“elfcorehdr=”,如果有这个参数表明是小内核启动,会将这个地址作为ELF头部进行深度解析。
3.4.3 分析
vmcore-dmesg.c这个文件,使用vmcore这个文件作为输入,输出是dmesg的信息。分析ELF文件,获取PT_NOTE的program header,再关注NOTE结点中的“VMCOREINFO”的name字段,获取其中的desc字符串信息输出到终端即可。
更多推荐
所有评论(0)