add support for displaying values in stl containers; also moved stl_simplify_name here.
win.cpp: fix WinXP dependency This was SVN commit r2438.
This commit is contained in:
parent
8669593b33
commit
c4bf180bfc
@ -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<char, char_traits<char>, std::allocator<char> >" =>
|
||||
// "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<char,", "string<")
|
||||
REPLACE("std::basic_string<unsigned short,", "wstring<")
|
||||
STRIP("std::char_traits<char>,")
|
||||
STRIP("std::char_traits<unsigned short>,")
|
||||
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;
|
||||
|
@ -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
|
||||
|
271
source/lib/debug_stl.cpp
Normal file
271
source/lib/debug_stl.cpp
Normal file
@ -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<char, char_traits<char>, std::allocator<char> >" =>
|
||||
// "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<char,", "string<")
|
||||
REPLACE("std::basic_string<unsigned short,", "wstring<")
|
||||
STRIP("std::char_traits<char>,")
|
||||
STRIP("std::char_traits<unsigned short>,")
|
||||
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<class T> 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<int>
|
||||
{
|
||||
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<int>
|
||||
{
|
||||
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<int,int>
|
||||
{
|
||||
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<int>
|
||||
{
|
||||
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<int>
|
||||
{
|
||||
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<class T> 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>;
|
||||
*(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>((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;
|
||||
}
|
7
source/lib/debug_stl.h
Normal file
7
source/lib/debug_stl.h
Normal file
@ -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);
|
@ -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<uint>* 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<uint>* 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
|
||||
|
Loading…
Reference in New Issue
Block a user