From c4bf180bfc3bc39b9e4fa940caeec3b259f67f2d Mon Sep 17 00:00:00 2001 From: janwas Date: Sat, 25 Jun 2005 07:54:15 +0000 Subject: [PATCH] add support for displaying values in stl containers; also moved stl_simplify_name here. win.cpp: fix WinXP dependency This was SVN commit r2438. --- source/lib/debug.cpp | 122 +-------------- source/lib/debug.h | 9 ++ source/lib/debug_stl.cpp | 271 ++++++++++++++++++++++++++++++++++ source/lib/debug_stl.h | 7 + source/lib/sysdep/win/win.cpp | 28 +++- 5 files changed, 316 insertions(+), 121 deletions(-) create mode 100644 source/lib/debug_stl.cpp create mode 100644 source/lib/debug_stl.h diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index 38cb04b326..4ecedc02fb 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -22,6 +22,7 @@ #include "lib.h" #include "debug.h" +#include "debug_stl.h" #include "nommgr.h" // some functions here are called from within mmgr; disable its hooks // so that our allocations don't cause infinite recursion. @@ -138,125 +139,6 @@ static const size_t STRING_BUF_SIZE = 64*KiB; static char* string_buf; static char* string_buf_pos; - -// used in simplify_stl_name. -// TODO: check strcpy safety -#define REPLACE(what, with)\ - else if(!strncmp(src, (what), sizeof(what)-1))\ - {\ - src += sizeof(what)-1-1;/* see preincrement rationale*/\ - strcpy(dst, (with));\ - dst += sizeof(with)-1;\ - } -#define STRIP(what)\ - else if(!strncmp(src, (what), sizeof(what)-1))\ - {\ - src += sizeof(what)-1-1;/* see preincrement rationale*/\ - } - -// reduce complicated STL names to human-readable form (in place). -// e.g. "std::basic_string, std::allocator >" => -// "string". algorithm: strip undesired strings in one pass (fast). -// called from symbol_string_build. -// -// see http://www.bdsoft.com/tools/stlfilt.html and -// http://www.moderncppdesign.com/publications/better_template_error_messages.html -static void simplify_stl_name(char* name) -{ - // used when stripping everything inside a < > to continue until - // the final bracket is matched (at the original nesting level). - int nesting = 0; - - const char* src = name-1; // preincremented; see below. - char* dst = name; - - // for each character: (except those skipped as parts of strings) - for(;;) - { - int c = *(++src); - // preincrement rationale: src++ with no further changes would - // require all comparisons to subtract 1. incrementing at the - // end of a loop would require a goto, instead of continue - // (there are several paths through the loop, for speed). - // therefore, preincrement. when skipping strings, subtract - // 1 from the offset (since src is advanced directly after). - - // end of string reached - we're done. - if(c == '\0') - { - *dst = '\0'; - break; - } - - // we're stripping everything inside a < >; eat characters - // until final bracket is matched (at the original nesting level). - if(nesting) - { - if(c == '<') - nesting++; - else if(c == '>') - { - nesting--; - assert(nesting >= 0); - } - continue; - } - - // start if chain (REPLACE and STRIP use else if) - if(0) {} - else if(!strncmp(src, "::_Node", 7)) - { - // add a space if not already preceded by one - // (prevents replacing ">::_Node>" with ">>") - if(src != name && src[-1] != ' ') - *dst++ = ' '; - src += 7; - } - REPLACE("unsigned short", "u16") - REPLACE("unsigned int", "uint") - REPLACE("unsigned __int64", "u64") - STRIP(",0> ") - // early out: all tests after this start with s, so skip them - else if(c != 's') - { - *dst++ = c; - continue; - } - REPLACE("std::_List_nod", "list") - REPLACE("std::_Tree_nod", "map") - REPLACE("std::basic_string,") - STRIP("std::char_traits,") - STRIP("std::_Tmap_traits") - STRIP("std::_Tset_traits") - else if(!strncmp(src, "std::allocator<", 15)) - { - // remove preceding comma (if present) - if(src != name && src[-1] == ',') - dst--; - src += 15; - // strip everything until trailing > is matched - assert(nesting == 0); - nesting = 1; - } - else if(!strncmp(src, "std::less<", 10)) - { - // remove preceding comma (if present) - if(src != name && src[-1] == ',') - dst--; - src += 10; - // strip everything until trailing > is matched - assert(nesting == 0); - nesting = 1; - } - STRIP("std::") - else - *dst++ = c; - } -} - - static const char* symbol_string_build(void* symbol, const char* name, const char* file, int line) { // maximum bytes allowed per string (arbitrary). @@ -321,7 +203,7 @@ static const char* symbol_string_build(void* symbol, const char* name, const cha if(name) { snprintf(string+len, STRING_MAX-1-len, "%s", name); - simplify_stl_name(string+len); + stl_simplify_name(string+len); } return string; diff --git a/source/lib/debug.h b/source/lib/debug.h index 648bc42d8f..39bfaf5014 100644 --- a/source/lib/debug.h +++ b/source/lib/debug.h @@ -152,4 +152,13 @@ extern int debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* fil extern const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* context); + +//----------------------------------------------------------------------------- +// helper functions (used by implementation) +//----------------------------------------------------------------------------- + +typedef const u8* (*DebugIterator)(void* internal, size_t el_size); + +extern bool debug_is_bogus_pointer(const void* p); + #endif // #ifndef DEBUG_H_INCLUDED diff --git a/source/lib/debug_stl.cpp b/source/lib/debug_stl.cpp new file mode 100644 index 0000000000..f012399a7e --- /dev/null +++ b/source/lib/debug_stl.cpp @@ -0,0 +1,271 @@ +#include "precompiled.h" + +#include "debug_stl.h" + + + +// used in stl_simplify_name. +// TODO: check strcpy safety +#define REPLACE(what, with)\ + else if(!strncmp(src, (what), sizeof(what)-1))\ + {\ + src += sizeof(what)-1-1;/* see preincrement rationale*/\ + strcpy(dst, (with));\ + dst += sizeof(with)-1;\ + } +#define STRIP(what)\ + else if(!strncmp(src, (what), sizeof(what)-1))\ + {\ + src += sizeof(what)-1-1;/* see preincrement rationale*/\ + } + +// reduce complicated STL names to human-readable form (in place). +// e.g. "std::basic_string, std::allocator >" => +// "string". algorithm: strip undesired strings in one pass (fast). +// called from symbol_string_build. +// +// see http://www.bdsoft.com/tools/stlfilt.html and +// http://www.moderncppdesign.com/publications/better_template_error_messages.html +void stl_simplify_name(char* name) +{ + // used when stripping everything inside a < > to continue until + // the final bracket is matched (at the original nesting level). + int nesting = 0; + + const char* src = name-1; // preincremented; see below. + char* dst = name; + + // for each character: (except those skipped as parts of strings) + for(;;) + { + int c = *(++src); + // preincrement rationale: src++ with no further changes would + // require all comparisons to subtract 1. incrementing at the + // end of a loop would require a goto, instead of continue + // (there are several paths through the loop, for speed). + // therefore, preincrement. when skipping strings, subtract + // 1 from the offset (since src is advanced directly after). + + // end of string reached - we're done. + if(c == '\0') + { + *dst = '\0'; + break; + } + + // we're stripping everything inside a < >; eat characters + // until final bracket is matched (at the original nesting level). + if(nesting) + { + if(c == '<') + nesting++; + else if(c == '>') + { + nesting--; + assert(nesting >= 0); + } + continue; + } + + // start if chain (REPLACE and STRIP use else if) + if(0) {} + else if(!strncmp(src, "::_Node", 7)) + { + // add a space if not already preceded by one + // (prevents replacing ">::_Node>" with ">>") + if(src != name && src[-1] != ' ') + *dst++ = ' '; + src += 7; + } + REPLACE("unsigned short", "u16") + REPLACE("unsigned int", "uint") + REPLACE("unsigned __int64", "u64") + STRIP(",0> ") + // early out: all tests after this start with s, so skip them + else if(c != 's') + { + *dst++ = c; + continue; + } + REPLACE("std::_List_nod", "list") + REPLACE("std::_Tree_nod", "map") + REPLACE("std::basic_string,") + STRIP("std::char_traits,") + STRIP("std::_Tmap_traits") + STRIP("std::_Tset_traits") + else if(!strncmp(src, "std::allocator<", 15)) + { + // remove preceding comma (if present) + if(src != name && src[-1] == ',') + dst--; + src += 15; + // strip everything until trailing > is matched + assert(nesting == 0); + nesting = 1; + } + else if(!strncmp(src, "std::less<", 10)) + { + // remove preceding comma (if present) + if(src != name && src[-1] == ',') + dst--; + src += 10; + // strip everything until trailing > is matched + assert(nesting == 0); + nesting = 1; + } + STRIP("std::") + else + *dst++ = c; + } +} + +//----------------------------------------------------------------------------- + + +/* +// queue - same as deque +// stack - same as deque +*/ + +// if a container reports more elements than this, assume it's invalid. +// (only used for containers where we can't check anything else, e.g. map) +static const size_t MAX_CONTAINER_ELEMENTS = 0x1000000; + + + + +// we need a complete class for each container type and cannot +// templatize them, because the valid() function needs access to +// protected members of the containers. since we can't make it a friend +// without the cooperation of the system headers, they need to be +// in a derived class. + +// can't placement-new on top of the actual data because Any* will include +// vtbl ptr -> size is different and we trash the in-mem contents. +// actually we need to cast or placement-new (to validate the contents), +// so the validator class must not be virtual. + +// templates don't help; we need a separate class for each container +// (since there's a special implementation). templates don't simplify +// the dispatch (given name, call correct validator function) since type name is +// only known at runtime. + +template const u8* stl_iterator(void* it_mem, size_t el_size) +{ + UNUSED(el_size); + T::iterator* const it = (T::iterator*)it_mem; + T::reference el = *(*it); + const u8* p = (const u8*)⪙ + ++(*it); + return p; +} + +class Any_deque : public std::deque +{ +public: + bool valid(size_t el_size) const + { + const size_t el_per_bucket = 16 / el_size; // see _DEQUESIZ + // offset of initial element beyond end of first bucket + if(_Myoff >= el_per_bucket) + return false; + // more elements reported than fit in all buckets + if(_Mysize > _Mapsize * el_per_bucket) + return false; + return true; + } +}; + +class Any_list: public std::list +{ +public: + bool valid(size_t el_size) const + { + // way too many elements, must be garbage in _Mysize field + if(_Mysize > MAX_CONTAINER_ELEMENTS) + return false; + return !debug_is_bogus_pointer(_Myhead); + } +}; + +class Any_map : public std::map +{ +public: + bool valid(size_t el_size) const + { + // way too many elements, must be garbage in _Mysize field + if(_Mysize > MAX_CONTAINER_ELEMENTS) + return false; + return !debug_is_bogus_pointer(_Myhead); + } +}; + +class Any_set: public std::set +{ +public: + bool valid(size_t el_size) const + { + // way too many elements, must be garbage in _Mysize field + if(_Mysize > MAX_CONTAINER_ELEMENTS) + return false; + return !debug_is_bogus_pointer(_Myhead); + } +}; + +class Any_vector: public std::vector +{ +public: + bool valid(size_t el_size) const + { + // way too many elements, must be garbage in _Mylast + if(_Mylast - _Myfirst > MAX_CONTAINER_ELEMENTS) + return false; + // pointers are incorrectly ordered + if(_Myfirst > _Mylast || _Mylast > _Myend) + return false; + return !debug_is_bogus_pointer(_Myfirst); + } +}; + +// size of container in bytes +// not a ctor because we need to indicate if type_name was recognized + +template bool get_container_info(T* t, size_t size, size_t el_size, + size_t* el_count, DebugIterator* el_iterator, void* it_mem) +{ + assert(sizeof(T) == size); + assert(sizeof(T::iterator) < DEBUG_STL_MAX_ITERATOR_SIZE); + + *el_count = t->size(); + *el_iterator = stl_iterator; + *(T::iterator*)it_mem = t->begin(); + return t->valid(el_size); +} + + +int stl_get_container_info(wchar_t* type_name, const u8* p, size_t size, + size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem) +{ + bool valid; + +#define CONTAINER(name)\ + else if(!wcsncmp(type_name, L"std::" L###name, wcslen(L###name)+5))\ + valid = get_container_info((Any_##name*)p, size, el_size, el_count, el_iterator, it_mem); + + if(0) {} // kickoff + CONTAINER(deque) + CONTAINER(list) + CONTAINER(map) + CONTAINER(set) + CONTAINER(vector) + // unknown type, can't handle it + else + return 1; + + if(!valid) + return -1; + + return 0; +} diff --git a/source/lib/debug_stl.h b/source/lib/debug_stl.h new file mode 100644 index 0000000000..b4835a4b26 --- /dev/null +++ b/source/lib/debug_stl.h @@ -0,0 +1,7 @@ +extern void stl_simplify_name(char* name); + +// no STL iterator is larger than this. +const size_t DEBUG_STL_MAX_ITERATOR_SIZE = 64; + +extern int stl_get_container_info(wchar_t* type_name, const u8* p, size_t size, + size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem); diff --git a/source/lib/sysdep/win/win.cpp b/source/lib/sysdep/win/win.cpp index 3a5c307401..1288b98a4d 100755 --- a/source/lib/sysdep/win/win.cpp +++ b/source/lib/sysdep/win/win.cpp @@ -51,7 +51,6 @@ static BOOL CALLBACK is_this_our_window(HWND hWnd, LPARAM lParam) DWORD tid = GetWindowThreadProcessId(hWnd, &pid); UNUSED(tid); // the function can't fail -DWORD our_pid = GetCurrentProcessId(); if(pid == GetCurrentProcessId()) { *(HWND*)lParam = hWnd; @@ -621,6 +620,33 @@ static void at_exit(void) } +/* +static void test2(uint param2a, uint param2b, uint param2c, std::vector* param2) +{ +const uint const2 = 0x2020; +static const uint staticconst2 = 0x20202020; +static u8 static2 = 0x99; +uint local2 = 0x1234; +debug_printf("&static2 = %p\n", &static2); +debug_printf("param2 = %p\n&local2 = %p\n", param2, &local2); +debug_printf("¶m2a = %p\nparam2b = %p\nparam2c = %p\n", ¶m2a, ¶m2b, ¶m2c); +assert2(0 == 1); +} + +static void test1(uint param1a, uint param1b, uint param1c, std::vector* param1) +{ +const uint const1 = 0x1010; +static const uint staticconst1 = 0x10101010; +static u8 static1 = 0x88; +uint local1 = 0x5678; +debug_printf("&static1 = %p\n", &static1); +debug_printf("param1 = %p\n&local1 = %p\n", param1, &local1); +debug_printf("¶m1a = %p\nparam1b = %p\nparam1c = %p\n", ¶m1a, ¶m1b, ¶m1c); + +test2(param1a, param1b, param1c, param1); +} +*/ + #ifndef NO_MAIN_REDIRECT static #endif