mirror of
https://github.com/ProjectDreamland/area51.git
synced 2024-11-01 03:01:49 +01:00
281 lines
11 KiB
C++
281 lines
11 KiB
C++
//==============================================================================
|
|
//
|
|
// x_debug.hpp
|
|
//
|
|
//==============================================================================
|
|
|
|
#ifndef X_DEBUG_HPP
|
|
#define X_DEBUG_HPP
|
|
|
|
#if defined( TARGET_PS2_DVD ) || defined( TARGET_PS2_CLIENT )
|
|
//#define X_ASSERT_LITE
|
|
#endif
|
|
|
|
//==============================================================================
|
|
//
|
|
// This file provides basic, cross platform debugging assistance. The
|
|
// following groups of functionality are present:
|
|
//
|
|
// - Debugger message support.
|
|
// - Compiled breakpoint via BREAK.
|
|
// - Runtime validation via ASSERT and other related macros and functions.
|
|
// - Custom run-time validation failure handling mechanism.
|
|
// - TO DO: Error message and recovery support.
|
|
// - TO DO: Thread safe.
|
|
//
|
|
//==============================================================================
|
|
|
|
//==============================================================================
|
|
// INCLUDES
|
|
//==============================================================================
|
|
|
|
#ifndef X_TYPES_HPP
|
|
#include "x_types.hpp"
|
|
#endif
|
|
|
|
//==============================================================================
|
|
// Debugger Message
|
|
//==============================================================================
|
|
//
|
|
// The function x_DebugMsg works like x_printf except that (a) the output goes
|
|
// to the debugger if possible, and (b) it only works when X_DEBUG is defined.
|
|
//
|
|
//==============================================================================
|
|
|
|
#if defined(X_DEBUG)
|
|
void x_DebugMsg( const char* pFormatStr, ... );
|
|
void x_DebugLog( const char* pFormatStr, ... );
|
|
#else
|
|
inline void x_DebugDummyPrintToMakeDebugMsgDisappear (const char *,...) {}
|
|
#define x_DebugMsg if (0) x_DebugDummyPrintToMakeDebugMsgDisappear
|
|
#define x_DebugLog if (0) x_DebugDummyPrintToMakeDebugMsgDisappear
|
|
#endif
|
|
void x_DebugSetVersionString(const char *pAppVersion);
|
|
const char *x_DebugGetVersionString(void);
|
|
|
|
typedef void x_debug_crash_fn ( char *pBuffer, s32 Length );
|
|
|
|
void x_DebugSetCrashFunction(x_debug_crash_fn *Callback);
|
|
x_debug_crash_fn *x_DebugGetCrashFunction(void);
|
|
|
|
|
|
//==============================================================================
|
|
// The BREAK macro.
|
|
//==============================================================================
|
|
//
|
|
// The macro BREAK will cause a debugger breakpoint if possible on any given
|
|
// platform. If a breakpoint cannot be caused, then a divide by zero will be
|
|
// forced. Note that the BREAK macro is highly platform specific. The
|
|
// implementation of BREAK on some platforms prevents it from being used
|
|
// syntactically as an expression. It can only be used as a statement.
|
|
//
|
|
//==============================================================================
|
|
|
|
#ifdef BREAK
|
|
#undef BREAK
|
|
#endif
|
|
|
|
// If we can implement BREAK properly on any given platform, do it!
|
|
|
|
#ifdef TARGET_PC
|
|
#define BREAK { __asm int 3 }
|
|
#endif
|
|
|
|
#ifdef TARGET_PS2
|
|
#define BREAK asm("break")
|
|
#endif
|
|
|
|
#ifdef TARGET_NGC
|
|
#endif
|
|
|
|
#ifdef TARGET_XBOX
|
|
#endif
|
|
|
|
// Generic BREAK to be used if no proper version can be created.
|
|
|
|
#ifndef BREAK
|
|
extern volatile s32 DDBZ; // Debug Divide By Zero
|
|
#define BREAK (DDBZ=0,DDBZ=1/DDBZ)
|
|
#endif
|
|
|
|
//==============================================================================
|
|
// Runtime validation support.
|
|
//==============================================================================
|
|
//
|
|
// Most of the run-time validations are one form or another of an ASSERT. So,
|
|
// for lack of a better name, the presence of the compile time macro X_ASSERT
|
|
// activates the optional run-time validations. (And, of course, the absence
|
|
// of X_ASSERT deactivates them.)
|
|
//
|
|
// The following macros and functions are all designed to perform validation of
|
|
// expected conditions at run-time. Each takes an expression as the first
|
|
// parameter. The expression (expr) is expected to be true whenever evaluated.
|
|
//
|
|
// ASSERT ( expr )
|
|
// ASSERTS ( expr, message )
|
|
//
|
|
// VERIFY ( expr )
|
|
// VERIFYS ( expr, message )
|
|
//
|
|
// DEMAND ( expr )
|
|
//
|
|
// The macros ASSERT and ASSERTS completely evaporate in when X_ASSERT is not
|
|
// defined. Macros VERIFY and VERIFYS, lacking X_ASSERT, still evaluate the
|
|
// expression, but do not validate the result. Consider:
|
|
//
|
|
// ASSERT( CriticalInitialization() ); // EVIL without X_ASSERT!
|
|
// VERIFY( CriticalInitialization() ); // Safe without X_ASSERT.
|
|
//
|
|
// The ASSERTS and VERIFYS macros accept a message string to assist problem
|
|
// diagnosis. Upon a run-time failure, the message is displayed. For example:
|
|
//
|
|
// ASSERTS( Radius >= 0.0f, "Radius must be non-negative." );
|
|
//
|
|
// To place formatted strings within ASSERTS and VERIFYS, use the xfs class
|
|
// from x_string.hpp. For example:
|
|
//
|
|
// pFile = x_fopen( pFileName, "rt" );
|
|
// ASSERTS( pFile, xfs( "Failed to open file '%s'.", pFileName ) );
|
|
//
|
|
// For run-time validation that does NOT evaporate without X_ASSERT, use the
|
|
// DEMAND macro which behaves exactly like ASSERT.
|
|
//
|
|
// pFile = x_fopen( pFileName, "rt" );
|
|
// ASSERT( pFile ); // Evaporates without X_ASSERT.
|
|
// DEMAND( pFile ); // Never goes away.
|
|
//
|
|
// Available options:
|
|
//
|
|
// - As previously mentioned, the macro X_ASSERT enables the validation
|
|
// macros. X_ASSERT should be always be present in debug configurations.
|
|
//
|
|
// - The macro X_ASSERT_LITE causes the run-time validation macros to take on
|
|
// an alternate form which sacrifices feedback (the file name and the
|
|
// expression) for compiled code space. Since it is anticipated that use
|
|
// of X_ASSERT_LITE will be rare, there is not likely to be a standard
|
|
// build configuration which defines X_ASSERT_LITE. When needed, its
|
|
// definition should be temporarily forced upon the project. (One way to
|
|
// do this would be to simply add #define X_ASSERT_LITE to this file.)
|
|
//
|
|
//==============================================================================
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Clear out any pre-existing definitions of the run-time validation macros.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#ifdef ASSERT
|
|
#undef ASSERT
|
|
#endif
|
|
|
|
#ifdef ASSERTS
|
|
#undef ASSERTS
|
|
#endif
|
|
|
|
#ifdef VERIFY
|
|
#undef VERIFY
|
|
#endif
|
|
|
|
#ifdef VERIFYS
|
|
#undef VERIFYS
|
|
#endif
|
|
|
|
#ifdef DEMAND
|
|
#undef DEMAND
|
|
#endif
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Define the run-time validation macros.
|
|
//------------------------------------------------------------------------------
|
|
|
|
#ifdef X_ASSERT
|
|
|
|
#ifdef X_ASSERT_LITE
|
|
#define ASSERT(expr) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, NULL, NULL ) ) BREAK; } while( FALSE )
|
|
#define ASSERTS(expr,str) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, NULL, NULL ) ) BREAK; } while( FALSE )
|
|
#define VERIFY(expr) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, NULL, NULL ) ) BREAK; } while( FALSE )
|
|
#define VERIFYS(expr,str) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, NULL, NULL ) ) BREAK; } while( FALSE )
|
|
#else
|
|
#define ASSERT(expr) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, #expr, NULL ) ) BREAK; } while( FALSE )
|
|
#define ASSERTS(expr,str) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, #expr, str ) ) BREAK; } while( FALSE )
|
|
#define VERIFY(expr) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, #expr, NULL ) ) BREAK; } while( FALSE )
|
|
#define VERIFYS(expr,str) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, #expr, str ) ) BREAK; } while( FALSE )
|
|
#endif
|
|
|
|
#else
|
|
|
|
#if defined( TARGET_PC ) && defined( VENDOR_MS )
|
|
#define ASSERT(expr) (__assume(expr))
|
|
#define ASSERTS(expr,str) (__assume(expr))
|
|
#else
|
|
#define ASSERT(expr) ((void) 0 )
|
|
#define ASSERTS(expr,str) ((void) 0 )
|
|
#endif
|
|
#define VERIFY(expr) ((void)(expr))
|
|
#define VERIFYS(expr,str) ((void)(expr))
|
|
|
|
#endif
|
|
|
|
#ifdef X_ASSERT_LITE
|
|
#define DEMAND(expr) do{ if( !(expr) && RTFHandler( NULL, __LINE__, NULL, NULL ) ) BREAK; } while( FALSE )
|
|
#else
|
|
#define DEMAND(expr) do{ if( !(expr) && RTFHandler( __FILE__, __LINE__, #expr, NULL ) ) BREAK; } while( FALSE )
|
|
#endif
|
|
|
|
//==============================================================================
|
|
// Customizable run-time failure (RTF) behavior.
|
|
//==============================================================================
|
|
//
|
|
// When a run-time validation check fails, a "run-time failure" (or RTF)
|
|
// handler is called to examine and manage the situation. It is possible for
|
|
// the RTF handler to decide to even dismiss the failure and allow the program
|
|
// execution to continue. It is the RTF handler's responsibility to report the
|
|
// failure as best as possible.
|
|
//
|
|
// Default RTF handlers are provided by the x_files. On PC's, the default RTF
|
|
// handler uses Microsoft's stuff which is pretty good. On consoles, the
|
|
// default version simply prints an error message (via x_printf) and BREAKs.
|
|
// More sophisticated RTF handlers can be registered. Such handlers should
|
|
// probably be provided by the engine in use.
|
|
//
|
|
// A custom RTF handler could, for example, bring up an on-screen menu for user
|
|
// response. Some options could allow the program to essentially ignore the
|
|
// failure and keep running the program. Other options could attempt to divert
|
|
// program execution into specialized diagnostic modes.
|
|
//
|
|
// Functions:
|
|
//
|
|
// x_SetRTFHandler - Use the given RTF handler.
|
|
// x_GetRTFHandler - Get a pointer to the current RTF handler.
|
|
//
|
|
//==============================================================================
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Define a type for the RTF handler function named "rtf_fn". For a pointer to
|
|
// such a function, just use "rtf_fn*".
|
|
//
|
|
// The return value for RTF handlers is interpretted as follows:
|
|
// TRUE - Abort! The failure is fatal!
|
|
// FALSE - Do NOT abort. The failure is NOT fatal.
|
|
// Think of the return as an "Abort?" flag.
|
|
//
|
|
// Any of the string arguments to an RTF handler could potentially be NULL.
|
|
// All RTF handlers must be prepared for such cases.
|
|
//------------------------------------------------------------------------------
|
|
|
|
typedef xbool rtf_fn( const char* pFileName,
|
|
s32 LineNumber,
|
|
const char* pExprString,
|
|
const char* pMessageString );
|
|
|
|
typedef void log_fn( const char *pString);
|
|
|
|
void x_SetRTFHandler( rtf_fn* pRTFHandler );
|
|
rtf_fn* x_GetRTFHandler( void );
|
|
void x_SetLogHandler( log_fn *pLogHandler);
|
|
log_fn* x_GetLogHandler(void);
|
|
|
|
rtf_fn RTFHandler;
|
|
|
|
//==============================================================================
|
|
#endif // X_DEBUG_HPP
|
|
//==============================================================================
|