1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
|
/* Copyright (C) 2015 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* platform-independent debug support code.
*/
#ifndef INCLUDED_DEBUG
#define INCLUDED_DEBUG
// this module provides platform-independent debug facilities, useful for
// diagnosing and reporting program errors.
// - a symbol engine provides access to compiler-generated debug information and
// can also give a stack trace including local variables;
// - our more powerful assert() replacement gives a stack trace so
// that the underlying problem becomes apparent;
// - the output routines make for platform-independent logging and
// crashlogs with "last-known activity" reporting.
#include "lib/lib_api.h"
#include "lib/types.h" // intptr_t
#include "lib/status.h"
#include "lib/alignment.h"
#include "lib/code_annotation.h"
#include "lib/code_generation.h"
/**
* trigger a breakpoint when reached/"called".
* if defined as a macro, the debugger can break directly into the
* target function instead of one frame below it as with a conventional
* call-based implementation.
**/
#if MSC_VERSION
# define debug_break __debugbreak // intrinsic "function"
#else
extern void debug_break();
#endif
//-----------------------------------------------------------------------------
// output
//-----------------------------------------------------------------------------
/**
* write a formatted string to the debug channel, subject to filtering
* (see below). implemented via debug_puts - see performance note there.
*
* @param fmt Format string and varargs; see printf.
**/
LIB_API void debug_printf(const char* fmt, ...) PRINTF_ARGS(1);
/**
* translates and displays the given strings in a dialog.
* 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_DisplayMessage(const wchar_t* caption, const wchar_t* msg);
/// flags to customize debug_DisplayError behavior
enum DebugDisplayErrorFlags
{
/**
* disallow the Continue button. used e.g. if an exception is fatal.
**/
DE_NO_CONTINUE = 1,
/**
* 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
* forgetting to specify it, and disabling Suppress despite having
* passed a non-NULL pointer doesn't make much sense.
**/
DE_ALLOW_SUPPRESS = 2,
/**
* 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,
/**
* display just the given message; do not add any information about the
* call stack, do not write crashlogs, etc.
*/
DE_NO_DEBUG_INFO = 8
};
/**
* a bool that is reasonably certain to be set atomically.
* we cannot assume support for OpenMP (requires GCC 4.2) or C++0x,
* so we'll have to resort to intptr_t, cpu_CAS and COMPILER_FENCE.
**/
typedef volatile intptr_t atomic_bool;
/**
* value for suppress flag once set by debug_DisplayError.
* rationale: this value is fairly distinctive and helps when
* debugging the symbol engine.
* use 0 as the initial value to avoid allocating .rdata space.
**/
static const atomic_bool DEBUG_SUPPRESS = 0xAB;
/**
* choices offered by the error dialog that are returned
* by debug_DisplayError.
**/
enum ErrorReaction
{
/**
* ignore, continue as if nothing happened.
* note: value doesn't start at 0 because that is interpreted as a
* DialogBoxParam failure.
**/
ER_CONTINUE = 1,
/**
* trigger breakpoint, i.e. enter debugger.
* only returned if DE_MANUAL_BREAK was passed; otherwise,
* debug_DisplayError will trigger a breakpoint itself.
**/
ER_BREAK
};
/**
* all choices offered by the error dialog. those not defined in
* ErrorReaction are acted upon by debug_DisplayError and
* never returned to callers.
* (this separation avoids enumerator-not-handled warnings)
**/
enum ErrorReactionInternal
{
ERI_CONTINUE = ER_CONTINUE,
ERI_BREAK = ER_BREAK,
/**
* ignore and do not report again.
* note: non-persistent; only applicable during this program run.
**/
ERI_SUPPRESS,
/**
* exit the program immediately.
**/
ERI_EXIT,
/**
* special return value for the display_error app hook stub to indicate
* that it has done nothing and that the normal sys_display_error
* implementation should be called instead.
**/
ERI_NOT_IMPLEMENTED
};
/**
* display an error dialog with a message and stack trace.
*
* @param description text to show.
* @param flags: see DebugDisplayErrorFlags.
* @param context, lastFuncToSkip: see debug_DumpStack.
* @param file, line, func: location of the error (typically passed as
* WIDEN(__FILE__), __LINE__, __func__ from a macro)
* @param suppress pointer to a caller-allocated flag that can be used to
* suppress this error. if NULL, this functionality is skipped and the
* "Suppress" dialog button will be disabled.
* note: this flag is read and written exclusively here; caller only
* provides the storage. values: see DEBUG_SUPPRESS above.
* @return ErrorReaction (user's choice: continue running or stop?)
**/
LIB_API ErrorReaction debug_DisplayError(const wchar_t* description, size_t flags, void* context, const wchar_t* lastFuncToSkip, const wchar_t* file, int line, const char* func, atomic_bool* suppress);
// simplified version for just displaying an error message
#define DEBUG_DISPLAY_ERROR(description)\
do\
{\
CACHE_ALIGNED(u8) context[DEBUG_CONTEXT_SIZE];\
(void)debug_CaptureContext(context);\
(void)debug_DisplayError(description, 0, context, L"debug_DisplayError", WIDEN(__FILE__), __LINE__, __func__, 0);\
}\
while(0)
//
// filtering
//
/**
* debug output is very useful, but "too much of a good thing can kill you".
* we don't want to require different LOGn() macros that are enabled
* depending on "debug level", because changing that entails lengthy
* compiles and it's too coarse-grained. instead, we require all
* strings to start with "tag_string|" (exact case and no quotes;
* the alphanumeric-only \<tag_string\> identifies output type).
* they are then subject to filtering: only if the tag has been
* "added" via debug_filter_add is the appendant string displayed.
*
* this approach is easiest to implement and is fine because we control
* all logging code. LIMODS falls from consideration since it's not
* portable and too complex.
*
* notes:
* - filter changes only affect subsequent debug_*printf calls;
* output that didn't pass the filter is permanently discarded.
* - strings not starting with a tag are always displayed.
* - debug_filter_* can be called at any time and from the debugger,
* but are not reentrant.
*
* in future, allow output with the given tag to proceed.
* no effect if already added.
**/
LIB_API void debug_filter_add(const char* tag);
/**
* in future, discard output with the given tag.
* no effect if not currently added.
**/
LIB_API void debug_filter_remove(const char* tag);
/**
* clear all filter state; equivalent to debug_filter_remove for
* each tag that was debug_filter_add-ed.
**/
LIB_API void debug_filter_clear();
/**
* indicate if the given text would be printed.
* useful for a series of debug_printfs - avoids needing to add a tag to
* each of their format strings.
**/
LIB_API bool debug_filter_allows(const char* text);
/**
* call debug_puts if debug_filter_allows allows the string.
**/
LIB_API void debug_puts_filtered(const char* text);
/**
* write an error description and all logs into crashlog.txt
* (in unicode format).
*
* @param text description of the error (including stack trace);
* typically generated by debug_BuildErrorMessage.
*
* @return Status; ERR::REENTERED if reentered via recursion or
* multithreading (not allowed since an infinite loop may result).
**/
LIB_API Status debug_WriteCrashlog(const char* text);
//-----------------------------------------------------------------------------
// assertions
//-----------------------------------------------------------------------------
/**
* ensure the expression \<expr\> evaluates to non-zero. used to validate
* invariants in the program during development and thus gives a
* very helpful warning if something isn't going as expected.
* sprinkle these liberally throughout your code!
*
* to pass more information to users at runtime, you can write
* ENSURE(expression && "descriptive string").
**/
#define ENSURE(expr)\
do\
{\
static atomic_bool suppress__;\
if(!(expr))\
{\
switch(debug_OnAssertionFailure(WIDEN(#expr), &suppress__, WIDEN(__FILE__), __LINE__, __func__))\
{\
case ER_CONTINUE:\
break;\
case ER_BREAK:\
default:\
debug_break();\
break;\
}\
}\
}\
while(0)
/**
* same as ENSURE in debug mode, does nothing in release mode.
* (we don't override the `assert' macro because users may
* inadvertently include \<assert.h\> afterwards)
* (we do not provide an MFC-style VERIFY macro because the distinction
* between ENSURE and VERIFY is unclear. to always run code but only
* check for success in debug builds without raising unused-variable warnings,
* use ASSERT + UNUSED2.)
**/
#define ASSERT(expr) ENSURE(expr)
#ifdef NDEBUG
# undef ASSERT
# define ASSERT(expr)
#endif
/**
* display the error dialog with the given text. this is less error-prone than
* ENSURE(0 && "text"). note that "conditional expression is constant" warnings
* are disabled anyway.
*
* if being able to suppress the warning is desirable (e.g. for self-tests),
* then use DEBUG_WARN_ERR instead.
**/
#define debug_warn(expr) ENSURE(0 && (expr))
/**
* display the error dialog with text corresponding to the given error code.
* used by WARN_RETURN_STATUS_IF_ERR et al., which wrap function calls and automatically
* raise warnings and return to the caller.
**/
#define DEBUG_WARN_ERR(status)\
do\
{\
static atomic_bool suppress__;\
switch(debug_OnError(status, &suppress__, WIDEN(__FILE__), __LINE__, __func__))\
{\
case ER_CONTINUE:\
break;\
case ER_BREAK:\
default:\
debug_break();\
break;\
}\
}\
while(0)
/**
* called when a ENSURE/ASSERT fails;
* 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_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_OnAssertionFailure(const wchar_t* assert_expr, atomic_bool* suppress, const wchar_t* file, int line, const char* func) ANALYZER_NORETURN;
/**
* called when a DEBUG_WARN_ERR indicates an error occurred;
* notifies the user via debug_DisplayError.
*
* @param err Status value indicating the error that occurred
* @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_OnError(Status err, atomic_bool* suppress, const wchar_t* file, int line, const char* func) ANALYZER_NORETURN;
/**
* suppress (prevent from showing) the error dialog from subsequent
* debug_OnError for the given Status.
*
* rationale: for edge cases in some functions, warnings are raised in
* addition to returning an error code. self-tests deliberately trigger
* these cases and check for the latter but shouldn't cause the former.
* we therefore need to squelch them.
*
* @param err the Status to skip.
*
* note: only one concurrent skip request is allowed; call
* debug_StopSkippingErrors before the next debug_SkipErrors.
*/
LIB_API void debug_SkipErrors(Status err);
/**
* @return how many errors were skipped since the call to debug_SkipErrors()
**/
LIB_API size_t debug_StopSkippingErrors();
//-----------------------------------------------------------------------------
// symbol access
//-----------------------------------------------------------------------------
namespace ERR
{
const Status SYM_NO_STACK_FRAMES_FOUND = -100400;
const Status SYM_UNRETRIEVABLE_STATIC = -100401;
const Status SYM_UNRETRIEVABLE = -100402;
const Status SYM_TYPE_INFO_UNAVAILABLE = -100403;
const Status SYM_INTERNAL_ERROR = -100404;
const Status SYM_UNSUPPORTED = -100405;
const Status SYM_CHILD_NOT_FOUND = -100406;
// this limit is to prevent infinite recursion.
const Status SYM_NESTING_LIMIT = -100407;
// this limit is to prevent large symbols (e.g. arrays or linked lists)
// from taking up all available output space.
const Status SYM_SINGLE_SYMBOL_LIMIT = -100408;
}
namespace INFO
{
// one of the dump_sym* functions decided not to output anything at
// all (e.g. for member functions in UDTs - we don't want those).
// therefore, skip any post-symbol formatting (e.g. ) as well.
const Status SYM_SUPPRESS_OUTPUT = +100409;
}
/**
* Maximum number of characters (including null terminator) written to
* user's buffers by debug_ResolveSymbol.
**/
static const size_t DEBUG_SYMBOL_CHARS = 1000;
static const size_t DEBUG_FILE_CHARS = 100;
/**
* read and return symbol information for the given address.
*
* NOTE: the PDB implementation is rather slow (~500 us).
*
* @param ptr_of_interest address of symbol (e.g. function, variable)
* @param sym_name optional out; holds at least DEBUG_SYMBOL_CHARS;
* receives symbol name returned via debug info.
* @param file optional out; holds at least DEBUG_FILE_CHARS;
* receives base name only (no path; see rationale in wdbg_sym) of
* source file containing the symbol.
* @param line optional out; receives source file line number of symbol.
*
* note: all of the output parameters are optional; we pass back as much
* information as is available and desired.
* @return Status; INFO::OK iff any information was successfully
* retrieved and stored.
**/
LIB_API Status debug_ResolveSymbol(void* ptr_of_interest, wchar_t* sym_name, wchar_t* file, int* line);
static const size_t DEBUG_CONTEXT_SIZE = 2048; // Win32 CONTEXT is currently 1232 bytes
/**
* @param context must point to an instance of the platform-specific type
* (e.g. CONTEXT) or CACHE_ALIGNED storage of DEBUG_CONTEXT_SIZE bytes.
**/
LIB_API Status debug_CaptureContext(void* context);
/**
* write a complete stack trace (including values of local variables) into
* the specified buffer.
*
* @param buf Target buffer.
* @param maxChars Max chars of buffer (should be several thousand).
* @param context Platform-specific representation of execution state
* (e.g. Win32 CONTEXT). either specify an SEH exception's
* context record or use debug_CaptureContext to retrieve the current state.
* Rationale: intermediates such as debug_DisplayError change the
* context, so it should be captured as soon as possible.
* @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 Status; ERR::REENTERED if reentered via recursion or
* multithreading (not allowed since static data is used).
**/
LIB_API Status debug_DumpStack(wchar_t* buf, size_t maxChars, void* context, const wchar_t* lastFuncToSkip);
//-----------------------------------------------------------------------------
// helper functions (used by implementation)
//-----------------------------------------------------------------------------
/**
* [system-dependent] write a string to the debug channel.
* this can be quite slow (~1 ms)! On Windows, it uses OutputDebugString
* (entails context switch), otherwise stdout+fflush (waits for IO).
**/
LIB_API void debug_puts(const char* text);
/**
* return the caller of a certain function on the call stack.
*
* this function is useful for recording (partial) stack traces for
* memory allocation tracking, etc.
*
* @param context, lastFuncToSkip - see debug_DumpStack
* @return address of the caller
**/
LIB_API void* debug_GetCaller(void* context, const wchar_t* lastFuncToSkip);
/**
* check if a pointer appears to be totally invalid.
*
* 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.
*
* @param p pointer
* @return 1 if totally bogus, otherwise 0.
**/
LIB_API int debug_IsPointerBogus(const void* p);
/// does the given pointer appear to point to code?
LIB_API bool debug_IsCodePointer(void* p);
/// does the given pointer appear to point to the stack?
LIB_API bool debug_IsStackPointer(void* p);
/**
* inform the debugger of the current thread's name.
*
* (threads are easier to keep apart when they are identified by
* name rather than TID.)
**/
LIB_API void debug_SetThreadName(const char* name);
/**
* holds memory for an error message.
**/
struct ErrorMessageMem
{
// rationale:
// - error messages with stack traces require a good deal of memory
// (hundreds of KB). static buffers of that size are undesirable.
// - the heap may be corrupted, so don't use malloc.
// instead, "lib/sysdep/vm.h" functions should be safe.
// - alloca is a bit iffy (the stack may be maxed out), non-portable and
// complicates the code because it can't be allocated by a subroutine.
// - this method is probably slow, but error messages aren't built often.
// if necessary, first try malloc and use mmap if that fails.
void* pa_mem;
};
/**
* free memory from the error message.
*
* @param emm ErrorMessageMem*
**/
LIB_API void debug_FreeErrorMessage(ErrorMessageMem* emm);
/**
* build a string describing the given error.
*
* 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 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_FreeErrorMessage when no longer needed.
**/
LIB_API const wchar_t* debug_BuildErrorMessage(const wchar_t* description, const wchar_t* fn_only, int line, const char* func, void* context, const wchar_t* lastFuncToSkip, ErrorMessageMem* emm);
#endif // #ifndef INCLUDED_DEBUG
|