一种快速定位bug的记录-回放调试系统
2016-11-08王维维尚云海
江 山 王维维 蒋 龙 尚云海
1(浙江大学超大规模集成电路设计研究所 浙江 杭州 310027)2(亚德诺半导体技术(上海)有限公司软件部 上海 200021)3(杭州中天微系统有限公司软件部 浙江 杭州 310012)
一种快速定位bug的记录-回放调试系统
江山1王维维1蒋龙2尚云海3
1(浙江大学超大规模集成电路设计研究所浙江 杭州 310027)2(亚德诺半导体技术(上海)有限公司软件部上海 200021)3(杭州中天微系统有限公司软件部浙江 杭州 310012)
嵌入式程序调试中,程序行为的不确定性加大了bug复现的难度,而多线程程序调试中此问题尤为严重。记录-回放调试技术能有效地解决该问题,但是目前它仍然存在许多技术缺陷,比如会使程序性能下降、调试效率低等,对此提出并实现了一种快速定位bug的记录-回放调试系统。该系统通过记录多线程程序的调度事件,在整体调试时运行性能损耗不高于10%的情况下,实现多线程程序运行顺序的精确回放功能。并且该系统结合进程快照技术,在回放模式下从快照点开始回放程序,能够有效地加速bug的定位,提高调试效率,对于长运行时间的大型程序的调试帮助尤为有效。
嵌入式多线程可逆调试器进程快照线程调度bug定位
0 引 言
软件开发中,调试往往占据了40%甚至以上的产品研发时间[1]。迭代调试是一种常见的,重要的调试手段,它通过重复程序的执行,重现bug,定位bug。但是嵌入式LINUX多线程程序的行为不确定性会使得有些bug的重现非常困难,即使提供相同的程序输入。程序的行为不确定性来源很多,例如实时控制信号的处理、音/视频数据流、网络数据包以及多线程之间的内存竞争等。
可逆调试技术提供了程序反向运行的能力[2]。其中,记录-回放调试能有效解决程序不确定性带来的问题。目前的多线程可逆调试技术领域的研究主要集中在提高记录-回放调试的效率和速度上。如Honarmand在文献[3]中针对源程序中需要加入一些调试功能代码的情况,通过在记录阶段额外地记录许多行为确定性事件,复用修改前程序在记录阶段生成的日志文件进行回放。Nicolas在文献[4]中解决的是同样的问题,文中通过在编译时将新增的调试功能代码与原始代码分开,使得重新编译后的程序的原始部分在回放阶段运行不受影响的方式来实现。Nima为了加快回放速度,在文献[5]中设计了一种使用硬件加速的记录-回放调试方法。Xu Zhou为了减小记录文件的大小,在文献[6]中设计了一种加限制条件的记录-回放调试方法,规定每个线程只能访问自己的内存空间,以此降低多线程间内存竞争的几率。Yann-Hang Lee则在文献[7]中提出了一种回放能力可变的记录-回放调试系统,这个系统在回放阶段有最大回放模式和最小回放模式。
但是,目前没有研究来解决缩小bug范围,实现快速定位bug的问题。并且发现,大多数的记录-回放调试是运行库层面实现的,运行库编译进应用程序中,影响应用程序的开发;对于某些应用程序不通过运行时库,直接使用的系统调用等事件无法捕捉。
本文提出一种快速定位bug的嵌入式多线程记录-回放调试系统。该调试系统有如下三个特点:
1) 该系统提出一种新的事件记录思路,它直接通过对多线程调度顺序的记录来实现多线程程序运行的准确回放。
2) 该系统在多线程调度事件基础上采用进程快照技术,在回放阶段从最近的快照点恢复进程,加快bug复现和定位。
3) 该系统实现不修改原有多线程程序、不修改运行时库,完全不影响程序的开发。
1 记录-回放调试系统的实现
本文提出的记录-回放调试系统,在记录阶段完成线程调度事件记录,并周期性生成进程快照;在回放阶段,首先将进程切换到快照点,然后按照记录的调度事件进行回放。
该记录-回放调试系统实现包含两个部分,一是host机上的交叉调试器GDB(以下简称host机GDB),主要负责调试系统的记录/回放模式的选择。二是嵌入式LINUX调试平台,该平台分为两个部分,一部分是用来模拟硬件系统的Qemu模拟器,Qemu是具有很好移植性的高效动态翻译器,提供系统模式和用户模式两种功能,本文使用系统模式;另一部分是运行在Qemu模拟器上的系统软件,包括嵌入式LINUX内核和gdbserver,在嵌入式交叉调试中,gdbserver作为host机GDB与嵌入式平台的数据通道,通过remote协议与host机GDB进行数据通信,实现LINUX平台嵌入式交叉调试,整体实现框架如图1所示。
图1 记录-回放调试系统框架
本文为host机GDB增加三个用于记录-回放的调试命令[8],通过扩展的remote协议发送给嵌入式端gdbserver,分别用来启动gdbserver的记录/回放工作模式以及设置进程快照的生成周期。针对嵌入式LINUX调试平台上的系统软件,本文提出了gdbserver对记录-回放调试功能的扩展方案,为原有gdbserver增加日志管理模块,该模块在记录模式下对多线程的线程间调度顺序以及进程快照进行记录,并在回放模式下首先根据快照恢复程序,然后为gdbserver提供线程运行的顺序,实现回放;嵌入式LINUX内核方面,本文扩展了用于调试的PTRACE系统调用接口,并改进调度模块,使之能够捕捉到线程调度事件的发生、获取进程快照信息。针对嵌入式平台上的Qemu模拟器,本文在原有模拟器的基础上增加trace模块,用于统计线程在用户态走过的指令数。
整个设计分为多个独立的模块,每个模块功能简单,易于实现。因为绕开了对运行时库函数的封装,所以设计工作量较小。最终多线程应用程序不需要任何改动即可在该框架运行。
1.1host机GDB
本文通过GDB内部提供的add_setshow_boolean_cmd()接口,为host机GDB增加如下三个命令:
set record-mode on
set replay-mode on
set snap-period xxx
分别用来启动嵌入式LINUX调试平台的记录模式与回放模式,以及将快照生成周期设置为xxx(累计xxx次调度事件记录后生成一次进程快照)。
该命令扩展与gdbserver通信的remote协议,添加记录运行程序的命令包和回放运行程序的命令包。在嵌入式gdbserver端也需要根据remote协议完善相应命令包的解析工作。
1.2嵌入式LINUX内核和gdbserver
1.2.1记录模式
记录模式下的工作包括记录线程调度事件以及周期性生成进程快照。
线程调度事件记录的基本逻辑是捕捉到调试线程被内核调度出去的时刻,然后将该时刻下目标线程的位置记录在log文件中,如图2所示。本文将线程被内核调度的行为定义为调度事件,线程被调度表示调度事件的发生。当调度事件发生时,需要记录线程此时运行的位置,但不能仅记录线程pc值,因为仅凭pc值在是不能确定线程运行的位置的(如循环逻辑),log文件如下格式记录:
图2 线程调度顺序记录原理
根据上述格式,LINUX内核在记录模式下记录调度事件发生时被调试线程的准确位置并缓存,最终gdbserver的日志管理模块获取该数据,保存到log文件中。
上述逻辑中有两个关键点:一是调度事件的产生和捕获;二是LINUX内核获取user_pc_count的值。
对于进程快照的记录,首先用户通过gdb命令设置好snap-period,gdbserver对记录的调度事件进行计数,当计数满snap-period之后就发起一次进程快照记录,记录在snap文件中。
进程快照需要记录的信息包括:可执行文件的代码、数据段内容、进程的堆区、各个线程的栈、寄存器、待处理的信号以及进程打开的文件。
记录模式通过对LINUX内核PTRACE系统调用进行扩展实现,为gdbserver在记录模式下运行线程时提供记录模式运行接口[9],见表1所示。基于现有LINUX调度原理[10]。首先,对LINUX调度单元进行修改,进入调度单元后,若当前线程是记录模式下运行的线程,内核会缓存此线程的user_pc_count,然后向此线程发送信号。由于该线程处在被调试状态,信号在该线程被调度后会被gdbserver进程捕获,随后gdbserver的日志管理模块调用PTRACE系统调用,获取内核缓存下的user_pc_count,记录值日志文件中,并且对线程调度事件进行计数,如果达到snap-period,那么发起PTRACE系统调用获取进程快照,保存进程快照到snap文件中。整个流程如图3所示。
表1 PTRACE系统调用扩展以及功能说明
图3 记录模式流程
针对user_pc_count的值,本文对Qemu模拟器的系统模式进行扩展,实现trace模块,该模块可以统计特定线程在用户态所走过的指令数,具体参见1.3节Qemu模拟器部分。
1.2.2回放模式
回放模式工作分为两部分,首先根据snap文件中的进程快照恢复进程;其次根据log文件中的线程调度顺序控制程序回放运行。
图4 添加snap加载方式
进程快照的恢复基于gdb在建立调试进程时进行execve()系统来实现。该系统调用的加载可执行文件,将进程放入调度队列中。该系统调用的关键数据结构是struct linux_binfmt,保存可执行文件的加载方法。每种可执行文件都对应一个linux_binfmt结构体。这些结构体形成一个format链表。本文自定义snap文件格式是基于elf文件格式的可执行文件,可以为其添加一个linux_binfmt结构体[11],如图4所示。然后将snap文件作为execve()系统调用的参数,使用binfmt_snap加载方式。
进程快照恢复完毕,gdbserver的日志管理模块根据记录阶段产生的log文件,组织线程间运行顺序,然后gdbserver根据运行顺序和运行指令数,依次运行每个线程,整个逻辑见图5所示。通过缩小回放的范围,提高回放阶段的效率[12]。
图5 回放模式工作原理
回放模式下,gdbserver如何控制指定线程走过特定的指令数是关键点。这里不能通过指令单步的方式来控制线程的运行,那样调试效率将会非常低,本文扩展了LINUX内核PTRACE接口,增加一个系统调用来完成此功能,请求类型为PTRACE_CON_XINSNS,对应PTRACE值为21。该系统调用的功能是运行目标线程,使之在用户态运行n条指令。
为了实现此功能,本文对Qemu模拟器进行了扩展,实现trace模块,该模块可以指定线程运行特定指令数。详见1.3节。
每次调度事件的回放,首先由gdbserver通过扩展PTRACE系统调用向内核发起运行目标线程N条指令的要求,内核会在运行该线程之前设定Qemu的trace模块,然后运行线程;当线程运行完成指定指令数之后,trace模块会产生异常,LINUX内核异常处理中会向该线程发送信号,由于线程处于被调试状态,gdbserver会捕获该信号,至此,gdbserver完成了一次记录在日志文件中的调度事件的模拟,整个流程如图6所示。
图6 调度事件回放框架
1.3Qemu模拟器
为了实现记录-回放的功能,本文为Qemu模拟器增加trace模块。该模块完成两个功能:一是统计特定线程在用户态走过的指令数;二是统计工作完成后产生异常。
本文通过协处理器的方式为Qemu加入该功能模块,最终,提供给LINUX操作系统的是一些通用的协处理器指令。
实现方案的基本思路是在指令翻译的时候去建立target指令与本地机指令之间的映射关系,然后在执行模块中根据这种映射关系完成指令的统计功能。本文在Qemu模拟器的TCG模块中的每个TB末尾增加一条本地指令,实现钩子函数,该函数用于统计本TB块的指令信息[13]。在执行模块中,每当执行一个TB,就会进入预设的统计函数中做trace工作。在回放模式时,当执行完指定指令数时,异常处理模块会引发一个trace异常,由LINUX系统调用来处理该异常,整个实现框架如图7所示。
图7 Qemu模拟器trace功能扩展框架
2 实验与评估
针对本文设计的所做实验主要考察两个指标:1) 在不采取进程快照的情况下,基于调度事件的记录-回放调试系统对程序运行性能的影响;2) 测试快照的生成时间、快照的snap文件大小以及快照恢复耗时。
实验使用LTP benchmark来评价本记录-回放调试系统,该benchmark有多个不同的多线程运行方式,这里选择了一些经典、常用的多线程模式来评价该调试系统。本次试验所用软件环境为Linux 2.6.9-101.ELlargesmp,gcc版本是3.4.6.硬件环境为实验室所用服务器,Intel(R) Xeon(R) X5690, CPU主频3.47 GHz,24核,64G内存。
如图8、图9所示。实验记录了在不采用程序快照时,调试器系统工作在记录模式下时和工作在回放模式下时运行多线程的时间与多线程本身运行时间的比较结果,通过8组LTP benchmark的测试结果得出,在记录模式下,多线程程序运行平均时间是正常运行的1.045倍,而在回放模式下,多线程程序平均运行速度是正常运行速度的1.145倍。
图8 记录-回放调试系统实验结果(无单位)
图9 记录-回放调试系统实验结果(续)(无单位)
快照的生成时间、快照的snap文件大小以及快照的恢复时间,和线程的数目以及程序本身有关系。取平均数据,快照生成时间为7.5 ms,snap文件大小为3.1 MB,快照恢复时间为9.3 ms。
假设程序的标准运行时间为T,在记录过程中产生了n次进程快照;回放阶段,如果没有快照从头开始运行,按照实验结果回放时间耗时t1;在使用n次进程快照的情况下回放耗时t2。那么用户对于进程快照周期的选择应该满足下列条件:
t1=1.145T
(1)
(2)
t2 (3) 由实验结果可知,当被调试程序运行时间较长,达到秒级时,本文设计的调试系统对于bug定位速度提高显著。而且,通常情况下,在调试过程中,一次记录要回放多次。所以在实际应用中,采用本文设计进程快照与调度事件结合的记录-回放调试模式,在消除多线程程序的行为不确定性的基础上,能有效减少回放时间,加速定位bug,提高调试效率。 针对嵌入式多线程软件调试的困难,本文设计了一款记录-回放调试器,并在C-SKY平台[14]上进行了实现。实验结果与预期基本一致,效果良好,实现了对bug的快速定位,提高了多线程程序的调试效率。 本次设计中,对于外部激励需要保证,回放阶段的程序外部激励要与记录阶段一致,这是本次设计中没有解决的问题。下一阶段需要在现有框架下做扩展,增加在回放阶段对外部激励进行回放的功能。多核架构中,并行程序的行为不确定问题更加突出[15],下一步工作研究多核的可逆调试技术。 [1] Gregory Tassey.The economic impacts of inadequate infrastructure for software testing[R].U.S Department of Commerce,Tech Rep:RTI7007.011,2002. [2] Jakob Engblom.A review of reverse debugging[C]//System,Software,SoC and Silicon Debug Conference (S4D).Vienna:IEEE,2012:1-6. [3] Honarmand Nima,Josep Torrellas.Replay Debugging:Leveraging Record and Replay for Program Debugging[C]//International Symposium on Computer Architecture (ISCA).Minneapolis,USA:IEEE,2014:455-456. [4] Nicolas Viennot,Siddharth Nair,Jason Nieh.Transparent mutable replay for multicore debugging and patch validation[C]//Proceedings of the eighteenth International conference on Architectural support for programming languages and operating systems.NewYork,USA:ACM SIGARCH,2013:127-138. [5] Nima Honarmand,Nathan Dautenhahn,Josep Torrellas.Cyrus:unintrusive application-level record-replay for replay parallelism[C]//Proceedings of the eighteenth International conference on Architectural support for programming languages and operating systems.NewYork,USA:ACM SIGARCH,2013:193-206. [6] Xu Zhou,Kai Lu,Xicheng Lu,et al.RReplay:A record & replay system based on restricted multi-threading[C]//2012 International Conference on Computer Science & Service System (CSSS).NanJing,China:IEEE,2012:2247-2250.[7] YannHang Lee,Young Wn Song,Rohit Girme,et al.Replay Debugging for Multi-threaded Embedded Software[C]//2010 IEE/IFIP 8thInternational Conference on Embeded and Ubiquitious Compting (EUC).Hong Kong,China:IEEE,2010:15-22. [8] 邵腾冈,张俊飞.基于虚拟机日志记录回放的可逆调试方法[J].计算机应用与软件,2011,28(4):140-142. [9] 陈必泉,黄承慧.GDBSERVER原理分析及其应用[J].计算机工程与设计,2005,26(3):746-749. [10] 雷铭哲,张勇.Linux线程机制研究[J].火力与指挥控制,2010,35(2):112-118. [11] 罗琰.基于内核模式下进程快照的可回溯调试研究及初步实现[D].杭州:浙江大学,2008. [12] Yan Wang,Harish Pati.Dr Debug:Deterministic Replay based Cyclic Debugging with Dynamic Slicing[C]//Proceedings of A nnual IEEE/ACM International Symposium on Code Generation and Optimization.New York,USA:ACM,2014:98-98. [13] Pavel Dovgalyuk.Deterministic Replay of System’s Execution with Multi-target QEMU Simulator for Dynamic Analysis and Reverse Debugging[C]//2012 16thEuropean Conference on software Maintenance and Reengineering(CSMR).Szeged Hungary:IEEE,2012:553-556. [14] 潘赟.CK-CPU嵌入式系统开发教程[M].北京:科学出版社,2011. [15] Nima Honarmand.Record And Deterministic replay of parallel programs on multiprocessors[D].Illinois:University of Illinois Urbana-Champaign,2014. A FAST BUG-LOCATING RECORD AND REPLAY DEBUGGING SYSTEM Jiang Shan1Wang Weiwei1Jiang Long2Shang Yunhai3 1(InstituteofVLSIDesign,ZhejiangUniversity,Hangzhou310027,Zhejiang,China)2(DepartmentofSoftware,AnalogDevicesInc(Shanghai),Shanghai200021,China)3(DepartmentofSoftware,C-SKYMicrosystems,Co.,LTD,Hangzhou310012,Zhejiang,China) While debugging embedded Linux, the uncertainty of program behaviour increases the difficulty of bug reproduction, and this is particularly serious in debugging multi-thread programs. Record and replay debugging technology is can effectively solve this problem, but at present it still has a couple of technical flaws, such as degrading program performance and low debugging efficiency, etc. In this paper we present and implement a fast bug-locating record and replay debugging system. By recording the scheduling events of multi-thread program, it realises the function of accurate multi-thread program operation order replay under the condition of less than 10% of performance loss in overall debugging. In addition, this debugging system, incorporating process snapshot, can effectively speed up bug locating by commencing the replay program from snapshot point in replay mode. This improves the efficiency of debugging, and is effective particularly in debugging large-scale program with long running time. EmbeddedMulti-threadReverse debuggerProcess snapshotThread schedulingBug-locating 2015-07-26。核高基重大专项(2010ZX01030-001-001-002)。江山,硕士生,主研领域:嵌入式工具软件研究。王维维,副教授。蒋龙,工程师。尚云海,工程师。 TP311 A 10.3969/j.issn.1000-386x.2016.10.0493 结 语