使用基于时间的断点调试计算机程序的方法和系统的制作方法

文档序号:6376978阅读:396来源:国知局
专利名称:使用基于时间的断点调试计算机程序的方法和系统的制作方法
技术领域
本发明涉及在调试器中使用断点,特别涉及使用调试器启动基于时间的中断。
背景技术
调试器是对于开发人员进行编写程序代码和维护现有的代码至关重要的工具。现有的调试器为程序员提供了许多功能,帮助程序员消除现有的缺陷,或了解程序的操作。一个现有的调试器功能是插入被称为“断点”的装置。
表1显示了不同类型的断点的列表,以及它们是如何操作的。程序的执行在断点处停止,以便程序员可以一行一行地单步执行代码,或者根据需要恢复程序的执行。当断点有效时,程序员可以确定所执行的每一行代码中的程序变量的值。
断点是任何调试器中最常用的功能。当前的调试器提供了许多种断点。取决于到达的断点的类型,调试器一般来说停止程序的执行。
表1

与现有的调试器关联的一个缺点是,没有在一定的时间消逝之后使程序停止执行的断点。即,没有只是在一定的时间段消逝之后生效的断点。有两个解决办法用于获得相同的效果。下面将简要概述一下这两种方法。
第一个解决办法涉及人工跟踪时间,然后当指定的时间段消逝之后将程序中断。此技术不准确,特别是当时间延迟非常小时。此外,还需要程序员人工跟踪时间。因此,此技术相对比较麻烦,并且易于出错。对于时间延迟较大的情况,如果程序员未能中断程序执行,那么整个过程就可能需要再次重复。
第二个解决办法涉及用附加代码(如表2中显示的代码)对原始代码进行重新编程。
表2if(time_has_elapsed())int x=5;/*Dummy Statement*/如果用表2中显示的附加语句重新编译代码,并在“DummyStatement”处设置断点,则获得相同的效果。
然而,此技术的一个特定的缺点是,需要更改源文件。如果生成过程要花较长的时间,那么,更改代码和重新生成的过程是不受欢迎的。此外,程序员还必须确保删除上面的代码,以便进行发布或用于“准备好制作”的版本。
鉴于上述情况,显而易见,需要一种改进的提供基于时间的断点的方式,供在调试计算机程序时使用。

发明内容
在调试器中提供一种基于时间的断点功能,让程序员在任何一行代码设置基于时间的断点。如果该行在执行,而指定的时间消逝,那么程序会因此而停止执行。调试器为每一个这样的断点跟踪消逝的时间。可以有许多条件用于为每一个断点计算时间。
断点的计时器可以在程序开始执行时启动。断点的计时器可以在包含断点的行首次到达时启动。断点的计时器也可以基于一个进程获得的CPU时间。
每当程序的执行由于另一个断点而停止时,调试器都处理每一断点的时间计算。断点在一定的时间消逝之后生效,而在生效之前不起作用。
基于时间的断点可以与现有的断点结合使用,以向程序员提供更强大的功能和灵活性。和诸如特定条件或者存储更改之类的其他参数相比,如果“时间”是用于中断程序执行的优选参数,则基于时间的断点比较有用。


