Linux pstore实现自动“抓捕”内核崩溃日志实例分析

49次阅读
没有评论

共计 6862 个字符,预计需要花费 18 分钟才能阅读完成。

今天就跟大家聊聊有关 Linux pstore 实现自动“抓捕”内核崩溃日志实例分析,可能很多人都不太了解,为了让大家更加了解,丸趣 TV 小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

简介

pstore 文件系统 (是的,这是个文件系统) 是 Persistent Storage 的缩写,最早在 2010 年由 Tony Luck   设计并合入 Linux 主分支,设计的初衷是在内核 Panic/Oops 时能自动转存内核日志(log_buf),在 Panic 重启后,把转存的日志以文件形式呈现到用户空间以分析内核崩溃问题。

这对分析那种小概率且没办法抓到现场的问题非常实用,尤其是现在智能互联网的设备逐渐普及的时候,远端的设备可以自己捕抓崩溃日志再通过网络传输到服务器,维护人员就可以根据收集来的日志定位和解决问题,然后通过 OTA 让设备升级迭代。

根据网上搜寻的资料,在 pstore 文件系统之前其实有不少类似的实现。

apanic

Android 最早的 panic 信息记录的方案。在 linux  2.6 的安卓的内核中找到,却没有提交到社区,后来被放弃维护了。网上找不到放弃的原因,我自己猜测是因为其只适用于 mtd  nand,然而现在的 Android 基本用的都是 emmc。apanic 应该是 Android Panic 的缩写吧,可以实现在内核崩溃时,把日志转存到 mtd  nand。

ramoops

这里指的是最早的 ramoops 实现,在最新代码已经整合入 pstore 中,以 pstore/ram 的后端形式存在。ramoops 可以把日志转存到重启不掉电的 ram 中。这里对 ram 有一点要求,即使重启 ram 的数据也不能丢失。

crashlog

这是 openwrt 提供的内核 patch,并没有提交到内核社区。它也是基于 ram,只能转存 Panic/Oops 的日志。

mtdoops

MTD 子系统支持的功能,与 pstore 非常相似,只支持转存 Panic/Oops 日志,不能以文件呈现,需要用户自行解析整个 MTD 分区。(因为功能的相似,我实现了 mtdpstore 用于替代 mtdoops)

kdump

如果说 pstore 是个轻量级的内核崩溃日志转存的方案,kdump 则是一个重量级的问题分析工具。在崩溃时,由 kdump 产生一个用于捕抓当前信息的内核,该内核会收集内存所有信息到 dump  core 文件中。在重启后,捕抓到的信息保存在特定的文件中。类似的还有 netdump 和 diskdump。kdump 的方案适用于服务器这种有大量资源的设备,功能也非常强大,但对嵌入式设备非常不友好。

pstore 经过长期迭代,除了转存 Panic/Oops 的日志之外(dmesg 前端),还支持 pmsg、console 和 ftrace 的前端,除了 pstore/ram 的后端之外,还有我设计的 pstore/blk 后端,除了支持转存到 ram 之外,还有 block  device 和 mtd device。

pstore 的前端,是指转存的日志类型,pstore 的后端,是指转存到什么类型的设备。

目前支持以下几个前端:

dmesg:主要是转存 Panic/Oops 时 log_buf 里面的内核日志

pmsg:提供给用户空间存储日志的入口,在 Android 里有看到被用于存储系统的日志。

console:终端日志

ftrace:function trace 的信息

目前支持以下几种后端:

pstore/ram:Persistent Ram,重启不会丢数据的内存

pstore/blk:(v5.8 以后的版本)所有可写的块设备,例如磁盘、U 盘、emmc、NFTL nand 等

mtd device:(v5.8 以后的版本)mtd 设备,例如 mtd nand。(mtd 设备的支持依赖于 pstore/blk   后端,准确来说不是一种独立后端)

怎么用

就像把大象装入冰箱只需要打开冰箱,把大象放进去,关上冰箱门的 3 个步骤,使用 pstore 也只需要 3 个步骤:

使能 pstore

挂载 pstore 文件系统

读取 转存的日志文件

详细的说明可以看源码上的文档,本文只做基本功能的介绍。

Documentation/admin-guide/ramoops.rst

Documentation/admin-guide/pstore-blk.rst

使能

在 menuconfig 中选择内核 pstore 模块

$ make menuconfig |-  File systems |-  Miscellaneous filesystems |-  Persistent store support |-  Log kernel console messages # console  前端  |-  Log user space messages # pmsg  前端  |-  Persistent function tracer # ftrace  前端  |-  Log panic/oops to a RAM buffer # pstore/ram  后端  |-  Log panic/oops to a block device # pstore/blk  后端

