From aea236830025c4e100cc8bc59a043daffbb005bf Mon Sep 17 00:00:00 2001 From: janwas Date: Sat, 27 Sep 2008 10:05:11 +0000 Subject: [PATCH] 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. --- source/lib/allocators/dynarray.cpp | 2 +- source/lib/allocators/tests/test_allocators.h | 2 +- source/lib/debug.cpp | 211 +++++++++--------- source/lib/debug.h | 117 +++++----- source/lib/debug_stl.cpp | 2 +- source/lib/lib_errors.cpp | 1 + source/lib/lib_errors.h | 1 + source/lib/res/graphics/unifont.cpp | 2 +- source/lib/res/h_mgr.h | 2 +- source/lib/self_test.cpp | 2 +- source/lib/self_test.h | 4 +- source/lib/sysdep/arch.h | 2 + source/lib/sysdep/os/linux/ldbg.cpp | 35 ++- source/lib/sysdep/os/osx/odbg.cpp | 8 +- source/lib/sysdep/os/unix/udbg.cpp | 4 +- .../lib/sysdep/os/win/tests/test_wdbg_sym.h | 10 +- source/lib/sysdep/os/win/wdbg.cpp | 19 +- source/lib/sysdep/os/win/wdbg_heap.cpp | 4 +- source/lib/sysdep/os/win/wdbg_heap.h | 2 +- source/lib/sysdep/os/win/wdbg_sym.cpp | 113 ++++------ source/lib/sysdep/os/win/whrt/whrt.cpp | 2 +- source/lib/sysdep/os/win/wposix/werrno.h | 2 +- source/lib/sysdep/os/win/wprofiler.cpp | 2 +- source/lib/sysdep/os/win/wseh.cpp | 13 +- source/lib/sysdep/os/win/wsysdep.cpp | 2 +- source/lib/sysdep/sysdep.h | 4 +- source/lib/tests/test_lockfree.h | 2 +- source/lib/tests/test_rand.h | 4 +- source/lib/tests/test_secure_crt.h | 54 ++--- source/lib/timer.cpp | 2 +- source/network/SocketBase.cpp | 2 +- source/network/StreamSocket.cpp | 2 +- source/ps/GameSetup/GameSetup.cpp | 2 +- source/tools/atlas/GameInterface/GameLoop.cpp | 2 +- 34 files changed, 296 insertions(+), 342 deletions(-) diff --git a/source/lib/allocators/dynarray.cpp b/source/lib/allocators/dynarray.cpp index 984a8036ee..b5cb7c6512 100644 --- a/source/lib/allocators/dynarray.cpp +++ b/source/lib/allocators/dynarray.cpp @@ -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. diff --git a/source/lib/allocators/tests/test_allocators.h b/source/lib/allocators/tests/test_allocators.h index 38c3e8f48c..3a2a1af03a 100644 --- a/source/lib/allocators/tests/test_allocators.h +++ b/source/lib/allocators/tests/test_allocators.h @@ -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)); } diff --git a/source/lib/debug.cpp b/source/lib/debug.cpp index 62361fc342..cb867b3dbe 100644 --- a/source/lib/debug.cpp +++ b/source/lib/debug.cpp @@ -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); +} + + diff --git a/source/lib/debug.h b/source/lib/debug.h index bba0043dd2..a13871ffe4 100644 --- a/source/lib/debug.h +++ b/source/lib/debug.h @@ -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 diff --git a/source/lib/debug_stl.cpp b/source/lib/debug_stl.cpp index 693ed882ef..1efbbf510e 100644 --- a/source/lib/debug_stl.cpp +++ b/source/lib/debug_stl.cpp @@ -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 diff --git a/source/lib/lib_errors.cpp b/source/lib/lib_errors.cpp index 1a3c5c27fc..f3d37936c3 100644 --- a/source/lib/lib_errors.cpp +++ b/source/lib/lib_errors.cpp @@ -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); diff --git a/source/lib/lib_errors.h b/source/lib/lib_errors.h index 8374930e21..eb130d814f 100644 --- a/source/lib/lib_errors.h +++ b/source/lib/lib_errors.h @@ -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; diff --git a/source/lib/res/graphics/unifont.cpp b/source/lib/res/graphics/unifont.cpp index 7926da0b4d..5e9d450774 100644 --- a/source/lib/res/graphics/unifont.cpp +++ b/source/lib/res/graphics/unifont.cpp @@ -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); // and are read directly from font file. // negative values don't make sense, but that's all we can check. diff --git a/source/lib/res/h_mgr.h b/source/lib/res/h_mgr.h index 9cd0d265fe..8fc4c999f3 100644 --- a/source/lib/res/h_mgr.h +++ b/source/lib/res/h_mgr.h @@ -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); diff --git a/source/lib/self_test.cpp b/source/lib/self_test.cpp index dfd0e95a16..48cf8963e9 100644 --- a/source/lib/self_test.cpp +++ b/source/lib/self_test.cpp @@ -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; diff --git a/source/lib/self_test.h b/source/lib/self_test.h index 703fd28891..167ea6f124 100644 --- a/source/lib/self_test.h +++ b/source/lib/self_test.h @@ -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; diff --git a/source/lib/sysdep/arch.h b/source/lib/sysdep/arch.h index c227b501c4..052e2bcf42 100644 --- a/source/lib/sysdep/arch.h +++ b/source/lib/sysdep/arch.h @@ -49,4 +49,6 @@ # define ARCH_MIPS 0 #endif +#define ARCH_X86_X64 (ARCH_IA32|ARCH_AMD64) + #endif // #ifndef INCLUDED_ARCH diff --git a/source/lib/sysdep/os/linux/ldbg.cpp b/source/lib/sysdep/os/linux/ldbg.cpp index 6df45ed3c0..447ca699a4 100644 --- a/source/lib/sysdep/os/linux/ldbg.cpp +++ b/source/lib/sysdep/os/linux/ldbg.cpp @@ -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 frames in the backtrace"); + //assert((bt_size >= (int)skip) && "Need at least 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 500); - debug_error_message_free(&emm); + debug_FreeErrorMessage(&emm); } // also used by test_stl as an element type diff --git a/source/lib/sysdep/os/win/wdbg.cpp b/source/lib/sysdep/os/win/wdbg.cpp index 3cf7013024..6f7f5b6356 100644 --- a/source/lib/sysdep/os/win/wdbg.cpp +++ b/source/lib/sysdep/os/win/wdbg.cpp @@ -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 diff --git a/source/lib/sysdep/os/win/wdbg_heap.cpp b/source/lib/sysdep/os/win/wdbg_heap.cpp index 4ae7904bd6..3fb1b3a476 100644 --- a/source/lib/sysdep/os/win/wdbg_heap.cpp +++ b/source/lib/sysdep/os/win/wdbg_heap.cpp @@ -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) diff --git a/source/lib/sysdep/os/win/wdbg_heap.h b/source/lib/sysdep/os/win/wdbg_heap.h index d39b593fa3..601c491ace 100644 --- a/source/lib/sysdep/os/win/wdbg_heap.h +++ b/source/lib/sysdep/os/win/wdbg_heap.h @@ -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). **/ diff --git a/source/lib/sysdep/os/win/wdbg_sym.cpp b/source/lib/sysdep/os/win/wdbg_sym.cpp index f2a10bc5c9..aee8f8e5f0 100644 --- a/source/lib/sysdep/os/win/wdbg_sym.cpp +++ b/source/lib/sysdep/os/win/wdbg_sym.cpp @@ -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; diff --git a/source/lib/sysdep/os/win/whrt/whrt.cpp b/source/lib/sysdep/os/win/whrt/whrt.cpp index b85a98aaed..93c3825aef 100644 --- a/source/lib/sysdep/os/win/whrt/whrt.cpp +++ b/source/lib/sysdep/os/win/whrt/whrt.cpp @@ -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(;;) { diff --git a/source/lib/sysdep/os/win/wposix/werrno.h b/source/lib/sysdep/os/win/wposix/werrno.h index 7b4efff409..26daa737f0 100644 --- a/source/lib/sysdep/os/win/wposix/werrno.h +++ b/source/lib/sysdep/os/win/wposix/werrno.h @@ -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 diff --git a/source/lib/sysdep/os/win/wprofiler.cpp b/source/lib/sysdep/os/win/wprofiler.cpp index ab1f2e0f7b..e67a95d03e 100644 --- a/source/lib/sysdep/os/win/wprofiler.cpp +++ b/source/lib/sysdep/os/win/wprofiler.cpp @@ -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; diff --git a/source/lib/sysdep/os/win/wseh.cpp b/source/lib/sysdep/os/win/wseh.cpp index 9dea25f602..0763b1ce6e 100644 --- a/source/lib/sysdep/os/win/wseh.cpp +++ b/source/lib/sysdep/os/win/wseh.cpp @@ -195,17 +195,17 @@ static const wchar_t* GetExceptionDescription(const EXCEPTION_POINTERS* ep, // return location at which the exception occurred. -// params: see debug_resolve_symbol. +// params: see debug_ResolveSymbol. static void GetExceptionLocus(const EXCEPTION_POINTERS* ep, char* file, int* line, char* func) { // HACK: 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 diff --git a/source/lib/sysdep/os/win/wsysdep.cpp b/source/lib/sysdep/os/win/wsysdep.cpp index 07c1038e37..9e33465032 100644 --- a/source/lib/sysdep/os/win/wsysdep.cpp +++ b/source/lib/sysdep/os/win/wsysdep.cpp @@ -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; diff --git a/source/lib/sysdep/sysdep.h b/source/lib/sysdep/sysdep.h index 3a4328cd3e..50a0a17300 100644 --- a/source/lib/sysdep/sysdep.h +++ b/source/lib/sysdep/sysdep.h @@ -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); diff --git a/source/lib/tests/test_lockfree.h b/source/lib/tests/test_lockfree.h index 250ef6ccf3..8bf56aea5e 100644 --- a/source/lib/tests/test_lockfree.h +++ b/source/lib/tests/test_lockfree.h @@ -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_; diff --git a/source/lib/tests/test_rand.h b/source/lib/tests/test_rand.h index 4a7db4b247..5fae348e44 100644 --- a/source/lib/tests/test_rand.h +++ b/source/lib/tests/test_rand.h @@ -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)); } diff --git a/source/lib/tests/test_secure_crt.h b/source/lib/tests/test_secure_crt.h index 26f0f1d8d8..d242b2fcf6 100644 --- a/source/lib/tests/test_secure_crt.h +++ b/source/lib/tests/test_secure_crt.h @@ -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 } diff --git a/source/lib/timer.cpp b/source/lib/timer.cpp index 558b71a558..215e5c27ff 100644 --- a/source/lib/timer.cpp +++ b/source/lib/timer.cpp @@ -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() { diff --git a/source/network/SocketBase.cpp b/source/network/SocketBase.cpp index 6b1caeb4ec..96f9e8406d 100644 --- a/source/network/SocketBase.cpp +++ b/source/network/SocketBase.cpp @@ -221,7 +221,7 @@ void CSocketBase::Shutdown() void *WaitLoopThreadMain(void *) { - debug_set_thread_name("net_wait"); + debug_SetThreadName("net_wait"); GLOBAL_LOCK(); CSocketBase::RunWaitLoop(); diff --git a/source/network/StreamSocket.cpp b/source/network/StreamSocket.cpp index 16f523440c..fe5157bd8f 100644 --- a/source/network/StreamSocket.cpp +++ b/source/network/StreamSocket.cpp @@ -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; diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 125f84eb20..b7fad08eb5 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -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"); diff --git a/source/tools/atlas/GameInterface/GameLoop.cpp b/source/tools/atlas/GameInterface/GameLoop.cpp index 1d1e36c225..5ed591f622 100644 --- a/source/tools/atlas/GameInterface/GameLoop.cpp +++ b/source/tools/atlas/GameInterface/GameLoop.cpp @@ -54,7 +54,7 @@ GameLoopState* g_GameLoop = &state; static void* LaunchWindow(void* data) { const wchar_t* windowName = reinterpret_cast(data); - debug_set_thread_name("atlas_window"); + debug_SetThreadName("atlas_window"); Atlas_StartWindow(windowName); return NULL; }