Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

603 lines
19 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 1999-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dndbg.c
  6. * Content: debug support for DirectPlay8
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 05-20-99 aarono Created
  12. * 07-16-99 johnkan Fixed include of OSInd.h, defined WSPRINTF macro
  13. * 07-19-99 vanceo Explicitly declared OutStr as returning void for NT
  14. * Build environment.
  15. * 07-22-99 a-evsch Check for multiple Inits, and release CritSec when DebugPrintf
  16. * returns early.
  17. * 08-02-99 a-evsch Added LOGPF support. LW entries only go into shared-file log
  18. * 08-31-99 johnkan Removed include of <OSIND.H>
  19. * 02-17-00 rodtoll Added Memory / String validation routines
  20. * 05-23-00 RichGr IA64: Changed some DWORDs to DWORD_PTRs to make va_arg work OK.
  21. * 07-16-00 jchauvin IA64: Added %p parsing to change back to %x for Win9x machines in DebugPrintf, DebugPrintfNoLock, LogPrintf
  22. * 07-24-00 RichGr IA64: As there's no separate build for Win9x, added code to detect Win9x for the %p parse-and-replace.
  23. * 07-29-00 masonb Rewrite to add logging by subcomponent, perf improvements, process ID
  24. * 08/28/2000 masonb Voice Merge: Modified asm in DebugPrintf to preserve registers that may have affected Voice
  25. * 03/29/2001 RichGr If DPINST is defined for Performance Instrumentation, allow free build to pick up the code.
  26. *
  27. * Notes:
  28. *
  29. * Use /Oi compiler option for strlen()
  30. *
  31. ***************************************************************************/
  32. #include "dncmni.h"
  33. #include "memlog.h"
  34. #if defined(DBG) || defined(DPINST)
  35. void DebugPrintfInit(void);
  36. void DebugPrintfFini(void);
  37. // The constructor of this will be called prior to DllMain and the destructor
  38. // after DllMain, so we can be assured of having the logging code properly
  39. // initialized and deinitialized for the life of the module.
  40. struct _InitDbg
  41. {
  42. _InitDbg() { DebugPrintfInit(); }
  43. ~_InitDbg() { DebugPrintfFini(); }
  44. } DbgInited;
  45. //===============
  46. // Debug support
  47. //===============
  48. /*******************************************************************************
  49. This file contains support for the following types of logging:
  50. 1. Logging to a VXD (Win9x only)
  51. 2. Logging to a shared memory region
  52. 3. Logging to the Debug Output
  53. 4. Logging to a message box
  54. 5. FUTURE: Logging to a file
  55. General:
  56. ========
  57. Debug Logging and playback is designed to operate on both Win9x and
  58. Windows NT (Windows 2000). A shared file is used to capture information
  59. and can be played back using dp8log.exe.
  60. Under NT you can use the 'dt' command of NTSD to dump structures. For
  61. example:
  62. dt DIRECTPLAYOBJECT <some memory address>
  63. will show all of the members of the DIRECTPLAYOBJECT structure at the
  64. specified address. Some features are available only in post-Win2k
  65. versions of NTSD which can be obtained at http://dbg.
  66. Logging:
  67. ========
  68. Debug Logging is controlled by settings in the WIN.INI file, under
  69. the section heading [DirectPlay8]. There are several settings:
  70. debug=9
  71. controls the default debug level. All messages, at or below that debug level
  72. are printed. You can control logging by each component specified in the
  73. g_rgszSubCompName member by adding its name to the end of the 'debug' setting:
  74. debug.addr=9
  75. sets the logging level for the addressing subcomponent to 9, leaving all
  76. others at either their specified level or the level specified by 'debug'
  77. if there is no specific level specified.
  78. The second setting controls where the log is seen. If not specified, all
  79. debug logs are sent through the standard DebugPrint and will appear in a
  80. debugger if it is attached.
  81. log=0 {no debug output}
  82. log=1 {spew to console only}
  83. log=2 {spew to shared memory log only}
  84. log=3 {spew to console and shared memory log}
  85. log=4 {spew to message box}
  86. This setting can also be divided by subcomponent, so:
  87. log=3
  88. log.protocol=2
  89. sends logs for the 'protocol' subcomponent to the shared memory log only, and
  90. all other logs to both locations.
  91. example win.ini...
  92. [DirectPlay8]
  93. Debug=7 ; lots of spew
  94. log=2 ; don't spew to debug window
  95. [DirectPlay8]
  96. Debug=0 ; only fatal errors spewed to debug window
  97. Asserts:
  98. ========
  99. Asserts are used to validate assumptions in the code. For example
  100. if you know that the variable jojo should be > 700 and are depending
  101. on it in subsequent code, you SHOULD put an assert before the code
  102. that acts on that assumption. The assert would look like:
  103. DNASSERT(jojo>700);
  104. Asserts generally will produce 3 lines of debug spew to highlight the
  105. breaking of the assumption. You can add text to your asserts by ANDing:
  106. DNASSERT(jojo>700 && "Jojo was too low");
  107. Will show the specified text when the assert occurs. For testing, you might
  108. want to set the system to break in on asserts. This is done in the
  109. [DirectPlay8] section of WIN.INI by setting BreakOnAssert=TRUE:
  110. [DirectPlay8]
  111. Debug=0
  112. BreakOnAssert=1
  113. Verbose=1
  114. The Verbose setting enables logging of file, function, and line information.
  115. Debug Breaks:
  116. =============
  117. When something really severe happens and you want the system to break in
  118. so that you can debug it later, you should put a debug break in the code
  119. path. Some people use the philosophy that all code paths must be
  120. verified by hand tracing each one in the debugger. If you abide by this
  121. you should place a DEBUG_BREAK() in every code path and remove them
  122. from the source as you trace each. When you have good coverage but
  123. some unhit paths (error conditions) you should force those paths in
  124. the debugger.
  125. Debug Logging to Shared Memory Region:
  126. ======================================
  127. All processes will share the same memory region, and will log the specified amount
  128. of activity. The log can be viewed with the DPLOG.EXE utility.
  129. Debug Logging to Debug Output:
  130. ==============================
  131. This option uses OutputDebugString to log the specified amount of activity.
  132. Debug Logging to Message Box:
  133. ==============================
  134. This option uses MessageBox to log the specified amount of activity.
  135. ==============================================================================*/
  136. #undef DPF_SUBCOMP
  137. #define DPF_SUBCOMP DN_SUBCOMP_COMMON
  138. #define ASSERT_BUFFER_SIZE 8192
  139. #define ASSERT_BANNER_STRING "************************************************************"
  140. #define ASSERT_MESSAGE_LEVEL 0
  141. #define PROF_SECT _T("DirectPlay8")
  142. DWORD g_dwMemLogNumEntries = 40000; // Default Num entries for MEM log, settable in win.ini
  143. DWORD g_dwMemLogLineSize = DPLOG_MAX_STRING; // Default number of bytes per log entry
  144. //
  145. // Globals for shared memory based logging
  146. //
  147. #ifndef DPNBUILD_SINGLEPROCESS
  148. HANDLE g_hMemLogFile = 0; // NOTE: This is 0 because CreateFileMapping returns 0 on failure
  149. HANDLE g_hMemLogMutex = 0; // NOTE: This is 0 because CreateMutex returns 0 on failure
  150. #endif // ! DPNBUILD_SINGLEPROCESS
  151. PSHARED_LOG_FILE g_pMemLog = 0;
  152. BOOL g_fMemLogInited = FALSE;
  153. #ifndef DPNBUILD_SINGLEPROCESS
  154. DWORD g_fAssertGrabMutex = FALSE;
  155. #endif // ! DPNBUILD_SINGLEPROCESS
  156. // Values for g_rgDestination
  157. #define LOG_TO_DEBUG 1
  158. #define LOG_TO_MEM 2
  159. #define LOG_TO_MSGBOX 4
  160. LPTSTR g_rgszSubCompName[] =
  161. {
  162. _T("UNK"), // DN_SUBCOMP_GLOBAL 0
  163. _T("CORE"), // DN_SUBCOMP_CORE 1
  164. _T("ADDR"), // DN_SUBCOMP_ADDR 2
  165. _T("LOBBY"), // DN_SUBCOMP_LOBBY 3
  166. _T("PROTOCOL"), // DN_SUBCOMP_PROTOCOL 4
  167. _T("VOICE"), // DN_SUBCOMP_VOICE 5
  168. _T("DPNSVR"), // DN_SUBCOMP_DPNSVR 6
  169. _T("WSOCK"), // DN_SUBCOMP_WSOCK 7
  170. _T("MODEM"), // DN_SUBCOMP_MODEM 8
  171. _T("COMMON"), // DN_SUBCOMP_COMMON 9
  172. _T("NATHELP"), // DN_SUBCOMP_NATHELP 10
  173. _T("TOOLS"), // DN_SUBCOMP_TOOLS 11
  174. _T("THREADPOOL"), // DN_SUBCOMP_THREADPOOL 12
  175. _T("MAX"), // DN_SUBCOMP_MAX 13 // NOTE: this should never get used, but
  176. // is needed due to the way DebugPrintfInit
  177. // is written, since it reads one past the end.
  178. };
  179. #define MAX_SUBCOMPS (sizeof(g_rgszSubCompName)/sizeof(g_rgszSubCompName[0]) - 1)
  180. #ifdef _XBOX
  181. #pragma TODO(vanceo, "See below, fix DNGetProfileInt")
  182. extern UINT g_rgLevel[MAX_SUBCOMPS];
  183. extern UINT g_rgDestination[MAX_SUBCOMPS];
  184. extern UINT g_rgBreakOnAssert[MAX_SUBCOMPS];
  185. #pragma TODO(vanceo, "Don't define these globals, force the application to decide logging levels?")
  186. // = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12
  187. UINT g_rgLevel[MAX_SUBCOMPS] = {1, 9, 1, 1, 7, 1, 1, 7, 1, 1, 1, 1, 7};
  188. UINT g_rgDestination[MAX_SUBCOMPS] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2};
  189. //UINT g_rgDestination[MAX_SUBCOMPS] = {3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2};
  190. UINT g_rgBreakOnAssert[MAX_SUBCOMPS] = {1, 3, 1, 1, 3, 1, 1, 3, 1, 1, 1, 1, 3};
  191. #else // ! _XBOX
  192. UINT g_rgLevel[MAX_SUBCOMPS] = {0};
  193. UINT g_rgDestination[MAX_SUBCOMPS] = {LOG_TO_DEBUG | LOG_TO_MEM};
  194. UINT g_rgBreakOnAssert[MAX_SUBCOMPS] = {1};// if non-zero, causes DEBUG_BREAK on false asserts.
  195. #endif // ! _XBOX
  196. // if TRUE, file/line/module information is printed and logged.
  197. DWORD g_fLogFileAndLine = FALSE;
  198. // Create a shared file for logging information on the fly
  199. // This support allows the current log to be dumped from the
  200. // user mode DP8LOG.EXE application. This is useful when debugging
  201. // in MSSTUDIO or in NTSD. When DP8LOG.EXE is invoked, note that
  202. // the application will get halted until the log is completely dumped
  203. // so it is best to dump the log to a file.
  204. #undef DPF_MODNAME
  205. #define DPF_MODNAME "InitMemLogString"
  206. static BOOL InitMemLogString(VOID)
  207. {
  208. if(!g_fMemLogInited)
  209. {
  210. BOOL fInitLogFile = TRUE;
  211. #ifdef DPNBUILD_SINGLEPROCESS
  212. g_pMemLog = (PSHARED_LOG_FILE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (DPLOG_HEADERSIZE + (DPLOG_ENTRYSIZE*g_dwMemLogNumEntries)));
  213. if (!g_pMemLog)
  214. {
  215. return FALSE;
  216. }
  217. #else // ! DPNBUILD_SINGLEPROCESS
  218. g_hMemLogFile = CreateFileMapping(INVALID_HANDLE_VALUE, DNGetNullDacl(), PAGE_READWRITE, 0, (DPLOG_HEADERSIZE + (DPLOG_ENTRYSIZE*g_dwMemLogNumEntries)), GLOBALIZE_STR _T(BASE_LOG_MEMFILENAME));
  219. if (!g_hMemLogFile)
  220. {
  221. return FALSE;
  222. }
  223. if (GetLastError() == ERROR_ALREADY_EXISTS)
  224. {
  225. fInitLogFile = FALSE;
  226. }
  227. g_hMemLogMutex = CreateMutex(DNGetNullDacl(), FALSE, GLOBALIZE_STR _T(BASE_LOG_MUTEXNAME));
  228. if (!g_hMemLogMutex)
  229. {
  230. CloseHandle(g_hMemLogFile);
  231. g_hMemLogFile = 0;
  232. return FALSE;
  233. }
  234. g_pMemLog = (PSHARED_LOG_FILE)MapViewOfFile(g_hMemLogFile, FILE_MAP_ALL_ACCESS,0,0,0);
  235. if (!g_pMemLog)
  236. {
  237. CloseHandle(g_hMemLogMutex);
  238. g_hMemLogMutex = 0;
  239. CloseHandle(g_hMemLogFile);
  240. g_hMemLogFile = 0;
  241. return FALSE;
  242. }
  243. #endif // ! DPNBUILD_SINGLEPROCESS
  244. // NOTE: The above 3 functions do return NULL in the case of a failure,
  245. // not INVALID_HANDLE_VALUE
  246. if (fInitLogFile)
  247. {
  248. g_pMemLog->nEntries = g_dwMemLogNumEntries;
  249. g_pMemLog->cbLine = g_dwMemLogLineSize;
  250. g_pMemLog->iWrite = 0;
  251. }
  252. else
  253. {
  254. // This happens when someone before us has already created the mem log. Could be a previous DPlay instance or TestNet.
  255. g_dwMemLogNumEntries = g_pMemLog->nEntries;
  256. g_dwMemLogLineSize = g_pMemLog->cbLine;
  257. }
  258. if (g_dwMemLogNumEntries && g_dwMemLogLineSize)
  259. {
  260. g_fMemLogInited = TRUE;
  261. }
  262. }
  263. return g_fMemLogInited;
  264. }
  265. // Log a string to a shared file. This file can be dumped using the
  266. // DPLOG.EXE utility.
  267. //
  268. // dwLength is in bytes and does not include the '\0'
  269. //
  270. void MemLogString(LPCTSTR str, size_t dwLength)
  271. {
  272. PMEMLOG_ENTRY pEntry;
  273. size_t cbCopy;
  274. // If this isn't inited, InitMemLogString failed earlier
  275. if(!g_fMemLogInited)
  276. {
  277. return;
  278. }
  279. #ifndef DPNBUILD_SINGLEPROCESS
  280. WaitForSingleObject(g_hMemLogMutex, INFINITE);
  281. #endif // ! DPNBUILD_SINGLEPROCESS
  282. pEntry = (PMEMLOG_ENTRY)(((PUCHAR)(g_pMemLog + 1)) + (g_pMemLog->iWrite * (sizeof(MEMLOG_ENTRY) + g_dwMemLogLineSize)));
  283. g_pMemLog->iWrite = (g_pMemLog->iWrite + 1) % g_dwMemLogNumEntries;
  284. #ifndef DPNBUILD_SINGLEPROCESS
  285. ReleaseMutex(g_hMemLogMutex);
  286. #endif // ! DPNBUILD_SINGLEPROCESS
  287. pEntry->tLogged = GETTIMESTAMP();
  288. cbCopy = dwLength + sizeof(TCHAR); // Add the terminating NULL
  289. if(cbCopy > g_dwMemLogLineSize)
  290. {
  291. cbCopy = g_dwMemLogLineSize;
  292. }
  293. memcpy(pEntry->str, str, cbCopy);
  294. pEntry->str[(cbCopy / sizeof(TCHAR)) - 2] = _T('\n'); // Ensure we always end with a return
  295. pEntry->str[(cbCopy / sizeof(TCHAR)) - 1] = _T('\0'); // Ensure we always NULL terminate
  296. }
  297. // DebugPrintfInit() - initialize DPF support.
  298. void DebugPrintfInit()
  299. {
  300. BOOL fUsingMemLog = FALSE;
  301. TCHAR szLevel[32] = {0};
  302. _tcscpy(szLevel, _T("debug"));
  303. TCHAR szDest[32] = {0};
  304. _tcscpy(szDest, _T("log"));
  305. TCHAR szBreak[32] = {0};
  306. _tcscpy(szBreak, _T("breakonassert"));
  307. // Loop through all the subcomps, and get the level and destination for each
  308. for (int iSubComp = 0; iSubComp < MAX_SUBCOMPS; iSubComp++)
  309. {
  310. // NOTE: The setting under "debug" sets the default and will be used if you
  311. // don't specify settings for each subcomp
  312. #if ((defined(_XBOX)) && (! defined(XBOX_ON_DESKTOP)))
  313. #pragma BUGBUG(vanceo, "Make DNGetProfileInt work")
  314. g_rgLevel[iSubComp] = DNGetProfileInt(PROF_SECT, szLevel, g_rgLevel[iSubComp]);
  315. g_rgDestination[iSubComp] = DNGetProfileInt(PROF_SECT, szDest, g_rgDestination[iSubComp]);
  316. g_rgBreakOnAssert[iSubComp] = DNGetProfileInt( PROF_SECT, szBreak, g_rgBreakOnAssert[iSubComp]);
  317. #else // ! _XBOX or XBOX_ON_DESKTOP
  318. g_rgLevel[iSubComp] = DNGetProfileInt(PROF_SECT, szLevel, g_rgLevel[0]);
  319. g_rgDestination[iSubComp] = DNGetProfileInt(PROF_SECT, szDest, g_rgDestination[0]);
  320. g_rgBreakOnAssert[iSubComp] = DNGetProfileInt( PROF_SECT, szBreak, g_rgBreakOnAssert[0]);
  321. #endif // ! _XBOX or XBOX_ON_DESKTOP
  322. if (g_rgDestination[iSubComp] & LOG_TO_MEM)
  323. {
  324. fUsingMemLog = TRUE;
  325. }
  326. // Set up for the next subcomp
  327. _tcscpy(szLevel + 5, _T(".")); // 5 is strlen of "debug", we are building debug.addr, etc.
  328. _tcscpy(szLevel + 6, g_rgszSubCompName[iSubComp + 1]);
  329. _tcscpy(szDest + 3, _T(".")); // 3 is strlen of "log", we are building log.addr, etc.
  330. _tcscpy(szDest + 4, g_rgszSubCompName[iSubComp + 1]);
  331. _tcscpy(szBreak + 13, _T(".")); // 13 is strlen of "breakonassert", we are building breakonassert.addr, etc.
  332. _tcscpy(szBreak + 14, g_rgszSubCompName[iSubComp + 1]);
  333. }
  334. g_dwMemLogNumEntries = DNGetProfileInt( PROF_SECT, _T("MemLogEntries"), 40000);
  335. g_fLogFileAndLine = DNGetProfileInt( PROF_SECT, _T("Verbose"), 0);
  336. #ifndef DPNBUILD_SINGLEPROCESS
  337. g_fAssertGrabMutex = DNGetProfileInt( PROF_SECT, _T("AssertGrabMutex"), 0);
  338. #endif // ! DPNBUILD_SINGLEPROCESS
  339. if (fUsingMemLog)
  340. {
  341. // Open the shared log file
  342. InitMemLogString();
  343. }
  344. }
  345. // DebugPrintfFini() - release resources used by DPF support.
  346. void DebugPrintfFini()
  347. {
  348. if(g_pMemLog)
  349. {
  350. #ifdef DPNBUILD_SINGLEPROCESS
  351. HeapFree(GetProcessHeap(), 0, g_pMemLog);
  352. #else // ! DPNBUILD_SINGLEPROCESS
  353. UnmapViewOfFile(g_pMemLog);
  354. #endif // ! DPNBUILD_SINGLEPROCESS
  355. g_pMemLog = NULL;
  356. }
  357. #ifndef DPNBUILD_SINGLEPROCESS
  358. if(g_hMemLogMutex)
  359. {
  360. CloseHandle(g_hMemLogMutex);
  361. g_hMemLogMutex = 0;
  362. }
  363. if(g_hMemLogFile)
  364. {
  365. CloseHandle(g_hMemLogFile);
  366. g_hMemLogFile = 0;
  367. }
  368. #endif // ! DPNBUILD_SINGLEPROCESS
  369. g_fMemLogInited = FALSE;
  370. }
  371. void DebugPrintfX(LPCTSTR szFile, DWORD dwLine, LPCTSTR szModName, DWORD dwSubComp, DWORD dwDetail, ...)
  372. {
  373. DNASSERT(dwSubComp < MAX_SUBCOMPS);
  374. if(g_rgLevel[dwSubComp] < dwDetail)
  375. {
  376. return;
  377. }
  378. TCHAR cMsg[ ASSERT_BUFFER_SIZE ];
  379. va_list argptr;
  380. LPTSTR pszCursor = cMsg;
  381. va_start(argptr, dwDetail);
  382. #ifdef UNICODE
  383. WCHAR szFormat[ASSERT_BUFFER_SIZE];
  384. LPSTR szaFormat;
  385. szaFormat = (LPSTR) va_arg(argptr, DWORD_PTR);
  386. STR_jkAnsiToWide(szFormat, szaFormat, ASSERT_BUFFER_SIZE);
  387. #else
  388. LPSTR szFormat;
  389. szFormat = (LPSTR) va_arg(argptr, DWORD_PTR);
  390. #endif // UNICODE
  391. cMsg[0] = 0;
  392. #ifdef WIN95
  393. TCHAR *psz = NULL;
  394. CHAR cTemp[ ASSERT_BUFFER_SIZE ];
  395. strcpy(cTemp, szFormat); // Copy to a local string that we can modify.
  396. szFormat = cTemp; // Point szFormat at the local string
  397. while (psz = strstr(szFormat, "%p")) // Look for each "%p".
  398. *(psz+1) = 'x'; // Substitute 'x' for 'p'. Don't try to expand
  399. #endif // WIN95
  400. // Prints out / logs as:
  401. // 1. Verbose
  402. // subcomp:dwDetail:ProcessId:ThreadId:File:Function:Line:DebugString
  403. // e.g.
  404. // ADDR:2:0450:0378:(c:\somefile.cpp)BuildURLA(L25)Can you believe it?
  405. //
  406. // 2. Regular
  407. // subcomp:dwDetail:ProcessId:ThreadId:Function:DebugString
  408. #ifndef DPNBUILD_SINGLEPROCESS
  409. pszCursor += wsprintf(pszCursor,_T("%s:%1d:%04x:%04x:"),g_rgszSubCompName[dwSubComp],dwDetail,GetCurrentProcessId(),GetCurrentThreadId());
  410. #else
  411. pszCursor += wsprintf(pszCursor,_T("%s:%1d:%04x:"),g_rgszSubCompName[dwSubComp],dwDetail,GetCurrentThreadId());
  412. #endif // ! DPNBUILD_SINGLEPROCESS
  413. if (g_fLogFileAndLine)
  414. {
  415. LPCTSTR c;
  416. int i = _tcslen(szFile);
  417. if (i < 25)
  418. {
  419. c = szFile;
  420. }
  421. else
  422. {
  423. c = szFile + i - 25;
  424. }
  425. pszCursor += wsprintf(pszCursor, _T("(%s)(L%d)"), c, dwLine);
  426. }
  427. pszCursor += wsprintf(pszCursor, _T("%s: "), szModName);
  428. pszCursor += wvsprintf(pszCursor, szFormat, argptr);
  429. _tcscpy(pszCursor, _T("\n"));
  430. pszCursor += _tcslen(pszCursor);
  431. if(g_rgDestination[dwSubComp] & LOG_TO_DEBUG)
  432. {
  433. // log to debugger output
  434. OutputDebugString(cMsg);
  435. }
  436. if(g_rgDestination[dwSubComp] & LOG_TO_MEM)
  437. {
  438. // log to shared file, pass length not including '\0'
  439. MemLogString(cMsg, ((PBYTE)pszCursor - (PBYTE)cMsg));
  440. }
  441. #ifndef _XBOX
  442. if(g_rgDestination[dwSubComp] & LOG_TO_MSGBOX)
  443. {
  444. // log to Message Box
  445. MessageBox(NULL, cMsg, _T("DirectPlay Log"), MB_OK);
  446. }
  447. #endif // ! _XBOX
  448. va_end(argptr);
  449. return;
  450. }
  451. //
  452. // NOTE: I don't want to get into error checking for buffer overflows when
  453. // trying to issue an assertion failure message. So instead I just allocate
  454. // a buffer that is "bug enough" (I know, I know...)
  455. //
  456. void _DNAssert( LPCTSTR szFile, DWORD dwLine, LPCTSTR szFnName, DWORD dwSubComp, LPCTSTR szCondition, DWORD dwLevel )
  457. {
  458. TCHAR buffer[ASSERT_BUFFER_SIZE];
  459. // For level 1 we always print the message to the log, but we may not actually break. For other levels
  460. // we either print and break or do neither.
  461. if (dwLevel <= g_rgBreakOnAssert[dwSubComp] || dwLevel == 1)
  462. {
  463. // Build the debug stream message
  464. wsprintf( buffer, _T("ASSERTION FAILED! File: %s Line: %d: %s"), szFile, dwLine, szCondition);
  465. // Actually issue the message. These messages are considered error level
  466. // so they all go out at error level priority.
  467. DebugPrintfX(szFile, dwLine, szFnName, dwSubComp, ASSERT_MESSAGE_LEVEL, ASSERT_BANNER_STRING );
  468. DebugPrintfX(szFile, dwLine, szFnName, dwSubComp, ASSERT_MESSAGE_LEVEL, "%s", buffer );
  469. DebugPrintfX(szFile, dwLine, szFnName, dwSubComp, ASSERT_MESSAGE_LEVEL, ASSERT_BANNER_STRING );
  470. // Should we drop into the debugger?
  471. if(g_rgBreakOnAssert[dwSubComp])
  472. {
  473. #ifndef DPNBUILD_SINGLEPROCESS
  474. // Don't let dpnsvr keep writing to the log
  475. if (g_hMemLogMutex && g_fAssertGrabMutex)
  476. {
  477. WaitForSingleObject(g_hMemLogMutex, INFINITE);
  478. }
  479. #endif // ! DPNBUILD_SINGLEPROCESS
  480. // Into the debugger we go...
  481. DEBUG_BREAK();
  482. #ifndef DPNBUILD_SINGLEPROCESS
  483. if (g_hMemLogMutex && g_fAssertGrabMutex)
  484. {
  485. ReleaseMutex(g_hMemLogMutex);
  486. }
  487. #endif // ! DPNBUILD_SINGLEPROCESS
  488. }
  489. }
  490. }
  491. #endif // DBG