Click here to Skip to main content
15,896,726 members
Articles / Programming Languages / C++

Scaling of memory intensive multi-threaded applications to SMMP computers

Rate me:
Please Sign up or sign in to vote.
4.92/5 (74 votes)
2 Jan 200422 min read 284.6K   2K   99  
This article discusses impact of the multithreaded run-time library's memory manager on scaling memory-intensive server applications to Shared Memory Multiprocessor computers.
  • rtl_scaling_sources.zip
    • rtl_scaling_sources
      • processes1cpu_results
        • test.2.1.1.log
        • test.2.2.1.log
        • test.2.2.2.log
        • test.2.3.1.log
        • test.2.3.2.log
        • test.2.3.3.log
        • test.2.4.1.log
        • test.2.4.2.log
        • test.2.4.3.log
        • test.2.4.4.log
        • test.2.6.1.log
        • test.2.6.2.log
        • test.2.6.3.log
        • test.2.6.4.log
        • test.2.6.5.log
        • test.2.6.6.log
      • processes2cpu_results
        • test.1.1.1.log
        • test.1.2.1.log
        • test.1.2.2.log
        • test.1.3.1.log
        • test.1.3.2.log
        • test.1.3.3.log
        • test.1.4.1.log
        • test.1.4.2.log
        • test.1.4.3.log
        • test.1.4.4.log
        • test.1.6.1.log
        • test.1.6.2.log
        • test.1.6.3.log
        • test.1.6.4.log
        • test.1.6.5.log
        • test.1.6.6.log
        • test.2.1.1.log
        • test.2.2.1.log
        • test.2.2.2.log
        • test.2.3.1.log
        • test.2.3.2.log
        • test.2.3.3.log
        • test.2.4.1.log
        • test.2.4.2.log
        • test.2.4.3.log
        • test.2.4.4.log
        • test.2.6.1.log
        • test.2.6.2.log
        • test.2.6.3.log
        • test.2.6.4.log
        • test.2.6.5.log
        • test.2.6.6.log
        • test.3.1.1.log
        • test.3.2.1.log
        • test.3.2.2.log
        • test.3.3.1.log
        • test.3.3.2.log
        • test.3.3.3.log
        • test.3.4.1.log
        • test.3.4.2.log
        • test.3.4.3.log
        • test.3.4.4.log
        • test.3.6.1.log
        • test.3.6.2.log
        • test.3.6.3.log
        • test.3.6.4.log
        • test.3.6.5.log
        • test.3.6.6.log
        • test.4.1.1.log
        • test.4.2.1.log
        • test.4.2.2.log
        • test.4.3.1.log
        • test.4.3.2.log
        • test.4.3.3.log
        • test.4.4.1.log
        • test.4.4.2.log
        • test.4.4.3.log
        • test.4.4.4.log
        • test.4.6.1.log
        • test.4.6.2.log
        • test.4.6.3.log
        • test.4.6.4.log
        • test.4.6.5.log
        • test.4.6.6.log
      • run_processes.bat
      • run_processes_impl.bat
      • run_threads.bat
      • run_threads_impl.bat
      • sources
      • TestSMP2.exe
      • threads1cpu_results
      • threads2cpu_results
///
/// @file	QAFDebug.h "../Include/QAFDebug.h"
/// @brief	Macros for reporting critical errors.
///         
///			This file defines a set of macros that replaces some of 
///			standard ATL and C++ macros. It is strongly recommended to use
///			these macros in all functions. <P>
///				
///			The general guideline of using these macros: if you write code 
///			that should never fail under normal conditions, use these macros to report
///			the cases when something extra-ordinary happens. <P>
///				
///			For the moment these macros are defined in both RELEASE and DEBUG builds.
///			They use OutputDebugString() and log file to report about errors. <P>
///				
///			You must add QAFDebug.cpp to your project in order to use this header file.
///

#ifndef _QAFDEBUG_H_
#define _QAFDEBUG_H_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

