如果消毒剂没有显示任何内容,我该如何调试内存崩溃?
How can I debug memory crash if sanitizer doesn't show anything?
我有一个复杂的应用程序在退出时崩溃。我无法用最小的例子重现错误。当在应用程序退出时调用 class 的析构函数并且共享指针成员被销毁时,会发生崩溃。我基本上在做的是:
// plugin (.so loaded at runtime)
// called during application run
void SomePluginClass::foo()
{
auto ptr = std::make_shared<int>();
libraryObj.bar(ptr);
}
// library (.so linked to the executable and the plugin)
// SomeLibraryClass.hpp
class SomeLibraryClass
{
public
// ... some other code
~SomeLibraryClass();
void bar(std::shared_ptr<int> ptr);
private:
std::shared_ptr<int> m_ptr{};
}
// SomeLibraryClass.cpp
// called during application run
void SomeLibraryClass::bar(std::shared_ptr<int> ptr) { m_ptr = ptr; }
// called on application exit and cleanup
SomeLibraryClass::~SomeLibraryClass()
{
// crash happens here
// use_count shows 1
// reset() used here for debugging purposes as it causes the same crash as implicit destructor call
m_ptr.reset();
}
我尝试 运行 使用 Valgrind
和 gcc 地址清理程序的应用程序 - 它们在 运行 期间都没有显示任何问题,但在崩溃后显示问题.例如,这里有几行消毒剂的输出:
==11744==ERROR: AddressSanitizer: SEGV on unknown address 0x7f56b3ba0c20 (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0)
==11744==The signal is caused by a READ memory access.
#0 0x555ac6680eac in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/7/bits/shared_ptr_base.h:154
#1 0x555ac6680b33 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/7/bits/shared_ptr_base.h:684
#2 0x7f56e5e562cd in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/7/bits/shared_ptr_base.h:1123
#3 0x7f56e5e56574 in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::reset() /usr/include/c++/7/bits/shared_ptr_base.h:1235
数字 (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0)
是什么意思?
我还能做些什么来找到崩溃源?
如果您想在销毁 SomeLibraryClass 对象时销毁在库外分配的资源,那么您就是在搞乱资源所有权。你不应该那样做。如果你只是想释放你对ptr管理的obj的共享所有权,你根本不需要调用'm_ptr.reset()'。
What does numbers (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0) mean?
在崩溃时(这是由于试图访问地址 0x7f56b3ba0c20
引起的),程序计数器 (PC)、帧指针 (BP) 和堆栈指针 (SP) 寄存器的值为 [=分别为 12=、0x7ffc9d3ce920
和 0x7ffc9d3ce910
。
程序计数器值对应std::_Sp_counted_base<...>::_M_release()
函数。
我们不知道崩溃地址 0x7f56b3ba0c20
的来源。它不在当前堆栈指针附近,看起来不像堆地址(尽管它可能是),也不像随机垃圾。 ASan 也不知道这个地址是从哪里来的。
一个可能的解释:地址在堆上,然后它被删除并移动到隔离区(ASan 用它来告诉你悬空访问),但随后隔离区 容量超过了其他 delete
s,导致 ASan "forget" 它对那个地址的了解(ASan 不能永远保留每个已删除内存块的信息——这会导致你 运行内存不足)。
您可以尝试增加 ASan 隔离缓冲区的大小:
ASAN_OPTIONS=quarantine_size_mb=4096
(默认为 256,增加直到 运行 内存不足或直到 ASan 告诉您实际上正在访问悬空内存)。
我有一个复杂的应用程序在退出时崩溃。我无法用最小的例子重现错误。当在应用程序退出时调用 class 的析构函数并且共享指针成员被销毁时,会发生崩溃。我基本上在做的是:
// plugin (.so loaded at runtime)
// called during application run
void SomePluginClass::foo()
{
auto ptr = std::make_shared<int>();
libraryObj.bar(ptr);
}
// library (.so linked to the executable and the plugin)
// SomeLibraryClass.hpp
class SomeLibraryClass
{
public
// ... some other code
~SomeLibraryClass();
void bar(std::shared_ptr<int> ptr);
private:
std::shared_ptr<int> m_ptr{};
}
// SomeLibraryClass.cpp
// called during application run
void SomeLibraryClass::bar(std::shared_ptr<int> ptr) { m_ptr = ptr; }
// called on application exit and cleanup
SomeLibraryClass::~SomeLibraryClass()
{
// crash happens here
// use_count shows 1
// reset() used here for debugging purposes as it causes the same crash as implicit destructor call
m_ptr.reset();
}
我尝试 运行 使用 Valgrind
和 gcc 地址清理程序的应用程序 - 它们在 运行 期间都没有显示任何问题,但在崩溃后显示问题.例如,这里有几行消毒剂的输出:
==11744==ERROR: AddressSanitizer: SEGV on unknown address 0x7f56b3ba0c20 (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0)
==11744==The signal is caused by a READ memory access.
#0 0x555ac6680eac in std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() /usr/include/c++/7/bits/shared_ptr_base.h:154
#1 0x555ac6680b33 in std::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() /usr/include/c++/7/bits/shared_ptr_base.h:684
#2 0x7f56e5e562cd in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() /usr/include/c++/7/bits/shared_ptr_base.h:1123
#3 0x7f56e5e56574 in std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::reset() /usr/include/c++/7/bits/shared_ptr_base.h:1235
数字 (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0)
是什么意思?
我还能做些什么来找到崩溃源?
如果您想在销毁 SomeLibraryClass 对象时销毁在库外分配的资源,那么您就是在搞乱资源所有权。你不应该那样做。如果你只是想释放你对ptr管理的obj的共享所有权,你根本不需要调用'm_ptr.reset()'。
What does numbers (pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0) mean?
在崩溃时(这是由于试图访问地址 0x7f56b3ba0c20
引起的),程序计数器 (PC)、帧指针 (BP) 和堆栈指针 (SP) 寄存器的值为 [=分别为 12=、0x7ffc9d3ce920
和 0x7ffc9d3ce910
。
程序计数器值对应std::_Sp_counted_base<...>::_M_release()
函数。
我们不知道崩溃地址 0x7f56b3ba0c20
的来源。它不在当前堆栈指针附近,看起来不像堆地址(尽管它可能是),也不像随机垃圾。 ASan 也不知道这个地址是从哪里来的。
一个可能的解释:地址在堆上,然后它被删除并移动到隔离区(ASan 用它来告诉你悬空访问),但随后隔离区 容量超过了其他 delete
s,导致 ASan "forget" 它对那个地址的了解(ASan 不能永远保留每个已删除内存块的信息——这会导致你 运行内存不足)。
您可以尝试增加 ASan 隔离缓冲区的大小:
ASAN_OPTIONS=quarantine_size_mb=4096
(默认为 256,增加直到 运行 内存不足或直到 ASan 告诉您实际上正在访问悬空内存)。