简体   繁体   English

BOOST 单元测试堆栈溢出

[英]BOOST Unit Test stack overflow

I am now using Boost Unit Test to perform unit test for my project.我现在正在使用Boost Unit Test为我的项目执行单元测试。 Every time I run the unit test, I got a memory stack problem.每次运行单元测试时,都会遇到内存堆栈问题。 I debug into the source code of BOOST library, and I find that the problem comes from invoking the following codes in unit_test_suite.hpp file:调试到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 );
}

The error information I have obtained from VC10 is:我从VC10得到的错误信息是:

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

I was wondering what's wrong with the test program.我想知道测试程序有什么问题。 Thanks!谢谢!

EDIT Based on the suggestions I looked through my codes, and very strange things happen: if the test suite is defined in same program with main(), it works;编辑根据我查看代码的建议,发生了非常奇怪的事情:如果测试套件与 main() 在同一程序中定义,则它可以工作; however, if the test suite is from a .dll, the error will occur.但是,如果测试套件来自 .dll,则会发生错误。 I list the following codes to illustrate my problem:我列出以下代码来说明我的问题:

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;
    }


} 

The above codes work very well.上面的代码工作得很好。 But if the test suite is from a .dll, for example:但如果测试套件来自 .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;
    }

}

Then if I invoke this test suite, with the following codes:然后,如果我使用以下代码调用此测试套件:

 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;

        }

The annoying stack overflow error will happen.会发生烦人的堆栈溢出错误。

Summery of the Problems问题总结

  (1) boost dll with MDd (Succeed) 

If I link the boost unit test library (with the definition -DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB )and the running executable program with the same dynamic run-time library (Multi-thread Debug Dll (MDd)), it will work.如果我将 boost 单元测试库(定义为-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB )和正在运行的可执行程序与相同的动态运行时库(多线程调试 Dll (MDd))链接起来,它将起作用。

(2) boost dll with MTd (Failed)

If the boost unit test library (with the definition -DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB )and the running executable program are compiled and link with the same static run-time libray (Multi-thred Debu (MTd)), I will have a crash, but the crash is different from the one I reported above:如果 boost 单元测试库(定义为-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB )和正在运行的可执行程序被编译并链接到相同的静态运行时库(多线程调试(MTd)),我将有一个崩溃,但崩溃与我上面报告的崩溃不同:在此处输入图片说明

(3) boost static lib with MDd (Failed)

If the boost is built as a static library (with the definition of -DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB ), and both the boost library and the executable program are built with the same dynamic run-time library (MDd).如果 boost 构建为静态库(定义为-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB ),并且 boost 库和可执行程序都使用相同的动态运行时库 (MDd) 构建。 The following crash will happen:将发生以下崩溃:在此处输入图片说明

(4) boost static lib with MTd (Failed)

If the boost is built as a static library (with the definition of -DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB ), and both the boost library and the executable program are built with the same static run-time library (MTd).如果 boost 构建为静态库(定义为-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB ),并且 boost 库和可执行程序都使用相同的静态运行时库 (MTd) 构建。 The following crash will happen:将发生以下崩溃:在此处输入图片说明

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

The point of unit tests is to find problems in code early.单元测试的重点是尽早发现代码中的问题。 That worked, it is just that you found the problem very early.那行得通,只是你早就发现了问题。 Too early to even allow the unit test to run properly.甚至让单元测试正常运行还为时过早。

Functions in a DLL that returns pointers to C++ objects are a problem in general. DLL 中返回指向 C++ 对象的指针的函数通常是一个问题。 It will only come to a good end when the layout of the C++ object exactly matches the assumptions made by the compiler when it compiled both your DLL and your EXE.只有当 C++ 对象的布局与编译器在编译 DLL 和 EXE 时所做的假设完全匹配时,它才会有好的结局。 And that the object lives on a heap that both modules have access to, required since the DLL creates the object and your EXE needs to delete it.并且该对象位于两个模块都可以访问的堆上,这是必需的,因为 DLL 创建了该对象,而您的 EXE 需要删除它。

