首页 > 解决方案 > 如果消毒剂没有显示任何内容,我该如何调试内存崩溃?

问题描述

我有一个复杂的应用程序在退出时崩溃。我无法通过最小的示例重现错误。当在应用程序退出时调用类的析构函数并且共享指针成员被破坏时,就会发生崩溃。我基本上在做的是这样的:

// 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();
}

我尝试使用Valgrindgcc address sanitizer 运行应用程序——它们在运行时都没有显示任何问题,但在崩溃后显示了问题。例如,这里有一些 sanitizer 的输出:

==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)意思?

我还能做些什么来找到崩溃源?

标签: c++crashaddress-sanitizer

解决方案


数字(pc 0x555ac6680ead bp 0x7ffc9d3ce920 sp 0x7ffc9d3ce910 T0)是什么意思?

在崩溃时(由试图访问地址引起0x7f56b3ba0c20),程序计数器 (PC)、帧指针 (BP) 和堆栈指针 (SP) 寄存器的值分别为0x555ac6680ead,0x7ffc9d3ce9200x7ffc9d3ce910

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

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

一种可能的解释:地址在堆上,然后被删除并移动到隔离区(ASan 用它来告诉您有关悬空访问的信息),但随后隔离区容量被其他deletes 超出,导致 ASan “忘记”它所知道的关于那个地址(ASan 不能永远保存关于每个已删除内存块的信息——这会导致你的内存用完)。

您可以尝试使用以下方法增加 ASan 隔离缓冲区的大小:

ASAN_OPTIONS=quarantine_size_mb=4096

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


推荐阅读