Pyrogenesis HEAD
Pyrogenesis, a RTS Engine
debug.cpp File Reference
#include "precompiled.h"
#include "lib/debug.h"
#include "lib/alignment.h"
#include "lib/app_hooks.h"
#include "lib/fnv_hash.h"
#include "lib/sysdep/sysdep.h"
#include "lib/sysdep/vm.h"
#include <cstdarg>
#include <cstring>
#include <cstdio>
Include dependency graph for debug.cpp:

Classes

class  PrintfWriter
 

Namespaces

namespace  anonymous_namespace{debug.cpp}
 

Enumerations

enum  SkipStatus { INVALID , VALID , BUSY }
 

Functions

 STATUS_ADD_DEFINITIONS (debugStatusDefinitions)
 
void debug_filter_add (const char *tag)
 debug output is very useful, but "too much of a good thing can kill you". More...
 
void debug_filter_remove (const char *tag)
 in future, discard output with the given tag. More...
 
void debug_filter_clear ()
 clear all filter state; equivalent to debug_filter_remove for each tag that was debug_filter_add-ed. More...
 
bool debug_filter_allows (const char *text)
 indicate if the given text would be printed. More...
 
void debug_printf (const char *fmt,...)
 write a formatted string to the debug channel, subject to filtering (see below). More...
 
void debug_puts_filtered (const char *text)
 call debug_puts if debug_filter_allows allows the string. More...
 
Status debug_WriteCrashlog (const wchar_t *text)
 
const wchar_t * debug_BuildErrorMessage (const wchar_t *description, const wchar_t *filename, int line, const char *func, void *context, const wchar_t *lastFuncToSkip)
 build a string describing the given error. More...
 
void debug_DisplayMessage (const wchar_t *caption, const wchar_t *msg)
 translates and displays the given strings in a dialog. More...
 
static bool ShouldSuppressError (std::atomic< bool > *suppress)
 
static ErrorReactionInternal CallDisplayError (const wchar_t *text, size_t flags)
 
static ErrorReaction PerformErrorReaction (ErrorReactionInternal er, size_t flags, std::atomic< bool > *suppress)
 
ErrorReaction debug_DisplayError (const wchar_t *description, size_t flags, void *context, const wchar_t *lastFuncToSkip, const wchar_t *pathname, int line, const char *func, std::atomic< bool > *suppress)
 display an error dialog with a message and stack trace. More...
 
void debug_SkipErrors (Status err)
 suppress (prevent from showing) the error dialog from subsequent debug_OnError for the given Status. More...
 
size_t debug_StopSkippingErrors ()
 
static bool ShouldSkipError (Status err)
 
ErrorReaction debug_OnError (Status err, std::atomic< bool > *suppress, const wchar_t *file, int line, const char *func)
 called when a DEBUG_WARN_ERR indicates an error occurred; notifies the user via debug_DisplayError. More...
 
ErrorReaction debug_OnAssertionFailure (const wchar_t *expr, std::atomic< bool > *suppress, const wchar_t *file, int line, const char *func)
 called when a ENSURE/ASSERT fails; notifies the user via debug_DisplayError. More...
 

Variables

constexpr std::size_t anonymous_namespace{debug.cpp}::MESSAGE_SIZE = 512 * KiB / sizeof(wchar_t)
 
wchar_t anonymous_namespace{debug.cpp}::g_MessageBuffer [MESSAGE_SIZE]
 
static const StatusDefinition debugStatusDefinitions []
 
static const size_t MAX_TAGS = 20
 
static u32 tags [MAX_TAGS]
 
static size_t num_tags
 
static std::atomic< bool > isExiting { false }
 
static std::atomic< SkipStatusskipStatus { INVALID }
 
static Status errorToSkip
 
static size_t numSkipped
 

Enumeration Type Documentation

◆ SkipStatus

enum SkipStatus
Enumerator
INVALID 
VALID 
BUSY 

Function Documentation

◆ CallDisplayError()

static ErrorReactionInternal CallDisplayError ( const wchar_t *  text,
size_t  flags 
)
static

◆ debug_BuildErrorMessage()

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 
)

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.

Parameters
descriptiongeneral description of the problem.
fn_onlyfilename (no path) of source file that triggered the error.
line,funcexact position of the error.
context,lastFuncToSkipsee debug_DumpStack.

◆ debug_DisplayError()

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,
std::atomic< bool > *  suppress 
)

display an error dialog with a message and stack trace.

