janwas
f3b3e0be6e
* debug_write_crashlog and debug_dump_stack are now responsible for detecting reentrancy (reported via new ERR_REENTERED error code). * export debug_error_message_build to allow unit test of stack dumper * split+clean up debug_display_error to allow this. * error_description_r now returns buf for convenience * ia32: fix typo causing disassembly to fail * wdbg_sym: bugfix causing incorrect debug_walk_stack return value. prevent recursive locking, provide locked version of debug_resolve_symbol. add skip_this_frame for convenience. refs #130 This was SVN commit r4067.
260 lines
8.2 KiB
C++
260 lines
8.2 KiB
C++
// 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_internal.h" // HWND
|
|
#include "lib/debug.h" // no wdbg_sym interface needed
|
|
#include "lib/sysdep/sysdep.h"
|
|
#include "lib/sysdep/win/win_internal.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,0,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()
|
|
{
|
|
m_test_addrs(123, 3.1415926535897932384626, "pchar string", 0xf00d);
|
|
}
|
|
};
|