1
0
forked from mirrors/0ad

fixes to stack trace code under x64 (from work):

- displaying symbol values requires stack pointer, not frame pointer
- update debug_IsPointerBogus
- skip-n-frames code changed to skip-all-frames-up-to-func (more
reliable in the face of inlining)

debug.cpp: cleanup

This was SVN commit r6410.
This commit is contained in:
janwas
2008-09-27 10:05:11 +00:00
parent 699ab8865f
commit aea2368300
34 changed files with 296 additions and 342 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ static LibError validate_da(DynArray* da)
const int prot = da->prot;
// note: this happens if max_size == 0
// if(debug_is_pointer_bogus(base))
// if(debug_IsPointerBogus(base))
// WARN_RETURN(ERR::_1);
// note: don't check if base is page-aligned -
// might not be true for 'wrapped' mem regions.
@@ -23,7 +23,7 @@ public:
u8 buf[4];
TS_ASSERT_OK(da_read(&da, buf, 4));
TS_ASSERT_EQUALS(read_le32(buf), 0x78563412); // read correct value
debug_skip_next_err(ERR::FAIL);
debug_SkipNextError(ERR::FAIL);
TS_ASSERT(da_read(&da, buf, 1) < 0); // no more data left
TS_ASSERT_OK(da_free(&da));
}
+102 -109
View File
@@ -47,24 +47,24 @@ wchar_t* debug_log_pos = debug_log;
// write to memory buffer (fast)
void debug_wprintf_mem(const wchar_t* fmt, ...)
{
const ssize_t chars_left = (ssize_t)LOG_CHARS - (debug_log_pos-debug_log);
debug_assert(chars_left >= 0);
const ssize_t charsLeft = (ssize_t)LOG_CHARS - (debug_log_pos-debug_log);
debug_assert(charsLeft >= 0);
// potentially not enough room for the new string; throw away the
// older half of the log. we still protect against overflow below.
if(chars_left < 512)
if(charsLeft < 512)
{
const size_t copy_size = sizeof(wchar_t) * LOG_CHARS/2;
const size_t copySize = sizeof(wchar_t) * LOG_CHARS/2;
wchar_t* const middle = &debug_log[LOG_CHARS/2];
cpu_memcpy(debug_log, middle, copy_size);
memset(middle, 0, copy_size);
cpu_memcpy(debug_log, middle, copySize);
memset(middle, 0, copySize);
debug_log_pos -= LOG_CHARS/2; // don't assign middle (may leave gap)
}
// write into buffer (in-place)
va_list args;
va_start(args, fmt);
int len = vswprintf(debug_log_pos, chars_left-2, fmt, args);
int len = vswprintf(debug_log_pos, charsLeft-2, fmt, args);
va_end(args);
debug_log_pos += len+2;
@@ -194,22 +194,22 @@ void debug_printf(const wchar_t* fmt, ...)
//-----------------------------------------------------------------------------
LibError debug_write_crashlog(const wchar_t* text)
LibError debug_WriteCrashlog(const wchar_t* text)
{
// avoid potential infinite loop if an error occurs here.
static uintptr_t in_progress;
if(!cpu_CAS(&in_progress, 0, 1))
static uintptr_t isBusy;
if(!cpu_CAS(&isBusy, 0, 1))
return ERR::REENTERED; // NOWARN
OsPath path = OsPath(ah_get_log_dir())/"crashlog.txt";
FILE* f = fopen(path.string().c_str(), "w");
if(!f)
{
in_progress = 0;
isBusy = 0;
WARN_RETURN(ERR::FAIL);
}
fputwc(0xfeff, f); // BOM
fputwc(0xFEFF, f); // BOM
fwprintf(f, L"%ls\n", text);
fwprintf(f, L"\n\n====================================\n\n");
@@ -219,7 +219,7 @@ LibError debug_write_crashlog(const wchar_t* text)
fwprintf(f, L"Last known activity:\n\n %ls\n", debug_log);
fclose(f);
in_progress = 0;
isBusy = 0;
return INFO::OK;
}
@@ -229,10 +229,10 @@ LibError debug_write_crashlog(const wchar_t* text)
//-----------------------------------------------------------------------------
// translates and displays the given strings in a dialog.
// this is typically only used when debug_display_error has failed or
// this is typically only used when debug_DisplayError has failed or
// is unavailable because that function is much more capable.
// implemented via sys_display_msg; see documentation there.
void debug_display_msgw(const wchar_t* caption, const wchar_t* msg)
void debug_DisplayMessage(const wchar_t* caption, const wchar_t* msg)
{
sys_display_msg(ah_translate(caption), ah_translate(msg));
}
@@ -242,11 +242,11 @@ void debug_display_msgw(const wchar_t* caption, const wchar_t* msg)
// 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;
static bool isExiting;
// 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)
static bool ShouldSuppressError(u8* suppress)
{
if(!suppress)
return false;
@@ -254,7 +254,7 @@ static bool should_suppress_error(u8* suppress)
if(*suppress == DEBUG_SUPPRESS)
return true;
if(exit_requested)
if(isExiting)
return true;
return false;
@@ -263,68 +263,67 @@ static bool should_suppress_error(u8* suppress)
// (NB: this may appear obscene, but deep stack traces have been
// observed to take up > 256 KiB)
static const size_t message_size_bytes = 512*KiB;
static const size_t messageSize = 512*KiB;
void debug_error_message_free(ErrorMessageMem* emm)
void debug_FreeErrorMessage(ErrorMessageMem* emm)
{
page_aligned_free(emm->pa_mem, message_size_bytes);
page_aligned_free(emm->pa_mem, messageSize);
}
// split out of debug_display_error because it's used by the self-test.
const wchar_t* debug_error_message_build(
// split out of debug_DisplayError because it's used by the self-test.
const wchar_t* debug_BuildErrorMessage(
const wchar_t* description,
const char* fn_only, int line, const char* func,
size_t skip, void* context,
const char* filename, int line, const char* func,
void* context, const char* lastFuncToSkip,
ErrorMessageMem* emm)
{
// rationale: see ErrorMessageMem
emm->pa_mem = page_aligned_alloc(message_size_bytes);
emm->pa_mem = page_aligned_alloc(messageSize);
if(!emm->pa_mem)
return L"(insufficient memory to generate error message)";
wchar_t* const buf = (wchar_t*)emm->pa_mem;
const size_t max_chars = message_size_bytes / sizeof(wchar_t);
wchar_t* pos = buf; size_t chars_left = max_chars; int len;
const size_t maxChars = messageSize / sizeof(wchar_t);
wchar_t* pos = buf; size_t charsLeft = maxChars; int len;
// header
len = swprintf(pos, chars_left,
len = swprintf(pos, charsLeft,
L"%ls\r\n"
L"Location: %hs:%d (%hs)\r\n"
L"\r\n"
L"Call stack:\r\n"
L"\r\n",
description, fn_only, line, func);
description, filename, line, func);
if(len < 0)
{
fail:
return L"(error while formatting error message)";
}
pos += len; chars_left -= len;
pos += len; charsLeft -= len;
// append stack trace
if(!context)
skip += 2; // skip debug_error_message_build and debug_display_error
LibError ret = debug_dump_stack(pos, chars_left, skip, context);
LibError ret = debug_DumpStack(pos, charsLeft, context, lastFuncToSkip);
if(ret == ERR::REENTERED)
{
len = swprintf(pos, chars_left,
len = swprintf(pos, charsLeft,
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.)"
);
if(len < 0) goto fail; pos += len; chars_left -= len;
if(len < 0) goto fail; pos += len; charsLeft -= len;
}
else if(ret != INFO::OK)
{
char description_buf[100] = {'?'};
len = swprintf(pos, chars_left,
len = swprintf(pos, charsLeft,
L"(error while dumping stack: %hs)",
error_description_r(ret, description_buf, ARRAY_SIZE(description_buf))
);
if(len < 0) goto fail; pos += len; chars_left -= len;
if(len < 0) goto fail; pos += len; charsLeft -= len;
}
else // success
{
len = (int)wcslen(buf);
pos = buf+len; chars_left = max_chars-len;
pos = buf+len; charsLeft = maxChars-len;
}
// append OS error (just in case it happens to be relevant -
@@ -335,18 +334,18 @@ fail:
error_description_r(errno_equiv, description_buf, ARRAY_SIZE(description_buf));
char os_error[100] = "?";
sys_error_description_r(0, os_error, ARRAY_SIZE(os_error));
len = swprintf(pos, chars_left,
len = swprintf(pos, charsLeft,
L"\r\n"
L"errno = %d (%hs)\r\n"
L"OS error = %hs\r\n",
errno, description_buf, os_error
);
if(len < 0) goto fail; pos += len; chars_left -= len;
if(len < 0) goto fail; pos += len; charsLeft -= len;
return buf;
}
static ErrorReaction call_display_error(const wchar_t* text, size_t flags)
static ErrorReaction CallDisplayError(const wchar_t* text, size_t flags)
{
// first try app hook implementation
ErrorReaction er = ah_display_error(text, flags);
@@ -357,16 +356,16 @@ static ErrorReaction call_display_error(const wchar_t* text, size_t flags)
return er;
}
static ErrorReaction carry_out_ErrorReaction(ErrorReaction er, size_t flags, u8* suppress)
static ErrorReaction PerformErrorReaction(ErrorReaction er, size_t flags, u8* suppress)
{
const bool manual_break = (flags & DE_MANUAL_BREAK) != 0;
const bool shouldHandleBreak = (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)
if(shouldHandleBreak)
{
debug_break();
er = ER_CONTINUE;
@@ -379,7 +378,7 @@ static ErrorReaction carry_out_ErrorReaction(ErrorReaction er, size_t flags, u8*
break;
case ER_EXIT:
exit_requested = true; // see declaration
isExiting = true; // see declaration
#if OS_WIN
// prevent (slow) heap reporting since we're exiting abnormally and
@@ -393,13 +392,13 @@ static ErrorReaction carry_out_ErrorReaction(ErrorReaction er, size_t flags, u8*
return er;
}
ErrorReaction debug_display_error(const wchar_t* description,
size_t flags, size_t skip, void* context,
const char* file, int line, const char* func,
ErrorReaction debug_DisplayError(const wchar_t* description,
size_t flags, void* context, const char* lastFuncToSkip,
const char* pathname, int line, const char* func,
u8* suppress)
{
// "suppressing" this error means doing nothing and returning ER_CONTINUE.
if(should_suppress_error(suppress))
if(ShouldSuppressError(suppress))
return ER_CONTINUE;
// fix up params
@@ -410,105 +409,99 @@ ErrorReaction debug_display_error(const wchar_t* description,
if(suppress)
flags |= DE_ALLOW_SUPPRESS;
// .. deal with incomplete file/line info
if(!file || file[0] == '\0')
file = "unknown";
if(!pathname || pathname[0] == '\0')
pathname = "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);
const char* filename = path_name_only(pathname);
// display in output window; double-click will navigate to error location.
debug_printf("%s(%d): %ls\n", fn_only, line, description);
debug_printf("%s(%d): %ls\n", filename, line, description);
ErrorMessageMem emm;
const wchar_t* text = debug_error_message_build(description,
fn_only, line, func, skip, context, &emm);
const wchar_t* text = debug_BuildErrorMessage(description, filename, line, func, context, lastFuncToSkip, &emm);
debug_write_crashlog(text);
ErrorReaction er = call_display_error(text, flags);
debug_WriteCrashlog(text);
ErrorReaction er = CallDisplayError(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
// running is no longer necessary. debug_DisplayError now determines our
// window handle and is modal.
// must happen before carry_out_ErrorReaction because that may exit.
debug_error_message_free(&emm);
// must happen before PerformErrorReaction because that may exit.
debug_FreeErrorMessage(&emm);
return carry_out_ErrorReaction(er, flags, suppress);
return PerformErrorReaction(er, flags, suppress);
}
// strobe indicating expected_err is valid and the next error should be
// strobe indicating expectedError is valid and the next error should be
// compared against that / skipped if equal to it.
// set/reset via cpu_CAS for thread-safety (hence uintptr_t).
static uintptr_t expected_err_valid;
static LibError expected_err;
static uintptr_t isExpectedErrorValid;
static LibError expectedError;
void debug_skip_next_err(LibError err)
void debug_SkipNextError(LibError err)
{
if(cpu_CAS(&expected_err_valid, 0, 1))
expected_err = err;
if(cpu_CAS(&isExpectedErrorValid, 0, 1))
expectedError = err;
else
debug_assert(0); // internal error: concurrent attempt to skip assert/error
}
static bool should_skip_this_error(LibError err)
static bool ShouldSkipThisError(LibError err)
{
// (compare before resetting strobe - expected_err may change afterwards)
bool was_expected_err = (expected_err == err);
// (compare before resetting strobe - expectedError may change afterwards)
bool isExpected = (expectedError == err);
// (use cpu_CAS to ensure only one error is skipped)
if(cpu_CAS(&expected_err_valid, 1, 0))
if(cpu_CAS(&isExpectedErrorValid, 1, 0))
{
debug_assert(was_expected_err);
return was_expected_err;
debug_assert(isExpected);
return isExpected;
}
return false;
}
// to share code between assert and error skip mechanism, we treat the former as
// an error. choose the code such that no one would want to warn of it.
static const LibError assert_err = INFO::OK;
void debug_skip_next_assert()
ErrorReaction debug_OnError(LibError err, u8* suppress, const char* file, int line, const char* func)
{
debug_skip_next_err(assert_err);
}
static bool should_skip_this_assert()
{
return should_skip_this_error(assert_err);
}
ErrorReaction debug_assert_failed(const char* expr, u8* suppress,
const char* file, int line, const char* func)
{
if(should_skip_this_assert())
return ER_CONTINUE;
size_t 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)
{
if(should_skip_this_error(err))
if(ShouldSkipThisError(err))
return ER_CONTINUE;
size_t skip = 1; void* context = 0;
void* context = 0; const char* lastFuncToSkip = __func__;
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: return value was %d (%hs)", err, err_buf);
return debug_display_error(buf, DE_MANUAL_BREAK, skip,context, file,line,func, suppress);
return debug_DisplayError(buf, DE_MANUAL_BREAK, context, lastFuncToSkip, file,line,func, suppress);
}
void debug_SkipNextAssertion()
{
// to share code between assert and error skip mechanism, we treat the
// former as an error.
debug_SkipNextError(ERR::ASSERTION_FAILED);
}
static bool ShouldSkipThisAssertion()
{
return ShouldSkipThisError(ERR::ASSERTION_FAILED);
}
ErrorReaction debug_OnAssertionFailure(const char* expr, u8* suppress, const char* file, int line, const char* func)
{
if(ShouldSkipThisAssertion())
return ER_CONTINUE;
void* context = 0; const char* lastFuncToSkip = __func__;
wchar_t buf[400];
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed: \"%hs\"", expr);
return debug_DisplayError(buf, DE_MANUAL_BREAK, context, lastFuncToSkip, file,line,func, suppress);
}
+52 -65
View File
@@ -50,22 +50,22 @@ LIB_API void debug_printf(const wchar_t* fmt, ...);
/**
* translates and displays the given strings in a dialog.
* this is typically only used when debug_display_error has failed or
* this is typically only used when debug_DisplayError has failed or
* is unavailable because that function is much more capable.
* implemented via sys_display_msg; see documentation there.
**/
LIB_API void debug_display_msgw(const wchar_t* caption, const wchar_t* msg);
LIB_API void debug_DisplayMessage(const wchar_t* caption, const wchar_t* msg);
/// flags to customize debug_display_error behavior
/// flags to customize debug_DisplayError behavior
enum DebugDisplayErrorFlags
{
/**
* disallow the Continue button. used e.g. if an exception is fatal.
**/
DE_NO_CONTINUE = 1,
DE_NO_CONTINUE = 1,
/**
* enable the Suppress button. set automatically by debug_display_error if
* enable the Suppress button. set automatically by debug_DisplayError 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
@@ -75,15 +75,15 @@ enum DebugDisplayErrorFlags
DE_ALLOW_SUPPRESS = 2,
/**
* do not trigger a breakpoint inside debug_display_error; caller
* do not trigger a breakpoint inside debug_DisplayError; caller
* will take care of this if ER_BREAK is returned. this is so that the
* debugger can jump directly into the offending function.
**/
DE_MANUAL_BREAK = 4
DE_MANUAL_BREAK = 4
};
/**
* value for suppress flag once set by debug_display_error.
* value for suppress flag once set by debug_DisplayError.
* rationale: this value is fairly distinctive and helps when
* debugging the symbol engine.
* initial value is 0 rather that another constant; this avoids
@@ -106,20 +106,20 @@ enum ErrorReaction
/**
* trigger breakpoint, i.e. enter debugger.
* only returned if DE_MANUAL_BREAK was passed; otherwise,
* debug_display_error will trigger a breakpoint itself.
* debug_DisplayError 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.
* acted on by debug_DisplayError; never returned to caller.
**/
ER_SUPPRESS,
/**
* exit the program immediately.
* acted on by debug_display_error; never returned to caller.
* acted on by debug_DisplayError; never returned to caller.
**/
ER_EXIT,
@@ -127,7 +127,7 @@ enum ErrorReaction
* 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.
* acted on by debug_DisplayError; never returned to caller.
**/
ER_NOT_IMPLEMENTED
};
@@ -137,7 +137,7 @@ enum ErrorReaction
*
* @param description text to show.
* @param flags: see DebugDisplayErrorFlags.
* @param context, skip: see debug_dump_stack.
* @param context, lastFuncToSkip: see debug_DumpStack.
* @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
@@ -147,16 +147,13 @@ enum ErrorReaction
* provides the storage. values: see DEBUG_SUPPRESS above.
* @return ErrorReaction (user's choice: continue running or stop?)
**/
LIB_API ErrorReaction debug_display_error(const wchar_t* description,
size_t flags, size_t skip, void* context,
const char* file, int line, const char* func,
u8* suppress);
LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const char* lastFuncToSkip, 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 DEBUG_DISPLAY_ERROR(text) debug_display_error(text, 0, 0,0, __FILE__,__LINE__,__func__, 0)
#define DEBUG_DISPLAY_ERROR(text) debug_DisplayError(text, 0, 0, "debug_DisplayError", __FILE__,__LINE__,__func__, 0)
//
@@ -221,12 +218,12 @@ LIB_API void debug_wprintf_mem(const wchar_t* fmt, ...);
* (in unicode format).
*
* @param text description of the error (including stack trace);
* typically generated by debug_error_message_build.
* typically generated by debug_BuildErrorMessage.
*
* @return LibError; ERR::REENTERED if reentered via recursion or
* multithreading (not allowed since an infinite loop may result).
**/
LIB_API LibError debug_write_crashlog(const wchar_t* text);
LIB_API LibError debug_WriteCrashlog(const wchar_t* text);
//-----------------------------------------------------------------------------
@@ -256,7 +253,7 @@ STMT(\
static u8 suppress__;\
if(!(expr))\
{\
switch(debug_assert_failed(#expr, &suppress__, __FILE__, __LINE__, __func__))\
switch(debug_OnAssertionFailure(#expr, &suppress__, __FILE__, __LINE__, __func__))\
{\
case ER_BREAK:\
debug_break();\
@@ -280,7 +277,7 @@ STMT(\
#define debug_warn(expr) \
STMT(\
static u8 suppress__;\
switch(debug_assert_failed(expr, &suppress__, __FILE__, __LINE__, __func__))\
switch(debug_OnAssertionFailure(expr, &suppress__, __FILE__, __LINE__, __func__))\
{\
case ER_BREAK:\
debug_break();\
@@ -299,7 +296,7 @@ STMT(\
#define DEBUG_WARN_ERR(err)\
STMT(\
static u8 suppress__;\
switch(debug_warn_err(err, &suppress__, __FILE__, __LINE__, __func__))\
switch(debug_OnError(err, &suppress__, __FILE__, __LINE__, __func__))\
{\
case ER_BREAK:\
debug_break();\
@@ -312,32 +309,28 @@ STMT(\
/**
* called when a debug_assert fails;
* notifies the user via debug_display_error.
* notifies the user via debug_DisplayError.
*
* @param assert_expr the expression that failed; typically passed as
* #expr in the assert macro.
* @param suppress see debug_display_error.
* @param suppress see debug_DisplayError.
* @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?)
**/
LIB_API ErrorReaction debug_assert_failed(const char* assert_expr,
u8* suppress,
const char* file, int line, const char* func);
LIB_API ErrorReaction debug_OnAssertionFailure(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.
* notifies the user via debug_DisplayError.
*
* @param err LibError value indicating the error that occurred
* @param suppress see debug_display_error.
* @param suppress see debug_DisplayError.
* @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?)
**/
LIB_API ErrorReaction debug_warn_err(LibError err,
u8* suppress,
const char* file, int line, const char* func);
LIB_API ErrorReaction debug_OnError(LibError err, u8* suppress, const char* file, int line, const char* func);
/**
@@ -356,14 +349,14 @@ LIB_API ErrorReaction debug_warn_err(LibError err,
* note: this is thread-safe, but to prevent confusion, only one
* concurrent skip request is allowed.
*/
LIB_API void debug_skip_next_err(LibError err);
LIB_API void debug_SkipNextError(LibError err);
/**
* same as debug_skip_next_err, but for asserts.
* same as debug_SkipNextError, but for asserts.
* note that this is implemented in terms of it, so only one assert or
* error skip request may be active at a time.
*/
LIB_API void debug_skip_assert();
LIB_API void debug_SkipNextAssertion();
//-----------------------------------------------------------------------------
@@ -397,7 +390,7 @@ namespace INFO
/**
* maximum number of characters (including trailing \0) written to
* user's buffers by debug_resolve_symbol.
* user's buffers by debug_ResolveSymbol.
**/
const size_t DBG_SYMBOL_LEN = 1000;
const size_t DBG_FILE_LEN = 100;
@@ -420,7 +413,7 @@ const size_t DBG_FILE_LEN = 100;
* @return LibError; INFO::OK iff any information was successfully
* retrieved and stored.
**/
LIB_API LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line);
LIB_API LibError debug_ResolveSymbol(void* ptr_of_interest, char* sym_name, char* file, int* line);
/**
* write a complete stack trace (including values of local variables) into
@@ -428,16 +421,19 @@ LIB_API LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, cha
*
* @param buf target buffer
* @param max_chars of buffer (should be several thousand)
* @param skip number of stack frames (i.e. functions on call stack) to skip.
* this prevents error-reporting functions like debug_assert_failed from
* cluttering up the trace.
* @param context platform-specific representation of execution state
* (e.g. Win32 CONTEXT). if not NULL, tracing starts there; this is useful
* for exceptions. otherwise, tracing starts from the current call stack.
* @param lastFuncToSkip is used for omitting error-reporting functions like
* debug_OnAssertionFailure from the stack trace. it is either 0 (skip nothing) or
* a substring of a function's name (this allows platform-independent
* matching of stdcall-decorated names).
* rationale: this is safer than specifying a fixed number of frames,
* which can be incorrect due to inlining.
* @return LibError; ERR::REENTERED if reentered via recursion or
* multithreading (not allowed since static data is used).
**/
LIB_API LibError debug_dump_stack(wchar_t* buf, size_t max_chars, size_t skip, void* context);
LIB_API LibError debug_DumpStack(wchar_t* buf, size_t maxChars, void* context, const char* lastFuncToSkip);
//-----------------------------------------------------------------------------
@@ -452,17 +448,12 @@ LIB_API LibError debug_dump_stack(wchar_t* buf, size_t max_chars, size_t skip, v
LIB_API void debug_puts(const char* text);
/**
* return address of the Nth function on the call stack.
* return the caller of a certain function on the call stack.
*
* @param skip number of stack frames (i.e. functions on call stack) to skip.
* @param context platform-specific representation of execution state
* (e.g. Win32 CONTEXT). if not NULL, tracing starts there; this is useful
* for exceptions. otherwise, tracing starts from the current call stack.
* @return address of Nth function
*
* note: this does not access debug symbols and is therefore quite fast.
* @param context, lastFuncToSkip - see debug_DumpStack
* @return address of the caller
**/
LIB_API void* debug_get_nth_caller(size_t skip, void* context);
LIB_API void* debug_GetCaller(void* context, const char* lastFuncToSkip);
/**
* check if a pointer appears to be totally invalid.
@@ -473,13 +464,13 @@ LIB_API void* debug_get_nth_caller(size_t skip, void* context);
* @param p pointer
* @return 1 if totally bogus, otherwise 0.
**/
LIB_API int debug_is_pointer_bogus(const void* p);
LIB_API int debug_IsPointerBogus(const void* p);
/// does the given pointer appear to point to code?
LIB_API bool debug_is_code_ptr(void* p);
LIB_API bool debug_IsCodePointer(void* p);
/// does the given pointer appear to point to the stack?
LIB_API bool debug_is_stack_ptr(void* p);
LIB_API bool debug_IsStackPointer(void* p);
/**
@@ -488,7 +479,7 @@ LIB_API bool debug_is_stack_ptr(void* p);
* (threads are easier to keep apart when they are identified by
* name rather than TID.)
**/
LIB_API void debug_set_thread_name(const char* name);
LIB_API void debug_SetThreadName(const char* name);
/**
@@ -498,7 +489,7 @@ struct ErrorMessageMem
{
// rationale:
// - error messages with stack traces require a good deal of memory
// (dozens of KB). static buffers of that size are undesirable.
// (hundreds of KB). static buffers of that size are undesirable.
// - the heap may be corrupted, so don't use malloc. allocator.h's
// page_aligned_malloc (implemented via mmap) should be safe.
// - alloca is a bit iffy (the stack may be maxed out), non-portable and
@@ -513,27 +504,23 @@ struct ErrorMessageMem
*
* @param ErrorMessageMem*
**/
LIB_API void debug_error_message_free(ErrorMessageMem* emm);
LIB_API void debug_FreeErrorMessage(ErrorMessageMem* emm);
/**
* build a string describing the given error.
*
* this is a helper function used by debug_dump_stack and is made available
* this is a helper function used by debug_DumpStack and is made available
* so that the self-test doesn't have to display the error dialog.
*
* @param description: general description of the problem.
* @param fn_only filename (no path) of source file that triggered the error.
* @param line, func: exact position of the error.
* @param skip, context: see debug_dump_stack.
* @param context, lastFuncToSkip: see debug_DumpStack.
* @param emm memory for the error message. caller should allocate
* stack memory and set alloc_buf*; if not, there will be no
* fallback in case heap alloc fails. should be freed via
* debug_error_message_free when no longer needed.
* debug_FreeErrorMessage when no longer needed.
**/
LIB_API const wchar_t* debug_error_message_build(
const wchar_t* description,
const char* fn_only, int line, const char* func,
size_t skip, void* context,
ErrorMessageMem* emm);
LIB_API const wchar_t* debug_BuildErrorMessage(const wchar_t* description, const char* fn_only, int line, const char* func, void* context, const char* lastFuncToSkip, ErrorMessageMem* emm);
#endif // #ifndef INCLUDED_DEBUG
+1 -1
View File
@@ -538,7 +538,7 @@ static bool IsContainerValid(const T& t, size_t el_count)
{
// valid pointer
const u8* front = (const u8*)&*t.begin(); // (note: map doesn't have front)
if(debug_is_pointer_bogus(front))
if(debug_IsPointerBogus(front))
return false;
// note: don't test back() because that depends on el_size and
+1
View File
@@ -140,6 +140,7 @@ ERROR_ASSOCIATE(ERR::LOGIC, "Logic error in code", -1);
ERROR_ASSOCIATE(ERR::TIMED_OUT, "Timed out", -1);
ERROR_ASSOCIATE(ERR::REENTERED, "Single-call function was reentered", -1);
ERROR_ASSOCIATE(ERR::CORRUPTED, "File/memory data is corrupted", -1);
ERROR_ASSOCIATE(ERR::ASSERTION_FAILED, "Assertion failed", -1);
ERROR_ASSOCIATE(ERR::INVALID_PARAM, "Invalid function argument", EINVAL);
ERROR_ASSOCIATE(ERR::INVALID_HANDLE, "Invalid Handle (argument)", -1);
+1
View File
@@ -384,6 +384,7 @@ const LibError LOGIC = -100010;
const LibError TIMED_OUT = -100011;
const LibError REENTERED = -100012;
const LibError CORRUPTED = -100013;
const LibError ASSERTION_FAILED = -100014;
// function arguments
const LibError INVALID_PARAM = -100020;
+1 -1
View File
@@ -167,7 +167,7 @@ static LibError UniFont_validate(const UniFont* f)
{
if(f->ht < 0)
WARN_RETURN(ERR::_1);
if(debug_is_pointer_bogus(f->glyphs_id) || debug_is_pointer_bogus(f->glyphs_size))
if(debug_IsPointerBogus(f->glyphs_id) || debug_IsPointerBogus(f->glyphs_size))
WARN_RETURN(ERR::_2);
// <LineSpacing> and <Height> are read directly from font file.
// negative values don't make sense, but that's all we can check.
+1 -1
View File
@@ -178,7 +178,7 @@ called automatically when the Handle is dereferenced or freed.
static LibError Type_validate(const Res1* r);
{
const int permissible_flags = 0x01;
if(debug_is_pointer_bogus(r->data))
if(debug_IsPointerBogus(r->data))
WARN_RETURN(ERR::_1);
if(r->flags & ~permissible_flags)
WARN_RETURN(ERR::_2);
+1 -1
View File
@@ -15,7 +15,7 @@
#include "self_test.h"
#include "timer.h"
// checked by debug_assert_failed; disables asserts if true (see above).
// checked by debug_OnAssertionFailure; disables asserts if true (see above).
// set/cleared by self_test_run.
bool self_test_active = false;
+2 -2
View File
@@ -115,7 +115,7 @@ For further details, see below.
// macro magic (stringize+prepend L) and we already display file+line.
#define TEST(condition) STMT(\
if(!(condition))\
debug_display_error(L"Self-test failed");\
debug_DisplayError(L"Self-test failed");\
)
@@ -158,7 +158,7 @@ extern int self_test_run(void(*func)());
extern int self_test_register(SelfTestRecord* r);
// checked by debug_assert_failed; disables asserts if true (see above).
// checked by debug_OnAssertionFailure; disables asserts if true (see above).
// set/cleared by run_self_test.
extern bool self_test_active;
+2
View File
@@ -49,4 +49,6 @@
# define ARCH_MIPS 0
#endif
#define ARCH_X86_X64 (ARCH_IA32|ARCH_AMD64)
#endif // #ifndef INCLUDED_ARCH
+17 -18
View File
@@ -59,20 +59,19 @@ struct symbol_lookup_context
bool found;
};
void* debug_get_nth_caller(size_t n, void *UNUSED(context))
void* debug_GetCaller(void* UNUSED(context), const char* UNUSED(lastFuncToSkip))
{
// bt[0] == debug_get_nth_caller
// bt[1] == caller of get_nth_caller
// bt[2] == 1:st caller (n==1)
void *bt[n+2];
int bt_size;
bt_size=backtrace(bt, n+2);
assert(bt_size >= (int)(n+2) && "Need at least n+2 frames in get_nth_caller");
return bt[n+1]; // n==1 => bt[2], and so forth
// bt[0] == this function
// bt[1] == our caller
// bt[2] == the first caller they are interested in
// HACK: we currently don't support lastFuncToSkip (would require debug information),
// instead just returning the caller of the function calling us
void *bt[3];
int bt_size = backtrace(bt, 3);
return bt[2];
}
LibError debug_dump_stack(wchar_t* buf, size_t max_chars, size_t skip, void* UNUSED(context))
LibError debug_DumpStack(wchar_t* buf, size_t max_chars, void* UNUSED(context), const char* UNUSED(lastFuncToSkip))
{
++skip; // Skip ourselves too
@@ -86,19 +85,19 @@ LibError debug_dump_stack(wchar_t* buf, size_t max_chars, size_t skip, void* UNU
bt_size=backtrace(bt, ARRAY_SIZE(bt));
// did we get enough backtraced frames?
assert((bt_size >= (int)skip) && "Need at least <skip> frames in the backtrace");
//assert((bt_size >= (int)skip) && "Need at least <skip> frames in the backtrace");
// Assumed max length of a single print-out
static const size_t MAX_OUT_CHARS=1024;
for (size_t i=skip;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
for (size_t i=0;(int)i<bt_size && bufpos+MAX_OUT_CHARS < bufend;i++)
{
char file[DBG_FILE_LEN];
char symbol[DBG_SYMBOL_LEN];
int line;
int len;
if (debug_resolve_symbol(bt[i], symbol, file, &line) == 0)
if (debug_ResolveSymbol(bt[i], symbol, file, &line) == 0)
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x) %hs:%d %hs\n", bt[i], file, line, symbol);
else
len = swprintf(bufpos, MAX_OUT_CHARS, L"(0x%08x)\n", bt[i]);
@@ -217,11 +216,11 @@ void udbg_bfd_init(void)
char symbol[DBG_SYMBOL_LEN];
char file[DBG_FILE_LEN];
int line;
debug_resolve_symbol(debug_get_nth_caller(3), symbol, file, &line);
debug_ResolveSymbol(debug_get_nth_caller(3), symbol, file, &line);
printf("%s (%s:%d)\n", symbol, file, line);
for (int i=0;i<1000000;i++)
{
debug_resolve_symbol(debug_get_nth_caller(1), symbol, file, &line);
debug_ResolveSymbol(debug_get_nth_caller(1), symbol, file, &line);
}
}
#endif
@@ -309,7 +308,7 @@ static LibError debug_resolve_symbol_dladdr(void *ptr, char* sym_name, char* fil
return INFO::OK;
}
LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
LibError debug_ResolveSymbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
{
ONCE(udbg_bfd_init());
@@ -374,7 +373,7 @@ LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file,
return INFO::OK;
}
void debug_set_thread_name(char const* UNUSED(name))
void debug_SetThreadName(char const* UNUSED(name))
{
// Currently unimplemented
}
+4 -4
View File
@@ -6,22 +6,22 @@
#include "lib/sysdep/sysdep.h"
#include "lib/debug.h"
void* debug_get_nth_caller(size_t UNUSED(n), void *UNUSED(context))
void* debug_GetCaller(void* UNUSED(context), const char* UNUSED(lastFuncToSkip))
{
return NULL;
}
LibError debug_dump_stack(wchar_t* UNUSED(buf), size_t UNUSED(max_chars), size_t UNUSED(skip), void* UNUSED(context))
LibError debug_DumpStack(wchar_t* UNUSED(buf), size_t UNUSED(max_chars), size_t UNUSED(skip), void* UNUSED(context))
{
return ERR::NOT_IMPLEMENTED;
}
LibError debug_resolve_symbol(void* UNUSED(ptr_of_interest), char* UNUSED(sym_name), char* UNUSED(file), int* UNUSED(line))
LibError debug_ResolveSymbol(void* UNUSED(ptr_of_interest), char* UNUSED(sym_name), char* UNUSED(file), int* UNUSED(line))
{
return ERR::NOT_IMPLEMENTED;
}
void debug_set_thread_name(char const* UNUSED(name))
void debug_SetThreadName(char const* UNUSED(name))
{
// Currently unimplemented
}
+2 -2
View File
@@ -82,13 +82,13 @@ void debug_puts(const char* text)
// TODO: Do these properly. (I don't know what I'm doing; I just
// know that these functions are required in order to compile...)
int debug_write_crashlog(const char* UNUSED(file), wchar_t* UNUSED(header),
int debug_WriteCrashlog(const char* UNUSED(file), wchar_t* UNUSED(header),
void* UNUSED(context))
{
abort();
}
int debug_is_pointer_bogus(const void* UNUSED(p))
int debug_IsPointerBogus(const void* UNUSED(p))
{
return false;
}
@@ -54,15 +54,15 @@ class TestWdbgSym : public CxxTest::TestSuite
// this test now always runs. therefore, just make sure a decent
// amount of text (not just "(failed)" error messages) was produced.
//
// however, we can't call debug_dump_stack directly because
// however, we can't call debug_DumpStack directly because
// it'd be reentered if an actual error comes up.
// therefore, use debug_display_error with DE_HIDE_DIALOG.
// therefore, use debug_DisplayError with DE_HIDE_DIALOG.
// unfortunately this means we can no longer get at the error text.
// a sanity check of the text length has been added to debug_display_error
// a sanity check of the text length has been added to debug_DisplayError
ErrorMessageMem emm = {0};
const wchar_t* text = debug_error_message_build(L"dummy", 0,0,0, 0,0, &emm);
const wchar_t* text = debug_BuildErrorMessage(L"dummy", 0,0,0, 0,0, &emm);
TS_ASSERT(wcslen(text) > 500);
debug_error_message_free(&emm);
debug_FreeErrorMessage(&emm);
}
// also used by test_stl as an element type
+11 -8
View File
@@ -19,12 +19,15 @@
// return 1 if the pointer appears to be totally bogus, otherwise 0.
// this check is not authoritative (the pointer may be "valid" but incorrect)
// but can be used to filter out obviously wrong values in a portable manner.
int debug_is_pointer_bogus(const void* p)
int debug_IsPointerBogus(const void* p)
{
#if ARCH_IA32
if(p < (void*)0x10000)
return true;
if(p >= (void*)(uintptr_t)0x80000000)
#if ARCH_AMD64
if(p == (const void*)0xCCCCCCCCCCCCCCCCull)
return true;
#elif ARCH_IA32
if(p == (const void*)0xCCCCCCCCul)
return true;
#endif
@@ -40,11 +43,11 @@ int debug_is_pointer_bogus(const void* p)
}
bool debug_is_code_ptr(void* p)
bool debug_IsCodePointer(void* p)
{
uintptr_t addr = (uintptr_t)p;
// totally invalid pointer
if(debug_is_pointer_bogus(p))
if(debug_IsPointerBogus(p))
return false;
// comes before load address
static const HMODULE base = GetModuleHandle(0);
@@ -55,11 +58,11 @@ bool debug_is_code_ptr(void* p)
}
bool debug_is_stack_ptr(void* p)
bool debug_IsStackPointer(void* p)
{
uintptr_t addr = (uintptr_t)p;
// totally invalid pointer
if(debug_is_pointer_bogus(p))
if(debug_IsPointerBogus(p))
return false;
// not aligned
if(addr % sizeof(void*))
@@ -95,7 +98,7 @@ void wdbg_printf(const char* fmt, ...)
// displays instead of just the thread handle.
//
// see "Setting a Thread Name (Unmanaged)": http://msdn2.microsoft.com/en-us/library/xcb2z8hs(vs.71).aspx
void debug_set_thread_name(const char* name)
void debug_SetThreadName(const char* name)
{
// we pass information to the debugger via a special exception it
// swallows. if not running under one, bail now to avoid
+2 -2
View File
@@ -825,7 +825,7 @@ static void PrintCallStack(const uintptr_t* callers, size_t numCallers)
for(size_t i = 0; i < numCallers; i++)
{
char name[DBG_SYMBOL_LEN] = {'\0'}; char file[DBG_FILE_LEN] = {'\0'}; int line = -1;
LibError err = debug_resolve_symbol((void*)callers[i], name, file, &line);
LibError err = debug_ResolveSymbol((void*)callers[i], name, file, &line);
wdbg_printf(" ");
if(err != INFO::OK)
wdbg_printf("(error %d resolving PC=%p) ", err, callers[i]);
@@ -920,7 +920,7 @@ static LibError wdbg_heap_Init()
// load symbol information now (fails if it happens during shutdown)
char name[DBG_SYMBOL_LEN]; char file[DBG_FILE_LEN]; int line;
(void)debug_resolve_symbol(wdbg_heap_Init, name, file, &line);
(void)debug_ResolveSymbol(wdbg_heap_Init, name, file, &line);
int ret = _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, ReportHook);
if(ret == -1)
+1 -1
View File
@@ -24,7 +24,7 @@ LIB_API void wdbg_heap_Enable(bool);
/**
* check heap integrity.
* errors are reported by the CRT or via debug_display_error.
* errors are reported by the CRT or via debug_DisplayError.
* no effect if called between wdbg_heap_Enable(false) and the next
* wdbg_heap_Enable(true).
**/
+40 -73
View File
@@ -180,7 +180,7 @@ static LibError debug_resolve_symbol_lk(void* ptr_of_interest, char* sym_name, c
// sym_name and file must hold at least the number of chars above;
// file is the base name only, not path (see rationale in wdbg_sym).
// the PDB implementation is rather slow (~500µs).
LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
LibError debug_ResolveSymbol(void* ptr_of_interest, char* sym_name, char* file, int* line)
{
WinScopedLock lock(WDBG_SYM_CS);
return debug_resolve_symbol_lk(ptr_of_interest, sym_name, file, line);
@@ -246,11 +246,11 @@ static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
void* prev_fp = (void*)(uintptr_t)sf->AddrFrame .Offset;
void* prev_ip = (void*)(uintptr_t)sf->AddrPC .Offset;
void* prev_ret = (void*)(uintptr_t)sf->AddrReturn.Offset;
if(!debug_is_stack_ptr(prev_fp))
if(!debug_IsStackPointer(prev_fp))
WARN_RETURN(ERR::_11);
if(prev_ip && !debug_is_code_ptr(prev_ip))
if(prev_ip && !debug_IsCodePointer(prev_ip))
WARN_RETURN(ERR::_12);
if(prev_ret && !debug_is_code_ptr(prev_ret))
if(prev_ret && !debug_IsCodePointer(prev_ret))
WARN_RETURN(ERR::_13);
// read stack frame
@@ -258,9 +258,9 @@ static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
void* ret_addr = ((void**)prev_fp)[1];
if(!fp)
return INFO::ALL_COMPLETE;
if(!debug_is_stack_ptr(fp))
if(!debug_IsStackPointer(fp))
WARN_RETURN(ERR::_14);
if(!debug_is_code_ptr(ret_addr))
if(!debug_IsCodePointer(ret_addr))
return ERR::FAIL; // NOWARN (invalid address)
void* target;
@@ -268,7 +268,7 @@ static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
RETURN_ERR(err);
if(target) // were able to determine it from the call instruction
{
if(!debug_is_code_ptr(target))
if(!debug_IsCodePointer(target))
return ERR::FAIL; // NOWARN (invalid address)
}
@@ -282,17 +282,10 @@ static LibError ia32_walk_stack(_tagSTACKFRAME64* sf)
#endif
static void skip_this_frame(size_t& skip, void* context)
{
if(!context)
skip++;
}
typedef VOID (WINAPI *PRtlCaptureContext)(PCONTEXT);
static PRtlCaptureContext s_RtlCaptureContext;
LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip, const CONTEXT* pcontext)
LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, const CONTEXT* pcontext, const char* lastFuncToSkip)
{
// to function properly, StackWalk64 requires a CONTEXT on
// non-x86 systems (documented) or when in release mode (observed).
@@ -306,8 +299,6 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
// .. need to determine context ourselves.
else
{
skip_this_frame(skip, (void*)pcontext);
// there are 4 ways to do so, in order of preference:
// - asm (easy to use but currently only implemented on IA32)
// - RtlCaptureContext (only available on WinXP or above)
@@ -389,14 +380,21 @@ LibError wdbg_sym_WalkStack(StackFrameCallback cb, uintptr_t cbData, size_t skip
// no more frames found - abort. note: also test FP because
// StackWalk64 sometimes erroneously reports success.
void* fp = (void*)(uintptr_t)sf.AddrFrame.Offset;
void* const fp = (void*)(uintptr_t)sf.AddrFrame.Offset;
if(err != INFO::OK || !fp)
return ret;
if(skip)
if(lastFuncToSkip)
{
skip--;
continue;
void* const pc = (void*)(uintptr_t)sf.AddrPC.Offset;
char func[DBG_SYMBOL_LEN];
err = debug_ResolveSymbol(pc, func, 0, 0);
if(err == INFO::OK)
{
if(strstr(func, lastFuncToSkip))
lastFuncToSkip = 0;
continue;
}
}
ret = cb(&sf, cbData);
@@ -428,11 +426,10 @@ static LibError nth_caller_cb(const _tagSTACKFRAME64* sf, uintptr_t cbData)
return INFO::OK;
}
void* debug_get_nth_caller(size_t skip, void* pcontext)
void* debug_GetCaller(void* pcontext, const char* lastFuncToSkip)
{
void* func;
skip_this_frame(skip, pcontext);
LibError ret = wdbg_sym_WalkStack(nth_caller_cb, (uintptr_t)&func, skip, (const CONTEXT*)pcontext);
LibError ret = wdbg_sym_WalkStack(nth_caller_cb, (uintptr_t)&func, (const CONTEXT*)pcontext, lastFuncToSkip);
return (ret == INFO::OK)? func : 0;
}
@@ -626,48 +623,10 @@ static LibError dump_sym(DWORD id, const u8* p, DumpState state);
// all we can do is display FP-relative variables.
enum CV_HREG_e
{
CV_REG_EAX = 17,
CV_REG_ECX = 18,
CV_REG_EDX = 19,
CV_REG_EBX = 20,
CV_REG_ESP = 21,
CV_REG_EBP = 22,
CV_REG_ESI = 23,
CV_REG_EDI = 24
CV_AMD64_RSP = 335
};
#if 0
// (no longer needed - reg was always 0 (unknown), so there's no point in
// cluttering the stack trace with register strings)
static const wchar_t* string_for_register(CV_HREG_e reg)
{
switch(reg)
{
case CV_REG_EAX:
return L"eax";
case CV_REG_ECX:
return L"ecx";
case CV_REG_EDX:
return L"edx";
case CV_REG_EBX:
return L"ebx";
case CV_REG_ESP:
return L"esp";
case CV_REG_EBP:
return L"ebp";
case CV_REG_ESI:
return L"esi";
case CV_REG_EDI:
return L"edi";
default:
{
static wchar_t buf[19];
swprintf(buf, ARRAY_SIZE(buf), L"0x%x", reg);
return buf;
}
}
}
#endif
static void dump_error(LibError err)
{
@@ -876,7 +835,9 @@ static bool IsRelativeToFramePointer(DWORD flags, DWORD reg)
{
if(flags & SYMFLAG_FRAMEREL) // note: this is apparently obsolete
return true;
if(flags & SYMFLAG_REGREL && reg == CV_REG_EBP)
if((flags & SYMFLAG_REGREL) == 0)
return false;
if(reg == CV_REG_EBP || reg == CV_AMD64_RSP)
return true;
return false;
}
@@ -918,9 +879,11 @@ static LibError DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const
uintptr_t addr = sym->Address;
if(IsRelativeToFramePointer(sym->Flags, sym->Register))
{
#if ARCH_AMD64
addr += sf->AddrStack.Offset;
#else
addr += sf->AddrFrame.Offset;
#ifdef NDEBUG
# if defined(NDEBUG)
// NB: the addresses of register-relative symbols are apparently
// incorrect [VC8, 32-bit Wow64]. the problem occurs regardless of
// IA32_STACK_WALK_ENABLED and with both ia32_asm_GetCurrentContext
@@ -931,6 +894,7 @@ static LibError DetermineSymbolAddress(DWORD id, const SYMBOL_INFOW* sym, const
addr += sizeof(void*);
else
addr += sizeof(void*) * 2;
# endif
#endif
}
else if(IsUnretrievable(sym->Flags))
@@ -1289,7 +1253,7 @@ static bool ptr_already_visited(const u8* p)
else
{
// warn user - but only once (we can't use the regular
// debug_display_error and wdbg_assert doesn't have a
// debug_DisplayError and wdbg_assert doesn't have a
// suppress mechanism)
static bool haveComplained;
if(!haveComplained)
@@ -1317,7 +1281,7 @@ static LibError dump_sym_pointer(DWORD type_id, const u8* p, DumpState state)
// bail if it's obvious the pointer is bogus
// (=> can't display what it's pointing to)
if(debug_is_pointer_bogus(p))
if(debug_IsPointerBogus(p))
return INFO::OK;
// avoid duplicates and circular references
@@ -1578,7 +1542,11 @@ static LibError udt_dump_normal(const wchar_t* type_name, const u8* p, size_t si
DWORD ofs = 0;
if(!SymGetTypeInfo(hProcess, mod_base, child_id, TI_GET_OFFSET, &ofs))
continue;
debug_assert(ofs < size);
if(ofs >= size)
{
debug_printf("INVALID_UDT %ws %d %d\n", type_name, ofs, size);
}
//debug_assert(ofs < size);
if(!fits_on_one_line)
INDENT;
@@ -1834,17 +1802,16 @@ static LibError dump_frame_cb(const _tagSTACKFRAME64* sf, uintptr_t UNUSED(cbDat
}
LibError debug_dump_stack(wchar_t* buf, size_t max_chars, size_t skip, void* pcontext)
LibError debug_DumpStack(wchar_t* buf, size_t maxChars, void* pcontext, const char* lastFuncToSkip)
{
static uintptr_t already_in_progress;
if(!cpu_CAS(&already_in_progress, 0, 1))
return ERR::REENTERED; // NOWARN
out_init(buf, max_chars);
out_init(buf, maxChars);
ptr_reset_visited();
skip_this_frame(skip, pcontext);
LibError ret = wdbg_sym_WalkStack(dump_frame_cb, 0, skip, (const CONTEXT*)pcontext);
LibError ret = wdbg_sym_WalkStack(dump_frame_cb, 0, (const CONTEXT*)pcontext, lastFuncToSkip);
already_in_progress = 0;
+1 -1
View File
@@ -226,7 +226,7 @@ static HANDLE hUpdateThread;
static unsigned __stdcall UpdateThread(void* UNUSED(data))
{
debug_set_thread_name("whrt_UpdateThread");
debug_SetThreadName("whrt_UpdateThread");
for(;;)
{
+1 -1
View File
@@ -59,7 +59,7 @@
// defined by winsock2 and also Linux (with different values)
// (values derived from winsock2 WSA* constants minus WSABASEERR)
// update: disabled on newer Boost versions because filesystem drags in boost/cerrno.hpp
#if BOOST_VERSION <= 103401
#if !defined(BOOST_VERSION) || BOOST_VERSION <= 103401
#define EWOULDBLOCK 35
#define EINPROGRESS 36
#define EALREADY 37
+1 -1
View File
@@ -90,7 +90,7 @@ static sem_t exit_flag;
static void* prof_thread_func(void* UNUSED(data))
{
debug_set_thread_name("eip_sampler");
debug_SetThreadName("eip_sampler");
const long _1e6 = 1000000;
const long _1e9 = 1000000000;
+7 -6
View File
@@ -195,17 +195,17 @@ static const wchar_t* GetExceptionDescription(const EXCEPTION_POINTERS* ep,
// return location at which the exception <er> occurred.
// params: see debug_resolve_symbol.
// params: see debug_ResolveSymbol.
static void GetExceptionLocus(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
// points to kernel32!RaiseException. we use debug_GetCaller to
// determine the real location.
const size_t skip = 1; // skip RaiseException
void* func_addr = debug_get_nth_caller(skip, ep->ContextRecord);
(void)debug_resolve_symbol(func_addr, func, file, line);
const char* const lastFuncToSkip = "RaiseException";
void* func_addr = debug_GetCaller(ep->ContextRecord, lastFuncToSkip);
(void)debug_ResolveSymbol(func_addr, func, file, line);
}
@@ -267,7 +267,8 @@ long __stdcall wseh_ExceptionFilter(struct _EXCEPTION_POINTERS* ep)
size_t flags = 0;
if(ep->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
flags = DE_NO_CONTINUE;
ErrorReaction er = debug_display_error(message, flags, 1,ep->ContextRecord, file,line,func, 0);
const char* const lastFuncToSkip = STRINGIZE(DECORATED_NAME(wseh_ExceptionFilter));
ErrorReaction er = debug_DisplayError(message, flags, ep->ContextRecord, lastFuncToSkip, file,line,func, 0);
debug_assert(er == ER_CONTINUE); // nothing else possible
// invoke the Win32 default handler - it calls ExitProcess for
+1 -1
View File
@@ -259,7 +259,7 @@ ErrorReaction sys_display_error(const wchar_t* text, size_t flags)
// failed; warn user and make sure we return an ErrorReaction.
if(ret == 0 || ret == -1)
{
debug_display_msgw(L"Error", L"Unable to display detailed error dialog.");
debug_DisplayMessage(L"Error", L"Unable to display detailed error dialog.");
return ER_CONTINUE;
}
return (ErrorReaction)ret;
+2 -2
View File
@@ -27,7 +27,7 @@
* @param msg message contents
*
* implemented as a MessageBox on Win32 and printf on Unix.
* called from debug_display_msgw.
* called from debug_DisplayMessage.
**/
extern void sys_display_msg(const wchar_t* caption, const wchar_t* msg);
@@ -38,7 +38,7 @@ extern void sys_display_msg(const wchar_t* caption, const wchar_t* msg);
* @param flags: see DebugDisplayErrorFlags.
* @return ErrorReaction (except ER_EXIT, which is acted on immediately)
*
* called from debug_display_error unless overridden by means of
* called from debug_DisplayError unless overridden by means of
* ah_display_error.
**/
extern ErrorReaction sys_display_error(const wchar_t* text, size_t flags);
+1 -1
View File
@@ -109,7 +109,7 @@ class TestMultithread : public CxxTest::TestSuite
static void* thread_func(void* arg)
{
debug_set_thread_name("LF_test");
debug_SetThreadName("LF_test");
ThreadFuncParam* param = (ThreadFuncParam*)arg;
TestMultithread* this_ = param->this_;
+2 -2
View File
@@ -8,9 +8,9 @@ public:
// complain if huge interval or min > max
void TestParam()
{
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TS_ASSERT_EQUALS(rand(1, 0), size_t(0));
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TS_ASSERT_EQUALS(rand(2, ~0u), size_t(0));
}
+27 -27
View File
@@ -107,64 +107,64 @@ public:
void test_param_validation()
{
#if EMULATE_SECURE_CRT
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,0,0 , EINVAL,""); // all invalid
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(d1,1,0 , EINVAL,""); // src = 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CPY(d1,0,s1, ERANGE,""); // max_dst_chars = 0
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d1,1, s1, ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d1,1, s5, ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CPY2(d5,5, s5, ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d1,1 ,s1,1, ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d1,1 ,s5,1, ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCPY(d5,5 ,s5,5, ERANGE,"");
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,0,0 , EINVAL,""); // all invalid
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,0,s1, EINVAL,""); // dst = 0, max = 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(0 ,1,s1, EINVAL,""); // dst = 0, max > 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(d1,1,0 , EINVAL,""); // src = 0
debug_skip_next_err(ERR::INVALID_PARAM);
debug_SkipNextError(ERR::INVALID_PARAM);
TEST_CAT(d1,0,s1, ERANGE,""); // max_dst_chars = 0
debug_skip_next_err(ERR::STRING_NOT_TERMINATED);
debug_SkipNextError(ERR::STRING_NOT_TERMINATED);
TEST_CAT(no_null,5,s1, ERANGE,""); // dst not terminated
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d1,1, s1, "",ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d1,1, s5, "",ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s10, "",ERANGE,""); // empty, total overflow
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s5, "12345",ERANGE,""); // not empty, overflow
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_CAT2(d10,10, s10, "12345",ERANGE,""); // not empty, total overflow
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d1,1, s1,1, "",ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d1,1, s5,5, "",ERANGE,"");
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s10,10, "",ERANGE,""); // empty, total overflow
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s5,5, "12345",ERANGE,""); // not empty, overflow
debug_skip_next_err(ERR::BUF_SIZE);
debug_SkipNextError(ERR::BUF_SIZE);
TEST_NCAT(d10,10, s10,10, "12345",ERANGE,""); // not empty, total overflow
#endif
}
+1 -1
View File
@@ -170,7 +170,7 @@ ScopeTimer::~ScopeTimer()
// note that overflow isn't an issue either way (63 bit cycle counts
// at 10 GHz cover intervals of 29 years).
#if ARCH_IA32 && CONFIG2_TIMER_ALLOW_RDTSC
#if ARCH_X86_X64 && CONFIG2_TIMER_ALLOW_RDTSC
void TimerUnit::SetToZero()
{
+1 -1
View File
@@ -221,7 +221,7 @@ void CSocketBase::Shutdown()
void *WaitLoopThreadMain(void *)
{
debug_set_thread_name("net_wait");
debug_SetThreadName("net_wait");
GLOBAL_LOCK();
CSocketBase::RunWaitLoop();
+1 -1
View File
@@ -18,7 +18,7 @@ CStreamSocket::~CStreamSocket()
void *CStreamSocket_ConnectThread(void *data)
{
debug_set_thread_name("net_connect");
debug_SetThreadName("net_connect");
CStreamSocket *pSock=(CStreamSocket *)data;
PS_RESULT res=PS_OK;
+1 -1
View File
@@ -839,7 +839,7 @@ void EarlyInit()
// If you ever want to catch a particular allocation:
//_CrtSetBreakAlloc(321);
debug_set_thread_name("main");
debug_SetThreadName("main");
// add all debug_printf "tags" that we are interested in:
debug_filter_add("TIMER");
@@ -54,7 +54,7 @@ GameLoopState* g_GameLoop = &state;
static void* LaunchWindow(void* data)
{
const wchar_t* windowName = reinterpret_cast<const wchar_t*>(data);
debug_set_thread_name("atlas_window");
debug_SetThreadName("atlas_window");
Atlas_StartWindow(windowName);
return NULL;
}