【问题标题】:Why do conditional breakpoints slow my program down so much?为什么条件断点会大大减慢我的程序速度?
【发布时间】:2010-11-17 08:27:57
【问题描述】:

当我在一个循环中调试出现问题的东西时,比如在第 600 次迭代时,每次都必须中断可能会很痛苦。因此,我尝试设置一个条件断点,仅在 I = 600 时才中断。这可行,但现在需要将近一分钟才能到达该点,而在此之前几乎是瞬时的。这是怎么回事,有什么办法可以解决吗?

【问题讨论】:

  • 这从来都不是问题。我不记得它是什么时候开始的,但我似乎记得 Delphi 2009 及更早的版本似乎并没有以任何明显的方式放慢条件断点。我相信从那以后的一次更新中出现了一些问题。我在 Embarcadero 开了一个案子,看看能不能解决这个问题。我确信这是某种错误或超低效率。

标签: delphi debugging breakpoints


【解决方案1】:

它会减慢它的速度,因为每次你到达那个点时,它都必须检查你的状况。

我倾向于临时创建另一个像这样的变量(在 C 中,但在 Delphi 中应该可行)。

int xyzzynum = 600;
while (true) {
    doSomething();
    if (--xyzzynum == 0)
        xyzzynum = xyzzynum;
}

然后我在"xyzzynum = xyzzynum;" 行上放置了一个非条件断点。

程序全速运行,直到循环 600 次,因为调试器只是进行正常的断点中断,而不是每次都检查条件。

您可以根据需要使条件变得复杂。

【讨论】:

  • +1 在这种情况下,操作可能会使用一些简单的东西,比如 if I = 600 then write;并在 write 子句上设置断点。
【解决方案2】:

根据 Mason 的回答,如果程序是使用定义的调试条件构建的,您可以只编译 int 3 汇编程序:

{$ifdef debug}
{$message warn 'debug breakpoint present in code'}
if <condition here> then
  asm int 3 end;
{$endif}

因此,当您在 ide 中进行调试时,您的项目选项中有调试条件。当您为您的客户构建最终产品时(使用您的构建脚本?),您不会包含该符号,因此它不会被编译。

我还包含了 $message 编译器指令,因此您在编译时会看到一个警告,让您知道代码仍然存在。如果您在使用 int 3 的任何地方都这样做,那么您将有一个很好的位置列表,您可以双击这些位置直接进入有问题的代码。

N@

【讨论】:

    【解决方案3】:

    梅森的解释很好。
    通过测试您在调试器下运行的代码,他的代码可以变得更安全

    if (DebugHook <> 0) and <your specific condition here> then
      asm int 3 end;
    

    当应用程序正常运行时,这不会做任何事情,如果它在调试器下运行(无论是从 IDE 启动还是附加到调试器),它将停止。
    如果您不在调试器下,则使用布尔快捷方式 &lt;your specific condition here&gt; 甚至不会被评估。

    【讨论】:

    • 它还会给出 [Pascal 警告] Dist.dpr(72): W1002 Symbol 'DebugHook' is specific to a platform - 让他们在签入前很容易找到。
    【解决方案4】:

    当您遇到断点时,Windows 会停止进程并通知调试器。它必须切换上下文,评估条件,决定不,你不想被通知它,重新启动过程并切换回来。这可能需要很多处理器周期。如果你在一个紧密的循环中执行它,它会比循环的一次迭代花费几个数量级的处理器周期。

    如果您愿意稍微弄乱您的代码,有一种方法可以在不产生所有这些开销的情况下执行条件断点。

    if <condition here> then
      asm int 3 end;
    

    这是一个简单的汇编指令,可以手动向操作系统发送断点通知。现在您可以在程序内评估您的状况,而无需切换上下文。请确保在完成后再次将其取出。如果 int 3 在未连接到调试器的程序中发生故障,则会引发异常。

    【讨论】:

    • 梅森:很好的总结;请注意,很容易将其中一些结构留在代码中。所以我建议通过一些备注(我使用带有@@@ 的东西)或宏来完成,这些保证不会出现在实际产品中。
    • 这是标记为 Delphi,它没有预处理器宏或 @@@ 注释。确保不留下任何内容的一种简单方法是在构建发布版本之前将代码库 grep 为“int 3”。
    • 我一般不加汇编语言,而是加一条语句“volatile int e = 9;”。这给了我一个声明来设置断点。还要添加一个#pragma(如果 Delphi 允许)来警告你有调试代码。
    • 你可以做这样的事情。与您的 #pragma 等效的内容类似于:{$MESSAGE HINT Debug code here.}
    • 这是一个错误的答案!您不想冒险在程序中留下错误的 int 3 。看看 Pax 的答案——你设置断点的无害行。如果它最终出现在生产代码中,这不会造成麻烦。
    【解决方案5】:

    通常情况下,条件断点的工作方式是在代码中插入适当的中断指令,然后检查您指定的条件。它将在每次迭代时进行检查,并且很可能是检查的实现方式导致了延迟,因为调试器不太可能编译并将完整的检查和断点代码插入现有代码中。

    如果您将条件后跟一个没有副作用的操作直接放入代码中并在该操作上中断,那么您可能能够加速此操作的一种方法。完成后请记住删除条件和操作。

    【讨论】:

    • 可能不是条件检查本身,而是上下文切换以及停止和重新启动进程的开销。无论如何,您都不会注意到使用正常的断点,因为无论如何您都会进入调试器,但是在它继续运行的条件下,它会很快变得非常明显。
    【解决方案6】:

    任何调试器中的条件断点(我只是在此推测)都需要进程在每次遇到断点时在程序和调试器之间来回翻转。这个过程很耗时,但我认为您无能为力。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 2019-05-29
      • 2018-03-11
      • 2012-03-15
      • 2015-06-14
      相关资源
      最近更新 更多