调试堆上损坏的对象
Debugging a Corrupted Object on the Heap
我正在调试一个重要的软件项目,其中堆上有一堆对象。在某个时间点(至少)这些对象之一被损坏。
我在我的 class 中添加了一个 const 成员作为金丝雀,事实上,它在执行过程中被破坏了。通常我会向这个变量添加一个观察点,以确定何时写入内存。但是,我不知道哪个实例被覆盖了,因为存储在 class 中的任何信息也会被损坏。
我的对象太多,无法在每个对象上设置观察点,而且我无法使用较小的输入集进行重现。 运行 valgrind 我看到 "Invalid read of size 4",这是我正在读取的 4 个字节的 canary int,但此时已经太晚了。
关于如何从这里开始的任何建议?
可能这不够具体,但当我遇到类似问题时,这就是我最终做的事情。我假设您可以确定性地重现您的问题。
我的策略是先找出是哪个实例导致了问题。这是我在暴露症状的特定线路上使用计数器完成的。例如,在 Visual Studio 上,我会设置一个在第 100000 次命中时触发的断点,这样它就永远不会触发;但是 Visual Studio 仍然告诉你在执行过程中遇到了多少次断点。通过反复试验,我会发现问题发生在第 20 次遇到断点时,因此我将断点设置为在第 19 次迭代时触发,以便能够在损坏发生之前区分适当的实例。
从那里开始,我可以获得之前损坏的变量的地址,并使用调试器找出发生了什么:收集有关故障实例的足够信息。
然后,我在关键的地方设置了断点,这些断点是由条件触发的:例如。仅针对具有适当地址或成员中具有特定值的实例触发。
您可能会准确地知道症状出现的时间,但不是问题所在,但这仍然是问题。
希望对您有所帮助!
Running valgrind I see "Invalid read of size 4", which is my canary int of 4 bytes being read but at this point it's already too late.
你很困惑:如果 valgrind 告诉你你在做无效读取(大概是因为对象已经被释放),那么你正在读取 danging(已经被释放)的对象, 正是你的问题。
您不应尝试访问此类对象,在您释放/删除该对象后您的 Canary 已被更改/损坏的事实无关紧要。
我设法找出导致我的问题的原因。原来我正在看的物体根本就不存在。就像@employed-russian 一样,我想知道我的对象是否在我不知道的地方被删除了。在析构函数上放置断点没有产生任何结果,所以唯一合理的解释是指针本身无效,指向的内存不是我的 class 的有效实例。
瞧瞧;我取消引用的指针未被另一个 class 的某个构造函数初始化。当我添加一个 null 的显式检查并且 Valgrind 的错误变成 Conditional jump or move depends on uninitialised value(s)
时,我明白了。通过使用--track-origins=yes
,我很快找出了未初始化数据的来源,即初始化列表中缺少的指针。
(我知道编译器可以使用 -Wuninitialized
检测未初始化的值,但显然我的 clang(苹果)版本不想在启用 -Wall
的情况下提及它。)
我正在调试一个重要的软件项目,其中堆上有一堆对象。在某个时间点(至少)这些对象之一被损坏。
我在我的 class 中添加了一个 const 成员作为金丝雀,事实上,它在执行过程中被破坏了。通常我会向这个变量添加一个观察点,以确定何时写入内存。但是,我不知道哪个实例被覆盖了,因为存储在 class 中的任何信息也会被损坏。
我的对象太多,无法在每个对象上设置观察点,而且我无法使用较小的输入集进行重现。 运行 valgrind 我看到 "Invalid read of size 4",这是我正在读取的 4 个字节的 canary int,但此时已经太晚了。
关于如何从这里开始的任何建议?
可能这不够具体,但当我遇到类似问题时,这就是我最终做的事情。我假设您可以确定性地重现您的问题。
我的策略是先找出是哪个实例导致了问题。这是我在暴露症状的特定线路上使用计数器完成的。例如,在 Visual Studio 上,我会设置一个在第 100000 次命中时触发的断点,这样它就永远不会触发;但是 Visual Studio 仍然告诉你在执行过程中遇到了多少次断点。通过反复试验,我会发现问题发生在第 20 次遇到断点时,因此我将断点设置为在第 19 次迭代时触发,以便能够在损坏发生之前区分适当的实例。
从那里开始,我可以获得之前损坏的变量的地址,并使用调试器找出发生了什么:收集有关故障实例的足够信息。
然后,我在关键的地方设置了断点,这些断点是由条件触发的:例如。仅针对具有适当地址或成员中具有特定值的实例触发。
您可能会准确地知道症状出现的时间,但不是问题所在,但这仍然是问题。
希望对您有所帮助!
Running valgrind I see "Invalid read of size 4", which is my canary int of 4 bytes being read but at this point it's already too late.
你很困惑:如果 valgrind 告诉你你在做无效读取(大概是因为对象已经被释放),那么你正在读取 danging(已经被释放)的对象, 正是你的问题。
您不应尝试访问此类对象,在您释放/删除该对象后您的 Canary 已被更改/损坏的事实无关紧要。
我设法找出导致我的问题的原因。原来我正在看的物体根本就不存在。就像@employed-russian 一样,我想知道我的对象是否在我不知道的地方被删除了。在析构函数上放置断点没有产生任何结果,所以唯一合理的解释是指针本身无效,指向的内存不是我的 class 的有效实例。
瞧瞧;我取消引用的指针未被另一个 class 的某个构造函数初始化。当我添加一个 null 的显式检查并且 Valgrind 的错误变成 Conditional jump or move depends on uninitialised value(s)
时,我明白了。通过使用--track-origins=yes
,我很快找出了未初始化数据的来源,即初始化列表中缺少的指针。
(我知道编译器可以使用 -Wuninitialized
检测未初始化的值,但显然我的 clang(苹果)版本不想在启用 -Wall
的情况下提及它。)