// note: this is more of an on-demand display of the stack trace than // self-test of it. // TODO: compare against known-good result? // problem: results may differ by compiler (e.g. due to differing STL) #include "lib/self_test.h" #include "lib/sysdep/win/win.h" // HWND #include "lib/debug.h" // no wdbg_sym interface needed #include "lib/sysdep/sysdep.h" #include "lib/sysdep/win/win.h" #include <queue> #include <deque> #include <list> #include <map> #include <stack> class TestWdbgSym : public CxxTest::TestSuite { #pragma optimize("", off) static void m_test_array() { struct Small { int i1; int i2; }; struct Large { double d1; double d2; double d3; double d4; }; Large large_array_of_large_structs[8] = { { 0.0,0.0,0.0,0.0 } }; UNUSED2(large_array_of_large_structs); Large small_array_of_large_structs[2] = { { 0.0,0.0,0.0,0.0 } }; UNUSED2(small_array_of_large_structs); Small large_array_of_small_structs[8] = { { 1,2 } }; UNUSED2(large_array_of_small_structs); Small small_array_of_small_structs[2] = { { 1,2 } }; UNUSED2(small_array_of_small_structs); int ints[] = { 1,2,3,4,5 }; UNUSED2(ints); wchar_t chars[] = { 'w','c','h','a','r','s',0 }; UNUSED2(chars); // note: prefer simple error (which also generates stack trace) to // exception, because it is guaranteed to work (no issues with the // debugger swallowing exceptions). //DISPLAY_ERROR(L"wdbg_sym self test: check if stack trace below is ok."); //RaiseException(0xf001,0,0,0); // note: we don't want any kind of dialog to be raised, because // this test now always runs. therefore, just make sure a decent // amount of text (not just "(failed)" error messages) was produced. // // however, we can't call debug_dump_stack directly because // it'd be reentered if an actual error comes up. // therefore, use debug_display_error with DE_HIDE_DIALOG. // unfortunately this means we can no longer get at the error text. // a sanity check of the text length has been added to debug_display_error ErrorMessageMem emm = {0}; const wchar_t* text = debug_error_message_build(L"dummy", 0,0,0, 0,0, &emm); TS_ASSERT(wcslen(text) > 500); debug_error_message_free(&emm); } // also used by test_stl as an element type struct Nested { int nested_member; struct Nested* self_ptr; }; static void m_test_udt() { Nested nested = { 123 }; nested.self_ptr = &nested; typedef struct { u8 s1; u8 s2; char s3; } Small; Small small__ = { 0x55, 0xaa, -1 }; UNUSED2(small__); struct Large { u8 large_member_u8; std::string large_member_string; double large_member_double; } large = { 0xff, "large struct string", 123456.0 }; UNUSED2(large); class Base { int base_int; std::wstring base_wstring; public: Base() : base_int(123), base_wstring(L"base wstring") { } }; class Derived : private Base { double derived_double; public: Derived() : derived_double(-1.0) { } } derived; m_test_array(); } // STL containers and their contents static void m_test_stl() { std::vector<std::wstring> v_wstring; v_wstring.push_back(L"ws1"); v_wstring.push_back(L"ws2"); std::deque<int> d_int; d_int.push_back(1); d_int.push_back(2); d_int.push_back(3); std::deque<std::string> d_string; d_string.push_back("a"); d_string.push_back("b"); d_string.push_back("c"); std::list<float> l_float; l_float.push_back(0.1f); l_float.push_back(0.2f); l_float.push_back(0.3f); l_float.push_back(0.4f); std::map<std::string, int> m_string_int; m_string_int.insert(std::make_pair<std::string,int>("s5", 5)); m_string_int.insert(std::make_pair<std::string,int>("s6", 6)); m_string_int.insert(std::make_pair<std::string,int>("s7", 7)); std::map<int, std::string> m_int_string; m_int_string.insert(std::make_pair<int,std::string>(1, "s1")); m_int_string.insert(std::make_pair<int,std::string>(2, "s2")); m_int_string.insert(std::make_pair<int,std::string>(3, "s3")); std::map<int, int> m_int_int; m_int_int.insert(std::make_pair<int,int>(1, 1)); m_int_int.insert(std::make_pair<int,int>(2, 2)); m_int_int.insert(std::make_pair<int,int>(3, 3)); STL_HASH_MAP<std::string, int> hm_string_int; hm_string_int.insert(std::make_pair<std::string,int>("s5", 5)); hm_string_int.insert(std::make_pair<std::string,int>("s6", 6)); hm_string_int.insert(std::make_pair<std::string,int>("s7", 7)); STL_HASH_MAP<int, std::string> hm_int_string; hm_int_string.insert(std::make_pair<int,std::string>(1, "s1")); hm_int_string.insert(std::make_pair<int,std::string>(2, "s2")); hm_int_string.insert(std::make_pair<int,std::string>(3, "s3")); STL_HASH_MAP<int, int> hm_int_int; hm_int_int.insert(std::make_pair<int,int>(1, 1)); hm_int_int.insert(std::make_pair<int,int>(2, 2)); hm_int_int.insert(std::make_pair<int,int>(3, 3)); std::set<uintptr_t> s_uintptr; s_uintptr.insert(0x123); s_uintptr.insert(0x456); // empty std::deque<u8> d_u8_empty; std::list<Nested> l_nested_empty; std::map<double,double> m_double_empty; std::multimap<int,u8> mm_int_empty; std::set<uint> s_uint_empty; std::multiset<char> ms_char_empty; std::vector<double> v_double_empty; std::queue<double> q_double_empty; std::stack<double> st_double_empty; #if HAVE_STL_HASH STL_HASH_MAP<double,double> hm_double_empty; STL_HASH_MULTIMAP<double,std::wstring> hmm_double_empty; STL_HASH_SET<double> hs_double_empty; STL_HASH_MULTISET<double> hms_double_empty; #endif #if HAVE_STL_SLIST STL_SLIST<double> sl_double_empty; #endif std::string str_empty; std::wstring wstr_empty; m_test_udt(); // uninitialized std::deque<u8> d_u8_uninit; std::list<Nested> l_nested_uninit; std::map<double,double> m_double_uninit; std::multimap<int,u8> mm_int_uninit; std::set<uint> s_uint_uninit; std::multiset<char> ms_char_uninit; std::vector<double> v_double_uninit; std::queue<double> q_double_uninit; std::stack<double> st_double_uninit; #if HAVE_STL_HASH STL_HASH_MAP<double,double> hm_double_uninit; STL_HASH_MULTIMAP<double,std::wstring> hmm_double_uninit; STL_HASH_SET<double> hs_double_uninit; STL_HASH_MULTISET<double> hms_double_uninit; #endif #if HAVE_STL_SLIST STL_SLIST<double> sl_double_uninit; #endif std::string str_uninit; std::wstring wstr_uninit; } // also exercises all basic types because we need to display some values // anyway (to see at a glance whether symbol engine addrs are correct) static void m_test_addrs(int p_int, double p_double, char* p_pchar, uintptr_t p_uintptr) { debug_printf("\nTEST_ADDRS\n"); uint l_uint = 0x1234; bool l_bool = true; UNUSED2(l_bool); wchar_t l_wchars[] = L"wchar string"; enum TestEnum { VAL1=1, VAL2=2 } l_enum = VAL1; u8 l_u8s[] = { 1,2,3,4 }; void (*l_funcptr)(void) = m_test_stl; static double s_double = -2.718; static char s_chars[] = {'c','h','a','r','s',0}; static void (*s_funcptr)(int, double, char*, uintptr_t) = m_test_addrs; static void* s_ptr = (void*)(uintptr_t)0x87654321; static HDC s_hdc = (HDC)0xff0; debug_printf("p_int addr=%p val=%d\n", &p_int, p_int); debug_printf("p_double addr=%p val=%g\n", &p_double, p_double); debug_printf("p_pchar addr=%p val=%s\n", &p_pchar, p_pchar); debug_printf("p_uintptr addr=%p val=%lu\n", &p_uintptr, p_uintptr); debug_printf("l_uint addr=%p val=%u\n", &l_uint, l_uint); debug_printf("l_wchars addr=%p val=%ws\n", &l_wchars, l_wchars); debug_printf("l_enum addr=%p val=%d\n", &l_enum, l_enum); debug_printf("l_u8s addr=%p val=%d\n", &l_u8s, l_u8s); debug_printf("l_funcptr addr=%p val=%p\n", &l_funcptr, l_funcptr); m_test_stl(); int uninit_int; UNUSED2(uninit_int); float uninit_float; UNUSED2(uninit_float); double uninit_double; UNUSED2(uninit_double); bool uninit_bool; UNUSED2(uninit_bool); HWND uninit_hwnd; UNUSED2(uninit_hwnd); } #pragma optimize("", on) public: void test_stack_trace() { // TODO: restore this when it doesn't cause annoying assertion failures // m_test_addrs(123, 3.1415926535897932384626, "pchar string", 0xf00d); } };