main
cpp 501 lines 14.2 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5
6 // Exit macros
7 #define DExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
8 #define DExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
9 #define DExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
10 #define DExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
11 #define DExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
12 #define DExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_DUTIL, x, s, __VA_ARGS__)
13 #define DExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__)
14 #define DExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__)
15 #define DExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_DUTIL, p, x, e, s, __VA_ARGS__)
16 #define DExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_DUTIL, p, x, s, __VA_ARGS__)
17 #define DExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_DUTIL, e, x, s, __VA_ARGS__)
18 #define DExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_DUTIL, g, x, s, __VA_ARGS__)
19
20 // No need for OACR to warn us about using non-unicode APIs in this file.
21 #pragma prefast(disable:25068)
22
23 // Asserts & Tracing
24
25 const int DUTIL_STRING_BUFFER = 1024;
26 static HMODULE Dutil_hAssertModule = NULL;
27 static DUTIL_ASSERTDISPLAYFUNCTION Dutil_pfnDisplayAssert = NULL;
28 static BOOL Dutil_fNoAsserts = FALSE;
29 static REPORT_LEVEL Dutil_rlCurrentTrace = REPORT_STANDARD;
30 static BOOL Dutil_fTraceFilenames = FALSE;
31 static DUTIL_CALLBACK_TRACEERROR vpfnTraceErrorCallback = NULL;
32
33 thread_local static DWORD vtdwSuppressTraceErrorSource = 0;
34
35
36 DAPI_(HRESULT) DutilInitialize(
37 __in_opt DUTIL_CALLBACK_TRACEERROR pfnTraceErrorCallback
38 )
39 {
40 HRESULT hr = S_OK;
41
42 vpfnTraceErrorCallback = pfnTraceErrorCallback;
43
44 return hr;
45 }
46
47
48 DAPI_(void) DutilUninitialize()
49 {
50 vpfnTraceErrorCallback = NULL;
51 }
52
53
54 DAPI_(HRESULT) DutilSizetToDword(SIZE_T sizet, DWORD* pdw)
55 {
56 if (DWORD_MAX < sizet)
57 {
58 return E_INVALIDARG;
59 }
60
61 *pdw = static_cast<DWORD>(sizet);
62 return S_OK;
63 }
64
65
66 DAPI_(BOOL) DutilSuppressTraceErrorSource()
67 {
68 if (DWORD_MAX == vtdwSuppressTraceErrorSource)
69 {
70 return FALSE;
71 }
72
73 ++vtdwSuppressTraceErrorSource;
74 return TRUE;
75 }
76
77 DAPI_(BOOL) DutilUnsuppressTraceErrorSource()
78 {
79 if (0 == vtdwSuppressTraceErrorSource)
80 {
81 return FALSE;
82 }
83
84 --vtdwSuppressTraceErrorSource;
85 return TRUE;
86 }
87
88 /*******************************************************************
89 Dutil_SetAssertModule
90
91 *******************************************************************/
92 extern "C" void DAPI Dutil_SetAssertModule(
93 __in HMODULE hAssertModule
94 )
95 {
96 Dutil_hAssertModule = hAssertModule;
97 }
98
99
100 /*******************************************************************
101 Dutil_SetAssertDisplayFunction
102
103 *******************************************************************/
104 extern "C" void DAPI Dutil_SetAssertDisplayFunction(
105 __in DUTIL_ASSERTDISPLAYFUNCTION pfn
106 )
107 {
108 Dutil_pfnDisplayAssert = pfn;
109 }
110
111
112 /*******************************************************************
113 Dutil_AssertMsg
114
115 *******************************************************************/
116 extern "C" void DAPI Dutil_AssertMsg(
117 __in_z LPCSTR szMessage
118 )
119 {
120 static BOOL fInAssert = FALSE; // TODO: make this thread safe (this is a cheap hack to prevent re-entrant Asserts)
121
122 HRESULT hr = S_OK;
123 DWORD er = ERROR_SUCCESS;
124
125 int id = IDRETRY;
126 HKEY hkDebug = NULL;
127 HANDLE hAssertFile = INVALID_HANDLE_VALUE;
128 char szPath[MAX_PATH] = { };
129 DWORD cch = 0;
130
131 if (fInAssert)
132 {
133 return;
134 }
135 fInAssert = TRUE;
136
137 char szMsg[DUTIL_STRING_BUFFER];
138 hr = ::StringCchCopyA(szMsg, countof(szMsg), szMessage);
139 DExitOnFailure(hr, "failed to copy message while building assert message");
140
141 if (Dutil_pfnDisplayAssert)
142 {
143 // call custom function to display the assert string
144 if (!Dutil_pfnDisplayAssert(szMsg))
145 {
146 ExitFunction();
147 }
148 }
149 else
150 {
151 OutputDebugStringA(szMsg);
152 }
153
154 if (!Dutil_fNoAsserts)
155 {
156 er = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Delivery\\Debug", 0, KEY_QUERY_VALUE, &hkDebug);
157 if (ERROR_SUCCESS == er)
158 {
159 cch = countof(szPath);
160 er = ::RegQueryValueExA(hkDebug, "DeliveryAssertsLog", NULL, NULL, reinterpret_cast<BYTE*>(szPath), &cch);
161 szPath[countof(szPath) - 1] = '\0'; // ensure string is null terminated since registry won't guarantee that.
162 if (ERROR_SUCCESS == er)
163 {
164 hAssertFile = ::CreateFileA(szPath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
165 if (INVALID_HANDLE_VALUE != hAssertFile)
166 {
167 if (INVALID_SET_FILE_POINTER != ::SetFilePointer(hAssertFile, 0, 0, FILE_END))
168 {
169 if (SUCCEEDED(::StringCchCatA(szMsg, countof(szMsg), "\r\n")))
170 {
171 ::WriteFile(hAssertFile, szMsg, lstrlenA(szMsg), &cch, NULL);
172 }
173 }
174 }
175 }
176 }
177
178 // if anything went wrong while fooling around with the registry, just show the usual assert dialog box
179 if (ERROR_SUCCESS != er)
180 {
181 hr = ::StringCchCatA(szMsg, countof(szMsg), "\nAbort=Debug, Retry=Skip, Ignore=Skip all");
182 DExitOnFailure(hr, "failed to concat string while building assert message");
183
184 id = ::MessageBoxA(0, szMsg, "Debug Assert Message",
185 MB_SERVICE_NOTIFICATION | MB_TOPMOST |
186 MB_DEFBUTTON2 | MB_ABORTRETRYIGNORE);
187 }
188 }
189
190 if (id == IDABORT)
191 {
192 if (Dutil_hAssertModule)
193 {
194 ::GetModuleFileNameA(Dutil_hAssertModule, szPath, countof(szPath));
195
196 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "Module is running from: %s\nIf you are not using pdb-stamping, place your PDB near the module and attach to process id: %d (0x%x)", szPath, ::GetCurrentProcessId(), ::GetCurrentProcessId());
197 if (SUCCEEDED(hr))
198 {
199 ::MessageBoxA(0, szMsg, "Debug Assert Message", MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK);
200 }
201 }
202
203 ::DebugBreak();
204 }
205 else if (id == IDIGNORE)
206 {
207 Dutil_fNoAsserts = TRUE;
208 }
209
210 LExit:
211 ReleaseFileHandle(hAssertFile);
212 ReleaseRegKey(hkDebug);
213 fInAssert = FALSE;
214 }
215
216
217 /*******************************************************************
218 Dutil_Assert
219
220 *******************************************************************/
221 extern "C" void DAPI Dutil_Assert(
222 __in_z LPCSTR szFile,
223 __in int iLine
224 )
225 {
226 HRESULT hr = S_OK;
227 char szMessage[DUTIL_STRING_BUFFER] = { };
228 hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i", szFile, iLine);
229 if (SUCCEEDED(hr))
230 {
231 Dutil_AssertMsg(szMessage);
232 }
233 else
234 {
235 Dutil_AssertMsg("Assert failed to build string");
236 }
237 }
238
239
240 /*******************************************************************
241 Dutil_AssertSz
242
243 *******************************************************************/
244 extern "C" void DAPI Dutil_AssertSz(
245 __in_z LPCSTR szFile,
246 __in int iLine,
247 __in_z __format_string LPCSTR szMsg
248 )
249 {
250 HRESULT hr = S_OK;
251 char szMessage[DUTIL_STRING_BUFFER] = { };
252
253 hr = ::StringCchPrintfA(szMessage, countof(szMessage), "Assertion failed in %s, %i\n%s", szFile, iLine, szMsg);
254 if (SUCCEEDED(hr))
255 {
256 Dutil_AssertMsg(szMessage);
257 }
258 else
259 {
260 Dutil_AssertMsg("Assert failed to build string");
261 }
262 }
263
264
265 /*******************************************************************
266 Dutil_TraceSetLevel
267
268 *******************************************************************/
269 extern "C" void DAPI Dutil_TraceSetLevel(
270 __in REPORT_LEVEL rl,
271 __in BOOL fTraceFilenames
272 )
273 {
274 Dutil_rlCurrentTrace = rl;
275 Dutil_fTraceFilenames = fTraceFilenames;
276 }
277
278
279 /*******************************************************************
280 Dutil_TraceGetLevel
281
282 *******************************************************************/
283 extern "C" REPORT_LEVEL DAPI Dutil_TraceGetLevel()
284 {
285 return Dutil_rlCurrentTrace;
286 }
287
288
289 /*******************************************************************
290 Dutil_Trace
291
292 *******************************************************************/
293 extern "C" void DAPIV Dutil_Trace(
294 __in_z LPCSTR szFile,
295 __in int iLine,
296 __in REPORT_LEVEL rl,
297 __in_z __format_string LPCSTR szFormat,
298 ...
299 )
300 {
301 AssertSz(REPORT_NONE != rl, "REPORT_NONE is not a valid tracing level");
302
303 HRESULT hr = S_OK;
304 char szOutput[DUTIL_STRING_BUFFER] = { };
305 char szMsg[DUTIL_STRING_BUFFER] = { };
306
307 if (Dutil_rlCurrentTrace < rl)
308 {
309 return;
310 }
311
312 va_list args;
313 va_start(args, szFormat);
314 hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
315 va_end(args);
316
317 if (SUCCEEDED(hr))
318 {
319 LPCSTR szPrefix = "Trace/u";
320 switch (rl)
321 {
322 case REPORT_STANDARD:
323 szPrefix = "Trace/s";
324 break;
325 case REPORT_VERBOSE:
326 szPrefix = "Trace/v";
327 break;
328 case REPORT_DEBUG:
329 szPrefix = "Trace/d";
330 break;
331 }
332
333 if (Dutil_fTraceFilenames)
334 {
335 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s [%s,%d]: %s\r\n", szPrefix, szFile, iLine, szOutput);
336 }
337 else
338 {
339 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "%s: %s\r\n", szPrefix, szOutput);
340 }
341
342 if (SUCCEEDED(hr))
343 {
344 OutputDebugStringA(szMsg);
345 }
346 // else fall through to the case below
347 }
348
349 if (FAILED(hr))
350 {
351 if (Dutil_fTraceFilenames)
352 {
353 ::StringCchPrintfA(szMsg, countof(szMsg), "Trace [%s,%d]: message too long, skipping\r\n", szFile, iLine);
354 }
355 else
356 {
357 ::StringCchPrintfA(szMsg, countof(szMsg), "Trace: message too long, skipping\r\n");
358 }
359
360 szMsg[countof(szMsg)-1] = '\0';
361 OutputDebugStringA(szMsg);
362 }
363 }
364
365
366 /*******************************************************************
367 Dutil_TraceError
368
369 *******************************************************************/
370 extern "C" void DAPIV Dutil_TraceError(
371 __in_z LPCSTR szFile,
372 __in int iLine,
373 __in REPORT_LEVEL rl,
374 __in HRESULT hrError,
375 __in_z __format_string LPCSTR szFormat,
376 ...
377 )
378 {
379 HRESULT hr = S_OK;
380 char szOutput[DUTIL_STRING_BUFFER] = { };
381 char szMsg[DUTIL_STRING_BUFFER] = { };
382
383 // if this is NOT an error report and we're not logging at this level, bail
384 if (REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl)
385 {
386 return;
387 }
388
389 va_list args;
390 va_start(args, szFormat);
391 hr = ::StringCchVPrintfA(szOutput, countof(szOutput), szFormat, args);
392 va_end(args);
393
394 if (SUCCEEDED(hr))
395 {
396 if (Dutil_fTraceFilenames)
397 {
398 if (FAILED(hrError))
399 {
400 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: %s\r\n", hrError, szFile, iLine, szOutput);
401 }
402 else
403 {
404 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: %s\r\n", szFile, iLine, szOutput);
405 }
406 }
407 else
408 {
409 if (FAILED(hrError))
410 {
411 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: %s\r\n", hrError, szOutput);
412 }
413 else
414 {
415 hr = ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: %s\r\n", szOutput);
416 }
417 }
418
419 if (SUCCEEDED(hr))
420 {
421 OutputDebugStringA(szMsg);
422 }
423 // else fall through to the failure case below
424 }
425
426 if (FAILED(hr))
427 {
428 if (Dutil_fTraceFilenames)
429 {
430 if (FAILED(hrError))
431 {
432 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x [%s,%d]: message too long, skipping\r\n", hrError, szFile, iLine);
433 }
434 else
435 {
436 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError [%s,%d]: message too long, skipping\r\n", szFile, iLine);
437 }
438 }
439 else
440 {
441 if (FAILED(hrError))
442 {
443 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError 0x%x: message too long, skipping\r\n", hrError);
444 }
445 else
446 {
447 ::StringCchPrintfA(szMsg, countof(szMsg), "TraceError: message too long, skipping\r\n");
448 }
449 }
450
451 szMsg[countof(szMsg)-1] = '\0';
452 OutputDebugStringA(szMsg);
453 }
454 }
455
456
457 DAPIV_(void) Dutil_TraceErrorSource(
458 __in_z LPCSTR szFile,
459 __in int iLine,
460 __in REPORT_LEVEL rl,
461 __in UINT source,
462 __in HRESULT hr,
463 __in_z __format_string LPCSTR szFormat,
464 ...
465 )
466 {
467 // if this callback is currently suppressed, or
468 // if this is NOT an error report and we're not logging at this level, bail
469 if (vtdwSuppressTraceErrorSource || REPORT_ERROR != rl && Dutil_rlCurrentTrace < rl)
470 {
471 return;
472 }
473
474 if (DUTIL_SOURCE_UNKNOWN != source && vpfnTraceErrorCallback)
475 {
476 va_list args;
477 va_start(args, szFormat);
478 vpfnTraceErrorCallback(szFile, iLine, rl, source, hr, szFormat, args);
479 va_end(args);
480 }
481 }
482
483
484 /*******************************************************************
485 Dutil_RootFailure
486
487 *******************************************************************/
488 extern "C" void DAPI Dutil_RootFailure(
489 __in_z LPCSTR szFile,
490 __in int iLine,
491 __in HRESULT hrError
492 )
493 {
494 #ifndef DEBUG
495 UNREFERENCED_PARAMETER(szFile);
496 UNREFERENCED_PARAMETER(iLine);
497 UNREFERENCED_PARAMETER(hrError);
498 #endif // DEBUG
499
500 TraceError(hrError, "Root failure at %s:%d", szFile, iLine);
501 }