我才知道有数据断点。在过去的 5 年里,我一直使用 Visual Studio 在 C++ 中工作,我从未使用过数据断点。
有人可以阐明什么是数据断点,何时使用它们以及如何将它们与 VS 一起使用?
根据我的理解,当我们想要检查变量值的变化时,我们可以设置一个数据断点。在这种情况下,我们可以在变量值上设置一个带有条件的数据断点。
还有其他例子吗?
我才知道有数据断点。在过去的 5 年里,我一直使用 Visual Studio 在 C++ 中工作,我从未使用过数据断点。
有人可以阐明什么是数据断点,何时使用它们以及如何将它们与 VS 一起使用?
根据我的理解,当我们想要检查变量值的变化时,我们可以设置一个数据断点。在这种情况下,我们可以在变量值上设置一个带有条件的数据断点。
还有其他例子吗?
好的 ol' Daniel LeCheminant对数据断点的作用有一个可靠的答案,所以我将介绍一些突出有用用途的轶事:
任何您知道会发生什么变化,但很少或根本不知道更改它的代码在哪里的场景(否则您可以简单地使用条件断点)。具体来说,
“不可能”的场景- 程序崩溃,因为变量X
是NULL
,而变量X
永远不应该是,NULL
因为任何地方都没有代码将变量设置X
为NULL
. 在初始化的代码中放置一个普通断点X
,当它被命中时,设置一个数据断点来观察对NULL
. 更常见的是内存释放过早的情况,并且仍然有指向它的指针:使用数据断点找出谁在释放内存。
乏味的场景- 第三方库正在对您的数据结构做坏、讨厌、可怕的事情。你知道它正在发生,因为有人在破坏你的数据,显然你的代码是完美的。但你不知道何时何地。当然,您可以单步执行一兆字节的反汇编 DLL……但是,当您可以在数据上设置数据断点时,请坐等它被破坏!
Heisenbugs - 类似于不可能的情况,但是当你观察得太近时它们会消失,这样正常的断点 - 甚至是条件断点 - 都是无用的。时序和用户输入敏感逻辑特别容易受到这类事情的影响。由于数据断点根本不需要调试器实际中断,直到时间合适,假设您可以想出一个仅在实际发生难以捉摸的错误时才会更改的内存位置,您可以使用数据断点设置陷阱Heisenbug 并在当场抓住它。
意大利面场景——在旧的、腐烂的代码库中很常见,在这些代码库中,到处都可以访问全局数据。是的,你可以使用普通的条件断点......但你需要数百个。数据断点使它变得容易。
定义:
数据断点允许您在存储在指定内存位置的值更改时中断执行。
来自 MSDN:如何:设置数据断点:
如何设置内存更改断点
从 Debug 菜单中,选择 New Breakpoint 并单击 New Data Breakpoint
——或者——</p>
在 Breakpoints 窗口菜单中,单击 New 下拉菜单并选择 New Data Breakpoint。
出现新建断点对话框。
在地址框中,输入一个内存地址或计算为内存地址的表达式。例如,&foo 在变量 foo 的内容发生变化时中断。
在 Byte Count 框中,输入您希望调试器监视的字节数。例如,如果您输入 4,调试器将监视从 &foo 开始的四个字节,并在其中任何一个字节更改值时中断。
单击确定。
到目前为止,我们已经有了一个很好的定义和一堆很好的理论解释。
让我们举一个具体的例子!
我目前正在研究一个相当大且复杂的代码库。我对一段代码做了一个小的安全更改,并开始在一个完全不相关的代码库中崩溃内存分配器。这通常表明您在内存管理方面做得非常错误 - 双重删除或写入越界。
值得庆幸的是,我们可以选择打开一个调试内存管理器来检查这样的事情。我打开它,它立即开始报告内存块保护违规,这意味着某些内容超出了范围。问题是这个报告只有在内存被释放后才会出现——基本上是在说“嘿,有些东西坏了。希望你能弄清楚是什么!”
不幸的是,这个特定的内存块,在释放时,与字面上成千上万的其他内存块完全没有区别。幸运的是,我们的调试框架使用连续 ID 标记每个分配,并且损坏的内存具有一致的 ID(如果您好奇的话,#9667。)稍后在内存管理器中的一个快速断点,我能够找到那个位置内存已分配。事实证明,这也不是立即有帮助的。
但那时,我有几个重要的组成部分:
鉴于此,我可以在该特定字节上设置一个数据断点,然后点击“开始”并找出损坏发生的位置。
我所做的 - 它导致了一个错误的错误,我现在正在修复它。
这是数据断点如何有用的具体示例。:)
我相信数据断点是当某些内存设置为某个值时会发生的断点。例如,您可以在典型 for 循环中的 i == 10 时设置断点,以在第 10 次迭代后停止。您还可以观察堆上变量的变化,例如等待修改类的成员。