上述两个后端 2 选 1 即可,前端就根据自己的需求选择,至于 dmesg 前端,默认使能没得选。如果希望用在 mtd 设备上,还需要选择 mtdpstore 模块:

$ make menuconfig |-  Device Drivers |-  Memory Technology Device (MTD) support |-  Log panic/oops to an MTD buffer based on pstore

选上就可以用了? 虽然我非常想说“是的”,但事实却有点“骨感”。即使所有前端都使用默认配置,pstore/ram 至少也需要知道可用的内存范围吧?pstore/blk 至少也需要知道使用哪个块设备吧?

pstore/ram 支持 模块参数(cmdline)、设备树、和 Platform Data 的 3 种配置方式,从代码来看,优先级关系是:模块参数  Platform Data 设备树。

pstore/blk 支持 Kconfig 和 模块参数 (cmdline) 的两种配置方式,且模块参数比 Kconfig 有更高的优先级。

pstore/ram 我接触也不多,直接介绍 pstore/blk 的使用方法。对新同学来说,请忽略一大堆乱七八糟的属性配置(使用默认值),只需要告诉 pstore/blk 后端使用哪个块设备即可。

在 Kconfig 中配置:

$ make menuconfig |-  File systems |-  Miscellaneous filesystems |-  Persistent store support |-  Log panic/oops to a block device # pstore/blk  后端  |-  () block device identifier #  使用哪个块设备?

如果使用 cmdline,可以这么写:

pstore_blk.blkdev=XXXX

或者以模块加载:

$ sudo insmod pstore_blk.ko blkdev=XXX

这里的块设备可以是代表整个磁盘的 sda,也可以是代表某个分区的 mmcblk0p4。虽然支持 7 种变体,但常用的还是两种:

/dev/ disk_name : 例如,使用 U 盘的第 2 个分区,则是 /dev/sdb2

major : minor:例如,mmc 设备第 6 个分区,则是 179:6

形式大概是这样:

$ sudo insmod pstore_blk.ko blkdev=/dev/sdb2

或者

$ cat /proc/cmdline .... pstore_blk.blkdev=179:6 ...

如果是 mtd 设备,可以直接指定 mtd 分区名或者编号,例如:

pstore_blk.blkdev=pstore #  假设存在名为 pstore 的 MTD 分区

OK,对新同学来说,到这里配置就够了。可以从我的 github(见参考链接 [2]) 上看到我之前是怎么测试的。如果需要知道每个配置项的作用,还是看内核文档吧(ramoops.rst   或 pstore_blk.rst),或者在 Kconfig 中按 h 显示相关配置项的说明。

挂载

在使能且正确配置设备后,启动的时候应该会有这样的日志:

pstore_zone: registered pstore_blk as backend for kmsg(Oops,panic_write) pstore: Registered pstore_blk as persistent store backend

这代表 pstore 找到了设备且正常注册。接下来,我们还需要通过挂载的形式触发 pstore 从设备读取数据。常见的挂载是这样的:

mount -t pstore pstore /sys/fs/pstore

挂载后,通过 mount 能看到类似这样的信息:

# mount ... pstore on /sys/fs/pstore type pstore (rw,relatime) ...

如果曾经触发过崩溃日志,在挂载点应该有类似这样的文件:

# ll /sys/fs/pstore ... -r--r--r-- 1 root root 15521 Jan 1 00:06 dmesg-pstore_blk-0 ...

如果需要验证,咱们可以这样主动触发内核崩溃:

# echo c   /proc/sysrq-trigger

我是在 U 盘、SD 卡、mmc、nand 上验证的,maintainer Kees Cook   提供了另外一种基于 loop 的验证方法,实现用文件模拟块设备。当然这方法不适用于转存 Panic 日志,只能用于 Oops 或者其他前端:

# insmod pstore.ko compress=off # insmod pstore_zone.ko # truncate pstore-blk.raw --size 100M # losetup -f --show pstore-blk.raw /dev/loop0 # insmod pstore_blk.ko blkdev=/dev/loop0 kmsg_size=16 console_size=64 best_effort=on

读取

经过上述的挂载后,可以在挂载点看到转存的日志文件。既然是文件,肯定支持文件的一系列操作,例如读取、删除。

