【问题标题】:throwing an exception causes segmentation fault抛出异常会导致分段错误
【发布时间】:2010-02-09 19:31:49
【问题描述】:
Collection CollectionFactory::createFromMap(const std::string& name,
        const DataMap& dm) const
{
    if (!Collection::isNameValid(name))
    {
        const std::string error = "invalid collection name";
        throw std::invalid_argument(error);
    }
    Collection c(name, dm);
    dm.initDataCollection(&c, true);
    return c;
}

每当执行 throw 语句时,我都会遇到分段错误。这是 Valgrind 输出的原因。我不知道发生了什么。

==21124== Invalid read of size 1
==21124==    at 0x41D2190: parse_lsda_header(_Unwind_Context*, unsigned char const*, lsda_header_info*) (eh_personality.cc:62)
==21124==    by 0x41D24A9: __gxx_personality_v0 (eh_personality.cc:228)
==21124==    by 0x4200220: _Unwind_RaiseException (unwind.inc:109)
==21124==    by 0x41D2C9C: __cxa_throw (eh_throw.cc:75)
==21124==    by 0x4079BFB: corestore::CollectionFactory::createFromMap(std::string const&, corestore::DataMap const&) const (CollectionFactory.C:43)
==21124==    by 0x8188F86: CollectionFactoryTest::testCreateNewFromMap_InvalidName() (CollectionFactoryTest.C:91)
==21124==    by 0x81895D3: CppUnit::TestCaller<CollectionFactoryTest>::runTest() (TestCaller.h:166)
==21124==    by 0x40D1BB5: CppUnit::TestCaseMethodFunctor::operator()() const (TestCase.cpp:34)
==21124==    by 0x40C18E3: CppUnit::DefaultProtector::protect(CppUnit::Functor const&, CppUnit::ProtectorContext const&) (DefaultProtector.cpp:15)
==21124==    by 0x40CD0FC: CppUnit::ProtectorChain::ProtectFunctor::operator()() const (ProtectorChain.cpp:20)
==21124==    by 0x40CCA65: CppUnit::ProtectorChain::protect(CppUnit::Functor const&, CppUnit::ProtectorContext const&) (ProtectorChain.cpp:77)
==21124==    by 0x40DC6C4: CppUnit::TestResult::protect(CppUnit::Functor const&, CppUnit::Test*, std::string const&) (TestResult.cpp:178)
==21124==  Address 0xc82f is not stack'd, malloc'd or (recently) free'd

我已经对单元测试进行了多次迭代,但这是一个与所有其他迭代相同的错误:

void CollectionFactoryTest::testCreateNewFromMap_InvalidName()
{
    const char* MAP_FILE =
            "smallMapWithThreeSets.xml";
    const char* NAME1 = "name/invalidname";
    const char* NAME2 = "name/invalidname";

    DataMapReader dmr;
    DataMap dm = dmr.getDataMapFromFile(MAP_FILE);

    CollectionFactory cf;
    try
    {
        cf.createFromMap(NAME1, dm);
    }
    catch (std::exception const& e)
    {
        std::cerr << e.what() << std::endl;
    }

    /*CPPUNIT_ASSERT_THROW(cf.createFromMap(NAME1, dm), std::invalid_argument);
    CPPUNIT_ASSERT_THROW(cf.createFromMap(NAME2, dm), std::invalid_argument);*/
}

每个请求,isNameValid 的内容:

bool Collection::isNameValid(const std::string& name)
{
    /* can't be blank */
    if(name.length() == 0)
    {
        return false;
    }
    /* Can't contain '/' */
    if(name.find('/') != std::string::npos)
    {
        return false;
    }
    return true;
}