Parameters
descriptiontext to show.
flagssee DebugDisplayErrorFlags.
context,lastFuncToSkipsee debug_DumpStack.
file,line,funclocation of the error (typically passed as WIDEN(FILE), LINE, func from a macro)
suppresspointer 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.
Returns
ErrorReaction (user's choice: continue running or stop?)

◆ debug_DisplayMessage()

void debug_DisplayMessage ( const wchar_t *  caption,
const wchar_t *  msg 
)

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.

◆ debug_filter_add()

void debug_filter_add ( const char *  tag)

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.

◆ debug_filter_allows()

bool debug_filter_allows ( const char *  text)

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.

◆ debug_filter_clear()

void debug_filter_clear ( )

clear all filter state; equivalent to debug_filter_remove for each tag that was debug_filter_add-ed.

◆ debug_filter_remove()

void debug_filter_remove ( const char *  tag)

in future, discard output with the given tag.

no effect if not currently added.

◆ debug_OnAssertionFailure()

ErrorReaction debug_OnAssertionFailure ( const wchar_t *  assert_expr,
std::atomic< bool > *  suppress,
const wchar_t *  file,
int  line,
const char *  func 
)

called when a ENSURE/ASSERT fails; notifies the user via debug_DisplayError.

Parameters
assert_exprthe expression that failed; typically passed as #expr in the assert macro.
suppresssee debug_DisplayError.
file,linesource file name and line number of the spot that failed
funcname of the function containing it
Returns
ErrorReaction (user's choice: continue running or stop?)

◆ debug_OnError()

ErrorReaction debug_OnError ( Status  err,
std::atomic< bool > *  suppress,
const wchar_t *  file,
int  line,
const char *  func 
)

called when a DEBUG_WARN_ERR indicates an error occurred; notifies the user via debug_DisplayError.

Parameters
errStatus value indicating the error that occurred
suppresssee debug_DisplayError.
file,linesource file name and line number of the spot that failed
funcname of the function containing it
Returns
ErrorReaction (user's choice: continue running or stop?)

◆ debug_printf()

void debug_printf ( const char *  fmt,
  ... 
)

write a formatted string to the debug channel, subject to filtering (see below).

implemented via debug_puts - see performance note there.

Parameters
fmtFormat string and varargs; see printf.

◆ debug_puts_filtered()

void debug_puts_filtered ( const char *  text)

call debug_puts if debug_filter_allows allows the string.

◆ debug_SkipErrors()

void debug_SkipErrors ( Status  err)

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.

Parameters
errthe Status to skip.

note: only one concurrent skip request is allowed; call debug_StopSkippingErrors before the next debug_SkipErrors.

◆ debug_StopSkippingErrors()

size_t debug_StopSkippingErrors ( )
Returns
how many errors were skipped since the call to debug_SkipErrors()

◆ debug_WriteCrashlog()

Status debug_WriteCrashlog ( const wchar_t *  text)

◆ PerformErrorReaction()

static ErrorReaction PerformErrorReaction ( ErrorReactionInternal  er,
size_t  flags,
std::atomic< bool > *  suppress 
)
static

◆ ShouldSkipError()

static bool ShouldSkipError ( Status  err)
static

◆ ShouldSuppressError()

static bool ShouldSuppressError ( std::atomic< bool > *  suppress)
static

◆ STATUS_ADD_DEFINITIONS()

STATUS_ADD_DEFINITIONS ( debugStatusDefinitions  )

Variable Documentation

◆ debugStatusDefinitions

const StatusDefinition debugStatusDefinitions[]
static
Initial value:
= {
{ ERR::SYM_NO_STACK_FRAMES_FOUND, L"No stack frames found" },
{ ERR::SYM_UNRETRIEVABLE_STATIC, L"Value unretrievable (stored in external module)" },
{ ERR::SYM_UNRETRIEVABLE, L"Value unretrievable" },
{ ERR::SYM_TYPE_INFO_UNAVAILABLE, L"Error getting type_info" },
{ ERR::SYM_INTERNAL_ERROR, L"Exception raised while processing a symbol" },
{ ERR::SYM_UNSUPPORTED, L"Symbol type not (fully) supported" },
{ ERR::SYM_CHILD_NOT_FOUND, L"Symbol does not have the given child" },
{ ERR::SYM_NESTING_LIMIT, L"Symbol nesting too deep or infinite recursion" },
{ ERR::SYM_SINGLE_SYMBOL_LIMIT, L"Symbol has produced too much output" },
{ INFO::SYM_SUPPRESS_OUTPUT, L"Symbol was suppressed" }
}
const Status SYM_UNRETRIEVABLE
Definition: debug.h:398
const Status SYM_UNRETRIEVABLE_STATIC
Definition: debug.h:397
const Status SYM_CHILD_NOT_FOUND
Definition: debug.h:402
const Status SYM_TYPE_INFO_UNAVAILABLE
Definition: debug.h:399
const Status SYM_NO_STACK_FRAMES_FOUND
Definition: debug.h:396
const Status SYM_NESTING_LIMIT
Definition: debug.h:404
const Status SYM_INTERNAL_ERROR
Definition: debug.h:400
const Status SYM_UNSUPPORTED
Definition: debug.h:401
const Status SYM_SINGLE_SYMBOL_LIMIT
Definition: debug.h:407
const Status SYM_SUPPRESS_OUTPUT
Definition: debug.h:415

◆ errorToSkip

Status errorToSkip
static

◆ isExiting

std::atomic<bool> isExiting { false }
static

◆ MAX_TAGS

const size_t MAX_TAGS = 20
static

◆ num_tags

size_t num_tags
static

◆ numSkipped

size_t numSkipped
static

◆ skipStatus

std::atomic<SkipStatus> skipStatus { INVALID }
static

◆ tags

u32 tags[MAX_TAGS]
static