///
/// @def QAF_DISABLED
/// @brief Disable all error log staff, you may define it for release builds if you want.
///
/// I ENABLE reporting even in RELEASE builds, it makes the program a bit larger (not affecting performance)
/// but instead I get the full diagnostics for all problems. To disable the error reporting in release builds
/// either uncomment these lines or define the QAF_DISABLED conditional define for the release builds.
/// 
#ifndef QAF_DISABLED
	#define QAF_DISABLED
	// If you want to disable the error log in release builds, uncomment this test of _DEBUG
	//#ifndef _DEBUG
		#undef QAF_DISABLED
	//#endif
#endif

///
/// @def QAF_LOGFILE_DISABLED
/// @brief Disable writing to the error log file, you may define it for release builds if you want.
///
/// I ENABLE writting to the error log file even in RELEASE builds , it makes the program a bit larger 
/// (not affecting performance) but instead I get the full diagnostics for all problems. 
/// To disable writting to the error log file in release builds either uncomment these lines or define the 
/// QAF_LOGFILE_DISABLED conditional define for the release builds.
/// 
#ifndef QAF_LOGFILE_DISABLED
	#define QAF_LOGFILE_DISABLED
	// If you want to disable the error log in release builds, uncomment this test of _DEBUG
	//#ifndef _DEBUG
		#undef QAF_LOGFILE_DISABLED
	//#endif
#endif

///
/// @def QAF_OUTPUTDEBUGSTRING_DISABLED
/// @brief Disable calling OutputDebugString(), I recommend you to define it for release builds.
///
/// I DISABLE reporting using OutputDebugString() in RELEASE builds (since usually nobody will trace it).
///
#ifndef QAF_OUTPUTDEBUGSTRING_DISABLED
	#define QAF_OUTPUTDEBUGSTRING_DISABLED
	// This will enable the reporting back for DEBUG build
	#ifdef _DEBUG
		#undef QAF_OUTPUTDEBUGSTRING_DISABLED
	#endif
#endif

/// @def QAF_UNITTEST_DISABLED
/// @brief Disable unit tests related staff, I recommend you to define it for release builds 
/// and disable unit tests too.
///
/// I DISABLE part of the functionality related to the unit tests in RELEASE build to optimize the code.
/// Defining this directive removes all the synchronization and memory mapped file staff from the code.
///
#ifndef _DEBUG
	#define QAF_UNITTEST_DISABLED
#endif