【问题讨论】:

    标签: c++


    【解决方案1】:

    这是第一个 Valgrind 错误还是以前的错误?

    我的猜测是以前的一些,其中一个正在破坏内存并导致 throw 中断。

    【讨论】:

    • 之前的所有错误都涉及系统调用(sigaction、write)中的未初始化值或未初始化值的条件跳转,因此这些不会破坏内存。
    • 你能减少问题吗?如果你只运行 testCreateNewFromMap_InvalidName 你还会得到段错误吗?
    • 我创建了一个单独的程序。这只是主要的。它所做的只是那个测试中的内容。出现相同的分段错误。我还创建了一个单独的程序,它只是抛出一个异常(不使用我的库),它可以工作。另外,如果我没有抓住,我会得到一个干净的退出,并打印出“Aborted”。
    • 你能再简化一些吗?如果您摆脱 DataMapReader 并只使用一个空的 DataMap 会怎样(因为在您使用 DataMap 之前会引发异常,所以没关系)。
    • 是的。这样做了,问题仍然存在。
    【解决方案2】:

    Dave,您的执行路径中缺少代码,这会阻止您确定问题:

    • DataMapReader 构造函数;
    • DataMapReader::getDataMapFromFile() 的实现;
    • DataMap 构造函数;
    • CollectionFactory 构造函数;

    我建议在分段错误仍然可重现的情况下尝试尽可能多地剥离测试用例。

    我对这个问题有以下猜测:

    • DataMapReader::getDataMapFromFile() 接受对 std::string 的引用,然后将其存储在静态内存或 DataMapReader 实例或 DataMap 实例中。这会导致未定义的行为,因为被引用的 std::string 对象会在退出 DataMapReader::getDataMapFromFile() 后立即销毁,所以所有剩余的引用都会自动失效。
    • DataMapReader::getDataMapFromFile() 返回对存储在堆栈上的 DataMap 实例的引用。一旦 DataMapReader::getDataMapFromFile() 返回并且在调用 dm 的复制构造函数之前,此类实例将被销毁,这会导致未定义的行为。这种情况不太可能发生,因为编译器通常会警告返回指向存储在堆栈中的对象的指针或引用。
    • 如果 DataMapReader::getMapFromFile() 按值返回 DataMap 并且 DataMap 没有明确定义的复制构造函数和/或复制赋值运算符,或者它对那些构造函数和/或运算符的实现不正确,从而不正确地处理指针的复制和/或对 DataMap 拥有的成员的引用(或按值存储的其他成员递归地存在相同的问题)。在这种情况下,在从 DataMapReader::getMapFromFile() 返回之前,可以在为存储在堆栈上的实例调用的 DataMap 析构函数中销毁引用的成员,这会导致未定义的行为。

    虽然这与问题中提到的分段错误无关,但相同的潜在问题可能适用于下面在 CollectionFactory::createFromMap() 中引发异常的代码:

    • 如果输入参数(std::string 和 DataMap)的生命周期小于 Collection 实例的生命周期,则集合构造函数不应存储对它们的引用。
    • DataMap.initDataCollection() 不应存储指向 Collection 实例的指针,因为在堆栈上创建的 Collection 实例的生命周期小于 DataMap 实例的生命周期。集合实例将在从 CollectionFactory::createFromMap() 返回之前被销毁。
    • 集合应该正确实现了复制构造函数和/或复制赋值运算符,否则它会遇到上述问题。

    【讨论】:

      【解决方案3】:

      你是如何链接代码的?例如,如果您正在创建共享库,则可能需要为与位置无关的代码指定编译标志,例如 -fPIC (gcc/g++)。

      【讨论】:

      • 在共享库中抛出异常。事实上,当我静态链接时,错误就消失了。但是,在工作中,我们有两种操作系统配置。较新的 64 位系统使用 11.1 英特尔编译器而不是旧的 9.1。它还使用 gcc4+ 而不是 3.3,并且正在更新的系统上运行。我正在等待与英特尔合作,看看这是否是 9.1 编译器的已知问题。
      猜你喜欢
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 2010-09-14
      • 2013-05-10
      • 2021-05-13
      • 1970-01-01
      • 2018-06-17
      • 1970-01-01
      相关资源
      最近更新 更多