summaryrefslogtreecommitdiff
path: root/src/vm/dwreport.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/dwreport.cpp')
-rw-r--r--src/vm/dwreport.cpp2055
1 files changed, 1 insertions, 2054 deletions
diff --git a/src/vm/dwreport.cpp b/src/vm/dwreport.cpp
index 57d67e7c22..0890f3f334 100644
--- a/src/vm/dwreport.cpp
+++ b/src/vm/dwreport.cpp
@@ -27,75 +27,13 @@
#include "utilcode.h"
#include "../dlls/mscorrc/resource.h" // for resource ids
-#include "imagehlp.h"
-
EFaultRepRetVal DoReportFault(EXCEPTION_POINTERS * pExceptionInfo);
-// Should the CLR use Watson to report fatal errors and unhandled exceptions?
-static BOOL g_watsonErrorReportingEnabled = FALSE;
// Variables to control launching Watson only once, but making all threads wait for that single launch to finish.
LONG g_watsonAlreadyLaunched = 0; // Used to note that another thread has done Watson.
-HandleHolder g_hWatsonCompletionEvent = NULL; // Used to signal that Watson has finished.
-
-const WCHAR kErrorReportingPoliciesKey[] = W("SOFTWARE\\Policies\\Microsoft\\PCHealth\\ErrorReporting");
-const WCHAR kErrorReportingKey[] = W("SOFTWARE\\Microsoft\\PCHealth\\ErrorReporting");
-
-const WCHAR kShowUIValue[] = W("ShowUI");
-const WCHAR kForceQueueModeValue[] = W("ForceQueueMode");
-const WCHAR kDoReportValue[] = W("DoReport");
-const WCHAR kAllOrNoneValue[] = W("AllOrNone");
-const WCHAR kIncludeMSAppsValue[] = W("IncludeMicrosoftApps");
-const WCHAR kIncludeWindowsAppsValue[] = W("IncludeWindowsApps");
-const WCHAR kExclusionListKey[] = W("SOFTWARE\\Microsoft\\PCHealth\\ErrorReporting\\ExclusionList");
-const WCHAR kInclusionListKey[] = W("SOFTWARE\\Microsoft\\PCHealth\\ErrorReporting\\InclusionList");
-const WCHAR kExclusionListSubKey[] = W("\\ExclusionList");
-const WCHAR kInclusionListSubKey[] = W("\\InclusionList");
-
-
-// Default values for various registry keys
-const DWORD kDefaultShowUIValue = 1;
-const DWORD kDefaultForceQueueModeValue = 0;
-const DWORD kDefaultDoReportValue = 1;
-const DWORD kDefaultAllOrNoneValue = 1;
-const DWORD kDefaultExclusionValue = 0;
-const DWORD kDefaultInclusionValue = 0;
-const DWORD kDefaultIncludeMSAppsValue = 1;
-const DWORD kDefaultIncludeWindowsAppsValue = 1;
-
-// Default value for the default debugger and auto debugger attach settings.
-const BOOL kDefaultDebuggerIsWatson = FALSE;
-const BOOL kDefaultAutoValue = FALSE;
-
-// When debugging the watson process itself, the faulting process will spin
-// waiting for Watson to signal various events. If these waits time out, the
-// faulting process will go ahead and exit, which is sub-optimal if you need to
-// inspect the faulting process with the debugger at the same time. In debug
-// builds, use a longer wait time, since watson may be stopped under the
-// debugger for a while.
-
-#ifdef _DEBUG
-const DWORD kDwWaitTime = DW_TIMEOUT_VALUE * 1000;
-#else
-const DWORD kDwWaitTime = DW_TIMEOUT_VALUE;
-#endif
-
-#ifdef _TARGET_X86_
- const DWORD kWatsonRegKeyOptions = 0;
-#else
- const DWORD kWatsonRegKeyOptions = KEY_WOW64_32KEY;
-#endif
-
-const WCHAR kWatsonPath[] = WATSON_INSTALLED_REG_SUBPATH;
-#if defined(_TARGET_X86_)
-const WCHAR kWatsonValue[] = WATSON_INSTALLED_REG_VAL;
-#else
-const WCHAR kWatsonValue[] = WATSON_INSTALLED_REG_VAL_IA64;
-#endif
-const WCHAR* kWatsonImageNameOnLonghorn = W("\\dw20.exe");
-
typedef HMODULE (*AcquireLibraryHandleFn)(LPCWSTR);
template <AcquireLibraryHandleFn AcquireLibraryHandleFnPtr, bool RequiresFree>
@@ -143,43 +81,11 @@ typedef SimpleModuleHolder<CLRLoadLibrary, true> WerModuleHolder;
BOOL IsWatsonEnabled()
{
LIMITED_METHOD_CONTRACT;
- return g_watsonErrorReportingEnabled;
+ return TRUE;
}
//------------------------------------------------------------------------------
// Description
-// Initializes watson global critsec and event. Records whether run via
-// managed .exe.
-//
-// Parameters
-// fFlags -- the COINITIEE flags used to start the runtime.
-//
-// Returns
-// TRUE -- always
-//------------------------------------------------------------------------------
-BOOL InitializeWatson(COINITIEE fFlags)
-{
- LIMITED_METHOD_CONTRACT;
-
- // Watson is enabled for all SKUs
- g_watsonErrorReportingEnabled = TRUE;
-
- LOG((LF_EH, LL_INFO10, "InitializeWatson: %s\n", g_watsonErrorReportingEnabled ? "enabled" : "disabled"));
-
- if (!IsWatsonEnabled())
- {
- return TRUE;
- }
-
- // Create the event that all-but-the-first threads will wait on (the first thread
- // will set the event when Watson is done.)
- g_hWatsonCompletionEvent = WszCreateEvent(NULL, TRUE /*manual reset*/, FALSE /*initial state*/, NULL);
- return (g_hWatsonCompletionEvent != NULL);
-
-} // BOOL InitializeWatson()
-
-//------------------------------------------------------------------------------
-// Description
// Register out-of-process Watson callbacks provided in DAC dll for WIN7 or later
//
// Parameters
@@ -199,7 +105,6 @@ BOOL RegisterOutOfProcessWatsonCallbacks()
{
NOTHROW;
GC_NOTRIGGER;
- PRECONDITION(RunningOnWin7());
}
CONTRACTL_END;
@@ -272,85 +177,9 @@ BOOL RegisterOutOfProcessWatsonCallbacks()
}
//------------------------------------------------------------------------------
-// CreateWatsonSharedMemory
-//
// Description
-//
-// Creates a shared memory block for communication with Watson
-//
-// Parameters
-// hWatsonSharedMemory -- [out] The handle to the watson shared memory.
-// ppWatsonSharedMemory -- [out] A pointer to the Watson shared memory.
-// Returns
-// S_OK -- if the function complete normally.
-// FALSE -- otherwise
-// Exceptions
-// None
-//------------------------------------------------------------------------------
-HRESULT CreateWatsonSharedMemory(HANDLE* hWatsonSharedMemory,
- DWSharedMem** ppWatsonSharedMemory);
-
//------------------------------------------------------------------------------
// Description
-// Alerts the host that the thread is leaving the runtime, and sleeps
-// waiting for an object to be signalled
-//
-// Parameters
-// handle -- the handle to wait on
-// timeout -- the length of time to wait
-//
-// Returns
-// DWORD -- The return value from WaitForSingleObject
-//
-// Exceptions
-// None
-//
-// Notes
-// winwrap.h prevents us from using SetEvent by including
-// #define SetEvent Dont_Use_SetEvent
-// This is because using SetEvent within the runtime will result in poor
-// interaction with any sort of host process (e.g. SQL). We can use the
-// SetEvent/WaitForSingleObject primitives as long as we do some other work to
-// make sure the host understands.
-//------------------------------------------------------------------------------
-#undef SetEvent
-DWORD ClrWaitForSingleObject(HANDLE handle, DWORD timeout)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- SO_TOLERANT;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- CONTRACT_VIOLATION(ThrowsViolation);
-
- return WaitForSingleObject(handle, timeout);
-} // DWORD ClrWaitForSingleObject()
-
-//------------------------------------------------------------------------------
-// Helper class to set an event in destructor -- allows setting an event on the
-// way out of a function.
-//
-// Used to synchronize multiple threads with unhandled exceptions -- only the
-// first will run Watson, and all the rest will wait on the first one to be
-// done.
-//------------------------------------------------------------------------------
-class SettingEventHolder
-{
-public:
- SettingEventHolder(HANDLE &event) : m_event(event), m_bSetIt(FALSE) { LIMITED_METHOD_CONTRACT; }
- ~SettingEventHolder() { LIMITED_METHOD_CONTRACT; if (m_bSetIt && m_event) SetEvent(m_event); }
- void EnableSetting() { LIMITED_METHOD_CONTRACT; m_bSetIt = TRUE; }
- DWORD DoWait(DWORD timeout=INFINITE_TIMEOUT) { WRAPPER_NO_CONTRACT; return m_event ? ClrWaitForSingleObject(m_event, timeout) : 0; }
-
-private:
- HANDLE m_event; // The event to set
- BOOL m_bSetIt; // If true, set event in destructor.
-};
-
HRESULT DwGetFileVersionInfo(
__in_z LPCWSTR wszFilePath,
USHORT& major,
@@ -381,168 +210,6 @@ HRESULT DwGetFileVersionInfo(
return result;
}
-enum MicrosoftAppTypes
-{
- MicrosoftAppTypesNone = 0,
- MicrosoftAppTypesWindows = 0x1,
- MicrosoftAppTypesOther = 0x2
-};
-
-inline void SetMSFTApp(DWORD &AppType) { LIMITED_METHOD_CONTRACT; AppType |= MicrosoftAppTypesOther; }
-inline void SetMSFTWindowsApp(DWORD &AppType) { LIMITED_METHOD_CONTRACT; AppType |= MicrosoftAppTypesWindows; }
-
-inline BOOL IsMSFTApp(DWORD AppType) { LIMITED_METHOD_CONTRACT; return (AppType & MicrosoftAppTypesOther) ? TRUE : FALSE; }
-inline BOOL IsMSFTWindowsApp(DWORD AppType) { LIMITED_METHOD_CONTRACT; return (AppType & MicrosoftAppTypesWindows) ? TRUE : FALSE; }
-
-
-//------------------------------------------------------------------------------
-// Description
-// Determine if the application is a Microsoft application.
-//
-// Parameters
-// wszFilePath Path to a file to exctract the information from
-// pAppTypes [out] Put MicrosoftAppTypes here.
-//
-// Returns
-// S_OK If the function succeede
-// E_XXXX Failure result.
-//
-// Exceptions
-// None
-//------------------------------------------------------------------------------
-HRESULT DwCheckCompany( // S_OK or error.
- __in_z LPWSTR wszFilePath, // Path to the executable.
- DWORD* pAppTypes) // Non-microsoft, microsoft, microsoft windows.
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- //
- // Note that this code is equivalent to FusionGetFileVersionInfo, found in fusion\asmcache\asmcache.cpp
- //
-
- HRESULT hr = S_OK; // result of some operation
- DWORD dwHandle = 0;
- DWORD bufSize = 0; // Size of allocation for VersionInfo.
- DWORD ret;
-
- // Avoid confusion
- *pAppTypes = MicrosoftAppTypesNone;
-
- // Find the buffer size for the version info structure we need to create
- EX_TRY
- {
- bufSize = GetFileVersionInfoSizeW(wszFilePath, &dwHandle);
- if (!bufSize)
- {
- hr = HRESULT_FROM_GetLastErrorNA();
- }
- }
- EX_CATCH
- {
- hr = E_OUTOFMEMORY;
- }
- EX_END_CATCH(SwallowAllExceptions);
- if (!bufSize)
- {
- return hr;
- }
-
- // Allocate the buffer for the version info structure
- // _alloca() can't return NULL -- raises STATUS_STACK_OVERFLOW.
- BYTE* pVersionInfoBuffer = reinterpret_cast< BYTE* >(_alloca(bufSize));
-
- // Extract the version information blob. The version information
- // contains much more than the actual item of interest.
- {
- // If the previoud GetFileVersionInfoSizeW succeeds, version.dll has been loaded
- // in the process, and delay load of GetFileVersionInfoW will not throw.
- CONTRACT_VIOLATION(ThrowsViolation);
- ret = GetFileVersionInfoW(wszFilePath, dwHandle, bufSize, pVersionInfoBuffer);
-
- if (!ret)
- {
- return HRESULT_FROM_GetLastErrorNA();
- }
- }
-
- // Extract the actual CompanyName and compare it to "Microsoft" and
- // "MicrosoftWindows"
-
- // Get the language and codepage for the version info.
- UINT size = 0;
- struct
- {
- WORD language;
- WORD codePage;
- }* translation;
-
- {
- // If the previoud GetFileVersionInfoSizeW succeeds, version.dll has been loaded
- // in the process, and delay load of GetFileVersionInfoW will not throw.
- CONTRACT_VIOLATION(ThrowsViolation);
- ret = VerQueryValueW(pVersionInfoBuffer, W("\\VarFileInfo\\Translation"),
- reinterpret_cast< void **>(&translation), &size);
-
- if (!ret || size == 0)
- {
- return HRESULT_FROM_GetLastErrorNA();
- }
- }
-
- // Build the query key for the language-specific company name resource.
- WCHAR buf[64]; //----+----1----+----2----+----3----+----4
- _snwprintf_s(buf, NumItems(buf), _TRUNCATE, W("\\StringFileInfo\\%04x%04x\\CompanyName"),
- translation->language, translation->codePage);
-
- // Get the company name.
- WCHAR *name;
- {
- // If the previoud GetFileVersionInfoSizeW succeeds, version.dll has been loaded
- // in the process, and delay load of GetFileVersionInfoW will not throw.
- CONTRACT_VIOLATION(ThrowsViolation);
- ret = VerQueryValueW(pVersionInfoBuffer, buf,
- reinterpret_cast< void** >(&name), &size);
- }
-
- // If there is company name info, check it.
- if (ret != 0 && size != 0 && wcsstr(name, W("Microsoft")))
- {
- SetMSFTApp(*pAppTypes);
- }
-
-
- // Now build the query key for the language-specific product name resource.
- _snwprintf_s(buf, NumItems(buf), _TRUNCATE, W("\\StringFileInfo\\%04x%04x\\ProductName"),
- translation->language, translation->codePage);
-
- // Get the product name.
- {
- // If the previoud GetFileVersionInfoSizeW succeeds, version.dll has been loaded
- // in the process, and delay load of GetFileVersionInfoW will not throw.
- CONTRACT_VIOLATION(ThrowsViolation);
- ret = VerQueryValueW(pVersionInfoBuffer, buf,
- reinterpret_cast< void** >(&name), &size);
- }
-
- // If there is product name info, check it.
- if (ret != 0 && size != 0 && wcsstr(name, W("Microsoft\x0ae Windows\x0ae")))
- {
- SetMSFTWindowsApp(*pAppTypes);
- }
-
- return S_OK;
-
-} // HRESULT DwCheckCompany()
-
-
-//------------------------------------------------------------------------------
-// Description
// Read the description from the resource section.
//
// Parameters
@@ -797,914 +464,6 @@ int DwGetAssemblyVersion( // Number of characters written.
} // int DwGetAssemblyVersion()
-
-//------------------------------------------------------------------------------
-// CLRWatsonHelper class
-//
-// Certain registry keys affect the behavior of watson. In particulary, they
-// control
-// o whether or not a Watson report should result in UI popups
-// o which debugger should be used to JIT attach to the faulting process
-// o whether error reports should be sent at all.
-// This class is a holder for static functions that access these registry keys
-// to determine the proper settings.
-//
-//------------------------------------------------------------------------------
-class CLRWatsonHelper
-{
-public:
- enum WHDebugAction
- {
- WHDebug_InvalidValue,
- WHDebug_AutoLaunch,
- WHDebug_AskToLaunch,
- WHDebug_DontLaunch
- } m_debugAction;
-
- enum WHReportAction
- {
- WHReport_InvalidValue,
- WHReport_AutoQueue,
- WHReport_AskToSend,
- WHReport_DontSend
- } m_reportAction;
-
- enum WHDialogAction
- {
- WHDialog_InvalidValue,
- WHDialog_OkToPopup,
- WHDialog_DontPopup
- } m_dialogAction;
-
- CLRWatsonHelper()
- : m_debugAction(WHDebug_InvalidValue),
- m_reportAction(WHReport_InvalidValue),
- m_dialogAction(WHDialog_InvalidValue)
- { LIMITED_METHOD_CONTRACT; }
-
- void Init(BOOL bIsManagedFault, TypeOfReportedError tore);
-
- // Does the current interactive USER have sufficient permissions to
- // launch Watson or a debugger against this PROCESS?
- BOOL CurrentUserHasSufficientPermissions();
-
- // Should a debugger automatically, or should the user be queried for a debugger?
- BOOL ShouldDebug();
-
- // Should a managed debugger be launched, without even asking?
- BOOL ShouldAutoAttach();
-
- // Should Watson include a "Debug" button?
- BOOL ShouldOfferDebug();
-
- // Should a Watson report be generated?
- BOOL ShouldReport();
-
- // Should there be a popup? Possibly with only "quit"?
- BOOL ShouldShowUI();
-
- // If a Watson report is generated, should it be auto-queued?
- // (vs asking the user what to do about it)
- BOOL ShouldQueueReport();
-
-private:
- // Looks in HKCU/Software/Policies/Microsoft/PCHealth/ErrorReporting
- // then in HKLM/ " " " " "
- // then in HKCU/SOftware/Microsoft/PCHealth/ErrorReporting
- // then in HKLM/ " " " "
- static int GetPCHealthConfigLong( // Return value from registry or default.
- LPCWSTR szName, // Name of value to get.
- int iDefault); // Default value to return if not found.
-
- // Like above, but searches for a subkey with the given value.
- static BOOL GetPCHealthConfigSubKeyLong(// Return value from registry or default.
- LPCWSTR szSubKey, // Name of the subkey.
- LPCWSTR szName, // Name of value to get.
- int iDefault, // Default value to return if not found.
- DWORD *pValue); // Put value here.
-
- void AssertValid()
- {
- LIMITED_METHOD_CONTRACT;
- _ASSERTE(m_debugAction != WHDebug_InvalidValue);
- _ASSERTE(m_reportAction != WHReport_InvalidValue);
- _ASSERTE(m_dialogAction != WHDialog_InvalidValue);
- }
-
-}; // class CLRWatsonHelper
-
-//------------------------------------------------------------------------------
-// Description
-// Initialization for watson helper class.
-//
-// Parameters
-// bIsManagedFault - true if EXCEPTION_COMPLUS or fault from jitted code.
-// - false otherwise
-//
-//
-// Notes:
-// - Launches and Pops always happen to the same session in which the
-// process is running.
-// - This function computes what actions should happen, but doesn't do any.
-//
-// This routine returns which actions should be taken given the current registry
-// settings and environment. It implements the following matrix:
-//
-// <<-- AutoLaunch -->>
-// TRUE FALSE
-// Interactive process A3 B2
-// Non-interactive process A3 C1
-//
-// Action codes:
-// A - Auto attach debugger
-// B - Ask to attach debugger
-// C - Don't attach debugger
-//
-// 1 - Auto Queue Watson report
-// 2 - Ask to Send Watson report
-// 3 - Don't send Watson report
-//
-//
-// CLRWatsonHelper::Init
-//------------------------------------------------------------------------------
-void CLRWatsonHelper::Init(
- BOOL bIsManagedFault, // Is the fault in question from managed code?
- TypeOfReportedError tore) // What sort of error is this?
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // Initialize returned values
- WHDebugAction tmpDebugAction = WHDebug_InvalidValue;
- WHReportAction tmpReportAction = WHReport_InvalidValue;
- WHDialogAction tmpDialogAction = WHDialog_InvalidValue;
-
- // First run the matrix, then later provide the over-rides
- BOOL fRunningInteractive = RunningInteractive();
-
- if (fRunningInteractive)
- {
- // Interactive services and interactive apps running as LocalSystem are considered non-interactive
- // so that we don't display any UI for them. Note that we should check the process token (and not the
- // thread token if the thread is impersonating a user) to determine if the app is running as LocalSystem.
- // This is because Watson displays UI for us and Watson is run by calling CreateProcess. CreateProcess
- // always creates child processes using the process token.
-
- BOOL fLocalSystemOrService;
- if (RunningAsLocalSystemOrService(fLocalSystemOrService) != ERROR_SUCCESS)
- {
- // Err on the side of caution; treat the app as non-interactive
- fRunningInteractive = FALSE;
- }
- else if (fLocalSystemOrService)
- {
- fRunningInteractive = FALSE;
- }
- }
-
- BOOL bAutoLaunch = FALSE;
- SString ssDummy;
-
- GetDebuggerSettingInfo(ssDummy, &bAutoLaunch);
-
- if (bAutoLaunch)
- {
- tmpDebugAction = WHDebug_AutoLaunch;
- tmpReportAction = WHReport_DontSend;
- tmpDialogAction = WHDialog_DontPopup;
- }
- else
- {
- if (fRunningInteractive)
- {
- tmpDebugAction = WHDebug_AskToLaunch;
- tmpReportAction = WHReport_AskToSend;
- tmpDialogAction = WHDialog_OkToPopup;
- }
- else
- {
- // Non-interactive process
- tmpDebugAction = WHDebug_DontLaunch;
- tmpReportAction = WHReport_AutoQueue;
- tmpDialogAction = WHDialog_DontPopup;
- }
- }
-
- // If this is a breakpoint, never send a report.
- if (tore.IsBreakpoint())
- tmpReportAction = WHReport_DontSend;
-
- // Store off the results.
- m_debugAction = tmpDebugAction;
- m_reportAction = tmpReportAction;
- m_dialogAction = tmpDialogAction;
-
- // Done. Log some stuff in debug mode.
- #if defined(_DEBUG)
- {
- char *(rda[]) = {"InvalidValue", "AutoDebug", "AskToDebug", "DontDebug"};
- char *(rwa[]) = {"InvalidValue", "AutoQueue", "AskToSend", "DontSend"};
- char *(rdlga[]) = {"InvalidValue", "OkToPopup", "DontPopup"};
- LOG((LF_EH, LL_INFO100, "CLR Watson: debug action: %s\n", rda[m_debugAction]));
- LOG((LF_EH, LL_INFO100, "CLR Watson: report action: %s\n", rwa[m_reportAction]));
- LOG((LF_EH, LL_INFO100, "CLR Watson: dialog action: %s\n", rdlga[m_dialogAction]));
- #define LB(expr) LOG((LF_EH, LL_INFO100, "CLR Watson: " #expr ": %s\n", ((expr) ? "true" : "false") ))
- LB(CurrentUserHasSufficientPermissions());
- LB(ShouldDebug());
- LB(ShouldAutoAttach());
- LB(ShouldOfferDebug());
- LB(ShouldReport());
- LB(ShouldQueueReport());
- #undef LB
- }
- #endif
-
-} // void CLRWatsonHelper::Init()
-
-
-//------------------------------------------------------------------------------
-// CurrentUserHasSufficientPermissions
-//
-// Determines if the user logged in has the correct permissions to launch Watson.
-//
-// Parameters:
-// None.
-//
-// Returns:
-// TRUE if the user has sufficient permissions, else FALSE
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::CurrentUserHasSufficientPermissions()
-{
- // TODO! Implement!
- return TRUE;
-} // BOOL CLRWatsonHelper::CurrentUserHasSufficientPermissions()
-
-
-
-//------------------------------------------------------------------------------
-// Description
-// Determines whether we will show Watson at all.
-//
-// Parameters
-// none
-//
-// Returns
-// TRUE -- If Watson should show UI.
-// FALSE -- Otherwise
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldShowUI()
-{
- WRAPPER_NO_CONTRACT;
-
- AssertValid();
-
- return (m_dialogAction == WHDialog_OkToPopup);
-} // BOOL CLRWatsonHelper::ShouldShowUI()
-
-//------------------------------------------------------------------------------
-// Description
-// Determines whether a debugger will (or may be) launched. True if there
-// is an auto-launch debugger, or if we will ask the user.
-//
-// Parameters
-// none
-//
-// Returns
-// TRUE -- If a debugger might be attached.
-// FALSE -- Otherwise
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldDebug()
-{
- LIMITED_METHOD_CONTRACT;
-
- return ShouldOfferDebug() || ShouldAutoAttach();
-} // BOOL CLRWatsonHelper::ShouldDebug()
-
-//------------------------------------------------------------------------------
-// Description
-// Determines whether or not the Debug button should be present in the
-// Watson dialog
-//
-// Parameters
-// none
-//
-// Returns
-// TRUE -- if the Debug button should be displayed
-// FALSE -- otherwise
-//
-// Notes
-// This means "is there an appropriate debugger registered for auto attach?"
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldOfferDebug()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- AssertValid();
-
- // Permission check.
- if (!CurrentUserHasSufficientPermissions())
- {
- return FALSE;
- }
-
- // Check based on DbgJitDebugLaunchSetting & interactivity.
- if (m_debugAction != WHDebug_AskToLaunch)
- {
- // Don't ask the user about debugging. Do or don't debug; but don't ask.
- return FALSE;
- }
-
- SString ssDebuggerString;
- GetDebuggerSettingInfo(ssDebuggerString, NULL);
-
- // If there is no debugger installed, don't offer to debug, since we can't.
- if (ssDebuggerString.IsEmpty())
- {
- return FALSE;
- }
-
- return TRUE;
-
-} // BOOL CLRWatsonHelper::ShouldOfferDebug()
-
-//------------------------------------------------------------------------------
-//
-// ShouldAutoAttach
-//
-// Description
-// Determines whether or not a debugger should be launched
-// automatically, without prompting the user.
-//
-// Parameters
-// None.
-//
-// Returns
-// TRUE -- If a debugger should be auto-attached.
-// FALSE -- Otherwise
-//
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldAutoAttach()
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- AssertValid();
-
- // Permissions check.
- if (!CurrentUserHasSufficientPermissions())
- {
- return FALSE;
- }
-
- return (m_debugAction == WHDebug_AutoLaunch);
-} // BOOL CLRWatsonHelper::ShouldAutoAttach()
-
-
-//------------------------------------------------------------------------------
-// Description
-// Returns whether a Watson report should be generated.
-//
-// Parameters
-// none
-//
-// Returns
-// TRUE - a Watson report should be generated (with a minidump).
-// FALSE - don't generate a report.
-//
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldReport()
-{
- WRAPPER_NO_CONTRACT;
-
- AssertValid();
-
- // If we queue or ask, we should generate.
- return (m_reportAction == WHReport_AutoQueue) || (m_reportAction == WHReport_AskToSend);
-
-} // BOOL CLRWatsonHelper::ShouldReport()
-
-
-//------------------------------------------------------------------------------
-// Description
-// If a Watson report is generated, returns whether it should be auto-queued.
-// (vs asking the user what to do about it)
-//
-// Parameters
-// none
-//
-// Returns
-// TRUE - any Watson report should be be queued.
-// FALSE - any Watson report is posed to the user for "send" or "don't send".
-//
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::ShouldQueueReport()
-{
- WRAPPER_NO_CONTRACT;
-
- AssertValid();
-
- // If we queue a report.
- return (m_reportAction == WHReport_AutoQueue);
-
-} // BOOL CLRWatsonHelper::ShouldQueueReport()
-
-//------------------------------------------------------------------------------
-// Description
-// Reads a PCHealth configuration LONG value from the registry.
-//
-// Parameters
-// szName -- name of the value
-// iDefault -- default value, if not found
-//
-// Returns
-// The value read, or default if no value found.
-//
-// Exceptions
-// None
-//
-// NOtes:
-// Looks in HKCU/Software/Policies/Microsoft/PCHealth/ErrorReporting
-// then in HKLM/ " " " " "
-// then in HKCU/SOftware/Microsoft/PCHealth/ErrorReporting
-// then in HKLM/ " " " "
-//------------------------------------------------------------------------------
-int CLRWatsonHelper::GetPCHealthConfigLong( // Return value from registry or default.
- LPCTSTR szName, // Name of value to get.
- int iDefault) // Default value to return if not found.
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- long iValue; // Actual value from registry.
-
- // Try HKCR policy key
- if (GetRegistryLongValue(HKEY_CURRENT_USER, kErrorReportingPoliciesKey, szName, &iValue, FALSE))
- return iValue;
-
- // Try HKLM policy key
- if (GetRegistryLongValue(HKEY_LOCAL_MACHINE, kErrorReportingPoliciesKey, szName, &iValue, FALSE))
- return iValue;
-
- // Try HKCR key
- if (GetRegistryLongValue(HKEY_CURRENT_USER, kErrorReportingKey, szName, &iValue, FALSE))
- return iValue;
-
- // Try HKLM key
- if (GetRegistryLongValue(HKEY_LOCAL_MACHINE, kErrorReportingKey, szName, &iValue, FALSE))
- return iValue;
-
- // None of them had value -- return default.
- return iDefault;
-} // long CLRWatsonHelper::GetPCHealthConfigLong()
-
-//------------------------------------------------------------------------------
-// Description
-// Reads a PCHealth configuration LONG value from the registry, from a
-// given subkey.
-//
-// Parameters
-// szSubKey -- name of the subkey.
-// szName -- name of the value
-// iDefault -- default value, if not found
-// pValue -- put value here.
-//
-// Returns
-// TRUE - a value was found in the registry
-// FALSE - no value found.
-//
-// Exceptions
-// None
-//
-// NOtes:
-// Looks in HKCU/Software/Policies/Microsoft/PCHealth/ErrorReporting
-// then in HKLM/ " " " " "
-// then in HKCU/SOftware/Microsoft/PCHealth/ErrorReporting
-// then in HKLM/ " " " "
-//------------------------------------------------------------------------------
-BOOL CLRWatsonHelper::GetPCHealthConfigSubKeyLong( // Return value from registry or default.
- LPCWSTR szSubKey, // Name of the subkey.
- LPCWSTR szName, // Name of value to get.
- int iDefault, // Default value to return if not found.
- DWORD *pValue) // Put the value (registry or default) here.
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- long iValue; // Actual value from registry.
-
- // Only one thread will *ever* enter this function, so it is safe to use a static
- // buffer. We know the the longest strings we will want to catenate. Size
- // the buffer appropriately, and we're set.
- static WCHAR rcBuf[lengthof(kErrorReportingPoliciesKey) + lengthof(kInclusionListSubKey) + 3];
-
- _ASSERT( (wcslen(kErrorReportingPoliciesKey) + wcslen(szSubKey) + 1) < lengthof(rcBuf));
-
- // Try HKCR policy key
- wcscpy_s(rcBuf, COUNTOF(rcBuf), kErrorReportingPoliciesKey);
- wcsncat_s(rcBuf, COUNTOF(rcBuf), szSubKey, lengthof(rcBuf)-wcslen(rcBuf)-1);
-
- if (GetRegistryLongValue(HKEY_CURRENT_USER, rcBuf, szName, &iValue, FALSE))
- {
- *pValue = iValue;
- return TRUE;
- }
-
- // Try the HKLM policy key
- if (GetRegistryLongValue(HKEY_LOCAL_MACHINE, rcBuf, szName, &iValue, FALSE))
- {
- *pValue = iValue;
- return TRUE;
- }
-
- // Try HKCR key
- wcscpy_s(rcBuf, COUNTOF(rcBuf), kErrorReportingKey);
- wcsncat_s(rcBuf, COUNTOF(rcBuf), szSubKey, lengthof(rcBuf)-wcslen(rcBuf)-1);
-
- if (GetRegistryLongValue(HKEY_CURRENT_USER, rcBuf, szName, &iValue, FALSE))
- {
- *pValue = iValue;
- return TRUE;
- }
-
- // Try HKLM key
- if (GetRegistryLongValue(HKEY_LOCAL_MACHINE, rcBuf, szName, &iValue, FALSE))
- {
- *pValue = iValue;
- return TRUE;
- }
-
- // None of them had value -- return default.
- *pValue = iDefault;
- return FALSE;
-} // long CLRWatsonHelper::GetPCHealthConfigLong()
-
-
-//------------------------------------------------------------------------------
-//------------------------------------------------------------------------------
-HRESULT CreateWatsonSharedMemory(
- HANDLE *hWatsonSharedMemory,
- DWSharedMem **ppWatsonSharedMemory)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- // Watson needs to inherit the shared memory block, so we have to set up
- // security attributes to make that happens.
- SECURITY_ATTRIBUTES securityAttributes;
- memset(&securityAttributes, 0, sizeof(securityAttributes));
- securityAttributes.nLength = sizeof(securityAttributes);
- securityAttributes.bInheritHandle = TRUE;
-
- _ASSERTE(NULL != hWatsonSharedMemory);
- _ASSERTE(NULL != ppWatsonSharedMemory);
-
- *hWatsonSharedMemory = NULL;
- *ppWatsonSharedMemory = NULL;
-
- // In cases where we have to return form this function with a failure, we
- // need to clean up the handle. Use a holder to take care of that for us.
- HandleHolder hTemp =
- WszCreateFileMapping(INVALID_HANDLE_VALUE,
- &securityAttributes,
- PAGE_READWRITE,
- 0,
- sizeof(DWSharedMem),
- NULL);
-
- if (hTemp == NULL)
- {
- return HRESULT_FROM_GetLastErrorNA();
- }
-
- DWSharedMem* pTemp =
- static_cast< DWSharedMem* >(CLRMapViewOfFile(hTemp,
- FILE_MAP_ALL_ACCESS,
- 0,
- 0,
- sizeof(DWSharedMem)));
-
- if (NULL == pTemp)
- {
- return HRESULT_FROM_GetLastErrorNA();
- }
-
- memset(pTemp, 0, sizeof(DWSharedMem));
- *hWatsonSharedMemory = hTemp;
- *ppWatsonSharedMemory = pTemp;
-
- // We're ready to exit normally and pass the IPC block's handle back to our
- // caller, so we don't want to close it.
- hTemp.SuppressRelease();
-
- return S_OK;
-} // HRESULT CreateWatsonSharedMemory()
-
-
-
-const WCHAR* kWatsonImageNameOnVista = W("\\dw20.exe");
-
-//------------------------------------------------------------------------------
-// Description
-// A helper function to launch the Watson process and wait for it to
-// complete
-// Parameters
-// hWatsonSharedMemory
-// Handle to the shared memory block to pass to Watson. This handle
-// must be inheritable.
-// hEventAlive
-// hEventDone
-// hMutex
-// Returns
-// true - If watson executed normally
-// false - if watson was unable to launch, reported an error, or
-// appeared to hang/crash
-//------------------------------------------------------------------------------
-BOOL RunWatson(
- HANDLE hWatsonSharedMemory,
- HANDLE hEventAlive,
- HANDLE hEventDone,
- HANDLE hMutex)
-{
- CONTRACTL
- {
- NOTHROW;
- GC_NOTRIGGER;
- MODE_ANY;
- PRECONDITION(!RunningOnWin7());
- }
- CONTRACTL_END;
-
- // Since we're doing our own error reporting, we don't want to pop up the
- // OS Watson dialog/GPF Dialog. Supress it now.
-
- PROCESS_INFORMATION processInformation;
- STARTUPINFOW startupInfo;
- memset(&startupInfo, 0, sizeof(STARTUPINFOW));
- startupInfo.cb = sizeof(STARTUPINFOW);
-
- HRESULT hr = S_OK;
- PathString watsonAppName;
- PathString watsonCommandLine;
- EX_TRY
- {
- do
- {
-
-
-
-
- {
- HKEYHolder hKey;
- // Look for key \\HKLM\Software\Microsoft\PCHealth\ErrorReporting\DW\Installed"
- DWORD ret = WszRegOpenKeyEx(HKEY_LOCAL_MACHINE,
- kWatsonPath,
- 0,
- KEY_READ | kWatsonRegKeyOptions,
- &hKey);
-
- if (ERROR_SUCCESS != ret)
- {
- hr = E_FAIL;
- break;
- }
-
-
- // Look in ...\DW\Installed for dw0200 (dw0201 on ia64). This will be
- // the full path to the executable.
-
- ClrRegReadString(hKey, kWatsonValue, watsonAppName);
-
-
- COUNT_T len = watsonCommandLine.GetCount();
- WCHAR* buffer = watsonCommandLine.OpenUnicodeBuffer(len);
- _snwprintf_s(buffer,
- len,
- _TRUNCATE,
- W("dw20.exe -x -s %lu"),
- PtrToUlong(hWatsonSharedMemory));
- watsonCommandLine.CloseBuffer();
-
- }
- } while (false);
- }
- EX_CATCH_HRESULT(hr);
-
-
- if (hr != S_OK)
- {
- return false;
- }
-
- {
- BOOL ret = WszCreateProcess(watsonAppName,
- watsonCommandLine,
- NULL,
- NULL,
- TRUE,
- NULL,
- NULL,
- NULL,
- &startupInfo,
- &processInformation);
-
- if (FALSE == ret)
- {
- //
- // Watson failed to start up.
- //
- // This can happen if e.g. Watson wasn't installed on the machine.
- //
- return FALSE;
- }
- }
-
-
-
- // Wait for watson to finish.
- //
- // This code was more-or-less pasted directly out of the test app for
- // watson, found at
- //
- // \\redist\redist\Watson\dw20_latest\neutral\retail\0\testcrash.cpp
-
- // These handles need to live until we're done waiting for the watson
- // process to finish execution.
- HandleHolder hProcess(processInformation.hProcess);
- HandleHolder hThread(processInformation.hThread);
-
-
- BOOL watsonSignalledCompletion = FALSE, bDWRunning = TRUE;
-
- while (bDWRunning)
- {
- if (WAIT_OBJECT_0 == ClrWaitForSingleObject(hEventAlive,
- kDwWaitTime))
- {
- // Okay, Watson's still pinging us; see if it's finished.
- if (WAIT_OBJECT_0 == ClrWaitForSingleObject(hEventDone, 1))
- {
- bDWRunning = FALSE;
- watsonSignalledCompletion = TRUE;
- }
-
- // If watson is finished (i.e. has signaled hEventDone),
- // bDWRunning is false and we'll fall out of the loop. If
- // watson isn't finished, we'll go back to waiting for the
- // next ping on hEventAlive
- continue;
- }
-
- // we timed-out waiting for DW to respond.
- DWORD dw = WaitForSingleObject(hMutex, DW_TIMEOUT_VALUE);
-
- if (WAIT_TIMEOUT == dw)
- {
- // either DW's hung or crashed, we must carry on. Let watson
- // no that we're giving up on watson, in case it comes back
- // from the hang.
- SetEvent(hEventDone);
- bDWRunning = FALSE;
- }
- else if (WAIT_ABANDONED == dw)
- {
- // The mutex was abandoned, which means Watson crashed on
- // us.
- bDWRunning = FALSE;
-
- ReleaseMutex(hMutex);
- }
- else
- {
- // Check one last time to see if Watson has woken up.
- if (WAIT_OBJECT_0 != ClrWaitForSingleObject(hEventAlive, 1))
- {
- // Nope. hasn't woken up. Give up on Watson
- SetEvent(hEventDone);
- bDWRunning = FALSE;
- }
- else
- {
- // Oh, it HAS woken up! See if it's finished as well.
- if (WAIT_OBJECT_0 == ClrWaitForSingleObject(hEventDone, 1))
- {
- bDWRunning = FALSE;
- watsonSignalledCompletion = TRUE;
- }
- }
-
- ReleaseMutex(hMutex);
- }
- }
-
- // Go ahead and bail if Watson didn't exit for some reason.
- if (!watsonSignalledCompletion)
- {
- return FALSE;
- }
-
- // We're now done with hProcess and hThread, it's safe to let the
- // HandleHolders destroy them now.
- //
- // We don't need to wait for the Watson process to exit; once it's signalled
- // "hEventDone" it's safe to assume that Watson will not try communicating
- // with us anymore and we have succeeded.
- return true;
-} // BOOL RunWatson()
-
-
-//
-// Constants used to control various aspects of Watson's behavior.
-//
-
-
-// Flags controlling the minidump Watson creates.
-const DWORD kMiniDumpType = MiniDumpNormal;
-const DWORD kThreadWriteFlags = ThreadWriteThread | ThreadWriteContext | ThreadWriteStack;
-const DWORD kModuleWriteFlags = ModuleWriteModule; // | ModuleWriteDataSeg ?
-
-
-
-// Reporting. The defaults are fine here
-const DWORD kReportingFlags = 0;
-
-//
-// Enable these flags if the report should be queued (i.e., if no UI should be
-// shown, but a report should still be sent).
-//
-
-// Enable these flags are for bfDWRFlags
-const DWORD kQueuingReportingFlags = fDwrForceToAdminQueue | fDwrIgnoreHKCU;
-
-// Enable these flags in the bfDWUFlags field
-const DWORD kQueuingUIFlags = fDwuNoEventUI;
-
-//
-// No reporting flags. Enable these flags if an error report should not be sent.
-//
-
-// Enable these flags in bfDWRFlags if a report is not to be sent.
-const DWORD kNoReportFlags = fDwrNeverUpload;
-
-
-// UI Flags
-//
-// We need to use the light plea, since we may be reporting faults for
-// Non-Microsoft software (if some random 3rd party app throws an exception, we
-// can't really promise that their error report will be used to fix the
-// problem).
-//
-const DWORD kUIFlags = fDwuDenySuspend | fDwuShowFeedbackLink;
-
-// Exception mode flags. By default, the "restart" and "recover" buttons are
-// checked. We need to turn that behavior off. We also need to use the
-// minidump API to gather the heap dump, in order to get a managed-aware
-// minidump. Finally, release the dumping thread before doing the cabbing
-// for performance reasons.
-const DWORD kExceptionModeFlags = fDweDefaultQuit | fDweGatherHeapAsMdmp | fDweReleaseBeforeCabbing;
-
-// "Miscellaneous" flags. These flags are only used by Office.
-const DWORD kMiscFlags = 0;
-
-// Flags to control which buttons are available on the Watson dialog.
-//
-// We will only display the "Send Error Report" and "Don't Send" buttons
-// available -- we're not going to make the "restart" or "recover" checkboxes
-// available by default.
-const DWORD kOfferFlags = msoctdsQuit;
-
-//------------------------------------------------------------------------------
-// Description
// Returns the IP of the instruction that caused the exception to occur.
// For managed exceptions this may not match the Exceptions contained in
// the exception record.
@@ -2160,477 +919,6 @@ HRESULT GetBucketParametersForCurrentException(
} // HRESULT GetBucketParametersForCurrentException()
-//------------------------------------------------------------------------------
-// Description
-//
-// Parameters
-// pExceptionInfo -- information about the exception that caused the error.
-// If the error is not the result of an exception, pass NULL for this
-// parameter
-// tore -- Information about the fault
-// pThread -- Thread object for faulting thread, could be NULL
-// dwThreadID -- OS Thread ID for faulting thread
-//
-// Returns
-// FaultReportResult -- enumeration indicating the
-// FaultReportResultAbort -- if Watson could not execute normally
-// FaultReportResultDebug -- if Watson executed normally, and the user
-// chose to debug the process
-// FaultReportResultQuit -- if Watson executed normally, and the user
-// chose to end the process (e.g. pressed "Send Error Report" or
-// "Don't Send").
-//
-// Exceptions
-// None.
-//------------------------------------------------------------------------------
-#ifdef _PREFAST_
-#pragma warning(push)
-#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
-#endif
-FaultReportResult DoFaultReportWorker( // Was Watson attempted, successful? Run debugger?
- EXCEPTION_POINTERS *pExceptionInfo, // Information about the fault.
- TypeOfReportedError tore, // What sort of error is this?
- Thread *pThread, // Thread object for faulting thread, could be NULL
- DWORD dwThreadID) // OS Thread ID for faulting thread
-{
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(!RunningOnWin7());
-
- LOG((LF_EH, LL_INFO100, "DoFaultReportWorker: at sp %p ...\n", GetCurrentSP()));
-
- if (!IsWatsonEnabled())
- {
- return FaultReportResultQuit;
- }
-
- // If we've already tried to report a Watson crash once, we don't really
- // want to pester the user about this exception. This can occur in certain
- // pathological programs.
- // For events other than user breakpoint, we only want to report once.
- // For user breakpoints, report whenever the thread wants to.
- if (!tore.IsUserBreakpoint())
- {
- // If Watson already launched (say, on another thread)...
- if (FastInterlockCompareExchange(&g_watsonAlreadyLaunched, 1, 0) != 0)
- {
- // wait until Watson process is completed
- ClrWaitForSingleObject(g_hWatsonCompletionEvent, INFINITE_TIMEOUT);
- return FaultReportResultQuit;
- }
- }
-
- // Assume an unmanaged fault until we determine otherwise.
- BOOL bIsManagedFault = FALSE;
-
- // IF we don't have an ExceptionInfo, what does that mean?
- if (pExceptionInfo)
- {
- if (IsExceptionFromManagedCode(pExceptionInfo->ExceptionRecord))
- {
- // This is a managed fault.
- bIsManagedFault = TRUE;
- }
- }
-
- // Figure out what we should do.
- CLRWatsonHelper policy;
- policy.Init(bIsManagedFault, tore);
-
- if (policy.ShouldAutoAttach())
- {
- return FaultReportResultDebug;
- }
-
- // Is there anything for Watson to do? (Either report, or ask about debugging?)
- if ((!policy.ShouldReport()) && (!policy.ShouldOfferDebug()) && (!policy.ShouldShowUI()))
- {
- // Hmm ... we're not supposed to report anything or pop up a dialog. In
- // this case, we can stop right now.
- return FaultReportResultQuit;
- }
-
- HANDLE hWatsonSharedMemory;
- DWSharedMem *pWatsonSharedMemory;
- {
- HRESULT hr = CreateWatsonSharedMemory(&hWatsonSharedMemory,
- &pWatsonSharedMemory);
- if (FAILED(hr))
- {
- return FaultReportResultAbort;
- }
- }
-
- // Some basic bookkeeping for Watson
- pWatsonSharedMemory->dwSize = sizeof(DWSharedMem);
- pWatsonSharedMemory->dwVersion = DW_CURRENT_VERSION;
- pWatsonSharedMemory->pid = GetCurrentProcessId();
- pWatsonSharedMemory->tid = dwThreadID;
- _snwprintf_s(pWatsonSharedMemory->wzEventLogSource,
- NumItems(pWatsonSharedMemory->wzEventLogSource),
- _TRUNCATE,
- W(".NET Runtime %0d.%0d Error Reporting"),
- VER_MAJORVERSION,
- VER_MINORVERSION);
- pWatsonSharedMemory->eip = (pExceptionInfo) ? reinterpret_cast< DWORD_PTR >(pExceptionInfo->ExceptionRecord->ExceptionAddress) : NULL;
-
- // If we set exception pointers, the debugger will automatically do a .ecxr on them. SO,
- // don't set the pointers unless it really is an exception and we have a
- // a good context record
- if (tore.IsException() ||
- (tore.IsFatalError() && pExceptionInfo && pExceptionInfo->ContextRecord &&
- (pExceptionInfo->ContextRecord->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
- )
- {
- pWatsonSharedMemory->pep = pExceptionInfo;
- }
- else
- {
- pWatsonSharedMemory->pep = NULL;
- }
-
- // Handles to kernel objects that Watson uses.
- //
- // We're expecting these handles to be valid until the Watson child process
- // has run to completion. Make sure these holders stay in scope until after
- // the call to RunWatson
-
- HandleHolder hEventDone(NULL),
- hEventNotifyDone(NULL),
- hEventAlive(NULL),
- hMutex(NULL),
- hProc(NULL),
- sharedMemoryHolder(hWatsonSharedMemory);
- {
- // SECURITY_ATTRIBUTES so the handles can be inherited (by Watson).
- SECURITY_ATTRIBUTES securityAttributes =
- { sizeof(SECURITY_ATTRIBUTES), NULL, true };
-
- hEventDone = WszCreateEvent(&securityAttributes, FALSE, FALSE, NULL);
- if (hEventDone == NULL)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: WszCreateEvent returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
- pWatsonSharedMemory->hEventDone = hEventDone;
-
-
- hEventNotifyDone = WszCreateEvent(&securityAttributes, FALSE, FALSE, NULL);
- if (hEventNotifyDone == NULL)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: WszCreateEvent returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
- pWatsonSharedMemory->hEventNotifyDone = hEventNotifyDone;
-
-
- hEventAlive = WszCreateEvent(&securityAttributes, FALSE, FALSE, NULL);
- if (hEventAlive == NULL)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: WszCreateEvent returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
- pWatsonSharedMemory->hEventAlive = hEventAlive;
-
-
- hMutex = WszCreateMutex(&securityAttributes, FALSE, NULL);
- if (hMutex == NULL)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: WszCreateEvent returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
- pWatsonSharedMemory->hMutex = hMutex;
- }
-
- // During error reporting we need to do dump collection, freeze threads inside the process, read memory blocks
- // (if you register memory), read stuff from the PEB, create remote threads for recovery. So it needs quite a
- // lot of permissions; we end up with PROCESS_ALL_ACCESS to satisfy all required permissions.
- hProc = OpenProcess(PROCESS_ALL_ACCESS,
- TRUE,
- pWatsonSharedMemory->pid);
- if (hProc == NULL)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: OpenProcess returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
-
- pWatsonSharedMemory->hProc = hProc;
-
-
- // Flags to control reporting, queuing, etc.
- DWORD reportingFlags = kReportingFlags; // 0
- DWORD uiFlags = kUIFlags; // fDwuDenySuspend | fDwuShowFeedbackLink
- DWORD dwEflags = kExceptionModeFlags; // fDweDefaultQuit | fDweGatherHeapAsMdmp
-
- // Reporting flags...
- if (policy.ShouldQueueReport())
- { // If we should queue a report,
- // turn on kQueueingReportingFlags, which is fDwrForceToAdminQueue | fDwrIgnoreHKCU
- reportingFlags |= kQueuingReportingFlags;
- }
- else
- if (!policy.ShouldReport())
- { // We shouldn't report at all,
- // turn on kNoReportFlags, which is fDwrNeverUpload, which means "don't report"
- reportingFlags |= kNoReportFlags;
- }
- else
- {
- // Ask to report.
- }
-
- // Offer flags...
- DWORD offerFlags = kOfferFlags; // msoctdsQuit
- if (policy.ShouldOfferDebug())
- { // Turn on msoctdsDebug, which adds "Debug" button.
- offerFlags |= msoctdsDebug;
- }
- else
- { // No debug, so ignore aeDebug
- dwEflags |= fDweIgnoreAeDebug;
- }
-
- // UI flags...
- if (policy.ShouldQueueReport() && !policy.ShouldOfferDebug())
- { // Queue report headlessly. Turn on kQueueingUIFlags, which is fDwuNoEventUI.
- uiFlags |= kQueuingUIFlags;
- }
-
- pWatsonSharedMemory->bfmsoctdsOffer = offerFlags; // From above
- pWatsonSharedMemory->bfDWRFlags = reportingFlags; // From above
- pWatsonSharedMemory->bfDWUFlags = uiFlags; // From above
- pWatsonSharedMemory->bfDWEFlags = dwEflags; // From above
- pWatsonSharedMemory->bfDWMFlags = kMiscFlags; // 0
-
- // We're going to rely on Watson's default localization behavior.
- pWatsonSharedMemory->lcidUI = 0;
-
- // By default, Watson will terminate the process after snapping a
- // minidump. Notify & LetRun flags disable that.
- pWatsonSharedMemory->bfmsoctdsNotify = msoctdsNull;
- pWatsonSharedMemory->bfmsoctdsLetRun = offerFlags;
-
- {
- PathString wzModuleFileName;
- DWORD dwRet = WszGetModuleFileName(NULL,
- wzModuleFileName);
- BaseBucketParamsManager::CopyStringToBucket(pWatsonSharedMemory->wzModuleFileName, NumItems(pWatsonSharedMemory->wzModuleFileName), wzModuleFileName);
-
- _ASSERTE(0 != dwRet);
- if (0 == dwRet)
- {
- LOG((LF_EH, LL_INFO100, "CLR Watson: WszGetModuleFileName returned error, GetLastError(): %#x\n", GetLastError()));
- return FaultReportResultAbort;
- }
- }
-
- // We're going capture the same minidump information for all modules, so set wzDotDataDlls to "*"
- if (sizeof(DW_ALLMODULES) <= sizeof(pWatsonSharedMemory->wzDotDataDlls))
- {
- memcpy(pWatsonSharedMemory->wzDotDataDlls, DW_ALLMODULES, sizeof(DW_ALLMODULES));
- }
- else
- {
- // Assert, but go on
- _ASSERTE(sizeof(DW_ALLMODULES) <= sizeof(pWatsonSharedMemory->wzDotDataDlls));
- pWatsonSharedMemory->wzDotDataDlls[0] = 0;
- }
-
- // UI Customization
- //
- // The only UI customization we perform is to set the App Name. Currently we
- // do this just by using the executable name.
- //
- {
- PathString buf; // Buffer for path for description.
- LPCWSTR pName ; // Pointer to filename or description.
- int size; // Size of description.
- HMODULE hModule; // Handle to module.
- DWORD result; // Return code
-
- // Get module name.
- hModule = WszGetModuleHandle(NULL);
- result = WszGetModuleFileName(hModule, buf);
-
- if (result == 0)
- { // Couldn't get module name. This should never happen.
- pName = W("<<unknown>>");
- }
- else
- { // re-use the buf for pathname and description.
- size = DwGetAppDescription(buf, buf);
- pName = buf.GetUnicode();
- // If the returned size was zero, buf wasn't changed, and still contains the path.
- // find just the filename part.
- if (size == 0)
- { // Look for final '\'
- pName = wcsrchr(buf, W('\\'));
- // If found, skip it; if not, point to full name.
- pName = pName ? pName+1 : (LPCWSTR)buf;
- }
- }
-
- wcsncpy_s(pWatsonSharedMemory->uib.wzGeneral_AppName,
- COUNTOF(pWatsonSharedMemory->uib.wzGeneral_AppName),
- pName,
- _TRUNCATE);
-
- // For breakpoint, need to customize the "We're sorry..." message
- if (tore.IsBreakpoint())
- {
- LCID lcid = 0;
- // Get the message.
- StackSString sszMain_Intro_Bold;
- StackSString sszMain_Intro_Reg;
- EX_TRY
- {
- sszMain_Intro_Bold.LoadResource(CCompRC::Debugging, IDS_WATSON_DEBUG_BREAK_INTRO_BOLD);
- sszMain_Intro_Reg.LoadResource(CCompRC::Debugging, IDS_WATSON_DEBUG_BREAK_INTRO_REG);
- // Try to determine the language used for the above resources
- // At the moment this OS call is a heuristic which should match most of the time. But the
- // CLR is starting to support languages that don't even have LCIDs, so this may not always
- // be correct (and there may be NO LCID we can pass to watson). Long term, the correct fix
- // here is to get out of the game of making watson policy / UI decisions. This is happening
- // for Windows 7.
- lcid = GetThreadLocale();
- }
- EX_CATCH
- {
- // Just don't customize.
- }
- EX_END_CATCH(SwallowAllExceptions)
-
- // If we were able to get a string, set it.
- if (sszMain_Intro_Reg.GetCount() > 0)
- {
- // Instead of "<app.exe> has encountered an error and nees to close...", say
- // "<app.exe> has encountered a user-defined breakpoint."
- wcsncpy_s(pWatsonSharedMemory->uib.wzMain_Intro_Bold, COUNTOF(pWatsonSharedMemory->uib.wzMain_Intro_Bold), sszMain_Intro_Bold, _TRUNCATE);
- // Instead of "If you were in the middle of something...", say
- // "A breakpoint in an application indicates a program error..."
- wcsncpy_s(pWatsonSharedMemory->uib.wzMain_Intro_Reg, COUNTOF(pWatsonSharedMemory->uib.wzMain_Intro_Reg), sszMain_Intro_Reg, _TRUNCATE);
-
- pWatsonSharedMemory->bfDWUFlags = fDwuDenySuspend;
-
- pWatsonSharedMemory->lcidUI = lcid;
- }
- }
-
- }
-
- // Get the bucket parameters.
- switch (tore.GetType())
- {
- case TypeOfReportedError::NativeThreadUnhandledException:
- // Let Watson provide the buckets for a native thread.
- break;
- case TypeOfReportedError::UnhandledException:
- case TypeOfReportedError::FatalError:
- case TypeOfReportedError::UserBreakpoint:
- case TypeOfReportedError::NativeBreakpoint:
- // For managed exception or exceptions that come from managed code, we get the managed bucket parameters,
- // which will be displayed in the "details" section on any UI.
- //
- // Otherwise, use the unmanaged IP to bucket.
- if (bIsManagedFault)
- {
- RetrieveManagedBucketParameters(pExceptionInfo?pExceptionInfo->ExceptionRecord:NULL, &pWatsonSharedMemory->gmb, tore, pThread);
- }
- break;
- default:
- _ASSERTE(!"Unexpected TypeOfReportedException");
- break;
- }
-
- // dwThisThreadExFlags and dwOtherThreadExFlags are only used on IA64.
- CustomMinidumpBlock cmb =
- {
- TRUE, // fCustomMinidump
- kMiniDumpType, // dwMinidumpType : MiniDumpNormal
- FALSE, // fOnlyThisThread
- kThreadWriteFlags, // dwThisThreadFlags : ThreadWriteThread | ThreadWriteContext | ThreadWriteStack
- kThreadWriteFlags, // dwOtherThreadFlags
- 0, // dwThisThreadExFlags
- 0, // dwOtherThreadExFlags
- kModuleWriteFlags, // dwPreferredModuleFlags
- kModuleWriteFlags // dwOtherModuleFlags.
- };
-
- pWatsonSharedMemory->cmb = cmb;
-
- // At this point, the IPC block is all ready to go
- BOOL result = false;
- // There are two calls to RunWatson below. We want the second call to execute iff
- // secondInvocation is true.
- BOOL secondInvocation = true;
-
-
- EX_TRY
- {
- bool fRunWatson = false;
-#if defined(_TARGET_X86_)
- bool fGuardPagePresent = false;
-
- // There is an unfortunate side effect of calling ReadProcessMemory() out-of-process on IA64 WOW.
- // On all platforms (IA64 native & WOW64, AMD64 native & WOW64, and x86 native), if we call
- // ReadProcessMemory() out-of-process on a page with PAGE_GUARD protection, the read operation
- // fails as expected. However, on IA64 WOW64 only, the PAGE_GUARD protection is removed after
- // the read operation. Even IA64 native preserves the PAGE_GUARD protection.
- // See VSW 451447 for more information.
- if ((pThread != NULL) && pThread->DetermineIfGuardPagePresent())
- {
- fGuardPagePresent = true;
- }
-#endif // _TARGET_X86_
-
- if (secondInvocation)
- {
- fRunWatson = true;
- result = RunWatson(hWatsonSharedMemory,
- pWatsonSharedMemory->hEventAlive,
- pWatsonSharedMemory->hEventDone,
- pWatsonSharedMemory->hMutex);
- }
-
-#if defined(_TARGET_X86_)
- if (fRunWatson && fGuardPagePresent)
- {
- // This shouldn't cause a problem because guard pages are present in the first place.
- _ASSERTE(pThread != NULL);
- pThread->RestoreGuardPage();
- }
-#endif // _TARGET_X86_
- }
- EX_CATCH
- {
- // We couldn't wait around for watson to execute for some reason.
- result = false;
- }
- EX_END_CATCH(SwallowAllExceptions)
-
- // It's now safe to close all the synchronization and process handles.
-
- if (!result)
- {
- // Hmmm ... watson couldn't execute correctly.
- return FaultReportResultAbort;
- }
-
- LOG((LF_EH, LL_INFO100, "CLR Watson: returned 0x%x\n", pWatsonSharedMemory->msoctdsResult));
-
- // If user clicked "Debug"
- if (msoctdsDebug == pWatsonSharedMemory->msoctdsResult)
- {
- return FaultReportResultDebug;
- }
-
- // No debugging, successful completion.
- return FaultReportResultQuit;
-} // FaultReportResult DoFaultReportWorker()
-#ifdef _PREFAST_
-#pragma warning(pop)
-#endif
-
class WatsonThreadData {
public:
@@ -2650,188 +938,7 @@ class WatsonThreadData {
FaultReportResult result; // Result of invoking Watson
};
-class WatsonSOExceptionAddress {
- public:
-
- WatsonSOExceptionAddress()
- {
- m_SystemMethod = NULL;
- m_UserMethod = NULL;
- }
-
- SLOT m_SystemMethod; // IP in the first method on the stack which is in a system module
- SLOT m_UserMethod; // IP in the first method on the stack which is in a non-system module
-};
-
-//------------------------------------------------------------------------------
-// Description
-// This function is the stack walk callback for a thread that hit a soft SO (i.e., a SO caused by a
-// failed stack probe).
-//
-// Parameters
-// pCf -- A pointer to the current CrawlFrame
-// data - A pointer to WatsonSOExceptionAddress instance
-//
-// Returns:
-// SWA_ABORT to stop the stack crawl
-// SWA_CONTINUE to continue crawling the stack
-//
-// Exceptions
-// None.
-//------------------------------------------------------------------------------
-StackWalkAction WatsonSOStackCrawlCallback(CrawlFrame* pCf, void* pParam)
-{
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(pParam != NULL);
- WatsonSOExceptionAddress *pData = (WatsonSOExceptionAddress *) pParam;
-
- SLOT ip;
-
- if (pCf->IsFrameless())
- {
- ip = (PBYTE)GetControlPC(pCf->GetRegisterSet());
- }
- else
- {
- ip = (SLOT) pCf->GetFrame()->GetIP();
- }
-
- MethodDesc *pMD = pCf->GetFunction();
-
- if (pMD != NULL)
- {
- if (pMD->GetModule()->IsSystem())
- {
- if (pData->m_SystemMethod == NULL)
- {
- pData->m_SystemMethod = ip;
- }
- return SWA_CONTINUE;
- }
- else
- {
- _ASSERTE(pData->m_UserMethod == NULL);
- pData->m_UserMethod = ip;
- return SWA_ABORT;
- }
- }
- else
- {
- return SWA_CONTINUE;
- }
-
-}// WatsonSOCrawlCallBack
-
-//------------------------------------------------------------------------------
-// Description
-// Wrapper function for DoFaultReport. This function is called for SOs.
-// It sets up the ExceptionInfo appropriately for soft SOs (caused by
-// failed stack probes) before callign DoFaultReport.
-//
-// Parameters
-// pParam -- A pointer to a WatsonThreadData instance
-//
-// Exceptions
-// None.
-//------------------------------------------------------------------------------
-DWORD WINAPI DoFaultReportWorkerCallback(LPVOID pParam)
-{
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(pParam != NULL);
-
- WatsonThreadData* pData = (WatsonThreadData*) pParam;
-
- EXCEPTION_POINTERS ExceptionInfo;
- EXCEPTION_RECORD ExceptionRecord;
- PEXCEPTION_POINTERS pExceptionInfo = pData->pExceptionInfo;
-
- if (IsSOExceptionCode(pExceptionInfo->ExceptionRecord->ExceptionCode))
- {
- EX_TRY
- {
- if (ShouldLogInEventLog())
- {
- EventReporter reporter(EventReporter::ERT_StackOverflow);
- reporter.Report();
- }
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions);
- }
-
- // The purpose of the loop below is to avoid deadlocks during the abnormal process termination.
- // We will try to acquire the lock for 100 times to see whether we can successfully grap it. If we
- // can, then we can setup the thread and report the fault without worrying about the deadlock.
- // Otherwise we won't report the fault. It's still possible that we can enter the critical section
- // and report the fault without having deadlocks after this spin, but compared to the risky of
- // having deadlock, we still prefer not to report the fault if we can't get the lock after spin.
- BOOL isThreadSetup = false;
- for (int i = 0; i < 100; i++)
- {
- if (ThreadStore::CanAcquireLock())
- {
- SetupThread();
- isThreadSetup = true;
- break;
- }
- __SwitchToThread(30, CALLER_LIMITS_SPINNING);
- }
-
- if (isThreadSetup)
- {
- GCX_COOP();
-
- if (pData->pThread != NULL && pExceptionInfo != NULL &&
- pExceptionInfo->ContextRecord == NULL &&
- pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW &&
- pExceptionInfo->ExceptionRecord->ExceptionAddress == 0)
- {
- // In the case of a soft SO on a managed thread, we set the ExceptionAddress to one of the following
- //
- // 1. The first method on the stack that is in a non-system module.
- // 2. Failing that, the first method on the stack that is in a system module
-
- CONTEXT ContextRecord;
- memset(&ContextRecord, 0, sizeof(CONTEXT));
-
- ExceptionInfo.ContextRecord = &ContextRecord; // To display the "Send" button, dw20 wants a non-NULL pointer
- ExceptionRecord = *(pExceptionInfo->ExceptionRecord);
- ExceptionInfo.ExceptionRecord = &ExceptionRecord;
- pExceptionInfo = &ExceptionInfo;
-
- WatsonSOExceptionAddress WatsonExceptionAddresses;
-
- pData->pThread->StackWalkFrames(
- WatsonSOStackCrawlCallback,
- &WatsonExceptionAddresses,
- FUNCTIONSONLY|ALLOW_ASYNC_STACK_WALK);
-
- if (WatsonExceptionAddresses.m_UserMethod != NULL)
- {
- pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_UserMethod;
- }
- else if (WatsonExceptionAddresses.m_SystemMethod != NULL)
- {
- pExceptionInfo->ExceptionRecord->ExceptionAddress = WatsonExceptionAddresses.m_SystemMethod;
- }
-
- }
- pData->result = DoFaultReportWorker(
- pExceptionInfo,
- pData->tore,
- pData->pThread,
- pData->dwThreadID);
- }
-
-
- return 0;
-
-} // void DoFaultReportFavorWorker()
DWORD WINAPI ResetWatsonBucketsCallbackForStackOverflow(LPVOID pParam)
{
@@ -2840,7 +947,6 @@ DWORD WINAPI ResetWatsonBucketsCallbackForStackOverflow(LPVOID pParam)
THROWS;
GC_TRIGGERS;
PRECONDITION(IsWatsonEnabled());
- PRECONDITION(RunningOnWin7());
PRECONDITION(pParam != NULL);
}
CONTRACTL_END;
@@ -2886,7 +992,6 @@ void ResetWatsonBucketsFavorWorker(void * pParam)
NOTHROW;
GC_NOTRIGGER;
PRECONDITION(IsWatsonEnabled());
- PRECONDITION(RunningOnWin7());
PRECONDITION(pParam != NULL);
}
CONTRACTL_END;
@@ -2905,41 +1010,6 @@ void ResetWatsonBucketsFavorWorker(void * pParam)
}
-//------------------------------------------------------------------------------
-// Description
-// This function is called by the Debugger thread in response to a favor
-// posted to it by the faulting thread. The faulting thread uses the
-// Debugger thread to invoke Watson in the case of stack overflows.
-// Since the debugger thread doesn't have a managed Thread object,
-// it cannot be directly used to call DoFaultReport. Instead, this function
-// spawns a worker thread and waits for it to complete.
-//
-// Parameters
-// pParam -- A pointer to a WatsonThreadData instance
-//
-// Exceptions
-// None.
-//------------------------------------------------------------------------------
-void DoFaultReportFavorWorker(void* pParam)
-{
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(pParam != NULL);
-
- HANDLE hThread = NULL;
- DWORD dwThreadId;
-
- hThread = ::CreateThread(NULL, 0, DoFaultReportWorkerCallback, pParam, 0, &dwThreadId);
- if (hThread != NULL)
- {
- WaitForSingleObject(hThread, INFINITE);
- CloseHandle(hThread);
- }
-
- return;
-
-} // void DoFaultReportFavorWorker()
-
//----------------------------------------------------------------------------
// CreateThread() callback to invoke native Watson or put up our fake Watson
// dialog depending on m_fDoReportFault value.
@@ -3019,129 +1089,6 @@ VOID WINAPI DoFaultReportDoFavorCallback(LPVOID pFaultReportInfoAsVoid)
}
}
-
-
-//------------------------------------------------------------------------------
-// Description
-//
-// Parameters
-// pExceptionInfo -- information about the exception that caused the error.
-// If the error is not the result of an exception, pass NULL for this
-// parameter
-// tore -- Information about the fault
-// Returns
-// FaultReportResult -- enumeration indicating the
-// FaultReportResultAbort -- if Watson could not execute normally
-// FaultReportResultDebug -- if Watson executed normally, and the user
-// chose to debug the process
-// FaultReportResultQuit -- if Watson executed normally, and the user
-// chose to end the process (e.g. pressed "Send Error Report" or
-// "Don't Send").
-//
-// Exceptions
-// None.
-//------------------------------------------------------------------------------
-FaultReportResult DoFaultReport( // Was Watson attempted, successful? Run debugger?
- EXCEPTION_POINTERS *pExceptionInfo, // Information about the fault.
- TypeOfReportedError tore) // What sort of error is this?
-{
- WRAPPER_NO_CONTRACT;
-
- _ASSERTE(!RunningOnWin7());
-
- LOG((LF_EH, LL_INFO100, "DoFaultReport: at sp %p ...\n", GetCurrentSP()));
-
- Thread *pThread = GetThread();
-
- // If watson isn't available (eg. in Silverlight), then use a simple dialog box instead
- if (!IsWatsonEnabled())
- {
- if (!pThread)
- {
- return FaultReportResultAbort;
- }
-
- // Since the StackOverflow handler also calls us, we must keep our stack budget
- // to a minimum. Thus, we will launch a thread to do the actual work.
- FaultReportInfo fri;
- fri.m_fDoReportFault = FALSE;
- fri.m_pExceptionInfo = pExceptionInfo;
- fri.m_threadid = GetCurrentThreadId();
- // DoFaultCreateThreadReportCallback will overwrite this - if it doesn't, we'll assume it failed.
- fri.m_faultReportResult = FaultReportResultAbort;
-
- GCX_PREEMP();
-
-
- if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
- {
- DoFaultReportCreateThreadCallback(&fri);
- }
- else
- {
- // Stack overflow case - we don't have enough stack on our own thread so let the debugger
- // helper thread do the work.
- if (!g_pDebugInterface || FAILED(g_pDebugInterface->RequestFavor(DoFaultReportDoFavorCallback, &fri)))
- {
- // If we can't initialize the debugger helper thread or we are running on the debugger helper
- // thread, give it up. We don't have enough stack space.
-
- }
- }
-
- return fri.m_faultReportResult;
- }
-
-
- // Check if the current thread has the permission to open a process handle of the current process.
- // If not, the current thread may have been impersonated, we have to launch Watson from a new thread as in SO case.
- BOOL fOpenProcessFailed = FALSE;
- if (pExceptionInfo->ExceptionRecord->ExceptionCode != STATUS_STACK_OVERFLOW)
- {
- HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());
- fOpenProcessFailed = hProcess == NULL;
- }
-
- if ((pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_OVERFLOW) || fOpenProcessFailed)
- {
-
- WatsonThreadData* pData = new(nothrow) WatsonThreadData(
- pExceptionInfo,
- tore,
- pThread,
- GetCurrentThreadId(),
- FaultReportResultAbort); // default result
-
- if (pData == NULL)
- {
- return FaultReportResultAbort;
- }
-
- GCX_PREEMP();
-
- if (!g_pDebugInterface ||
- // When GC is in progress and current thread is either a GC thread or a managed
- // thread under Coop mode, this will let the new generated DoFaultReportCallBack
- // thread trigger a deadlock. So in this case, we should directly abort the fault
- // report to avoid the deadlock.
- ((IsGCThread() || pThread->PreemptiveGCDisabled()) && GCHeapUtilities::IsGCInProgress()) ||
- FAILED(g_pDebugInterface->RequestFavor(DoFaultReportFavorWorker, pData)))
- {
- // If we can't initialize the debugger helper thread or we are running on the debugger helper
- // thread, return without invoking Watson. We don't have enough stack space.
-
- delete pData;
- return FaultReportResultAbort;
- }
-
- FaultReportResult ret = pData->result;
- delete pData;
- return ret;
- }
-
- return DoFaultReportWorker(pExceptionInfo, tore, GetThread(), GetCurrentThreadId());
-} // FaultReportResult DoFaultReport()
-
// look at the type of the contract failure. if it's a precondition then we want to blame the caller
// of the method that originated the ContractException not just the first non-contract runtime frame.
// if this isn't a ContractException then we default to Invariant which won't skip the extra frame.