root@TinaLinux:/sys/fs/pstore# head -n 10 dmesg-pstore_blk-1 Oops: Total 2 times Oops#1 Part1  6 [ 2.743794] Bluetooth: RFCOMM socket layer initialized  6 [ 2.743813] Bluetooth: RFCOMM ver 1.11  6 [ 2.743822] 8021q: 802.1Q VLAN Support v1.8  3 [ 2.751766] reg-virt-consumer reg-virt-consumer.1: Failed to obtain supply  drivevbus : -517  3 [ 2.752330] reg-virt-consumer reg-virt-consumer.1: Failed to obtain supply  drivevbus : -517  5 [ 2.752742] ubi0: attaching mtd4  5 [ 2.890302] random: crng init done  5 [ 2.965927] ubi0: scanning is finished root@TinaLinux:/sys/fs/pstore# ll drwxr-x--- 2 root root 0 Jan 1 00:11 . drwxr-xr-x 5 root root 0 Jan 1 00:11 .. -r--r--r-- 1 root root 15521 Jan 1 00:06 dmesg-pstore_blk-0 -r--r--r-- 1 root root 15128 Jan 1 00:11 dmesg-pstore_blk-1 root@TinaLinux:/sys/fs/pstore# rm dmesg-pstore_blk-1 root@TinaLinux:/sys/fs/pstore# ll drwxr-x--- 2 root root 0 Jan 1 00:13 . drwxr-xr-x 5 root root 0 Jan 1 00:11 .. -r--r--r-- 1 root root 15521 Jan 1 00:06 dmesg-pstore_blk-0

对 dmesg 前端的 Panic/Oops 日志,pstore 会自动添加两行统计信息。例如:

Oops: Total 2 times #  表示触发了 Oops,且是自系统安装后第一次启动以来第 2 次触发 Oops。 Oops#1 Part1 #  表示这是上一次运行期间第 1 次触发 Oops 的日志。

可以发现,第一行是累计总的触发次数,第二行是上一次启动触发的次数。

每个文件名的格式都是 前端名 – 后端名 -,例如 dmesg-pstore_blk- 1 表示 dmesg 前端,pstore_blk 后端以及是 dmesg 前端的第 1 个 zone 的日志。

当然,除了 dmesg 前端外,其他前端的名字大概是这样的:

# ll -r--r--r-- 1 root root 31 1 月  15 11:53 console-pstore-blk-0 -r--r--r-- 1 root root 3666 1 月  15 11:53 demsg-pstore-blk-0 -r--r--r-- 1 root root 65524 1 月  15 11:53 ftrace-pstore-blk-0 -r--r--r-- 1 root root 9 1 月  15 11:53 pmsg-pstore-blk-0

除此之外,每个文件的时间戳表示 崩溃触发的时间。上例中,由于系统并没有实现同步更新系统时间,所以时间戳不合理。

展望未来

正如我前文说的,pstore 在物联网设备逐渐普及的现在,能发挥很大的作用,例如智能音箱和扫地机已经用起来了。

全功能支持

到目前为止,不管是块设备还是 mtd 设备,社区的代码都没能做到 pstore 的全部前端的支持。

设备 dmesg(Oops)dmesg(Panic)pmsgconsoleftrace 块设备 YNYYYMTD 设备 YYNNNram 设备 YYYYY

块设备如果需要记录 Panic 日志,需要提供一个在 Panic 时写块设备的接口。我在全志的 mmc 和 nand 驱动中实现了这样的接口,却因为种种原因不适合提交到社区。社区块驱动的适配寄希望于更多同学的努力了。

MTD 设备很早前就有了 panic_write()的定义,因此可以支持 Panic 日志转存。不支持其他前端,则是因为其擦写的物理特性。对 pmsg,console,ftrace 等这些不能页对齐写入的前端,还需要更多的适配工作。

迁移 pstore/ram

在当前 pstore 的目录结构是这样的:

$ tree fs/pstore fs/pstore/ ├── blk.c # pstore/blk  后端的实现  ├── ftrace.c # ftrace  前端的实现  ├── inode.c # pstore  文件系统的注册与操作  ├── internal.h ├── Kconfig ├── Makefile ├── platform.c # pstore  前后端功能的核心  ├── pmsg.c # pmsg  前端的实现  ├── ram.c # pstore/ram  后端的实现  ├── ram_core.c # pstore/ram  后端的实现  └── zone.c # pstore/zone  实现存储空间的分配和管理

在我的补丁之前,只支持转存日志到 ram,因此如果研读代码,我们会发现 ram.c 和 ram_core.c 实现了两部分功能:

dram 空间分配与管理

dram 的读写操作

我实现的 blk.c 支持了转存到块设备。但是后来发现不管 pstore/ram 还是 pstore/blk,他们对于存储空间的分配和管理极度相似,我就提炼出了 pstore/zone。于是乎,期望的代码层次应该是这样的:

pstore/ram 要整合入 pstore/zone 已经与 maintainer 达成共识,但还需要更多同学一同努力做更多兼容,例如 ecc 的支持。

看完上述内容,你们对 Linux pstore 实现自动“抓捕”内核崩溃日志实例分析有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注丸趣 TV 行业资讯频道,感谢大家的支持。

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计6862字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)