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
24enum 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
35const DWORD kInvalidParamsCount = 0xffffffff;
36
37struct 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
50const 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
60DWORD 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//------------------------------------------------------------------------------
125class BytesToBase32
126{
127private:
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
171public:
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.
181const 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.
194const 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//------------------------------------------------------------------------------
209WCHAR 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//------------------------------------------------------------------------------
285int 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.
308class BaseBucketParamsManager
309{
310private:
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
328protected:
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
349public:
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
356BaseBucketParamsManager::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
382BaseBucketParamsManager::~BaseBucketParamsManager()
383{
384 LIMITED_METHOD_CONTRACT;
385
386 _ASSERTE(m_countParamsLogged == GetCountBucketParamsForEvent(m_pGmb->wzEventTypeName));
387}
388
389void 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
405WCHAR* 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
446void 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
476void 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
500void 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
535void 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
567void 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
657void 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
713void 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
766void 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
791void 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
810void 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
880void 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
893void 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
906void 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
933DWORD 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!!
979bool 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.
1026bool 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.
1062void 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
1124OBJECTREF 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//------------------------------------------------------------------------------
1185int 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
1257void 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
1277class CLR20r3BucketParamsManager : public BaseBucketParamsManager
1278{
1279public:
1280 CLR20r3BucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1281 ~CLR20r3BucketParamsManager();
1282
1283 virtual void PopulateBucketParameters();
1284};
1285
1286CLR20r3BucketParamsManager::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
1298CLR20r3BucketParamsManager::~CLR20r3BucketParamsManager()
1299{
1300 LIMITED_METHOD_CONTRACT;
1301}
1302
1303void 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
1327class MoCrashBucketParamsManager : public BaseBucketParamsManager
1328{
1329public:
1330 MoCrashBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1331 ~MoCrashBucketParamsManager();
1332
1333 virtual void PopulateBucketParameters();
1334};
1335
1336MoCrashBucketParamsManager::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
1348MoCrashBucketParamsManager::~MoCrashBucketParamsManager()
1349{
1350 LIMITED_METHOD_CONTRACT;
1351}
1352
1353void 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
1378class WinPhoneBucketParamsManager : public BaseBucketParamsManager
1379{
1380public:
1381 WinPhoneBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1382 ~WinPhoneBucketParamsManager();
1383
1384 virtual void PopulateBucketParameters();
1385};
1386
1387WinPhoneBucketParamsManager::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
1399WinPhoneBucketParamsManager::~WinPhoneBucketParamsManager()
1400{
1401 LIMITED_METHOD_CONTRACT;
1402}
1403
1404void 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
1429WatsonBucketType 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