1

我现在正在使用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))链接起来,它将起作用。

(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)),我会崩溃,但崩溃不同于我在上面报道的那个: 在此处输入图像描述

(3) boost static lib with MDd (Failed)

如果 boost 构建为静态库(具有 的定义-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB),并且 boost 库和可执行程序都使用相同的动态运行时库 (MDd) 构建。将发生以下崩溃: 在此处输入图像描述

(4) boost static lib with MTd (Failed)

如果 boost 构建为静态库(具有 的定义-DBOOST_TEST_NO_MAIN -DBOOST_ALL_NO_LIB),并且 boost 库和可执行程序都使用相同的静态运行时库 (MTd) 构建。将发生以下崩溃: 在此处输入图像描述

4

1 回答 1

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

单元测试的重点是尽早发现代码中的问题。这行得通,只是你早就发现了问题。甚至让单元测试正常运行还为时过早。

DLL 中返回指向 C++ 对象的指针的函数通常是一个问题。只有当 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 及更高版本将使用调试堆,当它注意到传递给 ::operator delete 的指针无效时,它会调用断点。一定要让链接器自动找到正确的 Boost .lib 来链接,不要自己强求。

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

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

于 2013-05-28T10:16:57.217 回答