一种残膜回收机防缠绕挑膜装置的制 一种秧草收获机用电力驱动行走机构

用于时间旅行调试和分析的计算机系统、计算机系统处实施的方法和硬件存储设备与流程

2022-11-30 11:06:23 来源:中国专利 TAG:

用于时间旅行调试和分析的计算机系统、计算机系统处实施的方法和硬件存储设备
1.本技术是申请日为2017年11月6日、中国国家申请号为201780069596.2、发明名称为“用于时间旅行调试和分析的计算机系统、计算机系统处实施的方法和硬件存储设备”的中国发明专利申请的分案申请。


背景技术:

2.当在软件应用的开发期间编写源代码时,开发人员通常花费大量时间“调试”源代码以在代码中找到运行时错误。例如,开发人员可以采用几种方法来再现和本地化源代码故障(bug),诸如基于不同输入来观察程序的行为、插入调试代码(例如,打印变量值、追踪执行的分支等)、临时去除代码部分等。追踪运行时错误用以查明代码故障并占用应用开发时间的很大一部分。
3.已经开发了调试应用(“调试器”)以便协助代码调试进程。许多这种工具提供跟踪、可视化和更改计算机代码的执行的能力。例如,调试器可以可视化代码指令(例如,源代码、汇编代码等)和变量值的执行,并使得用户能够更改代码执行的各方面。通常,调试器使用户能够在源代码中设置“断点”(例如,源代码中的特定指令或语句),当在执行期间到达该断点时,使程序的执行被暂停。当源代码执行被暂停时,可以向用户呈现变量值并给予选项以选择如何继续(例如,通过终止执行、通过像正常那样继续执行、通过进入、跳过或退出语句/函数调用等)。然而,经典的调试器使得只能在单个方向(向前)中观察/更改代码执行。例如,经典的调试器不支持用户选择返回到之前的断点。
4.新出现的调试形式是“时间行程”调试,其中程序的执行被记录到跟踪中,然后可以对其进行向前和向后重放和分析。


技术实现要素:

