870 lines
32 KiB
C++
870 lines
32 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
#ifndef DBG_H
|
|
#define DBG_H
|
|
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include "basetypes.h"
|
|
#include "dbgflag.h"
|
|
#include "platform.h"
|
|
|
|
#ifdef POSIX
|
|
#define __cdecl
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// dll export stuff
|
|
//-----------------------------------------------------------------------------
|
|
#ifndef STATIC_TIER0
|
|
|
|
#ifdef TIER0_DLL_EXPORT
|
|
#define DBG_INTERFACE DLL_EXPORT
|
|
#define DBG_OVERLOAD DLL_GLOBAL_EXPORT
|
|
#define DBG_CLASS DLL_CLASS_EXPORT
|
|
#else
|
|
#define DBG_INTERFACE DLL_IMPORT
|
|
#define DBG_OVERLOAD DLL_GLOBAL_IMPORT
|
|
#define DBG_CLASS DLL_CLASS_IMPORT
|
|
#endif
|
|
|
|
#else // BUILD_AS_DLL
|
|
|
|
#define DBG_INTERFACE extern
|
|
#define DBG_OVERLOAD
|
|
#define DBG_CLASS
|
|
#endif // BUILD_AS_DLL
|
|
|
|
class Color;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Usage model for the Dbg library
|
|
//
|
|
// 1. Spew.
|
|
//
|
|
// Spew can be used in a static and a dynamic mode. The static
|
|
// mode allows us to display assertions and other messages either only
|
|
// in debug builds, or in non-release builds. The dynamic mode allows us to
|
|
// turn on and off certain spew messages while the application is running.
|
|
//
|
|
// Static Spew messages:
|
|
//
|
|
// Assertions are used to detect and warn about invalid states
|
|
// Spews are used to display a particular status/warning message.
|
|
//
|
|
// To use an assertion, use
|
|
//
|
|
// Assert( (f == 5) );
|
|
// AssertMsg( (f == 5), ("F needs to be %d here!\n", 5) );
|
|
// AssertFunc( (f == 5), BadFunc() );
|
|
// AssertEquals( f, 5 );
|
|
// AssertFloatEquals( f, 5.0f, 1e-3 );
|
|
//
|
|
// The first will simply report that an assertion failed on a particular
|
|
// code file and line. The second version will display a print-f formatted
|
|
// message
|
|
// along with the file and line, the third will display a generic
|
|
//message and
|
|
// will also cause the function BadFunc to be executed, and the last two
|
|
// will report an error if f is not equal to 5 (the last one asserts
|
|
//within a particular tolerance).
|
|
//
|
|
// To use a warning, use
|
|
//
|
|
// Warning("Oh I feel so %s all over\n", "yummy");
|
|
//
|
|
// Warning will do its magic in only Debug builds. To perform spew in *all*
|
|
// builds, use RelWarning.
|
|
//
|
|
// Three other spew types, Msg, Log, and Error, are compiled into all
|
|
//builds. These error types do *not* need two sets of parenthesis.
|
|
//
|
|
// Msg( "Isn't this exciting %d?", 5 );
|
|
// Error( "I'm just thrilled" );
|
|
//
|
|
// Dynamic Spew messages
|
|
//
|
|
// It is possible to dynamically turn spew on and off. Dynamic spew is
|
|
// identified by a spew group and priority level. To turn spew on for a
|
|
// particular spew group, use SpewActivate( "group", level ). This will
|
|
// cause all spew in that particular group with priority levels <= the
|
|
// level specified in the SpewActivate function to be printed. Use DSpew
|
|
// to perform the spew:
|
|
//
|
|
// DWarning( "group", level, "Oh I feel even yummier!\n" );
|
|
//
|
|
// Priority level 0 means that the spew will *always* be printed, and group
|
|
// '*' is the default spew group. If a DWarning is encountered using a group
|
|
// whose priority has not been set, it will use the priority of the default
|
|
// group. The priority of the default group is initially set to 0.
|
|
//
|
|
// Spew output
|
|
//
|
|
// The output of the spew system can be redirected to an externally-supplied
|
|
// function which is responsible for outputting the spew. By default, the
|
|
// spew is simply printed using printf.
|
|
//
|
|
// To redirect spew output, call SpewOutput.
|
|
//
|
|
// SpewOutputFunc( OutputFunc );
|
|
//
|
|
// This will cause OutputFunc to be called every time a spew message is
|
|
// generated. OutputFunc will be passed a spew type and a message to print.
|
|
// It must return a value indicating whether the debugger should be invoked,
|
|
// whether the program should continue running, or whether the program
|
|
// should abort.
|
|
//
|
|
// 2. Code activation
|
|
//
|
|
// To cause code to be run only in debug builds, use DBG_CODE:
|
|
// An example is below.
|
|
//
|
|
// DBG_CODE(
|
|
// {
|
|
// int x = 5;
|
|
// ++x;
|
|
// }
|
|
// );
|
|
//
|
|
// Code can be activated based on the dynamic spew groups also. Use
|
|
//
|
|
// DBG_DCODE( "group", level,
|
|
// { int x = 5; ++x; }
|
|
// );
|
|
//
|
|
// 3. Breaking into the debugger.
|
|
//
|
|
// To cause an unconditional break into the debugger in debug builds only, use
|
|
// DBG_BREAK
|
|
//
|
|
// DBG_BREAK();
|
|
//
|
|
// You can force a break in any build (release or debug) using
|
|
//
|
|
// DebuggerBreak();
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/* Various types of spew messages */
|
|
// I'm sure you're asking yourself why SPEW_ instead of DBG_ ?
|
|
// It's because DBG_ is used all over the place in windows.h
|
|
// For example, DBG_CONTINUE is defined. Feh.
|
|
enum SpewType_t {
|
|
SPEW_MESSAGE = 0,
|
|
SPEW_WARNING,
|
|
SPEW_ASSERT,
|
|
SPEW_ERROR,
|
|
SPEW_LOG,
|
|
|
|
SPEW_TYPE_COUNT
|
|
};
|
|
|
|
enum SpewRetval_t { SPEW_DEBUGGER = 0, SPEW_CONTINUE, SPEW_ABORT };
|
|
|
|
/* type of externally defined function used to display debug spew */
|
|
typedef SpewRetval_t (*SpewOutputFunc_t)(SpewType_t spewType,
|
|
const tchar *pMsg);
|
|
|
|
/* Used to redirect spew output */
|
|
DBG_INTERFACE void SpewOutputFunc(SpewOutputFunc_t func);
|
|
|
|
/* Used to get the current spew output function */
|
|
DBG_INTERFACE SpewOutputFunc_t GetSpewOutputFunc(void);
|
|
|
|
/* This is the default spew fun, which is used if you don't specify one */
|
|
DBG_INTERFACE SpewRetval_t DefaultSpewFunc(SpewType_t type, const tchar *pMsg);
|
|
|
|
/* Same as the default spew func, but returns SPEW_ABORT for asserts */
|
|
DBG_INTERFACE SpewRetval_t DefaultSpewFuncAbortOnAsserts(SpewType_t type,
|
|
const tchar *pMsg);
|
|
|
|
/* Should be called only inside a SpewOutputFunc_t, returns groupname, level,
|
|
* color */
|
|
DBG_INTERFACE const tchar *GetSpewOutputGroup(void);
|
|
DBG_INTERFACE int GetSpewOutputLevel(void);
|
|
DBG_INTERFACE const Color *GetSpewOutputColor(void);
|
|
|
|
/* Used to manage spew groups and subgroups */
|
|
DBG_INTERFACE void SpewActivate(const tchar *pGroupName, int level);
|
|
DBG_INTERFACE bool IsSpewActive(const tchar *pGroupName, int level);
|
|
|
|
/* Used to display messages, should never be called directly. */
|
|
DBG_INTERFACE void _SpewInfo(SpewType_t type, const tchar *pFile, int line);
|
|
DBG_INTERFACE SpewRetval_t _SpewMessage(PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE SpewRetval_t _DSpewMessage(const tchar *pGroupName, int level,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE SpewRetval_t
|
|
ColorSpewMessage(SpewType_t type, const Color *pColor,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...) FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE void _ExitOnFatalAssert(const tchar *pFile, int line);
|
|
DBG_INTERFACE bool ShouldUseNewAssertDialog();
|
|
|
|
DBG_INTERFACE bool SetupWin32ConsoleIO();
|
|
|
|
// Returns true if they want to break in the debugger.
|
|
DBG_INTERFACE bool DoNewAssertDialog(const tchar *pFile, int line,
|
|
const tchar *pExpression);
|
|
|
|
// Allows the assert dialogs to be turned off from code
|
|
DBG_INTERFACE bool AreAllAssertsDisabled();
|
|
DBG_INTERFACE void SetAllAssertsDisabled(bool bAssertsEnabled);
|
|
|
|
// Provides a callback that is called on asserts regardless of spew levels
|
|
typedef void (*AssertFailedNotifyFunc_t)(const char *pchFile, int nLine,
|
|
const char *pchMessage);
|
|
DBG_INTERFACE void SetAssertFailedNotifyFunc(AssertFailedNotifyFunc_t func);
|
|
DBG_INTERFACE void CallAssertFailedNotifyFunc(const char *pchFile, int nLine,
|
|
const char *pchMessage);
|
|
|
|
/* True if -hushasserts was passed on command line. */
|
|
DBG_INTERFACE bool HushAsserts();
|
|
|
|
#if defined(USE_SDL)
|
|
DBG_INTERFACE void SetAssertDialogParent(struct SDL_Window *window);
|
|
DBG_INTERFACE struct SDL_Window *GetAssertDialogParent();
|
|
#endif
|
|
|
|
/* Used to define macros, never use these directly. */
|
|
|
|
#ifdef _PREFAST_
|
|
// When doing /analyze builds define _AssertMsg to be __analysis_assume. This
|
|
// tells the compiler to assume that the condition is true, which helps to
|
|
// suppress many warnings. This define is done in debug and release builds. The
|
|
// unfortunate !! is necessary because otherwise /analyze is incapable of
|
|
// evaluating all of the logical expressions that the regular compiler can
|
|
// handle. Include _msg in the macro so that format errors in it are detected.
|
|
#define _AssertMsg(_exp, _msg, _executeExp, _bFatal) \
|
|
do { \
|
|
__analysis_assume(!!(_exp)); \
|
|
_msg; \
|
|
} while (0)
|
|
#define _AssertMsgOnce(_exp, _msg, _bFatal) \
|
|
do { \
|
|
__analysis_assume(!!(_exp)); \
|
|
_msg; \
|
|
} while (0)
|
|
// Force asserts on for /analyze so that we get a __analysis_assume of all of
|
|
// the constraints.
|
|
#define DBGFLAG_ASSERT
|
|
#define DBGFLAG_ASSERTFATAL
|
|
#define DBGFLAG_ASSERTDEBUG
|
|
#else
|
|
#define _AssertMsg(_exp, _msg, _executeExp, _bFatal) \
|
|
do { \
|
|
if (!(_exp)) { \
|
|
_SpewInfo(SPEW_ASSERT, __TFILE__, __LINE__); \
|
|
SpewRetval_t ret = \
|
|
_SpewMessage("%s", static_cast<const char *>(_msg)); \
|
|
CallAssertFailedNotifyFunc(__TFILE__, __LINE__, _msg); \
|
|
_executeExp; \
|
|
if (ret == SPEW_DEBUGGER) { \
|
|
if (!ShouldUseNewAssertDialog() || \
|
|
DoNewAssertDialog(__TFILE__, __LINE__, _msg)) { \
|
|
DebuggerBreak(); \
|
|
} \
|
|
if (_bFatal) { \
|
|
_ExitOnFatalAssert(__TFILE__, __LINE__); \
|
|
} \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
#define _AssertMsgOnce(_exp, _msg, _bFatal) \
|
|
do { \
|
|
static bool fAsserted; \
|
|
if (!fAsserted) { \
|
|
_AssertMsg(_exp, _msg, (fAsserted = true), _bFatal); \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
/* Spew macros... */
|
|
|
|
// AssertFatal macros
|
|
// AssertFatal is used to detect an unrecoverable error condition.
|
|
// If enabled, it may display an assert dialog (if DBGFLAG_ASSERTDLG is turned
|
|
// on or running under the debugger), and always terminates the application
|
|
|
|
#ifdef DBGFLAG_ASSERTFATAL
|
|
|
|
#define AssertFatal(_exp) \
|
|
_AssertMsg(_exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), true)
|
|
#define AssertFatalOnce(_exp) \
|
|
_AssertMsgOnce(_exp, _T("Assertion Failed: ") _T(#_exp), true)
|
|
#define AssertFatalMsg(_exp, _msg, ...) \
|
|
_AssertMsg(_exp, (const tchar *)CDbgFmtMsg(_msg, ##__VA_ARGS__), \
|
|
((void)0), true)
|
|
#define AssertFatalMsgOnce(_exp, _msg) _AssertMsgOnce(_exp, _msg, true)
|
|
#define AssertFatalFunc(_exp, _f) _AssertMsg( _exp, _T("Assertion Failed: " _T(#_exp), _f, true )
|
|
#define AssertFatalEquals(_exp, _expectedValue) \
|
|
AssertFatalMsg2((_exp) == (_expectedValue), _T("Expected %d but got %d!"), \
|
|
(_expectedValue), (_exp))
|
|
#define AssertFatalFloatEquals(_exp, _expectedValue, _tol) \
|
|
AssertFatalMsg2(fabs((_exp) - (_expectedValue)) <= (_tol), \
|
|
_T("Expected %f but got %f!"), (_expectedValue), (_exp))
|
|
#define VerifyFatal(_exp) AssertFatal(_exp)
|
|
#define VerifyEqualsFatal(_exp, _expectedValue) \
|
|
AssertFatalEquals(_exp, _expectedValue)
|
|
|
|
#define AssertFatalMsg1(_exp, _msg, a1) AssertFatalMsg(_exp, _msg, a1)
|
|
#define AssertFatalMsg2(_exp, _msg, a1, a2) AssertFatalMsg(_exp, _msg, a1, a2)
|
|
#define AssertFatalMsg3(_exp, _msg, a1, a2, a3) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3)
|
|
#define AssertFatalMsg4(_exp, _msg, a1, a2, a3, a4) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4)
|
|
#define AssertFatalMsg5(_exp, _msg, a1, a2, a3, a4, a5) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4, a5)
|
|
#define AssertFatalMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4, a5, a6)
|
|
#define AssertFatalMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7)
|
|
#define AssertFatalMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8)
|
|
#define AssertFatalMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
|
|
AssertFatalMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
|
|
|
#else // DBGFLAG_ASSERTFATAL
|
|
|
|
#define AssertFatal(_exp) ((void)0)
|
|
#define AssertFatalOnce(_exp) ((void)0)
|
|
#define AssertFatalMsg(_exp, _msg) ((void)0)
|
|
#define AssertFatalMsgOnce(_exp, _msg) ((void)0)
|
|
#define AssertFatalFunc(_exp, _f) ((void)0)
|
|
#define AssertFatalEquals(_exp, _expectedValue) ((void)0)
|
|
#define AssertFatalFloatEquals(_exp, _expectedValue, _tol) ((void)0)
|
|
#define VerifyFatal(_exp) (_exp)
|
|
#define VerifyEqualsFatal(_exp, _expectedValue) (_exp)
|
|
|
|
#define AssertFatalMsg1(_exp, _msg, a1) ((void)0)
|
|
#define AssertFatalMsg2(_exp, _msg, a1, a2) ((void)0)
|
|
#define AssertFatalMsg3(_exp, _msg, a1, a2, a3) ((void)0)
|
|
#define AssertFatalMsg4(_exp, _msg, a1, a2, a3, a4) ((void)0)
|
|
#define AssertFatalMsg5(_exp, _msg, a1, a2, a3, a4, a5) ((void)0)
|
|
#define AssertFatalMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) ((void)0)
|
|
#define AssertFatalMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) ((void)0)
|
|
#define AssertFatalMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) ((void)0)
|
|
#define AssertFatalMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
|
|
((void)0)
|
|
|
|
#endif // DBGFLAG_ASSERTFATAL
|
|
|
|
// Assert macros
|
|
// Assert is used to detect an important but survivable error.
|
|
// It's only turned on when DBGFLAG_ASSERT is true.
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
|
|
#define Assert(_exp) \
|
|
_AssertMsg(_exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false)
|
|
#define AssertMsg(_exp, _msg, ...) \
|
|
_AssertMsg(_exp, (const tchar *)CDbgFmtMsg(_msg, ##__VA_ARGS__), \
|
|
((void)0), false)
|
|
#define AssertOnce(_exp) \
|
|
_AssertMsgOnce(_exp, _T("Assertion Failed: ") _T(#_exp), false)
|
|
#define AssertMsgOnce(_exp, _msg) _AssertMsgOnce(_exp, _msg, false)
|
|
#define AssertFunc(_exp, _f) \
|
|
_AssertMsg(_exp, _T("Assertion Failed: ") _T(#_exp), _f, false)
|
|
#define AssertEquals(_exp, _expectedValue) \
|
|
AssertMsg2((_exp) == (_expectedValue), _T("Expected %d but got %d!"), \
|
|
(_expectedValue), (_exp))
|
|
#define AssertFloatEquals(_exp, _expectedValue, _tol) \
|
|
AssertMsg2(fabs((_exp) - (_expectedValue)) <= (_tol), \
|
|
_T("Expected %f but got %f!"), (_expectedValue), (_exp))
|
|
#define Verify(_exp) Assert(_exp)
|
|
#define VerifyMsg1(_exp, _msg, a1) AssertMsg1(_exp, _msg, a1)
|
|
#define VerifyMsg2(_exp, _msg, a1, a2) AssertMsg2(_exp, _msg, a1, a2)
|
|
#define VerifyMsg3(_exp, _msg, a1, a2, a3) AssertMsg3(_exp, _msg, a1, a2, a3)
|
|
#define VerifyEquals(_exp, _expectedValue) AssertEquals(_exp, _expectedValue)
|
|
#define DbgVerify(_exp) Assert(_exp)
|
|
|
|
#define AssertMsg1(_exp, _msg, a1) AssertMsg(_exp, _msg, a1)
|
|
#define AssertMsg2(_exp, _msg, a1, a2) AssertMsg(_exp, _msg, a1, a2)
|
|
#define AssertMsg3(_exp, _msg, a1, a2, a3) AssertMsg(_exp, _msg, a1, a2, a3)
|
|
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4)
|
|
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4, a5)
|
|
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4, a5, a6)
|
|
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7)
|
|
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8)
|
|
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) \
|
|
AssertMsg(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
|
|
|
#else // DBGFLAG_ASSERT
|
|
|
|
#define Assert(_exp) ((void)0)
|
|
#define AssertOnce(_exp) ((void)0)
|
|
#define AssertMsg(_exp, _msg, ...) ((void)0)
|
|
#define AssertMsgOnce(_exp, _msg) ((void)0)
|
|
#define AssertFunc(_exp, _f) ((void)0)
|
|
#define AssertEquals(_exp, _expectedValue) ((void)0)
|
|
#define AssertFloatEquals(_exp, _expectedValue, _tol) ((void)0)
|
|
#define Verify(_exp) (_exp)
|
|
#define VerifyMsg1(_exp, _msg, a1) (_exp)
|
|
#define VerifyMsg2(_exp, _msg, a1, a2) (_exp)
|
|
#define VerifyMsg3(_exp, _msg, a1, a2, a3) (_exp)
|
|
#define VerifyEquals(_exp, _expectedValue) (_exp)
|
|
#define DbgVerify(_exp) (_exp)
|
|
|
|
#define AssertMsg1(_exp, _msg, a1) ((void)0)
|
|
#define AssertMsg2(_exp, _msg, a1, a2) ((void)0)
|
|
#define AssertMsg3(_exp, _msg, a1, a2, a3) ((void)0)
|
|
#define AssertMsg4(_exp, _msg, a1, a2, a3, a4) ((void)0)
|
|
#define AssertMsg5(_exp, _msg, a1, a2, a3, a4, a5) ((void)0)
|
|
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) ((void)0)
|
|
#define AssertMsg6(_exp, _msg, a1, a2, a3, a4, a5, a6) ((void)0)
|
|
#define AssertMsg7(_exp, _msg, a1, a2, a3, a4, a5, a6, a7) ((void)0)
|
|
#define AssertMsg8(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8) ((void)0)
|
|
#define AssertMsg9(_exp, _msg, a1, a2, a3, a4, a5, a6, a7, a8, a9) ((void)0)
|
|
|
|
#endif // DBGFLAG_ASSERT
|
|
|
|
// The Always version of the assert macros are defined even when DBGFLAG_ASSERT
|
|
// is not, so they will be available even in release.
|
|
#define AssertAlways(_exp) \
|
|
_AssertMsg(_exp, _T("Assertion Failed: ") _T(#_exp), ((void)0), false)
|
|
#define AssertMsgAlways(_exp, _msg) _AssertMsg(_exp, _msg, ((void)0), false)
|
|
|
|
// Stringify a number
|
|
#define V_STRINGIFY_INTERNAL(x) #x
|
|
// Extra level of indirection needed when passing in a macro to avoid getting
|
|
// the macro name instead of value
|
|
#define V_STRINGIFY(x) V_STRINGIFY_INTERNAL(x)
|
|
|
|
// Macros to help decorate warnings or errors with the location in code
|
|
#define FILE_LINE_FUNCTION_STRING \
|
|
__FILE__ "(" V_STRINGIFY(__LINE__) "):" __FUNCTION__ ":"
|
|
#define FILE_LINE_STRING __FILE__ "(" V_STRINGIFY(__LINE__) "):"
|
|
#define FUNCTION_LINE_STRING __FUNCTION__ "(" V_STRINGIFY(__LINE__) "): "
|
|
|
|
// Handy define for inserting clickable messages into the build output.
|
|
// Use like this:
|
|
// #pragma MESSAGE("Some message")
|
|
#define MESSAGE(msg) message(__FILE__ "(" V_STRINGIFY(__LINE__) "): " msg)
|
|
|
|
#if !defined(_X360) || !defined(_RETAIL)
|
|
|
|
/* These are always compiled in */
|
|
DBG_INTERFACE void Msg(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void DMsg(const tchar *pGroupName, int level,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE void MsgV(PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
va_list arglist);
|
|
|
|
DBG_INTERFACE void Warning(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void DWarning(const tchar *pGroupName, int level,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE void WarningV(PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
va_list arglist);
|
|
|
|
DBG_INTERFACE void Log(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void DLog(const tchar *pGroupName, int level,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE void LogV(PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
va_list arglist);
|
|
|
|
#ifdef Error
|
|
// p4.cpp does a #define Error Warning and in that case the Error prototype
|
|
// needs to be consistent with the Warning prototype.
|
|
DBG_INTERFACE void Error(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
#else
|
|
DBG_INTERFACE void NORETURN Error(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void NORETURN ErrorV(PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
va_list arglist);
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
inline void Msg(...) {}
|
|
inline void DMsg(...) {}
|
|
inline void MsgV(PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist) {}
|
|
inline void Warning(PRINTF_FORMAT_STRING const tchar *pMsg, ...) {}
|
|
inline void WarningV(PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist) {}
|
|
inline void DWarning(...) {}
|
|
inline void Log(...) {}
|
|
inline void DLog(...) {}
|
|
inline void LogV(PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist) {}
|
|
inline void Error(...) {}
|
|
inline void ErrorV(PRINTF_FORMAT_STRING const tchar *pMsg, va_list arglist) {}
|
|
|
|
#endif
|
|
|
|
// You can use this macro like a runtime assert macro.
|
|
// If the condition fails, then Error is called with the message. This macro is
|
|
// called like AssertMsg, where msg must be enclosed in parenthesis:
|
|
//
|
|
// ErrorIfNot( bCondition, ("a b c %d %d %d", 1, 2, 3) );
|
|
#define ErrorIfNot(condition, msg) \
|
|
if (condition) \
|
|
; \
|
|
else { \
|
|
Error msg; \
|
|
}
|
|
|
|
#if !defined(_X360) || !defined(_RETAIL)
|
|
|
|
/* A couple of super-common dynamic spew messages, here for convenience */
|
|
/* These looked at the "developer" group */
|
|
DBG_INTERFACE void DevMsg(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void DevWarning(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void DevLog(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
|
|
/* default level versions (level 1) */
|
|
DBG_OVERLOAD void DevMsg(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_OVERLOAD void DevWarning(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_OVERLOAD void DevLog(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
|
|
/* These looked at the "console" group */
|
|
DBG_INTERFACE void ConColorMsg(int level, const Color &clr,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(3, 4);
|
|
DBG_INTERFACE void ConMsg(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void ConWarning(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void ConLog(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
|
|
/* default console version (level 1) */
|
|
DBG_OVERLOAD void ConColorMsg(const Color &clr,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(2, 3);
|
|
DBG_OVERLOAD void ConMsg(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_OVERLOAD void ConWarning(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_OVERLOAD void ConLog(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
|
|
/* developer console version (level 2) */
|
|
DBG_INTERFACE void ConDColorMsg(const Color &clr,
|
|
PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void ConDMsg(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void ConDWarning(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
DBG_INTERFACE void ConDLog(PRINTF_FORMAT_STRING const tchar *pMsg, ...)
|
|
FMTFUNCTION(1, 2);
|
|
|
|
/* These looked at the "network" group */
|
|
DBG_INTERFACE void NetMsg(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void NetWarning(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
DBG_INTERFACE void NetLog(int level, PRINTF_FORMAT_STRING const tchar *pMsg,
|
|
...) FMTFUNCTION(2, 3);
|
|
|
|
void ValidateSpew(class CValidator &validator);
|
|
|
|
#else
|
|
|
|
inline void DevMsg(...) {}
|
|
inline void DevWarning(...) {}
|
|
inline void DevLog(...) {}
|
|
inline void ConMsg(...) {}
|
|
inline void ConLog(...) {}
|
|
inline void NetMsg(...) {}
|
|
inline void NetWarning(...) {}
|
|
inline void NetLog(...) {}
|
|
|
|
#endif
|
|
|
|
DBG_INTERFACE void COM_TimestampedLog(PRINTF_FORMAT_STRING char const *fmt, ...)
|
|
FMTFUNCTION(1, 2);
|
|
|
|
/* Code macros, debugger interface */
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
|
|
#define DBG_CODE(_code) \
|
|
if (0) \
|
|
; \
|
|
else { \
|
|
_code \
|
|
}
|
|
#define DBG_CODE_NOSCOPE(_code) _code
|
|
#define DBG_DCODE(_g, _l, _code) \
|
|
if (IsSpewActive(_g, _l)) { \
|
|
_code \
|
|
} else { \
|
|
}
|
|
#define DBG_BREAK() DebuggerBreak() /* defined in platform.h */
|
|
|
|
#else /* not _DEBUG */
|
|
|
|
#define DBG_CODE(_code) ((void)0)
|
|
#define DBG_CODE_NOSCOPE(_code)
|
|
#define DBG_DCODE(_g, _l, _code) ((void)0)
|
|
#define DBG_BREAK() ((void)0)
|
|
|
|
#endif /* _DEBUG */
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef _RETAIL
|
|
class CScopeMsg {
|
|
public:
|
|
CScopeMsg(const char *pszScope) {
|
|
m_pszScope = pszScope;
|
|
Msg("%s { ", pszScope);
|
|
}
|
|
~CScopeMsg() { Msg("} %s", m_pszScope); }
|
|
const char *m_pszScope;
|
|
};
|
|
#define SCOPE_MSG(msg) CScopeMsg scopeMsg(msg)
|
|
#else
|
|
#define SCOPE_MSG(msg)
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Macro to assist in asserting constant invariants during compilation
|
|
|
|
// This implementation of compile time assert has zero cost (so it can safely be
|
|
// included in release builds) and can be used at file scope or function scope.
|
|
// We're using an ancient version of GCC that can't quite handle some
|
|
// of our complicated templates properly. Use some preprocessor trickery
|
|
// to workaround this
|
|
#ifdef __GNUC__
|
|
#define COMPILE_TIME_ASSERT(pred) typedef int UNIQUE_ID[(pred) ? 1 : -1]
|
|
#else
|
|
#if _MSC_VER >= 1600
|
|
// If available use static_assert instead of weird language tricks. This
|
|
// leads to much more readable messages when compile time assert constraints
|
|
// are violated.
|
|
#define COMPILE_TIME_ASSERT(pred) \
|
|
static_assert(pred, "Compile time assert constraint is not true: " #pred)
|
|
#else
|
|
// Due to gcc bugs this can in rare cases (some template functions) cause
|
|
// redeclaration errors when used multiple times in one scope. Fix by adding
|
|
// extra scoping.
|
|
#define COMPILE_TIME_ASSERT(pred) \
|
|
typedef char compile_time_assert_type[(pred) ? 1 : -1];
|
|
#endif
|
|
#endif
|
|
// ASSERT_INVARIANT used to be needed in order to allow COMPILE_TIME_ASSERTs at
|
|
// global scope. However the new COMPILE_TIME_ASSERT macro supports that by
|
|
// default.
|
|
#define ASSERT_INVARIANT(pred) COMPILE_TIME_ASSERT(pred)
|
|
|
|
#ifdef _DEBUG
|
|
template <typename DEST_POINTER_TYPE, typename SOURCE_POINTER_TYPE>
|
|
inline DEST_POINTER_TYPE assert_cast(SOURCE_POINTER_TYPE *pSource) {
|
|
Assert(static_cast<DEST_POINTER_TYPE>(pSource) ==
|
|
dynamic_cast<DEST_POINTER_TYPE>(pSource));
|
|
return static_cast<DEST_POINTER_TYPE>(pSource);
|
|
}
|
|
#else
|
|
#define assert_cast static_cast
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Templates to assist in validating pointers:
|
|
|
|
// Have to use these stubs so we don't have to include windows.h here.
|
|
|
|
DBG_INTERFACE void _AssertValidReadPtr(void *ptr, int count = 1);
|
|
DBG_INTERFACE void _AssertValidWritePtr(void *ptr, int count = 1);
|
|
DBG_INTERFACE void _AssertValidReadWritePtr(void *ptr, int count = 1);
|
|
DBG_INTERFACE void AssertValidStringPtr(const tchar *ptr,
|
|
int maxchar = 0xFFFFFF);
|
|
|
|
#ifdef DBGFLAG_ASSERT
|
|
|
|
FORCEINLINE void AssertValidReadPtr(const void *ptr, int count = 1) {
|
|
_AssertValidReadPtr((void *)ptr, count);
|
|
}
|
|
FORCEINLINE void AssertValidWritePtr(const void *ptr, int count = 1) {
|
|
_AssertValidWritePtr((void *)ptr, count);
|
|
}
|
|
FORCEINLINE void AssertValidReadWritePtr(const void *ptr, int count = 1) {
|
|
_AssertValidReadWritePtr((void *)ptr, count);
|
|
}
|
|
|
|
#else
|
|
|
|
FORCEINLINE void AssertValidReadPtr(const void *ptr, int count = 1) {}
|
|
FORCEINLINE void AssertValidWritePtr(const void *ptr, int count = 1) {}
|
|
FORCEINLINE void AssertValidReadWritePtr(const void *ptr, int count = 1) {}
|
|
#define AssertValidStringPtr AssertValidReadPtr
|
|
|
|
#endif
|
|
|
|
#define AssertValidThis() AssertValidReadWritePtr(this, sizeof(*this))
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Macro to protect functions that are not reentrant
|
|
|
|
#ifdef _DEBUG
|
|
class CReentryGuard {
|
|
public:
|
|
CReentryGuard(int *pSemaphore) : m_pSemaphore(pSemaphore) {
|
|
++(*m_pSemaphore);
|
|
}
|
|
|
|
~CReentryGuard() { --(*m_pSemaphore); }
|
|
|
|
private:
|
|
int *m_pSemaphore;
|
|
};
|
|
|
|
#define ASSERT_NO_REENTRY() \
|
|
static int fSemaphore##__LINE__; \
|
|
Assert(!fSemaphore##__LINE__); \
|
|
CReentryGuard ReentryGuard##__LINE__(&fSemaphore##__LINE__)
|
|
#else
|
|
#define ASSERT_NO_REENTRY()
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Purpose: Inline string formatter
|
|
//
|
|
|
|
#include "valve_off.h"
|
|
class CDbgFmtMsg {
|
|
public:
|
|
CDbgFmtMsg(PRINTF_FORMAT_STRING const tchar *pszFormat, ...)
|
|
FMTFUNCTION(2, 3) {
|
|
va_list arg_ptr;
|
|
|
|
va_start(arg_ptr, pszFormat);
|
|
_vsntprintf(m_szBuf, sizeof(m_szBuf) - 1, pszFormat, arg_ptr);
|
|
va_end(arg_ptr);
|
|
|
|
m_szBuf[sizeof(m_szBuf) - 1] = 0;
|
|
}
|
|
|
|
operator const tchar *() const { return m_szBuf; }
|
|
|
|
private:
|
|
tchar m_szBuf[256];
|
|
};
|
|
#include "valve_on.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Purpose: Embed debug info in each file.
|
|
//
|
|
#if defined(_WIN32) && !defined(_X360)
|
|
|
|
#ifdef _DEBUG
|
|
#pragma comment(compiler)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Purpose: Wrap around a variable to create a simple place to put a breakpoint
|
|
//
|
|
|
|
#ifdef _DEBUG
|
|
|
|
template <class Type>
|
|
class CDataWatcher {
|
|
public:
|
|
const Type &operator=(const Type &val) { return Set(val); }
|
|
|
|
const Type &operator=(const CDataWatcher<Type> &val) {
|
|
return Set(val.m_Value);
|
|
}
|
|
|
|
const Type &Set(const Type &val) {
|
|
// Put your breakpoint here
|
|
m_Value = val;
|
|
return m_Value;
|
|
}
|
|
|
|
Type &GetForModify() { return m_Value; }
|
|
|
|
const Type &operator+=(const Type &val) { return Set(m_Value + val); }
|
|
|
|
const Type &operator-=(const Type &val) { return Set(m_Value - val); }
|
|
|
|
const Type &operator/=(const Type &val) { return Set(m_Value / val); }
|
|
|
|
const Type &operator*=(const Type &val) { return Set(m_Value * val); }
|
|
|
|
const Type &operator^=(const Type &val) { return Set(m_Value ^ val); }
|
|
|
|
const Type &operator|=(const Type &val) { return Set(m_Value | val); }
|
|
|
|
const Type &operator++() { return (*this += 1); }
|
|
|
|
Type operator--() { return (*this -= 1); }
|
|
|
|
Type operator++(int) // postfix version..
|
|
{
|
|
Type val = m_Value;
|
|
(*this += 1);
|
|
return val;
|
|
}
|
|
|
|
Type operator--(int) // postfix version..
|
|
{
|
|
Type val = m_Value;
|
|
(*this -= 1);
|
|
return val;
|
|
}
|
|
|
|
// For some reason the compiler only generates type conversion warnings for
|
|
// this operator when used like CNetworkVarBase<unsigned tchar> = 0x1 (it
|
|
// warns about converting from an int to an unsigned char).
|
|
template <class C>
|
|
const Type &operator&=(C val) {
|
|
return Set(m_Value & val);
|
|
}
|
|
|
|
operator const Type &() const { return m_Value; }
|
|
|
|
const Type &Get() const { return m_Value; }
|
|
|
|
const Type *operator->() const { return &m_Value; }
|
|
|
|
Type m_Value;
|
|
};
|
|
|
|
#else
|
|
|
|
template <class Type>
|
|
class CDataWatcher {
|
|
private:
|
|
CDataWatcher(); // refuse to compile in non-debug builds
|
|
};
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#endif /* DBG_H */
|