图1是显示如何在存储器中的程序的可执行映像中插入基于时间的断点的步骤的流程图。
图2是显示当程序的可执行映像在调试器中运行时如何在其中插入基于时间的断点的略图。
图3是显示如何在存储器中的程序的可执行映像中插入触发器的流程图。
图4是当在程序执行期间遇到触发器时调试器所采取的步骤的流程图。
图5是当在程序执行期间遇到触发器时调试器所采取的步骤的流程图,包括涉及安装重现触发器的步骤。
图6是其中相对于时间显示了程序执行和单个基于时间的断点的示例的略图。
图7是其中相对于时间显示了程序执行和单个基于时间的断点以及多个调试操作的示例的略图。
图8是其中相对于时间显示了程序执行和多个基于时间的断点的示例的略图。
图9是适合于执行参考图1到8描述的技术的计算机系统的略图。
具体实施例方式
调试是一个通过观察存储器内容或者通过观察程序的执行流来从程序中消除缺陷的过程。调试过程帮助查找和纠正在源文件的编译期间未被发现的逻辑错误。调试器是帮助完成此过程并在代码中的程序员指示的位置中断程序执行的工具。这种类型的调试技术叫做“交互调试”,其中,调试器中断正在被调试的程序的执行,并让程序员通过用户界面与调试器进行交互。
该技术还叫做“断点调试”,因为调试器通过让程序员在程序中设置断点,并让程序员观察和改变程序的状态来中止程序执行。
在任何时刻,程序状态都包括寄存器值和用于存储程序的数据的存储器内容。为提高速度并确保简单,调试器允许程序员直接在源代码中设置断点。源代码通过使用存储在程序文件中的附加信息(被称为“调试信息”)被映射到机器指令。此调试信息可以包括在同一个文件,也可以作为一个单独的文件存在。
用适当的选项编译源文件通常会生成此调试信息。因此,调试器能够将源代码信息映射到对应的机器级指令。源代码包含在源文件中,而机器指令存储在程序的可执行文件中。编译过程,后面接着是链接,可以将源代码指令转换为机器指令。由编译过程生成的一部分调试信息用于将源级指令映射到机器指令,或执行相反的操作。
调试器从该文件中读取此信息并将该信息放入诸如结构阵列、树、散列表等等之类的适当数据结构中。当调试器在存储器中的程序的可执行映像中插入断点时,此数据结构用于将机器代码与源代码进行关联。
断点不是在编译时设置的,而是在调试器执行过程当程序位于存储器中时设置的。断点是在程序启动时在存储器中设置的。当程序暂停执行并等待程序员完成调试操作(如检查存储器或设置新断点)时,也可以插入新断点。此设置断点的过程是通过用户界面执行的,该用户界面还可以在程序员退出调试器时存储断点列表,以便当在下一会话期间对程序进行调试时断点不会丢失。
调试过程涉及控制另一个程序的执行的一个程序。控制程序是调试器,而被控制的程序是正在被调试的程序。这是一种主从关系的形式,调试器根据程序员的需要来控制执行流。因此,调试器应该能够访问、读取和写入到子程序的存储器。调试器可能希望读取或写入子程序的机器指令、寄存器值或变量。除读取存储器、向程序员报告程序的状态之外,调试器还可能希望改变子程序的存储器的内容。此能力对设置断点,或者当程序员使用用户界面请求调试器写入到一些变量或寄存器时改变存储器是必不可少的。
现代的操作系统在一种叫做“保护模式”的特殊模式下操作处理器。保护模式的一个重要功能是,不允许一个程序访问另一个程序的存储器,以避免存储器损坏。操作系统提供了程序间相互进行通信的机制。这种让两个进程相互通信的功能叫做“进程间通信”,程序使用它的场合相当广泛。可能存在需要一个程序向另一个程序通知发生了某一个事件的情况。事件可以是窗口缩放,资源被释放等等。
这些事件通过操作系统信号或消息进行传达。信号是进程间通信的一种模式。一个重要事件是通知子进程立即暂停执行。任何过程都可以产生信号,但这对可能需要在任何已知时间点立即中止程序执行的调试器有用处。当程序执行由于引发异常的中断指令而中止时,这是不同的。基于异常的进程的中止允许断点按如下所述的方法来实现。
在基于Unix的操作系统中,产生一个叫做SIGSTOP的信号将在那一瞬间停止进程。一旦信号被传输给进程,该进程就立即暂停执行。不同的操作系统中也存在对应的消息。如上所述,信号是两个进程借以可以相互进行通信的机制。因此,由一个进程发送信号,另一个进程接受信号。信号通常通过操作系统进行路由,而操作系统又必须确保该信号被发送到正确的进程。内核程序还向进程发送信号,以向它们通知诸如无效存储器存取、输入输出错误之类的各种事件。因此,当前操作系统提供了一个进程借以可以通知另一个进程直到进一步通知之前中止其执行的机制。
操作系统在硬件时钟的帮助下维护系统时间。在内部,操作系统内核使用硬件时钟预定多重处理系统中的进程的时间。内核程序还提供到程序的系统调用,以获得任何与时间相关的功能。用户可以使用这些调用来设置时间或者获得系统时间,或者在一定的时间间隔之后定期获得通知。
为了精确,操作系统还可以提供一个到主板硬件上的实时时钟(RTC)的接口。程序可以直接与RTC进行通信,以获得较高的精确度。因此,程序可以请求操作系统或者RTC当指定的时间段消逝之后通知它们。这些计时器可以是“一次性的”计时器,只通知进程一次,或者是周期性的计时器,当指定的时间间隔消逝之后每隔一定间隔向进程发送信号。
如上所述,调试器控制正在被调试的子进程。调试器还截获发送给子进程的信号,以获得对子进程的完全控制。调试器接收发送给子进程的所有信号,并依据程序员是否指示而将它们路由到子进程。当一些指令在子进程中生成异常时,首先向调试器发送对应的信号。此机制检测是否到达了中断指令。中断异常通知调试器,说子进程已经暂停执行,因为子进程已经执行了一个程序员使用调试器在其中设置了断点的指令。
断点可以用硬件和软件两种方式来实施。调试器通常使用这两种统称为断点的实施方式。一些硬件提供了通过利用作为芯片的组成部分的调试寄存器来设置断点的功能。这些类型的断点被称为硬件断点,并可以是两种类型之一(i)数据断点或(ii)指令断点。
当访问指定的存储器位置或输入输出端口时,数据断点中断程序执行。每当到达代码中的指定位置时,指令断点都停止程序执行。中断执行将把控制转移到调试器过程。这是使用硬件来实现的。
例如,在Intel x86中央处理单元体系结构中,有八个调试寄存器(D0到D7)。有四个寄存器(即,D0、D1、D2、D3)用于设置断点。这些寄存器存储了将为其设置断点的32位线性地址。寄存器D4和D5用于进行高级调试。寄存器D6和D7分别用作状态和控制调试寄存器。
为设置指令断点,指令的地址存储在任何未使用的调试寄存器D0到D3。相应地设置D7,以指出断点是指令断点。当处理器试图在指定的地址执行指令时,硬件引发一个调试异常。异常是在执行指令之前引发的,需要调试过程,以清除断点,以便不会再次产生异常。
相形之下,对于数据断点,只是在访问存储器位置或输入/输出端口之后才会生成陷阱异常。硬件还提供机器指令以单步执行子程序。
硬件断点所存在的一个问题是,此功能依赖于硬件,一些体系结构可能不支持硬件断点。硬件断点在数量方面受到限制,因此,调试器还要实施软件断点。软件断点是通过更改正在被调试的程序的可执行代码来实施的断点。这是在存储器中进行的,而不是在可执行文件中进行的。正在被调试的程序的代码由调试器“修补”。
图1用流程图显示了当程序在调试器中运行时在存储器中的程序的可执行映像中插入断点所涉及的步骤。
当在一行代码中设置断点时,调试器执行下面的表1中显示的下列步骤。
表1步骤110 调试器确定为其请求断点的指令。
步骤110 调试器将此原始指令存储在计算机存储器中一个位置,以便可以随后检索原始指令。
步骤110 调试器将原始指令的地址存储在一个基于专用硬件的调试寄存器中(在硬件支持这样的寄存器的情况下)。如果硬件不支持这样的寄存器,则调试器在存储器中的程序代码中插入一个指令,当执行时将引发异常。在这两种情况下,都会产生异常,控制将转到调试器线程。
每当中断指令引发异常时都会命中或到达断点。然后控制转到调试器过程。调试器通知用户,说明断点已经到达。当用户继续执行时,调试器用原始指令(如上所述,调试器以前存储的)替换中断指令,然后执行指令。在执行指令之后,调试器再次存储原始指令,并用中断指令替换原始指令,以便断点不会丢失。这相当于为一个指令禁用断点,然后一旦原始指令执行完毕再启用断点。通常,一旦断点被命中,所有断点都保留中断指令,但此行为对于各种不同的断点可能会有所不同。例如,当断点被命中时,临时断点不用中断指令替换原始指令,因为临时断点是一次性使用的断点,因此,它们不需要保留中断指令。
图2概要显示了图1用流程图显示的过程的示例。程序代码片段210包括一个断点。随后,调试器确定在对应的机器指令220中的什么位置放置断点。在图2中,为便于理解,使用了一个汇编指令示例。然后,调试器用断点230替换相关的计算机指令240,并将原始指令240保存在存储器中。
如上所述,调试器需要修补代码并插入会引发异常的代码。这可以直接由调试器来完成。许多操作系统不允许一个进程访问另一个进程的地址空间。操作系统通常提供一个系统调用(这是一个从内核程序请求服务的接口),以允许调试器修补代码并读取正在被调试的程序的变量。系统调用充当控制在调试器中运行的子程序的机制。这允许调试器截取异常和信号以获得对程序的完全控制。
调试器使用几乎所有Unix克隆所提供的ptrace()系统调用,ptrace()系统调用允许调试器读取或写入存储器、寄存器、监视子程序的地址空间中的点(硬件中的数据断点)。调试器可以通过更改存储器中的代码来设置软件断点,或者,调试器可以通过设置子进程的寄存器来设置硬件断点。
当一个程序在调试器中运行时,调试器充当父程序,并将正在被调试的程序衍生为其子程序。因此,调试器和正在被调试的程序具有父子关系。调试器的主要用途是控制和观察子程序。因此,在衍生子进程之后,在子程序正在运行时,调试器处于等待状态。每当一个信号被传输给子程序时,操作系统都停止子程序。然后,操作系统通知父程序(在此情况下,为调试器),说明子程序已经停止。调试器可以查询子程序停止的原因。
当程序员插入断点并在调试器中运行程序时,调试器将程序衍生为其子程序,并等待接收操作系统的通知。当到达一个断点时,程序引发异常,子程序转到停止状态。操作系统通知调试器,说明子程序已经停止。因此,在到达断点时,控制被从子程序转到调试器。当子程序在运行时,调试器无限期地等待在子程序中发生一些事件。当控制转到调试器之后,子进程始终处于停止状态。
总之,存在可以借以实现下列功能的机制。
1.调试器可以通过使用在编译期间生成的调试信息来将机器指令映射到源代码,或执行相反的操作。
2.一个进程能够暂停另一个进程的执行,只要进程选择这样做。为达到此目的,进程使用一个机制来通知另一个进程。例如,一个进程可以通过使用由操作系统提供的信号来通知另一个进程。
3.调试器能够完全控制子进程。这是对任何交互调试器的基本要求。
4.操作系统通过接口调用的系统调用,或者通过允许进程与硬件时钟直接进行通信来向进程提供定时信息。
5.调试器可以通过访问和改变子进程的地址空间或寄存器来在硬件或软件中插入和禁用断点。这可以通过直接改变子程序的存储器(或寄存器)来完成,或通过请求操作系统这样做。
当程序员需要时,所描述的调试器允许程序在一定的时间停止执行。这样的使用时间作为停止程序执行的参数的断点类型,在这里叫做“基于时间的断点”。
基于时间的断点如上所述,断点用于在交互调试器中中止程序执行,以便程序员可以查询程序的状态和执行流。这是通过在硬件中设置专用寄存器或者通过在软件修补存储器来完成的。插入基于时间的断点涉及检测何时启动计时器,跟踪已经消逝的时间,然后在适当的时候中断子程序的执行。
和大多数其他断点一样,基于时间的断点与一行代码关联。当执行相关的一行代码时,启动对应的计时器。一行代码的执行充当计时器的触发器。一旦执行该行,触发器就被激活,计时器被启动。一旦指定时间已经消逝,程序就停止执行。下面将讲述获得这样的效果的细节。
触发器当程序员通过用户界面向调试器提供一个命令时,一个程序在调试器中“运行”。此命令可以是在调试器提供的命令行上键入的“run”或“go”,也可以采用快捷键或鼠标点击操作的形式。当程序即将运行时,调试器通过上文描述的机制在存储器中的可执行映像中设置断点。
用户在源文件中设置基于时间的断点,调试器确定充当启动计时器的触发器的指令。检查在子进程中执行的指令的位置,然后将此位置与触发器位置进行比较,这一过程相对比较耗时,并且效率低下。
相应地,使用替代方法。不是判断是否到达触发器,只要子进程到达设置了触发器的指令,子进程就通知父进程。
图3用流程图显示了如何在存储器中的程序的可执行映像插入触发器所涉及的步骤。所使用的技术类似于用于断点的技术,因此,插入触发器所使用的步骤类似于插入断点所使用的步骤。图3的这些步骤使用带有相应的编号的步骤在下面的表2中列出。
表2步骤310程序员使用调试器的用户界面设置基于时间的断点,然后如果程序正在被首次启动,则启动程序执行。如果程序由于断点而被中止,则程序继续执行。
步骤320基于时间的断点在源代码设置,调试器在可执行程序的映像中确定对应的指令。此指令充当断点的触发器,被称为“触发器指令”,负责启动计时器。
步骤330当程序继续或者在运行时,调试器将存储在触发器的位置的指令保存在其自己的存储器中。然后,调试器用中断指令替换该指令。可选地,调试器还可以使用硬件寄存器来存储触发器指令的地址。在这种情况下,每当触发器指令即将执行时,硬件就通知调试器的过程。
步骤340判断是否还有其他断点存在。如果还有其他断点存在,则执行步骤350。如果没有其他断点,则进入步骤360。
步骤350如果还有其他基于时间的断点,则调试器确定下一个基于时间的断点,并获得该基于时间的断点。然后,处理过程返回到步骤330。
步骤360如果没有其他基于时间的断点,则可以设置其他类型的断点。
步骤370继续执行程序。
由于上文参考图3描述的过程非常类似于设置断点,调试器可以毫无困难地设置触发器。当在调试器中执行程序的过程中到达触发器指令时,程序执行中断,以允许调试器启动计时器,而不是让程序员执行调试操作。程序执行的中止是临时的。使用调试器的程序员不会注意到程序执行过程中的任何差异。
触发器指令启动断点的计时器,然后恢复程序执行。
图4用流程图显示了当在程序执行期间遇到触发器时调试器所涉及的步骤。下面的表3使用带有相应的编号的步骤描述了这些步骤。当到达一个触发器时,调试器引发中断异常,与普通断点的情况相类似。
因此,调试器判断中断异常是由触发器产生的还是由断点产生的。如果是触发器引发的中断异常,那么就启动计时器,程序继续执行。如果是断点引发了中断异常,那么,调试器就允许程序员执行普通的调试操作。
表3步骤410当一个子程序引发中断异常时,调试器会得到有关异常的通知。
步骤420判断是否由触发器引发了异常。如果确实是由触发器引发的异常,则执行步骤430。如果异常不是由触发器引发的,则执行步骤450。
步骤430如果发生异常的指令是一个触发器指令,那么,在使用软件断点的情况下中断指令替换原始指令。在使用硬件断点的情况下,通过寄存器对于指令禁用断点。
步骤440对于调试器中的断点启动计时器。进入步骤460。
步骤450如果异常是由于断点引发的,那么,允许程序员执行普通的调试操作。
步骤460恢复程序的执行。
调试器需要在其基于时间的断点列表中执行简单搜索,以便判断中断指令是触发器还是断点。
上文描述的步骤允许程序员只设置一次性使用的触发器。即,一旦到达触发器,触发器就丢失,除非重新启动程序。作为上述触发器的扩展,可以通过重新插入触发器来实施重现触发器,如下面的表4所描述,优先于上文步骤430中所描述的执行。
表4步骤530在使用软件断点的情况下,用原始指令替换中断指令。在使用硬件断点的情况下,通过寄存器对于指令禁用断点。使用在程序的可执行映像中设置触发器所采用的相同技术,执行单指令,然后在触发器位置重新插入中断指令。
图5用流程图显示了安装重现触发器所需要的附加步骤。对于重现触发器,实施一个涉及应该如何启动计时器,计时器是否已经正在运行的策略。调试器可以选择忽略这样的触发器,或者可以重新启动相应的计时器。或者,也可以为每一个触发器维护不同的计时器。这样的策略决策,以及重现触发器的实施是所描述的“一次性使用”触发器的简单扩展。
图5涉及对应于参考图4描述的步骤410到420和440到460的步骤510到520和540到560。即,图5涉及图4的所有步骤,但步骤430除外,该步骤替换为如上面的表4所描述的步骤530。
在内部,断点和触发器强制正在被调试的程序引发异常,以便调试器可以得到事件的通知。触发器和断点两者都插入中断指令以达到此效果。一旦命中中断指令,触发器和断点就不同。一方面,断点导致调试器将控制转到用户界面线程,以便程序员能够检查存储器内容和寄存器值。程序执行只有在程序员选择这样做的情况下才会恢复。因此,每当命中断点时,程序员都会知道。
相形之下,触发器在内部启动计时器,并继续程序执行,而不会将控制转到用户界面线程。一旦基于时间的断点的计时器启动,程序就恢复执行。程序是否继续执行不取决于程序员的输入,如果在触发器被命中没有通知用户,触发器的功能没有损失。因此,断点实际为程序员中断程序执行,而触发器立刻中断程序执行,以便计时器可以启动。
计时器计时器是基于时间的断点中的跟踪自从触发器首先到达以来消逝的时间的实体。计时器是使用外部时钟直接地或者通过与时钟连接的操作系统实施的。
计时器可以提供不同方式,用于计算消逝的时间,计算调试操作期间的时间,当指定的时间消逝之后要执行的操作。下面将分别描述这些方面。
程序员选择他或她希望用来在计时器启动之后计算时间的技术。所选技术完全地取决于程序员的特定需要。
在多道程序设计环境中,通常不给程序分配CPU(中央处理单元)的全部处理时间。程序只使用总可用时间的一小部分,被称为“时间片”。时间分片给人的感觉好像是许多进程正在同时运行,即使只有一个CPU。
用于时间计算的技术的不同示例如下(i)“挂钟”时间跟踪消逝的时间。时间计算是这样执行的,似乎一个外部实体正在使用精确的秒表计算消逝的时间。
(ii)计算是基于程序正在执行中时消逝的时间来进行的。这意味着,只有当进程正在获取其自己的那一部分时间片时才进行时间计算。这类似于只有当进程(正在被调试的)被分配了CPU时间时才运行的一个外部精确的秒表。当其他进程正在获取CPU时间时,秒表处于暂停状态。
(iii)程序正在执行中时消逝的时间和内核程序代表进程完成请求所花的时间。内核程序执行许多低级的工作来完成一个进程的请求。这是代表进程执行的,以避免低级的损坏(如存储器、硬盘上的文件的损坏等等)。此技术类似于技术(ii),但还包括内核程序被分配给进程的时间。
(iv)基于系统的“运行时间”计算的时间。这是自从系统启动以来消逝的时间。
调试器要求操作系统提供一个用于进行时间计算的功能,因为操作系统与时钟耦合并提供一个到用户空间程序的接口。内核程序可以提供技术(ii)、(iii)和(iv),因为操作系统负责调度进程或者跟踪自从系统启动以来的时间。
定时信息还可以通过除了从操作系统之外的其他技术来获得。例如,在硬件中,当时间消逝之后,计时器可能向芯片发送信号。此事件可能实际设置调试寄存器中的位,以启用断点。
上文描述的四种技术是使用内核程序提供的计时器功能的现有的技术。这些类型的时间计算方法是现有的操作系统(如Linux)所使用的方法。依据调试器的需要和可以用来实施其他时间计算技术的设备,也可以包括其他时间计算技术。
计时器可以是两种类型之一- “周期性的”或“一次性的”。一次性的计时器只将计时器到期的信息通知进程一次,而周期性的计时器重复地执行相同的操作。计时器类型的选择取决于将要实现什么。在使用基于时间的断点的情况下,对消逝的时间,或进程消耗的CPU时间,甚至系统的运行时间进行定时的检查。
当调试器停止子进程的执行时,在调试器再次运行进程之前,不会给子进程分配时间片。因此,操作系统通常不基于子进程的CPU时间提供警报机制。
警报是当计时器到期时发送给进程的通知。
警报是为“挂钟”计时器提供的,因为时钟始终在运行,不管被分配了CPU时间的进程是什么。因此,如果时间计算技术是“挂钟”时间计算技术之外的技术,那么将进行定期检查以判断时间是否消逝。
为向程序员提供更大的灵活性,当调试操作正在执行时,调试器可以自动暂停计时器。如果程序员在为断点的计时器计算消逝的总时间时不想包括花在调试上的时间,这可能是一个非常有用的功能。对于不同的实施方式,这样的计算的精确度可能不同。
如这里所描述的,对于这样的计算,可以使用周期性的计时器。当消逝的时间是基于给进程分配的CPU时间时,也可能需要周期性的计时器,因为当计时器到期时内核程序不通知调试器。为此,调试器将定时检查给进程分配的CPU时间。对于断点计时器的最优解决方案是每一个断点有一个计时器。每一这样的计时器应能够调用其选择的函数。
在实践中,由于多个计时器的局限和开销,可以使用主周期性的计时器来跟踪所有断点的到期时间。此后,这样的周期性的计时器叫做“增量计时器”,因为每个增量时间单位给增量计时器分配了一个脉冲,以便计时器可以跟踪调试器的时间,或者就其他计时器是否到期进行定期检查。
因此,“启动断点的计时器”可以这样来实施,以便断点的到期时间向增量计时器注册,该断点没有单独的计时器。触发器是启动断点的计时器的机制。一旦计时器到期,调试器应该调用相应的函数。
调试器用相应的增量值启动增量计时器来控制计时器的精度。此计时器可以在调试器初始化时或者当到达第一个触发器时设置和启动。当设置了周期性的计时器时,每当计时器到期时,操作系统都通知进程。同样,在使用增量计时器的情况下,内核程序在每个增量时间单位之后通知调试器。
当计时器到期时调用的过程叫做“处理程序过程”。处理程序过程是根据正在使用计时器的应用程序预期的功能而编程的。
在调试器的情况下,这可以是计算消逝的时间,或者执行计时器是否到期的检查。可选地,可以指示增量计时器在一定的时段内停止计算时间。例如,可以指示增量计时器当程序员想在一个断点被命中时检查程序的状态时停止。因此,增量计时器只有在计时器能够下跟踪消逝的时间的情况下才这样做。
增量计时器的一个实施方式可以涉及调试器为每个增量时间单位到期的间隔计时器安装信号处理程序。当为计时器指定的间隔到期时,内核程序向调试器发送信号,此时调用此处理程序。
图6概要显示了程序员选择放置一个单个基于时间的断点的简单示例。在子程序执行期间没有调试中断,因此,可以假设,程序员没有放置任何其他类型的断点,或者当遇到触发器时调试器禁用了任何这样的断点。
图6包括时间轴610。在时间T1 620到达触发器,程序员希望程序在时间T1′620′中断。计时器的到期时间是T秒。即,T=T1′-T1。
由于有一个单个基于时间的断点,当时间是T1时调试器启动增量计时器,然后不断地检查消逝的时间是否到达T1′。当发生这种情况时,调试器向子进程发送一个信号,以通知子进程停止其执行。然后,程序员可以检查程序的状态。
此简单的情况不涉及其他调试操作。如果调试器是这样设计的,以便所有调试操作被禁用,基于时间的断点只限于一个,那么一个简单的间隔计时器可以替换增量计时器。
当遇到触发器时间隔计时器开始计算时间,只有在计时器到期的情况下才会通知调试器。在接收到通知时,调试器应该中止子程序的执行。
图7概要显示了涉及当一个程序正在调试器中执行时的调试操作的两种情况。第一种情况在到达触发器的时间和程序即将中断的时间之间只有一个调试操作。
程序员可能希望忽略在调试期间花费的时间。在第一种情况下,计算的T1 720和T1′720′之间消逝的时间不简单是这两个时间之间的差。这是因为,相关时间还包括时间TD 725,这是在调试期间花费的时间。因此,如果程序员选择忽略时间TD 725,那么增量计时器在执行调试操作期间就会停止。这样的功能是理想的,特别是在TD 725大于T的情况下。
第二种情况涉及花费时间TD1 735和TD2 740的两个调试会话。此第二种情况是与第一种情况相同,只是调试时间分为两个部分,并分布在时间轴710上。如果两个调试操作替换为单个调试操作以便TD=TD1+TD2,那么两种情况基本上相同。如果在调试操作期间停止增量计时器,然后如果为增量计时器绘制一个时间曲线图,对于没有调试操作的简单情况,这样的图形与图6中显示的相同。
图6和7的时间轴610、710只显示了计时器计算的虚拟时间,,而不是消逝的实际时间。因此,即使在到达触发器的时间和程序员希望中断程序的时间之间有调试操作,增量计时器的时间曲线图相当于根本没有调试操作的情况。这有停止增量计时器的机制的情况下是可能的。
当消耗的CPU时间充当到期时间时,内核程序计算当进程在调试器的控制下停止的时间。因此,调试操作不影响这种计算。“挂钟”计时器是程序员可能希望计时器在调试操作期间停止计算的计时器。
图8概要显示了在程序的执行期间有多个基于时间的断点的另一个示例。沿着时间轴810按照T1 820、T2 830、T3 840和T4850的顺序遇到了触发器。为计算多个到期时间T1′ 820′、T2′830′、T3′840′和T4′850′,增量计时器代表具有最早的到期时间的触发器工作。在图8中,当计时器在时间T1 820首先遇到第一个触发器时,计时器在时间T1′820′设置其到期时间。当遇到第二个触发器时,到期时间不改变,因为T1′ 820′少于T2′830′。对于第三个触发器,当计时器在时间T3′ 840′遇到触发器时重新调整其到期时间。这是因为,第三个触发器的到期时间(T3′840′) 少于向计时器注册的到期时间(T1′820′)。第四个触发器的到期时间在T4′850′,这是所有到期时间中最高的。当到达第一个到期时间时,调试器采取适当的操作。然后,计时器将其到期时间重新调整到T1′820′,现在这是剩余的三个触发器的所有到期时间中最小的到期时间。
随着执行的继续进行,到期时间被设置为T2′830′和T4′850′,并且顺序也是如此。计时器的到期时间的变化基于这样的事实T3′840′<T1′820′<T2′830′<T4′ 850′。因此,每当遇到触发器时,都重新调整到期时间。如上所述,如果程序中断执行,则增量计时器的图形与没有调试操作的情况相同。如果计时器停止计算这些时段内的时间,这是可能的。同样,如果在这种情况下使用这样的计时器,那么增量计时器的时间曲线图与图8的相同。
基于时间的断点和计时器的实施方式,可能不同,上述情况可能不同于所描述的实施方式。每一种情况下的最终目标是允许调试器在指定的时间消逝之后暂停执行。如果程序员希望计时器停止计算调试操作期间的时间,那么调试器应该通知计时器何时停止。此通知可以是这样的形式设置布尔标志或撤消注册计时器的处理程序过程,然后,一旦程序员选择继续执行程序,再重新注册标志。内核程序通常不提供暂停计时器的功能。然而,如果内核程序提供此功能,那么调试器就可以使用该功能。
计时器可能需要如上所述改变其到期时间。因此,当遇到触发器时,应该将新到期时间与当前的到期时间进行比较,以便判断应该使用这两个中的哪一个。计时器也可以维护一个到期时间的排序列表,以当到达当前到期时间时选择新到期时间。在将一个到期时间与另一个到期时间进行比较时,所有到期时间都要基于共同的时间线,并与共同的时间线进行比较。为此,到期时间应向计时器注册。
下面的表5显示了一个非常简单的处理程序过程的伪代码示例。
表5<pre listing-type="program-listing">  Timer_Delta_handler()  {  if(Expiry Setting==WALL CLOCK)  {  if timer is disabled then  {  /*do nothing and exit the handler*/  return;  }  else  {  time elapsed-time elapsed+delta+CORRECTION,  if time elapsed≥current expiry  {&lt;!-- SIPO &lt;DP n="19"&gt; --&gt;&lt;dp n="d19"/&gt;  Notify the child program to suspend its execution.  Timer_Disabled=TRUE;  current expiry=get_next_expiry();  }  }  }  else if(Expiry Setting==CPU TIME  {  if(child′s CPU time,consumed==expiry time)  {  Notify the child program to suspend its  execution.  expiry=got_next_expiry();  }  }  else if/*More case*/  }</pre>触发器和计时器是启动计时器并跟踪消逝的时间所必需的。时间消逝之后要采取的操作可以改变,以获得各种类型的基于时间的断点。不是暂停程序执行,而是可以在时间消逝之后插入一个新断点。这提供了在某一个时段内保持睡眠状态的延迟断点的功能。调试器在子程序的启动期间或者当程序继续时插入断点。为进一步扩展这一思想,通过使用触发器-计时器机制,所有或一些断点可以在不久之后启用。这就允许程序员定义“无断点时区”,在这段时间内,所有断点都暂停,或者不起作用。在实践中,如果在调试程序时调试器依赖消逝的时间来执行某些任务,则可以使用触发器-计时器机制。
计算机硬件和软件图9概要显示了一个计算机系统900,该系统可用于执行一个进程中的实施这里描述的技术的步骤。计算机系统900是为执行被编程为执行所描述的技术的计算机软件而提供的。此计算机软件在安装在计算机系统900上的合适的操作系统下执行。
计算机软件涉及一组编程逻辑指令,它们能够被计算机系统900解释,以便指示计算机系统900执行这些指令指定的预先确定的功能。计算机软件可以是以任何语言、代码或表示法记录的表达式,包括一组指令,用于使兼容的信息处理系统直接或者在转换到另一种语言、代码或表示法之后执行特定的功能。
计算机软件是以适当的计算机语言编写的包括语句的计算机程序编程的。计算机程序被使用编译器处理为具有适合于操作系统执行的二进制格式的计算机软件。计算机软件以涉及各种软件组件或者代码装置的方式编程,可以执行所描述的技术的进程中的特定步骤。
计算机系统900的组件包括计算机920、输入设备910、915以及视频显示器990。计算机920包括处理器940、存储模块950、输入/输出(I/O)接口960、965、视频接口945,以及存储设备955。
处理器940是执行操作系统以及在操作系统下执行的计算机软件的中央处理单元(CPU)。存储模块950包括随机存取存储器(RAM)和只读存储器(ROM),并在处理器940的指导下使用。
视频接口945连接到视频显示器990,并提供视频信号,以便显示在视频显示器990上。从由键盘910和鼠标915构成的输入设备910、915中提供操作计算机920的用户输入。存储设备955可以包括磁盘驱动器或者任何其他合适的非易失性存储介质。
计算机920的每一个组件都连接到总线930,总线930包括数据、地址和控制总线,以允许这些组件通过总线930相互进行通信。
计算机系统900可以通过输入/输出(I/O)接口965使用到网络980(表示为“因特网”)的通信信道985连接到一个或多个其他类似的计算机。
计算机软件程序可以作为计算机程序产品提供,并记录在便携存储介质上。在这种情况下,计算机软件程序被计算机系统900从存储设备955访问。或者,计算机软件可以由计算机920直接从网络980进行访问。不论是哪一种情况,用户都可以使用键盘910和鼠标915与计算机系统900进行交互,以操作在计算机920上执行的编程计算机软件。
所描述的计算机系统900只用于说明计算机系统的其他配置或类型也同样可以用于实施所描述的技术。前述的内容只是适合于实施所描述的技术的示例或特定类型的计算机系统。
应用一些程序在执行了比较长的时间之后会表现出性能问题。程序员可能希望为某一个函数放置断点,以便当到达指定的时间之后程序停止执行。在这样的情况下,基于时间的断点是理想的。
在其它情况下,由于产品的逻辑中出现错误,每当消逝一定的时间之后,程序就可能需要调试。一个示例可以一个这样的程序,因为在3个小时的执行之后不能输出任何数据,因此需要调试。在这样的情况下,基于时间的断点也可以有用。
另一个示例是在某一个时间段内被命中多次而在此之后命中次数变少的任何函数。如果所需的效果是在低命中次数期间调试函数,以及如果高命中次数的时间是已知的,那么基于时间的断点可以只在低命中次数期间开始生效。例如,假设一个应用程序,从应用程序开始执行的最初10秒钟期间经常访问一个特定文件。这10秒钟代表应用程序初始化所花的时间。
初始化完成之后,对文件的访问不频繁,而是基于用户事件。现在,假设程序的某一“有缺陷的”部分可能正在不正确地读取文件,并且这是在调查中的需要解决的缺陷。此缺陷在应用程序完成初始化之后仍存在。在这种情况下,程序员可以在文件访问例程中设置一个断点,以便断点只在10秒钟之后生效,即,在应用程序初始化一完成即生效。这里,断点在10秒钟的延迟之后设置,程序员不必在初始化期间的函数中中断,而只有在用户事件导致应用程序访问文件的情况下才中断。
假设一个必须在预先确定的时间段内(如T秒钟)不中断地(甚至通过断点)执行的程序,因为这样的不中断的操作对于程序的逻辑正确地执行至关重要。在执行T秒钟之后,程序的执行可以中断。在这样的情况下,使用基于时间的断点是理想的,因为一个人可能不能判断T秒钟的变量的值。相应地,在这种情况下,和诸如变量计数之类的其他可能的参数相比,时间对于设置断点是更合适的参数。
所描述的实施方式自动地操作,无需程序员人工干预,与现有的方法相比,准确,不容易出错。
其他变化所描述的提供基于时间的断点的技术扩展了当用户指定的时间段消逝之后断点的期限。所涉及的时间计算类似于上文描述的方法。如果用户可以确定,到达断点之后,从当程序恢复执行时的时间开始消逝某一个时段之后不需要该断点,那么这样的功能是理想的。在这种情况下,计时器是恢复执行时启动的,而不是在首次遇到断点时启动的。计时器跟踪自从程序恢复执行以来消逝的时间,如果在指定的时间内断点没有被命中,那么断点将被禁用。这就允许程序员通过使用时间作为参数来禁用断点,利用该参数在调试会话期间访问断点。这样的断点的使用取决于正在被调试的程序的逻辑。
例如,如果程序员知道,断点没有用,除非断点自从程序恢复执行以来在十秒钟内到达,那么程序员可以选择在十秒钟消逝之后让断点到期。在计时器到期之前如果断点被命中要执行的操作依据实施方式而有所不同。例如,一个实施方式可以选择即使断点被命中断点到期之后仍继续。同样,另一种实施方式可以选择重置计时器,当执行恢复之后再次启动计时器。第三种实施方式可以选择将断点作为任何普通断点,如果该断点在指定的时间消逝之前被命中。在任何情况下,断点基于时间的度量到期,而不是基于计数值。
上述方法还可以扩展到一种特殊类型的断点,如果在受监视的时间段内一直没有用户干预(即,输入),则继续执行。例如,如果用户自从断点到达以来三秒钟内没有按下任何键,那么程序应该继续,而不是等待用户输入。如果程序员希望观察程序的执行,并希望在一个函数暂停以确定是否到达该函数,那么此功能可以提供帮助。计时器跟踪自从断点被命中以来的时间。如果时间消逝,而没有程序员提供任何用户输入,则调试器自动恢复程序的执行。
有了这种类型的断点,程序员就不必通过提供基于键盘或基于鼠标的输入来强制调试器继续执行。
这种功能对于那些使用调试器作为理解程序的功能的学习工具的程序员特定有帮助。这样的功能还允许程序员专心于其他任务,如记录有关程序的执行的笔记,或者,参考类别图表,或者要求提供用户输入对程序员造成不便的任何其他任务。
结束语这里在提供基于时间的断点的调试器的上下文中描述了一种方法、计算机系统和计算机软件。
那些精通相关技术的人员将知道,可以对这里描述的技术和方案进行各种变更和修改。
权利要求
1.一种适合于帮助调试计算机软件程序的、使用基于时间的断点调试计算机程序的方法,该方法包括下列步骤遇到与正在被调试的代码的执行关联的断点;激活计时器以监视时间的度量;监视计时器以确定受监视的时间何时超过预先确定的时间段;一旦受监视的时间超过预先确定的时间段,执行预先确定的操作。
2.根据权利要求1所述的方法,其特征在于,受监视的计时器测量自从遇到断点以来消逝的时间。
3.根据权利要求1所述的方法,其特征在于,受监视的计时器测量自从遇到断点以来消逝的时间,在该时间内,中央处理单元执行相关进程。
4.根据权利要求1所述的方法,其特征在于,受监视的计时器测量自从遇到断点以来消逝的时间,在该时间内,中央处理单元执行相关进程和操作系统内核。
5.根据权利要求1所述的方法,其特征在于,预先确定的操作包括暂停执行相关进程的步骤,该进程涉及正在被调试的代码。
6.根据权利要求1所述的方法,其特征在于预先确定的操作包括在正在被调试的代码中插入其他断点的步骤。
7.根据权利要求1所述的方法,进一步包括下列步骤一旦遇到断点,暂停执行相关进程,该进程涉及正在被调试的代码。
8.根据权利要求7所述的方法,其特征在于,预先确定的操作包括恢复执行被中止的进程的步骤,该进程涉及正在被调试的代码。
9.根据权利要求7所述的方法,进一步包括在所说的暂停执行相关进程的步骤之后恢复执行被中止的相关进程的步骤。
10.根据权利要求9所述的方法,其特征在于,受监视的计时器测量自从恢复相关进程的执行以来消逝的时间。
11.根据权利要求10所述的方法,其特征在于,预先确定的操作包括下列步骤重新暂停执行相关进程;停用正在被调试的代码中的遇到的断点;以及在停用遇到的断点之后,恢复重新暂停的相关进程的执行。
12.根据权利要求10所述的方法,其特征在于,预先确定的操作包括下列步骤重新暂停执行相关进程;删除正在被调试的代码中的遇到的断点;以及在停用遇到的断点之后,恢复重新暂停的相关进程的执行。
13.根据权利要求8所述的方法,其特征在于,只有在用户输入在受监视的时间内没有被接收的情况下才执行预先确定的操作。
14.一种适合于帮助调试计算机软件程序的、使用基于时间的断点调试计算机程序的记录在介质上的计算机软件,该计算机软件包括用于遇到与正在被调试的代码的执行关联的断点的代码装置;用于激活计时器以监视时间的度量的代码装置;用于监视计时器以确定受监视的时间何时超过预先确定的时间段的代码装置;用于一旦受监视的时间超过预先确定的时间段便执行预先确定的操作的代码装置。
15.一种适合于帮助调试计算机软件程序的、使用基于时间的断点调试计算机程序的计算机系统,该计算机系统包括用于遇到与正在被调试的代码的执行关联的断点的装置;用于激活计时器以监视时间的度量的装置;用于监视计时器以确定受监视的时间何时超过预先确定的时间段的装置;用于一旦受监视的时间超过预先确定的时间段便执行预先确定的操作的装置。
全文摘要
在调试器中提供一种基于时间的断点功能,以允许程序员在任何一行代码设置基于时间的断点。如果该行在执行,而指定时间的消逝,那么程序执行会因此而停止。调试器为每一个这样的基于时间的断点跟踪消逝的时间。可以有许多条件用于为每一个断点计算时间。
文档编号G06F11/28GK1487415SQ0315495
公开日2004年4月7日 申请日期2003年8月25日 优先权日2002年8月26日
发明者莫赫特·凯尔拉, 莫赫特 凯尔拉 申请人:国际商业机器公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1