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) 

ブースト単体テストライブラリ(定義付き-DBOOST_ALL_DYN_LINK -DBOOST_TEST_NO_MAIN -DBOOST_TEST_DYN_LINK -DBOOST_ALL_NO_LIB)と実行中の実行可能プログラムを同じダイナミックランタイムライブラリ(マルチスレッドデバッグDll(MDd))にリンクすると、機能します。

(2) boost dll with MTd (Failed)

ブーストユニットテストライブラリ(定義付き-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_LIBBoostライブラリと実行可能プログラムの両方が同じ動的ランタイムライブラリ(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();

単体テストのポイントは、コードの問題を早期に発見することです。それはうまくいきました、それはあなたが問題を非常に早く見つけたということだけです。ユニットテストを正しく実行するには早すぎます。

C++オブジェクトへのポインタを返すDLLの関数は一般的に問題です。これは、C ++オブジェクトのレイアウトが、DLLとEXEの両方をコンパイルしたときにコンパイラーが行った仮定と完全に一致する場合にのみ適切に終了します。また、DLLがオブジェクトを作成し、EXEがオブジェクトを削除する必要があるため、オブジェクトは両方のモジュールがアクセスできるヒープ上に存在する必要があります。

オブジェクトを適切に削除するには、DLLとEXEの両方が同じCRTバージョンを共有している必要があります。/ MTを使用してプログラムをビルドし、静的バージョンのCRTを要求すると、問題が発生します。関連するコンパイラ設定は、C / C ++、コード生成、ランタイムライブラリ設定です。デバッグ構成では/MDdを使用する必要があり、リリース構成では/MDを使用する必要があります。EXEプロジェクトとDLLプロジェクトの両方、およびコンパイル時のBoostライブラリ。/MTdおよび/MTの場合、DLLにはCRTの独自のコピーがリンクされており、独自のヒープを使用して割り当てられます。のオブジェクトを使用しているため、EXEはオブジェクトを適切に削除できませんヒープ。とにかくそうすることは未定義の振る舞いを生み出すでしょう。XPよりも新しいバージョンのWindowsでプログラムを実行した場合にのみ、幸運になる傾向があります。Vista以降では、デバッガーを接続して単体テストを実行するとデバッグヒープが使用され、::operatordeleteに渡されたポインターが無効であることに気付いたときにブレークポイントが呼び出されます。リンク先の正しい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 に答える