如果消毒剂没有显示任何内容,我该如何调试内存崩溃?

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=、0x7ffc9d3ce9200x7ffc9d3ce910

程序计数器值对应std::_Sp_counted_base<...>::_M_release()函数。

我们不知道崩溃地址 0x7f56b3ba0c20 的来源。它不在当前堆栈指针附近,看起来不像堆地址(尽管它可能是),也不像随机垃圾。 ASan 也不知道这个地址是从哪里来的。

一个可能的解释:地址在堆上,然后它被删除并移动到隔离区(ASan 用它来告诉你悬空访问),但随后隔离区 容量超过了其他 deletes,导致 ASan "forget" 它对那个地址的了解(ASan 不能永远保留每个已删除内存块的信息——这会导致你 运行内存不足)。

您可以尝试增加 ASan 隔离缓冲区的大小:

ASAN_OPTIONS=quarantine_size_mb=4096

(默认为 256,增加直到 运行 内存不足或直到 ASan 告诉您实际上正在访问悬空内存)。