智能手机发展了近十年,魅族手机是最早建立起稳定性体系和标准的终端厂商之一。
系统需求金字塔的由下至上分别是功能可用、功能完整、稳定无故障、高可维护性、性能优越。
稳定性问题的来源多样化,大多可以总结为工艺问题、硬件问题、芯片原厂问题、上游社区问题以及终端厂商问题。内核稳定性问题主要包括宕机/死机以及异常重启。
稳定性工作包含design、Development、Test、Release以及Feedback5个流程。
我们会从大数据中筛选出用户使用过程中的稳定性问题,不断地迭代系统,让系统更加稳定。
稳定性开发的目的在于通过提前干预的方法,让更多 bug 在开发阶段暴露出来,提高量产固件的稳定性,减轻后续维护压力,也提升用户体验。稳定性开发的内容包括代码静态分析(Static analysis)、动态故障检测(Dynamic check)、故障现场记录(Record fault)以及故障分析工具(Fault analysis tool)。
代码静态分析的目的在于通过提前干预的方法,让更多 bug 在开发阶段暴露出来,提高量产固件的稳定性,减轻后续维护压力。过程中使用到的开源工具包括Section Mismatch检测、Smatch、Sparse、Coccicheck,商业工具包括Klocwork、Fortify、Pc-lint。
运行时动态监测包括死锁检测、内核组件( warm 变 bug 的注入)以及内存踩踏的检查。
Lockdep是内核原生自带的检查锁的工具,全方位检查spinlock, mutex, rwlock, rcu 的锁使用场景和方法是否引起死锁风险。
比如系统发生lrq storm会卡住调度,卡住调度又会引发一连串的问题,而这些发散的问题很难往前追溯原因。最合适的方法是在 report bad lrq时进行拦截,直接终止系统的运行。
另外,关键的warn还包括:
- smp_processor_id() used in preemptable:在获取到 cpu id 到使用cpuid 的过程中,进程可能发生迁移从一个 cpu 迁移到另一个 cpu,这可能导致 cpu id 不准确,进而导致后续代码逻辑执行错误。如smp_processor_id()没有禁止调度,则可能出现风险,除非smp_processor_id()用在已经绑定了某个CPU的进程上。对于该 WARN,我们直接将其变为 BUG() 避免后续问题发散的风险。
- schedule in atomic:在原子上下文中,不允许系统调度,否则会WARN。
- sleep in atomic:如果它所在函数处于原子上下文(atomic context)(如,spinlock, irq-handler…),调用可能引起睡眠的函数如 mutex_lock,wait_for_completion等,会报 WARN, sleep 意味着调度出去。
- Bad irq warning:如果出现没有注册 specific handler,或没有正确返回 IRQ_HANDLED,或 100000个中断有 99900 没处理等共享中断问题,会触发 bad irq WARN,需要及早拦截。
- spinlock debug warning:使用未初始化spinlock、重复上锁、重复解锁、wrong owner解锁等情况,spinlock 会报 WARN,如果不及时处理,可能会破坏锁结构体,埋下隐患。
- Mutex debug warning :同上。
- Workqueue warning:执行 work 时,不能留锁不释放,不能关中断、关抢占,否则会报 WARN;work 执行 timeout 时,可能卡住,也会报WARN。
踩内存问题难以分析,很大原因在于抓取到的并不是第一现场。如上图,S为程序开始的节点,A为内存异常的第一现场,B才是我们抓取到的现场,分析问题的关键在于如何从B往前推导到A。
踩内存的类型大致可分为内存越界(out-of-bounds)、访问已经释放的内存(use-after-free)和飞踩等。检测手段也较为丰富,包括:
- 局部变量踩内存检测:stack protector
- 基于 page 的踩内存检测:page poisoning(滞后被动)
- 基于 slub 的踩内存检测:slub debug、meizu slub debug enhance(滞后被动)
- 全局第一现场踩内存检测(最优手段):KASAN(高开销)、MTE(AMR V8.5 以上)、kfence(低开销、粒度大: page,采样率低)、MMU protect buddy/slab
- 固定地址踩内存检测:watchpoint
魅族此前曾采用MMU protect buddy/slab,将buddy没有分配出去的页面设置为 MMU 不可访问。一旦访问读写则会报错,增加了很多不可访问的黑洞来提高抓取到第一现场的概率。
固定地址踩内存的检查
魅族的踩内存问题检测手段Meizu slub debug enhance原理如下:将object size-alloc size填充为Red zone,在free时check_objects,检查object size-alloc size是否全为red zone,以确认内存是否曾被踩踏。
龙蜥社区Anolis kfence enhance的原理为:在datapage的两边加上了fencepage电子栅栏,利用MMU的特性将fencepage设置为不可访问。如果对datapage的访问越过了page边界,则会立刻触发异常。
另外,龙蜥社区实现了动态开关和全量检查,性能更优于Linux社区。
原生代码无法很好地提供分析材料,魅族需要自己支持更丰富和完善的手段来 debug,包括通过组合键触发 sysrq 捕获问题场景、提供 reset_reason 节点信息、提供重启前日志/sys/fs/pstore/console-ramoops-0、用户不可刷除日志记录 rstinfo、Kernel 异常重启 ouc 上报、Lmk 等关键位置 log 增强。
分析 dump 会使用到的工具有Ramdump parser、Trace32 simulator、Crash。
MEIZU BSP云平台能够自动化分析 dump 并且输出总结性报告,可以在网页上直接查看和分析log。魅族内部的产线端、用户反馈端、测试端均统一接入了该平台,开发人员可以很方便地使用,可在前端专注分析报告日志来提高整体的分析效率。
智能日志分析系统可以接入Stability / Battery / recovery / android reboot / power 等模块。
稳定性测试包括功能性测试和压力测试。
上图为我们对稳定性问题的归纳。
内存问题也是常见的稳定性问题。我们也对其进行了总结归纳并列出了对应的解决方案。
治理稳定性问题的过程中,我们遇到了几个较大挑战。
第一,软件低概率问题测试和复现。特殊场景比如打开了某个开关或多个条件才能触发的问题,这要求我们具备搭建复现模型的能力,能够快速构建复现模型。我们期望能够通过黑盒、白盒提供更多的主动检查工具,将代码覆盖度测试得更充分。
第二,内存故障问题的定位。比如MTE硬件检测无法使用,没有低消耗的第一现场检查手段;同soc里不同cpu踩内存(异构核踩内存(如GPU/SensorHub/aop和AP处理器共享内存场景);厂商提供了没有源码的二进制软件;工艺不良带来的虚埠等硬件不良筛查。
第三,卡死问题的定位。比如进程间死锁、CPU间死锁、资源耗尽、显示模块出现故障等。我们认为需要构建自动分析模型库来解决此类问题。
关于龙蜥峰会 eBPF & Linux 稳定性专场课件获取方式:
【PPT 课件获取】:关注微信公众号(OpenAnolis),回复“龙蜥课件” 即可获取。有任何疑问请随时咨询龙蜥助手—小龙(微信:openanolis_assis)。
【视频回放】:视频回放可前往龙蜥官网https://openanolis.cn/video 查看。