1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | // |
5 | // FILE: dwbucketmanager.hpp |
6 | // |
7 | // This file contains the manager types for differents types of Watson buckets |
8 | // and various helper types. |
9 | // |
10 | |
11 | // |
12 | |
13 | // |
14 | // ============================================================================ |
15 | |
16 | #ifndef DWBUCKETMANAGER_HPP |
17 | #define DWBUCKETMANAGER_HPP |
18 | |
19 | #ifdef FEATURE_WINDOWSPHONE |
20 | #include "corhost.h" |
21 | #endif |
22 | |
23 | // this will be used as an index into g_WerEventTraits |
24 | enum WatsonBucketType |
25 | { |
26 | CLR20r3 = 0, |
27 | MoCrash, |
28 | #ifdef FEATURE_WINDOWSPHONE |
29 | WinPhoneCrash, |
30 | #endif |
31 | // insert new types above this line |
32 | EndOfWerBucketTypes |
33 | }; |
34 | |
35 | const DWORD kInvalidParamsCount = 0xffffffff; |
36 | |
37 | struct WerEventTypeTraits |
38 | { |
39 | const LPCWSTR EventName; |
40 | const DWORD CountParams; |
41 | INDEBUG(const WatsonBucketType BucketType); |
42 | |
43 | WerEventTypeTraits(LPCWSTR name, DWORD params DEBUG_ARG(WatsonBucketType type)) |
44 | : EventName(name), CountParams(params) DEBUG_ARG(BucketType(type)) |
45 | { |
46 | _ASSERTE(params < kInvalidParamsCount); |
47 | } |
48 | }; |
49 | |
50 | const WerEventTypeTraits g_WerEventTraits[] = |
51 | { |
52 | WerEventTypeTraits(W("CLR20r3" ), 9 DEBUG_ARG(CLR20r3)), |
53 | WerEventTypeTraits(W("MoAppCrash" ), 9 DEBUG_ARG(MoCrash)) |
54 | #ifdef FEATURE_WINDOWSPHONE |
55 | // unfortunately Apollo uses the same event name |
56 | ,WerEventTypeTraits(W("CLR20r3" ), 9 DEBUG_ARG(WinPhoneCrash)) |
57 | #endif |
58 | }; |
59 | |
60 | DWORD GetCountBucketParamsForEvent(LPCWSTR wzEventName) |
61 | { |
62 | CONTRACTL |
63 | { |
64 | NOTHROW; |
65 | GC_NOTRIGGER; |
66 | MODE_ANY; |
67 | SUPPORTS_DAC; |
68 | } |
69 | CONTRACTL_END; |
70 | |
71 | if (wzEventName == NULL) |
72 | { |
73 | _ASSERTE(!"missing event name when retrieving bucket params count" ); |
74 | return 10; |
75 | } |
76 | |
77 | DWORD countParams = kInvalidParamsCount; |
78 | for (int index = 0; index < EndOfWerBucketTypes; ++index) |
79 | { |
80 | if (wcscmp(wzEventName, g_WerEventTraits[index].EventName) == 0) |
81 | { |
82 | _ASSERTE(index == g_WerEventTraits[index].BucketType); |
83 | countParams = g_WerEventTraits[index].CountParams; |
84 | break; |
85 | } |
86 | } |
87 | |
88 | if (countParams == kInvalidParamsCount) |
89 | { |
90 | _ASSERTE(!"unknown event name when retrieving bucket params count" ); |
91 | countParams = 10; |
92 | } |
93 | |
94 | return countParams; |
95 | } |
96 | |
97 | #ifndef DACCESS_COMPILE |
98 | |
99 | #include "dwreport.h" |
100 | #include <msodwwrap.h> |
101 | #include "dbginterface.h" |
102 | #include <sha1.h> |
103 | |
104 | #ifdef FEATURE_APPX |
105 | #include "appxutil.h" |
106 | #endif |
107 | |
108 | //------------------------------------------------------------------------------ |
109 | // Description |
110 | // Converts an array of bytes to a string of base32 encoded characters. |
111 | // |
112 | // Constructor |
113 | // pData -- The bytes to be converted. |
114 | // nData -- Count of bytes to be converted. |
115 | // |
116 | // Convert |
117 | // pOut -- Put converted bytes here. |
118 | // nOut -- Max number of characters to put |
119 | // |
120 | // returns -- Number of characters put. |
121 | // |
122 | // Notes |
123 | // Five bytes of input produces 8 characters of output. |
124 | //------------------------------------------------------------------------------ |
125 | class BytesToBase32 |
126 | { |
127 | private: |
128 | // Five doesn't go into 8 very well, so we will wind up with 8 characters per |
129 | // five bytes of input. Specifically, a block of 5 bytes will be formatted |
130 | // like this: |
131 | // 7 6 5 4 3 2 1 0 <-- bit # |
132 | // 0 1 1 1 1 1 2 2 2 |
133 | // 1 2 2 3 3 3 3 3 4 <-- which character does the bit go to? |
134 | // 2 4 4 4 4 5 5 5 5 |
135 | // 3 5 6 6 6 6 6 7 7 |
136 | // 4 7 7 7 8 8 8 8 8 |
137 | // This structure defines 2 masks and 3 shift values per 5-bit value. |
138 | // The first mask is the mask from the first byte. The first two |
139 | // shifts are a left- OR a right- shift for the bits obtained via that mask. |
140 | // If there is a second mask, that is to get bits from the next byte, |
141 | // shifted right by the second shift value. Finally, there is a bit to |
142 | // indicate that the scanner should advance to the next byte. |
143 | // Referring to the table above, the decoder values for the first 5-bit |
144 | // value will be: |
145 | // m1 : 0xf8 - mask |
146 | // l1 : 0 - no left shift |
147 | // r1 : 3 - right shift 3 bits |
148 | // m2 : 0 - no second mask |
149 | // r2 : 0 - no second right shift |
150 | // skip : 0 - don't skip to next byte (still 3 more bits, for the second 5-bits. |
151 | struct decoder_ |
152 | { |
153 | unsigned int m1 : 8; // Mask 1 |
154 | unsigned int l1 : 4; // Left shift 1 |
155 | unsigned int r1 : 4; // Right shift 2 |
156 | unsigned int m2 : 8; // Mask 2 |
157 | unsigned int r2 : 4; // Right shift 2 |
158 | unsigned int skip:4; // Skip to next input byte |
159 | }; |
160 | |
161 | static const decoder_ decoder[8]; // Array of decoder specs. |
162 | static const WCHAR base32[33]; // Array of 33 characters: A-Z, 0-5, = |
163 | |
164 | BYTE *pData; // Pointer to data. |
165 | int nData; // Total bytes of data. |
166 | |
167 | BYTE *pEnd; |
168 | |
169 | int nWhich; // Where in the sequence of 8 5-bit datums? |
170 | |
171 | public: |
172 | BytesToBase32(BYTE *p, int n) : pData(p), nData(n), nWhich(0) { LIMITED_METHOD_CONTRACT; pEnd = pData + nData; } |
173 | |
174 | WCHAR GetNextChar(); |
175 | BOOL MoreChars() { LIMITED_METHOD_CONTRACT; return pData < pEnd; } |
176 | |
177 | int Convert(__inout_ecount(nOut) LPWSTR pOut, int nOut); |
178 | }; |
179 | |
180 | // This table tells how to pick out 5-bits at a time (8 times) from 5-bytes of data. |
181 | const BytesToBase32::decoder_ BytesToBase32::decoder[8] = |
182 | { // m1 l1 r1 m2 r2 skip |
183 | {0xf8, 0, 3, 0x00, 0, 0}, |
184 | {0x07, 2, 0, 0xc0, 6, 1}, |
185 | {0x3e, 0, 1, 0x00, 0, 0}, |
186 | {0x01, 4, 0, 0xf0, 4, 1}, |
187 | {0x0f, 1, 0, 0x80, 7, 1}, |
188 | {0x7c, 0, 2, 0x00, 0, 0}, |
189 | {0x03, 3, 0, 0xe0, 5, 1}, |
190 | {0x1f, 0, 0, 0x00, 0, 1}, |
191 | }; |
192 | |
193 | // Array of characters with which to encode. |
194 | const WCHAR BytesToBase32::base32[33] = {'A','B','C','D','E','F','G','H','I','J','K','L', 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6'}; |
195 | |
196 | //------------------------------------------------------------------------------ |
197 | // Description |
198 | // Converts 5-bits to a character; fundamental base32 encoding. |
199 | // |
200 | // Parameters |
201 | // none |
202 | // |
203 | // Returns |
204 | // The next 5-bits, converted to a character. Also advances the |
205 | // character pointer. When no characters remain to be converted, |
206 | // returns W('6') |
207 | // |
208 | //------------------------------------------------------------------------------ |
209 | WCHAR BytesToBase32::GetNextChar() |
210 | { |
211 | LIMITED_METHOD_CONTRACT; |
212 | |
213 | unsigned int result = 0; |
214 | |
215 | _ASSERTE(pData <= pEnd); |
216 | _ASSERTE(nWhich >= 0 && nWhich < lengthof(decoder)); |
217 | |
218 | // If out of data, return signal value, > any valid char. |
219 | if (pData == pEnd) |
220 | return base32[lengthof(base32)-1]; |
221 | |
222 | #if defined(_DEBUG) |
223 | if (decoder[nWhich].l1) |
224 | { // There is a l1 shift. |
225 | _ASSERTE(decoder[nWhich].m1); // There should be a m1 mask |
226 | _ASSERTE(decoder[nWhich].r1 == 0); // There should not be a r1 shift |
227 | _ASSERTE(decoder[nWhich].m2); // There shoulbe a m2 mask to fill in the rest of the bits. |
228 | _ASSERTE(decoder[nWhich].r2); // m2 bits never start in the right place; there must be a shift |
229 | // The masks, shifted, and or'd together should equal 0x1f, 5-bits. |
230 | _ASSERTE( ( (decoder[nWhich].m1 << decoder[nWhich].l1) | (decoder[nWhich].m2 >> decoder[nWhich].r2)) == 0x1f); |
231 | } |
232 | else |
233 | { // There is no l1 shift. |
234 | _ASSERTE(decoder[nWhich].m2 == 0); // There should not be any m2 bits |
235 | _ASSERTE( (decoder[nWhich].m1 >> decoder[nWhich].r1) == 0x1f); // The m1 bits, shifted should be 0x1f, 5-bits. |
236 | } |
237 | #endif |
238 | |
239 | // Mask off the bits. |
240 | result = *pData & decoder[nWhich].m1; |
241 | |
242 | // Shift left or right as needed. |
243 | if (decoder[nWhich].l1) |
244 | { // Shift up to make space for low-order bits from next byte. |
245 | result = result << decoder[nWhich].l1; |
246 | } |
247 | else |
248 | if (decoder[nWhich].r1) |
249 | { // Shift down into position. There should be no more bits from next byte. |
250 | result = result >> decoder[nWhich].r1; |
251 | } |
252 | |
253 | // Skip to next byte if appropriate. |
254 | if (decoder[nWhich].skip) |
255 | ++pData; |
256 | |
257 | // Grab more bits if specified, and more are available. |
258 | if (pData < pEnd && decoder[nWhich].m2) |
259 | { // All second-byte data are shifted right, so just mask and shift. |
260 | result |= ( (*pData & decoder[nWhich].m2) >> decoder[nWhich].r2); |
261 | } |
262 | |
263 | // Advance the 'state machine' -- which 5-bits from an 8-byte block. |
264 | if (++nWhich == lengthof(decoder)) |
265 | nWhich = 0; |
266 | |
267 | // Sanity check on value. |
268 | _ASSERTE(result < lengthof(base32)); |
269 | |
270 | return base32[result]; |
271 | } // WCHAR BytesToBase32::GetNextChar() |
272 | |
273 | //------------------------------------------------------------------------------ |
274 | // Description |
275 | // Performs the conversion of a buffer to base32. |
276 | // |
277 | // Parameters |
278 | // pOut -- Buffer to receive the characters. |
279 | // nOut -- Maximum characters to write to the buffer. |
280 | // |
281 | // Returns |
282 | // the number of characters copied to the output buffer. |
283 | // |
284 | //------------------------------------------------------------------------------ |
285 | int BytesToBase32::Convert( |
286 | __inout_ecount(nOut) LPWSTR pOut, |
287 | int nOut) |
288 | { |
289 | WRAPPER_NO_CONTRACT; |
290 | |
291 | int nWritten = 0; // Count of bytes written to output. |
292 | |
293 | // Stop when the buffer is full, or the bytes are fully converted. |
294 | while (nOut > 0 && MoreChars()) |
295 | { |
296 | *pOut = GetNextChar(); |
297 | ++pOut; |
298 | --nOut; |
299 | ++nWritten; |
300 | } |
301 | |
302 | return nWritten; |
303 | } // int BytesToBase32::Convert() |
304 | |
305 | // this abstract class provides base functionality for populating a bucket parameter in the GMB with some data. |
306 | // the actual mapping of ordinal parameter to data type (eg parameter 1 is app name) is handled in subclasses |
307 | // of this type. see GetBucketParamsManager() for retrieving a bucket params manager. |
308 | class BaseBucketParamsManager |
309 | { |
310 | private: |
311 | GenericModeBlock* m_pGmb; |
312 | TypeOfReportedError m_tore; |
313 | Thread* m_pThread; |
314 | OBJECTREF* m_pException; |
315 | INDEBUG(size_t m_countParamsLogged); |
316 | MethodDesc* m_pFaultingMD; |
317 | PCODE m_faultingPc; |
318 | |
319 | // misc helper functions |
320 | DWORD GetILOffset(); |
321 | bool GetFileVersionInfoForModule(Module* pModule, USHORT& major, USHORT& minor, USHORT& build, USHORT& revision); |
322 | bool IsCodeContractsFrame(MethodDesc* pMD); |
323 | void FindFaultingMethodInfo(); |
324 | OBJECTREF GetRealExceptionObject(); |
325 | WCHAR* GetParamBufferForIndex(BucketParameterIndex paramIndex); |
326 | void LogParam(__in_z LPCWSTR paramValue, BucketParameterIndex paramIndex); |
327 | |
328 | protected: |
329 | ~BaseBucketParamsManager(); |
330 | |
331 | typedef void (BaseBucketParamsManager::*DataPopulatorFunction)(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
332 | void PopulateBucketParameter(BucketParameterIndex paramIndex, DataPopulatorFunction pFnDataPopulator, int maxLength); |
333 | |
334 | void PopulateEventName(LPCWSTR eventTypeName); |
335 | // functions for retrieving data to go into various bucket parameters |
336 | void GetAppName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
337 | void GetAppVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
338 | void GetAppTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
339 | void GetModuleName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
340 | void GetModuleVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
341 | void GetModuleTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
342 | void GetMethodDef(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
343 | void GetIlOffset(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
344 | void GetExceptionName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
345 | void GetPackageMoniker(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
346 | void GetPRAID(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
347 | void GetIlRva(__out_ecount(maxLength) WCHAR* targetParam, int maxLength); |
348 | |
349 | public: |
350 | BaseBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE initialFaultingPc, Thread* pFaultingThread, OBJECTREF* pThrownException); |
351 | static int CopyStringToBucket(__out_ecount(targetMaxLength) LPWSTR pTargetParam, int targetMaxLength, __in_z LPCWSTR pSource, bool cannonicalize = false); |
352 | // function that consumers should call to populate the GMB |
353 | virtual void PopulateBucketParameters() = 0; |
354 | }; |
355 | |
356 | BaseBucketParamsManager::BaseBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE initialFaultingPc, Thread* pFaultingThread, OBJECTREF* pThrownException) |
357 | : m_pFaultingMD(NULL), m_faultingPc(initialFaultingPc), m_pGmb(pGenericModeBlock), m_tore(typeOfError), m_pThread(pFaultingThread), m_pException(pThrownException) |
358 | { |
359 | CONTRACTL |
360 | { |
361 | NOTHROW; |
362 | GC_NOTRIGGER; |
363 | MODE_ANY; |
364 | } |
365 | CONTRACTL_END; |
366 | |
367 | _ASSERTE(m_pGmb); |
368 | INDEBUG(m_countParamsLogged = 0); |
369 | |
370 | ZeroMemory(pGenericModeBlock, sizeof(GenericModeBlock)); |
371 | |
372 | EECodeInfo codeInfo(initialFaultingPc); |
373 | if (codeInfo.IsValid()) |
374 | { |
375 | m_pFaultingMD = codeInfo.GetMethodDesc(); |
376 | |
377 | if (m_pFaultingMD) |
378 | FindFaultingMethodInfo(); |
379 | } |
380 | } |
381 | |
382 | BaseBucketParamsManager::~BaseBucketParamsManager() |
383 | { |
384 | LIMITED_METHOD_CONTRACT; |
385 | |
386 | _ASSERTE(m_countParamsLogged == GetCountBucketParamsForEvent(m_pGmb->wzEventTypeName)); |
387 | } |
388 | |
389 | void BaseBucketParamsManager::PopulateEventName(LPCWSTR eventTypeName) |
390 | { |
391 | CONTRACTL |
392 | { |
393 | NOTHROW; |
394 | GC_NOTRIGGER; |
395 | MODE_ANY; |
396 | } |
397 | CONTRACTL_END; |
398 | |
399 | wcsncpy_s(m_pGmb->wzEventTypeName, DW_MAX_BUCKETPARAM_CWC, eventTypeName, _TRUNCATE); |
400 | |
401 | _ASSERTE(GetCountBucketParamsForEvent(eventTypeName)); |
402 | LOG((LF_EH, LL_INFO10, "Event : %S\n" , m_pGmb->wzEventTypeName)); |
403 | } |
404 | |
405 | WCHAR* BaseBucketParamsManager::GetParamBufferForIndex(BucketParameterIndex paramIndex) |
406 | { |
407 | CONTRACTL |
408 | { |
409 | NOTHROW; |
410 | GC_NOTRIGGER; |
411 | MODE_ANY; |
412 | } |
413 | CONTRACTL_END; |
414 | |
415 | _ASSERTE(paramIndex < InvalidBucketParamIndex); |
416 | switch (paramIndex) |
417 | { |
418 | case Parameter1: |
419 | return m_pGmb->wzP1; |
420 | case Parameter2: |
421 | return m_pGmb->wzP2; |
422 | case Parameter3: |
423 | return m_pGmb->wzP3; |
424 | case Parameter4: |
425 | return m_pGmb->wzP4; |
426 | case Parameter5: |
427 | return m_pGmb->wzP5; |
428 | case Parameter6: |
429 | return m_pGmb->wzP6; |
430 | case Parameter7: |
431 | return m_pGmb->wzP7; |
432 | case Parameter8: |
433 | return m_pGmb->wzP8; |
434 | case Parameter9: |
435 | return m_pGmb->wzP9; |
436 | default: |
437 | { |
438 | _ASSERTE(!"bad paramIndex" ); |
439 | // this is a back-stop to prevent returning NULL and having to have |
440 | // callers check for it. we should never get here though anyways. |
441 | return m_pGmb->wzP10; |
442 | } |
443 | } |
444 | } |
445 | |
446 | void BaseBucketParamsManager::PopulateBucketParameter(BucketParameterIndex paramIndex, DataPopulatorFunction pFnDataPopulator, int maxLength) |
447 | { |
448 | CONTRACTL |
449 | { |
450 | NOTHROW; |
451 | GC_NOTRIGGER; |
452 | MODE_ANY; |
453 | } |
454 | CONTRACTL_END; |
455 | |
456 | _ASSERTE(paramIndex < InvalidBucketParamIndex); |
457 | WCHAR* targetParam = GetParamBufferForIndex(paramIndex); |
458 | |
459 | // verify that we haven't already written data to this param |
460 | _ASSERTE(targetParam && targetParam[0] == W('\0')); |
461 | #ifdef FEATURE_WINDOWSPHONE |
462 | WCHAR const* overrideParam = g_CLRErrorReportingManager.GetBucketParamOverride(paramIndex); |
463 | if (overrideParam != NULL) |
464 | { |
465 | CopyStringToBucket(targetParam, maxLength, overrideParam, false); |
466 | } |
467 | else |
468 | #endif // FEATURE_WINDOWSPHONE |
469 | { |
470 | (this->*pFnDataPopulator)(targetParam, maxLength); |
471 | } |
472 | |
473 | LogParam(targetParam, paramIndex); |
474 | } |
475 | |
476 | void BaseBucketParamsManager::GetAppName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
477 | { |
478 | CONTRACTL |
479 | { |
480 | NOTHROW; |
481 | GC_NOTRIGGER; |
482 | MODE_ANY; |
483 | } |
484 | CONTRACTL_END; |
485 | |
486 | HMODULE hModule = WszGetModuleHandle(NULL); |
487 | PathString appPath; |
488 | |
489 | |
490 | if (GetCurrentModuleFileName(appPath) == S_OK) |
491 | { |
492 | CopyStringToBucket(targetParam, maxLength, appPath); |
493 | } |
494 | else |
495 | { |
496 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
497 | } |
498 | } |
499 | |
500 | void BaseBucketParamsManager::GetAppVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
501 | { |
502 | CONTRACTL |
503 | { |
504 | NOTHROW; |
505 | GC_NOTRIGGER; |
506 | MODE_ANY; |
507 | } |
508 | CONTRACTL_END; |
509 | |
510 | HMODULE hModule = WszGetModuleHandle(NULL); |
511 | PathString appPath; |
512 | |
513 | |
514 | WCHAR verBuf[23] = {0}; |
515 | USHORT major, minor, build, revision; |
516 | |
517 | if ((GetCurrentModuleFileName(appPath) == S_OK) && SUCCEEDED(DwGetFileVersionInfo(appPath, major, minor, build, revision))) |
518 | { |
519 | _snwprintf_s(targetParam, |
520 | maxLength, |
521 | _TRUNCATE, |
522 | W("%d.%d.%d.%d" ), |
523 | major, minor, build, revision); |
524 | } |
525 | else if (DwGetAssemblyVersion(appPath, verBuf, NumItems(verBuf)) != 0) |
526 | { |
527 | wcscpy_s(targetParam, maxLength, verBuf); |
528 | } |
529 | else |
530 | { |
531 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
532 | } |
533 | } |
534 | |
535 | void BaseBucketParamsManager::GetAppTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
536 | { |
537 | CONTRACTL |
538 | { |
539 | NOTHROW; |
540 | GC_NOTRIGGER; |
541 | MODE_ANY; |
542 | } |
543 | CONTRACTL_END; |
544 | |
545 | EX_TRY |
546 | { |
547 | CONTRACT_VIOLATION(GCViolation); |
548 | |
549 | HMODULE hModule = WszGetModuleHandle(NULL); |
550 | PEDecoder pe(hModule); |
551 | |
552 | ULONG ulTimeStamp = pe.GetTimeDateStamp(); |
553 | |
554 | _snwprintf_s(targetParam, |
555 | maxLength, |
556 | _TRUNCATE, |
557 | W("%x" ), |
558 | ulTimeStamp); |
559 | } |
560 | EX_CATCH |
561 | { |
562 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
563 | } |
564 | EX_END_CATCH(SwallowAllExceptions) |
565 | } |
566 | |
567 | void BaseBucketParamsManager::GetModuleName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
568 | { |
569 | CONTRACTL |
570 | { |
571 | NOTHROW; |
572 | GC_NOTRIGGER; |
573 | MODE_ANY; |
574 | } |
575 | CONTRACTL_END; |
576 | |
577 | Module* pModule = NULL; |
578 | |
579 | if (m_pFaultingMD != NULL) |
580 | pModule = m_pFaultingMD->GetModule(); |
581 | |
582 | bool failed = false; |
583 | |
584 | if (pModule) |
585 | { |
586 | // Get the assembly name, and determine its length, including terminating NULL. |
587 | Assembly* pAssembly = pModule->GetAssembly(); |
588 | LPCUTF8 utf8AssemblyName = pAssembly->GetSimpleName(); |
589 | const int assemblyNameLength = WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, NULL, 0); |
590 | |
591 | // full name and length. minor assumption that this is not multi-module. |
592 | WCHAR *fullName = NULL; |
593 | int fullNameLength = assemblyNameLength; |
594 | |
595 | if (pModule->IsManifest()) |
596 | { |
597 | // Single-module assembly; allocate a buffer and convert assembly name. |
598 | fullName = reinterpret_cast< WCHAR* >(_alloca(sizeof(WCHAR)*(fullNameLength))); |
599 | WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, fullName, fullNameLength); |
600 | } |
601 | else |
602 | { // This is a non-manifest module, which means it is a multi-module assembly. |
603 | // Construct a name like 'assembly+module'. |
604 | |
605 | // Get the module name, and determine its length, including terminating NULL. |
606 | LPCUTF8 utf8ModuleName = pModule->GetSimpleName(); |
607 | const int moduleNameLength = WszMultiByteToWideChar(CP_UTF8, 0, utf8ModuleName, -1, NULL, 0); |
608 | |
609 | // Full name length is assembly name length + module name length + 1 char for '+'. |
610 | // However, both assemblyNameLength and moduleNameLength include space for terminating NULL, |
611 | // but of course only one NULL is needed, so the final length is just the sum of the two lengths. |
612 | if (!ClrSafeInt<int>::addition(assemblyNameLength, moduleNameLength, fullNameLength)) |
613 | { |
614 | failed = true; |
615 | } |
616 | else |
617 | { |
618 | // Allocate a buffer with proper prefast checks. |
619 | int AllocLen; |
620 | if (!ClrSafeInt<int>::multiply(sizeof(WCHAR), fullNameLength, AllocLen)) |
621 | { |
622 | failed = true; |
623 | } |
624 | else |
625 | { |
626 | fullName = reinterpret_cast< WCHAR* >(_alloca(AllocLen)); |
627 | |
628 | // Convert the assembly name. |
629 | WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, fullName, assemblyNameLength); |
630 | |
631 | // replace NULL with '+' |
632 | _ASSERTE(fullName[assemblyNameLength-1] == 0); |
633 | fullName[assemblyNameLength-1] = W('+'); |
634 | |
635 | // Convert the module name after the '+' |
636 | WszMultiByteToWideChar(CP_UTF8, 0, utf8ModuleName,-1, &fullName[assemblyNameLength], moduleNameLength); |
637 | } |
638 | } |
639 | } |
640 | |
641 | if (!failed) |
642 | { |
643 | // Make sure NULL termination is right. |
644 | _ASSERTE(fullName[fullNameLength - 1] == 0); |
645 | |
646 | // Copy name in, with possible truncation or hashing. |
647 | CopyStringToBucket(targetParam, maxLength, fullName); |
648 | } |
649 | } |
650 | |
651 | if (!pModule || failed) |
652 | { |
653 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
654 | } |
655 | } |
656 | |
657 | void BaseBucketParamsManager::GetModuleVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
658 | { |
659 | CONTRACTL |
660 | { |
661 | NOTHROW; |
662 | GC_NOTRIGGER; |
663 | MODE_ANY; |
664 | } |
665 | CONTRACTL_END; |
666 | |
667 | Module* pModule = NULL; |
668 | |
669 | if (m_pFaultingMD != NULL) |
670 | pModule = m_pFaultingMD->GetModule(); |
671 | |
672 | bool failed = false; |
673 | |
674 | // @TODO: what if the it is in-memory module? It can have the version info. |
675 | // But we will not retrieve it right. |
676 | if (pModule) |
677 | { |
678 | USHORT major = 0, minor = 0, build = 0, revision = 0; |
679 | |
680 | bool gotFileVersion = GetFileVersionInfoForModule(pModule, major, minor, build, revision); |
681 | |
682 | // if we failed to get a version and this isn't the manifest module then try that |
683 | if (!gotFileVersion && !pModule->IsManifest()) |
684 | { |
685 | pModule = pModule->GetAssembly()->GetManifestModule(); |
686 | if (pModule) |
687 | gotFileVersion = GetFileVersionInfoForModule(pModule, major, minor, build, revision); |
688 | } |
689 | |
690 | if (!gotFileVersion) |
691 | { |
692 | // if we didn't get a file version then fall back to assembly version (typical for in-memory modules) |
693 | if (FAILED(pModule->GetAssembly()->GetVersion(&major, &minor, &build, &revision))) |
694 | failed = true; |
695 | } |
696 | |
697 | if (!failed) |
698 | { |
699 | _snwprintf_s(targetParam, |
700 | maxLength, |
701 | _TRUNCATE, |
702 | W("%d.%d.%d.%d" ), |
703 | major, minor, build, revision); |
704 | } |
705 | } |
706 | |
707 | if (!pModule || failed) |
708 | { |
709 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
710 | } |
711 | } |
712 | |
713 | void BaseBucketParamsManager::GetModuleTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
714 | { |
715 | CONTRACTL |
716 | { |
717 | NOTHROW; |
718 | GC_NOTRIGGER; |
719 | MODE_ANY; |
720 | } |
721 | CONTRACTL_END; |
722 | |
723 | Module* pModule = NULL; |
724 | |
725 | if (m_pFaultingMD != NULL) |
726 | pModule = m_pFaultingMD->GetModule(); |
727 | |
728 | bool failed = false; |
729 | |
730 | if (pModule) |
731 | { |
732 | EX_TRY |
733 | { |
734 | // We only store the IL timestamp in the native image for the |
735 | // manifest module. We should consider fixing this for Orcas. |
736 | PTR_PEFile pFile = pModule->GetAssembly()->GetManifestModule()->GetFile(); |
737 | |
738 | // for dynamic modules use 0 as the time stamp |
739 | ULONG ulTimeStamp = 0; |
740 | |
741 | if (!pFile->IsDynamic()) |
742 | { |
743 | ulTimeStamp = pFile->GetILImageTimeDateStamp(); |
744 | _ASSERTE(ulTimeStamp != 0); |
745 | } |
746 | |
747 | _snwprintf_s(targetParam, |
748 | maxLength, |
749 | _TRUNCATE, |
750 | W("%x" ), |
751 | ulTimeStamp); |
752 | } |
753 | EX_CATCH |
754 | { |
755 | failed = true; |
756 | } |
757 | EX_END_CATCH(SwallowAllExceptions) |
758 | } |
759 | |
760 | if (!pModule || failed) |
761 | { |
762 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
763 | } |
764 | } |
765 | |
766 | void BaseBucketParamsManager::GetMethodDef(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
767 | { |
768 | CONTRACTL |
769 | { |
770 | NOTHROW; |
771 | GC_NOTRIGGER; |
772 | MODE_ANY; |
773 | } |
774 | CONTRACTL_END; |
775 | |
776 | if (m_pFaultingMD) |
777 | { |
778 | mdMethodDef methodDef = m_pFaultingMD->GetMemberDef(); |
779 | _snwprintf_s(targetParam, |
780 | maxLength, |
781 | _TRUNCATE, |
782 | W("%x" ), |
783 | RidFromToken(methodDef)); |
784 | } |
785 | else |
786 | { |
787 | wcsncpy_s(targetParam, maxLength, W("missing" ), _TRUNCATE); |
788 | } |
789 | } |
790 | |
791 | void BaseBucketParamsManager::GetIlOffset(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
792 | { |
793 | CONTRACTL |
794 | { |
795 | NOTHROW; |
796 | GC_NOTRIGGER; |
797 | MODE_ANY; |
798 | } |
799 | CONTRACTL_END; |
800 | |
801 | DWORD ilOffset = GetILOffset(); |
802 | |
803 | _snwprintf_s(targetParam, |
804 | maxLength, |
805 | _TRUNCATE, |
806 | W("%x" ), |
807 | ilOffset); |
808 | } |
809 | |
810 | void BaseBucketParamsManager::GetExceptionName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
811 | { |
812 | CONTRACTL |
813 | { |
814 | NOTHROW; |
815 | GC_NOTRIGGER; |
816 | MODE_ANY; |
817 | } |
818 | CONTRACTL_END; |
819 | |
820 | if (m_tore.GetType() != TypeOfReportedError::StackOverflowException) |
821 | { |
822 | // At this point we have to switch to cooperative mode, because we need an OBJECTREF. |
823 | GCX_COOP(); |
824 | |
825 | OBJECTREF throwable = GetRealExceptionObject(); |
826 | |
827 | LPCWSTR pExceptionName = NULL; |
828 | |
829 | if (throwable == NULL) |
830 | { |
831 | // Don't have an exception object. Make up something reasonable. |
832 | switch (m_tore.GetType()) |
833 | { |
834 | case TypeOfReportedError::NativeThreadUnhandledException: |
835 | case TypeOfReportedError::UnhandledException: |
836 | pExceptionName = W("Exception" ); |
837 | break; |
838 | case TypeOfReportedError::FatalError: |
839 | pExceptionName = W("FatalError" ); |
840 | break; |
841 | case TypeOfReportedError::UserBreakpoint: |
842 | pExceptionName = W("Debugger.Break" ); |
843 | break; |
844 | case TypeOfReportedError::NativeBreakpoint: |
845 | pExceptionName = W("Breakpoint" ); |
846 | break; |
847 | default: |
848 | _ASSERTE(!"Unexpected TypeOfReportedError" ); |
849 | break; |
850 | } |
851 | } |
852 | else |
853 | { |
854 | MethodTable* pMT = OBJECTREFToObject(throwable)->GetMethodTable(); |
855 | DefineFullyQualifiedNameForClassWOnStack(); |
856 | |
857 | EX_TRY |
858 | { |
859 | pExceptionName = GetFullyQualifiedNameForClassNestedAwareW(pMT); |
860 | } |
861 | EX_CATCH |
862 | { |
863 | } |
864 | EX_END_CATCH(SwallowAllExceptions); |
865 | } |
866 | |
867 | _ASSERTE(pExceptionName); |
868 | |
869 | // Copy name in, with possible truncation or hashing. |
870 | CopyStringToBucket(targetParam, maxLength, pExceptionName); |
871 | } |
872 | else // StackOverflowException |
873 | { |
874 | // During StackOverflowException processing we may be under ThreadStore lock and cannot spawn a managed thread (otherwise deadlock). |
875 | // So we avoid using any managed heap objects and switching to GC_COOP. |
876 | CopyStringToBucket(targetParam, maxLength, W("System.StackOverflowException" )); |
877 | } |
878 | } |
879 | |
880 | void BaseBucketParamsManager::GetPackageMoniker(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
881 | { |
882 | CONTRACTL |
883 | { |
884 | NOTHROW; |
885 | GC_NOTRIGGER; |
886 | MODE_ANY; |
887 | } |
888 | CONTRACTL_END; |
889 | |
890 | _ASSERTE(!"AppX support NYI for CoreCLR" ); |
891 | } |
892 | |
893 | void BaseBucketParamsManager::GetPRAID(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
894 | { |
895 | CONTRACTL |
896 | { |
897 | NOTHROW; |
898 | GC_NOTRIGGER; |
899 | MODE_ANY; |
900 | } |
901 | CONTRACTL_END; |
902 | |
903 | _ASSERTE(!"PRAID support NYI for CoreCLR" ); |
904 | } |
905 | |
906 | void BaseBucketParamsManager::GetIlRva(__out_ecount(maxLength) WCHAR* targetParam, int maxLength) |
907 | { |
908 | CONTRACTL |
909 | { |
910 | NOTHROW; |
911 | GC_NOTRIGGER; |
912 | MODE_ANY; |
913 | } |
914 | CONTRACTL_END; |
915 | |
916 | DWORD ilOffset = GetILOffset(); |
917 | |
918 | if (ilOffset == MAXDWORD) |
919 | ilOffset = 0; |
920 | |
921 | if (m_pFaultingMD) |
922 | ilOffset += m_pFaultingMD->GetRVA(); |
923 | |
924 | _snwprintf_s(targetParam, |
925 | maxLength, |
926 | _TRUNCATE, |
927 | W("%x" ), |
928 | ilOffset); |
929 | } |
930 | |
931 | // helper functions |
932 | |
933 | DWORD BaseBucketParamsManager::GetILOffset() |
934 | { |
935 | CONTRACTL |
936 | { |
937 | NOTHROW; |
938 | GC_NOTRIGGER; |
939 | MODE_ANY; |
940 | } |
941 | CONTRACTL_END; |
942 | |
943 | DWORD nativeOffset = 0; |
944 | DWORD ilOffset = MAXDWORD; |
945 | |
946 | EECodeInfo codeInfo(m_faultingPc); |
947 | if (codeInfo.IsValid()) |
948 | { |
949 | nativeOffset = codeInfo.GetRelOffset(); |
950 | _ASSERTE(m_pFaultingMD == codeInfo.GetMethodDesc()); |
951 | } |
952 | |
953 | if (m_pFaultingMD) |
954 | { |
955 | EX_TRY |
956 | { |
957 | CONTRACT_VIOLATION(GCViolation); |
958 | _ASSERTE(g_pDebugInterface != NULL); |
959 | g_pDebugInterface->GetILOffsetFromNative( |
960 | m_pFaultingMD, |
961 | (const BYTE *)m_faultingPc, |
962 | nativeOffset, |
963 | &ilOffset); |
964 | } |
965 | EX_CATCH |
966 | { |
967 | // Swallow the exception, and just use MAXDWORD. |
968 | } |
969 | EX_END_CATCH(SwallowAllExceptions) |
970 | } |
971 | |
972 | return ilOffset; |
973 | } |
974 | |
975 | // attempts to get file version information for the specified module. |
976 | // returns true on success and all out params will contain data. |
977 | // on failure the out params are not touched. |
978 | // assumes that pModule is not NULL!! |
979 | bool BaseBucketParamsManager::GetFileVersionInfoForModule(Module* pModule, USHORT& major, USHORT& minor, USHORT& build, USHORT& revision) |
980 | { |
981 | CONTRACTL |
982 | { |
983 | NOTHROW; |
984 | GC_NOTRIGGER; |
985 | MODE_ANY; |
986 | PRECONDITION(pModule != NULL); |
987 | } |
988 | CONTRACTL_END; |
989 | |
990 | bool succeeded = false; |
991 | |
992 | PEFile* pFile = pModule->GetFile(); |
993 | if (pFile) |
994 | { |
995 | // if we have a native imaged loaded for this module then get the version information from that. |
996 | if (pFile->IsNativeLoaded()) |
997 | { |
998 | PEImage* pNativeImage = pFile->GetPersistentNativeImage(); |
999 | |
1000 | if (pNativeImage) |
1001 | { |
1002 | LPCWSTR niPath = pNativeImage->GetPath().GetUnicode(); |
1003 | if (niPath != NULL && niPath != SString::Empty() && SUCCEEDED(DwGetFileVersionInfo(niPath, major, minor, build, revision))) |
1004 | { |
1005 | succeeded = true; |
1006 | } |
1007 | } |
1008 | } |
1009 | |
1010 | // if we failed to get the version info from the native image then fall back to the IL image. |
1011 | if (!succeeded) |
1012 | { |
1013 | LPCWSTR modulePath = pFile->GetPath().GetUnicode(); |
1014 | if (modulePath != NULL && modulePath != SString::Empty() && SUCCEEDED(DwGetFileVersionInfo(modulePath, major, minor, build, revision))) |
1015 | { |
1016 | succeeded = true; |
1017 | } |
1018 | } |
1019 | } |
1020 | |
1021 | return succeeded; |
1022 | } |
1023 | |
1024 | // attempts to determine if the specified MethodDesc is one of the code contracts methods. |
1025 | // this is defined as any method on the System.Diagnostics.Contracts.__ContractsRuntime type. |
1026 | bool BaseBucketParamsManager::IsCodeContractsFrame(MethodDesc* pMD) |
1027 | { |
1028 | CONTRACTL |
1029 | { |
1030 | NOTHROW; |
1031 | GC_NOTRIGGER; |
1032 | MODE_ANY; |
1033 | PRECONDITION(pMD != NULL); |
1034 | } |
1035 | CONTRACTL_END; |
1036 | |
1037 | if (!pMD) |
1038 | return false; |
1039 | |
1040 | MethodTable* pMT = pMD->GetMethodTable_NoLogging(); |
1041 | LPCUTF8 pszNamespace = NULL; |
1042 | LPCUTF8 pszName = NULL; |
1043 | pszName = pMT->GetFullyQualifiedNameInfo(&pszNamespace); |
1044 | |
1045 | if (!pszName || !pszNamespace) |
1046 | return false; |
1047 | |
1048 | LPCUTF8 pszContractsNamespace = "System.Diagnostics.Contracts" ; |
1049 | LPCUTF8 pszContractsRuntimeType = "__ContractsRuntime" ; |
1050 | |
1051 | if (strcmp(pszNamespace, pszContractsNamespace) == 0 && |
1052 | strcmp(pszName, pszContractsRuntimeType) == 0) |
1053 | return true; |
1054 | |
1055 | return false; |
1056 | } |
1057 | |
1058 | // code contract failures will have several frames on the stack which are part of the code contracts infrastructure. |
1059 | // as such we don't want to blame any of these frames since they're just propagating the fault from the user's code. |
1060 | // the purpose of this function is to identify if the current faulting frame is part of the code contract infrastructure |
1061 | // and if it is to traverse the stack trace in the exception object until the first frame which isn't code contracts stuff. |
1062 | void BaseBucketParamsManager::FindFaultingMethodInfo() |
1063 | { |
1064 | CONTRACTL |
1065 | { |
1066 | NOTHROW; |
1067 | GC_NOTRIGGER; |
1068 | MODE_ANY; |
1069 | PRECONDITION(m_pFaultingMD != NULL); |
1070 | } |
1071 | CONTRACTL_END; |
1072 | |
1073 | // check if this frame is part of the code contracts infrastructure |
1074 | if (IsCodeContractsFrame(m_pFaultingMD)) |
1075 | { |
1076 | // it is so we need to do more searching to find the correct faulting MethodDesc. |
1077 | // iterate over each frame in the stack trace object until we find the first |
1078 | // frame that isn't part of the code contracts goop. |
1079 | GCX_COOP(); |
1080 | |
1081 | OBJECTREF throwable = GetRealExceptionObject(); |
1082 | |
1083 | if (throwable != NULL) |
1084 | { |
1085 | StackTraceArray traceData; |
1086 | EXCEPTIONREF(throwable)->GetStackTrace(traceData); |
1087 | |
1088 | GCPROTECT_BEGIN(traceData); |
1089 | |
1090 | size_t numElements = traceData.Size(); |
1091 | |
1092 | ContractFailureKind kind = GetContractFailureKind(throwable); |
1093 | |
1094 | // skip frame 0 since we already know it's part of code contracts |
1095 | for (size_t index = 1; index < numElements; ++index) |
1096 | { |
1097 | StackTraceElement const& cur = traceData[index]; |
1098 | |
1099 | MethodDesc* pMD = cur.pFunc; |
1100 | _ASSERTE(pMD); |
1101 | |
1102 | if (!IsCodeContractsFrame(pMD)) |
1103 | { |
1104 | // we want the next frame for preconditions however if we don't have it for some |
1105 | // reason then just use this frame (better than defaulting to the code contracts goop) |
1106 | if ((kind == CONTRACT_FAILURE_PRECONDITION) && (index + 1 < numElements)) |
1107 | { |
1108 | _ASSERTE(!IsCodeContractsFrame(traceData[index + 1].pFunc)); |
1109 | continue; |
1110 | } |
1111 | |
1112 | m_pFaultingMD = pMD; |
1113 | m_faultingPc = cur.ip; |
1114 | break; |
1115 | } |
1116 | } |
1117 | |
1118 | GCPROTECT_END(); |
1119 | } |
1120 | } |
1121 | } |
1122 | |
1123 | // gets the "real" exception object. it might be m_pException or the exception object on the thread |
1124 | OBJECTREF BaseBucketParamsManager::GetRealExceptionObject() |
1125 | { |
1126 | CONTRACTL |
1127 | { |
1128 | NOTHROW; |
1129 | GC_NOTRIGGER; |
1130 | MODE_COOPERATIVE; |
1131 | } |
1132 | CONTRACTL_END; |
1133 | |
1134 | OBJECTREF throwable = NULL; |
1135 | |
1136 | if (m_pException != NULL) |
1137 | { |
1138 | _ASSERTE(IsProtectedByGCFrame(m_pException)); |
1139 | throwable = *m_pException; |
1140 | } |
1141 | else if (m_tore.IsException()) |
1142 | { |
1143 | // If it is an exception, see if there is a Throwable object. |
1144 | if (m_pThread != NULL) |
1145 | { |
1146 | throwable = m_pThread->GetThrowable(); |
1147 | |
1148 | // If the "Throwable" is null, try the "LastThrownObject" |
1149 | if (throwable == NULL) |
1150 | throwable = m_pThread->LastThrownObject(); |
1151 | } |
1152 | } |
1153 | |
1154 | return throwable; |
1155 | } |
1156 | |
1157 | //------------------------------------------------------------------------------ |
1158 | // Description |
1159 | // Copies a string to a Watson bucket parameter. If the offered string is |
1160 | // longer than the maxLen, the string will be shortened. |
1161 | // |
1162 | // Parameters |
1163 | // pTargetParam -- the destination buffer. |
1164 | // targetMaxLength -- the max length of the parameter. |
1165 | // pSource -- the input string. |
1166 | // cannonicalize -- if true, cannonicalize the filename (tolower) |
1167 | // |
1168 | // Returns |
1169 | // the number of characters copied to the output buffer. zero indicates an |
1170 | // error. |
1171 | // |
1172 | // Notes |
1173 | // The truncation algorithm is this: |
1174 | // - if the value contains non-ascii characters, divide the maxLen by 4, |
1175 | // due to restrictions in Watson bucketing rules |
1176 | // - if the value fits, just copy it as-is |
1177 | // - if the value doesn't fit, strip any trailing ".dll", ".exe", ".netmodule", |
1178 | // or "Exception" |
1179 | // - if the value still doesn't fit, take a SHA1 hash of the source, and |
1180 | // encode in base32. |
1181 | // - if the value may require hashing, the maxlen should be at least 32, |
1182 | // because that is what a SHA1 hash coded in base32 will require. |
1183 | // - the maxlen does not include the terminating nul. |
1184 | //------------------------------------------------------------------------------ |
1185 | int BaseBucketParamsManager::CopyStringToBucket(__out_ecount(targetMaxLength) LPWSTR pTargetParam, int targetMaxLength, __in_z LPCWSTR pSource, bool cannonicalize) |
1186 | { |
1187 | CONTRACTL |
1188 | { |
1189 | NOTHROW; |
1190 | GC_NOTRIGGER; |
1191 | MODE_ANY; |
1192 | } |
1193 | CONTRACTL_END; |
1194 | |
1195 | // Array of suffixes to truncate if necessary. |
1196 | static const LPCWSTR truncations[] = |
1197 | { |
1198 | W("Exception" ), |
1199 | W(".dll" ), |
1200 | W(".exe" ), |
1201 | W(".netmodule" ), |
1202 | 0 |
1203 | }; |
1204 | |
1205 | int srcLen = static_cast<int>(wcslen(pSource)); |
1206 | |
1207 | // If the source contains unicode characters, they'll be encoded at 4 chars per char. |
1208 | int targLen = ContainsUnicodeChars(pSource) ? targetMaxLength / 4 : targetMaxLength; |
1209 | |
1210 | // If the string is too long, see if there is a suffix that can be trimmed. |
1211 | if (srcLen > targLen) |
1212 | { |
1213 | for (int i = 0; truncations[i]; ++i) |
1214 | { |
1215 | // how long is this suffix? |
1216 | int slen = static_cast<int>(wcslen(truncations[i])); |
1217 | |
1218 | // Could the string have this suffix? |
1219 | if (slen < srcLen) |
1220 | { |
1221 | // maybe -- check. |
1222 | if (SString::_wcsicmp(&pSource[srcLen - slen], truncations[i]) == 0) |
1223 | { |
1224 | // yes, the string does have this suffix. drop it. |
1225 | srcLen -= slen; |
1226 | break; |
1227 | } |
1228 | } |
1229 | } |
1230 | } |
1231 | |
1232 | // If the (possibly truncated) value fits, copy it and return. |
1233 | if (srcLen <= targLen) |
1234 | { |
1235 | wcsncpy_s(pTargetParam, DW_MAX_BUCKETPARAM_CWC, pSource, srcLen); |
1236 | |
1237 | if (cannonicalize) |
1238 | { |
1239 | // cannonicalize filenames so that the same exceptions tend to the same buckets. |
1240 | _wcslwr_s(pTargetParam, DW_MAX_BUCKETPARAM_CWC); |
1241 | } |
1242 | return srcLen; |
1243 | } |
1244 | |
1245 | // String didn't fit, so hash it. |
1246 | SHA1Hash hash; |
1247 | hash.AddData(reinterpret_cast<BYTE*>(const_cast<LPWSTR>(pSource)), (static_cast<int>(wcslen(pSource))) * sizeof(WCHAR)); |
1248 | |
1249 | // Encode in base32. The hash is a fixed size; we'll accept up to maxLen characters of the encoding. |
1250 | BytesToBase32 b32(hash.GetHash(), SHA1_HASH_SIZE); |
1251 | targLen = b32.Convert(pTargetParam, targetMaxLength); |
1252 | pTargetParam[targLen] = W('\0'); |
1253 | |
1254 | return targLen; |
1255 | } |
1256 | |
1257 | void BaseBucketParamsManager::LogParam(__in_z LPCWSTR paramValue, BucketParameterIndex paramIndex) |
1258 | { |
1259 | #ifdef _DEBUG |
1260 | LIMITED_METHOD_CONTRACT; |
1261 | |
1262 | _ASSERTE(paramIndex < InvalidBucketParamIndex); |
1263 | // the BucketParameterIndex enum starts at 0 however we refer to Watson |
1264 | // bucket params with 1-based indices so we add one to paramIndex. |
1265 | LOG((LF_EH, LL_INFO10, " p %d: %S\n" , paramIndex + 1, paramValue)); |
1266 | ++m_countParamsLogged; |
1267 | #endif |
1268 | } |
1269 | |
1270 | // specific manager classes for the various watson bucket types that the CLR reports. |
1271 | // each type is responsible for populating the GMB according to the event type schema. |
1272 | // to add support for a new schema simply inherit from the BaseBucketParamsManager and |
1273 | // in the PopulateBucketParameters() function fill out the GMB as required. then update |
1274 | // function GetBucketParamsManager() (and a few depedent functions) to return the new |
1275 | // type as required. |
1276 | |
1277 | class CLR20r3BucketParamsManager : public BaseBucketParamsManager |
1278 | { |
1279 | public: |
1280 | CLR20r3BucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException); |
1281 | ~CLR20r3BucketParamsManager(); |
1282 | |
1283 | virtual void PopulateBucketParameters(); |
1284 | }; |
1285 | |
1286 | CLR20r3BucketParamsManager::CLR20r3BucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException) |
1287 | : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException) |
1288 | { |
1289 | CONTRACTL |
1290 | { |
1291 | NOTHROW; |
1292 | GC_NOTRIGGER; |
1293 | MODE_ANY; |
1294 | } |
1295 | CONTRACTL_END; |
1296 | } |
1297 | |
1298 | CLR20r3BucketParamsManager::~CLR20r3BucketParamsManager() |
1299 | { |
1300 | LIMITED_METHOD_CONTRACT; |
1301 | } |
1302 | |
1303 | void CLR20r3BucketParamsManager::PopulateBucketParameters() |
1304 | { |
1305 | CONTRACTL |
1306 | { |
1307 | NOTHROW; |
1308 | GC_NOTRIGGER; |
1309 | MODE_ANY; |
1310 | } |
1311 | CONTRACTL_END; |
1312 | |
1313 | PopulateEventName(g_WerEventTraits[CLR20r3].EventName); |
1314 | |
1315 | // the "+ 1" is to explicitly indicate which fields need to specify space for NULL |
1316 | PopulateBucketParameter(Parameter1, &CLR20r3BucketParamsManager::GetAppName, 32); |
1317 | PopulateBucketParameter(Parameter2, &CLR20r3BucketParamsManager::GetAppVersion, 23 + 1); |
1318 | PopulateBucketParameter(Parameter3, &CLR20r3BucketParamsManager::GetAppTimeStamp, 8 + 1); |
1319 | PopulateBucketParameter(Parameter4, &CLR20r3BucketParamsManager::GetModuleName, 64); |
1320 | PopulateBucketParameter(Parameter5, &CLR20r3BucketParamsManager::GetModuleVersion, 23 + 1); |
1321 | PopulateBucketParameter(Parameter6, &CLR20r3BucketParamsManager::GetModuleTimeStamp, 8 + 1); |
1322 | PopulateBucketParameter(Parameter7, &CLR20r3BucketParamsManager::GetMethodDef, 6 + 1); |
1323 | PopulateBucketParameter(Parameter8, &CLR20r3BucketParamsManager::GetIlOffset, 8 + 1); |
1324 | PopulateBucketParameter(Parameter9, &CLR20r3BucketParamsManager::GetExceptionName, 32); |
1325 | } |
1326 | |
1327 | class MoCrashBucketParamsManager : public BaseBucketParamsManager |
1328 | { |
1329 | public: |
1330 | MoCrashBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException); |
1331 | ~MoCrashBucketParamsManager(); |
1332 | |
1333 | virtual void PopulateBucketParameters(); |
1334 | }; |
1335 | |
1336 | MoCrashBucketParamsManager::MoCrashBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException) |
1337 | : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException) |
1338 | { |
1339 | CONTRACTL |
1340 | { |
1341 | NOTHROW; |
1342 | GC_NOTRIGGER; |
1343 | MODE_ANY; |
1344 | } |
1345 | CONTRACTL_END; |
1346 | } |
1347 | |
1348 | MoCrashBucketParamsManager::~MoCrashBucketParamsManager() |
1349 | { |
1350 | LIMITED_METHOD_CONTRACT; |
1351 | } |
1352 | |
1353 | void MoCrashBucketParamsManager::PopulateBucketParameters() |
1354 | { |
1355 | CONTRACTL |
1356 | { |
1357 | NOTHROW; |
1358 | GC_NOTRIGGER; |
1359 | MODE_ANY; |
1360 | } |
1361 | CONTRACTL_END; |
1362 | |
1363 | PopulateEventName(g_WerEventTraits[MoCrash].EventName); |
1364 | |
1365 | // DW_MAX_BUCKETPARAM_CWC - 1 to ensure space for NULL |
1366 | PopulateBucketParameter(Parameter1, &MoCrashBucketParamsManager::GetPackageMoniker, DW_MAX_BUCKETPARAM_CWC - 1); |
1367 | PopulateBucketParameter(Parameter2, &MoCrashBucketParamsManager::GetPRAID, DW_MAX_BUCKETPARAM_CWC - 1); |
1368 | PopulateBucketParameter(Parameter3, &MoCrashBucketParamsManager::GetAppVersion, DW_MAX_BUCKETPARAM_CWC - 1); |
1369 | PopulateBucketParameter(Parameter4, &MoCrashBucketParamsManager::GetAppTimeStamp, DW_MAX_BUCKETPARAM_CWC - 1); |
1370 | PopulateBucketParameter(Parameter5, &MoCrashBucketParamsManager::GetModuleName, DW_MAX_BUCKETPARAM_CWC - 1); |
1371 | PopulateBucketParameter(Parameter6, &MoCrashBucketParamsManager::GetModuleVersion, DW_MAX_BUCKETPARAM_CWC - 1); |
1372 | PopulateBucketParameter(Parameter7, &MoCrashBucketParamsManager::GetModuleTimeStamp, DW_MAX_BUCKETPARAM_CWC - 1); |
1373 | PopulateBucketParameter(Parameter8, &MoCrashBucketParamsManager::GetExceptionName, DW_MAX_BUCKETPARAM_CWC - 1); |
1374 | PopulateBucketParameter(Parameter9, &MoCrashBucketParamsManager::GetIlRva, DW_MAX_BUCKETPARAM_CWC - 1); |
1375 | } |
1376 | |
1377 | #ifdef FEATURE_WINDOWSPHONE |
1378 | class WinPhoneBucketParamsManager : public BaseBucketParamsManager |
1379 | { |
1380 | public: |
1381 | WinPhoneBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException); |
1382 | ~WinPhoneBucketParamsManager(); |
1383 | |
1384 | virtual void PopulateBucketParameters(); |
1385 | }; |
1386 | |
1387 | WinPhoneBucketParamsManager::WinPhoneBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException) |
1388 | : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException) |
1389 | { |
1390 | CONTRACTL |
1391 | { |
1392 | NOTHROW; |
1393 | GC_NOTRIGGER; |
1394 | MODE_ANY; |
1395 | } |
1396 | CONTRACTL_END; |
1397 | } |
1398 | |
1399 | WinPhoneBucketParamsManager::~WinPhoneBucketParamsManager() |
1400 | { |
1401 | LIMITED_METHOD_CONTRACT; |
1402 | } |
1403 | |
1404 | void WinPhoneBucketParamsManager::PopulateBucketParameters() |
1405 | { |
1406 | CONTRACTL |
1407 | { |
1408 | NOTHROW; |
1409 | GC_NOTRIGGER; |
1410 | MODE_ANY; |
1411 | } |
1412 | CONTRACTL_END; |
1413 | |
1414 | PopulateEventName(g_WerEventTraits[WinPhoneCrash].EventName); |
1415 | |
1416 | // the "+ 1" is to explicitly indicate which fields need to specify space for NULL |
1417 | PopulateBucketParameter(Parameter1, &WinPhoneBucketParamsManager::GetAppName, DW_MAX_BUCKETPARAM_CWC - 1); |
1418 | PopulateBucketParameter(Parameter2, &WinPhoneBucketParamsManager::GetAppVersion, 23 + 1); |
1419 | PopulateBucketParameter(Parameter3, &WinPhoneBucketParamsManager::GetAppTimeStamp, 8 + 1); |
1420 | PopulateBucketParameter(Parameter4, &WinPhoneBucketParamsManager::GetModuleName, DW_MAX_BUCKETPARAM_CWC - 1); |
1421 | PopulateBucketParameter(Parameter5, &WinPhoneBucketParamsManager::GetModuleVersion, 23 + 1); |
1422 | PopulateBucketParameter(Parameter6, &WinPhoneBucketParamsManager::GetModuleTimeStamp, 8 + 1); |
1423 | PopulateBucketParameter(Parameter7, &WinPhoneBucketParamsManager::GetMethodDef, 6 + 1); |
1424 | PopulateBucketParameter(Parameter8, &WinPhoneBucketParamsManager::GetIlOffset, 8 + 1); |
1425 | PopulateBucketParameter(Parameter9, &WinPhoneBucketParamsManager::GetExceptionName, DW_MAX_BUCKETPARAM_CWC - 1); |
1426 | } |
1427 | #endif // FEATURE_WINDOWSPHONE |
1428 | |
1429 | WatsonBucketType GetWatsonBucketType() |
1430 | { |
1431 | CONTRACTL |
1432 | { |
1433 | NOTHROW; |
1434 | GC_NOTRIGGER; |
1435 | MODE_ANY; |
1436 | SUPPORTS_DAC; |
1437 | } |
1438 | CONTRACTL_END; |
1439 | |
1440 | |
1441 | #ifdef FEATURE_WINDOWSPHONE |
1442 | return WinPhoneCrash; |
1443 | #else |
1444 | return CLR20r3; |
1445 | #endif // FEATURE_WINDOWSPHONE |
1446 | } |
1447 | |
1448 | #endif // DACCESS_COMPILE |
1449 | |
1450 | #endif // DWBUCKETMANAGER_HPP |
1451 | |