示例性操作系统
回到附图,在该附图中,相同的标号数指示着相同的元件,并以一个适当的计算环境的实现来说明本发明。尽管这并不是所必需的,但是本发明的讨论采用了计算机可执行指令的主要内容,例如,由一台个人计算机执行的程序模块。程序模块一般可包括,例程、程序、对象、元件、数据结构、等等,它们可用于执行特殊的任务或实现特殊的抽象数据类型。
图1说明了一例适当的计算环境100实例,该实例依次讨论了所能够(全部或部分)实现的适用于Just-My-Code代码(JMC)的系统、装置和方法。示例性计算环境100仅仅只是一例适当的计算环境的实例,并不试图建议对本文所讨论的系统和方法的使用及功能作出任何限制。计算环境100不应该解释成其具有与计算环境100所说明的元件的任何一个或其组合有关的独立性或必要性。
本文所讨论的方法和系统可以众多其它通用或专用计算系统环境或配置进行操作。众所周知,适用于用户的计算系统、环境和/或配置的实例可以包括,但并不限制于,个人计算机、服务器计算机、微处理器系统、基于微处理器的系统、网络PC、小型计算机、大型计算机、包括任何上述系统或设备的分布式计算环境,以及其它等等。也可以在有限资源的客户机中实现框架紧缩型的或其子集的方式,例如,手持计算机或者其它计算设备。本发明也可以分布式计算环境来实现,在这类计算环境中,可以由通过通讯网络连接的远程处理设备来执行任务。在一个分布式计算环境中,程序模块可以放置于本机和远程存储器存储设备中。
正如图1所示,计算环境100包括一个采用计算机102形式的通用计算设备。计算机102的元件可以包括,但并不限制于,一个或多个处理器或处理器单元104,一个系统存储器106,以及一个总线108,其中,该总线将包括系统存储器106的各种不同的系统元件与处理器104相耦合。系统总线108表示几种类型的总线结构中的任何一种或多种,它包括,一种存储器总线或存储器控制器,一种外围总线,一种加速图形端口,以及使用多种总线结构中任一种的一个处理器或本机总线。举例来说,但并不限制于,这类架构可以包括工业标准结构(ISA)总线、微通道结构(MCA)总线、视频电子标准协会(VESA)本机总线、以及外围元件互连(PCI)总线(也称之为Mezzanine总线)。
计算机102典型地包括多种计算机可读介质。这类介质可以是计算机102可以访问的任何一种有效介质,并且它可以包括易失性或非易失性介质,可移动或不可移动的介质。在图1中,系统存储器106包括易失性存储器形式,例如,随机存取存储器(RAM)110;以及非易失性存储器形式,例如,只读存储器(ROM)112,的计算机可读介质。基本输入/输出系统(BIOS)114存储于ROM 112中,它包含着诸如在启动过程中有助于在计算机102各元件之间传递信息的例程。RAM 110一般包含着数据和/或程序模块,它可以由处理器104进行即时访问和/或当前正被操作。
计算机102还可以包括其它可移动/非移动性,易失性/非易失性计算机存储介质。例如,图1说明了一个适用于读写的非移动且非易失性的磁性介质的硬盘驱动器116(未显示且一般可称之为“硬盘驱动器”),一个适用于读写的可移动且非易失性磁盘120的磁盘驱动器118(即,软盘),以及一个适用于读写的可移动且非易失性光盘124的光盘驱动器122,光盘可包括CD-ROM/R/RW,DVD-ROM/R/RW/+R/RAM或者其它光介质。硬盘驱动器116,磁盘驱动器118和光盘驱动器122每一个采用一个或多个接口126与总线108相连接。
驱动器和相关的计算机可读介质提供了计算机可读指令、数据结构、程序模块、以及其它适用于计算机102的数据的非易失性存储。虽然本文所讨论的示例性环境采用了一个硬盘,一个可移动的磁盘120以及一个可移动的光盘124,但是,本领域的熟练技术人士应该理解的是,在该示例性计算环境中也可以使用其它类型能够存储计算机所能访问数据的计算机可读介质,例如,磁带盒、闪存存储卡、数字视频光盘、随机存取存储器(RAMs)、只读存储器(ROM),以及其它类似等等。
用户可以通过输入设备,例如,键盘140和定位设备142(例如,“鼠标器”)向计算机102提供命令和信息。其它输入设备(未显示)可以包括麦克风、操纵杆、游戏垫、碟型卫星天线、串行端口、扫描仪、摄像机、等等。这些和其它输入设备都可以通过与总线108相耦合的用户输入界面1 44连接着处理单元104,但是也可以通过其它接口和总线结构来连接,例如,并行端口、游戏端口或通用串行总线(USB)。
监视器146或者其它类型的显示设备也通过一个接口(例如,视频适配器148)与总线108相连接。除了监视器146之外,个人计算机一般包括其它外围输出设备(未显示),例如,扬声器和打印机,它们可以通过输出外围接口150相连接。
计算机102可以在使用与一个或多个远程计算机(例如,远程计算机152)逻辑连接的网络环境中操作。远程计算机152可以包括与计算机102有关的本文所讨论的许多或所有元件和性能。在图1中所显示的逻辑连接可以是一个局域网(LAN)154和一般的广域网(WAN)156。这类网络环境在办公室中是常见的,包括企业内部范围的计算机网络,企业内的互联网和因特网。
当在LAN网络环境中使用时,计算机102通过网络接口或适配器158与LAN 154相连接。当在WAN网络环境中使用时,计算机一般可包括一个调制解调器160或者适用于建立通过WAN 156通讯的其它部件。调制解调器160可以是内置的或者是外置的,它可以通过用户输入界面144或者其它合适的机制与系统总线108相连接。图1中所说明的是一个通过因特网的WAN的特殊实现。这里,计算机102采用调制解调器160来建立通过因特网162与至少一个远程计算机152的通讯。
在一个网络环境中,所表示的程序模块与计算机102有关或者部分与计算机102有关,该程序模块存储一个远程存储器存储设备中。即,正如图1所说明的那样,远程应用程序164可以驻留在远程计算机152的一个远程设备中。应该理解的是,所显示和所讨论的网络连接都是示例性的,也可以使用在计算机之间建立通讯链路的其它装置。
大量的程序模块可以存储于硬盘、磁盘120、光盘124、ROM 112、或者RAM 110,还可包括一个提供一个运行时间的环境、适用于Just-My-Code代码(JMC)调试的应用程序130、其它程序模块132(例如,设备驱动器等等),以及诸如源代码的程序数据134、中间汇编、等等的操作系统。
图2是一个进一步显示图1所示系统存储器106的示例性方面的方框图,它可以包括适用于Just-My-Code代码(JMC)调试应用程序130和程序数据134。应用程序130和程序模块134包括,例如,一个过程202,一个初级编译器204,一个适用于调试该过程202的调试程序(“调试器”)206,以及一个共享的运行时间组件(“运行时间”)208。过程202是已经编译成汇编程序(未显示)的源代码(未显示),并依次已经编译成原始代码210;年个202表示通过调试器206所执行的原始代码210。
源代码可以表示成以任何类型的计算机编程语言写成的计算机编程代码。具有调试开关(即,“/debug”) 被打开的初级编译器204,将源代码编译成汇编程序(未显示)。这样一个初级变压器可以是任何类型的计算机程序语言编译器,例如,C、C++、C#,VB,和/或已经改进成了实现JMC调试的其它类型编译器。汇编程序是众所周知,并且可表示成在源代码变换成平台所指定的适用于执行的原始代码中的一个中间阶段。为此,这样一个汇编程序可以包括,例如,独立于中间语言(IL)和相关元数据的平台/处理器。
用户可以将汇编程序下载到调试器206。适用于将一个汇编程序下载到一个应用的技术是众所周知的。例如,一个汇编程序可以通过指定(即,通过命令行或者UI)汇编程序的名称或者通过选择汇编程序下载到调试器206。在汇编程序下载的操作过程中,调试器206在汇编程序的不同位置上产生一列表感兴趣的代码,这感兴趣代码是与不感兴趣代码相比较而言。用户代码(迭代:源代码变换成一个汇编程序,以及,最后,正如以下所讨论的,变换成原始代码,该原始代码可作为一个过程来执行)一般都包括某些感兴趣代码和不感兴趣代码的组合。然而,用户代码并不包括属于运行时间208的主机代码。在一个实施例中,感兴趣的代码可以通过用户输入到用户输入界面144的任何一个方面来产生(图1)。例如,用户可以选择(即,通过一个定位设备、键盘、语音激励技术,等等)来指定在调试服务的对话框中所显示的多种方法中的一种方法,或者用户可以将编程结构的名称打入到命令行的界面,和/或其它等等。调试器206通过所暴露的调试API 216通知所识别的感兴趣的代码的调试服务214,而调试API 216也可以两个部分来实现,一个部分可以作为一个运行时间208的服务,而另一部分可以作为执行216的公共部分组件且可以由调试器206来消费。
在该点上,用户可指示调试器206开始JMC调试操作,例如,可通过使能一个JMC菜单项目、命令行,或者其它JMC使能指令,以及发布一个运行或启动命令。这就使得调试器206指示JIT编译器220将汇编程序变换成原始代码210,以便于以后能作为过程202来执行。
在汇编程序变换的操作过程中,JIT编译器220将JMC使能元件、标志222和调试探头224插入原始代码210的编程结构中(即,执行文件/二进制文件、动态连接库、分类目标和方法、静态函数,和/或其它类似)。在该实施例中,在分类方法和/或静态函数的前言之后立即插入该JIT使能元件。JIT编译器220通过分解感兴趣的代码列表/标识212来识别这类分类方法和/或静态函数,正如以上所讨论的,感兴趣的代码列表/标识212是由调试器206产生的。
调试器探头224参考一个相关的标志222,并通过一个编译时间常数指针值,来确定是否请求调试服务214来仿真已经遇上有效探头224所执行的特殊线程是否执行一个JMC单步操作,并且如果是的话,则暂停该过程202。正如我们现在所讨论的,这样的确定对干不激活的标志是不会产生,而是仅对激活的标志产生的。一个典型的调试探头24可以下列方式来表示:
If(*pFlag){call JMC_Probe},
式中,pFlag是一个指向对应标志222的编译时间常数指针(*表示该地址将再作为运行时间的间接引用以产生相关标志222的数值)。探头224的“JMC_Probe”部分是一个对JMC_Probe函数226的调用。通过指针间接指向的使用,与所插入的编程结构有关的多个编程的PFlags可以全都指向相同的标志222。
正如所说明的,调用JMC_Probe 226是一个条件,它取决于参考标志222是否激活或者不激活。为了讨论的目的,一个激活的标志222具有一个非零的数值,其中一个不激活的标志具有一个零数值或空数值。当标志222是激活的,我们就说该调试探头224是激活的(即,是一个有效的探头),反之亦然。例如,当执行的一个线程遇上了一个有效的探头224,则产生对JMC_Probe的调用。相类似,遇上一个不激活的探头224就不会产生这类调用。这就意味着翻转触发一个信号标志222可以分别激活或不激活各个相关的探头224。现在,我们讨论这些新颖的JMC调试探头224在调试过程中是怎样使用的。
当过程202已经暂停(通过任何传统的单步机制,例如,通过采用一个传统的断点)时,调试器206允许用户通过JMC单步命令228使得JMC步进通过过程202(即,JMC单步进入,JMC单步退出,和JMC单步跳过命令)。这类命令可以由用户来选择,例如,通过命令行和/或用户界面144的UI部分(图1)。响应于由调试器206对调试服务214发出的一个JMC单步命令228(即,JMC单步进入,单步退出,和单步跳过命令228的任何一个),调试服务大量使能标志222,并从而激活相关的探头224。用户不需要知道那一个探头是激活的。
激活调试探头224允许过程202的执行线程可以自由运行通过不感兴趣的代码(即,可以基本上全速和不需要暂停)。遇上一个激活探头224的任何线程,只要它是处于感兴趣代码中的,就只有在所遇到的探头224通过对JMC_Probe 226的调用才能暂停(通过TriggerMethodEntry 230),以确定该线程是JMC单步通过代码的。为了能达到这一点,JMC_Probe 226通过对调用堆栈的解码来滤去不能进行JMC单步操作的线程(见,其它数据232),从而识别与所遇到的(“已触发的”)调试探头224相关方法的指令指针(ip)和帧指针(fp)。如果当前的线程是进行JMC单步操作,则JMC_Probe 226就将ip和jp参数发送给TriggerMethodEntry 230,它可以随后将ip映射回该方法以确定该方法是否是感兴趣的方法,正如在感兴趣代码列表/标识212中所指示的。(运行时间208能够为JIT编译器220作充分的薄记工作,以完全保证一个调试探头224可以通过一个查找表的操作来确定该探头是否处于所感兴趣的代码中)。如果该方法是感兴趣的,则JMC_Probe 226就可以在调试探头224之后插入一个中断的操作码(即,一个断点234,JMC_Probe可以注入所有的断点234),并且继续过程的执行,使得执行的线程将会击中断点234以及在所感兴趣的代码中停止下来。
另外,如果JMC单步命令228是:
·JMC单步进入,调试服务214可激活所有的JMC_Probe 224并且在调用了单步进入的方法之后设置一个断点(不是JMC_Probe被插入的断点)。因为JMC_Probe 224是处在所有感兴趣代码的起始位置上,并且激活探头,如果调用最后调用了任何“感兴趣”代码(也许非直接地,通过调用最终包括“感兴趣”代码的不感兴趣/框架代码),则该线程将击中一个激活的探头,该探头将调用TriggerMethodEntry 230-停止该线程,在感兴趣的代码中单步完成。如果该调用未调用任何感兴趣的代码,则该线程将击中所插入的断点,它插入在单步进入方法的调用之后(即,不是JMC_Probe插入的断点234),并且JMC单步进入的操作将完成。采用这样的方法,进程202可以在JMC单步进入起始和它结束之间的时间内全速运行(即,不会被中间断点操作码中断)。
·JMC单步退出,JMC_Probe 226自动将一个断点234插入在由第一堆栈帧所标识的对一个感兴趣的方法的返回地址上。于是,断点就没有插入在调试探头224能够遇上的相同方法中。为了定位具有对一个感兴趣的方法的一个返回地址的第一堆栈帧,调试服务214完全走过过程堆栈(即,展开堆栈)。由于“其它数据”232的“感兴趣方法的数据”部分,调试服务214可采用一个或多个运行时间208的服务,以确定一个堆栈帧的地址是否为一个感兴趣方法的返回地址。这类感兴趣方法数据包括,例如,适用于各种感兴趣方法的各自基本上唯一的ID(即,一个GUID)。在一个实施例中,在调试探头224插入操作的过程中,由JIT编译器220产生感兴趣方法的数据。
·JMC单步跳过和线程不在该方法结束的地方执行一个指令的话,就会引起JMC_Probe 226产生类似于一个传统单步跳过的行为。如果该线程是在该方法结束的地方,则JMC_Probe 226就会产生类似于单步进入和单步退出的行为,使得该线程将停止在所执行的感兴趣代码的第一个实例(第一行)中。
响应JMC单步命令228的完成,这是由正在暂停的这个JMC单步线程所证明的,调试服务214禁止所有的探头(调试器206仅理解JMC单步操作;它不知道有关探头的其它事情)。不激活探头224是避免对JMC_Probe 226不必要调用的性能最优化,否则这种调用就可能在没有执行的线程是JMC单步指令的情况下发生。
在本实施例中,标志222是一个按模块基础上的使能/禁止(即,每个模块中被插入一个(1)标志)。采用这样的方式,在该模块中的各种方式都对pFlag具有相同的数值。这一特殊的实施例是基于代码是在按模块的基础上的感兴趣和不感兴趣的合理性的。应该意识到,不同的合理性可以以不同的实施例作为一个特殊实施例设计的一个函数来加以实现。例如,标志222也可能插入在按方法的基础上;可以对更多的标志222作些折中,可以通过改变/触发一单个标志222的值来使能较少的探头。
因此,遇到一个有效探头224的所有线程都会产生对JMC_Probe 226的调用,即使当前的线程不是JMC单步执行。只有当JMC_Probe 226确定该线程时一个JMC单步时,该线程才被暂停。类似的,如果所遇上的线程调试探头224不是有效的,则对JMC_Probe 226的调用就完全可以跳过,并且该线程可以正常(即,自由地运行)地继续执行通过该进程。
在该实施例中,对JMC_Probe功能的调用226可以在尺寸上得到充分的优化,因为它没有采用任何参数且没有返回数值。这就提供了用于优化所有的“调用位置”(产生对JMC_Probe函数226的各个调用的位置)。于是,如果对来自调试探头224的JMC_Probe的调用是来自探头的(即,当*pFlag!=0),则该调用就将不再是一个重的加权调用。这就意味着该调用并不需要对堆栈进行压入和弹出函数参数。这依次意味着与需要压入和弹出参数的函数相比较,JMC_Probe调用位置的加权要轻得多。一线程在遇到一个无效探头224要经受的唯一处理开销是它要进行处理以评估标志222(通过一个*pFlag)以及跳到插入的调试探头224之后的下一个指令。采用这一方式,所讨论的JMC调试操作允许线程以基本全速的方式自由运行,以通过所有不感兴趣的代码直至遇上所感兴趣的代码。
在本实施例中,运行时间208是基于一种公用语言基础结构(CLI),该公用语言基础结构提供了适用于可执行代码的规范以及它所运行的虚拟执行环境。于是,尽管在图2中分别进行了说明,但是过程208和运行时间202都是相同执行环境中的一个部分(即,过程202控制着运行时间208)。
示实例过程
图3显示了一例适用于JMC调试的示例性过程。为了讨论的目的,这些过程操作都可以参考图1和图9所示的程序模块和数据特性来讨论。在方框302,调试器206(图2)识别所感兴趣的计算机程序编程结构(即,对象方法和/或静态功能)。在一个实施例中,这是通过用户输入到用户输入界面144的任何方面来完成的(图1)。例如,用户可以选择(即,通过一个指点设备、键盘、语音激励技术,等等)来指定在调试服务的对话方框中所显示的多种方法中的一种方法,或者用户可以将编程结构的名称键入一个命令行的界面上,和/或类似的。这些用户输入可以存储于所感兴趣的代码列表/标识212中(图2),它可以由运行时间编译器220读取(图2),以识别感兴趣的编程结构(即,感兴趣的代码)。
在方框304,调试器206(图2)指令一个编译器,例如,图2所示的Just-In-Time(JIM)编译器220,以便于在编译操作过程中将JMC使能元件222和224自动插入计算机程序的各个独立的编程结构(即,方法)。在本实施例中,JIT编译器220编译一个汇编程序,以产生原始代码210(图2),并自动地将各个标志222和调试探头224插入到原始代码210中。在方框306,在调试器206的控制下作为进程202(图2)来执行原始代码210(图2)。该进程202包括任何数量的作为该进程的特殊实施的函数的执行线程。
在方框308,调试器206(图2)允许用户输入一个JMC单步命令228来JMC单步步进的一个线程以通过该进程202。因为该线程是JMC单步步进的,所以该线程可称之为一个“感兴趣的线程”。该进程202可以是多线程的并且包括一个不是JMC单步步进的线程,该线程可以称之为一个“不感兴趣的线程”。一个感兴趣的线程可以自由运行(无暂停的)通过不感兴趣的代码(即,与在方框302中所选择的编程结构中的所选择的/单独的结构所识别的感兴趣代码不相对应的任何代码)。感兴趣线程可由调试服务214自动暂停,无论何时只要它首次遇上一个有效的调试探头224。确定过程202的一个线程(图2)是否为一个感兴趣的线程以及所感兴趣的线程是否为一个在所选择的编程结构中的执行/访问的代码是由所遇上的调试探头224在运行时间确定的。于是,所有非JMC单步线程都可以自由运行通过在感兴趣代码和不感兴趣代码中所有遇上的调试探头224。
结论
所讨论的系统和方法提供了JMC调试。尽管已经讨论的系统和方法在语言上特定于结构性能和操作方法,但是在权利要求中所限定的主题内容并不一定要限制于上述的特定的性能和操作。相反,只是以实现权利要求的主题内容的示例性形式披露了特定的性能和操作。