论文部分内容阅读
软件调试一直是软件开发中的重要问题。人们对调试传统的单线程程序有丰富的经验。单线程程序的调试通常以循环调试和在线交互调试为基础,其中最重要的问题是错误的重现。通过不断地重现错误,通过交互调试工具反复观察系统在出错过程中的运行状态,不断丰富关于错误的知识,最终找到其根本原因。
但是近年来调试技术面临新的挑战。一方面,随着多核平台的普及,多线程并行程序越来越丰富;另一方面,随着以云计算为代表的大规模并行系统的快速发展,在这种系统上运行的大规模并行程序也愈发普遍。对高度并行的程序进行调试面临一些新的问题,其中最主要的问题是程序的非确定性。由于各种无法控制的非确定事件,在并行环境中难以通过反复运行一个程序重建相同的运行状态,因而也难以重现错误。这使以错误重现为基础的循环调试和在线交互调试失效。
对于无法重现的错误的调试,目前最有效的方法是以日志为基础进行分析。日志记录了程序在生产系统中运行时发生的事件序列和状态序列。当程序出现错误时,调试人员可以根据日志中含有的信息推断错误发生过程中程序运行的情况,进而找出错误的根本原因。和传统的在线交互调试不同,我们将基于日志的调试称为离线调试。
本文针对并行程序的离线调试进行了深入的研究。我们首先提出了离线调试的基本流程。在该流程中,我们对日志的生成、静态和动态的日志分析进行了研究。另外,还对离线调试辅助系统实现中的关键技术进行了讨论。
在日志生成方面,本文研究了在库函数、系统调用和指令三个层次上进行介入,通过侵入式的机制获取日志的方法。在库函数层次,本文提出了基于代码的录制机制,允许开发人员通过人工标注的方法注明非确定性并在运行时加以记录。在系统调用层次,本文提出了基于vDSO的系统调用录制机制,实现了纯用户态、轻量级的系统调用录制。在指令层次,本文提出了控制流录制的概念,通过区分控制流和数据流在调试中的不同作用,控制流录制在最底层的录制中调和了开销和准确性之间的矛盾。
在日志的静态分析方面,针对并行系统日志规模大、冗余大的特点,我们研究了从大量日志中找出异常部分的问题,提出了基于文法压缩的异常检测算法。和传统的基于统计和基于马尔可夫模型的方法不同,基于文法压缩的异常检测算法从全新的角度研究异常检测问题,实施方法更为简单。实验表明,基于文法压缩的异常检测算法不仅能够有效地处理日志异常检测问题,对文本分析、主机入侵检测方面的问题也能起到良好的效果。
在日志的动态分析方面,研究了利用日志对程序运行状态进行重建的方法。对含有足够非确定事件信息的日志,提出了替换法重放。对于控制流日志,提出了使用受控法进行重放的方法。
在实际生产系统中进行离线调试需要使用调试辅助系统。我们探讨了在实现调试辅助系统时的一些技术问题,提出了通过透明加载机制降低对运行时环境干扰和通过检查点机制降低日志存储压力的方法。为避免程序运行时行为发生改变,还对信号机制的处理细节进行了讨论。我们实现了Snitchaser和ReBranch两个开源的调试辅助系统,并测量了它们的性能开销。
利用离线调试工具,我们成功解决了一些实际软件中的非确定bugs。本文中我们通过三个案例介绍了对bugs进行离线调试的过程,展示了上述各种技术在实际调试过程中的作用。
本文提出的方法被实际应用于解决软件中的真实问题并取得了良好的结果。实验结果表明,采用本文所提出的方法实现的调试辅助系统适用于在实际的生产系统中解决复杂的非确定bugs。