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:
janwas 2005-06-27 00:25:10 +00:00
parent 9007d77030
commit 1367825d47
4 changed files with 197 additions and 53 deletions

View File

@ -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)

View File

@ -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();

View File

@ -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;
}

View File

@ -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);