5.本文的实施例涉及用于时间行程调试的记录和重放跟踪的新实施方式,相对于之前尝试,其可以产生几个数量级上的性能改进,使得能够记录其线程跨多个处理单元并发自由运行的多线程程序,并且可以产生相对于之前尝试的跟踪文件,具有大小减小几个数量级的跟踪文件。
6.在一些实施例中,一种用于使用高速缓存数据来记录对可执行实体的执行的可重放跟踪的方法包括跨一个或多个处理器的一个或多个处理单元执行可执行实体的一个或多个线程。方法还包括,在一个或多个线程的执行期间,独立地为每个线程记录分离的可重放跟踪。记录分离的可重放跟踪包括,对于每个线程,记录针对线程的初始处理器寄存器状态。记录分离的可重放跟踪还包括,对于每个线程,并且在基于线程的执行检测到处理器数据高速缓存未命中时,响应于处理器数据高速缓存未命中而记录被导入到处理器数据高速缓存中的至少一行高速缓存数据。
7.提供本发明内容是为了以简化的形式介绍一些概念,这些概念将在下面的具体描述中进一步描述。本发明内容不旨在标识所要求保护的主题内容的关键特征或必要特征,
也不旨在用于帮助确定所要求保护的主题内容的范围。
附图说明
8.为了描述可以获得本发明的上述和其他优点和特征的方式,将通过参考在附图中图示的具体实施例来呈现上面简要描述的本发明的更具体的描述。应当理解,这些附图仅描绘了本发明的典型实施例,并且因此不应当认为是对其范围的限制,本发明将通过附图的使用,利用附加特征和细节进行描述和解释,其中:
9.图1图示了示例计算机架构,其中时间行程调试的实施例可以操作;
10.图2图示了用于记录对多线程进程的执行的可重放跟踪的示例方法的流程图;
11.图3图示了对跨并发执线程的事件进行排序的示例;
12.图4图示了使用卷影副本的示例;
13.图5图示了环形缓冲器的示例;
14.图6图示了用于基于处理器高速缓存的跟踪的示例计算机架构;以及
15.图7图示了用于使用高速缓存数据来记录对可执行实体的执行的可重放跟踪的示例方法的流程图。
具体实施方式
16.本文的实施例涉及用于时间行程调试的记录和重放跟踪的新实施方式,其可以产生相对于之前尝试的数量级上的性能改进,使得能够记录其线程跨多个处理单元并发自由运行的多线程程序,并且可以产生相对于之前尝试的跟踪文件,具有大小减小几个数量级的跟踪文件。
17.通常,时间行程调试的目标是在跟踪中捕获可执行实体(例如,用户模式线程、内核线程、管理程序等)执行哪个处理器指令,使得可以在稍后的时间以所需的任何粒度来绝对精确地从该跟踪重放这些指令。能够重放作为应用代码的一部分被执行的每个指令,使得稍后能够再现应用的向后重放。例如,为了在向后方向上命中断点,将从该断点之前的时间重放跟踪,并且重放停止在断点命中的最后一次时,即在调试器当前正在分析代码流之前。
18.提供时间行程调试的之前尝试已经遭受了导致受限地采用的若干折衷。例如,之前尝试对代码执行施加了显著的限制,诸如要求跟踪包括所执行的所有指令的严格排序(即,完全顺序一致的记录模型)。例如,这可以通过要求在单个核上非并发地执行多线程程序,或者通过要求在多个核上以锁步方式非并发地执行程序指令来实现(例如,在一个处理器核上执行n个指令,然后在另一处理器核上执行n个指令,等等)。鉴于当今的高度多线程代码和高度并行的多核和超线程处理器,这是很大的限制。另外,之前尝试会导致程序性能显著下降,并且通常会产生异常大的跟踪文件(特别是在模拟多个核时),至少部分是因为它们确定性地记录每一条指令的执行,并在程序执行期间创建完整存储器状态的综合记录。前面的每一个都使时间行程调试处的之前尝试对于在生产环境中使用和用于程序执行的长期跟踪既异常地缓慢又不现实,特别是对于具有许多并发运行的线程的应用。
19.操作环境
20.初始地,图1图示了示例计算环境100,其中时间行程调试的实施例可以根据本发
明进行操作。本发明的实施例可以包括或利用专用或通用计算机系统101,其包括计算机硬件,诸如例如,一个或多个处理器102、系统存储器103、一个或多个数据存储库104,和/或输入/输出硬件105。
21.本发明范围内的实施例包括用于携带或存储计算机可执行指令和/或数据结构的物理和其他计算机可读介质。这种计算机可读介质可以是可以由计算机系统101访问的任何可用介质。存储计算机可执行指令和/或数据结构的计算机可读介质是计算机存储设备。携带计算机可执行指令和/或数据结构的计算机可读介质是传输介质。因此,通过示例而非限制,本发明的实施例可以包括至少两种截然不同的计算机可读介质:计算机存储设备和传输介质。
22.计算机存储设备是存储计算机可执行指令和/或数据结构的物理硬件设备。计算机存储设备包括各种计算机硬件,诸如ram、rom、eeprom、固态驱动器(“ssd”)、闪存、相变存储器(“pcm”)、光盘存储装置、磁盘存储装置或其他磁存储设备,或可以被用来以计算机可执行指令或数据结构的形式存储程序代码并且可以由计算机系统101访问和执行以实施本发明所公开的功能的任何其他(多个)硬件设备。因此,例如,计算机存储设备可以包括所描绘的系统存储器103和/或所描绘的数据存储库104,其可以存储计算机可执行指令和/或数据结构。
23.传输介质可以包括网络和/或数据链路,其可以被用来携带计算机可执行指令或数据结构形式的程序代码,并且可以由计算机系统101访问。“网络”被定义为一个或多个数据链路,其使得能够在计算机系统和/或模块和/或其他电子设备之间运输电子数据。当通过网络或另一通信连接(硬连线、无线或硬连线或无线的组合)向计算机系统传递或提供信息时,计算机系统可以将该连接视为传输介质。上述的组合也应当被包括在计算机可读介质的范围内。例如,输入/输出硬件105可以包括连接网络和/或数据链路的硬件(例如,网络接口模块(例如,“nic”)),该网络和/或数据链路可以被用来携带计算机可执行的指令或数据结构形式的程序代码。
24.另外,在到达各种计算机系统组件时,计算机可执行指令或数据结构形式的程序代码可以自动地从传输介质被传递到计算机存储设备(反之亦然)。例如,通过网络或数据链路接收的计算机可执行指令或数据结构可以被缓冲在nic(例如,输入/输出硬件105)内的ram中,并且然后最终传递到系统存储器103和/或在计算机系统101处的更少的易失性计算机存储设备(例如,数据存储库104)。因此,应当理解,计算机存储设备可以被包括在还(或甚至主要)利用传输介质的计算机系统组件中。
25.计算机可执行指令包括例如指令和数据,当在一个或多个处理器102处被执行时,该指令和数据使计算机系统101执行特定功能或功能的组。例如,计算机可执行指令可以是二进制文件、诸如汇编语言的中间格式指令,或甚至是源代码。
26.本领域技术人员将理解,本发明可以在具有许多类型的计算机系统配置(包括个人计算机、台式计算机、膝上型计算机、消息处理器、手持式设备、多处理器系统、基于微处理器或可编程的消费电子产品、网络pc、小型计算机、大型计算机、移动电话、pda、平板电脑、寻呼机、路由器、交换机等)的网络计算环境中被实践。本发明还可以在分布式系统环境中被实践,其中通过网络链接(通过硬连线数据链路、无线数据链路或通过硬连线和无线数据链路的组合)的本地计算机系统和远程计算机系统都执行任务。如此,在分布式系统环境
中,计算机系统可以包括多个组成计算机系统。在分布式系统环境中,程序模块可以位于本地存储器存储设备和远程存储器存储设备中。
27.如所图示的,数据存储库104可以存储表示时间行程调试器106和应用代码107的计算机可执行指令和/或数据结构,应用代码107是时间行程调试器106的跟踪/调试的主题。当这些程序正在执行(例如,使用(多个)处理器102)时,系统存储器103可以存储对应的运行时数据,诸如运行时数据结构、计算机可执行指令等。因此,图1将系统存储器103图示成包括时间行程调试器运行时数据106’和应用代码运行时数据107’。
28.如所描绘的,时间行程调试器106包括一个或多个组件或模块,诸如记录组件106a和重放组件106b。在适当的时候,这些组件还可以包括系统存储器103中的对应的运行时数据(被图示为记录运行时数据106a’和重放运行时数据106b’)。在执行期间,记录模块106a/记录运行时数据106a’记录一个或多个跟踪文件108,其记录(多个)处理器102处的应用代码107的执行。稍后,重放模块106b/重放运行时数据106b’可以结合应用代码107使用(多个)跟踪文件108来向前和向后重放应用代码107的执行。虽然(多个)跟踪文件108被描绘为被存储在数据存储库104中,但是这些跟踪文件108也可以至少临时地被记录在系统存储器103中或一些其他存储设备中。注意,记录组件106a可以存在于一个计算机系统处,并且重放组件106b可以存在于另一计算机系统处。如此,程序的执行可以在一个系统上被跟踪/记录,并且在另一系统上被重放。
29.图1包括(多个)处理器102的内部硬件组件的一般表示。如所图示的,(多个)处理器102包括一个或多个处理单元102a(即,核)。每个处理单元102a包括执行由应用定义的处理器指令的硬件逻辑,以及从预定义的处理器指令集架构中选择哪些指令的硬件逻辑。(多个)处理器102的特定指令集架构基于处理器制造商和处理器模型而变化。通用指令集架构包括来自intel公司的ia-64和ia-32架构、来自advanced micro devices公司的amd64架构、和来自arm holdings公司的各种高级risc机器(“arm”)架构,尽管存在许多其他指令集架构并且可以由本发明使用。通常,“指令”是由处理器可执行的最小外部可见(即,处理器外部)的代码单元。
30.处理单元102a从高速缓存102b获得处理器指令,并基于高速缓存102b中的数据、基于寄存器102c中的数据和/或没有输入数据来执行处理器指令。通常,高速缓存102b是存储系统存储器103的部分的处理器上副本的随机存取存储器的较小的量(即,相对于典型的系统存储器103的量较小)。例如,当执行应用代码107时,高速缓存102b包含应用代码运行时数据107’的部分。如果(多个)处理单元102a需要尚未被存储在高速缓存102b中的数据,则发生“高速缓存未命中”,并且从系统存储器103获取该数据(通常从高速缓存102b中驱逐一些其他数据)。高速缓存102b通常被划分为至少代码高速缓存和数据高速缓存。例如,当执行应用代码107时,代码高速缓存存储被存储在应用代码运行时数据107’中的处理器指令的至少一部分,并且数据高速缓存存储应用代码运行时数据107’的数据结构的至少一部分。通常,高速缓存102b被划分为分离的阶/级(例如,层1、层2和层3),其中一些阶(例如,层3)可能与处理器102分离存在。寄存器102c是基于(多个)处理器102的指令集架构而被定义的基于硬件的存储位置。
31.虽然没有明确地描绘,但是(多个)处理器102中的每个处理器通常包括多个处理单元102a。如此,计算机系统可以包括多个不同的处理器102,每个处理器包括多个处理核。
在这些情况下,处理器的高速缓存102b可以包括多个不同的高速缓存部分,每个高速缓存部分对应于不同的处理单元,并且寄存器可以包括不同的寄存器集合,每个寄存器集合对应于不同的处理单元。因此,计算机系统101可以同时在不同处理器102处和/或每个处理器内的不同处理单元102a处并发执行多个“线程”。
32.时间行程调试
33.如前所述,在时间行程调试中的之前的尝试会在单个处理器核上非并发地执行进程的多个线程,或者在不同的处理器和/或处理器核上非并发地执行多个线程,使得每个指令以精确的确定性的顺序被执行并记录。附加地,之前的尝试会以确定性的方式详尽地记录对进程的存储器状态的改变,使得在任何给定时间都知道每个存储器值。然而,本文的实施例能够并发执行和跟踪多个线程,去除了以精确的顺序执行和记录每个指令的要求,并且能够在记录远少于指令执行和存储器状态的完整的记录的同时,实现重放。
34.在概念级别,本文的实施例在一个或多个处理器上分离地记录对进程的一个或多个线程的执行的跟踪,并且将这些跟踪记录在(多个)跟踪文件108中,跟踪文件108可以被用来再现作为每个线程的一部分而被执行的每个处理器指令的输入和输出(不必记录每个执行的指令),跟踪文件108包括跨不同线程的指令执行的顺序的近似,并存储足够的信息来预测相关的存储器值,而不用详尽记录对存储器状态的完整改变。注意,虽然本文的实施例可以跟踪进程的所有线程,但是它们也可以仅跟踪进程的线程的子集。而且注意,本文的实施例可以跟踪在(多个)物理处理器上、在诸如通过软件模拟的处理器的(多个)虚拟处理器上和/或甚至在虚拟机环境(例如,来自microsoft corporation的.net、来自oracle corporation的java等)中的应用代码107的执行。作为在虚拟机环境内跟踪的示例,对java程序进行记录可以包括:记录java虚拟机的“虚拟处理器”执行了哪些操作和存储器读取。备选地,记录java程序可以包括记录java程序和java虚拟机两者(例如,通过记录“刚好”编译java代码(其执行垃圾收集器等)的本机代码的执行)。在后一种情况下,时间行程调试器106可以被配置成将不同层的重放分离(即,应用代码与java虚拟机代码)。
35.本发明的实施例是基于发明人的认识而被构建的,发明人认识到,处理器指令(包括虚拟机“虚拟处理器”指令)通常可以落入三种类别中的一种:(1)由于不产生可预测的输出而被标识为“非确定性”的指令,因为它们的输出不完全由通用寄存器或存储器中的数据确定,(2)其输入不依赖于存储器值的确定性指令(例如,它们仅取决于处理器寄存器值,或代码本身中所定义的值),以及(3)其输入取决于从存储器读取值的确定性指令。因此,在一些实施例中,可以通过解决以下三个对应的挑战来实现对指令的执行的重构:(1)如何记录产生不完全由其输入确定的输出的非确定性指令,(2)如何再现针对取决于寄存器的指令的输入寄存器的值,以及(3)如何再现针对取决于存储器读取的指令的输入存储器的值。
36.作为如何记录由不产生完全可预测输出的线程执行的“非确定性”指令(因为它们的输出不完全由通用寄存器或存储器中的数据确定)的第一个挑战的解决方案,实施例包括在对线程的跟踪中存储这种指令的执行的副作用。如本文所使用的,“非确定性”指令包括稍微不那么常见的指令,其(i)每次被执行时产生非确定性输出(例如,intel处理器上的rdtsc,其将自最后一个处理器复位以来的处理器周期数写入到寄存器中),(ii)可以产生确定性输出,但是依赖于未被记录组件106a追踪的输入(例如,调试寄存器、定时器等),和/或(iii)产生处理器特定信息(例如,intel处理器上的cpuid,其将特定于处理器的数据写
入到寄存器中)。存储这种指令的执行的副作用可以包括,例如,存储由指令的执行改变的寄存器值和/或存储器值。在诸如来自intel的一些架构中,诸如在虚拟机扩展(vmx)中发现的那些处理器特征可以被用来捕获用于在(多个)跟踪文件108中记录其副作用的指令。
37.作为第二个挑战的解决方案,再现针对由线程(例如,其输入仅取决于处理器寄存器值)执行的确定性指令的输入寄存器的值是直截了当的,因为它们是线程中的(多个)之前的指令的执行的输出。因此,可以将在线程的跟踪中记录整个系列的处理器指令的执行减少为再现在系列的开始处的寄存器值;(多个)跟踪文件108不需要存储在该系列中所执行的特定指令的记录,或中间寄存器值。这是因为实际指令在应用代码107本身中可用,并且在重放时可用。因此,可以在重放期间向这些指令提供所记录的输入(即,所记录的初始的寄存器值的集合)以与在跟踪线程期间执行的方式相同的方式执行。
38.作为第三个挑战的解决方案,为由其输入取决于存储器值的线程执行的确定性指令再现输入存储器的值,实施例包括在线程的跟踪中记录线程中的指令消费的存储器值(即,其读取),而不管指令所读取的值如何被写入存储器。换言之,一些实施例包括仅记录存储器读取而不记录存储器写入。例如,尽管可以由当前线程、由另一线程(包括内核,例如,作为处理中断的一部分)或者由硬件设备(例如,输入/输出硬件105)将值写入存储器,但是用于完整重放执行读取的线程的指令只需要线程指令读取的值。这是因为线程所读取的值(不一定是被写入存储器的所有值)决定了线程如何执行。虽然在一些实施例中,所读取的每个存储器值的值可以被存储在(多个)跟踪文件108中,但是其他实施例包括优化,诸如尝试预测合适的值而不必记录每个读取的预测技术。例如,在一些实施方式中,如果预测值是实际从存储器读取的值,则不需要在(多个)跟踪文件108中记录任何内容;然而,如果预测值与实际读取的值不匹配,则所读取的值被记录在(多个)跟踪文件108中。一种这样的预测技术是预测线程读取的下一个存储器值将与之前该线程读取的值相同,如下文所演示的。另一种预测技术是始终预测下一次存储器读取将具有零的值。在后面还讨论了其他示例预测技术。
39.附加地,由于彼此独立地记录每个线程,因此(多个)跟踪文件108不需要记录跨所有线程执行的每一个指令的严格排序(例如,使用完全顺序一致的记录模型,如上所述)。然而,记录指令执行顺序的近似可能有助于稍后的调试。因此,为了记录跨线程执行的指令的顺序的近似,实施例改为定义或标识具有定义的“可排序”和“不可排序”事件的“跟踪存储器模型”。然后,记录组件106a记录线程执行期间发生的“可排序”事件的执行序列。例如,实施例可以使用保证不重复的单调增加数(“min”)来记录跨线程发生的可排序事件的序列。通常,跟踪存储器模型应当定义线程如何通过共享存储器进行交互,并且定义它们对存储器中的数据的共享的使用。所使用的跟踪存储器模型可以是由被用来编译应用代码107的编程语言(例如,c 14)定义的存储器模型,或者是出于跟踪目的而被定义的一些其他存储器模型(诸如由时间行程调试器106定义的存储器模型)。
40.作为示例,第一跟踪存储器模型可以仅将内核调用(来自用户模式)、陷阱和异常视为可排序。这种跟踪存储器模型会具有很低的开销,因为这些操作本身相对“昂贵”,它们很可能被追踪,并提供排序的非常粗略的概述。
41.第二示例跟踪存储器模型可以将完整栅栏(fence)(即,具有获取和释放语义两者的操作)视为可排序。这种操作的示例可能包括intel的“锁定”指令、内核调用、异常和陷
阱。当代码使用“互锁”类型的原语来跨线程通信时(这在诸如来自microsoft corporation的windows的操作中是常见的),该存储器模型将为进程中发生的几乎所有的跨线程通信提供足够的排序。
42.第三示例跟踪存储器模型可以将所有的获取和释放视为可排序。该存储器模型可能适用于基于处理器的arm指令集,因为arm不会将大部分的加载和存储视为获取或释放。在其他架构上,诸如来自intel的(其中大多数的存储器访问是获取或释放),这相当于对几乎所有存储器访问进行排序。
43.第四示例跟踪存储器模型可以将所有存储器加载视为可排序。与其他示例存储器模型相比,这将提供强排序但可能导致性能降低。
44.前述存储器模型仅作为示例呈现,并且鉴于本文的公开内容,本领域技术人员将认识到,可以选择很多种存储器模型。
45.鉴于前述内容,图2图示了用于记录多线程进程的执行的可重放跟踪的方法200的示例流程图。如所描绘的,方法200包括标识存储器模型的动作201。动作201可以包括标识跟踪存储器模型,该跟踪存储器模型定义要跨多线程进程的多个线程进行排序的一个或多个可排序事件。例如,动作201可以包括记录组件106a标识或定义存储器模型,诸如(仅作为示例)将内核调用、陷阱和异常视为可排序;将完整栅栏视为可排序;将所有获取和释放视为可排序;将所有存储器加载视为可排序等,如上面所讨论的那样。
46.图2还描绘了方法200包括并发执行多个线程的动作202。动作202可以包括跨一个或多个处理器的一个或多个处理单元并发执行多个线程,同时利用记录组件106a观察它们的执行。例如,动作202可以包括在处理器的第一处理单元102a上执行进程的第一线程,同时在相同的处理器的第二处理单元102a上执行该进程的第二线程。作为另一示例,动作202可以包括在支持超线程的处理器的相同处理单元102a上执行该进程的两个线程。作为另一示例,动作202可以包括在不同处理器的不同处理单元102a上执行该进程的两个线程。前述的组合也是可能的。
47.图2还描绘了方法200包括独立地记录针对每个线程的跟踪的动作203。动作203可以包括,在执行多个线程期间,独立地为每个线程记录分离的可重放跟踪。鉴于此后的示例将清楚,针对每个线程的所记录的跟踪彼此独立,除了它们可以包括跨线程由min标识的可排序事件。如所图示的,动作203可以包括当线程执行时,由记录组件106a针对每个线程执行的子动作。
48.图2还描绘了动作203包括针对每个线程记录初始线程状态的动作204。例如,动作204可以包括存储初始处理器寄存器值的记录组件106a。其他初始线程状态可以包括用于进程的第一线程的线程环境块(teb)和/或进程环境块(peb)。记录teb和/或peb可以稍后提供有用的调试信息(例如,线程本地存储数据)。其他初始线程状态可以包括线程的执行堆栈,特别是在记录组件106a正在开始记录已经执行的线程的情况下。
49.图2还描绘了动作203可以包括记录非确定性指令的副作用的动作205。动作205可以包括记录由线程执行的至少一个非确定性处理器指令的副作用。例如,记录组件106a可以通过记录指令对寄存器值所做的任何改变来记录非确定性指令的副作用。
50.图2还描绘了动作203包括针对每个线程记录存储器读取的动作206。动作206可以包括记录由将存储器作为输入的线程执行的至少一个处理器指令执行的至少一个存储器
读取。例如,记录组件106a可以记录(多个)跟踪文件108,其可以被用来在重放时间期间再现从存储器读取的值。这可以包括记录每个读取,或者应用一个或多个算法来预测读取以减少需要被记录在(多个)跟踪文件108中的读取条目的数目。
51.图2还描绘了动作203包括针对每个线程记录可排序事件的动作207。动作207可以包括利用单调增加的数字记录由线程执行的至少一个可排序事件,该单调增加的数字将跨多个线程的其他可排序事件中的事件进行排序。例如,记录组件106a可以使用跨线程应用的min来记录由跟踪存储器模型可排序的事件的执行序列。如此,重放组件106b可以确保在重放期间跨线程对这些事件进行排序。一个示例可排序事件是跟踪的开始。结合下面表1和表2的示例给出其他事件。
52.如动作205、206和207之间的双端箭头所示,这些动作可以以任何顺序发生,并且可以在跟踪线程期间多次发生,如结合以下示例将变得清楚的。
53.当记录跟踪时,记录组件106a可以采用压缩技术来减小(多个)跟踪文件108的大小。例如,记录组件106a可以在将跟踪数据写入存储装置(无论是否它位于系统存储器103还是数据存储库104中)之前动态地压缩跟踪数据。备选地,记录组件106a可以在记录结束时压缩(多个)跟踪文件108。
54.表1-表5图示了使用方法200跟踪进程的执行的具体示例。特别地,表1-表5图示了用于记录多线程进程的单个线程的执行的示例技术。虽然为了简单和清楚起见,在该示例中仅记录了一个线程,但是记录技术不需要改变,不管修改程序状态的线程有多少。附加地,即使存在修改共享存储器的外部实体(例如,内核、硬件),也不需要改变记录技术。这是因为这些记录技术记录处理单元102a实际看到(读取)的内容并且在执行线程时如何行动,而不是集中于对执行进行约束以产生完全可预测的执行指令的序列。
55.初始地,表1图示了作为线程的一部分的待由处理单元102a执行的处理器指令的示例列表(例如,来自应用代码107)。
56.表1:处理器指令列表
[0057][0058]
在表1中,“地址”列指示存储器地址(例如,在高速缓存102b的指令部分中),在该地址中可以找到在“指令”列中所指定的指令。在该示例中,这些地址被简化为两位数。在指令中,r1-r7指示处理器寄存器102c,并且方括号(“[]”)中包含的数据指示存储器位置(例如,在高速缓存102b的数据部分中)。
[0059]
表2图示了针对线程的指令执行序列,包括可能潜在地被记录在(多个)跟踪文件108中以使得能够重放线程执行的寄存器状态和样本数据。
[0060]
表2:指令执行序列
[0061]
[0062][0063]
在表2中,“id#”列指示指令执行的特定序列,并且“地址”列指示在id#处执行的指令的指令地址(参见表1)。虽然不一定被记录在(多个)跟踪文件108中,但是包括列“r1
”‑“
r7”以说明一些处理器寄存器的状态改变,作为理解程序执行的辅助。“跟踪”列指示可以与指令执行有关地被记录的数据的类型。
[0064]
根据方法200,记录组件106a将标识定义“可排序”和“不可排序”指令/事件的跟踪存储器模型(动作201)。这使得记录组件106a能够根据min记录跨线程的排序(可排序)事件。稍后,在重放期间,这使得重放组件106b能够确保这些可排序指令以相对于彼此的适当顺序跨线程重放,并且还使得重放组件106b能够灵活地在线程内和跨线程重放不可排序指令。
[0065]
为了说明,图3提供了跨并发执行的线程(线程a和线程b)排序事件的示例300。特别地,图3图示了线程a的执行的时间线301,以及线程b的执行的并发时间线302。这些时间线301、302示出了七个可排序事件的执行的相对顺序。特别地,时间线301示出线程a执行了可排序事件3和5,并且时间线302示出线程b执行了可排序事件1、2、4、6和7。在重放期间,重
放组件106b确保这些可排序事件是以适当的顺序在它们相应的线程内被重放,并以其正确的顺序跨两个线程被重放(即,b1=》b2=》a3=》b4=》a5=》b6=b7)。
[0066]
不可排序指令相对于这些可排序指令被重放。例如,重放组件106b将在重放线程b的时间块304中的任何不可排序事件之前,重放线程b的时间块303中的任何不可排序事件。附加地,重放组件106b将在重放线程a的时间块305中的任何不可排序事件之前,重放线程b的时间块303中的不可排序事件。此外,重放组件106b将在重放线程b的时间块306中的任何不可排序的线程之前,重放线程a的时间块305中的不可排序事件。然而,重放组件106b可以不强制执行线程b的时间块305中的不可排序事件的重放与线程b的时间块304中的不可排序事件的重放之间的任何特定排序,因为无法仅根据可排序事件来确定排序。然而,如果这些指令都要访问共享存储器,则重放组件106b可以强制执行这些指令之间的某种排序,因为它们的排序可以至少部分地基于它们访问共享存储器的方式和时间(包括访问的值)来确定。
[0067]
返回图2,方法200将继续针对每个线程记录单个跟踪(动作203)。在执行针对给定线程的任何指令之前,记录组件106a记录初始状态/上下文(动作204),诸如处理器寄存器102c的初始值(在该示例中,即,对于每个r1-r7,初始值全是1(0xff))。
[0068]
然后,记录组件106a继续监控和记录线程的执行,在(多个)跟踪文件108中记录发生的适当指令/事件(诸如非确定性指令的执行的副作用(动作205)、存储器读取(动作206)和可排序事件(动作207))。
[0069]
例如,在id#0处,记录组件106a记录在地址10处的指令的执行(即,根据表1,“move r1《-{timer}”)。该指令读取定时器,并将该值放入寄存器(r1)中。在该示例中,假设所选择的跟踪存储器模型(动作201)将获得定时器定义为“可排序”指令,我们应当用min记录该指令。如此,记录组件106a在(多个)跟踪文件108中记录排序(可排序)事件“x”(动作207)。在该示例中,这在“跟踪”列中被表示为“instr:0《orderable event,x》”,意指自上次数据写入跟踪以来零指令,其记录由min x标识的排序(可排序)事件。
[0070]
注意,用于记录排序(可排序)事件的特定符号(min)可以是任何符号(因此使用通用“x”),只要它是保证不在相同的记录内重复并且单调增加(例如,a、b、c等;1、2、3等;10、20、30等)的值。一种可能的选项是使用处理器提供的定时器值,诸如由intel处理器上的rdtsc指令返回的值。因此,发生在“x”之后的操作将具有严格大于“x”的序列标识符,无论这些操作发生在哪个线程上。
[0071]
附加地,指令10是第一类非确定性指令,其产生不仅仅依赖于输入的结果(例如,没有输入,并且每次执行指令时返回的定时器值不同)。如此,记录组件106a在跟踪中记录其执行的副作用(动作205)。在该示例中,这在“跟踪”列中被表示为“instr:0《side effects,r1=t1,pc=11》”,意指自上次数据写入跟踪(“instr:0《seq.event,x》”)以来的零指令,其记录r1的新值(即时间t1),以及增加并记录程序计数器(pc,未示出)寄存器(即,到在地址11处的下一条指令)。
[0072]
虽然该示例将对pc的更新记录为副作用,但是一些实施例可以省略这样做,因为重放组件106b将能够基于对所执行的指令的分析来确定pc应当增加多远。然而,重新编码pc允许将“跳转”指令被记录为副作用,这提供了利用(多个)跟踪文件108的单个条目记录若干指令的执行的机会。
[0073]
此时,(多个)跟踪文件108具有足够的信息,使得重放组件106b可以设置初始线程状态(例如,将r1-r7的值设置为0xff),可以重放指令10(通过再现其副作用),并且可以将指令10相对于该线程内或者在记录组件106a追踪其并发执行的顺序的其他线程内的任何其他指令进行排序。
[0074]
接下来,在id#1处,指令11执行读取(读取存储器位置a的地址,并将值放在r2中,参见表1)。由于该指令读取存储器,因此记录组件106a以这种方式将其记录到(多个)跟踪文件108(动作206),使得在重放时重放组件106b能够再现或预测所读取的值。如前面所讨论的,这可以包括记录组件106a记录每次读取的值,尽管存在各种优化以减少需要被写入的数据量。一种方法是预测由当前指令读取的值是由线程上次读取的值(并被记录在(多个)跟踪文件108中)。在这种情况下,(多个)跟踪文件108不包含针对该线程的之前读取,因此需要记录该读取。在该示例中,这被表示为“instr:1《read value ptra》”,意指自上次数据写入跟踪(即,“instr:0《side effects,r1=t1,pc=11》”)以来的一条指令,其读取存储器位置a的地址。如所指示的,该地址(ptra)将由指令放到r2中。
[0075]
还需要记录在id#2和id#3(指令12和13)处所执行的指令,因为它们也是读取(动作206)。由记录组件106a记录在(多个)跟踪文件108中的数据类似于针对指令11所记录的数据。对于id#2(指令12),符号“instr:1《read value ptrb》”意指自上次数据写入跟踪以来的一条指令,其读取存储器位置b的地址。对于id#3(指令13),符号“instr:1《read value 5》”意指自上次数据写入跟踪以来的一条指令,其读取值5。注意,根据示例预测算法,这两者都需要被写入(多个)跟踪文件108,因为每个读取产生与之前读取不同的值。如所指示的,针对位置b(ptrb)的地址将由指令12放到r2中,并且值5将由指令13放到r4中。
[0076]
对于id#4和id#5,指令14和指令15是确定性的,没有来自存储器的读取,因此记录组件106a不需要在(多个)跟踪文件108中记录任何内容。类似地,对于id#6,指令16是确定性的并且仅取决于r5和r4的值。不需要在(多个)跟踪文件108中进行记录,因为重放组件106b将基于指令13和指令14的重放以及在id#3处的针对指令的跟踪数据来再现这些值。
[0077]
对于id#7,指令17也不需要记录任何跟踪数据,因为其行为(不进行跳转,由于r5的值不小于r4的值)完全由已经被记录(或隐含)在(多个)跟踪文件108中的数据确定。例如,在重放时,针对id#3的跟踪数据将使重放组件106b将值5放在r4中,并且将基于在id#4处的重放指令14将值0写入r5。
[0078]
对于id#8,指令18是来自存储器的读取(由[r2 r5]标识的存储器位置),并且因此应当被考虑用于记录(动作206)。假设记录组件106a在执行期间观察到已经读取了值5(如“r7”列所指示的)。虽然记录组件106a可以在(多个)跟踪文件108中记录该读取(例如,“instr:5《read value 5》”),但是它可以通过应用上面讨论的读取预测算法来避免这样做:如果所读取的值与来自线程的上次读取操作的值相同,则不记录它。在这种情况下,上次读取的值是5(id#3),因此记录组件106a不需要在(多个)跟踪文件108中记录针对id#8的任何内容。
[0079]
对于id#9-id#11,记录组件106a也不需要记录任何内容以能够重放指令19-21,因为执行这些指令所需的一切都是已知的。对于id#12(指令21),记录组件106a也不需要记录任何内容,因为该指令的输入仅是寄存器,并且因为写入存储器(即,由[r3 r6]标识的位置)不需要被记录(如之前所讨论的),虽然来自存储器的读取会影响如何执行指令和执行
哪些指令,但写入却不会。记录组件106a也不需要针对id#12-id#16(指令23、24,循环:16和17)记录任何内容,因为执行这些指令所需的一切都是已知的。
[0080]
对于id#17,遇到第二时间指令18(来自存储器的读取),其读取值0(如列“r7”中所指示的)。该值不是由记录组件106a使用样本预测算法预测的值(因为它与所读取的最后一个值5不同),因此根据动作206,记录组件106a将其添加到(多个)跟踪文件108中(例如,“instr:14《read value 0》”,其指示在记录上个数据之后的14个指令,其读取值0)。
[0081]
记录组件106a不需要向(多个)跟踪文件108添加针对id#18-id#22(指令18、20、21,循环:16和17)的任何内容。如前所述,重放引擎106b已经具有足够的信息来再现与记录组件106a所观察到的相同的结果。在重放时,重放组件106b将比较相同的值、进行相同的跳转等。
[0082]
id#23是第三次遇到指令18(来自存储器的读取),并且读取了另一个0(如“r7”列中所指示的)。虽然记录组件106a需要考虑将其添加到(多个)跟踪文件108(动作206),但是该值被预测算法预测(在id#17处的上个读取也是0),因此记录组件106a不会将任何内容记录到(多个)跟踪文件108中。
[0083]
再次,记录组件106a不需要向(多个)跟踪文件108记录针对id#24-id#28(指令19、20、21,循环:16和17)的任何内容,因为重放组件106b已经具有足够的信息来再现相同的结果。
[0084]
对于id#29,第四次遇到指令18(来自存储器的读取),其读取值2(如列“r7”中所指示的)。该值不是预测值(它与我们读取的上个值(0)不同),因此根据动作206,记录组件106a将其添加到(多个)跟踪文件108(自上个条目以来的12个指令,其读取值2)。
[0085]
记录组件106a也不需要向(多个)跟踪文件108添加针对id#30-id#37的任何内容(指令19、20、21、22、23、24,循环:16,和17)。再次,重放组件106b已经具有足够的信息来再现相同的结果。
[0086]
id#38(指令18)是另一存储器读取(来自位置[r2 r5])。如r7所指示的,读取值2,这与在id#29处的上次读取相同。虽然记录组件106a需要考虑将其写入(多个)跟踪文件108(动作206),但是根据预测算法,不需要这样做。
[0087]
记录组件106a也不需要向(多个)跟踪文件108添加针对id#39-id#46的任何内容(指令19、20、21、22、23、24,循环:16,和17)。再次,重放组件106b已经具有足够的信息来再现记录组件106a所观察到的相同结果。
[0088]
在id#9处,记录组件106a观察到在指令26处的另一定时器读取。由于根据我们的存储器模型,这是“可排序”事件(如结合id#0所讨论的),根据动作207,记录组件106a利用增加的标识符记录操作的序列/顺序(例如,说明在上个记录的数据之后的18个指令的数据,发生排序事件x n)。而且,由于这是非确定性指令,根据动作205,记录组件106a记录其副作用(例如,说明在上个记录的数据之后的零指令的数据,其记录效果:r2=t2,以及pc=26)。
[0089]
对于id#48,没有读取且指令是确定性的,因此记录组件不在(多个)跟踪文件108中记录任何内容。
[0090]
表3总结了记录组件106a可能已经记录在(多个)跟踪文件108中的跟踪数据的示例,作为跟踪该线程的执行的一部分。
[0091]
表3:带有预测的示例跟踪
[0092]
《初始上下文(例如,寄存器)》instr:0《orderable event,id x》instr:0《side effects,r1=t1,pc=11》instr:1《read value ptra》instr:1《read value ptrb》instr:1《read value 5》instr:14《read value 0》instr:12《read value 2》instr:18《orderable event,id:x n》instr:0《side effects,r2=t2,pc=26》
[0093]
重放组件106b稍后可以使用该跟踪数据来设置初始上下文(例如,寄存器状态),并且然后通过再现非确定性指令的副作用,并在需要时提供存储器值(使用记录期间所使用的预测算法的知识),在记录时与线程的代码(例如,表1)一起以与它们执行的相同的方式执行相同的指令。附加地,排序事件(x和x n)使得id#0和id#47处的指令能够以相对于记录时间期间被执行的其他线程中的排序事件相同的顺序来执行。因此,跟踪机制使得能够跟踪和重放单个执行的线程和多个并发执行的线程,同时记录少量的跟踪数据,并且远小于所执行的指令和完全存储器状态中的完全确定性记录。值得注意的是,在前面的示例中,记录组件106a没有追踪或记录何时、为何或何人(即,在该进程中的另一线程、早前的相同线程、另一进程、内核、硬件等)写入了由表1中的代码读取和消费的值。尽管如此,跟踪表3仍然使重放组件106b能够以所观察到的精确方式重放执行。
[0094]
如本领域技术人员将认识到的,鉴于本文的公开内容,在可以记录跟踪数据的特定方式上存在许多变化。例如,尽管示例跟踪基于与自条目上次进入跟踪以来所执行的指令的数目有关的指令计数来追踪事件发生的时间,但也可以使用绝对指令计数(例如,从跟踪的开始处开始的计数)。可以被用来唯一地标识所执行的每个指令的其他示例可以基于对所执行的cpu周期的数目的计数(相对于之前的条目,或者作为绝对值)、所进行的存储器访问的数量,和/或所采取的跳转的计数与处理器的程序计数器(其可以相对于上次执行的不连续或内核调用,或者从定义的时间绝对地)。当使用前述技术中的一种以上时,在记录某些类型的指令时可能需要小心,诸如“重复”指令(例如,intel架构上的rep)。特别地,在一些处理器上,重复指令实际上可以在每次迭代时利用相同的程序计数器执行,并且因此在这些处理器上,具有多次迭代的重复指令可以仅计为一个指令。在这些情况下,跟踪应当包括可以被用来区分每次迭代的信息。
[0095]
附加地,存在用于记录和预测所读取的值的各种机制。作为一个简单示例,实施例可以完全消除预测算法。虽然这将生成更长的跟踪,但是它也将去除重放组件106b配置有与记录组件106a相同的预测算法的限制。表4图示了在不使用预测算法的情况下,跟踪可能是什么样的,并且其中记录了每个存储器读取。如所图示的,在没有预测算法的情况下,跟踪将包括三个新条目(重点显示),而其他条目(即每个新条目后面的条目各自具有一个更新的指令计数,其反映来自它之前的新条目的计数。
[0096]
表4:没有预测情况下的示例跟踪
[0097]
《初始执行上下文(例如,寄存器)》instr:0《orderable event,id x》instr:0《side effects,r1=t1,pc=11》instr:1《read value ptra》instr:1《read value ptrb》instr:1《read value 5》instr:5《read value 5》instr:9《read value 0》instr:6《read value 0》instr:6《read value 2》instr:9《read value 2》instr:9《orderable event,id:x n》instr:0《side effects,r2=t2,pc=26》
[0098]
注意,如果(多个)跟踪文件108可以基于哪个指令消费所读取的值来映射所读取的值,则(多个)跟踪文件108不需要记录所读取的值的存储器地址。因此,产生多于一个读取的指令可能需要一种方法来标识哪个读取是(多个)跟踪文件108中的读取(即,当针对该指令只有一个读取时),或者哪个读取是哪个(即,如果(多个)跟踪文件108中有多个读取)。备选地,(多个)跟踪文件108可以包含用于这些读取或所有读取的存储器地址。(多个)跟踪文件108仅需要包括足够的信息以使重放组件106b能够读取记录组件106a观察到的相同值,并将它们匹配到相同的参数,以便它可以产生相同的结果。
[0099]
附加地,可能发生在代码流中不可发现的事件(即,代码执行中的不连续),诸如访问冲突、陷阱、中断、对内核的调用等。记录组件106a还需要记录这些事件,以便他们可以被重放。例如,表5图示了如果id#33(指令22)已经生成访问冲突,则可以被记录在(多个)跟踪文件108中的示例跟踪。
[0100]
表5:在异常情况下的示例跟踪
[0101]
《初始执行上下文(例如,寄存器)》instr:0《orderable event,id x》instr:0《side effects,r1=t1,pc=11》instr:1《read value ptra》instr:1《read value ptrb》instr:1《read value 5》instr:14《read value 0》instr:12《read value 2》instr:4《exception record》instr:0《exception context》《跟踪结束》
[0102]
特别地,跟踪包含“instr.4《exception record》”(表示发生异常,并且异常发生在上次条目进入跟踪后的4条指令)和“instr:0《exception context》”(与异常之后的重新启动执行并记录任何合适的状态有关地将指令计数复位为零)。虽然为了清楚起见,表5示
出了用于发出发生异常的信号和用于记录异常上下文的分离记录,但是它们可以在相同的条目中。将指令计数设置为零向重放组件106b发出这两个条目适用于相同的指令的信号。现在,由于(多个)跟踪文件108包含针对异常的条目,因此跟踪具有针对这种异常的精确位置。这使得重放组件106b能够在执行的相同的时刻(在记录时观察到的)引发异常。这很重要,因为通过查看代码流无法推断异常(因为它们的出现通常基于代码流中不存在的数据)。
[0103]
如上所述,记录组件106a可以采用用于跟踪所读取的值的各种机制,诸如预测将被读取的值等于上次所读取的值,并且如果值不同,则将该读取记录在(多个)跟踪文件108中。一种备选的方法使用高速缓存扩展该预测算法,使得记录组件106a预测从特定地址读取的最可能的值是从该地址被读取或写入该地址的最后值。因此,该方法需要维持进程的存储器范围的高速缓存,并使用高速缓存来追踪存储器读取和写入。
[0104]
为了避免该方法的开销(即,保持追踪被放置在程序的整个存储器范围中的最后值的高速缓存),一种改进是使记录组件106a创建比所寻址的完整存储器小得多的存储器的“卷影副本”(如下面所讨论的)。然后,对于观察到的每个读取,记录组件106a将所读取的指令的值与卷影副本处的匹配位置的值进行比较。如果值相同,则记录组件106a不需要在(多个)跟踪文件108中保存该读取的记录。如果值不同,则记录组件106a将该值记录到(多个)跟踪文件108中。记录组件106a可以在读取和/或写入时更新卷影副本,以增加在下次读取存储器位置时正确预测值的可能性。
[0105]
在一些实施例中,可以将卷影副本的大小定义为2^n个地址。然后,为了使地址与其卷影副本匹配,记录组件106a取存储器地址的低n位(即,其中n是确定卷影副本的大小的2的幂)。例如,对于n=16的值,卷影副本将是64k(2^16)个地址,并且记录组件106a取每个存储器地址的低16位,并将它们与卷影副本中的该偏移进行比较。在一些实施例中,用全零来初始化卷影副本。可以选择零,因为它是一个明确的值,并且因为从存储器中非常频繁地读取零。然而,取决于实施方式,可以备选地使用其他初始值的选择。
[0106]
图4图示了根据一些实施例的使用卷影副本的示例400。图4表示进程的可寻址存储器401。为了简化说明,示例400中的进程仅可以寻址2^4(16)个存储器位置,这仅是通常的进程能够在当代计算机上寻址的一小部分(其通常是大约2^32、2^64或更多个存储器位置)。在可寻址存储器401的表示中,地址列指定每个存储器位置的二进制地址(即,二进制地址0000-1111),并且值的列表示针对要在每个存储器位置处被存储的数据的位置。根据上述卷影副本,图4还表示可寻址存储器401的对应的卷影副本402,其在跟踪线程期间可以由记录组件106a保持。在该示例中,n的值等于2,因此卷影副本402存储2^2(4)个存储器位置(即,二进制地址00-11)。
[0107]
记录组件106a在任何时间检测到来自可寻址存储器401的读取时,记录组件106a将从可寻址存储器401读取的值与卷影副本402中的对应位置进行比较。例如,当读取任何的存储器位置401a、401b、401c或401d时(即,二进制地址0000、0100、1000或1100),记录组件106a将从该位置读取的值与卷影副本的位置402a中的值进行比较(即,二进制地址00,因为前述存储器地址中的每个的最后n个数字是00)。如果值匹配,则不需要将读取记录在(多个)跟踪文件108中。如果值不匹配,则记录组件106a将该读取记录在(多个)跟踪文件108中,并用该新的值更新卷影副本。
[0108]
尽管卷影副本402中的每个位置代表可寻址存储器402中的多个位置(在这种情况下为4个),但是注意,大多数程序可能从可寻址存储器402中的相同位置执行多次读取和/或对其写入(例如,读取或更新变量值),因此可能存在由卷影副本402预测的大量读取。在一些实施例中,当写入发生时,记录组件106a还可以更新卷影副本402,这可以进一步增加正确预测的可能性。
[0109]
在一些实施例中,不是在存储器地址的细粒度级别追踪存储器读取,而是记录组件106a可以在存储器页面(例如,基于页面表)级别追踪跨多个线程的读取。该实施例建立在存储器页面受到限制的认知的基础上,使得存储器的每页可以(i)由一个线程写入,而没有其他线程具有对页面的读或写访问权;或者(ii)由需要的许多个线程读取,但没有线程可以写入它。因此,实施例可以将线程分组成族,使得在记录时间期间,一个族内的线程总是在彼此之间非并发地执行(即,相同的族的两个线程不能并发执行)。然后,跨线程族地应用上述限制,使得除了并发页面访问之外,不同线程族的线程可以彼此并发运行。如果一个线程族“拥有”用于写入的页面,那么没有其他线程族可以访问它;然而,一个或多个线程族可以共享用于读取的页面。
[0110]
当线程族访问用于读取或写入的页面时,它需要知道页面的整个内容。如果页面是由已经被记录的线程族中的线程产生/写入的,则记录组件106a已经知道该页面的内容。因此,在一些实施例中,记录组件106a仅将标识写入线程释放页面的记录中的点的信息放在跟踪上。对于由外部实体(例如,内核、未被跟踪的线程、硬件组件等)生成/写入的页面,用于记录页面以使其在重放时可用的策略可以包括记录组件106a记录整个页面,或记录组件106a记录页面的压缩版本。如果之前已经记录页面,则另一策略包括记录组件106a仅存储页面的当前版本与之前记录之间的页面值上的差异。
[0111]
如前面所指示的,当调试由记录组件106a跟踪的代码时,通过重放组件106b从断点之前的时间替换(多个)跟踪文件108直到到达断点(例如,上次断点命中是在调试器当前正在分析代码流之前)来到达“向后”方向上的断点。使用上述跟踪,这将意味着从(多个)跟踪文件108的开始重放跟踪。虽然这对于较小的跟踪可能可接受,但对于较大的跟踪来说,这可能耗时且低效。为了改善重放的性能,在一些实施例中,记录组件106a在(多个)跟踪文件108中记录多个“关键帧”。然后,重放组件106b使用关键帧来更精细地“瞄准”断点。例如,在一些实施方式中,重放组件106b可以迭代地“返回”越来越多的关键帧(例如,每次迭代加倍关键帧的数目),直到达到所选择的断点为止。为了说明,重放组件可以返回一个关键帧并尝试命中断点,如果失败则可以返回两个关键帧并尝试命中断点,如果失败则可以返回四个关键帧并尝试命中断点等。
[0112]
通常,关键帧包括足够的信息,重放组件106b可以从关键帧开始重放执行,通常不考虑跟踪中的关键帧之前的内容。记录关键帧的确切时间和所收集的数据可能基于实施方式而变化。例如,以周期性间隔(例如,基于所执行的指令的数目、基于处理器周期、基于经过的时钟时间、基于根据跟踪存储器模型的“可排序”事件的发生等),记录组件106a可以在(多个)跟踪文件108中为每个线程记录关键帧的足够的信息以开始从关键帧重放每个线程的跟踪。该信息可以包括在取得关键帧时的硬件寄存器的状态。该信息还可以包括将存储器预测策略置于已知状态所需的任何信息,以便它可以从关键帧开始再现读取(例如,通过记录(多个)存储器快照、(多个)卷影副本、上次所读取的(多个)值等)。在一些实施例中,关
键帧可用于实现跟踪中的间隙。例如,如果由于任何原因而停止跟踪,则可以在(多个)跟踪文件108中记录该跟踪(例如,通过插入表示跟踪停止的符号和适当格式化的关键帧),并且然后稍后可以从(多个)跟踪文件108中的该点重新开始跟踪。
[0113]
在一些实施例中,一些关键帧可以包括不是使得能够在关键帧处重放所严格必需,但是证明在调试期间有用的信息(例如,帮助时间行程调试器106消费在重放期间所生成的数据,并以有用的形式呈现它)。该信息可以包括,例如,线程堆栈的一个或多个部分的副本、存储器的一个或多个部分的副本、线程的teb、进程的peb、加载的模块的标识及其标题等等。在一些实施例中,包括该附加信息(例如,“完整”关键帧)的关键帧可以比常规的关键帧(例如,“轻量级”关键帧)更不频繁地被生成和存储。可以在记录时由用户配置收集轻量级关键帧和/或完整关键帧的频率以及在每个关键帧中收集的特定数据。
[0114]
在一些实施例中,记录“完整”关键帧对于诸如可重复使用的环形缓冲器的特征也是有用的,这将在下面结合图5进行讨论。记录“轻量级”关键帧还使得重放组件106b能够并行化重放;关键帧之后的每个线程跟踪可以独立于其他线程被重放,并且因此可以并行重放。附加地,关键帧可以使重放组件106b能够并行地重放相同线程跟踪的不同段。例如,这样做可以有助于使用时间行程调试器106更快地命中断点。在一些实施例中,跨线程地协调对“完整”关键帧的记录(即,记录组件106a在大致相同的时间为进程的每个线程记录完整的关键帧),而与其他线程无关地为每个线程记录“轻量级”关键帧(即,记录组件106a为每个线程记录轻量级关键帧,这对于该线程来说是方便的或者其他方面有意义)。调整用于记录不同类型关键帧的条件可以灵活地平衡跟踪大小、重放能力、记录性能和重放性能。
[0115]
一些实施例可以包括使用“快照”,其包括进程的相关存储器的完整副本。例如,记录组件106a可以在开始对该进程的跟踪时获取进程的存储器的初始快照。这使得重放组件106b能够向时间行程调试器106的用户提供由该进程使用的所有存储器位置的值,而不仅仅是被观察到在记录期间被访问的那些存储器位置的值。
[0116]
在一些实施例中,(多个)跟踪文件包括可由重放组件106b使用以验证在重放时的程序的状态确实与记录期间存在的程序状态匹配的信息。例如,记录组件106a可以在(多个)跟踪文件108中包括诸如副本寄存器数据和/或寄存器数据的计算散列的周期性信息。该信息可以被包括在关键帧中,可以周期性地被包括在标准跟踪条目中,和/或可以基于所执行的指令的数目(例如,每x个指令放在跟踪中)而被包括等。在重放期间,重放组件106b可以比较在重放期间生成的执行状态数据中的对应的点处的记录的寄存器数据(和/或计算散列),以确保执行状态相同(即,如果重放期间的寄存器数据和/或该数据的计算散列匹配,则执行状态很可能相同;如果它们不匹配,则执行状态已经偏离)。
[0117]
如前所述,一些实施例包括记录包括有限容量的“环形缓冲器”的(多个)跟踪文件。特别地,环形缓冲器仅记录程序执行的尾部(例如,执行的最后n分钟、n小时、n天等)。概念上,环形缓冲器将新的跟踪数据添加到跟踪的前部/顶部,并从跟踪的后部/底部移除旧的跟踪数据。例如,在编程故障出现之前,一些应用可能会运行数天、数周甚至数月/数年。在这种情况下,即使在由所公开的实施例记录跟踪文件的紧凑性的情况下,也可能是不切实际的(例如,在所使用的磁盘空间量方面)并且没必要追踪程序执行的完整历史。附加地,使用环形缓冲器可能潜在地允许整个(多个)跟踪文件108被存储在ram中,这可以大大减少磁盘i/o并提高性能(在记录和重放两者期间)。
[0118]
当实施环形缓冲器时,实施例可以追踪“永久”跟踪信息和“临时”跟踪信息,其中“临时”信息被存储在环形缓冲器中。“永久”跟踪信息的示例可以包括一般信息,诸如所加载的模块的标识、正在被记录的进程的标识等。
[0119]
图5图示了根据一个或多个实施例的可重用环形缓冲器501的示例500。在该示例中,环形缓冲器501中的每个清空矩形表示环形缓冲器501中的标准跟踪条目(例如,诸如上面的表3的条目)。每个阴影矩形(例如,504a、504b、504c、504d、504e)表示关键帧。关键帧的频率和关键帧之间的条目的数目将根据实施方式变化。如箭头502和它覆盖的虚线条目所示,新条目(标准条目和关键帧两者)被添加到缓冲器的一端。如箭头503和它覆盖的虚线条目所指示的,从缓冲器的另一端去除最旧的现有条目(标准条目和关键帧两者)。可以逐个地或者以块(例如,基于经过的时间、条目的数目、关键帧的出现等)添加/去除条目。可基于程序执行的拖尾时间段的期望的长度来配置缓冲器的总大小。为了从环形缓冲器501重放,重放组件106b使用期望的关键帧来初始化状态数据,并且然后从那里重放程序执行。
[0120]
如前所述,环形缓冲器501的关键帧可以包括“完整”关键帧,其不仅使得能够通过重放组件106b从每个关键帧重放(例如,使用被存储在关键帧中的寄存器值),还使得能够使用来自每个关键帧的附加调试特征(例如,使用诸如线程的teb、数据高速缓存等的附加信息)。然而,环形缓冲器501的关键帧还可以包括“轻量级”关键帧(排他地或者是“完整”关键帧的补充)。
[0121]
在记录期间所使用的环形缓冲器的数目可以变化。例如,一些实施例可以针对每个线程使用分离的环形缓冲器(即,每个线程被分配多个存储器页面以用于记录跟踪数据),而其他实施例可以使用单个环形缓冲器来记录多个线程。当使用单个环形缓冲器时,仍然会分离地记录对每个线程的跟踪,但线程会记录到共享存储器页池。
[0122]
在第二实施例中(使用单个环形缓冲器来跟踪多个线程),每个线程可以从被分配给环形缓冲器的页面池中获得一个页面,并开始用跟踪数据填充它。当线程的页面满时,线程就可以从池中分配另一页面。当添加关键帧时,一些实施例尝试在针对每个线程的大致相同的时间添加它们。然后关键帧可以被用来将“代(generation)”分配给记录。例如,在第一关键帧之前记录的数据可以是“第一代”记录,并且在第一关键帧和第二关键帧之间记录的数据可以是“第二代”记录。当池中的页面已经用尽时,可以释放与最老一代记录(例如,第一代)相关联的页面以重新用于将来的记录。
[0123]
虽然前述公开内容主要集中于向(多个)跟踪文件108记录可用于重放代码的执行的信息,但是存在要写入(多个)跟踪文件的可能有助于记录组件106a的许多其他类型的数据。已经给出了将关键帧和附加调试信息写入跟踪的示例。可以标记的跟踪的其他类型的信息可以包括时序信息、性能计数器(例如,缓存未命中、分支错误预测等)以及不直接影响跟踪的重放,但仍然可以帮助同步(例如,在捕获用户界面时嵌入数据捕获,因此可以在重放时再现)的事件记录。此外,当跟踪用户模式代码时,记录组件106a可以用以下信息标记(多个)跟踪文件108诸如:(i)用户模式线程被调进或调出的时间,(ii)用户模式线程被暂停的时间,(iii)用户将焦点切换在被跟踪的应用的时间,(iv)应用接收的消息的记录,(v)用户模式进程导致页面错误的时间,等。鉴于本文的公开内容,本领域技术人员将认识到,用于记录任何前述内容的特定方式可以基于实施方式而变化。
[0124]
时间机器调试器106可以被实施成各种形式的软件组件。例如,时间机器调试器
106的至少一个或多个部分(例如,记录组件)可以被实施成注入到正被记录的进程的运行时存储器中的代码部分(即,“检测”正被记录的进程)、操作系统内核组件、完整机器仿真器(例如,bochs、快速仿真器(qemu)等)的一部分,和/或管理程序(例如,来自microsoft的hyper-v、linux上的xen等)的一部分。当被实施成仿真器或管理程序的一部分时,可以使时间机调试器106能够跟踪整个操作系统的执行。因此,时间机器调试器106可以跟踪用户模式代码的执行(例如,当被实施成注入的代码或作为内核的一部分时),和/或跟踪内核模式代码的执行,以及甚至整个操作系统(例如,当被实施成管理程序或仿真器的一部分时)。
[0125]
基于处理器高速缓存的实施方式
[0126]
虽然完全可以以软件实施时间机器调试器106,但是一些实施例包括基于硬件高速缓存的记录模型,其可以进一步减少与记录程序的执行相关联的开销。如前所述,该模型建立在记录组件106a需要创建记录(即,(多个)跟踪文件108)的一般原则的基础上,该记录使得重放组件106b能够以适当的顺序并且以使得每条指令产生与其在记录时产生的相同的输出的方式重放在记录时执行的每个指令。鉴于以上公开内容,清楚的是,(多个)跟踪文件108的重要组件包括可用于在重放时再现存储器读取的数据。如上面所讨论的,记录这种数据的实施例可以包括记录每个读取的值、使用预测算法来预测读取的值、使用存储器的卷影副本、记录存储器页面和/或页面表条目等。
[0127]
用于跟踪(包括记录/再现存储器读取)的基于硬件高速缓存的模型的实施例建立在处理器102(包括高速缓存102b)形成半封闭或准封闭系统的观察之上。为了进一步说明,图6图示了用于基于处理器高速缓存的跟踪的示例计算机架构600,其包括处理器601和系统存储器608,其可以映射到图1的(多个)处理器102和系统存储器103。
[0128]
在概念级别,当将数据加载到高速缓存603中时,处理器601就可以作为用于突发时间的半封闭或准封闭系统自己运行(无需任何输入)。特别地,在程序执行期间,每个处理单元602使用被存储在高速缓存605的数据高速缓存605部分中的数据并使用寄存器607来执行来自高速缓存603的代码高速缓存604部分的指令。例如,图6图示了代码高速缓存604包括用于存储程序代码的多个存储位置604a-604n(例如,高速缓存行),并且数据高速缓存605包括用于存储数据的多个存储位置605a-605n(例如,高速缓存行)。如之前结合图1所讨论的,高速缓存可以包括多个层(例如,级1、级2和级3等)。虽然为了描述简单而将高速缓存605描绘为在处理器601内,但是应当理解,高速缓存605的一个或多个部分(例如,级3高速缓存)实际上可以存在于处理器601之外。
[0129]
当处理单元602需要一些信息流入时(例如,因为它需要尚未在高速缓存603中的代码或数据),发生“高速缓存未命中”并且该信息从系统存储器608的适当的存储位置(即,例如,608a-608n)被带入高速缓存603中。例如,如果当指令在对应于位置608a(包含程序运行时数据)的存储器地址处执行存储器操作时发生数据高速缓存未命中,则该存储器(即,该地址和被存储在该地址处的数据)被带入数据高速缓存605的存储位置中的一个(例如,位置605a)中。在另一示例中,如果在指令在对应于位置608b(包含程序代码)的存储器地址处执行存储器操作时发生代码高速缓存未命中;则该存储器(即,该地址和被存储在该地址的数据)被带入代码高速缓存604的存储位置中的一个(例如,位置604a)中。当新数据被导入高速缓存603时,它可以代替高速缓存603中已经存在的信息。在这种情况下,旧信息被驱逐回系统存储器608中的正确地址。处理单元602然后使用高速缓存603中的新信息继续执
行,直到发生另一个高速缓存未命中并且再次将新信息带入到高速缓存603中为止。
[0130]
因此,用于记录/再现存储器读取的基于硬件高速缓存的模型的实施例依赖于以下实现:除了对未被高速缓存的存储器的访问(例如,对硬件组件和不可高速缓存的存储器的读取,如稍后所讨论的)之外,进程/线程在执行期间执行的所有存储器访问都通过处理器高速缓存603而被执行。结果,不是如上所述的那样创建可以再现每个单个存储器读取的记录(例如,每个读取的记录、卷影副本等),而是记录组件106a可以记录(i)带入高速缓存603中的数据(即,存储器地址和被存储在该地址的数据),和(ii)对未缓存存储器的任何读取。
[0131]
由于处理器102可以被视为半封闭或准封闭系统,因此可以由重放组件106b使用仿真处理器的仿真器及其高速缓存系统来复制在记录时间发生的处理器内的执行流。仿真器被配置成在由重放组件106b给予仿真器与在记录时发生的相同的输入时,在重放时产生与在记录时间期间发生的相同的结果。仿真器不需要是精确的cpu仿真器,只要它仿真指令执行(例如,它们的副作用),它不需要匹配物理处理器的时序、流水线行为等。因此,记录程序执行可以被减少到记录(i)流入到系统中的数据的复制品(例如,基于高速缓存未命中而被带入到高速缓存603中的数据,以及未高速缓存的读取),(ii)指导重放组件106b如何/何时在适当的时间应用于每个输入的数据(例如,使用所执行的指令的计数),以及(iii)描述要被仿真的系统的数据(即,处理器601,包括其高速缓存603)。这使得时间行程调试器106能够在记录跟踪时将处理器102建模为线性执行机器,而无需在处理器102内记录指令执行的内部并行性或流水线操作,并且无需保留处理器102内的每个指令的执行的特定时序的记录。
[0132]
作为总体概述,用于跟踪线程的基于硬件高速缓存的模型通过将处理器寄存器值保存到跟踪中开始,类似于上文描述的技术。附加地,该模型通过确保用于线程的处理器高速缓存603的适当部分为空开始。如下面所讨论的,记录组件106a记录被导入到数据高速缓存605的数据,并且还可以记录被导入到代码高速缓存604的数据。如此,需要在跟踪开始时清除数据高速缓存605,并且如果正在记录对代码高速缓存604的导入,则仅需要清除代码高速缓存604。然后,将线程的代码的至少一部分带入到代码高速缓存604中(例如,通过处理器基于所请求代码的存储器地址执行“高速缓存未命中”,并且如果正在记录对代码高速缓存604的导入,则将导入的高速缓存行存储在(多个)跟踪文件108中),并且处理单元602开始在代码高速缓存604中执行处理器指令。
[0133]
当代码执行其第一存储器操作(例如,对系统存储器608中的存储器地址的读取或写入)时,发生“高速缓存未命中”,因为数据高速缓存605是空的并且因此不包含正被访问的地址处的存储器的副本。如此,系统存储器608的正确部分被带入并存储在数据高速缓存605中的高速缓存行中。例如,如果存储器操作被寻址到系统存储器608中的位置608a处的存储器地址,则在存储器位置608a处的数据被写入数据高速缓存中的行(例如,位置605a)。由于数据已经被带入到高速缓存603中,因此记录组件106a将该数据(即,该地址和寻址的数据)记录到(多个)跟踪文件108。
[0134]
在该点之后,将来的存储器访问要么将新数据带入到高速缓存中(并且因此由记录组件106a记录到(多个)跟踪文件108中),要么针对已经被带入到高速缓存中的数据来执行(例如,它们可以从数据高速缓存605中的高速缓存行读取或对其写入)。不需要记录对已
经在高速缓存中的数据的后续读取。类似于上面结合图1至图5所描述的技术,记录组件106a不需要追踪对高速缓存中的数据的写入,因为可以通过执行具有记录的初始状态的指令并通过再现非确定性指令的副作用来再现这些写入。
[0135]
假设重放组件106b可以访问线程的原始代码,并且在记录时执行未被中断(例如,没有异常),那么如果重放组件106b以空的高速缓存和记录的寄存器值开始,则重放组件106b可以仿真处理器601的执行,包括在适当的时间将适当的数据带入到高速缓存603中,并在适当的时间再现非确定性指令的副作用。
[0136]
因此,与上面结合图1至图5所讨论的技术一样,当使用这种基于高速缓存的模型进行记录时,记录组件106a仍然在(多个)跟踪文件108中记录非确定性指令(他们的输出不唯一地由他们的输入值决定)的副作用。附加地,在一些实施例中,记录组件106a仍然选择跟踪存储器模型并且利用单调增加的数字在(多个)跟踪文件108中记录由线程执行的可排序事件,该单调增加的数字跨线程对这些事件进行排序。
[0137]
附加地,如稍后更详细讨论的,在一些实施例中,记录组件106a跟踪线程的控制流上的改变,这些改变不能独立地由线程的代码确定。例如,由于中断,控制流上的改变可能发生。这些可能包括,例如,异步过程调用(“apc”)、来自内核模式的调用等。
[0138]
此外,在一些实施例中,记录组件106a仅跟踪数据高速缓存605未命中,而在其他实施例中,记录组件106a跟踪数据高速缓存605未命中和代码高速缓存604未命中。特别地,如果线程仅执行非动态代码,则仅需要跟踪被导入到数据高速缓存605中的高速缓存行,因为需要被执行的所有代码在重放时可用。然而,如果线程包括动态代码支持,则还需要跟踪被导入到代码高速缓存605中的高速缓存行。
[0139]
更进一步地,在一些实施例中,记录组件106a跟踪哪些指令分离地从未高速缓存/不可高速缓存的存储器执行存储器读取,因为没有高速缓存未命中来追踪这些读取。未高速缓存/不可高速缓存的读取的示例是来自硬件设备的读取,或来自存储器的读取,其以其他方式被处理器601和/或操作系统认为是不可高速缓存的。在一些实施例中,通过其副作用而不是通过指令记录未高速缓存的读取,诸如记录包含所读取的值的寄存器值。
[0140]
如本文所描述的,由记录组件106a基于对事件的副作用(例如,通过记录事件之后的处理器寄存器的值)的记录来记录一些事件(例如,非确定性处理器指令、未高速缓存的读取等)的发生。这可以被一般化,因为记录组件106a可以基于记录其副作用来记录实际上任何事件的发生,即使事件可以以某种其他方式记录(例如,通过读取存储器值)或者可以从跟踪中被完全省略(例如,因为在给定适当状态时,它会在执行进程的代码期间发生)。在一些实施例中,通过它们的副作用来记录一些事件,即使从跟踪中省略它们或者以某种其他方式记录它们可以更加有效(例如,在跟踪文件大小方面)。
[0141]
例如,许多处理器包括模型专用寄存器(msr),其通常包括控制寄存器。例如,在intel架构中,msr被用于与调试、程序执行跟踪、计算机性能监控和切换cpu特征有关的控制。然而,在访问这些寄存器所需的处理器周期的数目方面,访问这些msr可能非常昂贵。在一些实施方式中,可以像正常处理器寄存器一样追踪msr,通过在跟踪开始时和/或在跟踪期间的各种时间(例如,在跟踪中的“关键帧”)记录它们的初始值,并且然后在线程执行期间追踪它们的值上的改变。然而,在追踪改变的值的复杂性方面,并且因为每次获得它们的值时必须产生前述访问惩罚(即,许多处理器周期),因此这可能变得非常昂贵。
[0142]
相反,一些实施例通过其副作用来追踪msr,而不是直接追踪它们的值。当通过它们的副作用追踪msr时,被跟踪的代码已经消费了它们的值并且接受了访问msr本身的惩罚,并且记录组件106a没有额外的惩罚来获得初始msr值(例如,在跟踪开始时和/或在关键帧时)以用于跟踪的目的。附加地,在整个跟踪中记录值改变没有增加的复杂性。
[0143]
在一些备选实施例中,可以将每个msr视为其好像是空的高速缓存行。因此,遵循本文所描述的基于高速缓存的跟踪技术,类似于存储器位置上的高速缓存未命中,记录对msr的第一次读取,使得在由线程第一次读取msr时将msr的值记录在跟踪中。然后,只有在所读取的值与已经被存储在跟踪中的值不同时,才会记录对msr的未来读取。
[0144]
图7图示了用于使用高速缓存数据记录对可执行实体的执行的可重放跟踪的示例方法700的流程图。如所描绘的,方法700包括并发执行一个或多个线程的动作701。动作701可以包括跨一个或多个处理器的一个或多个处理单元执行可执行实体(例如,进程、内核、管理程序等)的一个或多个线程(例如,用户模式线程、内核线程等),同时用记录组件106a观察它们的执行。例如,动作701可以包括在处理器601的第一处理单元602上执行第一线程,同时可能在相同的处理器601的第二处理单元602上执行第二线程。作为另一示例,动作701可以包括在支持超线程的处理器601的相同的处理单元602上执行两个线程。作为另一示例,动作701可以包括在不同处理器601的不同处理单元602上执行两个线程。前述的组合也是可能的。
[0145]
图7还描绘了方法700包括分离地为每个线程记录分离的跟踪的动作702。动作702可以包括,在执行一个或多个线程期间,分离地为每个线程记录分离的可重放跟踪。因此,如所图示的,动作702可以包括在每个线程执行时,由记录组件106a针对每个线程执行的子动作。
[0146]
图7还描绘了动作702包括针对每个线程记录初始状态的动作703。动作703可以包括记录针对线程的初始处理器寄存器状态。例如,记录组件106a可以记录对应于线程正在其上执行的处理单元602的寄存器607的状态。初始状态还可以包括附加信息,诸如存储器的快照、堆栈信息、线程的teb、进程的peb等。
[0147]
图7还描绘了动作702包括针对每个线程记录导入的高速缓存数据的行的动作704。动作704可以包括,在基于线程的执行检测到处理器数据高速缓存未命中时,响应于处理器数据高速缓存未命中,记录被导入到处理器数据高速缓存中的高速缓存数据的至少一行。例如,在执行代码高速缓存604中的指令期间,这些指令中的一个可以对系统存储器103中的尚未在数据高速缓存605中的特定地址执行存储器操作。如此,发生“高速缓存未命中”并且由处理器601将该地址处的数据导入到数据高速缓存605中。记录组件106a在(多个)跟踪文件108中创建该数据的记录。例如,如果将位置608a处的存储器地址导入高速缓存行605a,则记录组件106a在(多个)跟踪文件108中记录存储器地址的标识及其包含的数据。如果线程执行动态代码,则关于将指令加载到处理器代码高速缓存604也可能发生高速缓存未命中。如此,动作704还可以包括响应于处理器代码高速缓存未命中而记录被导入到处理器代码高速缓存640中的高速缓存数据的至少一行。
[0148]
图7还描绘了动作702还可以包括针对每个线程记录未高速缓存的读取的动作705。动作705可以包括基于线程的执行来记录至少一个未高速缓存的读取。例如,在执行代码高速缓存604中的指令期间,这些指令中的一个指令可以执行来自硬件设备的存储器地
址或来自被认为是不可高速缓存的系统存储器608的一部分的存储器读取。由于在这种情况下没有高速缓存未命中,所以记录组件106a参考该指令将所读取的值记录在(多个)跟踪文件108中。例如,记录组件106a可以通过记录包含所读取的值的寄存器值来记录未高速缓存的读取的副作用。如动作704和动作705之间的双端箭头所图示的,这些动作可以以任何顺序发生,并且在跟踪线程期间可以多次发生。
[0149]
如上面所讨论的,可以通过其副作用来记录一些事件。如此,方法700可以包括,在一个或多个线程的执行期间,通过记录其副作用来记录根据一个或多个线程中的至少一个线程的至少一个事件的发生。例如,记录组件106a可以通过其副作用来记录非确定性指令、未高速缓存的读取和/或对msr的访问,但实际上任何事件都可以通过其副作用来记录。
[0150]
如上面所讨论的,(多个)跟踪文件108可以使用各种技术(例如,使用指令计数、cpu周期、所采取的跳转的计数与程序计数器、存储器访问等)来记录事件发生的时间。在一些实施例中,使用指令计数(相对或绝对)来标识事件发生的时间可能是有利的,因为这样做可以在重放期间去除时序考虑因素。例如,出于重放的目的,底层硬件在记录时间服务缓存未命中所花费的时间可能无关紧要;只有在记录组件106a记录存储器地址“x”具有值“y”,并且该信息及时使其进入高速缓存603以便处理指令“z”时才重要。这可以显著简化重放组件106b精确重放跟踪的所需,因为不需要产生准确的时序。该方法还提供了将所有高速缓存未命中,或者仅将处理单元602实际消耗的那些高速缓存未命中包括在(多个)跟踪文件108中的选择。因此,例如,不需要跟踪由处理单元602从系统存储器608中的推测性读取(作为尝试预测将被执行的接下来的指令的一部分)。然而,如上所述,在其他实施例中,记录组件106a可以在(多个)跟踪文件108中记录时序信息。这样做可以使重放组件106b能够在重放时间暴露该时序,如果需要的话。
[0151]
值得注意的是,如果(多个)跟踪文件108将每个高速缓存条目映射到使其被带入到处理器601中的指令,则在将高速缓存行从高速缓存603驱逐回系统存储器608时,(多个)跟踪文件108不需要捕获任何信息。这是因为如果线程的代码再次需要来自系统存储器608的该数据,那么它将在那时由处理器601重新导入到高速缓存603中。在这种情况下,记录组件106a可以将该数据的重新导入作为新条目记录到(多个)跟踪文件108中。
[0152]
如前所述,在一些实施例中,记录组件106a跟踪不能独自由线程的代码确定的线程的控制流程的变化。例如,当记录用户模式代码时,可能存在需要跟踪的额外的输入,因为操作系统内核可以中断用户模式代码并因此成为外部数据源。例如,当发生一些类型的异常时,它们首先由内核处理,并且然后最终将控制返回到用户模式,该不连续是到系统中的输入。附加地,当进行在系统调用之后不“返回”到该指令的内核调用时,这也是到系统中的不连续。此外,当内核在将控制返回到用户模式代码之前改变处理器寄存器时,这也是到系统中的输入。因此,记录组件106a在(多个)跟踪文件108中创建这些不连续的记录,使得重放组件106b可以在重放时再现它们的副作用。类似地,当记录内核模式代码(例如,利用管理程序)时,可能发生其他外部输入(例如,陷阱、中断等),记录组件106a可以在(多个)跟踪文件中创建这些不连续的记录。
[0153]
在一些实施例中,这些不连续作为信息的流入被记录在(多个)跟踪文件108中。用于记录这些不连续的特定方式可以变化,只要(多个)跟踪文件108包含到被记录的代码中的所有输入以考虑不连续。例如,当从内核模式返回到用户模式时(例如,当正在记录用户
模式代码时),(多个)跟踪文件108可以包含已经由内核(或所有寄存器)改变的寄存器的集合。在另一示例中,(多个)跟踪文件包括系统调用之后的继续指令的地址的标识。在又一示例中,记录组件106a可以在返回到用户模式时刷新处理器高速缓存603、记录所有有效的高速缓存条目,和/或仅在/当使用它们时记录该条目。类似地,当记录内核模式代码时,(多个)跟踪文件可以包括对陷阱/中断之后的继续指令、陷阱/中断所做的任何更改等的记录。
[0154]
在一些实施例中,记录组件106a和重放组件106b被配置成跟踪和仿真处理器的转换旁视缓冲器(“tlb”),诸如图6中的tlb 606。特别地,注意,处理器高速缓存603有时是基于系统存储器608的存储器物理地址,而代码经常将由内核呈现的存储器虚拟地址称为线程/进程的抽象。tlb 606中的条目(例如,606a-606n)存储虚拟地址和物理地址之间的一些最近的转换。因此,在一些实施例中,记录组件106a将tlb 606中的每个新条目记录到(多个)跟踪文件中,跟踪文件为重放组件106b提供在重放时执行转换所需的所有数据。与数据高速缓存605一样,记录组件106a不需要记录来自tlb 606的任何驱逐。
[0155]
跟踪tlb 606的条目还提供几个其他益处。例如,tlb 606使记录组件106a能够知道哪些存储器页面未被高速缓存或不可用,使得可以记录对这些页面的读取(例如,动作706)。而且,tlb 606使记录组件106a能够考虑两个(或更多个)虚拟地址映射到相同的物理地址的情况。例如,如果高速缓存603基于第一虚拟地址驱逐一物理地址条目,并且然后线程经由不同的虚拟地址访问重叠的物理地址,则记录组件106a使用tlb 606来确定它需要将该访问记录为高速缓存未命中的一部分。
[0156]
一些实施例包括硬件辅助模型,其可以进一步减少基于高速缓存的跟踪的性能影响。如前所示,记录跟踪以空的高速缓存603开始(并且因此,高速缓存603被刷新)。此外,如果控制的流程中存在变化(例如,由于中断),则高速缓存603也可以作为从非记录(例如,在内核模式中时)到记录(例如,在执行已经转换回用户模式时)的转换的一部分而被刷新。然而,注意,刷新处理器高速缓存603在计算上可能是昂贵的。因此,实施例包括对处理器601的硬件修改,其可以防止在每次处理器601从非记录转换到记录时都需要刷新高速缓存603。这些示例参考设置位/清除位。应当理解,取决于实施方式,可以认为位用1或0来“设置”,并且通过将其切换到相反的值来“清除”。
[0157]
一些实施例利用可以被用来发信号通知高速缓存行的状态的一个或多个附加位来扩展每个高速缓存行条目(例如,代码高速缓存604的604a-604n和/或数据高速缓存605的605a-605n)。例如,可以设置一个位(例如,为1)来指示高速缓存行已经被记录到(多个)跟踪文件108中。然后,不是在转换时刷新高速缓存,而是处理器601仅需要在高速缓存行上切换(例如,为0)这些位。稍后,如果线程的代码在未设置该位的情况下消耗高速缓存行,则需要将该条目存储到跟踪中,就好像它是高速缓存未命中一样。设置和取消设置该位还使得记录组件106a能够避免跟踪由处理器601进行的任何推测性存储器访问,直到它知道线程的代码确实消耗了那些访问为止。
[0158]
用信号通知每个高速缓存行的状态的一个或多个位也可以以其他方式可用。例如,假设记录组件106a正在记录用户模式代码,并且这种代码调用内核然后返回到用户模式。在这种情况下,不是在处理器601返回到用户模式时清除每个高速缓存行位,而是处理器601可以仅在内核模式代码被修改的高速缓存行上清除该位,并且将设置的该位留在未被修改的任何高速缓存行上。这进一步减少了当从内核返回时,记录组件106a需要添加到
(多个)跟踪文件108的条目的量。然而,该技术可能不适用于所有的内核到用户模式的转换。例如,如果内核刚刚从另一进程上的线程切换到我们正在记录的线程,则应当跨所有高速缓存条目清除该位。
[0159]
值得注意的是,前述构思可用于使用超线程的环境中(即,在相同的处理单元/核上执行多个硬件线程)。许多处理器具有用于每个核的独占高速缓存(例如,每个核可以具有其自己的层1和层2高速缓存),并且还为所有核提供共享的高速缓存(例如,层3高速缓存)。因此,当使用超线程时,在核上执行的多个硬件线程(例如,“线程1”和“线程2”)共享用于该核的相同的独占高速缓存(例如,层1、层2),因此处理器标记由这些线程使用的与该线程相关联的每个缓存行。
[0160]
在一些实施例中,可以将一个或多个位添加到共享高速缓存中的每个高速缓存行,该一个或多个位用信号通知高速缓存行是否实际上已被线程修改。例如,假设正在跟踪“线程1”并且执行切换到在相同的核上的“线程2”。如果“线程2”访问被分配给“线程1”的该核的独占高速缓存中的高速缓存行,则仅当“线程2”已经修改了该高速缓存行时才可以切换该位。然后,当执行切换回“线程1”并且“线程1”访问该相同的高速缓存行时,如果“线程2”没有切换该位,则不需要将该行记录到跟踪中,因为高速缓存行的内容自从“线程1”上次访问它以来没有改变。前述内容也可以被应用于共享的高速缓存。例如,层3高速缓存中的每个高速缓存行可以包括用于它所服务的每个线程的位,该位指示线程是否已经“记录”高速缓存行中的当前值。例如,每个线程可以在消耗该行时将其位设置为1,并在写入该行时将所有其他值设置为0。
[0161]
在一些实施例中,(多个)跟踪文件108可以从它们在记录时执行的顺序乱序地记录条目。例如,假设基于自线程中最后一次不连续(包括内核调用)以来的指令的数目来记录条目。在这种情况下,可以将两个不连续之间的条目重新排序,而不会丢失信息。在一些实施例中,这样做可以实现更快的记录,因为处理器可以对存储器访问进行优化。例如,如果处理器执行指令的序列(例如,a、b和c),其导致产生到(多个)跟踪文件108的条目但是它们之间没有依赖性,则在重放时它们执行的顺序无关紧要。如果指令a访问尚未在高速缓存603中的数据,则处理器601花费许多处理器周期来访问系统存储器608;但是指令b和指令c访问已经在高速缓存603中的数据,然后它们可以被快速执行。然而,如果需要在(多个)跟踪文件108中按顺序记录指令,则必须保持指令b和指令c的执行记录(例如,在处理器的存储器资源中),直到指令a的执行完成为止。相比之下,如果允许乱序地记录指令,则可以在完成指令a之前将指令b和指令c写入(多个)跟踪文件108,从而释放那些资源。
[0162]
相应地,前述实施例提供了用于记录和重放用于时间行程调试的跟踪的新技术,其相对于之前尝试产生了几个数量级的性能改进,这使得能够记录其线程跨多个处理单元并发自由运行的多线程程序,并且相对于之前尝试的跟踪文件,其产生大小减小了几个数量级的跟踪文件等。这些改进极大地减少了跟踪和重放软件所需的计算资源(例如,存储器、处理器时间、存储空间)的量。如此,本文的实施例可用于现实世界的生产环境中,这极大地增强了时间行程调试的可用性和实用性。
[0163]
尽管以结构特征和/或方法动作专用的语言描述了本主题内容,但应当理解,所附权利要求中所定义的主题内容不必限于上文所描述的特征或动作,或者上述动作的顺序。而是,所描述的特征和动作被公开为实施权利要求的示例形式。
[0164]
在不脱离本发明的精神或基本特征的情况下,本发明可以以其他特定形式来实施。所描述的实施例在所有方面都应当被认为仅是说明性的而非限制性的。因此,本发明的范围由所附权利要求而不是前面的描述表示。在权利要求的含义和等同范围内的所有改变都被包含在其范围内。
再多了解一些

本文用于创业者技术爱好者查询,仅供学习研究,如用于商业用途,请联系技术所有人。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

相关文献