forked from 0ad/0ad
config: detect STL version
debug_stl: commented, improved validation functions and #ifdef-ed STL-version-specific code wdbg: prevent vectored exception handler from preempting __try (by This was SVN commit r2443.
This commit is contained in:
parent
9007d77030
commit
1367825d47
@ -37,6 +37,13 @@
|
||||
# define GCC_VERSION 0
|
||||
#endif
|
||||
|
||||
// STL
|
||||
#if defined(_CPPLIB_VER)
|
||||
# define STL_DINKUMWARE _CPPLIB_VER
|
||||
#else
|
||||
# define STL_DINKUMWARE 0
|
||||
#endif
|
||||
|
||||
// OS
|
||||
// .. Windows
|
||||
#if defined(_WIN32) || defined(WIN32)
|
||||
|
@ -376,7 +376,7 @@ ErrorReaction display_error(const wchar_t* description, int flags,
|
||||
// window handle and is modal.
|
||||
|
||||
// handle "break" request unless the caller wants to (doing so here
|
||||
// instead of within the dlgproc helps makes debugging easier)
|
||||
// instead of within the dlgproc yields a correct call stack)
|
||||
if(er == ER_BREAK && !(flags & DE_MANUAL_BREAK))
|
||||
{
|
||||
debug_break();
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "debug_stl.h"
|
||||
|
||||
|
||||
// portable debugging helper functions specific to the STL.
|
||||
|
||||
// used in stl_simplify_name.
|
||||
// TODO: check strcpy safety
|
||||
@ -121,37 +121,25 @@ void stl_simplify_name(char* name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// STL container debugging
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*
|
||||
// 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;
|
||||
// provide an iterator interface for arbitrary STL containers; this is
|
||||
// used to display their contents in stack traces. their type and
|
||||
// contents aren't known until runtime, so this is somewhat tricky.
|
||||
//
|
||||
// we assume STL containers aren't specialized on their content
|
||||
// type and use container<int>'s memory layout. vector<bool> will therefore
|
||||
// not be displayed correctly, but it is frowned upon anyway (since
|
||||
// address of its elements can't be taken).
|
||||
// to be 100% correct, we'd have to write an Any_container_type__element_type
|
||||
// class for each combination, but that is clearly infeasible.
|
||||
|
||||
|
||||
|
||||
|
||||
// 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.
|
||||
|
||||
// generic iterator - returns next element. dereferences and increments the
|
||||
// specific container iterator in it_mem.
|
||||
template<class T> const u8* stl_iterator(void* it_mem, size_t el_size)
|
||||
{
|
||||
UNUSED(el_size);
|
||||
@ -162,18 +150,68 @@ template<class T> const u8* stl_iterator(void* it_mem, size_t el_size)
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// validator classes for all STL containers
|
||||
//
|
||||
// containers might still be uninitialized when we call get_container_info on
|
||||
// them. we need to check if they are valid and only then use their contents.
|
||||
// to that end, we derive a validator class from each container,
|
||||
// cast the container's address to it, and call its valid() method.
|
||||
//
|
||||
// checks performed include: is size() realistic; does begin() come before
|
||||
// end(), etc. we need to leverage all invariants because the values are
|
||||
// random in release mode.
|
||||
//
|
||||
// rationale:
|
||||
// - we need a complete class for each container type because the
|
||||
// valid() function sometimes needs access to protected members of
|
||||
// the containers. since we can't grant access via friend without the
|
||||
// cooperation of the system headers, it needs to be in a derived class.
|
||||
// - since we cast our validator on top of the actual container,
|
||||
// it must not contain virtual functions (the vptr would shift addresses;
|
||||
// we can't really correct for this because it's totally non-portable).
|
||||
// - we don't bother with making this a template because there are enough
|
||||
// variations that we'd have to specialize everything anyway.
|
||||
|
||||
// basic sanity checks shared by all containers.
|
||||
static bool container_valid(const void* front, size_t el_count)
|
||||
{
|
||||
// # elements is unbelievably high; assume it's invalid.
|
||||
if(el_count > 0x1000000)
|
||||
return false;
|
||||
if(debug_is_bogus_pointer(front))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// shared by deque, queue and stack. this is specific to
|
||||
// the Dinkumware implementation but portable.
|
||||
static bool deque_valid(size_t el_count, size_t el_size,
|
||||
size_t num_skipped, size_t num_buckets)
|
||||
{
|
||||
const size_t el_per_bucket = 16 / el_size; // see _DEQUESIZ
|
||||
// initial element is beyond end of first bucket
|
||||
if(num_skipped >= el_per_bucket)
|
||||
return false;
|
||||
// more elements reported than fit in all buckets
|
||||
if(el_count > num_buckets * el_per_bucket)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
if(!container_valid(&front(), size()))
|
||||
return false;
|
||||
// more elements reported than fit in all buckets
|
||||
if(_Mysize > _Mapsize * el_per_bucket)
|
||||
#if STL_DINKUMWARE != 0
|
||||
if(!deque_valid(_Mysize, el_size, _Myoff, _Mapsize))
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -183,10 +221,10 @@ 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)
|
||||
UNUSED(el_size);
|
||||
if(!container_valid(&front(), size()))
|
||||
return false;
|
||||
return !debug_is_bogus_pointer(_Myhead);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -195,10 +233,27 @@ 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)
|
||||
UNUSED(el_size);
|
||||
const_iterator it = begin();
|
||||
if(!container_valid(&*it, size()))
|
||||
return false;
|
||||
return !debug_is_bogus_pointer(_Myhead);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// we assume this adapter was instantiated with container=deque!
|
||||
class Any_queue : public std::deque<int>
|
||||
{
|
||||
public:
|
||||
bool valid(size_t el_size) const
|
||||
{
|
||||
if(!container_valid(&front(), size()))
|
||||
return false;
|
||||
#if STL_DINKUMWARE != 0
|
||||
if(!deque_valid(_Mysize, el_size, _Myoff, _Mapsize))
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -207,10 +262,27 @@ 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)
|
||||
UNUSED(el_size);
|
||||
const_iterator it = begin();
|
||||
if(!container_valid(&*it, size()))
|
||||
return false;
|
||||
return !debug_is_bogus_pointer(_Myhead);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// we assume this adapter was instantiated with container=deque!
|
||||
class Any_stack : public std::deque<int>
|
||||
{
|
||||
public:
|
||||
bool valid(size_t el_size) const
|
||||
{
|
||||
if(!container_valid(&front(), size()))
|
||||
return false;
|
||||
#if STL_DINKUMWARE != 0
|
||||
if(!deque_valid(_Mysize, el_size, _Myoff, _Mapsize))
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -219,19 +291,65 @@ 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)
|
||||
UNUSED(el_size);
|
||||
if(!container_valid(&front(), size()))
|
||||
return false;
|
||||
// pointers are incorrectly ordered
|
||||
if(_Myfirst > _Mylast || _Mylast > _Myend)
|
||||
// more elements reported than reserved
|
||||
if(size() > capacity())
|
||||
return false;
|
||||
return !debug_is_bogus_pointer(_Myfirst);
|
||||
// front/back pointers incorrect
|
||||
if(&front() > &back())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// size of container in bytes
|
||||
// not a ctor because we need to indicate if type_name was recognized
|
||||
class Any_string : public std::string
|
||||
{
|
||||
public:
|
||||
bool valid(size_t el_size) const
|
||||
{
|
||||
assert(el_size == sizeof(char));
|
||||
if(!container_valid(c_str(), size()))
|
||||
return false;
|
||||
#if STL_DINKUMWARE != 0
|
||||
// less than the small buffer reserved - impossible
|
||||
if(_Myres < 15)
|
||||
return false;
|
||||
// more elements reported than reserved
|
||||
if(_Mysize > _Myres)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class Any_wstring : public std::wstring
|
||||
{
|
||||
public:
|
||||
bool valid(size_t el_size) const
|
||||
{
|
||||
assert(el_size == sizeof(wchar_t));
|
||||
if(!container_valid(c_str(), size()))
|
||||
return false;
|
||||
#if STL_DINKUMWARE != 0
|
||||
// less than the small buffer reserved - impossible
|
||||
if(_Myres < 15)
|
||||
return false;
|
||||
// more elements reported than reserved
|
||||
if(_Mysize > _Myres)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// check if the container is valid and return # elements and an iterator;
|
||||
// this is instantiated once for each type of container.
|
||||
// we don't do this in the Any_* ctors because we need to return bool valid and
|
||||
// don't want to throw an exception (may confuse the debug code).
|
||||
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)
|
||||
{
|
||||
@ -245,7 +363,13 @@ template<class T> bool get_container_info(T* t, size_t size, size_t el_size,
|
||||
}
|
||||
|
||||
|
||||
int stl_get_container_info(wchar_t* type_name, const u8* p, size_t size,
|
||||
// if <type_name> indicates the object <p, size> to be an STL container,
|
||||
// and given the size of its value_type (retrieved via debug information),
|
||||
// return number of elements and an iterator (any data it needs is stored in
|
||||
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
||||
// returns 0 on success, 1 if type_name is unknown, or -1 if the contents
|
||||
// are invalid (most likely due to being uninitialized).
|
||||
int stl_get_container_info(const 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;
|
||||
@ -258,8 +382,14 @@ int stl_get_container_info(wchar_t* type_name, const u8* p, size_t size,
|
||||
CONTAINER(deque)
|
||||
CONTAINER(list)
|
||||
CONTAINER(map)
|
||||
CONTAINER(queue)
|
||||
CONTAINER(set)
|
||||
CONTAINER(stack)
|
||||
CONTAINER(vector)
|
||||
else if(!wcsncmp(type_name, L"std::basic_string<char", 22))
|
||||
valid = get_container_info<Any_string>((Any_string*)p, size, el_size, el_count, el_iterator, it_mem);
|
||||
else if(!wcsncmp(type_name, L"std::basic_string<unsigned short", 32))
|
||||
valid = get_container_info<Any_wstring>((Any_wstring*)p, size, el_size, el_count, el_iterator, it_mem);
|
||||
// unknown type, can't handle it
|
||||
else
|
||||
return 1;
|
||||
@ -269,3 +399,4 @@ int stl_get_container_info(wchar_t* type_name, const u8* p, size_t size,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,13 @@
|
||||
extern void stl_simplify_name(char* name);
|
||||
|
||||
// no STL iterator is larger than this.
|
||||
// no STL iterator is larger than this; see below.
|
||||
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,
|
||||
// if <type_name> indicates the object <p, size> to be an STL container,
|
||||
// and given the size of its value_type (retrieved via debug information),
|
||||
// return number of elements and an iterator (any data it needs is stored in
|
||||
// it_mem, which must hold DEBUG_STL_MAX_ITERATOR_SIZE bytes).
|
||||
// returns 0 on success, 1 if type_name is unknown, or -1 if the contents
|
||||
// are invalid (most likely due to being uninitialized).
|
||||
extern int stl_get_container_info(const wchar_t* type_name, const u8* p, size_t size,
|
||||
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem);
|
||||
|
Loading…
Reference in New Issue
Block a user