To allow the object to be properly deleted, both the DLL and the EXE must share the same CRT version.若要正确删除对象,DLL 和 EXE必须共享相同的 CRT 版本。 You will get into trouble when you build your program with /MT, asking for the static version of the CRT.当您使用 /MT 构建您的程序时,您会遇到麻烦,要求 CRT 的静态版本。 The relevant compiler setting is C/C++, Code Generation, Runtime library setting.相关的编译器设置为 C/C++、代码生成、运行时库设置。 Your Debug configuration must use /MDd, your Release configuration must use /MD.您的 Debug 配置必须使用 /MDd,您的 Release 配置必须使用 /MD。 For both the EXE and the DLL project, as well as the Boost library when it was compiled.对于 EXE 和 DLL 项目,以及编译时的 Boost 库。 If it is /MTd and /MT then the DLL will have its own copy of the CRT linked into it and will use its own heap to allocate from.如果它是 /MTd 和 /MT,则 DLL 将拥有自己的 CRT 副本链接到其中,并将使用自己的堆进行分配。 The EXE cannot properly delete the object since it uses another heap. EXE 无法正确删除该对象,因为它使用了另一个堆。 Doing so anyway will produce undefined behavior.无论如何这样做都会产生未定义的行为。 Anything can happen, you tend to only get lucky when you run your program on a version of Windows that's newer than XP.任何事情都可能发生,只有在比 XP 更新的 Windows 版本上运行程序时,您才会走运。 Vista and up will use a debug heap when you run your unit test with a debugger attached, it invokes a breakpoint when it notices that the pointer passed to ::operator delete is invalid.当您在附加调试器的情况下运行单元测试时,Vista 及更高版本将使用调试堆,当它注意到传递给 ::operator delete 的指针无效时,它会调用一个断点。 Be sure to allow the linker to automatically find the correct Boost .lib to link to, don't force it yourself.确保让链接器自动找到要链接的正确 Boost .lib,不要自己强迫。

Object layout is more likely your problem, unfortunately much harder to diagnose.对象布局更可能是您的问题,不幸的是更难诊断。 You stay out of trouble by building your EXE and DLL with the exact same compiler settings.通过使用完全相同的编译器设置构建 EXE 和 DLL,您可以避免麻烦。 With the additional requirement that they must match the settings that were used to build the Boost libraries.附加要求它们必须与用于构建 Boost 库的设置相匹配。 Which certainly is the difficult part, that requires a time machine.这当然是困难的部分,这需要时间机器。 Particularly the _HAS_ITERATOR_DEBUGGING macro is a troublemaker, basic STL classes like std::vector will have a different layout that depends on the value of that macro.特别是 _HAS_ITERATOR_DEBUGGING 宏是一个麻烦制造者,像 std::vector 这样的基本 STL 类将具有不同的布局,这取决于该宏的值。

I realize this is very vague, but there isn't enough info in the question to truly diagnose this issue.我意识到这很模糊,但问题中没有足够的信息来真正诊断这个问题。 A very basic check you can do is putting the returned boost::unit_test::test_suite pointer in a watch expression.您可以做的一个非常基本的检查是将返回的 boost::unit_test::test_suite 指针放在 watch 表达式中。 If you suddenly see the members of that object change when you step into Boost code then you know you have an object layout problem.如果您在进入 Boost 代码时突然看到该对象的成员发生变化,那么您就知道存在对象布局问题。 What happens next is highly unpredictable, a stack overflow is certainly possible.接下来发生的事情是高度不可预测的,堆栈溢出肯定是可能的。 Yet another diagnostic is to use the Debug + Windows + Registers window.另一个诊断方法是使用 Debug + Windows + Registers 窗口。 Ensure that the ESP register value is stable when you step over functions.确保跨函数时 ESP 寄存器值稳定。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM