/**********************************************************************
UML StateWizard provides its software under the GPL License and
zlib/libpng License for open source projects.
Email us at info@intelliwizard.com for any information, suggestions and
feature requestions.
Home Page: http://www.intelliwizard.com
*************************************************************************/
/* =============================================================================
* Filename: sme_debug.cpp
*
* Copyright IntelliWizard Inc. www.intelliwizard.com
* All rights reserved.
* -----------------------------------------------------------------------------
* General description of this file:
*
* State machine engine debug support global header file.
* -----------------------------------------------------------------------------
* Revision History
* -----------------------------------------------------------------------------
* Version Date Author Revision Detail
* 1.0.0 2004/11/26 Initial
* 2004/11/26 $ Fix memory free bug.
* 2005/9/6 Module Tracer
2005/12/26 Merge Unicode edition with ASCII edition
* ===========================================================================*/
#include "stdafx.h"
#include "SrvAgent.h"
#include <stdio.h>
#include <stdarg.h>
#include <memory.h>
#include "sme_debug.h"
static SME_OUTPUT_DEBUG_ASCII_STR_PROC_T g_fnOutputDebugAsciiStr = NULL;
static SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T g_fnOutputDebugUnicodeStr = NULL;
static SME_MEM_OPR_HOOK_T g_fnMemOprHook = NULL;
#define MAX_MODULE_NUM32 4
#define MAX_MODULE_NUM 128
static unsigned long g_ModuleToLog[MAX_MODULE_NUM32] = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
static BOOL g_bStateTrackinTree =TRUE;
static BOOL g_bStateTrackinOutputWin =TRUE;
#ifdef _WIN32_WCE
HWND g_hWorkspaceWnd=NULL;
#else
extern HWND g_hWorkspaceWnd;
#endif
#define ID_COPYDATA_STATE_TRACK 1100
#define ID_COPYDATA_DEBUG_STR 1101
static BOOL OutputDebugAsciiStrToDS(const char * sDebugStr);
static BOOL OutputDebugUnicodeStrToDS(const unsigned short* sDebugStr);
/*******************************************************************************************
* DESCRIPTION: Turn on the debug string tracer for given module.
* INPUT:
* 1) nModuleID: The module ID, ranging from 0 to 127
* OUTPUT: None.
* NOTE:
*******************************************************************************************/
void SmeTurnOnModuleTracer(int nModuleID)
{
g_ModuleToLog[nModuleID>>5] |= (1 << (nModuleID%32));
}
/*******************************************************************************************
* DESCRIPTION: Turn off the debug string tracer of given module.
* INPUT:
* 1) nModuleID: The module ID, ranging from 0 to 127
* OUTPUT: None.
* NOTE:
*******************************************************************************************/
void SmeTurnOffModuleTracer(int nModuleID)
{
g_ModuleToLog[nModuleID>>5] &= ~(1 << (nModuleID%32));
}
/*******************************************************************************************
* DESCRIPTION: Turn on all module tracers.
* INPUT:
* None.
* OUTPUT: None.
* NOTE:
*******************************************************************************************/
void SmeTurnOnAllModuleTracers()
{
int i;
for (i=0; i<MAX_MODULE_NUM32; i++)
g_ModuleToLog[i] = 0xFFFFFFFF;
}
/*******************************************************************************************
* DESCRIPTION: Turn on all module tracers.
* INPUT:
* None
* OUTPUT: None.
* NOTE:
*******************************************************************************************/
void SmeTurnOffAllModuleTracers()
{
int i;
for (i=0; i<MAX_MODULE_NUM32; i++)
g_ModuleToLog[i] = 0x0;
}
/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:
* 1) sFormat: The format of output string
* 2) ...: The variable argument list.
* OUTPUT: None.
* NOTE:
*******************************************************************************************/
SME_OUTPUT_DEBUG_ASCII_STR_PROC_T SmeSetOutputDebugAsciiStrProc(SME_OUTPUT_DEBUG_ASCII_STR_PROC_T fnProc)
{
SME_OUTPUT_DEBUG_ASCII_STR_PROC_T fnOldProc = g_fnOutputDebugAsciiStr;
g_fnOutputDebugAsciiStr = fnProc;
return fnOldProc;
}
SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T SmeSetOutputDebugUnicodeStrProc(SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T fnProc)
{
SME_OUTPUT_DEBUG_UNICODE_STR_PROC_T fnOldProc = g_fnOutputDebugUnicodeStr;
g_fnOutputDebugUnicodeStr = fnProc;
return fnOldProc;
}
/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:
* 1) nModuleID: Range from 0 to (32*MAX_MODULE_NUM32 - 1), which identify a module.
* 2) sFormat: The format of output string in ASCII format.
* 3) ...: The variable argument list.
* OUTPUT: None.
* NOTE:
* 1) No matter, _UNICODE is defined or not, the format of output string is in ASCII format.
* It will be easier for developer to print Unicode string. Nevertheless, the string argument
* should be in Unicode format.
* 2) If the given module's tracer is turned on, print the debug string. If module id is -1, print it.
*******************************************************************************************/
#define SME_MAX_STR_BUF_LEN 512
void SmeUnicodeTrace(int nModuleID, const unsigned short * wsFormat, ...)
{
va_list ArgList;
static unsigned short sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII/Unicode string buffer */
if (nModuleID>=MAX_MODULE_NUM) return;
if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;
va_start(ArgList, wsFormat);
vswprintf(sBuf, wsFormat, ArgList);
//SME_ASSERT(vswprintf(sBuf, wsFormat, ArgList) <= SME_MAX_STR_BUF_LEN);
if (g_fnOutputDebugUnicodeStr)
(*g_fnOutputDebugUnicodeStr)(sBuf);
else
OutputDebugUnicodeStrToDS(sBuf);
va_end(ArgList);
return;
}
void SmeUnicodeTraceAscFmt(int nModuleID, const char * sFormat, ...)
{
va_list ArgList;
static unsigned short sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII/Unicode string buffer */
static unsigned short wsFormatBuf[SME_MAX_STR_BUF_LEN + 1];
unsigned int i;
if (nModuleID>=MAX_MODULE_NUM) return;
if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;
va_start(ArgList, sFormat);
//SME_ASSERT(strlen(sFormat) <= SME_MAX_STR_BUF_LEN);
for (i = 0; i <= strlen(sFormat); i++)
wsFormatBuf[i] = sFormat[i];
vswprintf(sBuf, wsFormatBuf, ArgList);
//SME_ASSERT(vswprintf(sBuf, wsFormatBuf, ArgList) <= SME_MAX_STR_BUF_LEN);
if (g_fnOutputDebugUnicodeStr)
(*g_fnOutputDebugUnicodeStr)(sBuf);
else
OutputDebugUnicodeStrToDS(sBuf);
va_end(ArgList);
return;
}
/*******************************************************************************************
* DESCRIPTION: Output debug string through user defined function.
* INPUT:
* 1) sFormat: The format of output string in ASCII format.
* 2) ...: The variable argument list. Text string should be in ASCII format.
* OUTPUT: None.
* NOTE:
* 1) No matter, _UNICODE is defined or not, the format of output string and text string argument should be in ASCII format.
* It will be easier for developer to print ACSII string if _UNICODE is defined.
* 2) If the given module's tracer is turned on, print the debug string. If module id is -1, print it.
*******************************************************************************************/
void SmeAsciiTrace(int nModuleID, const char * sFormat, ...)
{
va_list ArgList;
static char sBuf[SME_MAX_STR_BUF_LEN + 1]; /* ASCII string buffer */
if (nModuleID>=MAX_MODULE_NUM) return;
if (nModuleID!=-1 && (g_ModuleToLog[nModuleID>>5] & (1 << (nModuleID%32))) == 0) return;
va_start(ArgList, sFormat);
vsprintf(sBuf, sFormat, ArgList);
//SME_ASSERT(vsprintf(sBuf, sFormat, ArgList) <= SME_MAX_STR_BUF_LEN);
va_end(ArgList);
if (g_fnOutputDebugAsciiStr)
(*g_fnOutputDebugAsciiStr)(sBuf);
else
OutputDebugAsciiStrToDS(sBuf);
return;
}
/*******************************************************************************************
* The memory block information nodes will linked as a stack. The header will be g_pMemBlkList.
* The pNext property in SME_MEM_BLK_INFO_T will pointer to the previous node.
* NULL <--- Node.pNext <--- Node.pNext <--- g_pMemBlkList
*******************************************************************************************/
#define SME_NO_MANS_LAND 0xFDFDFDFD
#define SME_NEW_MEM_BLK 0xCD
struct SME_MEM_BLK_INFO_T
{
void* pUserData;
struct SME_MEM_BLK_INFO_T *pNext;
unsigned int nSize;
const char * sFile;
int nLine;
} SME_MEM_BLK_INFO_T;
static struct SME_MEM_BLK_INFO_T *g_pMemBlkList = NULL;
static int g_nMemBlkNum=0;
static int g_nTotalAllocMem=0;
/*******************************************************************************************
* DESCRIPTION: State machine engine memory allocation function in debug version.
* INPUT: nSize: Memory size;
* OUTPUT: Allocated memory pointer.
* NOTE:
* The new user data is set to SME_NEW_MEM_BLK and no man land is set to SME_NO_MANS_LAND.
*******************************************************************************************/
void * SmeMAllocDebug(unsigned int nSize, const char * sFile, int nLine)
{
void *p;
unsigned long *pLong;
struct SME_MEM_BLK_INFO_T *pMemBlkInfo;
/* Call pre-allocation hook function. Allocation abort if hook function returns FALSE. */
if (g_fnMemOprHook)
if ((*g_fnMemOprHook)(SME_HOOK_PRE_ALLOC, NULL, nSize, sFile, nLine) == FALSE)
return NULL;
/* Adjust to be 4-byte boundary aligned. */
nSize = ((nSize + 3) >> 2)<<2;
p = SmeMAllocRelease(nSize + sizeof(unsigned long));
if (p==NULL) return NULL;
/* Set new user data and no man land. */
memset(p, SME_NEW_MEM_BLK, nSize);
pLong = (unsigned long *)((unsigned char*)p + nSize);
*pLong = SME_NO_MANS_LAND;
pMemBlkInfo = (struct SME_MEM_BLK_INFO_T *)SmeMAllocRelease(sizeof(struct SME_MEM_BLK_INFO_T));
if (pMemBlkInfo==NULL) return p;
/* Push pMemBlkInfo to the memory block stack. */
pMemBlkInfo->pUserData = p;
pMemBlkInfo->nSize = nSize;
pMemBlkInfo->sFile = sFile;
pMemBlkInfo->nLine = nLine;
pMemBlkInfo->pNext = g_pMemBlkList;
g_pMemBlkList = pMemBlkInfo;
g_nMemBlkNum++;
g_nTotalAllocMem+=pMemBlkInfo->nSize;
/* Call post-allocation hook function. */
if (g_fnMemOprHook)
(*g_fnMemOprHook)(SME_HOOK_POST_ALLOC, p, nSize, sFile, nLine);
return p;
}
/*******************************************************************************************
* DESCRIPTION: State machine engine memory free function in release version.
* INPUT: pUserData: Memory block pointer;
* OUTPUT: The result depends on user defined memory free function.
* NOTE: It will check no man land to detect whether memory overwriting occurs.
*
*******************************************************************************************/
BOOL SmeMFreeDebug(void * pUserData, const char * sFile, int nLine)
{
struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
struct SME_MEM_BLK_INFO_T *pPre = g_pMemBlkList;
unsigned long *pLong;
/* Get memory block information from stack. */
while (pMemBlkInfo!=NULL && pMemBlkInfo->pUserData != pUserData)
{
pPre = pMemBlkInfo;
pMemBlkInfo = pMemBlkInfo->pNext;
};
if (pMemBlkInfo == NULL)
{
SME_ASCII_TRACE(-1, "Error! Try to delete an unknown or freed memory block at %s (%d).\n",
sFile, nLine);
return FALSE;
};
/* Call pre-free hook function. */
if (g_fnMemOprHook)
(*g_fnMemOprHook)(SME_HOOK_PRE_FREE,
pMemBlkInfo->pUserData,
pMemBlkInfo->nSize,
pMemBlkInfo->sFile,
pMemBlkInfo->nLine);
/* Check no man land. */
pLong = (unsigned long *)((unsigned char*)pMemBlkInfo->pUserData + pMemBlkInfo->nSize);
if (*pLong != SME_NO_MANS_LAND)
{
SME_ASCII_TRACE(-1,"Error! The memory block is damaged, which is allocated at %s (%d).\n",
pMemBlkInfo->sFile, pMemBlkInfo->nLine);
return FALSE;
}
/* Call post-free hook function. */
if (g_fnMemOprHook)
(*g_fnMemOprHook)(SME_HOOK_POST_FREE,
pMemBlkInfo->pUserData,
pMemBlkInfo->nSize,
pMemBlkInfo->sFile,
pMemBlkInfo->nLine);
/* Remove this memory block information node from list. */
if (pMemBlkInfo == g_pMemBlkList)
/* $$ What to remove is loacted in the header. */
g_pMemBlkList = g_pMemBlkList->pNext;
else
pPre->pNext = pMemBlkInfo->pNext;
g_nMemBlkNum--;
g_nTotalAllocMem-=pMemBlkInfo->nSize;
SmeMFreeRelease(pMemBlkInfo);
return SmeMFreeRelease(pUserData);
}
/*******************************************************************************************
* DESCRIPTION: Reset memory block information list.
* INPUT: None;
* OUTPUT: None.
* NOTE: It will release all allocated memory blocks including user data and memory information blocks.
*
*******************************************************************************************/
BOOL SmeDbgInit()
{
struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
if (g_pMemBlkList == NULL) return FALSE;
while (g_pMemBlkList!=NULL)
{
pMemBlkInfo = g_pMemBlkList;
g_pMemBlkList = g_pMemBlkList->pNext;
SmeMFreeRelease(pMemBlkInfo->pUserData);
SmeMFreeRelease(pMemBlkInfo);
}
g_nMemBlkNum=0;
g_nTotalAllocMem=0;
return TRUE;
}
/*******************************************************************************************
* DESCRIPTION: This function installs a user defined hook function and return the old hook function.
* Hook function is called every time memory is pre-allocated, post-allocated, pre-freed, or post-freed.
* INPUT:
* pMemOprHook: New memory operation hook function.
* OUTPUT: Old memory operation hook function.
*
* NOTE:
*******************************************************************************************/
SME_MEM_OPR_HOOK_T SmeSetMemOprHook(SME_MEM_OPR_HOOK_T pMemOprHook)
{
SME_MEM_OPR_HOOK_T fnOld = g_fnMemOprHook;
g_fnMemOprHook = pMemOprHook;
return fnOld;
}
/*******************************************************************************************
* DESCRIPTION: This function enumerates all allocated memory blocks. The fnEnumMemCallBack
* function is called once per allocated memory block and is passed the information for each block.
* INPUT:
* SME_ENUM_MEM_CALLBACK_T fnEnumMemCallBack: Memory block enumeration call back function.
* OUTPUT:
*
* NOTE:
* typedef BOOL (*SME_ENUM_MEM_CALLBACK_T)( void *pUserData, unsigned int nSize, const unsigned char *sFileName, int nLineNumber);
* If the function returns TRUE, the enumeration will continue. If the function returns FALSE, the enumeration will stop.
*******************************************************************************************/
BOOL SmeEnumMem(SME_ENUM_MEM_CALLBACK_T fnEnumMemCallBack)
{
struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
if (fnEnumMemCallBack==NULL) return FALSE;
/* Get memory block information from stack. */
while (pMemBlkInfo!=NULL)
{
if (FALSE == (*fnEnumMemCallBack)(pMemBlkInfo->pUserData,
pMemBlkInfo->nSize,
pMemBlkInfo->sFile,
pMemBlkInfo->nLine))
break;
pMemBlkInfo = pMemBlkInfo->pNext;
};
return TRUE;
}
/*******************************************************************************************
* DESCRIPTION: This function print the total allocated memory block size.
* INPUT:
* None.
* OUTPUT:
* NOTE:
* It will be used to detect memory leaks, if developer call it when application enter some status.
*******************************************************************************************/
void SmeAllocMemSize()
{
SME_ASCII_TRACE(-1,"Total Allocated Memory Size:%d bytes\n", g_nTotalAllocMem);
}
/*******************************************************************************************
* DESCRIPTION: This function dumps the allocated memory block information list.
* INPUT:
* None.
* OUTPUT:
* NOTE:
* It will be used to detect memory leaks, if developer call it when application enter some status.
*******************************************************************************************/
void SmeDumpMem()
{
struct SME_MEM_BLK_INFO_T *pMemBlkInfo = g_pMemBlkList;
SME_ASCII_TRACE(-1,"Memory dumping:\n");
/* Get memory block information from stack. */
while (pMemBlkInfo!=NULL)
{
SME_ASCII_TRACE(-1,"The memory block %X with size of %d is allocated at %s (%d).\n",
pMemBlkInfo->pUserData,
pMemBlkInfo->nSize,
pMemBlkInfo->sFile,
pMemBlkInfo->nLine);
pMemBlkInfo = pMemBlkInfo->pNext;
};
SME_ASCII_TRACE(-1,"In total, %d memory block(s) is(are) allocated.\n", g_nMemBlkNum);
return;
}
void SmeEnableStateTracking(BOOL bStateTree, BOOL bOutputWin)
{
g_bStateTrackinTree = bStateTree;
g_bStateTrackinOutputWin =bOutputWin;
}
#ifdef _WIN32_WCE
extern BOOL EST_StateTrack(struct SME_APP_T *pDestApp, SME_STATE_T nNewState);
#endif
/*******************************************************************************************
* DESCRIPTION: Send state update information to DS.
* INPUT:
* None.
* OUTPUT:
* NOTE: Format of Data: New State (4 bytes); Application name (char array);
* 1) If new state is -1, the given application is inactive.
* 2) If application name is "", the application thread ends.
If an application runs at multiple threads, only ONE instance will only to run.
*******************************************************************************************/
SME_THREAD_CONTEXT_PT GetThreadContext();
BOOL StateTrack(struct SME_EVENT_T *pEvent,
struct SME_APP_T *pDestApp,
SME_STATE_T nNewState)
{
#if (defined(DEBUG) || defined(_DEBUG))
/* Debug edition */
// Print state tracking in output window:
static BOOL bFirst = TRUE;
if (bFirst)
{
bFirst = FALSE;
OutputDebugAsciiStrToDS("\nStart state tracking:\n(Application, New State[, Event]) \n");
}
if (g_bStateTrackinOutputWin && pDestApp)
{
char sOutput[256];
// State Transition (
if (pEvent)
_snprintf(sOutput, sizeof(sOutput)-1, "(%s, %d, %d)\n",
pDestApp->sAppName, pDestApp->nAppState, pEvent->nEventID);
else
_snprintf(sOutput, sizeof(sOutput)-1, "(%s, %d)\n",
pDestApp->sAppName, pDestApp->nAppState);
OutputDebugAsciiStrToDS(sOutput);
}
if (!g_bStateTrackinTree)
return TRUE;
//// State Tracking in state tree.
#ifdef _WIN32_WCE
return EST_StateTrack(pDestApp, nNewState);
#else
unsigned char byBuf[4+256]={0};
long * pLong = (long *)byBuf;
int nLen=0;
COPYDATASTRUCT cds;
if (g_hWorkspaceWnd == NULL) return FALSE;
if (pDestApp!=NULL && pDestApp->sAppName!=NULL)
nLen = strlen(pDestApp->sAppName);
else nLen = 0;
if (nLen > 255) return FALSE;
*pLong = (long)nNewState;
if (nLen > 0)
strcpy((char*)(&byBuf[4]), pDestApp->sAppName);
cds.dwData = ID_COPYDATA_STATE_TRACK;
cds.cbData = 4+nLen+1;
cds.lpData = byBuf;
SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
return TRUE;
#endif
#else
/* Release edition */
return TRUE;
#endif
}
/*******************************************************************************************
* DESCRIPTION: Output debug string to DS.
* INPUT:
* sDebugStr: NULL-terminated text string.
* nSizeInByte: the total size of text string in Bytes including NULL terminal.
* OUTPUT:
* NOTE:
*******************************************************************************************/
BOOL OutputDebugAsciiStrToDS(const char* sDebugStr)
{
COPYDATASTRUCT cds;
if (g_hWorkspaceWnd == NULL) return FALSE;
cds.dwData = ID_COPYDATA_DEBUG_STR;
cds.cbData = strlen(sDebugStr)+1;
cds.lpData = (void*)sDebugStr;
SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
return TRUE;
}
BOOL OutputDebugUnicodeStrToDS(const unsigned short* sDebugStr)
{
COPYDATASTRUCT cds;
if (g_hWorkspaceWnd == NULL) return FALSE;
cds.dwData = ID_COPYDATA_DEBUG_STR;
cds.cbData = (wcslen(sDebugStr)+1)<<1;
cds.lpData = (void*)sDebugStr;
SendMessage(g_hWorkspaceWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);
return TRUE;
}