BOOST单元测试堆栈溢出

时间:2012-09-05 10:16:05

标签: c++ visual-studio-2010 boost

我现在正在使用Boost Unit Test为我的项目执行单元测试。每次运行单元测试时,都会出现内存堆栈问题。我调试了BOOST库的源代码,我发现问题来自于调用unit_test_suite.hpp文件中的以下代码:

void
traverse_test_tree( test_unit_id id, test_tree_visitor& V )
{
  global_i = global_i + 1;
   std::cout<<global_i<<std::endl;
    if( ut_detail::test_id_2_unit_type( id ) == tut_case )
        traverse_test_tree( framework::get<test_case>( id ), V );
    else
        traverse_test_tree( framework::get<test_suite>( id ), V );
}

我从VC10获得的错误信息是:

Unhandled exception at 0x779815de in  TestApplication.exe: 0xC00000FD: Stack overflow.

我想知道测试程序有什么问题。谢谢!

编辑根据我查看代码的建议,会发生非常奇怪的事情:如果测试套件在与main()相同的程序中定义,则可以正常工作;但是,如果测试套件来自.dll,则会发生错误。我列出以下代码来说明我的问题:

boost::unit_test::test_suite* main_global_test_suite;
     void Hellotestdll()
        {
            int i= 1;
            int j= 2;
            BOOST_CHECK(i == j);


        }
        boost::unit_test::test_suite* get_abc_test_suite()
        {
            test_suite* ts = BOOST_TEST_SUITE( "unit_geometric" );
            ts->add( BOOST_TEST_CASE( &Hellotestdll ) );
            return ts;
        }

int main( int argc, char* argv[] )
{
    try 
    {
        /**
        * Step 1. obtain options
        */
        char* optionLine[1024];
        int len;
        len = obtain_options(optionLine, argc, argv);
        /**
        * Step 2. perform unit test based on user's options
        */
        int test_status=0;  
        main_global_test_suite = get_abc_test_suite();  
        test_status = unit_test_main(run_global_test_suite, len, optionLine);
        return test_status;

    }
    catch(std::exception& e)
    {
        std::cout << e.what() << std::endl;
        return 1;
    }   
    catch (const std::string& s) 
    {
        std::cout << s << std::endl;
        return 1;
    }
    catch (...)
    {
        return 1;
    }


} 

以上代码非常有效。但是,如果测试套件来自.dll,例如:

// dll_header.h
namespace abc
{

    ABC_EXPORT boost::unit_test::test_suite* get_geometric_test_suite();
}
// dll_header.cpp
namespace abc
{

    using namespace boost;
    using namespace boost::unit_test;

     void Hellotestdllabc()
       {
        int i= 1;
        int j= 2;
               BOOST_CHECK(i == j);
     }

    boost::unit_test::test_suite* get_abc_test_suite()
    {
        test_suite* ts = BOOST_TEST_SUITE( "unit_abc" );

        ts->add( BOOST_TEST_CASE( &Hellotestdllabc ) );


        return ts;
    }

}

然后,如果我使用以下代码调用此测试套件:

 int main( int argc, char* argv[] )
    {
         ............
            /**
            * Step 2. perform unit test based on user's options
            */
            int test_status=0;  
            main_global_test_suite = abc::get_abc_test_suite();  
            test_status = unit_test_main(run_global_test_suite, len, optionLine);
            return test_status;

        }

会发生恼人的堆栈溢出错误。

问题总结

  (1) boost dll with MDd (Succeed) 

如果我将boost单元测试库(使用定义-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB)和正在运行的可执行程序与相同的动态运行时库(多线程调试DLL(MDd))链接起来,它将起作用。< / p>

(2) boost dll with MTd (Failed)

如果boost单元测试库(定义为-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB)和正在运行的可执行程序被编译并链接到相同的静态运行时库(Multi-thred Debu(MTd)),我将有一个崩溃,但崩溃与我上面报道的崩溃不同: enter image description here

(3) boost static lib with MDd (Failed)

如果将boost构建为静态库(具有-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB的定义),则boost库和可执行程序都使用相同的动态运行时库(MDd)构建。将发生以下崩溃: enter image description here

(4) boost static lib with MTd (Failed)

如果将boost构建为静态库(定义为-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB),则boost库和可执行程序都使用相同的静态运行时库(MTd)构建。将发生以下崩溃: enter image description here

1 个答案:

答案 0 :(得分:2)

   ABC_EXPORT boost::unit_test::test_suite* get_geometric_test_suite();

单元测试的目的是尽早发现代码中的问题。这很有效,只是你早期发现问题非常。太早甚至不能让单元测试正常运行。

返回指向C ++对象的指针的DLL中的函数通常是个问题。当C ++对象的布局完全匹配编译器在编译DLL和EXE时所做的假设时,它才会出现好结果。并且对象存在于两个模块都可以访问的堆上,因为DLL创建了对象,并且您的EXE需要删除它。

要允许正确删除对象,DLL和EXE 必须共享相同的CRT版本。使用/ MT构建程序时会遇到麻烦,要求提供CRT的静态版本。相关的编译器设置是C / C ++,代码生成,运行时库设置。您的Debug配置必须使用/ MDd,您的Release配置必须使用/ MD。对于EXE和DLL项目,以及编译时的Boost库。如果它是/ MTd和/ MT,那么DLL将有自己的CRT副本链接到它,并将使用自己的堆来分配。 EXE无法正确删除该对象,因为它使用另一个堆。无论如何,这样做会产生不确定的行为。任何事情都可能发生,当你在比XP更新的Windows版本上运行你的程序时,你往往会很幸运。当你运行附加调试器的单元测试时,Vista和up将使用调试堆,它会在注意到传递给:: operator delete的指针无效时调用断点。请务必让链接器自动找到要链接的正确Boost .lib,不要自己强制使用。

对象布局更可能是您的问题,不幸的是,诊断起来要困难得多。通过使用完全相同的编译器设置构建EXE和DLL,可以避免麻烦。附加要求是它们必须与用于构建Boost库的设置相匹配。这当然是困难的部分,需要时间机器。特别是_HAS_ITERATOR_DEBUGGING宏是一个麻烦制造者,像std :: vector这样的基本STL类将具有不同的布局,这取决于该宏的值。

我意识到这很模糊,但问题上没有足够的信息来真正诊断这个问题。您可以执行的一项非常基本的检查是将返回的boost :: unit_test :: test_suite指针放在监视表达式中。如果您在步入Boost代码时突然看到该对象的成员发生了更改,那么您知道您有一个对象布局问题。接下来发生的事情是高度不可预测的,堆栈溢出肯定是可能的。另一种诊断是使用Debug + Windows + Registers窗口。当您跳过功能时,请确保ESP寄存器值稳定。