代码之家  ›  专栏  ›  技术社区  ›  Dave

引发异常会导致分段错误

c++
  •  7
  • Dave  · 技术社区  · 15 年前
    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;
    }
    
    3 回复  |  直到 15 年前
        1
  •  2
  •   R Samuel Klatchko    15 年前

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

    我的猜测是,有以前的一个,其中之一是腐蚀记忆,导致抛出打破。

        2
  •  2
  •   valyala    14 年前

    • DataMapReader构造函数;
    • DataMapReader::getDataMapFromFile()的实现;
    • 数据映射构造器;

    我建议在分段错误仍然可以重现的情况下,尽量剥离测试用例。

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

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

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

    • 如果输入参数(std::string和DataMap)的生存期小于集合实例的生存期,则集合构造函数不应存储对这些参数的引用。
    • 集合应该正确实现了复制构造函数和/或复制赋值运算符,否则它将遇到上述问题。
        3
  •  1
  •   Tom    14 年前

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