/*++

Copyright (c) 1998-1999  Microsoft Corporation

Module Name:

    debug.h

Abstract:

    Debugging support for the DirSync project. None of these
    generate any code in the retail build.

Environment:

    User mode

Revision History:

    03/18/98 -srinivac-
        Created it

--*/


#ifndef _DEBUG_H_
#define _DEBUG_H_

#ifdef __cplusplus
extern "C" {
#endif

#if DBG

//
// External functions
//

STDAPI_(PCSTR) StripDirPrefixA(PCSTR);

//
// This variable maintains the current debug level. Any calls to generate
// debug messages succeeds if the requested level is greater than or equal
// to the current level.
//

extern DWORD gdwDebugLevel;

//
// List of debug levels for gdwDebugLevel
//

#define DBG_LEVEL_VERBOSE   0x00000001
#define DBG_LEVEL_INFO      0x00000002
#define DBG_LEVEL_WARNING   0x00000003
#define DBG_LEVEL_ERROR     0x00000004


//
// Internal macros. Don't call these directly
//

#define CHECK_DBG_LEVEL(level)  ((level) >= gdwDebugLevel)

#define DBGMSG(level, msg)                                              \
        {                                                               \
            if (CHECK_DBG_LEVEL(level))                                 \
            {                                                           \
                DbgPrint("DirSync(%d): %s(%u): ",                       \
                         GetCurrentThreadId(),                          \
                         StripDirPrefixA(__FILE__), __LINE__);          \
                DbgPrint msg;                                           \
            }                                                           \
        }


#define DBGPRINT(level, msg)                                            \
        {                                                               \
            if (CHECK_DBG_LEVEL(level))                                 \
            {                                                           \
                DbgPrint msg;                                           \
            }                                                           \
        }


//
// These are the main macros that you'll be using in your code.
// Note that you should enclose the msg in additional
// paranthesis as shown in the example below.
//
// WARNING(("Out of memory"));
// ERR(("Incorrect return value: %d", rc));
//

#define VERBOSE(msg)       DBGMSG(DBG_LEVEL_VERBOSE, msg)
#define INFO(msg)          DBGMSG(DBG_LEVEL_INFO,   msg)
#define WARNING(msg)       DBGMSG(DBG_LEVEL_WARNING, msg)
#define ERR(msg)           DBGMSG(DBG_LEVEL_ERROR,   msg)
#define ERR_RIP(msg)       DBGMSG(DBG_LEVEL_ERROR,   msg);RIP()
#define RIP()              DebugBreak()
#define DEBUGOUT(msg)      DbgPrint msg


//
// These macros are used for asserting certain conditions. They are
// independent of the debugging level.
// These also require additional paranthesis to enclose the msg as
// shown below.
//
// ASSERT(x > 0);
// ASSERTMSG(x > 0, ("x less than 0: x=%d", x));
//

#ifdef ASSERT
#undef ASSERT
#undef ASSERTMSG
#endif

#define ASSERT(expr)                                                    \
        {                                                               \
            if (!(expr))                                                \
            {                                                           \
                DbgPrint("DirSync(%d): Assert: %s(%u)\n",               \
                         GetCurrentThreadId(),                          \
                         StripDirPrefixA(__FILE__), __LINE__);          \
                DebugBreak();                                           \
            }                                                           \
        }


#define ASSERTMSG(expr, msg)                                            \
        {                                                               \
            if (!(expr))                                                \
            {                                                           \
                DbgPrint("DirSync(%d): Assert: %s(%u)\n",               \
                         GetCurrentThreadId(),                          \
                         StripDirPrefixA(__FILE__), __LINE__);          \
                DbgPrint msg;                                           \
                DbgPrint("\n");                                         \
                DebugBreak();                                           \
            }                                                           \
        }

#else // !DBG

#define DBGMSG(level, msg)
#define VERBOSE(msg)
#define INFO(msg)
#define WARNING(msg)
#define ERR(msg)
#define ERR_RIP(msg)
#define RIP()
#define DEBUGOUT(msg)

#define ASSERT(expr)
#define ASSERTMSG(expr, msg)

#endif

//
// The following macros let you enable debugging on a per feature basis.
// To use these macros, here is what you should do:
//
// At the beginning of the file (after header includes):
//
//  1. Define a bit constant for each capability you want to debug
//  2. For each feature, add the following line
//       DEFINE_FEATURE_FLAGS(featurename, flags);
//     where flags is a bit-wise OR of the capabilities you want to debug for
//     that feature
//  3. In your code add the following line wherever you want debug messages
//       FEATURE_DEBUG(featurename, flag, (msg));
//
//  E.g. let us say I am implementing a memory manager, and I would like to
//       trace memory allocations and frees. Here is what I would do
//
//       #define FLAG_ALLOCATE   1
//       #define FLAG_FREE       2
//
//       DEFINE_FEATURE_FLAGS(MemMgr, FLAG_ALLOCATE);
//
//       void *MemAlloc(DWORD dwSize)
//       {
//           FEATURE_DEBUG(MemMgr, FLAG_ALLOCATE, ("Memory of size %d allocated", dwSize));
//           ...
//       }
//
//       void MemFree(void *pvMem)
//       {
//           FEATURE_DEBUG(MemMgr, FKAG_FREE, ("Memory freed"));
//           ...
//       }
//
//  Note that I have set this up to send only alloc messages to the debugger,
//  but I can break into the debugger and modify dwMemMgrDbgFlags to
//  send free messages as well.
//
//  Once component testing of a feature is completed, flags parameter in
//  DEFINE_FEATURE_FLAGS should be changed to 0, so by default this feature
//  does not send debug messages to the debugger.
//

#if DBG

//
// Global debug flag that can used to set values to all other flags
//

extern DWORD gdwGlobalDbgFlags;

#define DEFINE_FEATURE_FLAGS(feature, flags)                            \
            DWORD gdw##feature##DbgFlags = (flags)

#define EXTERN_FEATURE_FLAGS(feature)                                   \
            extern DWORD gdw##feature##DbgFlags

#define FEATURE_DEBUG(feature, flag, msg)                               \
        {                                                               \
            if (gdw##feature##DbgFlags & (flag) ||                      \
                gdwGlobalDbgFlags & (flag))                             \
            {                                                           \
                DbgPrint msg;                                           \
            }                                                           \
        }

#define FEATURE_DEBUG_FN(feature, flag, func)                           \
        {                                                               \
            if (gdw##feature##DbgFlags & (flag) ||                      \
                gdwGlobalDbgFlags & (flag))                             \
            {                                                           \
                func;                                                   \
            }                                                           \
        }

#define FLAG_INFO               0x01
#define FLAG_VERBOSE            0x02
#define FLAG_FNTRACE            0x04
#define FLAG_FULLTRACE          0xFFFF

#else // !DBG

#define DEFINE_FEATURE_FLAGS(feature, flags)
#define EXTERN_FEATURE_FLAGS(feature)
#define FEATURE_DEBUG(feature, flag, msg)
#define FEATURE_DEBUG_FN(feature, flag, func)

#endif // !DBG

//
// Macros for error handling
//

#define BAIL_ON_FAILURE(hr)                             \
            if (FAILED(hr))                             \
            {                                           \
                goto error;                             \
            }

#define BAIL_ON_FAILURE_WITH_MSG(err, msg)              \
            if (FAILED(hr))                             \
            {                                           \
                ERR(msg);                               \
                goto error;                             \
            }

#define BAIL_ON_NULL(ptr)                               \
            if ((ptr) == NULL)                          \
            {                                           \
                ERR(("Error allocating memory\n"));     \
                hr = E_OUTOFMEMORY;                     \
                goto error;                             \
            }

#define BAIL()  goto error


#ifdef __cplusplus
}
#endif

#endif  // ifndef _DEBUG_H_

