/*++

Copyright (c) 1994  Microsoft Corporation
All rights reserved.

Module Name:

    Debug.h

Abstract:

    New debug services for spooler.

Author:

    Albert Ting (AlbertT)  15-Jan-1995

Revision History:

--*/

#ifndef _DBGLOG_H
#define _DBGLOG_H

/********************************************************************

    Setting up the debug support:
    =============================

    Define a MODULE prefix string.  Since this will be printed as
    a prefix to all debug output, it should be concise and unique.

    In your global header file:

        #define MODULE "prtlib:"

    Define a MODULE_DEBUG variable.  This is the actual symbol
    that the library will use to indicate debugging level.
    This DWORD is broken into two bitfield WORDs: the low word
    indicates which levels to print to the debugger; the high word
    breaks into the debugger.  The library takes the DebugLevel from
    a debug message, then ANDs it with the debug level.  If the bit
    is on, the corresponding action (print or break) is taken.

    In your global header file:

        #define MODULE_DEBUG PrtlibDebug

    Finally, the actual debug variable must be defined and initialized
    to a default debug level.  This must be done in exactly one
    *.c translation unit:

    In one of your source files:

        MODULE_DEBUG_INIT ( {LevelsToPrint}, {LevelsToBreak} );

    Adding logging to source code:
    ==============================

    The general format for debug message is:

        DBGMSG( {DebugLevel}, ( {args to printf} ));

    The DebugLevel dictates whether the level should print, break
    into the debugger, or just log to memory (logging always done).

    The args to printf must be placed in an extra set of parens,
    and should assume everything is ANSI.  To print LPTSTRs, use
    the TSTR macro:

        DBGMSG( DBG_WARN,
                ( "LPTSTR "TSTR", LPSTR %s, LPWSTR %ws\n",
                  TEXT("hello"), "hello", L"hello" ));

    Viewing DBGMSGs:
    ================

    Messages will print to the debugger (usermode, or kernel debugger
    if no usermode debugger is available) for all printable levels.
    To change the level, you can edit the MODULE_DEBUG variable
    (PrtlibDebug in the above example).

    By default, DBG_ERROR and DBG_WARNING messages a logged to the
    error log (stored at gpbterrlog).  All others are stored in the
    trace log (gpbttracelog).  These currently log to memory in
    a circular buffer.  Use the splx.dll extension to dump these
    logs.

    At compile time, you can switch these logs to go to file rather
    than memory.  They will be stored as the PID + index number in
    the default directory of the process.

********************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

//
// These values are strictly debug, but must be defined in the free
// build because the TStatus error checking uses them as the first ctr
// parameter.  (During inlining they are discarded.)
//

#define DBG_NONE      0x0000
#define DBG_INFO      0x0001
#define DBG_WARN      0x0002
#define DBG_WARNING   0x0002
#define DBG_ERROR     0x0004
#define DBG_TRACE     0x0008
#define DBG_SECURITY  0x0010
#define DBG_EXEC      0x0020
#define DBG_PORT      0x0040
#define DBG_NOTIFY    0x0080
#define DBG_PAUSE     0x0100

#define DBG_THREADM   0x0400
#define DBG_MIN       0x0800
#define DBG_TIME      0x1000
#define DBG_FOLDER    0x2000
#define DBG_NOHEAD    0x8000

#if DBG

extern DWORD MODULE_DEBUG;

//
// This should be used exactly once in a C file.  It defines
// the Debug variable, and also the DbgMsg function.
//
// If we are statically linking with SplLib (SplLib is a library, not
// a Dll), then we will get the definition from SplLib, so don't define
// it here.
//
#ifdef LINK_SPLLIB
#define MODULE_DEBUG_INIT( print, break )                              \
    DWORD MODULE_DEBUG = (DBG_PRINT( print ) | DBG_BREAK( break ))
#else
#define MODULE_DEBUG_INIT( print, break )                              \
    VOID                                                               \
    DbgMsg(                                                            \
        LPCSTR pszMsgFormat,                                           \
        ...                                                            \
        )                                                              \
    {                                                                  \
        CHAR szMsgText[1024];                                          \
        va_list vargs;                                                 \
                                                                       \
        va_start( vargs, pszMsgFormat );                               \
        wvsprintfA( szMsgText, pszMsgFormat, vargs );                  \
        va_end( vargs );                                               \
                                                                       \
        if( szMsgText[0]  &&  szMsgText[0] != ' ' ){                   \
            OutputDebugStringA( MODULE );                              \
        }                                                              \
        OutputDebugStringA( szMsgText );                               \
    }                                                                  \
    DWORD MODULE_DEBUG = (DBG_PRINT( print ) | DBG_BREAK( break ))
#endif

#define DBGSTR( str ) \
    ((str) ? (str) : TEXT("(NULL)"))

#ifdef UNICODE
#define TSTR "%ws"
#else
#define TSTR "%s"
#endif

#define DBG_PRINT_MASK 0xffff
#define DBG_BREAK_SHIFT 16

#define DBG_PRINT(x) (x)
#define DBG_BREAK(x) (((x) << DBG_BREAK_SHIFT)|(x))

#define SPLASSERT(expr)                      \
    if (!(expr)) {                           \
        DbgMsg( "Failed: %s\nLine %d, %s\n", \
                                #expr,       \
                                __LINE__,    \
                                __FILE__ );  \
        DebugBreak();                        \
    }

VOID
vDbgSingleThreadReset(
    PDWORD pdwThreadId
    );

VOID
vDbgSingleThread(
    PDWORD pdwThreadId
    );

VOID
vDbgSingleThreadNot(
    PDWORD pdwThreadId
    );

VOID
DbgMsg(
    LPCSTR pszMsgFormat,
    ...
    );           

#ifdef DBGLOG

#define DBGMSG( uDbgLevel, argsPrint )             \
        vDbgLogError( MODULE_DEBUG,                \
                      uDbgLevel,                   \
                      __LINE__,                    \
                      __FILE__,                    \
                      MODULE,                      \
                      pszDbgAllocMsgA argsPrint )  
                                                        
LPSTR
pszDbgAllocMsgA(
    LPCSTR pszMsgFormatA,
    ...
    );

VOID
vDbgLogError(
    UINT   uDbg,
    UINT   uDbgLevel,
    UINT   uLine,
    LPCSTR pszFileA,
    LPCSTR pszModuleA,
    LPCSTR pszMsgA
    );
#else

VOID
DbgBreakPoint(
    VOID
    );

#define DBGMSG( Level, MsgAndArgs )                 \
{                                                   \
    if( ( (Level) & 0xFFFF ) & MODULE_DEBUG ){      \
        DbgMsg MsgAndArgs;                          \
    }                                               \
    if( ( (Level) << 16 ) & MODULE_DEBUG )          \
        DbgBreakPoint();                            \
}

#endif


#define SINGLETHREAD_VAR(var) \
    DWORD dwSingleThread_##var

#define SINGLETHREAD(var) \
    vDbgSingleThread(&dwSingleThread_##var)

#define SINGLETHREADNOT(var) \
    vDbgSingleThreadNot(&dwSingleThread_##var)

#define SINGLETHREADRESET(var) \
    vDbgSingleThreadReset(&dwSingleThread_##var)

#else
#define MODULE_DEBUG_INIT( print, break )
#define DBGMSG( uDbgLevel, argsPrint )
#define SPLASSERT(exp)
#define SINGLETHREAD_VAR(var)
#define SINGLETHREAD(var)
#define SINGLETHREADNOT(var)
#define SINGLETHREADRESET(var)
#endif


//
// Automatic checking if an object is valid.
//
#if DBG

VOID
vWarnInvalid(
    PVOID pvObject,
    UINT uDbg,
    UINT uLine,
    LPCSTR pszFileA,
    LPCSTR pszModuleA
    );

#define VALID_PTR(x)                                                \
    ((( x ) && (( x )->bValid( ))) ?                                \
        TRUE :                                                      \
        ( vWarnInvalid( (PVOID)(x), MODULE_DEBUG, __LINE__, __FILE__, MODULE ), FALSE ))

#define VALID_OBJ(x)                                                \
    ((( x ).bValid( )) ?                                            \
        TRUE :                                                      \
        ( vWarnInvalid( (PVOID)&(x), MODULE_DEBUG, __LINE__, __FILE__, MODULE ), FALSE ))

#define VALID_BASE(x)                                               \
    (( x::bValid( )) ?                                              \
        TRUE :                                                      \
        ( vWarnInvalid( (PVOID)this, MODULE_DEBUG, __LINE__, __FILE__, MODULE ), FALSE ))

#else
#define VALID_PTR(x) \
    (( x ) && (( x )->bValid()))
#define VALID_OBJ(x) \
    (( x ).bValid())
#define VALID_BASE(x) \
    ( x::bValid( ))
#endif

#ifdef __cplusplus
}
#endif

#endif // _DBGLOG_H