2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
6 // FILE: dwbucketmanager.hpp
8 // This file contains the manager types for differents types of Watson buckets
9 // and various helper types.
15 // ============================================================================
17 #ifndef DWBUCKETMANAGER_HPP
18 #define DWBUCKETMANAGER_HPP
20 #ifdef FEATURE_WINDOWSPHONE
24 // this will be used as an index into g_WerEventTraits
29 #ifdef FEATURE_WINDOWSPHONE
32 // insert new types above this line
36 const DWORD kInvalidParamsCount = 0xffffffff;
38 struct WerEventTypeTraits
40 const LPCWSTR EventName;
41 const DWORD CountParams;
42 INDEBUG(const WatsonBucketType BucketType);
44 WerEventTypeTraits(LPCWSTR name, DWORD params DEBUG_ARG(WatsonBucketType type))
45 : EventName(name), CountParams(params) DEBUG_ARG(BucketType(type))
47 _ASSERTE(params < kInvalidParamsCount);
51 const WerEventTypeTraits g_WerEventTraits[] =
53 WerEventTypeTraits(W("CLR20r3"), 9 DEBUG_ARG(CLR20r3)),
54 WerEventTypeTraits(W("MoAppCrash"), 9 DEBUG_ARG(MoCrash))
55 #ifdef FEATURE_WINDOWSPHONE
56 // unfortunately Apollo uses the same event name
57 ,WerEventTypeTraits(W("CLR20r3"), 9 DEBUG_ARG(WinPhoneCrash))
61 DWORD GetCountBucketParamsForEvent(LPCWSTR wzEventName)
72 if (wzEventName == NULL)
74 _ASSERTE(!"missing event name when retrieving bucket params count");
78 DWORD countParams = kInvalidParamsCount;
79 for (int index = 0; index < EndOfWerBucketTypes; ++index)
81 if (wcscmp(wzEventName, g_WerEventTraits[index].EventName) == 0)
83 _ASSERTE(index == g_WerEventTraits[index].BucketType);
84 countParams = g_WerEventTraits[index].CountParams;
89 if (countParams == kInvalidParamsCount)
91 _ASSERTE(!"unknown event name when retrieving bucket params count");
98 #ifndef DACCESS_COMPILE
100 #include "dwreport.h"
101 #include <msodwwrap.h>
102 #include "dbginterface.h"
106 #include "appxutil.h"
109 //------------------------------------------------------------------------------
111 // Converts an array of bytes to a string of base32 encoded characters.
114 // pData -- The bytes to be converted.
115 // nData -- Count of bytes to be converted.
118 // pOut -- Put converted bytes here.
119 // nOut -- Max number of characters to put
121 // returns -- Number of characters put.
124 // Five bytes of input produces 8 characters of output.
125 //------------------------------------------------------------------------------
129 // Five doesn't go into 8 very well, so we will wind up with 8 characters per
130 // five bytes of input. Specifically, a block of 5 bytes will be formatted
132 // 7 6 5 4 3 2 1 0 <-- bit #
134 // 1 2 2 3 3 3 3 3 4 <-- which character does the bit go to?
138 // This structure defines 2 masks and 3 shift values per 5-bit value.
139 // The first mask is the mask from the first byte. The first two
140 // shifts are a left- OR a right- shift for the bits obtained via that mask.
141 // If there is a second mask, that is to get bits from the next byte,
142 // shifted right by the second shift value. Finally, there is a bit to
143 // indicate that the scanner should advance to the next byte.
144 // Referring to the table above, the decoder values for the first 5-bit
147 // l1 : 0 - no left shift
148 // r1 : 3 - right shift 3 bits
149 // m2 : 0 - no second mask
150 // r2 : 0 - no second right shift
151 // skip : 0 - don't skip to next byte (still 3 more bits, for the second 5-bits.
154 unsigned int m1 : 8; // Mask 1
155 unsigned int l1 : 4; // Left shift 1
156 unsigned int r1 : 4; // Right shift 2
157 unsigned int m2 : 8; // Mask 2
158 unsigned int r2 : 4; // Right shift 2
159 unsigned int skip:4; // Skip to next input byte
162 static const decoder_ decoder[8]; // Array of decoder specs.
163 static const WCHAR base32[33]; // Array of 33 characters: A-Z, 0-5, =
165 BYTE *pData; // Pointer to data.
166 int nData; // Total bytes of data.
170 int nWhich; // Where in the sequence of 8 5-bit datums?
173 BytesToBase32(BYTE *p, int n) : pData(p), nData(n), nWhich(0) { LIMITED_METHOD_CONTRACT; pEnd = pData + nData; }
176 BOOL MoreChars() { LIMITED_METHOD_CONTRACT; return pData < pEnd; }
178 int Convert(__inout_ecount(nOut) LPWSTR pOut, int nOut);
181 // This table tells how to pick out 5-bits at a time (8 times) from 5-bytes of data.
182 const BytesToBase32::decoder_ BytesToBase32::decoder[8] =
183 { // m1 l1 r1 m2 r2 skip
184 {0xf8, 0, 3, 0x00, 0, 0},
185 {0x07, 2, 0, 0xc0, 6, 1},
186 {0x3e, 0, 1, 0x00, 0, 0},
187 {0x01, 4, 0, 0xf0, 4, 1},
188 {0x0f, 1, 0, 0x80, 7, 1},
189 {0x7c, 0, 2, 0x00, 0, 0},
190 {0x03, 3, 0, 0xe0, 5, 1},
191 {0x1f, 0, 0, 0x00, 0, 1},
194 // Array of characters with which to encode.
195 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'};
197 //------------------------------------------------------------------------------
199 // Converts 5-bits to a character; fundamental base32 encoding.
205 // The next 5-bits, converted to a character. Also advances the
206 // character pointer. When no characters remain to be converted,
209 //------------------------------------------------------------------------------
210 WCHAR BytesToBase32::GetNextChar()
212 LIMITED_METHOD_CONTRACT;
214 unsigned int result = 0;
216 _ASSERTE(pData <= pEnd);
217 _ASSERTE(nWhich >= 0 && nWhich < lengthof(decoder));
219 // If out of data, return signal value, > any valid char.
221 return base32[lengthof(base32)-1];
224 if (decoder[nWhich].l1)
225 { // There is a l1 shift.
226 _ASSERTE(decoder[nWhich].m1); // There should be a m1 mask
227 _ASSERTE(decoder[nWhich].r1 == 0); // There should not be a r1 shift
228 _ASSERTE(decoder[nWhich].m2); // There shoulbe a m2 mask to fill in the rest of the bits.
229 _ASSERTE(decoder[nWhich].r2); // m2 bits never start in the right place; there must be a shift
230 // The masks, shifted, and or'd together should equal 0x1f, 5-bits.
231 _ASSERTE( ( (decoder[nWhich].m1 << decoder[nWhich].l1) | (decoder[nWhich].m2 >> decoder[nWhich].r2)) == 0x1f);
234 { // There is no l1 shift.
235 _ASSERTE(decoder[nWhich].m2 == 0); // There should not be any m2 bits
236 _ASSERTE( (decoder[nWhich].m1 >> decoder[nWhich].r1) == 0x1f); // The m1 bits, shifted should be 0x1f, 5-bits.
240 // Mask off the bits.
241 result = *pData & decoder[nWhich].m1;
243 // Shift left or right as needed.
244 if (decoder[nWhich].l1)
245 { // Shift up to make space for low-order bits from next byte.
246 result = result << decoder[nWhich].l1;
249 if (decoder[nWhich].r1)
250 { // Shift down into position. There should be no more bits from next byte.
251 result = result >> decoder[nWhich].r1;
254 // Skip to next byte if appropriate.
255 if (decoder[nWhich].skip)
258 // Grab more bits if specified, and more are available.
259 if (pData < pEnd && decoder[nWhich].m2)
260 { // All second-byte data are shifted right, so just mask and shift.
261 result |= ( (*pData & decoder[nWhich].m2) >> decoder[nWhich].r2);
264 // Advance the 'state machine' -- which 5-bits from an 8-byte block.
265 if (++nWhich == lengthof(decoder))
268 // Sanity check on value.
269 _ASSERTE(result < lengthof(base32));
271 return base32[result];
272 } // WCHAR BytesToBase32::GetNextChar()
274 //------------------------------------------------------------------------------
276 // Performs the conversion of a buffer to base32.
279 // pOut -- Buffer to receive the characters.
280 // nOut -- Maximum characters to write to the buffer.
283 // the number of characters copied to the output buffer.
285 //------------------------------------------------------------------------------
286 int BytesToBase32::Convert(
287 __inout_ecount(nOut) LPWSTR pOut,
292 int nWritten = 0; // Count of bytes written to output.
294 // Stop when the buffer is full, or the bytes are fully converted.
295 while (nOut > 0 && MoreChars())
297 *pOut = GetNextChar();
304 } // int BytesToBase32::Convert()
306 // this abstract class provides base functionality for populating a bucket parameter in the GMB with some data.
307 // the actual mapping of ordinal parameter to data type (eg parameter 1 is app name) is handled in subclasses
308 // of this type. see GetBucketParamsManager() for retrieving a bucket params manager.
309 class BaseBucketParamsManager
312 GenericModeBlock* m_pGmb;
313 TypeOfReportedError m_tore;
315 OBJECTREF* m_pException;
316 INDEBUG(size_t m_countParamsLogged);
317 MethodDesc* m_pFaultingMD;
320 // misc helper functions
322 bool GetFileVersionInfoForModule(Module* pModule, USHORT& major, USHORT& minor, USHORT& build, USHORT& revision);
323 bool IsCodeContractsFrame(MethodDesc* pMD);
324 void FindFaultingMethodInfo();
325 OBJECTREF GetRealExceptionObject();
326 WCHAR* GetParamBufferForIndex(BucketParameterIndex paramIndex);
327 int CopyStringToBucket(__out_ecount(targetMaxLength) LPWSTR pTargetParam, int targetMaxLength, __in_z LPCWSTR pSource, bool cannonicalize = false);
328 void LogParam(__in_z LPCWSTR paramValue, BucketParameterIndex paramIndex);
331 ~BaseBucketParamsManager();
333 typedef void (BaseBucketParamsManager::*DataPopulatorFunction)(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
334 void PopulateBucketParameter(BucketParameterIndex paramIndex, DataPopulatorFunction pFnDataPopulator, int maxLength);
336 void PopulateEventName(LPCWSTR eventTypeName);
337 // functions for retrieving data to go into various bucket parameters
338 void GetAppName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
339 void GetAppVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
340 void GetAppTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
341 void GetModuleName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
342 void GetModuleVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
343 void GetModuleTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
344 void GetMethodDef(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
345 void GetIlOffset(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
346 void GetExceptionName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
347 void GetPackageMoniker(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
348 void GetPRAID(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
349 void GetIlRva(__out_ecount(maxLength) WCHAR* targetParam, int maxLength);
352 BaseBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE initialFaultingPc, Thread* pFaultingThread, OBJECTREF* pThrownException);
354 // function that consumers should call to populate the GMB
355 virtual void PopulateBucketParameters() = 0;
358 BaseBucketParamsManager::BaseBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE initialFaultingPc, Thread* pFaultingThread, OBJECTREF* pThrownException)
359 : m_pFaultingMD(NULL), m_faultingPc(initialFaultingPc), m_pGmb(pGenericModeBlock), m_tore(typeOfError), m_pThread(pFaultingThread), m_pException(pThrownException)
370 INDEBUG(m_countParamsLogged = 0);
372 ZeroMemory(pGenericModeBlock, sizeof(GenericModeBlock));
374 EECodeInfo codeInfo(initialFaultingPc);
375 if (codeInfo.IsValid())
377 m_pFaultingMD = codeInfo.GetMethodDesc();
380 FindFaultingMethodInfo();
384 BaseBucketParamsManager::~BaseBucketParamsManager()
386 LIMITED_METHOD_CONTRACT;
388 _ASSERTE(m_countParamsLogged == GetCountBucketParamsForEvent(m_pGmb->wzEventTypeName));
391 void BaseBucketParamsManager::PopulateEventName(LPCWSTR eventTypeName)
401 wcsncpy_s(m_pGmb->wzEventTypeName, DW_MAX_BUCKETPARAM_CWC, eventTypeName, _TRUNCATE);
403 _ASSERTE(GetCountBucketParamsForEvent(eventTypeName));
404 LOG((LF_EH, LL_INFO10, "Event : %S\n", m_pGmb->wzEventTypeName));
407 WCHAR* BaseBucketParamsManager::GetParamBufferForIndex(BucketParameterIndex paramIndex)
417 _ASSERTE(paramIndex < InvalidBucketParamIndex);
440 _ASSERTE(!"bad paramIndex");
441 // this is a back-stop to prevent returning NULL and having to have
442 // callers check for it. we should never get here though anyways.
443 return m_pGmb->wzP10;
448 void BaseBucketParamsManager::PopulateBucketParameter(BucketParameterIndex paramIndex, DataPopulatorFunction pFnDataPopulator, int maxLength)
458 _ASSERTE(paramIndex < InvalidBucketParamIndex);
459 WCHAR* targetParam = GetParamBufferForIndex(paramIndex);
461 // verify that we haven't already written data to this param
462 _ASSERTE(targetParam && targetParam[0] == W('\0'));
463 #ifdef FEATURE_WINDOWSPHONE
464 WCHAR const* overrideParam = g_CLRErrorReportingManager.GetBucketParamOverride(paramIndex);
465 if (overrideParam != NULL)
467 CopyStringToBucket(targetParam, maxLength, overrideParam, false);
470 #endif // FEATURE_WINDOWSPHONE
472 (this->*pFnDataPopulator)(targetParam, maxLength);
475 LogParam(targetParam, paramIndex);
478 void BaseBucketParamsManager::GetAppName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
488 HMODULE hModule = WszGetModuleHandle(NULL);
489 WCHAR appPath[MAX_LONGPATH];
490 DWORD cchAppPath = NumItems(appPath);
492 if (GetCurrentModuleFileName(appPath, &cchAppPath) == S_OK)
494 CopyStringToBucket(targetParam, maxLength, appPath);
498 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
502 void BaseBucketParamsManager::GetAppVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
512 HMODULE hModule = WszGetModuleHandle(NULL);
513 WCHAR appPath[MAX_LONGPATH];
514 DWORD cchAppPath = NumItems(appPath);
517 USHORT major, minor, build, revision;
519 if ((GetCurrentModuleFileName(appPath, &cchAppPath) == S_OK) && SUCCEEDED(DwGetFileVersionInfo(appPath, major, minor, build, revision)))
521 _snwprintf_s(targetParam,
525 major, minor, build, revision);
527 else if (DwGetAssemblyVersion(appPath, verBuf, NumItems(verBuf)) != 0)
529 wcscpy_s(targetParam, maxLength, verBuf);
533 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
537 void BaseBucketParamsManager::GetAppTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
549 CONTRACT_VIOLATION(GCViolation);
551 HMODULE hModule = WszGetModuleHandle(NULL);
552 PEDecoder pe(hModule);
554 ULONG ulTimeStamp = pe.GetTimeDateStamp();
556 _snwprintf_s(targetParam,
564 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
566 EX_END_CATCH(SwallowAllExceptions)
569 void BaseBucketParamsManager::GetModuleName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
579 Module* pModule = NULL;
581 if (m_pFaultingMD != NULL)
582 pModule = m_pFaultingMD->GetModule();
588 // Get the assembly name, and determine its length, including terminating NULL.
589 Assembly* pAssembly = pModule->GetAssembly();
590 LPCUTF8 utf8AssemblyName = pAssembly->GetSimpleName();
591 const int assemblyNameLength = WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, NULL, 0);
593 // full name and length. minor assumption that this is not multi-module.
594 WCHAR *fullName = NULL;
595 int fullNameLength = assemblyNameLength;
597 if (pModule->IsManifest())
599 // Single-module assembly; allocate a buffer and convert assembly name.
600 fullName = reinterpret_cast< WCHAR* >(_alloca(sizeof(WCHAR)*(fullNameLength)));
601 WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, fullName, fullNameLength);
604 { // This is a non-manifest module, which means it is a multi-module assembly.
605 // Construct a name like 'assembly+module'.
607 // Get the module name, and determine its length, including terminating NULL.
608 LPCUTF8 utf8ModuleName = pModule->GetSimpleName();
609 const int moduleNameLength = WszMultiByteToWideChar(CP_UTF8, 0, utf8ModuleName, -1, NULL, 0);
611 // Full name length is assembly name length + module name length + 1 char for '+'.
612 // However, both assemblyNameLength and moduleNameLength include space for terminating NULL,
613 // but of course only one NULL is needed, so the final length is just the sum of the two lengths.
614 if (!ClrSafeInt<int>::addition(assemblyNameLength, moduleNameLength, fullNameLength))
620 // Allocate a buffer with proper prefast checks.
622 if (!ClrSafeInt<int>::multiply(sizeof(WCHAR), fullNameLength, AllocLen))
628 fullName = reinterpret_cast< WCHAR* >(_alloca(AllocLen));
630 // Convert the assembly name.
631 WszMultiByteToWideChar(CP_UTF8, 0, utf8AssemblyName, -1, fullName, assemblyNameLength);
633 // replace NULL with '+'
634 _ASSERTE(fullName[assemblyNameLength-1] == 0);
635 fullName[assemblyNameLength-1] = W('+');
637 // Convert the module name after the '+'
638 WszMultiByteToWideChar(CP_UTF8, 0, utf8ModuleName,-1, &fullName[assemblyNameLength], moduleNameLength);
645 // Make sure NULL termination is right.
646 _ASSERTE(fullName[fullNameLength - 1] == 0);
648 // Copy name in, with possible truncation or hashing.
649 CopyStringToBucket(targetParam, maxLength, fullName);
653 if (!pModule || failed)
655 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
659 void BaseBucketParamsManager::GetModuleVersion(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
669 Module* pModule = NULL;
671 if (m_pFaultingMD != NULL)
672 pModule = m_pFaultingMD->GetModule();
676 // @TODO: what if the it is in-memory module? It can have the version info.
677 // But we will not retrieve it right.
680 USHORT major = 0, minor = 0, build = 0, revision = 0;
682 bool gotFileVersion = GetFileVersionInfoForModule(pModule, major, minor, build, revision);
684 // if we failed to get a version and this isn't the manifest module then try that
685 if (!gotFileVersion && !pModule->IsManifest())
687 pModule = pModule->GetAssembly()->GetManifestModule();
689 gotFileVersion = GetFileVersionInfoForModule(pModule, major, minor, build, revision);
694 // if we didn't get a file version then fall back to assembly version (typical for in-memory modules)
695 if (FAILED(pModule->GetAssembly()->GetVersion(&major, &minor, &build, &revision)))
701 _snwprintf_s(targetParam,
705 major, minor, build, revision);
709 if (!pModule || failed)
711 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
715 void BaseBucketParamsManager::GetModuleTimeStamp(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
725 Module* pModule = NULL;
727 if (m_pFaultingMD != NULL)
728 pModule = m_pFaultingMD->GetModule();
736 // We only store the IL timestamp in the native image for the
737 // manifest module. We should consider fixing this for Orcas.
738 PTR_PEFile pFile = pModule->GetAssembly()->GetManifestModule()->GetFile();
740 // for dynamic modules use 0 as the time stamp
741 ULONG ulTimeStamp = 0;
743 if (!pFile->IsDynamic())
745 ulTimeStamp = pFile->GetILImageTimeDateStamp();
746 _ASSERTE(ulTimeStamp != 0);
749 _snwprintf_s(targetParam,
759 EX_END_CATCH(SwallowAllExceptions)
762 if (!pModule || failed)
764 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
768 void BaseBucketParamsManager::GetMethodDef(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
780 mdMethodDef methodDef = m_pFaultingMD->GetMemberDef();
781 _snwprintf_s(targetParam,
785 RidFromToken(methodDef));
789 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
793 void BaseBucketParamsManager::GetIlOffset(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
803 DWORD ilOffset = GetILOffset();
805 _snwprintf_s(targetParam,
812 void BaseBucketParamsManager::GetExceptionName(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
822 if (m_tore.GetType() != TypeOfReportedError::StackOverflowException)
824 // At this point we have to switch to cooperative mode, because we need an OBJECTREF.
827 OBJECTREF throwable = GetRealExceptionObject();
829 LPCWSTR pExceptionName = NULL;
831 if (throwable == NULL)
833 // Don't have an exception object. Make up something reasonable.
834 switch (m_tore.GetType())
836 case TypeOfReportedError::NativeThreadUnhandledException:
837 case TypeOfReportedError::UnhandledException:
838 pExceptionName = W("Exception");
840 case TypeOfReportedError::FatalError:
841 pExceptionName = W("FatalError");
843 case TypeOfReportedError::UserBreakpoint:
844 pExceptionName = W("Debugger.Break");
846 case TypeOfReportedError::NativeBreakpoint:
847 pExceptionName = W("Breakpoint");
850 _ASSERTE(!"Unexpected TypeOfReportedError");
856 MethodTable* pMT = OBJECTREFToObject(throwable)->GetMethodTable();
857 DefineFullyQualifiedNameForClassWOnStack();
861 pExceptionName = GetFullyQualifiedNameForClassNestedAwareW(pMT);
866 EX_END_CATCH(SwallowAllExceptions);
869 _ASSERTE(pExceptionName);
871 // Copy name in, with possible truncation or hashing.
872 CopyStringToBucket(targetParam, maxLength, pExceptionName);
874 else // StackOverflowException
876 // During StackOverflowException processing we may be under ThreadStore lock and cannot spawn a managed thread (otherwise deadlock).
877 // So we avoid using any managed heap objects and switching to GC_COOP.
878 CopyStringToBucket(targetParam, maxLength, W("System.StackOverflowException"));
882 void BaseBucketParamsManager::GetPackageMoniker(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
892 #ifndef FEATURE_CORECLR
893 bool success = false;
896 wcsncpy_s(targetParam, maxLength, AppX::GetHeadPackageMoniker(), _TRUNCATE);
902 EX_END_CATCH(SwallowAllExceptions);
906 // should this ever legitimately fail??
907 _ASSERTE(!"failed to get package moniker for watson");
908 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
911 _ASSERTE(!"AppX support NYI for CoreCLR");
912 #endif // FEATURE_CORECLR
915 void BaseBucketParamsManager::GetPRAID(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
925 #ifndef FEATURE_CORECLR
926 LPCWSTR pPraid = NULL;
927 if (SUCCEEDED(AppX::GetApplicationId(pPraid)))
929 _snwprintf_s(targetParam,
937 // should this ever legitimately fail??
938 _ASSERTE(!"failed to get PRAID for watson");
939 wcsncpy_s(targetParam, maxLength, W("missing"), _TRUNCATE);
942 _ASSERTE(!"PRAID support NYI for CoreCLR");
946 void BaseBucketParamsManager::GetIlRva(__out_ecount(maxLength) WCHAR* targetParam, int maxLength)
956 DWORD ilOffset = GetILOffset();
958 if (ilOffset == MAXDWORD)
962 ilOffset += m_pFaultingMD->GetRVA();
964 _snwprintf_s(targetParam,
973 DWORD BaseBucketParamsManager::GetILOffset()
983 DWORD nativeOffset = 0;
984 DWORD ilOffset = MAXDWORD;
986 EECodeInfo codeInfo(m_faultingPc);
987 if (codeInfo.IsValid())
989 nativeOffset = codeInfo.GetRelOffset();
990 _ASSERTE(m_pFaultingMD == codeInfo.GetMethodDesc());
997 CONTRACT_VIOLATION(GCViolation);
998 _ASSERTE(g_pDebugInterface != NULL);
999 g_pDebugInterface->GetILOffsetFromNative(
1001 (const BYTE *)m_faultingPc,
1007 // Swallow the exception, and just use MAXDWORD.
1009 EX_END_CATCH(SwallowAllExceptions)
1015 // attempts to get file version information for the specified module.
1016 // returns true on success and all out params will contain data.
1017 // on failure the out params are not touched.
1018 // assumes that pModule is not NULL!!
1019 bool BaseBucketParamsManager::GetFileVersionInfoForModule(Module* pModule, USHORT& major, USHORT& minor, USHORT& build, USHORT& revision)
1026 PRECONDITION(pModule != NULL);
1030 bool succeeded = false;
1032 PEFile* pFile = pModule->GetFile();
1035 // if we have a native imaged loaded for this module then get the version information from that.
1036 if (pFile->IsNativeLoaded())
1038 PEImage* pNativeImage = pFile->GetPersistentNativeImage();
1042 LPCWSTR niPath = pNativeImage->GetPath().GetUnicode();
1043 if (niPath != NULL && niPath != SString::Empty() && SUCCEEDED(DwGetFileVersionInfo(niPath, major, minor, build, revision)))
1050 // if we failed to get the version info from the native image then fall back to the IL image.
1053 LPCWSTR modulePath = pFile->GetPath().GetUnicode();
1054 if (modulePath != NULL && modulePath != SString::Empty() && SUCCEEDED(DwGetFileVersionInfo(modulePath, major, minor, build, revision)))
1064 // attempts to determine if the specified MethodDesc is one of the code contracts methods.
1065 // this is defined as any method on the System.Diagnostics.Contracts.__ContractsRuntime type.
1066 bool BaseBucketParamsManager::IsCodeContractsFrame(MethodDesc* pMD)
1073 PRECONDITION(pMD != NULL);
1080 MethodTable* pMT = pMD->GetMethodTable_NoLogging();
1081 LPCUTF8 pszNamespace = NULL;
1082 LPCUTF8 pszName = NULL;
1083 pszName = pMT->GetFullyQualifiedNameInfo(&pszNamespace);
1085 if (!pszName || !pszNamespace)
1088 LPCUTF8 pszContractsNamespace = "System.Diagnostics.Contracts";
1089 LPCUTF8 pszContractsRuntimeType = "__ContractsRuntime";
1091 if (strcmp(pszNamespace, pszContractsNamespace) == 0 &&
1092 strcmp(pszName, pszContractsRuntimeType) == 0)
1098 // code contract failures will have several frames on the stack which are part of the code contracts infrastructure.
1099 // as such we don't want to blame any of these frames since they're just propagating the fault from the user's code.
1100 // the purpose of this function is to identify if the current faulting frame is part of the code contract infrastructure
1101 // and if it is to traverse the stack trace in the exception object until the first frame which isn't code contracts stuff.
1102 void BaseBucketParamsManager::FindFaultingMethodInfo()
1109 PRECONDITION(m_pFaultingMD != NULL);
1113 // check if this frame is part of the code contracts infrastructure
1114 if (IsCodeContractsFrame(m_pFaultingMD))
1116 // it is so we need to do more searching to find the correct faulting MethodDesc.
1117 // iterate over each frame in the stack trace object until we find the first
1118 // frame that isn't part of the code contracts goop.
1121 OBJECTREF throwable = GetRealExceptionObject();
1123 if (throwable != NULL)
1125 StackTraceArray traceData;
1126 EXCEPTIONREF(throwable)->GetStackTrace(traceData);
1128 GCPROTECT_BEGIN(traceData);
1130 size_t numElements = traceData.Size();
1132 ContractFailureKind kind = GetContractFailureKind(throwable);
1134 // skip frame 0 since we already know it's part of code contracts
1135 for (size_t index = 1; index < numElements; ++index)
1137 StackTraceElement const& cur = traceData[index];
1139 MethodDesc* pMD = cur.pFunc;
1142 if (!IsCodeContractsFrame(pMD))
1144 // we want the next frame for preconditions however if we don't have it for some
1145 // reason then just use this frame (better than defaulting to the code contracts goop)
1146 if ((kind == CONTRACT_FAILURE_PRECONDITION) && (index + 1 < numElements))
1148 _ASSERTE(!IsCodeContractsFrame(traceData[index + 1].pFunc));
1152 m_pFaultingMD = pMD;
1153 m_faultingPc = cur.ip;
1163 // gets the "real" exception object. it might be m_pException or the exception object on the thread
1164 OBJECTREF BaseBucketParamsManager::GetRealExceptionObject()
1174 OBJECTREF throwable = NULL;
1176 if (m_pException != NULL)
1178 _ASSERTE(IsProtectedByGCFrame(m_pException));
1179 throwable = *m_pException;
1181 else if (m_tore.IsException())
1183 // If it is an exception, see if there is a Throwable object.
1184 if (m_pThread != NULL)
1186 throwable = m_pThread->GetThrowable();
1188 // If the "Throwable" is null, try the "LastThrownObject"
1189 if (throwable == NULL)
1190 throwable = m_pThread->LastThrownObject();
1197 //------------------------------------------------------------------------------
1199 // Copies a string to a Watson bucket parameter. If the offered string is
1200 // longer than the maxLen, the string will be shortened.
1203 // pTargetParam -- the destination buffer.
1204 // targetMaxLength -- the max length of the parameter.
1205 // pSource -- the input string.
1206 // cannonicalize -- if true, cannonicalize the filename (tolower)
1209 // the number of characters copied to the output buffer. zero indicates an
1213 // The truncation algorithm is this:
1214 // - if the value contains non-ascii characters, divide the maxLen by 4,
1215 // due to restrictions in Watson bucketing rules
1216 // - if the value fits, just copy it as-is
1217 // - if the value doesn't fit, strip any trailing ".dll", ".exe", ".netmodule",
1219 // - if the value still doesn't fit, take a SHA1 hash of the source, and
1220 // encode in base32.
1221 // - if the value may require hashing, the maxlen should be at least 32,
1222 // because that is what a SHA1 hash coded in base32 will require.
1223 // - the maxlen does not include the terminating nul.
1224 //------------------------------------------------------------------------------
1225 int BaseBucketParamsManager::CopyStringToBucket(__out_ecount(targetMaxLength) LPWSTR pTargetParam, int targetMaxLength, __in_z LPCWSTR pSource, bool cannonicalize)
1235 // Array of suffixes to truncate if necessary.
1236 static const LPCWSTR truncations[] =
1245 int srcLen = static_cast<int>(wcslen(pSource));
1247 // If the source contains unicode characters, they'll be encoded at 4 chars per char.
1248 int targLen = ContainsUnicodeChars(pSource) ? targetMaxLength / 4 : targetMaxLength;
1250 // If the string is too long, see if there is a suffix that can be trimmed.
1251 if (srcLen > targLen)
1253 for (int i = 0; truncations[i]; ++i)
1255 // how long is this suffix?
1256 int slen = static_cast<int>(wcslen(truncations[i]));
1258 // Could the string have this suffix?
1262 if (SString::_wcsicmp(&pSource[srcLen - slen], truncations[i]) == 0)
1264 // yes, the string does have this suffix. drop it.
1272 // If the (possibly truncated) value fits, copy it and return.
1273 if (srcLen <= targLen)
1275 wcsncpy_s(pTargetParam, DW_MAX_BUCKETPARAM_CWC, pSource, srcLen);
1279 // cannonicalize filenames so that the same exceptions tend to the same buckets.
1280 _wcslwr_s(pTargetParam, DW_MAX_BUCKETPARAM_CWC);
1285 // String didn't fit, so hash it.
1287 hash.AddData(reinterpret_cast<BYTE*>(const_cast<LPWSTR>(pSource)), (static_cast<int>(wcslen(pSource))) * sizeof(WCHAR));
1289 // Encode in base32. The hash is a fixed size; we'll accept up to maxLen characters of the encoding.
1290 BytesToBase32 b32(hash.GetHash(), SHA1_HASH_SIZE);
1291 targLen = b32.Convert(pTargetParam, targetMaxLength);
1292 pTargetParam[targLen] = W('\0');
1297 void BaseBucketParamsManager::LogParam(__in_z LPCWSTR paramValue, BucketParameterIndex paramIndex)
1300 LIMITED_METHOD_CONTRACT;
1302 _ASSERTE(paramIndex < InvalidBucketParamIndex);
1303 // the BucketParameterIndex enum starts at 0 however we refer to Watson
1304 // bucket params with 1-based indices so we add one to paramIndex.
1305 LOG((LF_EH, LL_INFO10, " p %d: %S\n", paramIndex + 1, paramValue));
1306 ++m_countParamsLogged;
1310 // specific manager classes for the various watson bucket types that the CLR reports.
1311 // each type is responsible for populating the GMB according to the event type schema.
1312 // to add support for a new schema simply inherit from the BaseBucketParamsManager and
1313 // in the PopulateBucketParameters() function fill out the GMB as required. then update
1314 // function GetBucketParamsManager() (and a few depedent functions) to return the new
1315 // type as required.
1317 class CLR20r3BucketParamsManager : public BaseBucketParamsManager
1320 CLR20r3BucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1321 ~CLR20r3BucketParamsManager();
1323 virtual void PopulateBucketParameters();
1326 CLR20r3BucketParamsManager::CLR20r3BucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException)
1327 : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException)
1338 CLR20r3BucketParamsManager::~CLR20r3BucketParamsManager()
1340 LIMITED_METHOD_CONTRACT;
1343 void CLR20r3BucketParamsManager::PopulateBucketParameters()
1353 PopulateEventName(g_WerEventTraits[CLR20r3].EventName);
1355 // the "+ 1" is to explicitly indicate which fields need to specify space for NULL
1356 PopulateBucketParameter(Parameter1, &CLR20r3BucketParamsManager::GetAppName, 32);
1357 PopulateBucketParameter(Parameter2, &CLR20r3BucketParamsManager::GetAppVersion, 23 + 1);
1358 PopulateBucketParameter(Parameter3, &CLR20r3BucketParamsManager::GetAppTimeStamp, 8 + 1);
1359 PopulateBucketParameter(Parameter4, &CLR20r3BucketParamsManager::GetModuleName, 64);
1360 PopulateBucketParameter(Parameter5, &CLR20r3BucketParamsManager::GetModuleVersion, 23 + 1);
1361 PopulateBucketParameter(Parameter6, &CLR20r3BucketParamsManager::GetModuleTimeStamp, 8 + 1);
1362 PopulateBucketParameter(Parameter7, &CLR20r3BucketParamsManager::GetMethodDef, 6 + 1);
1363 PopulateBucketParameter(Parameter8, &CLR20r3BucketParamsManager::GetIlOffset, 8 + 1);
1364 PopulateBucketParameter(Parameter9, &CLR20r3BucketParamsManager::GetExceptionName, 32);
1367 class MoCrashBucketParamsManager : public BaseBucketParamsManager
1370 MoCrashBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1371 ~MoCrashBucketParamsManager();
1373 virtual void PopulateBucketParameters();
1376 MoCrashBucketParamsManager::MoCrashBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException)
1377 : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException)
1388 MoCrashBucketParamsManager::~MoCrashBucketParamsManager()
1390 LIMITED_METHOD_CONTRACT;
1393 void MoCrashBucketParamsManager::PopulateBucketParameters()
1403 PopulateEventName(g_WerEventTraits[MoCrash].EventName);
1405 // DW_MAX_BUCKETPARAM_CWC - 1 to ensure space for NULL
1406 PopulateBucketParameter(Parameter1, &MoCrashBucketParamsManager::GetPackageMoniker, DW_MAX_BUCKETPARAM_CWC - 1);
1407 PopulateBucketParameter(Parameter2, &MoCrashBucketParamsManager::GetPRAID, DW_MAX_BUCKETPARAM_CWC - 1);
1408 PopulateBucketParameter(Parameter3, &MoCrashBucketParamsManager::GetAppVersion, DW_MAX_BUCKETPARAM_CWC - 1);
1409 PopulateBucketParameter(Parameter4, &MoCrashBucketParamsManager::GetAppTimeStamp, DW_MAX_BUCKETPARAM_CWC - 1);
1410 PopulateBucketParameter(Parameter5, &MoCrashBucketParamsManager::GetModuleName, DW_MAX_BUCKETPARAM_CWC - 1);
1411 PopulateBucketParameter(Parameter6, &MoCrashBucketParamsManager::GetModuleVersion, DW_MAX_BUCKETPARAM_CWC - 1);
1412 PopulateBucketParameter(Parameter7, &MoCrashBucketParamsManager::GetModuleTimeStamp, DW_MAX_BUCKETPARAM_CWC - 1);
1413 PopulateBucketParameter(Parameter8, &MoCrashBucketParamsManager::GetExceptionName, DW_MAX_BUCKETPARAM_CWC - 1);
1414 PopulateBucketParameter(Parameter9, &MoCrashBucketParamsManager::GetIlRva, DW_MAX_BUCKETPARAM_CWC - 1);
1417 #ifdef FEATURE_WINDOWSPHONE
1418 class WinPhoneBucketParamsManager : public BaseBucketParamsManager
1421 WinPhoneBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException);
1422 ~WinPhoneBucketParamsManager();
1424 virtual void PopulateBucketParameters();
1427 WinPhoneBucketParamsManager::WinPhoneBucketParamsManager(GenericModeBlock* pGenericModeBlock, TypeOfReportedError typeOfError, PCODE faultingPC, Thread* pFaultingThread, OBJECTREF* pThrownException)
1428 : BaseBucketParamsManager(pGenericModeBlock, typeOfError, faultingPC, pFaultingThread, pThrownException)
1439 WinPhoneBucketParamsManager::~WinPhoneBucketParamsManager()
1441 LIMITED_METHOD_CONTRACT;
1444 void WinPhoneBucketParamsManager::PopulateBucketParameters()
1454 PopulateEventName(g_WerEventTraits[WinPhoneCrash].EventName);
1456 // the "+ 1" is to explicitly indicate which fields need to specify space for NULL
1457 PopulateBucketParameter(Parameter1, &WinPhoneBucketParamsManager::GetAppName, DW_MAX_BUCKETPARAM_CWC - 1);
1458 PopulateBucketParameter(Parameter2, &WinPhoneBucketParamsManager::GetAppVersion, 23 + 1);
1459 PopulateBucketParameter(Parameter3, &WinPhoneBucketParamsManager::GetAppTimeStamp, 8 + 1);
1460 PopulateBucketParameter(Parameter4, &WinPhoneBucketParamsManager::GetModuleName, DW_MAX_BUCKETPARAM_CWC - 1);
1461 PopulateBucketParameter(Parameter5, &WinPhoneBucketParamsManager::GetModuleVersion, 23 + 1);
1462 PopulateBucketParameter(Parameter6, &WinPhoneBucketParamsManager::GetModuleTimeStamp, 8 + 1);
1463 PopulateBucketParameter(Parameter7, &WinPhoneBucketParamsManager::GetMethodDef, 6 + 1);
1464 PopulateBucketParameter(Parameter8, &WinPhoneBucketParamsManager::GetIlOffset, 8 + 1);
1465 PopulateBucketParameter(Parameter9, &WinPhoneBucketParamsManager::GetExceptionName, DW_MAX_BUCKETPARAM_CWC - 1);
1467 #endif // FEATURE_WINDOWSPHONE
1469 WatsonBucketType GetWatsonBucketType()
1480 #if defined(FEATURE_APPX) && !defined(FEATURE_CORECLR)
1481 if (AppX::IsAppXProcess() && !AppX::IsAppXNGen())
1484 #endif // FEATURE_APPX
1486 #ifdef FEATURE_WINDOWSPHONE
1487 return WinPhoneCrash;
1490 #endif // FEATURE_WINDOWSPHONE
1493 #endif // DACCESS_COMPILE
1495 #endif // DWBUCKETMANAGER_HPP