作者简介
廖威雄,就职于珠海全志科技股份有限公司,负责Linux IO全栈研发、性能优化、开源社区开发交流、Linux 内核开源社区pstore/blk,mtdpstore模块的作者、大客户存储技术支持、全志首个UBI存储方案主导人、全志首个RTOS NFTL主导人
我设计的内核模块
简介
这对分析那种小概率且没办法抓到现场的问题非常实用,尤其是现在智能互联网的设备逐渐普及的时候,远端的设备可以自己捕抓崩溃日志再通过网络传输到服务器,维护人员就可以根据收集来的日志定位和解决问题,然后通过OTA让设备升级迭代。
根据网上搜寻的资料,在
-
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 的方案适用于服务器这种有大量资源的设备,功能也非常强大,但对嵌入式设备非常不友好。
目前支持以下几个前端:
-
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
-
挂载 pstore文件系统
-
读取 转存的日志文件
详细的说明可以看源码上的文档,本文只做基本功能的介绍。
-
Documentation/admin-guide/ramoops.rst
-
Documentation/admin-guide/pstore-blk.rst
使能
在
1 | $ 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即可,前端就根据自己的需求选择,至于
1 | $ make menuconfig |-> Device Drivers |-> Memory Technology Device (MTD) support |-> Log panic/oops to an MTD buffer based on pstore |
选上就可以用了?虽然我非常想说“是的”,但事实却有点“骨感”。即使所有前端都使用默认配置,
在
1 2 3 4 5 6 | $ make menuconfig |-> File systems |-> Miscellaneous filesystems |-> Persistent store support |-> Log panic/oops to a block device # pstore/blk 后端 |-> () block device identifier # 使用哪个块设备? |
如果使用
1 | pstore_blk.blkdev=XXXX |
1 | 或者以模块加载: |
1 | $ sudo insmod pstore_blk.ko blkdev=XXX |
这里的块设备可以是代表整个磁盘的
-
/dev/ : 例如,使用U盘的第2个分区,则是/dev/sdb2 -
:例如,mmc设备第6个分区,则是: 179:6
形式大概是这样:
1 | $ sudo insmod pstore_blk.ko blkdev=/dev/sdb2 |
1 | 或者 |
1 | $ cat /proc/cmdline.... pstore_blk.blkdev=179:6 ... |
如果是
1 | pstore_blk.blkdev=pstore # 假设存在名为pstore的MTD分区 |
OK,对新同学来说,到这里配置就够了。可以从我的
挂载
在使能且正确配置设备后,启动的时候应该会有这样的日志:
1 | pstore_zone: registered pstore_blk as backend for kmsg(Oops,panic_write)pstore: Registered pstore_blk as persistent store backend |
这代表
1 | mount -t pstore pstore /sys/fs/pstore |
1 | 挂载后,通过mount能看到类似这样的信息: |
1 | # mount...pstore on /sys/fs/pstore type pstore (rw,relatime)... |
如果曾经触发过崩溃日志,在挂载点应该有类似这样的文件:
1 | # ll /sys/fs/pstore...-r--r--r-- 1 root root 15521 Jan 1 00:06 dmesg-pstore_blk-0... |
如果需要验证,咱们可以这样主动触发内核崩溃:
1 | # echo c > /proc/sysrq-trigger |
我是在U盘、SD卡、mmc、nand上验证的,maintainer Kees Cook 提供了另外一种基于
1 | # 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 |
读取
经过上述的挂载后,可以在挂载点看到转存的日志文件。既然是文件,肯定支持文件的一系列操作,例如读取、删除。
1 2 3 4 | root@TinaLinux:/sys/fs/pstore# head -n 10 dmesg-pstore_blk-1Oops: Total 2 timesOops#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# lldrwxr-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# lldrwxr-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 |
对
1 | Oops: Total 2 times # 表示触发了Oops,且是自系统安装后第一次启动以来第2次触发Oops。Oops#1 Part1 # 表示这是上一次运行期间第1次触发Oops的日志。 |
可以发现,第一行是累计总的触发次数,第二行是上一次启动触发的次数。
每个文件名的格式都是
当然,除了
1 | # 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 |
除此之外,每个文件的时间戳表示 崩溃触发的时间。上例中,由于系统并没有实现同步更新系统时间,所以时间戳不合理。
展望未来
正如我前文说的,
全功能支持
到目前为止,不管是块设备还是
| 设备 | dmesg(Oops) | dmesg(Panic) | pmsg | console | ftrace |
|---|---|---|---|---|---|
| 块设备 | Y | N | Y | Y | Y |
| MTD设备 | Y | Y | N | N | N |
| ram设备 | Y | Y | Y | Y | Y |
块设备如果需要记录
MTD设备很早前就有了
迁移pstore/ram
在当前
1 | $ tree fs/pstorefs/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 实现存储空间的分配和管理 |
在我的补丁之前,只支持转存日志到
-
dram空间分配与管理
-
dram的读写操作
我实现的
参考
1 2 | https://git.kernel.org/torvalds/c/829f3b9401fe7cc3c1f3642bb2520751a42a87df https://github.com/gmpy/articles/blob/master/pstore/Test-Pstore-Block.md |
(END)
更多精彩,尽在"Linux阅码场",扫描下方二维码关注
别忘了点个在看哦