more of the asserts.. also:

- increase stack trace buffer size (needed for some complicated nested
expressions)
- fix bug in container output - no longer treat iterator (i.e.
x::iterator) as an x string (now uses match_wildcard instead of simple
string compare)
- add unicode match_wildcard
- bugfix for vectored exception handler - was always ignoring exceptions
from main thread (fixed the previous fix where it was always grabbing
all exceptions ;p)
- better array display (display less of huge arrays)
- fix nesting limit (which was too low); make sure infinite
nesting/indirection can't happen

This was SVN commit r2448.
This commit is contained in:
janwas 2005-06-28 04:12:50 +00:00
parent ec6b78b252
commit a1e149ab11
9 changed files with 166 additions and 72 deletions

View File

@ -37,7 +37,7 @@ wchar_t* debug_log_pos = debug_log;
void debug_wprintf_mem(const wchar_t* fmt, ...)
{
const ssize_t chars_left = (ssize_t)LOG_CHARS - (debug_log_pos-debug_log);
assert2(chars_left >= 0);
debug_assert(chars_left >= 0);
// potentially not enough room for the new string; throw away the
// older half of the log. we still protect against overflow below.
@ -352,7 +352,7 @@ ErrorReaction display_error(const wchar_t* description, int flags,
debug_wprintf(L"%hs(%d): %s\n", filename, line, description);
wchar_t* text;
const size_t MAX_CHARS = 64*1024;
const size_t MAX_CHARS = 512*1024;
void* mem = malloc(MAX_CHARS*sizeof(wchar_t));
if(mem)
{

View File

@ -31,7 +31,7 @@
//-----------------------------------------------------------------------------
// check heap integrity (independently of mmgr).
// errors are reported by the CRT, e.g. via assert.
// errors are reported by the CRT, e.g. via debug_assert.
extern void debug_heap_check(void);
enum DebugHeapChecks
@ -51,7 +51,7 @@ extern void debug_heap_enable(DebugHeapChecks what);
//-----------------------------------------------------------------------------
// assert
// debug_assert
//-----------------------------------------------------------------------------
// notify the user that an assertion failed; displays a
@ -59,8 +59,9 @@ extern void debug_heap_enable(DebugHeapChecks what);
// returns one of UserErrorReaction.
extern enum ErrorReaction debug_assert_failed(const char* source_file, int line, const char* assert_expr);
// recommended use: assert2(expr && "descriptive string")
#define assert2(expr)\
// recommended use: debug_assert(expr && "descriptive string")
#undef debug_assert
#define debug_assert(expr)\
STMT(\
static unsigned char suppress__ = 0x55;\
if(suppress__ == 0x55 && !(expr))\
@ -88,8 +89,8 @@ extern void debug_wprintf(const wchar_t* fmt, ...);
// used for "last activity" reporting in the crashlog.
extern void debug_wprintf_mem(const wchar_t* fmt, ...);
// warn of unexpected state. less error-prone than assert(!"text");
#define debug_warn(str) assert2(0 && (str))
// warn of unexpected state. less error-prone than debug_assert(!"text");
#define debug_warn(str) debug_assert(0 && (str))
// TODO
extern int debug_write_crashlog(const wchar_t* text);

View File

@ -1,6 +1,7 @@
#include "precompiled.h"
#include "debug_stl.h"
#include "lib.h" // match_wildcardw
// portable debugging helper functions specific to the STL.
@ -62,7 +63,7 @@ void stl_simplify_name(char* name)
else if(c == '>')
{
nesting--;
assert(nesting >= 0);
debug_assert(nesting >= 0);
}
continue;
}
@ -102,7 +103,7 @@ void stl_simplify_name(char* name)
dst--;
src += 15;
// strip everything until trailing > is matched
assert(nesting == 0);
debug_assert(nesting == 0);
nesting = 1;
}
else if(!strncmp(src, "std::less<", 10))
@ -112,7 +113,7 @@ void stl_simplify_name(char* name)
dst--;
src += 10;
// strip everything until trailing > is matched
assert(nesting == 0);
debug_assert(nesting == 0);
nesting = 1;
}
STRIP("std::")
@ -309,7 +310,7 @@ class Any_string : public std::string
public:
bool valid(size_t el_size) const
{
assert(el_size == sizeof(char));
debug_assert(el_size == sizeof(char));
if(!container_valid(c_str(), size()))
return false;
#if STL_DINKUMWARE != 0
@ -329,7 +330,7 @@ class Any_wstring : public std::wstring
public:
bool valid(size_t el_size) const
{
assert(el_size == sizeof(wchar_t));
debug_assert(el_size == sizeof(wchar_t));
if(!container_valid(c_str(), size()))
return false;
#if STL_DINKUMWARE != 0
@ -353,8 +354,9 @@ public:
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);
if(sizeof(T) != size)
debug_assert(sizeof(T) == size);
debug_assert(sizeof(T::iterator) < DEBUG_STL_MAX_ITERATOR_SIZE);
*el_count = t->size();
*el_iterator = stl_iterator<T>;
@ -375,7 +377,7 @@ int stl_get_container_info(const wchar_t* type_name, const u8* p, size_t size,
bool valid;
#define CONTAINER(name)\
else if(!wcsncmp(type_name, L"std::" L###name, wcslen(L###name)+5))\
else if(match_wildcardw(type_name, L"std::" L###name L"<*>"))\
valid = get_container_info<Any_##name>((Any_##name*)p, size, el_size, el_count, el_iterator, it_mem);
if(0) {} // kickoff
@ -386,9 +388,9 @@ int stl_get_container_info(const wchar_t* type_name, const u8* p, size_t size,
CONTAINER(set)
CONTAINER(stack)
CONTAINER(vector)
else if(!wcsncmp(type_name, L"std::basic_string<char", 22))
else if(match_wildcardw(type_name, L"std::basic_string<char*>"))
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))
else if(match_wildcardw(type_name, L"std::basic_string<unsigned short*>"))
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

View File

@ -16,7 +16,7 @@
#include "precompiled.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -202,9 +202,9 @@ int ilog2(const float x)
// multiple must be a power of two.
uintptr_t round_up(const uintptr_t n, const uintptr_t multiple)
{
assert(is_pow2((long)multiple)); // also catches divide-by-zero
debug_assert(is_pow2((long)multiple)); // also catches divide-by-zero
const uintptr_t result = (n + multiple-1) & ~(multiple-1);
assert(n <= result && result < n+multiple);
debug_assert(n <= result && result < n+multiple);
return result;
}
@ -277,7 +277,7 @@ u8 fp_to_u8(double in)
}
int l = (int)(in * 255.0);
assert((unsigned int)l <= 255u);
debug_assert((unsigned int)l <= 255u);
return (u8)l;
}
@ -292,7 +292,7 @@ u16 fp_to_u16(double in)
}
long l = (long)(in * 65535.0);
assert((unsigned long)l <= 65535u);
debug_assert((unsigned long)l <= 65535u);
return (u16)l;
}
@ -306,7 +306,7 @@ int rand_up_to(int limit)
{
// (i64 avoids overflowing in multiply)
const i64 ret = ((i64)limit * rand()) / (RAND_MAX+1);
assert2(0 <= ret && ret < limit);
debug_assert(0 <= ret && ret < limit);
return (int)ret;
}
@ -391,3 +391,52 @@ int match_wildcard(const char* s, const char* w)
return (*w == '\0');
}
int match_wildcardw(const wchar_t* s, const wchar_t* w)
{
if(!w)
return 1;
// saved position in both strings, used to expand '*':
// s2 is advanced until match.
// initially 0 - we abort on mismatch before the first '*'.
const wchar_t* s2 = 0;
const wchar_t* w2 = 0;
while(*s)
{
const wchar_t wc = *w;
if(wc == '*')
{
// wildcard string ended with * => match.
if(*++w == '\0')
return 1;
w2 = w;
s2 = s+1;
}
// match one character
else if(towupper(wc) == towupper(*s) || wc == '?')
{
w++;
s++;
}
// mismatched character
else
{
// no '*' found yet => mismatch.
if(!s2)
return 0;
// resume at previous position+1
w = w2;
s = s2++;
}
}
// strip trailing * in wildcard string
while(*w == '*')
w++;
return (*w == '\0');
}

View File

@ -53,7 +53,7 @@ scope
#define LIB_H__
#include <stddef.h>
#include <assert.h>
#include "config.h"
#include "lib/types.h"
@ -107,7 +107,7 @@ STMT(\
int err__ = (int)((func) & UINT_MAX);\
if(err__ < 0)\
{\
assert2(0 && "FYI: CHECK_ERR reports that a function failed."\
debug_assert(0 && "FYI: CHECK_ERR reports that a function failed."\
"feel free to ignore or suppress this warning.");\
return err__;\
}\
@ -139,7 +139,7 @@ STMT(\
int err__ = (int)((func) & UINT_MAX);\
if(err__ < 0)\
{\
assert(0 && "FYI: CHECK_ERR reports that a function failed."\
debug_assert(0 && "FYI: CHECK_ERR reports that a function failed."\
"feel free to ignore or suppress this warning.");\
throw err__;\
}\
@ -231,7 +231,7 @@ enum LibError
//
// compile-time assert, especially useful for testing sizeof().
// compile-time debug_assert, especially useful for testing sizeof().
// no runtime overhead; may be used anywhere, including file scope.
//
@ -332,6 +332,6 @@ extern void base32(const int len, const u8* in, u8* out);
// which may contain '?' or '*' wildcards. if so, return 1, otherwise 0.
// note: NULL wildcard pattern matches everything!
extern int match_wildcard(const char* s, const char* w);
extern int match_wildcardw(const wchar_t* s, const wchar_t* w);
#endif // #ifndef LIB_H__

View File

@ -37,6 +37,10 @@
WIN_REGISTER_FUNC(wdbg_init);
#pragma data_seg()
// used to prevent the vectored exception handler from taking charge when
// an exception is raised from the main thread (allows __try blocks to
// get control). latched in wdbg_init.
static DWORD main_thread_id;
// protects the breakpoint helper thread.
@ -171,7 +175,7 @@ void debug_heap_enable(DebugHeapChecks what)
_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF;
break;
default:
assert2("debug_heap_enable: invalid what");
debug_assert("debug_heap_enable: invalid what");
}
_CrtSetDbgFlag(flags);
#endif // HAVE_DEBUGALLOC
@ -212,7 +216,7 @@ static void* while_suspended_thread_func(void* user_arg)
int ret = param->func(param->hThread, param->user_arg);
err = ResumeThread(param->hThread);
assert(err != 0);
debug_assert(err != 0);
return (void*)(intptr_t)ret;
@ -241,11 +245,11 @@ static int call_while_suspended(WhileSuspendedFunc func, void* user_arg)
pthread_t thread;
err = pthread_create(&thread, 0, while_suspended_thread_func, &param);
assert2(err == 0);
debug_assert(err == 0);
void* ret;
err = pthread_join(thread, &ret);
assert2(err == 0 && ret == 0);
debug_assert(err == 0 && ret == 0);
return (int)(intptr_t)ret;
}
@ -921,10 +925,10 @@ static const wchar_t* get_exception_locus(const EXCEPTION_POINTERS* ep)
// called* when an SEH exception was not caught by the app;
// provides detailed debugging information and exits.
// (via win.cpp!entry's __except or as a vectored handler; see below)
// (via win.cpp!entry's __except or vectored_exception_handler; see below)
//
// note: keep memory allocs and lock usage to an absolute minimum, because we may
// deadlock the process
// note: keep memory allocs and locking to an absolute minimum, because
// they may deadlock the process!
//
// rationale:
// we want to replace the OS "program error" dialog box because
@ -1006,7 +1010,7 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
if(ep->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
flags = DE_NO_CONTINUE;
ErrorReaction er = display_error(buf, flags, 1, ep->ContextRecord, file, line);
assert(er > 0);
debug_assert(er > 0);
wdbg_write_minidump(ep);
@ -1016,8 +1020,23 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
}
static LONG WINAPI vectored_exception_handler(EXCEPTION_POINTERS* ep)
{
// since we're called from the vectored handler chain,
// ignore exceptions from the main thread. this allows
// __try blocks to take charge; entry() catches all exceptions with a
// standard filter and relays them to wdbg_exception_filter.
if(main_thread_id == GetCurrentThreadId())
return EXCEPTION_CONTINUE_SEARCH;
return wdbg_exception_filter(ep);
}
static int wdbg_init(void)
{
// see decl
main_thread_id = GetCurrentThreadId();
// add vectored exception handler (if supported by the OS).
// see rationale above.
#if _WIN32_WINNT >= 0x0500 // this is how winbase.h tests for it
@ -1029,7 +1048,7 @@ static int wdbg_init(void)
// doesn't complain. it won't actually be unloaded anyway -
// there is at least one other reference.
if(pAddVectoredExceptionHandler)
pAddVectoredExceptionHandler(TRUE, wdbg_exception_filter);
pAddVectoredExceptionHandler(TRUE, vectored_exception_handler);
#endif
return 0;

View File

@ -1,4 +1,4 @@
// stack trace, improved assert and exception handler for Win32
// stack trace, improved debug_assert and exception handler for Win32
// Copyright (c) 2002-2005 Jan Wassenberg
//
// This program is free software; you can redistribute it and/or

View File

@ -1,4 +1,4 @@
// stack trace, improved assert and exception handler for Win32
// stack trace, improved debug_assert and exception handler for Win32
// Copyright (c) 2002-2005 Jan Wassenberg
//
// This program is free software; you can redistribute it and/or
@ -48,11 +48,9 @@ WIN_REGISTER_FUNC(wdbg_sym_shutdown);
#pragma data_seg()
// debug_warn usually uses assert2, but we don't want to call that from
// inside an assert2 (from inside another assert2 (from inside another assert2
// (... etc))), so just use the normal assert
//#undef debug_warn
//#define debug_warn(str) assert(0 && (str))
// note: it is safe to use debug_assert/debug_warn/CHECK_ERR even during a
// stack trace (which is triggered by debug_assert et al. in app code) because
// nested stack traces are ignored and only the error is displayed.
// protects dbghelp (which isn't thread-safe) and
@ -84,6 +82,10 @@ enum WdbgError
// an essential call to SymGetTypeInfo or SymFromIndex failed.
WDBG_TYPE_INFO_UNAVAILABLE = -100102,
// a generous limit on nesting depth has been reached.
// we abort to make sure we don't recurse infinitely.
WDBG_NESTING_LIMIT = -100103,
// exception raised while processing the symbol.
WDBG_INTERNAL_ERROR = -100200,
@ -130,7 +132,7 @@ static int sym_init()
SymSetOptions(SYMOPT_DEFERRED_LOADS/*/*|SYMOPT_DEBUG*/);
// loads symbols for all active modules.
BOOL ok = SymInitialize(hProcess, 0, TRUE);
assert2(ok);
debug_assert(ok);
mod_base = SymGetModuleBase64(hProcess, (u64)&sym_init);
IMAGE_NT_HEADERS* header = ImageNtHeader((void*)mod_base);
@ -222,7 +224,7 @@ int debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int*
// rationale: to function properly, StackWalk64 requires a CONTEXT on
// non-x86 systems (documented) or when in release mode (observed).
// exception handlers can call walk_stack with their context record;
// otherwise (e.g. dump_stack from assert2), we need to query it.
// otherwise (e.g. dump_stack from debug_assert), we need to query it.
// there are 2 platform-independent ways to do so:
// - intentionally raise an SEH exception, then proceed as above;
// - GetThreadContext while suspended (*).
@ -442,9 +444,10 @@ void* debug_get_nth_caller(uint n)
//
//////////////////////////////////////////////////////////////////////////////
// overflow is impossible in practice. keep in sync with DumpState.
static const uint MAX_INDIRECTION = 256;
static const uint MAX_LEVEL = 256;
// overflow is impossible in practice, but check for robustness.
// keep in sync with DumpState.
static const uint MAX_INDIRECTION = 255;
static const uint MAX_LEVEL = 255;
struct DumpState
{
@ -693,10 +696,25 @@ static int dump_sequence(DebugIterator el_iterator, void* internal,
return ret;
out(L"[%d] ", el_count);
const size_t num_elements_to_show = MIN(20, el_count);
const bool fits_on_one_line =
(el_size == sizeof(char) && el_count <= 16) ||
(el_size <= sizeof(int ) && el_count <= 8);
// choose formatting based on element size
bool fits_on_one_line;
size_t num_elements_to_show;
if(el_size == sizeof(char))
{
fits_on_one_line = el_count <= 16;
num_elements_to_show = MIN(16, el_count);
}
else if(el_size <= sizeof(int))
{
fits_on_one_line = el_count <= 8;
num_elements_to_show = MIN(12, el_count);
}
else
{
fits_on_one_line = false;
num_elements_to_show = MIN(8, el_count);
}
state.level++;
out(fits_on_one_line? L"{ " : L"\r\n");
@ -872,9 +890,9 @@ static int dump_sym_array(DWORD type_id, const u8* p, DumpState state)
if(!SymGetTypeInfo(hProcess, mod_base, el_type_id, TI_GET_LENGTH, &el_size_))
return WDBG_TYPE_INFO_UNAVAILABLE;
const size_t el_size = (size_t)el_size_;
assert(el_size != 0);
debug_assert(el_size != 0);
const size_t num_elements = size/el_size;
assert2(num_elements != 0);
debug_assert(num_elements != 0);
return dump_array(p, num_elements, el_type_id, el_size, state);
}
@ -912,7 +930,7 @@ static int dump_sym_base_type(DWORD type_id, const u8* p, DumpState state)
{
// boolean
case btBool:
assert(size == sizeof(bool));
debug_assert(size == sizeof(bool));
fmt = L"%hs";
data = (u64)(data? "true " : "false");
break;
@ -968,7 +986,7 @@ display_as_hex:
// character
case btChar:
case btWChar:
assert(size == sizeof(char) || size == sizeof(wchar_t));
debug_assert(size == sizeof(char) || size == sizeof(wchar_t));
// char*, wchar_t*
if(state.indirection)
{
@ -1006,7 +1024,7 @@ display_as_hex:
case btBit:
case btBSTR:
case btHresult:
return -1;
return WDBG_UNSUPPORTED;
}
out(fmt, data);
@ -1221,6 +1239,10 @@ static int dump_sym_pointer(DWORD type_id, const u8* p, DumpState state)
out(L" -> ");
if(!SymGetTypeInfo(hProcess, mod_base, type_id, TI_GET_TYPEID, &type_id))
return WDBG_TYPE_INFO_UNAVAILABLE;
// prevent infinite recursion just to be safe (shouldn't happen)
if(state.indirection >= MAX_INDIRECTION)
return WDBG_NESTING_LIMIT;
state.indirection++;
return dump_sym(type_id, p, state);
}
@ -1431,9 +1453,10 @@ static int udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size,
{
const bool fits_on_one_line = udt_fits_on_one_line(type_name, num_children, size);
// prevent infinite recursion just to be safe (shouldn't happen)
if(state.level >= MAX_LEVEL)
return WDBG_NESTING_LIMIT;
state.level++;
if(state.level > 20)
return -1;
out(fits_on_one_line? L"{ " : L"\r\n");
@ -1447,7 +1470,7 @@ static int udt_dump_normal(const wchar_t* type_name, const u8* p, size_t size,
DWORD ofs = 0;
if(!SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_OFFSET, &ofs))
continue;
assert(ofs < size);
debug_assert(ofs < size);
if(!fits_on_one_line)
INDENT;
@ -1698,8 +1721,8 @@ const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void*
if(!CAS(&already_in_progress, 0, 1))
{
wcscpy_s(buf, max_chars,
L"(cannot start a nested stack trace; what probably happened is that"
L"an assert/debug_warn/CHECK_ERR fired during the current trace.)"
L"(cannot start a nested stack trace; what probably happened is that "
L"an debug_assert/debug_warn/CHECK_ERR fired during the current trace.)"
);
return buf;
}
@ -1823,7 +1846,7 @@ Small small_array_of_small_structs[2] = { { 1,2 } };
int ar1[] = { 1,2,3,4,5 };
char ar2[] = { 't','e','s','t', 0 };
//assert2(0 && "test assert2"); // not exception (works when run from debugger)
//debug_assert(0 && "test debug_assert"); // not exception (works when run from debugger)
//__asm xor edx,edx __asm div edx // named SEH
//RaiseException(0x87654321, 0, 0, 0); // unknown SEH
//throw std::bad_exception("what() is ok"); // C++

View File

@ -17,7 +17,7 @@
#include "precompiled.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h> // __argc
@ -158,7 +158,7 @@ static int CALLBACK browse_cb(HWND hWnd, unsigned int msg, LPARAM lParam, LPARAM
int pick_directory(char* path, size_t buf_size)
{
assert(buf_size >= PATH_MAX);
debug_assert(buf_size >= PATH_MAX);
IMalloc* p_malloc;
SHGetMalloc(&p_malloc);
@ -185,7 +185,7 @@ int pick_directory(char* path, size_t buf_size)
//-----------------------------------------------------------------------------
// "program error" dialog (triggered by assert and exception)
// "program error" dialog (triggered by debug_assert and exception)
//-----------------------------------------------------------------------------
// support for resizing the dialog / its controls
@ -563,21 +563,21 @@ static bool cs_valid;
void win_lock(uint idx)
{
assert(idx < NUM_CS && "win_lock: invalid critical section index");
debug_assert(idx < NUM_CS && "win_lock: invalid critical section index");
if(cs_valid)
EnterCriticalSection(&cs[idx]);
}
void win_unlock(uint idx)
{
assert(idx < NUM_CS && "win_unlock: invalid critical section index");
debug_assert(idx < NUM_CS && "win_unlock: invalid critical section index");
if(cs_valid)
LeaveCriticalSection(&cs[idx]);
}
int win_is_locked(uint idx)
{
assert(idx < NUM_CS && "win_is_locked: invalid critical section index");
debug_assert(idx < NUM_CS && "win_is_locked: invalid critical section index");
if(!cs_valid)
return -1;
BOOL got_it = TryEnterCriticalSection(&cs[idx]);
@ -637,7 +637,7 @@ uint local2 = 0x1234;
debug_printf("&static2 = %p\n", &static2);
debug_printf("param2 = %p\n&local2 = %p\n", param2, &local2);
debug_printf("&param2a = %p\nparam2b = %p\nparam2c = %p\n", &param2a, &param2b, &param2c);
assert2(0 == 1);
debug_assert(0 == 1);
}
static void test1(uint param1a, uint param1b, uint param1c, std::vector<uint>* param1)