# merge all local changes; moving over to new SVN server
* app_hooks: add display_error; can be used by atlas to override our dialog box * lots of small fixes (mostly pertaining to headers) * debug: clean up display_error, protect from reentrancy, fix a few edge cases (e.g. error message from dtor -> exit pressed -> suppress all subsequent errors) * delay_load: add warning: NLSO ctors are unreliable since we're compiling into static lib This was SVN commit r4009.
This commit is contained in:
parent
92b9c07f95
commit
ee4c7965dd
@ -22,10 +22,9 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "posix.h"
|
||||
#include "sysdep/cpu.h" // CAS
|
||||
#include "lib/posix.h" // PROT_* constants for da_set_prot
|
||||
#include "lib/sysdep/cpu.h" // CAS
|
||||
#include "byte_order.h"
|
||||
|
||||
#include "allocators.h"
|
||||
|
||||
|
||||
|
@ -26,7 +26,8 @@
|
||||
#include <map>
|
||||
|
||||
#include "lib/types.h"
|
||||
#include "lib/posix.h" // PROT_* constants for da_set_prot
|
||||
#include "lib/sysdep/cpu.h" // CAS
|
||||
#include "lib/posix.h" // PROT_* constants for da_set_prot
|
||||
|
||||
//
|
||||
// page aligned allocator
|
||||
|
@ -145,6 +145,12 @@ static void log(const wchar_t* text)
|
||||
}
|
||||
|
||||
|
||||
static ErrorReaction display_error(const wchar_t* UNUSED(text), uint UNUSED(flags))
|
||||
{
|
||||
return ER_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// contains the current set of hooks. starts with the stub values and
|
||||
|
@ -107,6 +107,8 @@ FUNC(const wchar_t*, translate, (const wchar_t* text), (text), return)
|
||||
// default implementation uses stdout.
|
||||
FUNC(void, log, (const wchar_t* text), (text), (void))
|
||||
|
||||
FUNC(ErrorReaction, display_error, (const wchar_t* text, uint flags), (text, flags), return)
|
||||
|
||||
#endif // #ifdef FUNC
|
||||
|
||||
|
||||
|
@ -22,19 +22,30 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// converts 4 character string to u32 for easy comparison
|
||||
// can't pass code as string, and use s[0]..s[3], because
|
||||
// VC6/7 don't realize the macro is constant
|
||||
// (it should be useable as a switch{} expression)
|
||||
//
|
||||
// these casts are ugly but necessary. u32 is required because u8 << 8 == 0;
|
||||
// the additional u8 cast ensures each character is treated as unsigned
|
||||
// (otherwise, they'd be promoted to signed int before the u32 cast,
|
||||
// which would break things).
|
||||
|
||||
/**
|
||||
* convert 4 characters to u32 (at compile time) for easy comparison.
|
||||
* output is in native byte order; e.g. FOURCC_LE can be used instead.
|
||||
**/
|
||||
#define FOURCC(a,b,c,d) // real definition is below
|
||||
#undef FOURCC
|
||||
|
||||
// implementation rationale:
|
||||
// - can't pass code as string, and use s[0]..s[3], because
|
||||
// VC6/7 don't realize the macro is constant
|
||||
// (it should be usable as a switch{} expression)
|
||||
// - the casts are ugly but necessary. u32 is required because u8 << 8 == 0;
|
||||
// the additional u8 cast ensures each character is treated as unsigned
|
||||
// (otherwise, they'd be promoted to signed int before the u32 cast,
|
||||
// which would break things).
|
||||
|
||||
/// big-endian version of FOURCC
|
||||
#define FOURCC_BE(a,b,c,d) ( ((u32)(u8)a) << 24 | ((u32)(u8)b) << 16 | \
|
||||
((u32)(u8)c) << 8 | ((u32)(u8)d) << 0 )
|
||||
((u32)(u8)c) << 8 | ((u32)(u8)d) << 0 )
|
||||
|
||||
/// little-endian version of FOURCC
|
||||
#define FOURCC_LE(a,b,c,d) ( ((u32)(u8)a) << 0 | ((u32)(u8)b) << 8 | \
|
||||
((u32)(u8)c) << 16 | ((u32)(u8)d) << 24 )
|
||||
((u32)(u8)c) << 16 | ((u32)(u8)d) << 24 )
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
# define FOURCC FOURCC_BE
|
||||
@ -43,26 +54,32 @@
|
||||
#endif
|
||||
|
||||
|
||||
/// convert a little-endian number to/from native byte order.
|
||||
extern u16 to_le16(u16 x);
|
||||
extern u32 to_le32(u32 x);
|
||||
extern u64 to_le64(u64 x);
|
||||
extern u32 to_le32(u32 x); /// see to_le16
|
||||
extern u64 to_le64(u64 x); /// see to_le16
|
||||
|
||||
/// convert a big-endian number to/from native byte order.
|
||||
extern u16 to_be16(u16 x);
|
||||
extern u32 to_be32(u32 x);
|
||||
extern u64 to_be64(u64 x);
|
||||
extern u32 to_be32(u32 x); /// see to_be16
|
||||
extern u64 to_be64(u64 x); /// see to_be16
|
||||
|
||||
/// read a little-endian number from memory into native byte order.
|
||||
extern u16 read_le16(const void* p);
|
||||
extern u32 read_le32(const void* p);
|
||||
extern u64 read_le64(const void* p);
|
||||
extern u32 read_le32(const void* p); /// see read_le16
|
||||
extern u64 read_le64(const void* p); /// see read_le16
|
||||
|
||||
/// read a big-endian number from memory into native byte order.
|
||||
extern u16 read_be16(const void* p);
|
||||
extern u32 read_be32(const void* p);
|
||||
extern u64 read_be64(const void* p);
|
||||
extern u32 read_be32(const void* p); /// see read_be16
|
||||
extern u64 read_be64(const void* p); /// see read_be16
|
||||
|
||||
/// write a little-endian number to memory in native byte order.
|
||||
extern void write_le16(void* p, u16 x);
|
||||
extern void write_le32(void* p, u32 x);
|
||||
extern void write_le64(void* p, u64 x);
|
||||
extern void write_le32(void* p, u32 x); /// see write_le16
|
||||
extern void write_le64(void* p, u64 x); /// see write_le16
|
||||
|
||||
/// write a big-endian number to memory in native byte order.
|
||||
extern void write_be16(void* p, u16 x);
|
||||
extern void write_be32(void* p, u32 x);
|
||||
extern void write_be64(void* p, u64 x);
|
||||
extern void write_be32(void* p, u32 x); /// see write_be16
|
||||
extern void write_be64(void* p, u64 x); /// see write_be16
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "lib/sysdep/cpu.h" // CAS
|
||||
// some functions here are called from within mmgr; disable its hooks
|
||||
// so that our allocations don't cause infinite recursion.
|
||||
#include "nommgr.h"
|
||||
@ -312,7 +313,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);
|
||||
stl_simplify_name(string+len);
|
||||
debug_stl_simplify_name(string+len);
|
||||
}
|
||||
|
||||
return string;
|
||||
@ -452,79 +453,103 @@ void debug_display_msgw(const wchar_t* caption, const wchar_t* msg)
|
||||
}
|
||||
|
||||
|
||||
// display the error dialog. shows <description> along with a stack trace.
|
||||
// context and skip are as with debug_dump_stack.
|
||||
// flags: see DisplayErrorFlags. file and line indicate where the error
|
||||
// occurred and are typically passed as __FILE__, __LINE__.
|
||||
ErrorReaction debug_display_error(const wchar_t* description,
|
||||
int flags, uint skip, void* context, const char* file, int line)
|
||||
// when an error has come up and user clicks Exit, we don't want any further
|
||||
// errors (e.g. caused by atexit handlers) to come up, possibly causing an
|
||||
// infinite loop. it sucks to hide errors, but we assume that whoever clicked
|
||||
// exit really doesn't want to see any more errors.
|
||||
static bool exit_requested;
|
||||
|
||||
// this logic is applicable to any type of error. special cases such as
|
||||
// suppressing certain expected WARN_ERRs are done there.
|
||||
static bool should_suppress_error(u8* suppress)
|
||||
{
|
||||
if(!file || file[0] == '\0')
|
||||
file = "unknown";
|
||||
if(line <= 0)
|
||||
line = 0;
|
||||
if(!suppress)
|
||||
return false;
|
||||
|
||||
// translate
|
||||
description = ah_translate(description);
|
||||
if(*suppress == DEBUG_SUPPRESS)
|
||||
return true;
|
||||
|
||||
// display in output window; double-click will navigate to error location.
|
||||
const char* fn_only = path_name_only(file);
|
||||
debug_wprintf(L"%hs(%d): %ls\n", fn_only, line, description);
|
||||
if(exit_requested)
|
||||
return true;
|
||||
|
||||
// allocate memory for the stack trace. this needs to be quite large,
|
||||
// so preallocating is undesirable. it must work even if the heap is
|
||||
// corrupted (since that's an error we might want to display), so
|
||||
// we cannot rely on the heap alloc alone. what we do is try malloc,
|
||||
// fall back to alloca if it failed, and give up after that.
|
||||
wchar_t* text = 0;
|
||||
size_t max_chars = 256*KiB;
|
||||
// .. try allocating from heap
|
||||
void* heap_mem = malloc(max_chars*sizeof(wchar_t));
|
||||
text = (wchar_t*)heap_mem;
|
||||
// .. heap alloc failed; try allocating from stack
|
||||
if(!text)
|
||||
return false;
|
||||
}
|
||||
|
||||
static const wchar_t* build_error_message(wchar_t* buf, size_t max_chars,
|
||||
const wchar_t* description,
|
||||
const char* fn_only, int line, const char* func,
|
||||
uint skip, void* context,
|
||||
bool is_nested_error)
|
||||
{
|
||||
if(!buf)
|
||||
return L"(insufficient memory to generate error message)";
|
||||
|
||||
static const wchar_t fmt[] =
|
||||
L"%ls\r\n"
|
||||
L"Location: %hs:%d (%hs)\r\n"
|
||||
L"\r\n"
|
||||
L"Call stack:\r\n"
|
||||
L"\r\n";
|
||||
int len = swprintf(buf,max_chars,fmt, description, fn_only, line, func);
|
||||
if(len < 0)
|
||||
return L"(error while formatting error message)";
|
||||
|
||||
// add stack trace to end of message
|
||||
wchar_t* pos = buf+len; const size_t chars_left = max_chars-len;
|
||||
if(!is_nested_error)
|
||||
{
|
||||
max_chars = 128*KiB; // (stack limit is usually 1 MiB)
|
||||
text = (wchar_t*)alloca(max_chars*sizeof(wchar_t));
|
||||
}
|
||||
|
||||
// alloc succeeded; proceed
|
||||
if(text)
|
||||
{
|
||||
static const wchar_t fmt[] = L"%ls\r\n\r\nCall stack:\r\n\r\n";
|
||||
int len = swprintf(text, max_chars, fmt, description);
|
||||
// paranoia - only dump stack if this string output succeeded.
|
||||
if(len >= 0)
|
||||
{
|
||||
if(!context)
|
||||
skip++; // skip this frame
|
||||
debug_dump_stack(text+len, max_chars-len, skip, context);
|
||||
}
|
||||
if(!context)
|
||||
skip++; // skip this frame
|
||||
debug_dump_stack(pos, chars_left, skip, context);
|
||||
}
|
||||
// .. except when a stack trace is currently already in progress
|
||||
// (debug_dump_stack is not reentrant due to use of global buffer!)
|
||||
else
|
||||
text = L"(insufficient memory to display error message)";
|
||||
|
||||
debug_write_crashlog(text);
|
||||
ErrorReaction er = sys_display_error(text, flags);
|
||||
|
||||
// note: debug_break-ing here to make sure the app doesn't continue
|
||||
// running is no longer necessary. debug_display_error now determines our
|
||||
// window handle and is modal.
|
||||
|
||||
// handle "break" request unless the caller wants to (doing so here
|
||||
// instead of within the dlgproc yields a correct call stack)
|
||||
if(er == ER_BREAK && !(flags & DE_MANUAL_BREAK))
|
||||
{
|
||||
debug_break();
|
||||
er = ER_CONTINUE;
|
||||
wcscpy_s(pos, chars_left,
|
||||
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.)"
|
||||
);
|
||||
}
|
||||
|
||||
free(heap_mem); // no-op if not allocated from heap
|
||||
// after debug_break to ease debugging, but before exit to avoid leak.
|
||||
return buf;
|
||||
}
|
||||
|
||||
// exit requested. do so here to disburden callers.
|
||||
if(er == ER_EXIT)
|
||||
static ErrorReaction call_display_error(const wchar_t* text, uint flags)
|
||||
{
|
||||
// first try app hook implementation
|
||||
ErrorReaction er = ah_display_error(text, flags);
|
||||
// .. it's only a stub: default to normal implementation
|
||||
if(er == ER_NOT_IMPLEMENTED)
|
||||
er = sys_display_error(text, flags);
|
||||
|
||||
return er;
|
||||
}
|
||||
|
||||
static ErrorReaction carry_out_ErrorReaction(ErrorReaction er, uint flags, u8* suppress)
|
||||
{
|
||||
const bool manual_break = (flags & DE_MANUAL_BREAK) != 0;
|
||||
|
||||
switch(er)
|
||||
{
|
||||
case ER_BREAK:
|
||||
// handle "break" request unless the caller wants to (doing so here
|
||||
// instead of within the dlgproc yields a correct call stack)
|
||||
if(!manual_break)
|
||||
{
|
||||
debug_break();
|
||||
er = ER_CONTINUE;
|
||||
}
|
||||
break;
|
||||
|
||||
case ER_SUPPRESS:
|
||||
*suppress = DEBUG_SUPPRESS;
|
||||
er = ER_CONTINUE;
|
||||
break;
|
||||
|
||||
case ER_EXIT:
|
||||
exit_requested = true; // see declaration
|
||||
|
||||
// disable memory-leak reporting to avoid a flood of warnings
|
||||
// (lots of stuff will leak since we exit abnormally).
|
||||
debug_heap_enable(DEBUG_HEAP_NONE);
|
||||
@ -538,53 +563,107 @@ ErrorReaction debug_display_error(const wchar_t* description,
|
||||
return er;
|
||||
}
|
||||
|
||||
|
||||
ErrorReaction debug_assert_failed(const char* expr,
|
||||
const char* file, int line, const char* func)
|
||||
ErrorReaction debug_display_error(const wchar_t* description,
|
||||
uint flags, uint skip, void* context,
|
||||
const char* file, int line, const char* func,
|
||||
u8* suppress)
|
||||
{
|
||||
/*/*
|
||||
// for edge cases in some functions, warnings (=asserts) are raised in
|
||||
// addition to returning an error code. self-tests deliberately trigger
|
||||
// these cases and check for the latter but shouldn't cause the former.
|
||||
// we therefore squelch them here.
|
||||
// (note: don't do so in lib.h's CHECK_ERR or debug_assert to reduce
|
||||
// compile-time dependency on self_test.h)
|
||||
if(self_test_active)
|
||||
// "suppressing" this error means doing nothing and returning ER_CONTINUE.
|
||||
if(should_suppress_error(suppress))
|
||||
return ER_CONTINUE;
|
||||
*/
|
||||
// __FILE__ evaluates to the full path (albeit without drive letter)
|
||||
// which is rather long. we only display the base name for clarity.
|
||||
|
||||
// fix up params
|
||||
// .. translate
|
||||
description = ah_translate(description);
|
||||
// .. caller supports a suppress flag; set the corresponding flag so that
|
||||
// the error display implementation enables the Suppress option.
|
||||
if(suppress)
|
||||
flags |= DE_ALLOW_SUPPRESS;
|
||||
// .. deal with incomplete file/line info
|
||||
if(!file || file[0] == '\0')
|
||||
file = "unknown";
|
||||
if(line <= 0)
|
||||
line = 0;
|
||||
if(!func || func[0] == '\0')
|
||||
func = "?";
|
||||
// .. _FILE__ evaluates to the full path (albeit without drive letter)
|
||||
// which is rather long. we only display the base name for clarity.
|
||||
const char* fn_only = path_name_only(file);
|
||||
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[400];
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed at %hs:%d (%hs): \"%hs\"", fn_only, line, func, expr);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip, context, fn_only, line);
|
||||
|
||||
// display in output window; double-click will navigate to error location.
|
||||
debug_wprintf(L"%hs(%d): %ls\n", fn_only, line, description);
|
||||
|
||||
|
||||
// allocate memory for the error message. this needs to be quite large,
|
||||
// so preallocating is undesirable.
|
||||
// note: this code can't be moved into a subroutine due to alloca.
|
||||
wchar_t* buf = 0;
|
||||
size_t max_chars = 256*KiB;
|
||||
// .. try allocating from heap. can't rely on this because we might
|
||||
// be called upon to report heap corruption errors.
|
||||
void* heap_mem = malloc((max_chars+1)*sizeof(wchar_t));
|
||||
buf = (wchar_t*)heap_mem;
|
||||
// .. heap alloc failed; try allocating from stack. if this fails,
|
||||
// we give up and simply display a static error message.
|
||||
if(!buf)
|
||||
{
|
||||
max_chars = 128*KiB; // (stack limit is usually 1 MiB)
|
||||
buf = (wchar_t*)alloca((max_chars+1)*sizeof(wchar_t));
|
||||
}
|
||||
|
||||
static uintptr_t already_in_progress;
|
||||
const bool is_nested = !CAS(&already_in_progress, 0, 1);
|
||||
|
||||
const wchar_t* text = build_error_message(buf, max_chars, description,
|
||||
fn_only, line, func, skip, context, is_nested);
|
||||
|
||||
if(!is_nested) // avoids potential infinite loop
|
||||
debug_write_crashlog(text);
|
||||
|
||||
ErrorReaction er = call_display_error(text, flags);
|
||||
|
||||
|
||||
// note: debug_break-ing here to make sure the app doesn't continue
|
||||
// running is no longer necessary. debug_display_error now determines our
|
||||
// window handle and is modal.
|
||||
|
||||
// must happen before carry_out_ErrorReaction because that may exit.
|
||||
// note: no-op if not allocated from heap.
|
||||
free(heap_mem);
|
||||
|
||||
already_in_progress = 0;
|
||||
|
||||
return carry_out_ErrorReaction(er, flags, suppress);
|
||||
}
|
||||
|
||||
|
||||
ErrorReaction debug_warn_err(LibError err,
|
||||
|
||||
|
||||
ErrorReaction debug_assert_failed(const char* expr, u8* suppress,
|
||||
const char* file, int line, const char* func)
|
||||
{
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[400];
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed: \"%hs\"", expr);
|
||||
return debug_display_error(buf, DE_MANUAL_BREAK, skip,context, file,line,func, suppress);
|
||||
}
|
||||
|
||||
|
||||
ErrorReaction debug_warn_err(LibError err, u8* suppress,
|
||||
const char* file, int line, const char* func)
|
||||
{
|
||||
/*/*
|
||||
// for edge cases in some functions, warnings (=asserts) are raised in
|
||||
// addition to returning an error code. self-tests deliberately trigger
|
||||
// these cases and check for the latter but shouldn't cause the former.
|
||||
// we therefore squelch them here.
|
||||
// (note: don't do so in lib.h's CHECK_ERR or debug_assert to reduce
|
||||
// compile-time dependency on self_test.h)
|
||||
if(self_test_active)
|
||||
return ER_CONTINUE;
|
||||
*/
|
||||
// __FILE__ evaluates to the full path (albeit without drive letter)
|
||||
// which is rather long. we only display the base name for clarity.
|
||||
const char* fn_only = path_name_only(file);
|
||||
//TODO squelch certain errors once
|
||||
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[400];
|
||||
char err_buf[200]; error_description_r(err, err_buf, ARRAY_SIZE(err_buf));
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Function call failed at %hs:%d (%hs): return value was %d (%hs)", fn_only, line, func, err, err_buf);
|
||||
return debug_display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip,context, fn_only,line);
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Function call failed: return value was %d (%hs)", err, err_buf);
|
||||
return debug_display_error(buf, DE_MANUAL_BREAK, skip,context, file,line,func, suppress);
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,114 +106,6 @@ enum DebugHeapChecks
|
||||
extern void debug_heap_enable(DebugHeapChecks what);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debug_assert
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* make sure the expression <expr> evaluates to non-zero. used to validate
|
||||
* invariants in the program during development and thus gives a
|
||||
* very helpful warning if something isn't going as expected.
|
||||
* sprinkle these liberally throughout your code!
|
||||
*
|
||||
* recommended use is debug_assert(expression && "descriptive string") -
|
||||
* the string can pass more information about the problem on to whomever
|
||||
* is seeing the error.
|
||||
*
|
||||
* rationale: we call this "debug_assert" instead of "assert" for the
|
||||
* following reasons:
|
||||
* - consistency (everything here is prefixed with debug_) and
|
||||
* - to avoid inadvertent use of the much less helpful built-in CRT assert.
|
||||
* if we were to override assert, it would be difficult to tell whether
|
||||
* user source has included <assert.h> (possibly indirectly via other
|
||||
* headers) and thereby stomped on our definition.
|
||||
*
|
||||
* implementation rationale: 0x55 and 0xAA are distinctive values and
|
||||
* thus help debug the symbol engine.
|
||||
**/
|
||||
#define debug_assert(expr) \
|
||||
STMT(\
|
||||
static unsigned char suppress__ = 0x55;\
|
||||
if(suppress__ == 0x55 && !(expr))\
|
||||
{\
|
||||
switch(debug_assert_failed(#expr, __FILE__, __LINE__, __func__))\
|
||||
{\
|
||||
case ER_SUPPRESS:\
|
||||
suppress__ = 0xAA;\
|
||||
break;\
|
||||
case ER_CONTINUE:\
|
||||
break;\
|
||||
default:\
|
||||
case ER_BREAK:\
|
||||
debug_break();\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
)
|
||||
|
||||
/**
|
||||
* show a dialog to make sure unexpected states in the program are noticed.
|
||||
* this is less error-prone than "debug_assert(0 && "text");" and avoids
|
||||
* "conditional expression is constant" warnings. we'd really like to
|
||||
* completely eliminate the problem; replacing 0 literals with extern
|
||||
* volatile variables fools VC7 but isn't guaranteed to be free of overhead.
|
||||
* we therefore just squelch the warning (unfortunately non-portable).
|
||||
**/
|
||||
#define debug_warn(str) debug_assert((str) && 0)
|
||||
|
||||
|
||||
/**
|
||||
* if (LibError)err indicates an function failed, display the error dialog.
|
||||
* used by CHECK_ERR et al., which wrap function calls and automatically
|
||||
* warn user and return to caller.
|
||||
**/
|
||||
#define DEBUG_WARN_ERR(err)\
|
||||
STMT(\
|
||||
static unsigned char suppress__ = 0x55;\
|
||||
if(suppress__ == 0x55)\
|
||||
{\
|
||||
switch(debug_warn_err(err, __FILE__, __LINE__, __func__))\
|
||||
{\
|
||||
case ER_SUPPRESS:\
|
||||
suppress__ = 0xAA;\
|
||||
break;\
|
||||
case ER_CONTINUE:\
|
||||
break;\
|
||||
default:\
|
||||
case ER_BREAK:\
|
||||
debug_break();\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* called when a debug_assert fails;
|
||||
* notifies the user via debug_display_error.
|
||||
*
|
||||
* @param assert_expr the expression that failed; typically passed as
|
||||
* #expr in the assert macro.
|
||||
* @param file, line source file name and line number of the spot that failed
|
||||
* @param func name of the function containing it
|
||||
* @return ErrorReaction (user's choice: continue running or stop?)
|
||||
**/
|
||||
extern ErrorReaction debug_assert_failed(const char* assert_expr,
|
||||
const char* file, int line, const char* func);
|
||||
|
||||
/**
|
||||
* called when a DEBUG_WARN_ERR indicates an error occurred;
|
||||
* notifies the user via debug_display_error.
|
||||
*
|
||||
* @param err LibError value indicating the error that occurred
|
||||
* @param file, line source file name and line number of the spot that failed
|
||||
* @param func name of the function containing it
|
||||
* @return ErrorReaction (user's choice: continue running or stop?)
|
||||
**/
|
||||
extern ErrorReaction debug_warn_err(LibError err,
|
||||
const char* file, int line, const char* func);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// output
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -239,18 +131,22 @@ extern void debug_wprintf(const wchar_t* fmt, ...);
|
||||
extern void debug_display_msgw(const wchar_t* caption, const wchar_t* msg);
|
||||
|
||||
/// flags to customize debug_display_error behavior
|
||||
enum DisplayErrorFlags
|
||||
enum DebugDisplayErrorFlags
|
||||
{
|
||||
/**
|
||||
* allow the suppress button (requires calling via macro that
|
||||
* maintains a 'suppress' bool; see debug_assert)
|
||||
* disallow the Continue button. used e.g. if an exception is fatal.
|
||||
**/
|
||||
DE_ALLOW_SUPPRESS = 1,
|
||||
DE_NO_CONTINUE = 1,
|
||||
|
||||
/**
|
||||
* disallow the continue button. used e.g. if an exception is fatal.
|
||||
* enable the Suppress button. set automatically by debug_display_error if
|
||||
* it receives a non-NULL suppress pointer. a flag is necessary because
|
||||
* the sys_display_error interface doesn't get that pointer.
|
||||
* rationale for automatic setting: this may prevent someone from
|
||||
* forgetting to specify it, and disabling Suppress despite having
|
||||
* passed a non-NULL pointer doesn't make much sense.
|
||||
**/
|
||||
DE_NO_CONTINUE = 2,
|
||||
DE_ALLOW_SUPPRESS = 2,
|
||||
|
||||
/**
|
||||
* do not trigger a breakpoint inside debug_display_error; caller
|
||||
@ -260,24 +156,81 @@ enum DisplayErrorFlags
|
||||
DE_MANUAL_BREAK = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* value for suppress flag once set by debug_display_error.
|
||||
* rationale: this value is fairly distinctive and helps when
|
||||
* debugging the symbol engine.
|
||||
* initial value is 0 rather that another constant; this avoids
|
||||
* allocating .rdata space.
|
||||
**/
|
||||
const u8 DEBUG_SUPPRESS = 0xAB;
|
||||
|
||||
/**
|
||||
* choices offered by the shared error dialog
|
||||
**/
|
||||
enum ErrorReaction
|
||||
{
|
||||
/**
|
||||
* ignore, continue as if nothing happened.
|
||||
* note: value doesn't start at 0 because that is interpreted as a
|
||||
* DialogBoxParam failure.
|
||||
**/
|
||||
ER_CONTINUE = 1,
|
||||
|
||||
/**
|
||||
* trigger breakpoint, i.e. enter debugger.
|
||||
* only returned if DE_MANUAL_BREAK was passed; otherwise,
|
||||
* debug_display_error will trigger a breakpoint itself.
|
||||
**/
|
||||
ER_BREAK,
|
||||
|
||||
/**
|
||||
* ignore and do not report again.
|
||||
* note: non-persistent; only applicable during this program run.
|
||||
* acted on by debug_display_error; never returned to caller.
|
||||
**/
|
||||
ER_SUPPRESS,
|
||||
|
||||
/**
|
||||
* exit the program immediately.
|
||||
* acted on by debug_display_error; never returned to caller.
|
||||
**/
|
||||
ER_EXIT,
|
||||
|
||||
/**
|
||||
* special return value for the display_error app hook stub to indicate
|
||||
* that it has done nothing and that the normal sys_display_error
|
||||
* implementation should be called instead.
|
||||
* acted on by debug_display_error; never returned to caller.
|
||||
**/
|
||||
ER_NOT_IMPLEMENTED
|
||||
};
|
||||
|
||||
/**
|
||||
* display an error dialog with a message and stack trace.
|
||||
*
|
||||
* @param description text to show.
|
||||
* @param flags: see DisplayErrorFlags.
|
||||
* @param flags: see DebugDisplayErrorFlags.
|
||||
* @param context, skip: see debug_dump_stack.
|
||||
* @param file, line: location of the error (typically passed as
|
||||
* __FILE__, __LINE__ from a macro)
|
||||
* @param file, line, func: location of the error (typically passed as
|
||||
* __FILE__, __LINE__, __func__ from a macro)
|
||||
* @param suppress pointer to a caller-allocated flag that can be used to
|
||||
* suppress this error. if NULL, this functionality is skipped and the
|
||||
* "Suppress" dialog button will be disabled.
|
||||
* note: this flag is read and written exclusively here; caller only
|
||||
* provides the storage. values: see DEBUG_SUPPRESS above.
|
||||
* @return ErrorReaction (user's choice: continue running or stop?)
|
||||
**/
|
||||
extern ErrorReaction debug_display_error(const wchar_t* description,
|
||||
int flags, uint skip, void* context, const char* file, int line);
|
||||
uint flags, uint skip, void* context,
|
||||
const char* file, int line, const char* func,
|
||||
u8* suppress);
|
||||
|
||||
/**
|
||||
* convenience version, in case the advanced parameters aren't needed.
|
||||
* macro instead of providing overload/default values for C compatibility.
|
||||
**/
|
||||
#define DISPLAY_ERROR(text) debug_display_error(text, 0, 0,0, __FILE__,__LINE__)
|
||||
#define DISPLAY_ERROR(text) debug_display_error(text, 0, 0,0, __FILE__,__LINE__,__func__, 0)
|
||||
|
||||
|
||||
//
|
||||
@ -336,6 +289,104 @@ extern void debug_wprintf_mem(const wchar_t* fmt, ...);
|
||||
extern LibError debug_write_crashlog(const wchar_t* text);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debug_assert
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* make sure the expression <expr> evaluates to non-zero. used to validate
|
||||
* invariants in the program during development and thus gives a
|
||||
* very helpful warning if something isn't going as expected.
|
||||
* sprinkle these liberally throughout your code!
|
||||
*
|
||||
* recommended use is debug_assert(expression && "descriptive string") -
|
||||
* the string can pass more information about the problem on to whomever
|
||||
* is seeing the error.
|
||||
*
|
||||
* rationale: we call this "debug_assert" instead of "assert" for the
|
||||
* following reasons:
|
||||
* - consistency (everything here is prefixed with debug_) and
|
||||
* - to avoid inadvertent use of the much less helpful built-in CRT assert.
|
||||
* if we were to override assert, it would be difficult to tell whether
|
||||
* user source has included <assert.h> (possibly indirectly via other
|
||||
* headers) and thereby stomped on our definition.
|
||||
**/
|
||||
#define debug_assert(expr) \
|
||||
STMT(\
|
||||
static u8 suppress__;\
|
||||
if(!(expr))\
|
||||
{\
|
||||
switch(debug_assert_failed(#expr, &suppress__, __FILE__, __LINE__, __func__))\
|
||||
{\
|
||||
case ER_BREAK:\
|
||||
debug_break();\
|
||||
break;\
|
||||
default:\
|
||||
break;\
|
||||
}\
|
||||
}\
|
||||
)
|
||||
|
||||
/**
|
||||
* show a dialog to make sure unexpected states in the program are noticed.
|
||||
* this is less error-prone than "debug_assert(0 && "text");" and avoids
|
||||
* "conditional expression is constant" warnings. we'd really like to
|
||||
* completely eliminate the problem; replacing 0 literals with extern
|
||||
* volatile variables fools VC7 but isn't guaranteed to be free of overhead.
|
||||
* we therefore just squelch the warning (unfortunately non-portable).
|
||||
**/
|
||||
#define debug_warn(str) debug_assert((str) && 0)
|
||||
|
||||
|
||||
/**
|
||||
* if (LibError)err indicates an function failed, display the error dialog.
|
||||
* used by CHECK_ERR et al., which wrap function calls and automatically
|
||||
* warn user and return to caller.
|
||||
**/
|
||||
#define DEBUG_WARN_ERR(err)\
|
||||
STMT(\
|
||||
static u8 suppress__;\
|
||||
switch(debug_warn_err(err, &suppress__, __FILE__, __LINE__, __func__))\
|
||||
{\
|
||||
case ER_BREAK:\
|
||||
debug_break();\
|
||||
break;\
|
||||
default:\
|
||||
break;\
|
||||
}\
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* called when a debug_assert fails;
|
||||
* notifies the user via debug_display_error.
|
||||
*
|
||||
* @param assert_expr the expression that failed; typically passed as
|
||||
* #expr in the assert macro.
|
||||
* @param suppress see debug_display_error.
|
||||
* @param file, line source file name and line number of the spot that failed
|
||||
* @param func name of the function containing it
|
||||
* @return ErrorReaction (user's choice: continue running or stop?)
|
||||
**/
|
||||
extern ErrorReaction debug_assert_failed(const char* assert_expr,
|
||||
u8* suppress,
|
||||
const char* file, int line, const char* func);
|
||||
|
||||
/**
|
||||
* called when a DEBUG_WARN_ERR indicates an error occurred;
|
||||
* notifies the user via debug_display_error.
|
||||
*
|
||||
* @param err LibError value indicating the error that occurred
|
||||
* @param suppress see debug_display_error.
|
||||
* @param file, line source file name and line number of the spot that failed
|
||||
* @param func name of the function containing it
|
||||
* @return ErrorReaction (user's choice: continue running or stop?)
|
||||
**/
|
||||
extern ErrorReaction debug_warn_err(LibError err,
|
||||
u8* suppress,
|
||||
const char* file, int line, const char* func);
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// breakpoints
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -434,6 +485,8 @@ extern LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char
|
||||
* for exceptions. otherwise, tracing starts from the current call stack.
|
||||
* @return buf for convenience; writes an error string into it if
|
||||
* something goes wrong.
|
||||
*
|
||||
* not reentrant! (pointer to buf is stored in static variable)
|
||||
**/
|
||||
extern const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* context);
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "debug_stl.h"
|
||||
#include "lib.h" // match_wildcard
|
||||
|
||||
// used in stl_simplify_name.
|
||||
// used in debug_stl_simplify_name.
|
||||
// note: strcpy is safe because replacement happens in-place and
|
||||
// src is longer than dst (otherwise, we wouldn't be replacing).
|
||||
#define REPLACE(what, with)\
|
||||
@ -63,7 +63,7 @@
|
||||
//
|
||||
// see http://www.bdsoft.com/tools/stlfilt.html and
|
||||
// http://www.moderncppdesign.com/publications/better_template_error_messages.html
|
||||
char* stl_simplify_name(char* name)
|
||||
char* debug_stl_simplify_name(char* name)
|
||||
{
|
||||
// used when stripping everything inside a < > to continue until
|
||||
// the final bracket is matched (at the original nesting level).
|
||||
@ -506,14 +506,14 @@ public:
|
||||
// standard container adapters
|
||||
//
|
||||
|
||||
// stl_get_container_info makes sure this was actually instantiated with
|
||||
// debug_stl_get_container_info makes sure this was actually instantiated with
|
||||
// container = deque as we assume.
|
||||
class Any_queue : public Any_deque
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
// stl_get_container_info makes sure this was actually instantiated with
|
||||
// debug_stl_get_container_info makes sure this was actually instantiated with
|
||||
// container = deque as we assume.
|
||||
class Any_stack : public Any_deque
|
||||
{
|
||||
@ -643,7 +643,7 @@ template<class T> bool get_container_info(T* t, size_t size, size_t el_size,
|
||||
// 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 or an StlContainerError.
|
||||
LibError stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||
LibError debug_stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem)
|
||||
{
|
||||
// HACK: The debug_stl code breaks VS2005's STL badly, causing crashes in
|
||||
|
@ -23,22 +23,50 @@
|
||||
#ifndef DEBUG_STL_H_INCLUDED
|
||||
#define DEBUG_STL_H_INCLUDED
|
||||
|
||||
// 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).
|
||||
// returns <name> for convenience.
|
||||
extern char* stl_simplify_name(char* name);
|
||||
/**
|
||||
* reduce complicated STL symbol names to human-readable form.
|
||||
*
|
||||
* algorithm: remove/replace undesired substrings in one pass (fast).
|
||||
* example: "std::basic_string<char, char_traits<char>,
|
||||
* std::allocator<char> >" => "string".
|
||||
*
|
||||
* @param buffer holding input symbol name; modified in-place.
|
||||
* there is no length limit; must be large enough to hold typical STL
|
||||
* strings. DBG_SYMBOL_LEN chars is a good measure.
|
||||
* @return name for convenience.
|
||||
**/
|
||||
extern char* debug_stl_simplify_name(char* name);
|
||||
|
||||
|
||||
// no STL iterator is larger than this; see below.
|
||||
/**
|
||||
* abstraction of all STL iterators used by debug_stl.
|
||||
**/
|
||||
typedef const u8* (*DebugIterator)(void* internal, size_t el_size);
|
||||
|
||||
/**
|
||||
* no STL iterator is larger than this; see below.
|
||||
**/
|
||||
const size_t DEBUG_STL_MAX_ITERATOR_SIZE = 64;
|
||||
|
||||
// if <wtype_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 or an StlContainerError.
|
||||
extern LibError stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||
/**
|
||||
* prepare to enumerate the elements of arbitrarily typed STL containers.
|
||||
*
|
||||
* works by retrieving element count&size via debug information and hiding
|
||||
* the container's iterator implementation behind a common interface.
|
||||
* a basic sanity check is performed to see if the container memory is
|
||||
* valid and appears to be initialized.
|
||||
*
|
||||
* @param type_name exact type of STL container (see example above)
|
||||
* @param p raw memory holding the container
|
||||
* @param size sizeof(container)
|
||||
* @param el_size sizeof(value_type)
|
||||
* @param el_count out; number of valid elements in container
|
||||
* @param el_iterator out; callback function that acts as an iterator
|
||||
* @param it_mem out; buffer holding the iterator state. must be
|
||||
* at least DEBUG_STL_MAX_ITERATOR_SIZE bytes.
|
||||
* @return LibError (ERR_STL_*)
|
||||
**/
|
||||
extern LibError debug_stl_get_container_info(const char* type_name, const u8* p, size_t size,
|
||||
size_t el_size, size_t* el_count, DebugIterator* el_iterator, void* it_mem);
|
||||
|
||||
#endif // #ifndef DEBUG_STL_H_INCLUDED
|
||||
|
@ -59,12 +59,8 @@ scope
|
||||
#include <stddef.h>
|
||||
#include <math.h> // fabsf
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "lib/types.h"
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
#include "sysdep/cpu.h" // CAS
|
||||
//#include "sysdep/sysdep.h" // moved down; see below.
|
||||
|
||||
|
||||
#if defined(__cplusplus)
|
||||
|
@ -145,30 +145,6 @@ enum LibError {
|
||||
LIB_ERROR_DUMMY
|
||||
};
|
||||
|
||||
// choices offered by the shared error dialog
|
||||
enum ErrorReaction
|
||||
{
|
||||
// ignore, continue as if nothing happened.
|
||||
// note: don't start at 0 because that is interpreted as a
|
||||
// DialogBoxParam failure.
|
||||
ER_CONTINUE = 1,
|
||||
|
||||
// ignore and do not report again.
|
||||
// only returned if DE_ALLOW_SUPPRESS was passed.
|
||||
// note: non-persistent; only applicable during this program run.
|
||||
ER_SUPPRESS,
|
||||
|
||||
// trigger breakpoint, i.e. enter debugger.
|
||||
// only returned if DE_MANUAL_BREAK was passed; otherwise,
|
||||
// debug_display_error will trigger a breakpoint itself.
|
||||
ER_BREAK,
|
||||
|
||||
// exit the program immediately.
|
||||
// never returned; debug_display_error exits immediately.
|
||||
ER_EXIT
|
||||
};
|
||||
|
||||
|
||||
|
||||
// generate textual description of an error code.
|
||||
// stores up to <max_chars> in the given buffer.
|
||||
|
@ -28,6 +28,9 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#if OS_WIN
|
||||
// wgl.h is a private header and should only be included from here.
|
||||
// if this isn't defined, it'll complain.
|
||||
#define WGL_HEADER_NEEDED
|
||||
#include "lib/sysdep/win/wgl.h"
|
||||
#endif
|
||||
|
||||
|
@ -57,7 +57,9 @@
|
||||
|
||||
#include "lib/types.h"
|
||||
#include "lib/string_s.h" // CRT secure string
|
||||
#include "lib/debug.h"
|
||||
#include "lib/sysdep/sysdep.h"
|
||||
#include "lib/debug.h" // (sysdep.h pulls in debug.h)
|
||||
|
||||
|
||||
//
|
||||
// memory headers
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include "lib/allocators.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "file_internal.h"
|
||||
|
||||
static uintptr_t trace_initialized; // set via CAS
|
||||
|
@ -69,7 +69,7 @@
|
||||
// we hold a reference to prevent the actual unload. everything works ATM;
|
||||
// hopefully, OpenAL doesn't rely on them actually being unloaded.
|
||||
#if OS_WIN
|
||||
# define WIN_LOADLIBRARY_HACK 1
|
||||
# define WIN_LOADLIBRARY_HACK 0
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#if CPU_IA32
|
||||
# include "lib/sysdep/ia32.h"
|
||||
|
@ -22,22 +22,23 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "ia32.h"
|
||||
#include "lib/timer.h"
|
||||
|
||||
// HACK (see call to wtime_reset_impl)
|
||||
#if OS_WIN
|
||||
#include "lib/sysdep/win/wtime.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "lib/timer.h"
|
||||
#include "ia32.h"
|
||||
#include "cpu.h"
|
||||
|
||||
// HACK (see call to wtime_reset_impl)
|
||||
#if OS_WIN
|
||||
#include "lib/sysdep/win/wtime.h"
|
||||
#endif
|
||||
|
||||
#if !HAVE_MS_ASM && !HAVE_GNU_ASM
|
||||
#error ia32.cpp needs inline assembly support!
|
||||
#endif
|
||||
|
@ -24,6 +24,8 @@
|
||||
#define SYSDEP_H_INCLUDED
|
||||
|
||||
#include "lib/config.h"
|
||||
#include "lib/debug.h" // ErrorReaction
|
||||
|
||||
|
||||
// some functions among the sysdep API are implemented as macros
|
||||
// that redirect to the platform-dependent version. this is done where
|
||||
@ -157,7 +159,6 @@ extern void* alloca(size_t size);
|
||||
# define __func__ "(unknown)"
|
||||
#endif
|
||||
|
||||
#include "lib/debug.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// sysdep API
|
||||
@ -173,9 +174,10 @@ extern void* alloca(size_t size);
|
||||
extern void sys_display_msg(const char* caption, const char* msg);
|
||||
extern void sys_display_msgw(const wchar_t* caption, const wchar_t* msg);
|
||||
|
||||
// show the error dialog. flags: see DisplayErrorFlags.
|
||||
// show the error dialog. flags: see DebugDisplayErrorFlags.
|
||||
// called from debug_display_error.
|
||||
extern ErrorReaction sys_display_error(const wchar_t* text, int flags);
|
||||
// can be overridden by means of ah_display_error.
|
||||
extern ErrorReaction sys_display_error(const wchar_t* text, uint flags);
|
||||
|
||||
|
||||
//
|
||||
|
@ -61,13 +61,13 @@ LibError sys_on_each_cpu(void(*cb)())
|
||||
return ERR_NO_SYS;
|
||||
}
|
||||
|
||||
ErrorReaction sys_display_error(const wchar_t* text, int flags)
|
||||
ErrorReaction sys_display_error(const wchar_t* text, uint flags)
|
||||
{
|
||||
printf("%ls\n\n", text);
|
||||
|
||||
const bool manual_break = flags & DE_MANUAL_BREAK;
|
||||
const bool allow_suppress = flags & DE_ALLOW_SUPPRESS;
|
||||
const bool no_continue = flags & DE_NO_CONTINUE;
|
||||
const bool manual_break = (flags & DE_MANUAL_BREAK ) != 0;
|
||||
const bool allow_suppress = (flags & DE_ALLOW_SUPPRESS) != 0;
|
||||
const bool no_continue = (flags & DE_NO_CONTINUE ) != 0;
|
||||
|
||||
// until valid input given:
|
||||
for(;;)
|
||||
|
@ -24,6 +24,10 @@ struct DllLoadNotify;
|
||||
|
||||
extern void wdll_add_notify(DllLoadNotify*);
|
||||
|
||||
// note: this mechanism relies on the compiler calling non-local static
|
||||
// object ctors, which doesn't happen if compiling this code into
|
||||
// a static library. recommended workaround is to call wdll_add_notify via
|
||||
// win.cpp module init mechanism.
|
||||
struct DllLoadNotify
|
||||
{
|
||||
const char* dll_name;
|
||||
|
@ -42,7 +42,13 @@ static LibError get_ver(const char* module_path, char* out_ver, size_t out_ver_l
|
||||
DWORD unused;
|
||||
const DWORD ver_size = GetFileVersionInfoSize(module_path, &unused);
|
||||
if(!ver_size)
|
||||
WARN_RETURN(ERR_FAIL);
|
||||
{
|
||||
wchar_t buf[1000];
|
||||
swprintf(buf, 1000, L"path: %hs; GLE: %08X", module_path, GetLastError());
|
||||
DISPLAY_ERROR(buf);
|
||||
return ERR_FAIL;
|
||||
// WARN_RETURN(ERR_FAIL);
|
||||
}
|
||||
void* buf = malloc(ver_size);
|
||||
if(!buf)
|
||||
WARN_RETURN(ERR_NO_MEM);
|
||||
|
@ -22,18 +22,12 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "win_internal.h"
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h> // _aligned_malloc
|
||||
|
||||
|
||||
#define lock() win_lock(WAIO_CS)
|
||||
#define unlock() win_unlock(WAIO_CS)
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "win_internal.h"
|
||||
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(c))
|
||||
WIN_REGISTER_FUNC(waio_init);
|
||||
@ -41,6 +35,10 @@ WIN_REGISTER_FUNC(waio_init);
|
||||
WIN_REGISTER_FUNC(waio_shutdown);
|
||||
#pragma data_seg()
|
||||
|
||||
#define lock() win_lock(WAIO_CS)
|
||||
#define unlock() win_unlock(WAIO_CS)
|
||||
|
||||
|
||||
// return the largest sector size [bytes] of any storage medium
|
||||
// (HD, optical, etc.) in the system.
|
||||
//
|
||||
|
@ -596,29 +596,18 @@ static const wchar_t* get_exception_description(const EXCEPTION_POINTERS* ep)
|
||||
}
|
||||
|
||||
|
||||
// return an indication of where the exception <er> occurred (lang. neutral).
|
||||
// it is only valid until the next call, since static storage is used.
|
||||
static const wchar_t* get_exception_locus(const EXCEPTION_POINTERS* ep)
|
||||
// return location at which the exception <er> occurred.
|
||||
// params: see debug_resolve_symbol.
|
||||
static void get_exception_locus(const EXCEPTION_POINTERS* ep,
|
||||
char* file, int* line, char* func)
|
||||
{
|
||||
// HACK: <ep> provides no useful information - ExceptionAddress always
|
||||
// points to kernel32!RaiseException. we use debug_get_nth_caller to
|
||||
// determine the real location.
|
||||
|
||||
void* func = debug_get_nth_caller(1, ep->ContextRecord);
|
||||
// skip RaiseException
|
||||
|
||||
char func_name[DBG_SYMBOL_LEN];
|
||||
char file[DBG_FILE_LEN] = {0};
|
||||
int line = 0;
|
||||
(void)debug_resolve_symbol(func, func_name, file, &line);
|
||||
// note: file is the base path only (no drive letter), so there are
|
||||
// no problems with wdbg_exception_filter's "%[^:]" format string.
|
||||
|
||||
// note: keep formatting in sync with wdbg_exception_filter, which
|
||||
// extracts file/line for use with debug_display_error.
|
||||
static wchar_t locus[256];
|
||||
swprintf(locus, ARRAY_SIZE(locus), L"%hs (%hs:%d)", func_name, file, line);
|
||||
return locus;
|
||||
const uint skip = 1; // skip RaiseException
|
||||
void* func_addr = debug_get_nth_caller(skip, ep->ContextRecord);
|
||||
(void)debug_resolve_symbol(func_addr, func, file, line);
|
||||
}
|
||||
|
||||
|
||||
@ -675,7 +664,7 @@ extern void wdbg_write_minidump(EXCEPTION_POINTERS* ep);
|
||||
LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
||||
{
|
||||
// note: we risk infinite recursion if someone raises an SEH exception
|
||||
// from within this function. therefore, abort immediately if we've
|
||||
// from within this function. therefore, abort immediately if we have
|
||||
// already been called; the first error is the most important, anyway.
|
||||
static uintptr_t already_crashed = 0;
|
||||
if(!CAS(&already_crashed, 0, 1))
|
||||
@ -689,13 +678,10 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
||||
|
||||
// extract details from ExceptionRecord.
|
||||
const wchar_t* description = get_exception_description(ep);
|
||||
const wchar_t* locus = get_exception_locus (ep);
|
||||
|
||||
wchar_t fmt[50]; wchar_t func_name[DBG_SYMBOL_LEN] = L"?"; char file[DBG_FILE_LEN] = "?"; int line = 0;
|
||||
swprintf(fmt, ARRAY_SIZE(fmt), L"%%%ds (%%%dh[^:]:%%d)", DBG_SYMBOL_LEN, DBG_FILE_LEN);
|
||||
// bake in the string limits (future-proof)
|
||||
(void)swscanf(locus, fmt, func_name, file, &line);
|
||||
// don't care whether all 3 fields were filled (they default to "?")
|
||||
char file[DBG_FILE_LEN] = {0};
|
||||
int line = 0;
|
||||
char func_name[DBG_SYMBOL_LEN] = {0};
|
||||
get_exception_locus(ep, file, &line, func_name);
|
||||
|
||||
// this must happen before the error dialog because user could choose to
|
||||
// exit immediately there.
|
||||
@ -707,13 +693,14 @@ LONG WINAPI wdbg_exception_filter(EXCEPTION_POINTERS* ep)
|
||||
L"\r\n"
|
||||
L"Please let us know at http://bugs.wildfiregames.com/ and attach the crashlog.txt and crashlog.dmp files.\r\n"
|
||||
L"\r\n"
|
||||
L"Details: unhandled exception (%s at %s)\r\n";
|
||||
swprintf(buf, ARRAY_SIZE(buf), msg_fmt, description, locus);
|
||||
int flags = 0;
|
||||
L"Details: unhandled exception (%s)\r\n";
|
||||
swprintf(buf, ARRAY_SIZE(buf), msg_fmt, description);
|
||||
uint flags = 0;
|
||||
|
||||
if(ep->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
|
||||
flags = DE_NO_CONTINUE;
|
||||
ErrorReaction er = debug_display_error(buf, flags, 1,ep->ContextRecord, file,line);
|
||||
debug_assert(er > 0);
|
||||
ErrorReaction er = debug_display_error(buf, flags, 1,ep->ContextRecord, file,line,func_name, NULL);
|
||||
debug_assert(er == ER_CONTINUE); // nothing else possible
|
||||
|
||||
// invoke the Win32 default handler - it calls ExitProcess for
|
||||
// most exception types.
|
||||
|
@ -1458,7 +1458,7 @@ static LibError udt_dump_std(const wchar_t* wtype_name, const u8* p, size_t size
|
||||
size_t el_count;
|
||||
DebugIterator el_iterator;
|
||||
u8 it_mem[DEBUG_STL_MAX_ITERATOR_SIZE];
|
||||
err = stl_get_container_info(ctype_name, p, size, el_size, &el_count, &el_iterator, it_mem);
|
||||
err = debug_stl_get_container_info(ctype_name, p, size, el_size, &el_count, &el_iterator, it_mem);
|
||||
if(err != INFO_OK)
|
||||
goto not_valid_container;
|
||||
return dump_sequence(el_iterator, it_mem, el_count, el_type_id, el_size, state);
|
||||
@ -1485,7 +1485,7 @@ not_valid_container:
|
||||
snprintf(buf, ARRAY_SIZE(buf), "error %d while analyzing ", err);
|
||||
text = buf;
|
||||
}
|
||||
out(L"(%hs%hs)", text, stl_simplify_name(ctype_name));
|
||||
out(L"(%hs%hs)", text, debug_stl_simplify_name(ctype_name));
|
||||
return INFO_OK;
|
||||
}
|
||||
|
||||
@ -1871,16 +1871,6 @@ static LibError dump_frame_cb(const STACKFRAME64* sf, void* UNUSED(user_arg))
|
||||
// cluttering up the trace. returns the buffer for convenience.
|
||||
const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* pcontext)
|
||||
{
|
||||
static uintptr_t already_in_progress;
|
||||
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 debug_assert/debug_warn/CHECK_ERR fired during the current trace.)"
|
||||
);
|
||||
return buf;
|
||||
}
|
||||
|
||||
if(!pcontext)
|
||||
skip++; // skip this frame
|
||||
|
||||
@ -1895,7 +1885,6 @@ const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void*
|
||||
|
||||
unlock();
|
||||
|
||||
already_in_progress = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@
|
||||
// RAGE! Win32 OpenGL headers are full of crap we have to emulate
|
||||
// (must not include windows.h)
|
||||
|
||||
#if !OS_WIN
|
||||
#error "wgl.h: do not include if not compiling for Windows"
|
||||
#ifndef WGL_HEADER_NEEDED
|
||||
#error "wgl.h: why is this included from anywhere but ogl.h?"
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#error "win_internal.h: do not include if not compiling for Windows"
|
||||
#endif
|
||||
|
||||
#include "lib/lib.h" // BIT
|
||||
#include "lib/types.h" // intptr_t
|
||||
|
||||
|
||||
@ -374,7 +375,6 @@ extern int mainCRTStartup(void);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define BIT(n) (1ul << (n))
|
||||
#define FD_READ BIT(0)
|
||||
#define FD_WRITE BIT(1)
|
||||
#define FD_ACCEPT BIT(3)
|
||||
@ -382,8 +382,6 @@ extern int mainCRTStartup(void);
|
||||
#define FD_CLOSE BIT(5)
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// locking
|
||||
//
|
||||
|
@ -190,7 +190,7 @@ static void dlg_resize(HWND hDlg, WPARAM wParam, LPARAM lParam)
|
||||
struct DialogParams
|
||||
{
|
||||
const wchar_t* text;
|
||||
int flags;
|
||||
uint flags;
|
||||
};
|
||||
|
||||
|
||||
@ -300,12 +300,12 @@ static INT_PTR CALLBACK error_dialog_proc(HWND hDlg, unsigned int msg, WPARAM wP
|
||||
|
||||
// show error dialog with the given text and return user's reaction.
|
||||
// exits directly if 'exit' is clicked.
|
||||
ErrorReaction sys_display_error(const wchar_t* text, int flags)
|
||||
ErrorReaction sys_display_error(const wchar_t* text, uint flags)
|
||||
{
|
||||
// note: other threads might still be running, crash and take down the
|
||||
// process before we have a chance to display this error message.
|
||||
// ideally we would suspend them all and resume when finished; however,
|
||||
// they may be holding systemwide locks (e.g. heap or loader) that
|
||||
// they may be holding system-wide locks (e.g. heap or loader) that
|
||||
// are potentially needed by DialogBoxParam. in that case, deadlock
|
||||
// would result; this is much worse than a crash because no error
|
||||
// at all is displayed to the end-user. therefore, do nothing here.
|
||||
|
@ -22,19 +22,19 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
|
||||
#include "win_internal.h"
|
||||
#include <process.h> // _beginthreadex
|
||||
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "lib/adts.h"
|
||||
#include "lib/sysdep/ia32.h"
|
||||
|
||||
#include "win_internal.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <process.h> // _beginthreadex
|
||||
#include <time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include "lib/sysdep/cpu.h"
|
||||
|
||||
|
||||
// define to disable time sources (useful for simulating other systems)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "lib/lockfree.h"
|
||||
#include "lib/sysdep/cpu.h" // atomic_add
|
||||
#include "lib/timer.h"
|
||||
|
||||
// make sure the data structures work at all; doesn't test thread-safety.
|
||||
|
@ -23,16 +23,16 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "timer.h"
|
||||
#include "adts.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "lib/sysdep/ia32.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
|
||||
// rationale for wrapping gettimeofday and clock_gettime, instead of emulating
|
||||
// them where not available: allows returning higher-resolution timer values
|
||||
|
@ -5,9 +5,7 @@
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/input.h"
|
||||
#if CPU_IA32
|
||||
# include "lib/sysdep/ia32.h"
|
||||
#endif
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "lib/sysdep/gfx.h"
|
||||
#include "lib/res/res.h"
|
||||
#include "lib/res/file/trace.h"
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "lib/timer.h"
|
||||
#include "lib/sysdep/gfx.h"
|
||||
#include "lib/sysdep/snd.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "lib/res/res.h"
|
||||
#include "lib/res/graphics/tex.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user