嵌入式Linux崩溃分析实战:没有JTAG,怎么“空手“定位死机问题?
嵌入式Linux崩溃分析实战没有JTAG怎么空手定位死机问题一句话总结产品量产了才发现死机没有JTAG接口串口日志也不够用——用ramoops/pstore、kdump、ftrace这三个武器大部分崩溃问题不用硬件调试器也能定位。做嵌入式Linux产品的人最怕的不是功能做不出来而是产品在客户现场死机了你拿不到任何调试信息。量产设备通常没有JTAG接口成本原因或者外壳已经封死甚至串口都不一定引出来。这时候你怎么定位问题90%问题靠以下三个工具可以解决。今天把它们分享一下。武器一ramoops/pstore — 内核崩溃后的黑匣子问题内核panic了系统直接重启。你没看到任何日志只知道设备又重启了。方案用ramoops在ARM64上是pstore保留panic前后的内核日志重启后自动保存。操作内核配置CONFIG_PSTOREy CONFIG_PSTORE_RAMy CONFIG_PSTORE_CONSOLEy CONFIG_PSTORE_PMSGy CONFIG_PSTORE_FTRACEy设备树中预留内存reserved-memory { ramoops0xBF000000 { compatible ramoops; reg 0x0 0xBF000000 0x0 0x00100000; record-size 0x00020000; console-size 0x00020000; ftrace-size 0x00020000; pmsg-size 0x00020000; }; };重启后读取# 重启后检查mount-tpstore pstore /sys/fs/pstorels/sys/fs/pstore/cat/sys/fs/pstore/console-ramoops-0cat/sys/fs/pstore/dmesg-ramoops-*真实案例去年一个客户反馈说设备每天凌晨3点准时重启。我们在ramoops里可以看到类似如下关键线索[12345.678901] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 [12345.678902] Internal error: Oops: 96000004 [#1] PREEMPT SMP [12345.678903] Modules linked in: xxx_driver(O) [12345.678904] PC is at xxx_driver_ioctl0x34/0x120 [xxx_driver]一看就知道是驱动里的ioctl回调里解引用了一个空指针。再看调用栈是一个定时器在凌晨3点触发的清理任务。问题定位加修复两小时搞定。如果没有ramoops你可能要盲目加printk再等两天复现。武器二kdump kexec — 宕机后拍一张CT问题ramoops只能保留内核日志文本。如果问题需要查看完整的内存状态进程列表、内存分配、寄存器值ramoops不够用。方案kdump在系统崩溃时启动一个捕获内核把崩溃时的内存状态完整dump下来后面可以用crash工具分析。操作安装# 在Buildroot中开启BR2_PACKAGE_KEXECyBR2_PACKAGE_KEXEC_ZEROHUNTERy# 配置内核CONFIG_KEXECyCONFIG_CRASH_DUMPyCONFIG_PROC_VMCOREy预留内存内核启动参数crashkernel64M触发测试# 手动触发panic测试echoc/proc/sysrq-trigger重启后分析# vmcore文件位置ls/var/crash/# 用crash工具分析crash vmlinux /var/crash/vmcorecrash工具里最常用的命令bt # 看调用栈哪个函数导致的崩溃 log # 看内核日志 ps # 看进程列表 vm PID # 看某个进程的内存映射 files PID # 看某个进程打开的文件什么时候用kdump而不是ramoops怀疑是内存 corruption 问题需要看进程间的资源竞争状态需要分析spinlock死锁内核panic但日志信息不够kdump的代价是预留内存通常64-128MB和重启后转储的时间。对于内存敏感的设备ramoops更轻量。崩溃处理流程是否是否内核崩溃有kdump?启动捕获内核转储vmcore到存储系统重启用crash分析vmcore有ramoops?重启后从pstore读取分析kernel日志只能靠串口碰运气武器三ftrace — 追踪慢动作和神秘挂起问题系统没崩溃但莫名其妙卡住了——某个线程不响应了或者中断响应延迟突然变大。这种问题不致命但很烦人。方案ftrace是内核内置的追踪器可以记录函数调用、中断开关、调度事件。关键是零开销启用时才有额外开销。操作# 挂载tracefsmount-ttracefs tracefs /sys/kernel/tracing# 查看可用的追踪器cat/sys/kernel/tracing/available_tracers# 启用function_graph追踪echofunction_graph/sys/kernel/tracing/current_tracerechomy_driver_function/sys/kernel/tracing/set_ftrace_filterecho1/sys/kernel/tracing/tracing_on# 复现问题后echo0/sys/kernel/tracing/tracing_oncat/sys/kernel/tracing/trace高阶用法追踪中断延迟# 追踪irq_disable时间echoirqsoff/sys/kernel/tracing/current_tracerecho1/sys/kernel/tracing/tracing_on# 或者追踪抢占关闭时间echopreemptoff/sys/kernel/tracing/current_tracer真实案例一个工控设备偶尔出现控制输出延迟响应。用ftrace的irqsoff追踪器发现某个驱动的spin_lock_irqsave持有了超过50ms。# irqsoff追踪输出简化# tracer: irqsoff## irqsoff latency 50321 us (临界区长度50ms!)# 导致中断被屏蔽了50ms## 罪魁函数: xxx_driver_do_blocking_io# 调用路径:# xxx_driver_write - spin_lock_irqsave - xxx_driver_do_blocking_io问题驱动在spinlock保护的临界区里做了阻塞操作等待硬件完成标志。spinlock的本意是短时持有但硬件响应慢了导致中断被屏蔽了50ms。fix很简单用mutex代替spinlock或者把等待移到临界区外面。三件武器的选择策略场景工具原因内核panic重启ramoops/pstore轻量不需要预留大块内存需要完整内存分析kdump可做post-mortem分析系统卡死/延迟大ftrace零基线开销精准追踪用户态进程崩溃core dump跟嵌入式Linux关系不大用ulimit -c总结没有JTAG不代表不能调试。嵌入式Linux内核提供了足够多的调试工具关键是你知不知道用。我的习惯是所有量产产品必开ramoops pstore这是一个配置项的事儿成本为零收益巨大。kdump在内存充足256MB以上的设备上也推荐开启。ftrace则是按需启用系统出问题了再打开复现。别再跟我说量产设备没法调试——不是没法调试是你没配。这三个工具配置一次能用一辈子。P.S.如果你还在用出问题就重新烧个带printk的固件等复现的方式看完这篇花半天时间把ramoops加上。下次客户半夜打电话说设备死机了你不用去现场远程要个日志文件就能定位。这才是嵌入式工程师该有的水平。