/// @def QAF_SAVE_UNICODE_AS_UTF8
/// @brief Saves UNICODE error messages to the log file in UTF8 charset. 
///
/// When defined, it makes UNICODE error messages to be saved to the log file in UTF8 charset.
/// It improves readability of the error log file when working with both ASCII and UNICODE modules.
/// Minus is that encoding requires to allocate an additional buffer.
///
#ifdef _UNICODE
	#define QAF_SAVE_UNICODE_AS_UTF8
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Includes
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <winerror.h>
#include <tchar.h>
#include <stdio.h>

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Macros
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def	Q_ASSERT
/// @brief	This macro reports about critical error if @c bCondition evaluates to @c false.
/// @param	bCondition	Any expression that evaluates to @c bool or @c int
///	@return	bool (same as bCondition)
/// @author	Andrew Schetinin
/// @date	September 26, 2002
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Assertion raised  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  false
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code 
///	if( Q_ASSERT( TRUE == bVarShouldAlwaysBeTRUE ) ) // here it will write to the error log
///		do_something();
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_ASSERT( bCondition ) ((bCondition) ? true : (QAFDebug::OutputDebugStringEx( \
		_T(__FILE__), __LINE__, QAF_EXPR(_T(#bCondition)), QAFDEBUG_ERROR_ASSERTION ), false) )
#else 
	#define Q_ASSERT( bCondition ) (bCondition)
#endif

///
/// @def	Q_CHECK
/// @brief	This macro reports about critical error if @c exprConst != @c exprCheck.
/// @param	exprConst	Any expression that evaluates to @c bool or @c int
/// @param	exprCheck	Any expression that evaluates to @c bool or @c int
///	@return	bool (same as (exprConst == exprCheck))
/// @author	Andrew Schetinin
/// @date	April 10, 2003
///
///			This macro output a error report string to the error log (file and/or debug console). 
///         The important thing about this macro is that it outputs 
///         also the actual compared values (error codes, handles, etc.)
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Check error: got 10 (0xA) while expected 0 (0x0)  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  ERROR_SUCCESS == ret
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code 
///	if( Q_CHECK( ERROR_SUCCESS, ret ) ) // here it will write to the error log
///		do_something();
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_CHECK( exprConst, exprCheck ) QAFDebug::ReportCheckError( (exprConst), \
		(exprCheck), QAF_EXPR( _T(#exprConst) _T(" == ") _T(#exprCheck) ), _T(__FILE__), __LINE__ )
#else 
	#define Q_CHECK( exprConst, exprCheck ) ((exprConst) == (exprCheck))
#endif

///
/// @def	Q_INVALID
/// @brief	This macro reports about critical error if @c bCondition evaluates to @c true.
/// @param	bCondition	Any expression that evaluates to @c bool or @c int
///	@return	bool (same as bCondition)
/// @author	Andrew Schetinin
/// @date	December 11, 2002
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Assumption failed  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  2, The system cannot find the file specified.
/// 	expression:  NULL == lpszStringParam
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for testing input parameters or error conditions.
/// @code 
///	if( Q_INVALID( NULL == lpszStringParam ) ) // here it will write to the error log
///		return E_INVALIDARG;
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_INVALID( bCondition ) ((bCondition) ? (QAFDebug::OutputDebugStringEx( \
		_T(__FILE__), __LINE__, QAF_EXPR(_T(#bCondition)), QAFDEBUG_ERROR_INVALID_ASSUMPTION ), true) : false )
#else 
	#define Q_INVALID( bCondition ) (bCondition)
#endif

///
/// @def	Q_SUCCEEDED
/// @brief	Add reporting critical errors to the standard SUCCEEDED macro. 
/// @param	Status	HRESULT result code
///	@return	bool. @c true means "no error"
/// @author	Andrew Schetinin
/// @date	September 26, 2002
///
///			Generic test for success on any status value (non-negative numbers
///			indicate success). <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///			Another use for this macro is to report about a critical error in your function 
///			that is returned to the calling function.  
/// @code 
///	if( Q_SUCCEEDED(hr) ) // here it will write to the error log
///		do_something();
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_SUCCEEDED(hResultExpr) SUCCEEDED(Q_ERROR(hResultExpr))
#else 
	#define Q_SUCCEEDED(hResultExpr) SUCCEEDED(hResultExpr)
#endif

///
/// @def	Q_FAILED
/// @brief	Add reporting critical errors to the standard FAILED macro. 
/// @param	Status	HRESULT result code
///	@return	bool. @c true means "there is an error!"
/// @author	Andrew Schetinin
/// @date	September 26, 2002
///
///			Generic test for failure on any status value. <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///			Another use for this macro is to report about the error before returning from your function.
/// @code 
///	if( Q_FAILED(hr) ) // here it will write to the error log
///		return hr;
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_FAILED(hResultExpr) FAILED(Q_ERROR(hResultExpr))
#else 
	#define Q_FAILED(hResultExpr) FAILED(hResultExpr)
#endif

///
/// @def	Q_ERROR
/// @brief	Reports critical errors returned from your function before returning. 
/// @param	Status	HRESULT result code
///	@return	HRESULT (same as Status)
/// @author	Andrew Schetinin
/// @date	September 26, 2002
///
///			Generic test for failure on any status value. <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : The component's CLSID is missing or corrupt.  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0
/// 	expression:  Q_FAILED(0x800401F3)
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for testing return HRESULTs that usually always should return S_OK or S_FALSE.
///			Another use for this macro is to test return values of generic functions that 
///         usually never fail.
/// @code 
///	// This will report about the error in the exact place where it first happened.
///	return Q_ERROR( CoCreateInstance( clsid, NULL, dwCtx, IID_IDispatch, (void**)(&this->p) ) );
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_ERROR(hResultExpr) (QAFDebug::ReportComError( hResultExpr, _T(__FILE__), __LINE__ ))
#else 
	#define Q_ERROR(hResultExpr) (hResultExpr)
#endif

///
/// @def	Q_ERROR_SUCCESS
/// @brief	Reports critical errors from ERROR_SUCCESS group. 
/// @param	dwError	Error result code.
///	@return	bool (dwError == ERROR_SUCCESS)
/// @author	Andrew Schetinin
/// @date	March 19, 2003
///
///			Generic test for failure on error code. <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : More data is available.  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  123, More data is available. 
/// 	expression:  ERROR_SUCCESS != 123
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for testing return error codes that usually always should return ERROR_SUCCESS.
///			Another use for this macro is to test return values of generic functions that 
///         usually never fail.
/// @code 
///	// This will report about the error in the exact place where it first happened.
/// DWORD dwRes = RegQueryValueEx( hKey, _T("Value"), NULL, &dwType, (LPBYTE)szBuf, &dwSize );
///	if( Q_ERROR_SUCCESS(dwRes) ) 
///     /*do something*/;
/// @endcode 
///

#ifndef QAF_DISABLED
#define Q_ERROR_SUCCESS(dwErrorExpr) (ERROR_SUCCESS == QAFDebug::ReportWinError( dwErrorExpr, _T(__FILE__), __LINE__ ))
#else 
#define Q_ERROR_SUCCESS(dwErrorExpr) (ERROR_SUCCESS == (dwErrorExpr))
#endif

///
/// @def	Q_LOG
/// @brief	Reports critical errors with a string message. 
/// @param	lpszMessage	LPCTSTR message string
///	@return	void
/// @author	Andrew Schetinin
/// @date	January 28, 2003
///
///			This is a macro for reporting about critical errors in a user-understandable format. 
///			Generally it is preferable to Q_ASSERT(false). <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : My custom error message.  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0 
/// 	expression:  Error message
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for IFs without ELSE or testing return values of functions.
/// @code 
///	catch( ... )
///	{
///		Q_LOG( _T("Unknown exception catched") ); // here it will report about all exceptions
///	}
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_LOG(lpszMessage) (QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, QAFDEBUG_ERROR_LOG, lpszMessage ))
#else 
	#define Q_LOG(lpszMessage) (lpszMessage)
#endif

///
/// @def	Q_EXCEPTION
/// @brief	Reports critical exceptions. 
/// @param	e	CException object instance (MFC-style)
///	@return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///			This is a special macro to report about an MFC-style exception. <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Cannot open file  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0 
/// 	expression:  e
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for reporting about exceptions in CATCH clauses.
///			You may define your own exception classes (without MFC support), just define 
///			the GetErrorMessage() method and this macro will work.
/// @code 
///	catch( CMyException & e ) 
///	{
///		Q_EXCEPTION(e); // here it will write to the error log with the error message
///	}
///	catch( ... )
///	{
///		Q_LOG( _T("Unknown exception catched") ); // here it will report about any other exception
///	}
/// @endcode 
///

#ifndef QAF_DISABLED
	#define Q_EXCEPTION(e) \
	do { \
		TCHAR szBuf[250]; \
		if( (NULL == e) || (! e->GetErrorMessage( szBuf, 250, NULL )) ) \
			strcpy( szBuf, QAFDEBUG_ERROR_NO_MESSAGE ); \
		QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, QAF_EXPR(_T(#e)), szBuf ); \
	} while( false ) 
#else 
	#define Q_EXCEPTION(e) (e)
#endif

///
/// @def	Q_MAPI_ERROR
/// @brief	Reports critical exceptions. 
/// @param	hresult			(HRESULT) - MAPI error code
/// @param	piObjectMAPI	(any of MAPI interfaces)
///	@return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///			This is a special macro to report about an MAPI error. <P>
///
///			This macro output a error report string to the error log (file and/or debug console). 
///			The string looks like that: 
/// @code 
/// c:\SharedUnits\Dev\QMSOColl\QMSOColl.cpp(56) : Cannot login to MAPI storage  
/// 	time:        2003-12-08 16:11:34:003
/// 	process:     0x000006F4
/// 	thread:      0x00000148
/// 	application: c:\ErrorLog_demo\Debug\ErrorLog.exe <1.0.0.3>
/// 	module:      c:\SharedUnits\Dev\QMSOColl\Debug\QMSOColl.dll <1.0.0.10>
/// 	last error:  0 
/// 	expression:  hr
/// @endcode 
///			It is recommended to use this macro in all places where any error 
///			means program crash or non-predictable behavior of dependent code.
///			It will help to detect where first the error appeared.
///         Recommended for reporting about critical MAPI errors.
/// @code 
///	Q_MAPI_ERROR( hresult, piObjectMAPI );
/// @endcode 
///

#ifndef QAF_DISABLED
	// taken from MAPI Inside, they do not use SUCCEEDED 
	#define Q_MAPI_ERROR( hresult, piObjectMAPI ) \
	do { \
		HRESULT hrtemp39472 = hresult; \
		LPMAPIERROR pErr = NULL; \
		if( Q_ASSERT( NULL != piObjectMAPI ) \
			&& Q_ASSERT( S_OK == piObjectMAPI->GetLastError( hrtemp39472, 0, &pErr ) ) \
			&& Q_ASSERT( NULL != pErr ) ) \
		{ \
			int BUF_SIZE = 128 + _tcslen(pErr->lpszError) + _tcslen(pErr->lpszComponent); \
			LPTSTR szBuf = (LPTSTR)malloc( BUF_SIZE * sizeof(TCHAR) ); \
			if( Q_ASSERT( NULL != szBuf ) ) \
			{ \
				_stprintf( szBuf, _T("MAPI Error: %s. Component: %s. Version: %08X. ") \
					_T("LowLevelError: %08X. Context: %08X."), \
					((NULL != pErr->lpszError) ? pErr->lpszError : _T("")), \
					((NULL != pErr->lpszComponent) ? pErr->lpszComponent : _T("")), \
					pErr->ulVersion, pErr->ulLowLevelError, pErr->ulContext ); \
				TCHAR szHRBuf[20] = { 0 }; \
				_stprintf( szHRBuf, _T("Q_FAILED(%08X)"), hrtemp39472 ); \
				QAFDebug::OutputDebugStringEx( _T(__FILE__), __LINE__, szHRBuf, szBuf ); \
				free( szBuf ); \
			} \
		} \
	} while( false )
#else 
	#define Q_MAPI_ERROR( hresult, piObjectMAPI ) (hresult)
#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Defines related to the unit tests
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def	Q_ENABLE_DEBUG_LOG
/// @brief	Enable the error log in case if it was disabled because of the previous unit test failure. 
///	@return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///			This is a special macro for unit test functions. This macro ensures that the 
///			reporting is switched on. It is recommended to put it at the beginning of a single 
///			tests case (at the beginning of the function).
/// @code 
///	void CUnitTest::testCase01( void ) 
///	{
///		Q_ENABLE_DEBUG_LOG; // switch the error log on
///		CPPUNIT_ASSERT( Q_SUCCEEDED( QAFGetRegKey( HKCU_C_END, &str ) ) ); // write to the log if it fails
///		...
///	}
/// @endcode 
///

#define Q_ENABLE_DEBUG_LOG ;
#ifndef QAF_DISABLED
	#ifndef QAF_UNITTEST_DISABLED
		#undef Q_ENABLE_DEBUG_LOG
		#define Q_ENABLE_DEBUG_LOG QAFDebug::tryEnable();
	#endif 
#endif

///
/// @def	Q_SILENT
/// @brief	Temporary disable the error log and evaluate the expression. 
/// @param	expr	Any expression
///	@return	void
/// @author	Andrew Schetinin
/// @date	November 18, 2002
///
///			This is a special macro for unit test functions. It is useful for testing the wrong cases 
///			(for example, passing wrong parameters and checking that the function fails). 
///			For wrong test cases we do not want to report about errors because we want them to happen.
/// @code 
///	void CUnitTest::testCase01( void ) 
///	{
///		Q_ENABLE_DEBUG_LOG; // switch the error log on
///		CPPUNIT_ASSERT( Q_SUCCEEDED( QAFGetRegKey( HKCU_C_END, &str ) ) ); // write to the log if it fails
///		Q_SILENT( CPPUNIT_ASSERT( Q_FAILED( QAFGetRegKey( NULL, NULL ) ) ) ); // do not write to log 
///		...
///	}
/// @endcode 
///

#define Q_SILENT(expr) expr;
#ifndef QAF_DISABLED
	#ifndef QAF_UNITTEST_DISABLED
		#undef Q_SILENT
		#define Q_SILENT(expr) \
		do { \
			bool bLogDisabled = QAFDebug::tryDisable(); \
			expr; \
			if( bLogDisabled ) \
				QAFDebug::tryEnable(); \
		} while( false )
	#endif 
#endif

///
/// @def	Q_RETURN
/// @brief	If input HRESULT failed, then macro returns it.
/// @param	hr	HRESULT
///	@return	hr, if
/// @author	Andrey Krasnopolsky
/// @date	August 05, 2003
///
///			This macro is for testing HRESULTs. It gets HRESULT as input, tests it and
///			returns the same HRESULT in case of error.
///			Very useful if you need to break function execution in case of error.
/// @code 
/// // if the method returns E_XXX, it will exit and return the error code
///	Q_RETURN( CallSomeMethod() ); 
/// @endcode 
///

#define Q_RETURN(hr) do { HRESULT hrtmp83457 = hr; if( Q_SUCCEEDED(hrtmp83457) ) ; else return hrtmp83457; } while( false )

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Conditional defines
///////////////////////////////////////////////////////////////////////////////////////////////////////////

///
/// @def Q_SET_MODULE
/// @brief This macro allows setting the module name for additional logging options.
/// @param LPCTSTR, name of the DLL or EXE, 0-terminated, not longer than MAX_PATH characters.
/// @return void
/// @author	Andrew Schetinin
/// @date	December 7, 2003
///
/// This will switch on logging of the file name and version number of the current EXE or DLL.
/// It will be logged after the file name and path of the hosting process, 
/// only the module file name without path is logged: FILENAME.DLL(1.0.0.1)
///
#if !defined(QAF_DISABLED)
	#define Q_SET_MODULE( szModuleName ) QAFDebug::SetModule( szModuleName )
#else
	#define Q_SET_MODULE( szModuleName )
#endif

// Only if reporting is not disabled
#ifndef QAF_DISABLED

/// The name of the mutex for synchronizing the unit test support staff.
const LPCTSTR QAFDEBUG_SILENCE_MUTEX = _T("QAFDebugMutex001A");

/// Subfolder in the application data folder
const LPCTSTR QAFDEBUG_LOG_SUBFOLDER = _T("Company\\Log\\");

/// Name of the environment variable that may set the output debug log folder.
const LPCTSTR QAFDEBUG_LOG_ENV_VAR = _T("QAFERRORLOGPATH");

/// @brief Maximum log file size
/// 
/// Maximum log file size (there are two log files - one current and second previous).
/// When the current file exceeds this limit, it is renamed to the second name. 
/// The size is in TCHARs (it is translated to bytes). Generally 1 record takes about 
/// 300 TCHARs, so I reserve space for about 800-1500 records with 250,000 TCHARs limit.
/// This should be enough and it guaranties that the both log files together will not be 
/// larger than 1 Mb for UNICODE and 500 Kb for REGULAR build.
const DWORD QAFDEBUG_LOG_FILE_MAX_SIZE = (250 * 1024 * sizeof(TCHAR));

/// The current error log file name
const LPCTSTR QAFDEBUG_LOG_FILE_NAME = _T("error.log");

/// The previous error log file name
const LPCTSTR QAFDEBUG_LOG_OLD_FILE_NAME = _T("error.old.log");

/// The name of the memory-mapped-file that stores the shared flags
const LPCTSTR QDEBUG_SHMEMFILE = _T("QAFDbgMemFile01");

/// Errors in the reporting engine
#define QAFDEBUG_ERROR_PREFIX _T("Debug System Error --> ")

/// Fixed error message for assertion raised
#define QAFDEBUG_ERROR_ASSERTION _T("Assertion raised\r\n")

/// Fixed error message for invalid assumption raised
#define QAFDEBUG_ERROR_INVALID_ASSUMPTION _T("Invalid assumption is raised\r\n")

/// Fixed error message for unknown error
#define QAFDEBUG_ERROR_NO_MESSAGE _T("[Could not find any description for the error]\r\n")

/// Fixed expression for the custom error message
#define QAFDEBUG_ERROR_LOG _T("Error Message")

///
/// @def QAF_EXPR
/// @brief The Expression string is printed to the error log only in DEBUG builds.
///
///        It makes the binary a bit smaller for the release build.
///
#ifdef _DEBUG
	#define QAF_EXPR(expression) (expression) 
#else
	#define QAF_EXPR(expression) (NULL) 
#endif

#endif

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// class CQAFDebug - this is a service class - do not use it directly!
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef QAF_DISABLED

///
/// @namespace QAFDebug
/// @brief Namespace QAFDebug hides the debug functions from global scope.
///
namespace QAFDebug
{
	///
	/// @brief Try to enable the error log 
	/// @return bool true if the log is enabled successfully
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Try to enable the error log. This function is used only in unit tests.  
	///	Do not use it in your code!
	///
	bool tryEnable( void );
	
	///
	/// @brief Try to disable the error log 
	/// @return bool true if the log is disabled successfully
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Try to disable the error log. This function is used only in unit tests.  
	///	Do not use it in your code!
	///
	bool tryDisable( void );
	
	///
	/// @brief Reports about critical errors  
	/// @param	szFilename	LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const int - the line number returned by _LINE_
	/// @param	szExpression	LPCTSTR - the expression where the error was detected
	/// @param	szErrorMessage	LPCTSTR - the error message generated
	/// @param	pdwLastError	LPDWORD  - the last error code must be restored
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors.  
	///	This function is used by debug macroses. Do not use it in your code!
	///
	void OutputDebugStringEx( LPCTSTR szFilename, const int iLine, LPCTSTR szExpression, 
		LPCTSTR szErrorMessage, const LPDWORD pdwLastError = NULL );
	
	///
	/// @brief Reports about critical errors if HRESULT is failed  
	/// @param	hrStatus	HRESULT - tested on failure
	/// @param	szFile		LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const int - the line number returned by _LINE_
	///	@return	HRESULT		the same hrStatus that it received
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors if HRESULT is failed.  
	///	This function is used by debug macroses. Do not use it in your code!
	///
	HRESULT qafReportComError( const HRESULT hrStatus, LPCTSTR szFile, const int iLine );
	
	///
	/// @brief Reports about critical errors if dwError != ERROR_SUCCESS.  
	/// @param	dwError		DWORD - tested on failure
	/// @param	szFile		LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const int - the line number returned by _LINE_
	///	@return	DWORD		the same as dwError
	/// @author	Andrew Schetinin
	/// @date	March 19, 2003
	///
	/// Reports critical errors if dwError != ERROR_SUCCESS.  
	///	This function is used by debug macroses. Do not use it in your code!
	///
	DWORD ReportWinError( const DWORD dwError, LPCTSTR szFile, const int iLine );

	///
	/// @brief Reports about critical errors if HRESULT is failed  
	/// @param	hrStatus	HRESULT - tested on failure
	/// @param	szFile		LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const int - the line number returned by _LINE_
	///	@return	HRESULT		the same hrStatus that it received
	/// @author	Andrew Schetinin
	/// @date	November 20, 2002
	///
	/// Reports critical errors if HRESULT is failed.  
	///	This function is used by debug macroses. Do not use it in your code!
	///
	inline HRESULT ReportComError( const HRESULT hrStatus, LPCTSTR szFile, const int iLine )
	{
		if( FAILED(hrStatus) )
			qafReportComError( hrStatus, szFile, iLine );
		return hrStatus;
	}

	///
	/// @brief Reports about critical errors if ulConstExpr != ulCheckExpr  
	/// @param	ulConstExpr	unsigned long - the expected expression
	/// @param	ulCheckExpr	unsigned long - the returned expression
	/// @param	szExpr		LPCTSTR - the expression of ulCheckExpr
	/// @param	szFile		LPCTSTR - the file name returned by _FILE_
	/// @param	iLine		const int - the line number returned by _LINE_
	///	@return	bool		the same as (ulConstExpr == ulCheckExpr)
	/// @author	Andrew Schetinin
	/// @date	July 2, 2003
	///
	/// Reports critical errors if ulConstExpr != ulCheckExpr.  
	///	This function is used by debug macroses. Do not use it in your code!
	///
	inline bool ReportCheckError( const unsigned long ulConstExpr, 
		const unsigned long ulCheckExpr, LPCTSTR szExpr, LPCTSTR szFile, 
		const int iLine )
	{
		if( ulConstExpr == ulCheckExpr )
			return true;
		TCHAR szErrorMessage[100] = { 0 };
		_stprintf( szErrorMessage, _T("Check error: got %d (0x%X) while expected %d (0x%X)"), 
			ulCheckExpr, ulCheckExpr, ulConstExpr, ulConstExpr );
		OutputDebugStringEx( szFile, iLine, szExpr, szErrorMessage );
		return false;
	}

	///
	/// @brief Return an accessible directory name for all log files.  
	/// @param	lpszDirBuf	LPTSTR buffer for the directory name 
	/// @param	dwMaxLen	DWORD size of the buffer in characters (including the trailing zero)
	///	@return	DWORD		length of the returned string or 0 in case of error
	/// @author	Andrew Schetinin
	/// @date	February 6, 2003
	///
	/// Get the buffer and the buffer length in TCHAR characters including tailing 0x00(00).
	/// Returns the length of the written string or 0 if the directory name cannot be generated.
	/// The directory name is constructed from the: <p>
	/// 1. Try get the folder path from the environment variable QAFDEBUG_LOG_ENV_VAR <p>
	/// 2. Try CSIDL_APPDATA (C:\Documents and Settings\username\Application Data) + QAFDEBUG_LOG_SUBFOLDER <p>
	/// 3. Try CSIDL_COMMON_APPDATA (C:\Documents and Settings\All Users\Application Data) 
	///    + QAFDEBUG_LOG_SUBFOLDER <p>
	/// 4. Return 0 <p>
	/// If the folders are missing on the disk, they are created.
	///
	DWORD GetLogDir( LPTSTR lpszDirBuf, const DWORD dwMaxLen );

	///
	/// @brief This function allows setting the module name for additional logging options.
	/// @param LPCTSTR, name of the DLL or EXE, 0-terminated, not longer than MAX_PATH characters.
	/// @return void
	/// @author	Andrew Schetinin
	/// @date	December 7, 2003
	///
	/// This will switch on logging of the file name and version number of the current EXE or DLL.
	/// It will be logged after the file name and path of the hosting process, 
	/// only the module file name without path is logged: FILENAME.DLL(1.0.0.1)
	///
	void SetModule( LPCTSTR szModuleName );
		
}

#endif


///////////////////////////////////////////////
// END OF FILE
///////////////////////////////////////////////
#endif

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Team Leader OpTier
Israel Israel
Programming computers since entering the university in 1992, but dreaming of programming long time before putting hands on my first computer.

Experienced in cross-platform software development using C++ and Java, as well as rapid GUI development using Delphi/C#. Strong background in networking, relational databases, Web development, and mobile platforms.

Like playing guitar, visiting historical sites (not in the Internet, in the car Smile | :) ) and cooking meat with friends (sorry about vegetarians). Look for more information on www.schetinin.com

Comments and Discussions