diff options
Diffstat (limited to 'src')
25 files changed, 1734 insertions, 725 deletions
diff --git a/src/debug/di/dbgtransportmanager.cpp b/src/debug/di/dbgtransportmanager.cpp index e9b619de18..c5f60f3a02 100644 --- a/src/debug/di/dbgtransportmanager.cpp +++ b/src/debug/di/dbgtransportmanager.cpp @@ -74,6 +74,7 @@ HRESULT DbgTransportTarget::GetTransportForProcess(DWORD dwPID HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); if (hProcess == NULL) { + transport->Shutdown(); return HRESULT_FROM_GetLastError(); } @@ -160,7 +161,6 @@ void DbgTransportTarget::ReleaseTransport(DbgTransportSession *pTransport) _ASSERTE(!"Trying to release transport that doesn't belong to this DbgTransportTarget"); pTransport->Shutdown(); - delete pTransport; } HRESULT DbgTransportTarget::CreateProcess(LPCWSTR lpApplicationName, @@ -211,7 +211,6 @@ DbgTransportTarget::ProcessEntry::~ProcessEntry() m_hProcess = NULL; m_transport->Shutdown(); - delete m_transport; m_transport = NULL; } diff --git a/src/debug/ee/debugger.cpp b/src/debug/ee/debugger.cpp index 12a7473e8e..6279ed0499 100644 --- a/src/debug/ee/debugger.cpp +++ b/src/debug/ee/debugger.cpp @@ -904,7 +904,6 @@ HRESULT ValidateObject(Object *objPtr) #ifdef FEATURE_DBGIPC_TRANSPORT_VM -__attribute__((destructor)) void ShutdownTransport() { @@ -914,6 +913,15 @@ ShutdownTransport() g_pDbgTransport = NULL; } } + +void +AbortTransport() +{ + if (g_pDbgTransport != NULL) + { + g_pDbgTransport->AbortConnection(); + } +} #endif // FEATURE_DBGIPC_TRANSPORT_VM @@ -1742,21 +1750,16 @@ void Debugger::RaiseStartupNotification() // that it's flushed from any CPU cache into memory. InterlockedIncrement(&m_fLeftSideInitialized); +#ifndef FEATURE_DBGIPC_TRANSPORT_VM // If we are remote debugging, don't send the event now if a debugger is not attached. No one will be // listening, and we will fail. However, we still want to initialize the variable above. - BOOL fRaiseStartupNotification = TRUE; -#if defined(FEATURE_DBGIPC_TRANSPORT_VM) - fRaiseStartupNotification = (CORDebuggerAttached() ? TRUE : FALSE); -#endif - if (fRaiseStartupNotification) - { - DebuggerIPCEvent startupEvent; - InitIPCEvent(&startupEvent, DB_IPCE_LEFTSIDE_STARTUP, NULL, VMPTR_AppDomain::NullPtr()); - - SendRawEvent(&startupEvent); + DebuggerIPCEvent startupEvent; + InitIPCEvent(&startupEvent, DB_IPCE_LEFTSIDE_STARTUP, NULL, VMPTR_AppDomain::NullPtr()); + + SendRawEvent(&startupEvent); - // RS will set flags from OOP while we're stopped at the event if it wants to attach. - } + // RS will set flags from OOP while we're stopped at the event if it wants to attach. +#endif // FEATURE_DBGIPC_TRANSPORT_VM } @@ -1884,7 +1887,8 @@ void Debugger::SendCreateProcess(DebuggerLockHolder * pDbgLockHolder) pDbgLockHolder->Acquire(); } -#ifdef FEATURE_CORECLR +#if defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) + HANDLE g_hContinueStartupEvent = NULL; CLR_ENGINE_METRICS g_CLREngineMetrics = { @@ -1895,9 +1899,6 @@ CLR_ENGINE_METRICS g_CLREngineMetrics = { bool IsTelestoDebugPackInstalled() { -#ifdef FEATURE_PAL - return false; -#else RegKeyHolder hKey; if (ERROR_SUCCESS != WszRegOpenKeyEx(HKEY_LOCAL_MACHINE, FRAMEWORK_REGISTRY_KEY_W, 0, KEY_READ, &hKey)) return false; @@ -1916,10 +1917,8 @@ bool IsTelestoDebugPackInstalled() // RegCloseKey called by holder return debugPackInstalled; -#endif // FEATURE_PAL } - #define StartupNotifyEventNamePrefix W("TelestoStartupEvent_") const int cchEventNameBufferSize = sizeof(StartupNotifyEventNamePrefix)/sizeof(WCHAR) + 8; // + hex DWORD (8). NULL terminator is included in sizeof(StartupNotifyEventNamePrefix) HANDLE OpenStartupNotificationEvent() @@ -1957,7 +1956,8 @@ void NotifyDebuggerOfTelestoStartup() CloseHandle(g_hContinueStartupEvent); g_hContinueStartupEvent = NULL; } -#endif // FEATURE_CORECLR + +#endif // FEATURE_CORECLR && !FEATURE_PAL //--------------------------------------------------------------------------------------- // @@ -1996,10 +1996,8 @@ HRESULT Debugger::Startup(void) // Iff the debug pack is installed, then go through the telesto debugging pipeline. LOG((LF_CORDB, LL_INFO10, "Debugging service is enabled because debug pack is installed or Watson support is enabled)\n")); -#if !defined(FEATURE_DBGIPC_TRANSPORT_VM) // This may block while an attach occurs. NotifyDebuggerOfTelestoStartup(); -#endif } else { @@ -2013,164 +2011,162 @@ HRESULT Debugger::Startup(void) } #endif // FEATURE_CORECLR && !FEATURE_PAL - DebuggerLockHolder dbgLockHolder(this); + { + DebuggerLockHolder dbgLockHolder(this); - // Stubs in Stacktraces are always enabled. - g_EnableSIS = true; + // Stubs in Stacktraces are always enabled. + g_EnableSIS = true; - // We can get extra Interop-debugging test coverage by having some auxillary unmanaged - // threads running and throwing debug events. Keep these stress procs separate so that - // we can focus on certain problem areas. -#ifdef _DEBUG + // We can get extra Interop-debugging test coverage by having some auxillary unmanaged + // threads running and throwing debug events. Keep these stress procs separate so that + // we can focus on certain problem areas. + #ifdef _DEBUG - g_DbgShouldntUseDebugger = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNoDebugger) != 0; + g_DbgShouldntUseDebugger = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgNoDebugger) != 0; - // Creates random thread procs. - DWORD dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreads); - DWORD dwId; - DWORD i; + // Creates random thread procs. + DWORD dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreads); + DWORD dwId; + DWORD i; - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + if (dwRegVal > 0) { - int iProc = GetRandomInt(NumItems(g_pStressProcs)); - LPTHREAD_START_ROUTINE pStartRoutine = g_pStressProcs[iProc]; - ::CreateThread(NULL, 0, pStartRoutine, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created random thread (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + int iProc = GetRandomInt(NumItems(g_pStressProcs)); + LPTHREAD_START_ROUTINE pStartRoutine = g_pStressProcs[iProc]; + ::CreateThread(NULL, 0, pStartRoutine, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created random thread (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsIB); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsIB); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsCantStop); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsCantStop); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropCantStopStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread 'can't-stop' (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropCantStopStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread 'can't-stop' (%d) with tid=0x%x\n", i, dwId)); + } } - } - dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsOOB); - if (dwRegVal > 0) - { - for(i = 0; i < dwRegVal; i++) + dwRegVal = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DbgExtraThreadsOOB); + if (dwRegVal > 0) { - ::CreateThread(NULL, 0, DbgInteropOOBStressProc, NULL, 0, &dwId); - LOG((LF_CORDB, LL_INFO1000, "Created extra thread OOB (%d) with tid=0x%x\n", i, dwId)); + for (i = 0; i < dwRegVal; i++) + { + ::CreateThread(NULL, 0, DbgInteropOOBStressProc, NULL, 0, &dwId); + LOG((LF_CORDB, LL_INFO1000, "Created extra thread OOB (%d) with tid=0x%x\n", i, dwId)); + } } - } -#endif + #endif -#ifdef FEATURE_PAL - PAL_InitializeDebug(); -#endif // FEATURE_PAL + #ifdef FEATURE_PAL + PAL_InitializeDebug(); + #endif // FEATURE_PAL - // Lazily initialize the interop-safe heap + // Lazily initialize the interop-safe heap - // Must be done before the RC thread is initialized. - // @dbgtodo - In V2, LS was lazily initialized; but was eagerly pre-initialized if launched by debugger. - // (This was for perf reasons). But we don't want Launch vs. Attach checks in the LS, so we now always - // init. As we move more to OOP, this init will become cheaper. - { - LazyInit(); - DebuggerController::Initialize(); - } + // Must be done before the RC thread is initialized. + // @dbgtodo - In V2, LS was lazily initialized; but was eagerly pre-initialized if launched by debugger. + // (This was for perf reasons). But we don't want Launch vs. Attach checks in the LS, so we now always + // init. As we move more to OOP, this init will become cheaper. + { + LazyInit(); + DebuggerController::Initialize(); + } - InitializeHijackFunctionAddress(); + InitializeHijackFunctionAddress(); - // Create the runtime controller thread, a.k.a, the debug helper thread. - // Don't use the interop-safe heap b/c we don't want to lazily create it. - m_pRCThread = new DebuggerRCThread(this); - - _ASSERTE(m_pRCThread != NULL); // throws on oom - TRACE_ALLOC(m_pRCThread); + // Create the runtime controller thread, a.k.a, the debug helper thread. + // Don't use the interop-safe heap b/c we don't want to lazily create it. + m_pRCThread = new DebuggerRCThread(this); + _ASSERTE(m_pRCThread != NULL); // throws on oom + TRACE_ALLOC(m_pRCThread); - hr = m_pRCThread->Init(); - - _ASSERTE(SUCCEEDED(hr)); // throws on error + hr = m_pRCThread->Init(); + _ASSERTE(SUCCEEDED(hr)); // throws on error -#if defined(FEATURE_DBGIPC_TRANSPORT_VM) - // Create transport session and initialize it. - g_pDbgTransport = new DbgTransportSession(); - hr = g_pDbgTransport->Init(m_pRCThread->GetDCB(), m_pAppDomainCB); - if (FAILED(hr)) - ThrowHR(hr); + #if defined(FEATURE_DBGIPC_TRANSPORT_VM) + // Create transport session and initialize it. + g_pDbgTransport = new DbgTransportSession(); + hr = g_pDbgTransport->Init(m_pRCThread->GetDCB(), m_pAppDomainCB); + if (FAILED(hr)) + { + ShutdownTransport(); + ThrowHR(hr); + } -#ifdef FEATURE_PAL - PAL_SetShutdownCallback(ShutdownTransport); -#endif // FEATURE_PAL + #ifdef FEATURE_PAL + PAL_SetShutdownCallback(AbortTransport); + #endif // FEATURE_PAL + #endif // FEATURE_DBGIPC_TRANSPORT_VM - bool waitForAttach = CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_DbgWaitForDebuggerAttach) != 0; - if (waitForAttach) - { - // Mark this process as launched by the debugger and the debugger as attached. - g_CORDebuggerControlFlags |= DBCF_GENERATE_DEBUG_CODE; - MarkDebuggerAttachedInternal(); + RaiseStartupNotification(); - LazyInit(); - DebuggerController::Initialize(); - } -#endif // FEATURE_DBGIPC_TRANSPORT_VM + // Also initialize the AppDomainEnumerationIPCBlock + #if !defined(FEATURE_IPCMAN) || defined(FEATURE_DBGIPC_TRANSPORT_VM) + m_pAppDomainCB = new (nothrow) AppDomainEnumerationIPCBlock(); + #else + m_pAppDomainCB = g_pIPCManagerInterface->GetAppDomainBlock(); + #endif - RaiseStartupNotification(); - - // Also initialize the AppDomainEnumerationIPCBlock -#if !defined(FEATURE_IPCMAN) || defined(FEATURE_DBGIPC_TRANSPORT_VM) - m_pAppDomainCB = new (nothrow) AppDomainEnumerationIPCBlock(); -#else - m_pAppDomainCB = g_pIPCManagerInterface->GetAppDomainBlock(); -#endif + if (m_pAppDomainCB == NULL) + { + LOG((LF_CORDB, LL_INFO100, "D::S: Failed to get AppDomain IPC block from IPCManager.\n")); + ThrowHR(E_FAIL); + } - if (m_pAppDomainCB == NULL) - { - LOG((LF_CORDB, LL_INFO100, "D::S: Failed to get AppDomain IPC block from IPCManager.\n")); - ThrowHR(E_FAIL); - } + hr = InitAppDomainIPC(); + _ASSERTE(SUCCEEDED(hr)); // throws on error. - hr = InitAppDomainIPC(); - _ASSERTE(SUCCEEDED(hr)); // throws on error. + // See if we need to spin up the helper thread now, rather than later. + DebuggerIPCControlBlock* pIPCControlBlock = m_pRCThread->GetDCB(); + (void)pIPCControlBlock; //prevent "unused variable" error from GCC - // See if we need to spin up the helper thread now, rather than later. - DebuggerIPCControlBlock* pIPCControlBlock = m_pRCThread->GetDCB(); - (void)pIPCControlBlock; //prevent "unused variable" error from GCC + _ASSERTE(pIPCControlBlock != NULL); + _ASSERTE(!pIPCControlBlock->m_rightSideShouldCreateHelperThread); + { + // Create the win32 thread for the helper and let it run free. + hr = m_pRCThread->Start(); - _ASSERTE(pIPCControlBlock != NULL); + // convert failure to exception as with old contract + if (FAILED(hr)) + { + ThrowHR(hr); + } - _ASSERTE(!pIPCControlBlock->m_rightSideShouldCreateHelperThread); - { - // Create the win32 thread for the helper and let it run free. - hr = m_pRCThread->Start(); + LOG((LF_CORDB, LL_EVERYTHING, "Start was successful\n")); + } - // convert failure to exception as with old contract - if (FAILED(hr)) + #ifdef TEST_DATA_CONSISTENCY + // if we have set the environment variable TestDataConsistency, run the data consistency test. + // See code:DataTest::TestDataSafety for more information + if ((g_pConfig != NULL) && (g_pConfig->TestDataConsistency() == true)) { - ThrowHR(hr); + DataTest dt; + dt.TestDataSafety(); } - - LOG((LF_CORDB, LL_EVERYTHING, "Start was successful\n")); + #endif } -#ifdef TEST_DATA_CONSISTENCY - // if we have set the environment variable TestDataConsistency, run the data consistency test. - // See code:DataTest::TestDataSafety for more information - if((g_pConfig != NULL) && (g_pConfig->TestDataConsistency() == true)) - { - DataTest dt; - dt.TestDataSafety(); - } -#endif +#ifdef FEATURE_PAL + // Signal the debugger (via dbgshim) and wait until it is ready for us to + // continue. This needs to be outside the lock and after the transport is + // initialized. + PAL_NotifyRuntimeStarted(); +#endif // FEATURE_PAL // We don't bother changing this process's permission. // A managed debugger will have the SE_DEBUG permission which will allow it to open our process handle, @@ -2584,11 +2580,9 @@ void Debugger::StopDebugger(void) m_pRCThread->AsyncStop(); } - // Also clean up the AppDomain stuff since this is cross-process. TerminateAppDomainIPC (); - // // Tell the VM to clear out all references to the debugger before we start cleaning up, // so that nothing will reference (accidentally) through the partially cleaned up debugger. diff --git a/src/debug/ee/debugger.h b/src/debug/ee/debugger.h index 5e4ff53562..d14c83c87a 100644 --- a/src/debug/ee/debugger.h +++ b/src/debug/ee/debugger.h @@ -111,7 +111,9 @@ typedef DPTR(struct DebuggerIPCControlBlock) PTR_DebuggerIPCControlBlock; GPTR_DECL(Debugger, g_pDebugger); GPTR_DECL(EEDebugInterface, g_pEEInterface); +#ifndef FEATURE_PAL GVAL_DECL(HANDLE, g_hContinueStartupEvent); +#endif extern DebuggerRCThread *g_pRCThread; //--------------------------------------------------------------------------------------- diff --git a/src/debug/inc/dbgtransportsession.h b/src/debug/inc/dbgtransportsession.h index 3b38b87bb7..0c0b37e67d 100644 --- a/src/debug/inc/dbgtransportsession.h +++ b/src/debug/inc/dbgtransportsession.h @@ -306,10 +306,12 @@ enum IPCEventType class DbgTransportSession { public: - // No real work done in the constructor. Use Init() instead. DbgTransportSession(); + // Cleanup what is allocated/created in Init() + ~DbgTransportSession(); + // Allocates initial resources (including starting the transport thread). The session will start in the // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the // LS will perform an Accept() to wait for a connection request. The RS needs an IP address and port @@ -330,6 +332,28 @@ public: // Init() may be called again to start over from the beginning). void Shutdown(); + // Cleans up the named pipe connection so no tmp files are left behind. Does only + // the minimum and must be safe to call at any time. Called during PAL ExitProcess, + // TerminateProcess and for unhandled native exceptions and asserts. + void AbortConnection(); + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + _ASSERTE(m_ref > 0); + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + #ifndef RIGHT_SIDE_COMPILE // API used only by the LS to drive the transport into a state where it won't accept connections. This is // used when no proxy is detected at startup but it's too late to shutdown all of the debugging system @@ -614,6 +638,9 @@ private: #endif // _DEBUG + // Reference count + LONG m_ref; + // Some flags used to record how far we got in Init() (used for cleanup in Shutdown()). bool m_fInitStateLock; #ifndef RIGHT_SIDE_COMPILE diff --git a/src/debug/inc/debug-pal.h b/src/debug/inc/debug-pal.h deleted file mode 100644 index 90cf0c822e..0000000000 --- a/src/debug/inc/debug-pal.h +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -// - - -#ifndef Debug_PAL_H -#define Debug_PAL_H - -#if defined(FEATURE_PAL) -// This function looks for a dynamic module (libraryName) loaded into the process specified (pId) -// and returns its load address. NULL is module is not loaded. -void *GetDynamicLibraryAddressInProcess(DWORD pid, const char *libraryName); -#endif - -#endif //Debug_PAL_H
\ No newline at end of file diff --git a/src/debug/shared/dbgtransportsession.cpp b/src/debug/shared/dbgtransportsession.cpp index 01f4acbc66..5aa9f28e15 100644 --- a/src/debug/shared/dbgtransportsession.cpp +++ b/src/debug/shared/dbgtransportsession.cpp @@ -36,9 +36,37 @@ DbgTransportSession *g_pDbgTransport = NULL; // No real work done in the constructor. Use Init() instead. DbgTransportSession::DbgTransportSession() { + m_ref = 1; m_eState = SS_Closed; } +DbgTransportSession::~DbgTransportSession() +{ + DbgTransportLog(LC_Proxy, "DbgTransportSession::~DbgTransportSession() called"); + + // No other threads are now using session resources. We're free to deallocate them as we wish (if they + // were allocated in the first place). + if (m_hTransportThread) + CloseHandle(m_hTransportThread); + if (m_rghEventReadyEvent[IPCET_OldStyle]) + CloseHandle(m_rghEventReadyEvent[IPCET_OldStyle]); + if (m_rghEventReadyEvent[IPCET_DebugEvent]) + CloseHandle(m_rghEventReadyEvent[IPCET_DebugEvent]); + if (m_pEventBuffers) + delete [] m_pEventBuffers; + +#ifdef RIGHT_SIDE_COMPILE + if (m_hSessionOpenEvent) + CloseHandle(m_hSessionOpenEvent); + + if (m_hProcessExited) + CloseHandle(m_hProcessExited); +#endif // RIGHT_SIDE_COMPILE + + if (m_fInitStateLock) + m_sStateLock.Destroy(); +} + // Allocates initial resources (including starting the transport thread). The session will start in the // SS_Opening state. That is, the RS will immediately start trying to Connect() a connection while the LS will // perform an accept()/Accept() to wait for a connection request. The RS needs an IP address and port number @@ -59,6 +87,7 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer // Because of the above memset the embeded classes/structs need to be reinitialized especially // the two way pipe; it expects the in/out handles to be -1 instead of 0. + m_ref = 1; m_pipe = TwoWayPipe(); m_sStateLock = DbgTransportLock(); @@ -124,9 +153,13 @@ HRESULT DbgTransportSession::Init(DebuggerIPCControlBlock *pDCB, AppDomainEnumer // Start the transport thread which handles forming and re-forming connections, driving the session // state to SS_Open and receiving and initially processing all incoming traffic. + AddRef(); m_hTransportThread = CreateThread(NULL, 0, TransportWorkerStatic, this, 0, NULL); if (m_hTransportThread == NULL) + { + Release(); return E_OUTOFMEMORY; + } return S_OK; } @@ -178,30 +211,16 @@ void DbgTransportSession::Shutdown() #endif // RIGHT_SIDE_COMPILE } - // No other threads are now using session resources. We're free to deallocate them as we wish (if they - // were allocated in the first place). - if (m_hTransportThread) - CloseHandle(m_hTransportThread); - if (m_rghEventReadyEvent[IPCET_OldStyle]) - CloseHandle(m_rghEventReadyEvent[IPCET_OldStyle]); - if (m_rghEventReadyEvent[IPCET_DebugEvent]) - CloseHandle(m_rghEventReadyEvent[IPCET_DebugEvent]); - if (m_pEventBuffers) - delete [] m_pEventBuffers; - -#ifdef RIGHT_SIDE_COMPILE - if (m_hSessionOpenEvent) - CloseHandle(m_hSessionOpenEvent); - - if (m_hProcessExited) - { - CloseHandle(m_hProcessExited); - } -#endif // RIGHT_SIDE_COMPILE - - if (m_fInitStateLock) - m_sStateLock.Destroy(); + // The transport instance is no longer valid + Release(); +} +// Cleans up the named pipe connection so no tmp files are left behind. Does only +// the minimum and must be safe to call at any time. Called during PAL ExitProcess, +// TerminateProcess and for unhandled native exceptions and asserts. +void DbgTransportSession::AbortConnection() +{ + m_pipe.Disconnect(); } #ifndef RIGHT_SIDE_COMPILE @@ -2125,6 +2144,10 @@ void DbgTransportSession::TransportWorker() } } } // Leave m_sStateLock + + // Now release all the resources allocated for the transport now that the + // worker thread isn't using them anymore. + Release(); } // Given a fully initialized debugger event structure, return the size of the structure in bytes (this is not diff --git a/src/dlls/dbgshim/dbgshim.cpp b/src/dlls/dbgshim/dbgshim.cpp index 42a2d253f9..eeccfe8f2b 100644 --- a/src/dlls/dbgshim/dbgshim.cpp +++ b/src/dlls/dbgshim/dbgshim.cpp @@ -26,9 +26,7 @@ #include <getproductversionnumber.h> #include <dbgenginemetrics.h> -#if defined(FEATURE_PAL) -#include "debug-pal.h" -#else +#ifndef FEATURE_PAL #define PSAPI_VERSION 2 #include <psapi.h> #endif @@ -40,27 +38,23 @@ // Here's a High-level overview of the API usage From the debugger: -A debugger calls GetStartupNotificationEvent(pid of debuggee) to get an event, which is signalled when that -process loads a Telesto. The debugger thus waits on that event, and when it's signalled, it can call +A debugger calls GetStartupNotificationEvent(pid of debuggee) to get an event, which is signalled when +that process loads a Telesto. The debugger thus waits on that event, and when it's signalled, it can call EnumerateCLRs / CloseCLREnumeration to get an array of Telestos in the target process (including the one -that was just loaded). -It can then call CreateVersionStringFromModule, CreateDebuggingInterfaceFromVersion to attach to -any or all Telestos of interest. - +that was just loaded). It can then call CreateVersionStringFromModule, CreateDebuggingInterfaceFromVersion +to attach to any or all Telestos of interest. From the debuggee: -When a new Telesto spins up, it checks for the startup event (created via GetStartupNotificationEvent), and if it -exists, it will: +When a new Telesto spins up, it checks for the startup event (created via GetStartupNotificationEvent), and +if it exists, it will: - signal it - wait on the "Continue" event, thus giving a debugger a chance to attach to the telesto - Notes: - There is no CreateProcess (Launch) case. All Launching is really an "Early-attach case". */ - // Contract for public APIs. These must be NOTHROW. #define PUBLIC_CONTRACT \ CONTRACTL \ @@ -69,6 +63,504 @@ Notes: } \ CONTRACTL_END; \ +#ifdef FEATURE_PAL + +static +void +RuntimeStartupHandler( + char *pszModulePath, + HMODULE hModule, + PVOID parameter); + +#else // FEATURE_PAL + +static +DWORD +StartupHelperThread( + LPVOID p); + +static +HRESULT +GetContinueStartupEvent( + DWORD debuggeePID, + LPCWSTR szTelestoFullPath, + __out HANDLE *phContinueStartupEvent); + +#endif // FEATURE_PAL + +// Functions that we'll look for in the loaded Mscordbi module. +typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( + int iDebuggerVersion, + DWORD pid, + HMODULE hmodTargetCLR, + IUnknown **ppCordb); + +// +// Helper class for RegisterForRuntimeStartup +// +class RuntimeStartupHelper +{ + LONG m_ref; + DWORD m_processId; + PSTARTUP_CALLBACK m_callback; + PVOID m_parameter; +#ifdef FEATURE_PAL + PVOID m_unregisterToken; +#else + bool m_canceled; + HANDLE m_startupEvent; + DWORD m_threadId; + HANDLE m_threadHandle; +#endif // FEATURE_PAL + +public: + RuntimeStartupHelper(DWORD dwProcessId, PSTARTUP_CALLBACK pfnCallback, PVOID parameter) : + m_ref(1), + m_processId(dwProcessId), + m_callback(pfnCallback), + m_parameter(parameter), +#ifdef FEATURE_PAL + m_unregisterToken(NULL) +#else + m_canceled(false), + m_startupEvent(NULL), + m_threadId(0), + m_threadHandle(NULL) +#endif // FEATURE_PAL + { + } + + ~RuntimeStartupHelper() + { +#ifndef FEATURE_PAL + if (m_startupEvent != NULL) + { + CloseHandle(m_startupEvent); + } + if (m_threadHandle != NULL) + { + CloseHandle(m_threadHandle); + } +#endif // FEATURE_PAL + } + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + +#ifdef FEATURE_PAL + + HRESULT Register() + { + if (PAL_RegisterForRuntimeStartup(m_processId, RuntimeStartupHandler, this, &m_unregisterToken) != NO_ERROR) + { + return E_INVALIDARG; + } + return S_OK; + } + + void Unregister() + { + PAL_UnregisterForRuntimeStartup(m_unregisterToken); + } + + void InvokeStartupCallback(char *pszModulePath, HMODULE hModule) + { + IUnknown *pCordb = NULL; + HMODULE hMod = NULL; + HRESULT hr = S_OK; + + // If either of these are NULL, there was an error from the PAL + // callback. GetLastError returns the error code from the PAL. + if (pszModulePath == NULL || hModule == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto exit; + } + + PAL_CPP_TRY + { + FPCoreCLRCreateCordbObject fpCreate = NULL; + char dbiPath[MAX_LONGPATH]; + + char *pszLast = strrchr(pszModulePath, DIRECTORY_SEPARATOR_CHAR_A); + if (pszLast == NULL) + { + _ASSERT(!"InvokeStartupCallback: can find separator in coreclr path\n"); + hr = E_INVALIDARG; + goto exit; + } + + strncpy_s(dbiPath, _countof(dbiPath), pszModulePath, pszLast - pszModulePath); + strcat_s(dbiPath, _countof(dbiPath), DIRECTORY_SEPARATOR_STR_A MAKEDLLNAME_A("mscordbi")); + + hMod = LoadLibraryA(dbiPath); + if (hMod == NULL) + { + hr = CORDBG_E_DEBUG_COMPONENT_MISSING; + goto exit; + } + + fpCreate = (FPCoreCLRCreateCordbObject)GetProcAddress(hMod, "CoreCLRCreateCordbObject"); + if (fpCreate == NULL) + { + hr = CORDBG_E_INCOMPATIBLE_PROTOCOL; + goto exit; + } + + HRESULT hr = fpCreate(CorDebugVersion_2_0, m_processId, hModule, &pCordb); + _ASSERTE((pCordb == NULL) == FAILED(hr)); + if (FAILED(hr)) + { + goto exit; + } + + m_callback(pCordb, m_parameter, S_OK); + } + PAL_CPP_CATCH_ALL + { + hr = E_FAIL; + goto exit; + } + PAL_CPP_ENDTRY + + exit: + if (FAILED(hr)) + { + _ASSERTE(pCordb == NULL); + + if (hMod != NULL) + { + FreeLibrary(hMod); + } + + // Invoke the callback on error + m_callback(NULL, m_parameter, hr); + } + } + +#else // FEATURE_PAL + + HRESULT Register() + { + HRESULT hr = GetStartupNotificationEvent(m_processId, &m_startupEvent); + if (FAILED(hr)) + { + goto exit; + } + + // Add a reference for the thread handler + AddRef(); + + m_threadHandle = CreateThread( + NULL, + 0, + ::StartupHelperThread, + this, + 0, + &m_threadId); + + if (m_threadHandle == NULL) + { + hr = E_OUTOFMEMORY; + Release(); + goto exit; + } + + exit: + return hr; + } + + HRESULT InternalEnumerateCLRs(HANDLE** ppHandleArray, LPWSTR** ppStringArray, DWORD* pdwArrayLength) + { + int numTries = 0; + HRESULT hr; + + while (numTries < 10) + { + // EnumerateCLRs uses the OS API CreateToolhelp32Snapshot which can return ERROR_BAD_LENGTH or + // ERROR_PARTIAL_COPY. If we get either of those, we try wait 1/10th of a second try again (that + // is the recommendation of the OS API owners) + hr = EnumerateCLRs(m_processId, ppHandleArray, ppStringArray, pdwArrayLength); + if ((hr != HRESULT_FROM_WIN32(ERROR_PARTIAL_COPY)) && (hr != HRESULT_FROM_WIN32(ERROR_BAD_LENGTH))) + { + break; + } + Sleep(100); + numTries++; + } + + return hr; + } + + void WakeRuntimes(HANDLE *handleArray, DWORD arrayLength) + { + if (handleArray != NULL) + { + for (int i = 0; i < (int)arrayLength; i++) + { + HANDLE h = handleArray[i]; + if (h != NULL && h != INVALID_HANDLE_VALUE) + { + SetEvent(h); + } + } + } + } + + void Unregister() + { + m_canceled = true; + + HANDLE *handleArray = NULL; + LPWSTR *stringArray = NULL; + DWORD arrayLength = 0; + + // Wake up runtime(s) + HRESULT hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength); + if (SUCCEEDED(hr)) + { + WakeRuntimes(handleArray, arrayLength); + CloseCLREnumeration(handleArray, stringArray, arrayLength); + } + + // Wake up worker thread + SetEvent(m_startupEvent); + + // Don't need to wake up and wait for the worker thread if called on it + if (m_threadId != GetCurrentThreadId()) + { + // Wait for work thread to exit + WaitForSingleObject(m_threadHandle, INFINITE); + } + } + + HRESULT InvokeStartupCallback(bool *pCoreClrExists) + { + HANDLE *handleArray = NULL; + LPWSTR *stringArray = NULL; + DWORD arrayLength = 0; + HRESULT hr = S_OK; + + PAL_CPP_TRY + { + IUnknown *pCordb = NULL; + WCHAR verStr[MAX_LONGPATH]; + DWORD verLen; + + *pCoreClrExists = FALSE; + + hr = InternalEnumerateCLRs(&handleArray, &stringArray, &arrayLength); + if (FAILED(hr)) + { + goto exit; + } + + for (int i = 0; i < (int)arrayLength; i++) + { + *pCoreClrExists = TRUE; + + hr = CreateVersionStringFromModule(m_processId, stringArray[i], verStr, _countof(verStr), &verLen); + if (FAILED(hr)) + { + goto exit; + } + + hr = CreateDebuggingInterfaceFromVersion(verStr, &pCordb); + if (FAILED(hr)) + { + goto exit; + } + + m_callback(pCordb, m_parameter, S_OK); + + // Currently only the first coreclr module in a process is supported + break; + } + } + PAL_CPP_CATCH_ALL + { + hr = E_FAIL; + goto exit; + } + PAL_CPP_ENDTRY + + exit: + if (*pCoreClrExists) + { + // Wake up all the runtimes + WakeRuntimes(handleArray, arrayLength); + } + CloseCLREnumeration(handleArray, stringArray, arrayLength); + + return hr; + } + + void StartupHelperThread() + { + bool coreclrExists = false; + + HRESULT hr = InvokeStartupCallback(&coreclrExists); + if (SUCCEEDED(hr)) + { + if (!coreclrExists && !m_canceled) + { + // Wait until the coreclr runtime (debuggee) starts up + if (WaitForSingleObject(m_startupEvent, INFINITE) == WAIT_OBJECT_0) + { + if (!m_canceled) + { + hr = InvokeStartupCallback(&coreclrExists); + if (SUCCEEDED(hr)) + { + // We should always find a coreclr module + _ASSERTE(coreclrExists); + } + } + } + else + { + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + } + + if (FAILED(hr)) + { + m_callback(NULL, m_parameter, hr); + } + } + +#endif // FEATURE_PAL +}; + +#ifdef FEATURE_PAL + +static +void +RuntimeStartupHandler(char *pszModulePath, HMODULE hModule, PVOID parameter) +{ + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)parameter; + helper->InvokeStartupCallback(pszModulePath, hModule); +} + +#else // FEATURE_PAL + +static +DWORD +StartupHelperThread(LPVOID p) +{ + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)p; + helper->StartupHelperThread(); + helper->Release(); + return 0; +} + +#endif // FEATURE_PAL + +//----------------------------------------------------------------------------- +// Public API. +// +// RegisterForRuntimeStartup -- executes the callback when the coreclr runtime +// starts in the specified process. The callback is passed the proper ICorDebug +// instance for the version of the runtime or an error if something fails. This +// API works for launch and attach (and even the attach scenario if the runtime +// hasn't been loaded yet) equally on both xplat and Windows. The callback is +// always called on a separate thread. This API returns immediately. +// +// The callback is invoked when the coreclr runtime module is loaded during early +// initialization. The runtime is blocked during initialization until the callback +// returns. +// +// If the runtime is already loaded in the process (as in the normal attach case), +// the callback is executed and the runtime is not blocked. +// +// The callback is always invoked on a separate thread and this API returns immediately. +// +// Only the first coreclr module instance found in the target process is currently +// supported. +// +// dwProcessId -- process id of the target process +// pfnCallback -- invoked when coreclr runtime starts +// parameter -- data to pass to callback +// ppUnregisterToken -- pointer to put the UnregisterForRuntimeStartup token. +// +//----------------------------------------------------------------------------- +HRESULT +RegisterForRuntimeStartup( + __in DWORD dwProcessId, + __in PSTARTUP_CALLBACK pfnCallback, + __in PVOID parameter, + __out PVOID *ppUnregisterToken) +{ + PUBLIC_CONTRACT; + + if (pfnCallback == NULL || ppUnregisterToken == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + + RuntimeStartupHelper *helper = new (nothrow) RuntimeStartupHelper(dwProcessId, pfnCallback, parameter); + if (helper == NULL) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = helper->Register(); + if (FAILED(hr)) + { + helper->Release(); + helper = NULL; + } + } + + *ppUnregisterToken = helper; + return hr; +} + +//----------------------------------------------------------------------------- +// Public API. +// +// UnregisterForRuntimeStartup -- stops/cancels runtime startup notification. Needs +// to be called during the debugger's shutdown to cleanup the internal data. +// +// This API can be called in the startup callback. Otherwise, it will block until +// the callback thread finishes and no more callbacks will be initiated after this +// API returns. +// +// pUnregisterToken -- unregister token from RegisterForRuntimeStartup or NULL. +//----------------------------------------------------------------------------- +HRESULT +UnregisterForRuntimeStartup( + __in PVOID pUnregisterToken) +{ + PUBLIC_CONTRACT; + + if (pUnregisterToken != NULL) + { + RuntimeStartupHelper *helper = (RuntimeStartupHelper *)pUnregisterToken; + helper->Unregister(); + helper->Release(); + } + + return S_OK; +} + //----------------------------------------------------------------------------- // Public API. // @@ -89,9 +581,10 @@ const int cchEventNameBufferSize = (sizeof(StartupNotifyEventNamePrefix) + sizeo + 10 // + decimal session id DWORD + 1; // '\' after session id - -HRESULT GetStartupNotificationEvent(DWORD debuggeePID, - __out HANDLE* phStartupEvent) +HRESULT +GetStartupNotificationEvent( + __in DWORD debuggeePID, + __out HANDLE* phStartupEvent) { PUBLIC_CONTRACT; @@ -162,17 +655,12 @@ HRESULT GetStartupNotificationEvent(DWORD debuggeePID, } *phStartupEvent = startupEvent; + return S_OK; #else *phStartupEvent = NULL; + return E_NOTIMPL; #endif // FEATURE_PAL - - return S_OK; } - -HRESULT GetContinueStartupEvent(DWORD debuggeePID, - LPCWSTR szTelestoFullPath, - __out HANDLE* phContinueStartupEvent); - // Refer to clr\src\mscoree\mscorwks_ntdef.src. const WORD kOrdinalForMetrics = 2; @@ -196,9 +684,12 @@ const WORD kOrdinalForMetrics = 2; // coreclr.dll is ours. A malicious user can be running a process with a bogus coreclr.dll loaded. // That's why we need to be extra careful reading coreclr.dll in this function. //----------------------------------------------------------------------------- -void GetTargetCLRMetrics(LPCWSTR szTelestoFullPath, - CLR_ENGINE_METRICS * pEngineMetricsOut, - DWORD * pdwRVAContinueStartupEvent = NULL) +static +void +GetTargetCLRMetrics( + LPCWSTR szTelestoFullPath, + CLR_ENGINE_METRICS *pEngineMetricsOut, + DWORD *pdwRVAContinueStartupEvent = NULL) { CONTRACTL { @@ -343,13 +834,22 @@ void GetTargetCLRMetrics(LPCWSTR szTelestoFullPath, #else //TODO: So far on POSIX systems we only support one version of debugging interface // in future we might want to detect it the same way we do it on Windows. + pEngineMetricsOut->cbSize = sizeof(*pEngineMetricsOut); pEngineMetricsOut->dwDbiVersion = CorDebugLatestVersion; + pEngineMetricsOut->phContinueStartupEvent = NULL; + + if (pdwRVAContinueStartupEvent != NULL) + { + *pdwRVAContinueStartupEvent = NULL; + } #endif // FEATURE_PAL } - // Returns true iff the module represents CoreClr. -bool IsCoreClr(const WCHAR* pModulePath) +static +bool +IsCoreClr( + const WCHAR* pModulePath) { _ASSERTE(pModulePath != NULL); @@ -366,7 +866,11 @@ bool IsCoreClr(const WCHAR* pModulePath) } // Returns true iff the module sent is named CoreClr.dll and has the metrics expected in it's PE header. -bool IsCoreClrWithGoodHeader(HANDLE hProcess, HMODULE hModule) +static +bool +IsCoreClrWithGoodHeader( + HANDLE hProcess, + HMODULE hModule) { HRESULT hr = S_OK; @@ -419,10 +923,12 @@ bool IsCoreClrWithGoodHeader(HANDLE hProcess, HMODULE hModule) // Notes: // Callers use code:CloseCLREnumeration to free the returned arrays. //----------------------------------------------------------------------------- -HRESULT EnumerateCLRs(DWORD debuggeePID, - __out HANDLE** ppHandleArrayOut, - __out LPWSTR** ppStringArrayOut, - __out DWORD* pdwArrayLengthOut) +HRESULT +EnumerateCLRs( + DWORD debuggeePID, + __out HANDLE** ppHandleArrayOut, + __out LPWSTR** ppStringArrayOut, + __out DWORD* pdwArrayLengthOut) { PUBLIC_CONTRACT; @@ -432,7 +938,7 @@ HRESULT EnumerateCLRs(DWORD debuggeePID, HandleHolder hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, debuggeePID); if (NULL == hProcess) - ThrowHR(E_FAIL); + return E_FAIL; // These shouldn't be freed HMODULE modules[1000]; @@ -556,7 +1062,11 @@ HRESULT EnumerateCLRs(DWORD debuggeePID, // dwArrayLength -- array length originally returned by EnumerateCLRs // //----------------------------------------------------------------------------- -HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dwArrayLength) +HRESULT +CloseCLREnumeration( + __in HANDLE* pHandleArray, + __in LPWSTR* pStringArray, + __in DWORD dwArrayLength) { PUBLIC_CONTRACT; @@ -593,7 +1103,11 @@ HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dw // - NULL if the module is not loaded. // - else Throws. *ppBaseAddress = NULL //----------------------------------------------------------------------------- -BYTE* GetRemoteModuleBaseAddress(DWORD dwPID, LPCWSTR szFullModulePath) +static +BYTE* +GetRemoteModuleBaseAddress( + DWORD dwPID, + LPCWSTR szFullModulePath) { CONTRACTL { @@ -674,11 +1188,13 @@ const WCHAR *c_versionStrFormat = W("%08x;%08x;%p"); // The version string is an opaque string that can only be passed back to other // DbgShim APIs. //----------------------------------------------------------------------------- -HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, - LPCWSTR szModuleName, - __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, - DWORD cchBuffer, - __out DWORD* pdwLength) +HRESULT +CreateVersionStringFromModule( + __in DWORD pidDebuggee, + __in LPCWSTR szModuleName, + __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, + __in DWORD cchBuffer, + __out DWORD* pdwLength) { PUBLIC_CONTRACT; @@ -735,13 +1251,6 @@ HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, return S_OK; } -// Functions that we'll look for in the loaded Mscordbi module. -typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( - int iDebuggerVersion, - DWORD pid, - HMODULE hmodTargetCLR, - IUnknown ** ppCordb); - //----------------------------------------------------------------------------- // Parse a version string into useful data. // @@ -758,8 +1267,13 @@ typedef HRESULT (STDAPICALLTYPE *FPCoreCLRCreateCordbObject)( // The version string is coming from the target CoreClr and in the case of a corrupted target, could be // an arbitrary string. It should be treated as untrusted public input. //----------------------------------------------------------------------------- -HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * piDebuggerVersion, DWORD * pdwPidDebuggee, - HMODULE * phmodTargetCLR) +static +HRESULT +ParseVersionString( + LPCWSTR szDebuggeeVersion, + CorDebugInterfaceVersion *piDebuggerVersion, + DWORD *pdwPidDebuggee, + HMODULE *phmodTargetCLR) { if ((piDebuggerVersion == NULL) || (pdwPidDebuggee == NULL) || @@ -772,7 +1286,6 @@ HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * } int numFieldsAssigned = swscanf_s(szDebuggeeVersion, c_versionStrFormat, piDebuggerVersion, pdwPidDebuggee, phmodTargetCLR); - if (numFieldsAssigned != 3) { return E_FAIL; @@ -787,13 +1300,14 @@ HRESULT ParseVersionString(LPCWSTR szDebuggeeVersion, CorDebugInterfaceVersion * // Arguments: // szFullDbiPath - (in/out): on input, the directory containing dbi. On output, the full path to dbi.dll. //----------------------------------------------------------------------------- -void AppendDbiDllName(SString & szFullDbiPath) +static +void +AppendDbiDllName(SString & szFullDbiPath) { const WCHAR * pDbiDllName = DIRECTORY_SEPARATOR_STR_W MAKEDLLNAME_W(W("mscordbi")); szFullDbiPath.Append(pDbiDllName); } - //----------------------------------------------------------------------------- // Return a path to the dbi next to the runtime, if present. // @@ -805,8 +1319,13 @@ void AppendDbiDllName(SString & szFullDbiPath) // Notes: // This just calculates a filename and does not determine if the file actually exists. //----------------------------------------------------------------------------- -void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SString & szFullDbiPath, - SString & szFullCoreClrPath) +static +void +GetDbiFilenameNextToRuntime( + DWORD pidDebuggee, + HMODULE hmodTargetCLR, + SString & szFullDbiPath, + SString & szFullCoreClrPath) { szFullDbiPath.Clear(); @@ -830,8 +1349,6 @@ void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SStri ThrowHR(E_FAIL); } - - // Change: // c:\abc\coreclr.dll // 01234567890 // c:\abc\mscordbi.dll @@ -859,8 +1376,11 @@ void GetDbiFilenameNextToRuntime(DWORD pidDebuggee, HMODULE hmodTargetCLR, SStri // Return Value: // true if the versions match // - -bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrPath) +static +bool +CheckDbiAndRuntimeVersion( + SString & szFullDbiPath, + SString & szFullCoreClrPath) { #ifndef FEATURE_PAL DWORD dwDbiVersionMS = 0; @@ -886,7 +1406,6 @@ bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrP #endif // FEATURE_PAL } - //----------------------------------------------------------------------------- // Public API. // Given a version string, create the matching mscordbi.dll for it. @@ -903,11 +1422,11 @@ bool CheckDbiAndRuntimeVersion(SString & szFullDbiPath, SString & szFullCoreClrP // the right debug pack is not installed. // else Error. (*ppCordb will be null) //----------------------------------------------------------------------------- -HRESULT CreateDebuggingInterfaceFromVersionEx( - int iDebuggerVersion, - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb - ) +HRESULT +CreateDebuggingInterfaceFromVersionEx( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb) { PUBLIC_CONTRACT; @@ -925,8 +1444,6 @@ HRESULT CreateDebuggingInterfaceFromVersionEx( goto Exit; } - *ppCordb = NULL; - // // Step 1: Parse version information into internal data structures // @@ -940,7 +1457,7 @@ HRESULT CreateDebuggingInterfaceFromVersionEx( goto Exit; // - // Step 2: Find the proper Dbi module (mscordbi.dll) and load it. + // Step 2: Find the proper dbi module (mscordbi) and load it. // // Check for dbi next to target CLR. @@ -1020,10 +1537,7 @@ Exit: } // Set our outparam. - if (ppCordb != NULL) - { - *ppCordb = pCordb; - } + *ppCordb = pCordb; // On success case, mscordbi.dll is leaked. // - We never give the caller back the module handle, so our caller can't do FreeLibrary(). @@ -1032,7 +1546,6 @@ Exit: return hr; } - //----------------------------------------------------------------------------- // Public API. // Superceded by CreateDebuggingInterfaceFromVersionEx in SLv4. @@ -1049,16 +1562,15 @@ Exit: // the right debug pack is not installed. // else Error. (*ppCordb will be null) //----------------------------------------------------------------------------- -HRESULT CreateDebuggingInterfaceFromVersion( - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb +HRESULT +CreateDebuggingInterfaceFromVersion( + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb ) { PUBLIC_CONTRACT; - return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, - szDebuggeeVersion, - ppCordb); + return CreateDebuggingInterfaceFromVersionEx(CorDebugVersion_2_0, szDebuggeeVersion, ppCordb); } #ifndef FEATURE_PAL @@ -1075,9 +1587,11 @@ HRESULT CreateDebuggingInterfaceFromVersion( // Returns: // S_OK on success. //------------------------------------------------------------------------------ -HRESULT GetContinueStartupEvent(DWORD debuggeePID, - LPCWSTR szTelestoFullPath, - __out HANDLE* phContinueStartupEvent) +HRESULT +GetContinueStartupEvent( + DWORD debuggeePID, + LPCWSTR szTelestoFullPath, + __out HANDLE* phContinueStartupEvent) { if ((phContinueStartupEvent == NULL) || (szTelestoFullPath == NULL)) return E_INVALIDARG; @@ -1130,7 +1644,22 @@ HRESULT GetContinueStartupEvent(DWORD debuggeePID, #include "debugshim.h" #endif -HRESULT CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterface) +//----------------------------------------------------------------------------- +// Public API. +// +// Parameters: +// clsid +// riid +// ppInterface +// +// Return: +// S_OK on success. +//----------------------------------------------------------------------------- +HRESULT +CLRCreateInstance( + REFCLSID clsid, + REFIID riid, + LPVOID *ppInterface) { #if defined(FEATURE_CORESYSTEM) diff --git a/src/dlls/dbgshim/dbgshim.h b/src/dlls/dbgshim/dbgshim.h index 4623e0d344..f706b89c9b 100644 --- a/src/dlls/dbgshim/dbgshim.h +++ b/src/dlls/dbgshim/dbgshim.h @@ -9,28 +9,52 @@ #include <windows.h> -EXTERN_C HRESULT GetStartupNotificationEvent(DWORD debuggeePID, - __out HANDLE* phStartupEvent); - -EXTERN_C HRESULT CloseCLREnumeration(HANDLE* pHandleArray, LPWSTR* pStringArray, DWORD dwArrayLength); - -EXTERN_C HRESULT EnumerateCLRs(DWORD debuggeePID, - __out HANDLE** ppHandleArrayOut, - __out LPWSTR** ppStringArrayOut, - __out DWORD* pdwArrayLengthOut); - -EXTERN_C HRESULT CreateVersionStringFromModule(DWORD pidDebuggee, - LPCWSTR szModuleName, - __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, - DWORD cchBuffer, - __out DWORD* pdwLength); - -EXTERN_C HRESULT CreateDebuggingInterfaceFromVersionEx( - int iDebuggerVersion, - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb); - -EXTERN_C HRESULT CreateDebuggingInterfaceFromVersion( - LPCWSTR szDebuggeeVersion, - IUnknown ** ppCordb); +typedef VOID (*PSTARTUP_CALLBACK)(IUnknown *pCordb, PVOID parameter, HRESULT hr); + +EXTERN_C HRESULT +RegisterForRuntimeStartup( + __in DWORD dwProcessId, + __in PSTARTUP_CALLBACK pfnCallback, + __in PVOID parameter, + __out PVOID *ppUnregisterToken); + +EXTERN_C HRESULT +UnregisterForRuntimeStartup( + __in PVOID pUnregisterToken); + +EXTERN_C HRESULT +GetStartupNotificationEvent( + __in DWORD debuggeePID, + __out HANDLE* phStartupEvent); + +EXTERN_C HRESULT +EnumerateCLRs(DWORD debuggeePID, + __out HANDLE** ppHandleArrayOut, + __out LPWSTR** ppStringArrayOut, + __out DWORD* pdwArrayLengthOut); + +EXTERN_C HRESULT +CloseCLREnumeration( + __in HANDLE* pHandleArray, + __in LPWSTR* pStringArray, + __in DWORD dwArrayLength); + +EXTERN_C HRESULT +CreateVersionStringFromModule( + __in DWORD pidDebuggee, + __in LPCWSTR szModuleName, + __out_ecount_part(cchBuffer, *pdwLength) LPWSTR pBuffer, + __in DWORD cchBuffer, + __out DWORD* pdwLength); + +EXTERN_C HRESULT +CreateDebuggingInterfaceFromVersionEx( + __in int iDebuggerVersion, + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb); + +EXTERN_C HRESULT +CreateDebuggingInterfaceFromVersion( + __in LPCWSTR szDebuggeeVersion, + __out IUnknown ** ppCordb); diff --git a/src/dlls/dbgshim/dbgshim.ntdef b/src/dlls/dbgshim/dbgshim.ntdef index 942bc220cf..cc62a40304 100644 --- a/src/dlls/dbgshim/dbgshim.ntdef +++ b/src/dlls/dbgshim/dbgshim.ntdef @@ -4,6 +4,8 @@ ; ; ==--== EXPORTS + RegisterForRuntimeStartup + UnregisterForRuntimeStartup GetStartupNotificationEvent EnumerateCLRs CloseCLREnumeration diff --git a/src/inc/dacvars.h b/src/inc/dacvars.h index c45a65f491..6a0bcc2e11 100644 --- a/src/inc/dacvars.h +++ b/src/inc/dacvars.h @@ -309,7 +309,9 @@ DEFINE_DACVAR(ULONG, PTR_BYTE, WKS__gc_heap__background_saved_lowest_address, WK DEFINE_DACVAR(ULONG, PTR_BYTE, WKS__gc_heap__background_saved_highest_address, WKS::gc_heap::background_saved_highest_address) #ifdef FEATURE_CORECLR +#ifndef FEATURE_PAL DEFINE_DACVAR(ULONG, HANDLE, dac__g_hContinueStartupEvent, ::g_hContinueStartupEvent) +#endif // !FEATURE_PAL DEFINE_DACVAR(ULONG, DWORD, CorHost2__m_dwStartupFlags, CorHost2::m_dwStartupFlags) #endif // FEATURE_CORECLR diff --git a/src/pal/inc/pal.h b/src/pal/inc/pal.h index b937917922..5324b3bdde 100644 --- a/src/pal/inc/pal.h +++ b/src/pal/inc/pal.h @@ -481,9 +481,10 @@ typedef long time_t; #define PAL_INITIALIZE_NONE 0x00 #define PAL_INITIALIZE_SYNC_THREAD 0x01 #define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02 +#define PAL_INITIALIZE_STD_HANDLES 0x04 // PAL_Initialize() flags -#define PAL_INITIALIZE PAL_INITIALIZE_SYNC_THREAD +#define PAL_INITIALIZE (PAL_INITIALIZE_SYNC_THREAD | PAL_INITIALIZE_STD_HANDLES) // PAL_InitializeDLL() flags - don't start any of the helper threads #define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE @@ -569,16 +570,6 @@ PALAPI PAL_TerminateEx( int exitCode); -/*++ -Function: - PAL_SetShutdownCallback - -Abstract: - Sets a callback that is executed when the PAL is shut down because of - ExitProcess, TerminateProcess or PAL_Shutdown but not PAL_Terminate/Ex. - - NOTE: Currently only one callback can be set at a time. ---*/ typedef VOID (*PSHUTDOWN_CALLBACK)(void); PALIMPORT @@ -587,6 +578,31 @@ PALAPI PAL_SetShutdownCallback( IN PSHUTDOWN_CALLBACK callback); +typedef VOID (*PPAL_STARTUP_CALLBACK)( + char *modulePath, + HMODULE hModule, + PVOID parameter); + +PALIMPORT +DWORD +PALAPI +PAL_RegisterForRuntimeStartup( + IN DWORD dwProcessId, + IN PPAL_STARTUP_CALLBACK pfnCallback, + IN PVOID parameter, + OUT PVOID *ppUnregisterToken); + +PALIMPORT +DWORD +PALAPI +PAL_UnregisterForRuntimeStartup( + IN PVOID pUnregisterToken); + +PALIMPORT +BOOL +PALAPI +PAL_NotifyRuntimeStarted(); + PALIMPORT void PALAPI @@ -633,15 +649,6 @@ PAL_Random( IN OUT LPVOID lpBuffer, IN DWORD dwLength); -// This helper will be used *only* by the CoreCLR to determine -// if an address lies inside CoreCLR or not. -// -// This shouldnt be used by any other component that links into the PAL. -PALIMPORT -BOOL -PALAPI -PAL_IsIPInCoreCLR(IN PVOID address); - #ifdef PLATFORM_UNIX PALIMPORT diff --git a/src/pal/inc/rt/palrt.h b/src/pal/inc/rt/palrt.h index 7426d5654f..9f2194ac2b 100644 --- a/src/pal/inc/rt/palrt.h +++ b/src/pal/inc/rt/palrt.h @@ -1363,6 +1363,7 @@ typedef VOID (__stdcall *WAITORTIMERCALLBACK)(PVOID, BOOLEAN); #ifdef PLATFORM_UNIX #define DIRECTORY_SEPARATOR_CHAR_A '/' #define DIRECTORY_SEPARATOR_CHAR_W W('/') +#define DIRECTORY_SEPARATOR_STR_A "/" #define DIRECTORY_SEPARATOR_STR_W W("/") #define PATH_SEPARATOR_CHAR_W W(':') #define PATH_SEPARATOR_STR_W W(":") @@ -1370,6 +1371,7 @@ typedef VOID (__stdcall *WAITORTIMERCALLBACK)(PVOID, BOOLEAN); #else // PLATFORM_UNIX #define DIRECTORY_SEPARATOR_CHAR_A '\\' #define DIRECTORY_SEPARATOR_CHAR_W W('\\') +#define DIRECTORY_SEPARATOR_STR_A "\\" #define DIRECTORY_SEPARATOR_STR_W W("\\") #define PATH_SEPARATOR_CHAR_W W(';') #define PATH_SEPARATOR_STR_W W(";") diff --git a/src/pal/src/debug/debug.cpp b/src/pal/src/debug/debug.cpp index 9a9fefcdb0..715ea7d641 100644 --- a/src/pal/src/debug/debug.cpp +++ b/src/pal/src/debug/debug.cpp @@ -672,7 +672,7 @@ DBGAttachProcess( int attchmentCount; int savedErrno; #if HAVE_PROCFS_CTL - int fd; + int fd = -1; char ctlPath[1024]; #endif // HAVE_PROCFS_CTL @@ -822,7 +822,7 @@ DBGDetachProcess( { int nbAttachLeft; #if HAVE_PROCFS_CTL - int fd; + int fd = -1; char ctlPath[1024]; #endif // HAVE_PROCFS_CTL @@ -1253,7 +1253,7 @@ ReadProcessMemory( vm_map_t task; LONG_PTR bytesToRead; #elif HAVE_PROCFS_CTL - int fd; + int fd = -1; char memPath[64]; off_t offset; #elif !HAVE_TTRACE @@ -1549,7 +1549,7 @@ WriteProcessMemory( kern_return_t result; vm_map_t task; #elif HAVE_PROCFS_CTL - int fd; + int fd = -1; char memPath[64]; LONG_PTR bytesWritten; off_t offset; diff --git a/src/pal/src/exception/machexception.cpp b/src/pal/src/exception/machexception.cpp index 4142e7c3e9..4cd19d7be9 100644 --- a/src/pal/src/exception/machexception.cpp +++ b/src/pal/src/exception/machexception.cpp @@ -1123,30 +1123,6 @@ bool IsWithinCoreCLR(void *pAddr) return (pAddr >= s_pLowerBound) && (pAddr < s_pUpperBound); } -#if !defined(_MSC_VER) && defined(_WIN64) -BOOL PALAPI PAL_IsIPInCoreCLR(IN PVOID address) -{ - BOOL fIsAddressWithinCoreCLR = FALSE; - - PERF_ENTRY(PAL_IsIPInCoreCLR); - ENTRY("PAL_IsIPInCoreCLR (address=%p)\n", address); - - if (address != NULL) - { - if (IsWithinCoreCLR(address)) - { - fIsAddressWithinCoreCLR = TRUE; - } - } - - LOGEXIT("PAL_IsIPInCoreCLR returns %d\n", fIsAddressWithinCoreCLR); - PERF_EXIT(PAL_IsIPInCoreCLR); - - return fIsAddressWithinCoreCLR; -} - -#endif //!defined(_MSC_VER) && defined(_WIN64) - extern malloc_zone_t *s_pExecutableHeap; // from heap.cpp in #ifdef CACHE_HEAP_ZONE #pragma mark - diff --git a/src/pal/src/exception/signal.cpp b/src/pal/src/exception/signal.cpp index ef7b569f69..aa70c22fff 100644 --- a/src/pal/src/exception/signal.cpp +++ b/src/pal/src/exception/signal.cpp @@ -65,6 +65,8 @@ static void sigfpe_handler(int code, siginfo_t *siginfo, void *context); static void sigsegv_handler(int code, siginfo_t *siginfo, void *context); static void sigtrap_handler(int code, siginfo_t *siginfo, void *context); static void sigbus_handler(int code, siginfo_t *siginfo, void *context); +static void sigint_handler(int code, siginfo_t *siginfo, void *context); +static void sigquit_handler(int code, siginfo_t *siginfo, void *context); static void common_signal_handler(PEXCEPTION_POINTERS pointers, int code, native_context_t *ucontext); @@ -81,6 +83,8 @@ struct sigaction g_previous_sigtrap; struct sigaction g_previous_sigfpe; struct sigaction g_previous_sigbus; struct sigaction g_previous_sigsegv; +struct sigaction g_previous_sigint; +struct sigaction g_previous_sigquit; /* public function definitions ************************************************/ @@ -119,6 +123,8 @@ BOOL SEHInitializeSignals() handle_signal(SIGFPE, sigfpe_handler, &g_previous_sigfpe); handle_signal(SIGBUS, sigbus_handler, &g_previous_sigbus); handle_signal(SIGSEGV, sigsegv_handler, &g_previous_sigsegv); + handle_signal(SIGINT, sigint_handler, &g_previous_sigint); + handle_signal(SIGQUIT, sigquit_handler, &g_previous_sigquit); handle_signal(INJECT_ACTIVATION_SIGNAL, inject_activation_handler, NULL); @@ -162,6 +168,8 @@ void SEHCleanupSignals() restore_signal(SIGFPE, &g_previous_sigfpe); restore_signal(SIGBUS, &g_previous_sigbus); restore_signal(SIGSEGV, &g_previous_sigsegv); + restore_signal(SIGINT, &g_previous_sigint); + restore_signal(SIGQUIT, &g_previous_sigquit); } /* internal function definitions **********************************************/ @@ -209,6 +217,8 @@ static void sigill_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigill); } + + PROCShutdownProcess(); } /*++ @@ -254,6 +264,8 @@ static void sigfpe_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigfpe); } + + PROCShutdownProcess(); } /*++ @@ -322,6 +334,8 @@ static void sigsegv_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigsegv); } + + PROCShutdownProcess(); } /*++ @@ -366,8 +380,10 @@ static void sigtrap_handler(int code, siginfo_t *siginfo, void *context) { // We abort instead of restore the original or default handler and returning // because returning from a SIGTRAP handler continues execution past the trap. - abort(); + PROCAbort(); } + + PROCShutdownProcess(); } /*++ @@ -421,6 +437,52 @@ static void sigbus_handler(int code, siginfo_t *siginfo, void *context) // Restore the original or default handler and restart h/w exception restore_signal(code, &g_previous_sigbus); } + + PROCShutdownProcess(); +} + +/*++ +Function : + sigint_handler + + handle SIGINT signal + +Parameters : + POSIX signal handler parameter list ("man sigaction" for details) + + (no return value) +--*/ +static void sigint_handler(int code, siginfo_t *siginfo, void *context) +{ + TRACE("SIGINT signal; chaining to previous sigaction\n"); + + PROCShutdownProcess(); + + // Restore the original or default handler and resend signal + restore_signal(code, &g_previous_sigint); + kill(gPID, code); +} + +/*++ +Function : + sigquit_handler + + handle SIGQUIT signal + +Parameters : + POSIX signal handler parameter list ("man sigaction" for details) + + (no return value) +--*/ +static void sigquit_handler(int code, siginfo_t *siginfo, void *context) +{ + TRACE("SIGQUIT signal; chaining to previous sigaction\n"); + + PROCShutdownProcess(); + + // Restore the original or default handler and resend signal + restore_signal(code, &g_previous_sigquit); + kill(gPID, code); } /*++ @@ -480,10 +542,12 @@ PAL_ERROR InjectActivationInternal(CorUnix::CPalThread* pThread) int status = pthread_kill(pThread->GetPThreadSelf(), INJECT_ACTIVATION_SIGNAL); if (status != 0) { + PROCShutdownProcess(); + // Failure to send the signal is fatal. There are only two cases when sending // the signal can fail. First, if the signal ID is invalid and second, // if the thread doesn't exist anymore. - abort(); + PROCAbort(); } return NO_ERROR; diff --git a/src/pal/src/file/file.cpp b/src/pal/src/file/file.cpp index 352a0979b1..30a4c3bf34 100644 --- a/src/pal/src/file/file.cpp +++ b/src/pal/src/file/file.cpp @@ -141,9 +141,9 @@ typedef enum /* Static global. The init function must be called before any other functions and if it is not successful, no other functions should be done. */ -static HANDLE pStdIn; -static HANDLE pStdOut; -static HANDLE pStdErr; +static HANDLE pStdIn = INVALID_HANDLE_VALUE; +static HANDLE pStdOut = INVALID_HANDLE_VALUE; +static HANDLE pStdErr = INVALID_HANDLE_VALUE; /*++ Function : @@ -4942,12 +4942,25 @@ void FILECleanupStdHandles(void) stdin_handle = pStdIn; stdout_handle = pStdOut; stderr_handle = pStdErr; + pStdIn = INVALID_HANDLE_VALUE; pStdOut = INVALID_HANDLE_VALUE; pStdErr = INVALID_HANDLE_VALUE; - CloseHandle(stdin_handle); - CloseHandle(stdout_handle); - CloseHandle(stderr_handle); + + if (stdin_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stdin_handle); + } + + if (stdout_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stdout_handle); + } + + if (stderr_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(stderr_handle); + } } diff --git a/src/pal/src/include/pal/dbgmsg.h b/src/pal/src/include/pal/dbgmsg.h index bf16bc4b98..93172f7dc3 100644 --- a/src/pal/src/include/pal/dbgmsg.h +++ b/src/pal/src/include/pal/dbgmsg.h @@ -54,6 +54,7 @@ Available channels : LOADER : Loading API (LoadLibrary, etc); loader application HANDLE : Handle manager (CloseHandle, etc.) SHMEM : Shared Memory functions (for IPC) + PROCESS : Process related APIs THREAD : Threading mechanism EXCEPT : Structured Exception Handling functions CRT : PAL implementation of the C Runtime Library functions @@ -172,6 +173,7 @@ typedef enum DCI_LOADER, DCI_HANDLE, DCI_SHMEM, + DCI_PROCESS, DCI_THREAD, DCI_EXCEPT, DCI_CRT, diff --git a/src/pal/src/include/pal/init.h b/src/pal/src/include/pal/init.h index c482a0090b..6e58c0d08d 100644 --- a/src/pal/src/include/pal/init.h +++ b/src/pal/src/include/pal/init.h @@ -31,15 +31,6 @@ extern "C" /*++ Function: - PALShutdown - -Utility function to force PAL to shutdown state - ---*/ -void PALShutdown( void ); - -/*++ -Function: PALCommonCleanup Utility function to prepare for shutdown. diff --git a/src/pal/src/include/pal/process.h b/src/pal/src/include/pal/process.h index cd26349162..3f93df003c 100644 --- a/src/pal/src/include/pal/process.h +++ b/src/pal/src/include/pal/process.h @@ -69,7 +69,7 @@ Return Notes : This function takes ownership of lpwstrCmdLine, but not of lpwstrFullPath --*/ -BOOL PROCCreateInitialProcess(LPWSTR lpwstrCmdLine, LPWSTR lpwstrFullPath); +BOOL PROCCreateInitialProcess(LPWSTR lpwstrCmdLine, LPWSTR lpwstrFullPath); /*++ Function: @@ -121,14 +121,26 @@ VOID PROCProcessUnlock(VOID); /*++ Function: - PROCCleanupProcess + PROCAbort() + + Aborts the process after calling the shutdown cleanup handler. This function + should be called instead of calling abort() directly. + + Does not return +--*/ +PAL_NORETURN +void PROCAbort(); + +/*++ +Function: + PROCShutdownProcess - Do all cleanup work for TerminateProcess, but don't terminate the process. - If bTerminateUnconditionally is TRUE, we exit as quickly as possible. + Calls the abort handler to do any shutdown cleanup. Call be + called from the unhandled native exception handler. (no return value) --*/ -void PROCCleanupProcess(BOOL bTerminateUnconditionally); +void PROCShutdownProcess(); /*++ Function: diff --git a/src/pal/src/include/pal/procobj.hpp b/src/pal/src/include/pal/procobj.hpp index 8500fd1f09..866a3ae867 100644 --- a/src/pal/src/include/pal/procobj.hpp +++ b/src/pal/src/include/pal/procobj.hpp @@ -73,7 +73,8 @@ namespace CorUnix ps(PS_IDLE), dwExitCode(0), lAttachCount(0), - pProcessModules(NULL) + pProcessModules(NULL), + cProcessModules(0) { }; @@ -84,12 +85,7 @@ namespace CorUnix DWORD dwExitCode; LONG lAttachCount; ProcessModules *pProcessModules; - }; - - class CProcSharedData - { - public: - DWORD dwProcessId; + DWORD cProcessModules; }; PAL_ERROR diff --git a/src/pal/src/init/pal.cpp b/src/pal/src/init/pal.cpp index 626f077112..ab00bfb76c 100644 --- a/src/pal/src/init/pal.cpp +++ b/src/pal/src/init/pal.cpp @@ -506,11 +506,14 @@ Initialize( goto CLEANUP10; } - /* create file objects for standard handles */ - if(!FILEInitStdHandles()) + if (flags & PAL_INITIALIZE_STD_HANDLES) { - ERROR("Unable to initialize standard file handles\n"); - goto CLEANUP13; + /* create file objects for standard handles */ + if (!FILEInitStdHandles()) + { + ERROR("Unable to initialize standard file handles\n"); + goto CLEANUP13; + } } if (FALSE == CRTInitStdStreams()) @@ -794,6 +797,7 @@ PAL_TerminateEx( LOGEXIT("PAL_Terminate returns.\n"); } + // Declare the beginning of shutdown PALSetShutdownIntent(); LOGEXIT("PAL_TerminateEx is exiting the current process.\n"); @@ -836,7 +840,7 @@ BOOL PALIsThreadDataInitialized() Function: PALCommonCleanup -Utility function to prepare for shutdown. + Utility function to prepare for shutdown. --*/ void @@ -844,36 +848,24 @@ PALCommonCleanup() { static bool cleanupDone = false; + // Declare the beginning of shutdown + PALSetShutdownIntent(); + if (!cleanupDone) { cleanupDone = true; - PALSetShutdownIntent(); - // // Let the synchronization manager know we're about to shutdown // - CPalSynchMgrController::PrepareForShutdown(); #ifdef _DEBUG PROCDumpThreadList(); #endif } -} - -/*++ -Function: - PALShutdown - sets the PAL's initialization count to zero, so that PALIsInitialized will - return FALSE. called by PROCCleanupProcess to tell some functions that the - PAL isn't fully functional, and that they should use an alternate code path - -(no parameters, no retun vale) ---*/ -void PALShutdown() -{ + // Mark that the PAL is uninitialized init_count = 0; } diff --git a/src/pal/src/init/sxs.cpp b/src/pal/src/init/sxs.cpp index f568711344..f011047e67 100644 --- a/src/pal/src/init/sxs.cpp +++ b/src/pal/src/init/sxs.cpp @@ -15,6 +15,7 @@ #include "pal/thread.hpp" #include "../thread/procprivate.hpp" #include "pal/module.h" +#include "pal/process.h" #include "pal/seh.hpp" using namespace CorUnix; @@ -95,7 +96,7 @@ CreateCurrentThreadData() if (NO_ERROR != palError) { ASSERT("Unable to allocate pal thread: error %d - aborting\n", palError); - abort(); + PROCAbort(); } } diff --git a/src/pal/src/misc/dbgmsg.cpp b/src/pal/src/misc/dbgmsg.cpp index 4d345649b1..8d66254d82 100644 --- a/src/pal/src/misc/dbgmsg.cpp +++ b/src/pal/src/misc/dbgmsg.cpp @@ -84,6 +84,7 @@ static const char *dbg_channel_names[]= "LOADER", "HANDLE", "SHMEM", + "PROCESS", "THREAD", "EXCEPT", "CRT", diff --git a/src/pal/src/misc/miscpalapi.cpp b/src/pal/src/misc/miscpalapi.cpp index 15cb741684..c39fb7fbca 100644 --- a/src/pal/src/misc/miscpalapi.cpp +++ b/src/pal/src/misc/miscpalapi.cpp @@ -24,6 +24,7 @@ Revision History: #include "pal/palinternal.h" #include "pal/dbgmsg.h" #include "pal/file.h" +#include "pal/process.h" #include "pal/module.h" #include "pal/malloc.hpp" @@ -367,7 +368,7 @@ CoCreateGuid(OUT GUID * pguid) if (status != uuid_s_ok) { ASSERT("Unexpected uuid_create failure (status=%u)\n", status); - abort(); + PROCAbort(); } // Encode the uuid with little endian. diff --git a/src/pal/src/thread/process.cpp b/src/pal/src/thread/process.cpp index 4e2a9c5571..3d9a9c4f25 100644 --- a/src/pal/src/thread/process.cpp +++ b/src/pal/src/thread/process.cpp @@ -49,36 +49,19 @@ Abstract: #include <sys/time.h> #include <sys/resource.h> #include <debugmacrosext.h> +#include <semaphore.h> using namespace CorUnix; -SET_DEFAULT_DEBUG_CHANNEL(THREAD); - - -void -ProcessCleanupRoutine( - CPalThread *pThread, - IPalObject *pObjectToCleanup, - bool fShutdown, - bool fCleanupSharedState - ); - -PAL_ERROR -ProcessInitializationRoutine( - CPalThread *pThread, - CObjectType *pObjectType, - void *pImmutableData, - void *pSharedData, - void *pProcessLocalData - ); +SET_DEFAULT_DEBUG_CHANNEL(PROCESS); CObjectType CorUnix::otProcess( otiProcess, - ProcessCleanupRoutine, - ProcessInitializationRoutine, + NULL, + NULL, 0, sizeof(CProcProcessLocalData), - sizeof(CProcSharedData), + 0, PROCESS_ALL_ACCESS, CObjectType::SecuritySupported, CObjectType::SecurityInfoNotPersisted, @@ -90,6 +73,12 @@ CObjectType CorUnix::otProcess( CObjectType::NoOwner ); +static +DWORD +PALAPI +StartupHelperThread( + LPVOID p); + // // Helper memory page used by the FlushProcessWriteBuffers // @@ -131,8 +120,8 @@ Volatile<LONG> terminator = 0; // Process ID of this process. DWORD gPID = (DWORD) -1; -// Function to call during PAL/process shutdown -PSHUTDOWN_CALLBACK g_shutdownCallback = nullptr; +// Function to call during PAL/process shutdown/abort +Volatile<PSHUTDOWN_CALLBACK> g_shutdownCallback = nullptr; // // Key used for associating CPalThread's with the underlying pthread @@ -170,7 +159,8 @@ static int checkFileType(char *lpFileName); static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUnconditionally); -ProcessModules *CreateProcessModules(IN HANDLE hProcess, OUT LPDWORD lpCount); +ProcessModules *GetProcessModulesFromHandle(IN HANDLE hProcess, OUT LPDWORD lpCount); +ProcessModules *CreateProcessModules(IN DWORD dwProcessId, OUT LPDWORD lpCount); void DestroyProcessModules(IN ProcessModules *listHead); /*++ @@ -522,7 +512,6 @@ PrepareStandardHandleExit: return palError; } - PAL_ERROR CorUnix::InternalCreateProcess( CPalThread *pThread, @@ -544,7 +533,6 @@ CorUnix::InternalCreateProcess( IDataLock *pLocalDataLock = NULL; CProcProcessLocalData *pLocalData; IDataLock *pSharedDataLock = NULL; - CProcSharedData *pSharedData; CPalThread *pDummyThread = NULL; HANDLE hDummyThread = NULL; HANDLE hProcess = NULL; @@ -844,23 +832,6 @@ CorUnix::InternalCreateProcess( child_blocking_pipe = pipe_descs[0]; } - // - // Get the data pointers for the new process object - // - - palError = pobjProcessRegistered->GetSharedData( - pThread, - WriteLock, - &pSharedDataLock, - reinterpret_cast<void **>(&pSharedData) - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to obtain shared data for new process object\n"); - goto InternalCreateProcessExit; - } - palError = pobjProcessRegistered->GetProcessLocalData( pThread, WriteLock, @@ -1019,10 +990,6 @@ CorUnix::InternalCreateProcess( pLocalDataLock->ReleaseLock(pThread, TRUE); pLocalDataLock = NULL; - pSharedData->dwProcessId = processId; - pSharedDataLock->ReleaseLock(pThread, TRUE); - pSharedDataLock = NULL; - // // Release file handle info; we don't need them anymore. Note that // this must happen after we've released the data locks, as @@ -1376,16 +1343,12 @@ static BOOL PROCEndProcess(HANDLE hProcess, UINT uExitCode, BOOL bTerminateUncon // (1) it doesn't run atexit handlers // (2) can invoke CrashReporter or produce a coredump, // which is appropriate for TerminateProcess calls - - // If this turns out to be inappropriate for some case, where we - // call TerminateProcess vs. ExitProcess, then there needs to be - // a CLR wrapper for TerminateProcess and some exposure for PAL_abort() - // to selectively use that in all but those cases. - abort(); } - else + else + { exit(uExitCode); + } ASSERT(FALSE); // we shouldn't get here } @@ -1413,29 +1376,428 @@ PAL_SetShutdownCallback( g_shutdownCallback = callback; } +#define RuntimeStartupSemaphoreName "/RuntimeStartupEvent%08x" +#define RuntimeContinueSemaphoreName "/RuntimeContinueEvent%08x" + +static bool IsCoreClrModule(const char* pModulePath) +{ + // Strip off everything up to and including the last slash in the path to get name + const char* pModuleName = pModulePath; + while (strchr(pModuleName, '/') != NULL) + { + pModuleName = strchr(pModuleName, '/'); + pModuleName++; // pass the slash + } + + return _stricmp(pModuleName, MAKEDLLNAME_A("coreclr")) == 0; +} + +class PAL_RuntimeStartupHelper +{ + LONG m_ref; + bool m_canceled; + DWORD m_processId; + PPAL_STARTUP_CALLBACK m_callback; + PVOID m_parameter; + DWORD m_threadId; + HANDLE m_threadHandle; + + // Debugger waits on this semaphore and the runtime signals it on startup. + sem_t *m_startupSem; + + // Debuggee waits on this semaphore and the debugger signals it after the callback returns. + sem_t *m_continueSem; + +public: + PAL_RuntimeStartupHelper(DWORD dwProcessId, PPAL_STARTUP_CALLBACK pfnCallback, PVOID parameter) : + m_ref(1), + m_canceled(false), + m_processId(dwProcessId), + m_callback(pfnCallback), + m_parameter(parameter), + m_threadId(0), + m_threadHandle(NULL), + m_startupSem(SEM_FAILED), + m_continueSem(SEM_FAILED) + { + } + + ~PAL_RuntimeStartupHelper() + { + if (m_startupSem != SEM_FAILED) + { + char startupSemName[NAME_MAX - 4]; + sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, m_processId); + + sem_close(m_startupSem); + sem_unlink(startupSemName); + } + if (m_continueSem != SEM_FAILED) + { + char continueSemName[NAME_MAX - 4]; + sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, m_processId); + + sem_close(m_continueSem); + sem_unlink(continueSemName); + } + if (m_threadHandle != NULL) + { + CloseHandle(m_threadHandle); + } + } + + LONG AddRef() + { + LONG ref = InterlockedIncrement(&m_ref); + return ref; + } + + LONG Release() + { + LONG ref = InterlockedDecrement(&m_ref); + if (ref == 0) + { + delete this; + } + return ref; + } + + PAL_ERROR Register() + { + CPalThread *pThread = InternalGetCurrentThread(); + char startupSemName[NAME_MAX - 4]; + char continueSemName[NAME_MAX - 4]; + PAL_ERROR pe = NO_ERROR; + + sprintf_s(startupSemName, sizeof(startupSemName), RuntimeStartupSemaphoreName, m_processId); + sprintf_s(continueSemName, sizeof(continueSemName), RuntimeContinueSemaphoreName, m_processId); + + TRACE("PAL_RuntimeStartupHelper.Register startup '%s' continue '%s'\n", startupSemName, continueSemName); + + // Create the continue semaphore first so we don't race with PAL_NotifyRuntimeStarted. This open will fail if another + // debugger is trying to attach to this process because the name will already exist. + m_continueSem = sem_open(continueSemName, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO, 0); + if (m_continueSem == SEM_FAILED) + { + TRACE("sem_open(continue) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for a debugger + // connection. + m_startupSem = sem_open(startupSemName, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO, 0); + if (m_startupSem == SEM_FAILED) + { + TRACE("sem_open(startup) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + // Add a reference for the thread handler + AddRef(); + + pe = InternalCreateThread( + pThread, + NULL, + 0, + ::StartupHelperThread, + this, + 0, + UserCreatedThread, + &m_threadId, + &m_threadHandle); + + if (NO_ERROR != pe) + { + TRACE("InternalCreateThread failed %d\n", pe); + Release(); + goto exit; + } + + exit: + return pe; + } + + void Unregister() + { + m_canceled = true; + + // Tell the runtime to continue + if (sem_post(m_continueSem) != 0) + { + ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + + // Tell the worker thread to continue + if (sem_post(m_startupSem) != 0) + { + ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + + // Don't need to wait for the worker thread if unregister called on it + if (m_threadId != (DWORD)THREADSilentGetCurrentThreadId()) + { + // Wait for work thread to exit + if (WaitForSingleObject(m_threadHandle, INFINITE) != WAIT_OBJECT_0) + { + ASSERT("WaitForSingleObject\n"); + } + } + } + + PAL_ERROR InvokeStartupCallback(bool *pCoreClrExists) + { + PAL_ERROR pe = NO_ERROR; + + *pCoreClrExists = false; + + // Enumerate all the modules in the process and invoke the callback + // for the coreclr module if found. + DWORD count; + ProcessModules *listHead = CreateProcessModules(m_processId, &count); + if (listHead == NULL) + { + TRACE("CreateProcessModules failed for pid %d\n", m_processId); + pe = ERROR_INVALID_PARAMETER; + goto exit; + } + + for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) + { + if (IsCoreClrModule(entry->Name)) + { + *pCoreClrExists = true; + + PAL_CPP_TRY + { + TRACE("InvokeStartupCallback executing callback %s\n", entry->Name); + m_callback(entry->Name, entry->BaseAddress, m_parameter); + } + PAL_CPP_CATCH_ALL + { + } + PAL_CPP_ENDTRY + + // Currently only the first coreclr module in a process is supported + break; + } + } + + exit: + if (*pCoreClrExists) + { + // Wake up all the runtimes + if (sem_post(m_continueSem) != 0) + { + ASSERT("sem_post(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + } + } + + if (listHead != NULL) + { + DestroyProcessModules(listHead); + } + return pe; + } + + void StartupHelperThread() + { + bool coreclrExists = false; + + PAL_ERROR pe = InvokeStartupCallback(&coreclrExists); + if (pe == NO_ERROR) + { + if (!coreclrExists && !m_canceled) + { + // Wait until the coreclr runtime (debuggee) starts up + if (sem_wait(m_startupSem) == 0) + { + if (!m_canceled) + { + pe = InvokeStartupCallback(&coreclrExists); + if (pe == NO_ERROR) + { + // We should always find a coreclr module + _ASSERTE(coreclrExists); + } + } + } + else + { + TRACE("sem_wait(startup) failed: errno is %d (%s)\n", errno, strerror(errno)); + pe = ERROR_INVALID_HANDLE; + } + } + } + + if (pe != NO_ERROR) + { + SetLastError(pe); + m_callback(NULL, NULL, m_parameter); + } + } +}; + +static +DWORD +PALAPI +StartupHelperThread(LPVOID p) +{ + TRACE("PAL's StartupHelperThread starting\n"); + + PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)p; + helper->StartupHelperThread(); + helper->Release(); + return 0; +} + /*++ -Function: - PROCCleanupProcess - - Do all cleanup work for TerminateProcess, but don't terminate the process. - If bTerminateUnconditionally is TRUE, we exit as quickly as possible. + PAL_RegisterForRuntimeStartup -(no return value) +Parameters: + dwProcessId - process id of runtime process + pfnCallback - function to callback for coreclr module found + parameter - data to pass to callback + ppUnregisterToken - pointer to put PAL_UnregisterForRuntimeStartup token. + +Return value: + PAL_ERROR + +Note: + If the modulePath or hModule is NULL when the callback is invoked, an error occured + and GetLastError() will return the Win32 error code. + + The callback is always invoked on a separate thread and this API returns immediately. + + Only the first coreclr module is currently supported. + +--*/ +DWORD +PALAPI +PAL_RegisterForRuntimeStartup( + IN DWORD dwProcessId, + IN PPAL_STARTUP_CALLBACK pfnCallback, + IN PVOID parameter, + OUT PVOID *ppUnregisterToken) +{ + _ASSERTE(pfnCallback != NULL); + _ASSERTE(ppUnregisterToken != NULL); + + PAL_RuntimeStartupHelper *helper = new PAL_RuntimeStartupHelper(dwProcessId, pfnCallback, parameter); + + // Create the debuggee startup semaphore so the runtime (debuggee) knows to wait for + // a debugger connection. + PAL_ERROR pe = helper->Register(); + if (NO_ERROR != pe) + { + helper->Release(); + helper = NULL; + } + + *ppUnregisterToken = helper; + return pe; +} + +/*++ + PAL_UnregisterForRuntimeStartup + + Stops/cancels startup notification. This API can be called in the startup callback. Otherwise, + it will block until the callback thread finishes and no more callbacks will be initiated after + this API returns. + +Parameters: + dwUnregisterToken - token from PAL_RegisterForRuntimeStartup or NULL. + +Return value: + PAL_ERROR +--*/ +DWORD +PALAPI +PAL_UnregisterForRuntimeStartup( + IN PVOID pUnregisterToken) +{ + if (pUnregisterToken != NULL) + { + PAL_RuntimeStartupHelper *helper = (PAL_RuntimeStartupHelper *)pUnregisterToken; + helper->Unregister(); + helper->Release(); + } + return NO_ERROR; +} + +/*++ + PAL_NotifyRuntimeStarted + + Signals the debugger waiting for runtime startup notification to continue and + waits until the debugger signals us to continue. + +Parameters: + None + +Return value: + TRUE - succeeded, FALSE - failed --*/ -void PROCCleanupProcess(BOOL bTerminateUnconditionally) +BOOL +PALAPI +PAL_NotifyRuntimeStarted() { - if (g_shutdownCallback != nullptr) + char szStartupSemName[NAME_MAX - 4]; + char szContinueSemName[NAME_MAX - 4]; + sem_t *startupSem = SEM_FAILED; + sem_t *continueSem = SEM_FAILED; + BOOL result = TRUE; + + sprintf_s(szStartupSemName, sizeof(szStartupSemName), RuntimeStartupSemaphoreName, gPID); + sprintf_s(szContinueSemName, sizeof(szContinueSemName), RuntimeContinueSemaphoreName, gPID); + + TRACE("PAL_NotifyRuntimeStarted opening startup '%s' continue '%s'\n", szStartupSemName, szContinueSemName); + + // Open the debugger startup semaphore. If it doesn't exists, then we do nothing and + // the function is successful. + startupSem = sem_open(szStartupSemName, O_RDWR); + if (startupSem == SEM_FAILED) { - g_shutdownCallback(); + TRACE("sem_open(%s) failed: %d (%s)\n", szStartupSemName, errno, strerror(errno)); + goto exit; } - /* Declare the beginning of shutdown */ - PALSetShutdownIntent(); + // Open the debuggee continue semaphore. If we can open the startup sem and not this one + // something is seriously wrong. + continueSem = sem_open(szContinueSemName, O_RDWR); + if (continueSem == SEM_FAILED) + { + ASSERT("sem_open(%s) failed: %d (%s)\n", szContinueSemName, errno, strerror(errno)); + result = FALSE; + goto exit; + } - PALCommonCleanup(); + // Wake up the debugger waiting for startup + if (sem_post(startupSem) != 0) + { + ASSERT("sem_post(startupSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + result = FALSE; + goto exit; + } + + // Now wait until the debugger notification is finished + if (sem_wait(continueSem) != 0) + { + ASSERT("sem_wait(continueSem) failed: errno is %d (%s)\n", errno, strerror(errno)); + result = FALSE; + goto exit; + } - /* This must be called after PALCommonCleanup */ - PALShutdown(); +exit: + if (startupSem != SEM_FAILED) + { + sem_close(startupSem); + } + if (continueSem != SEM_FAILED) + { + sem_close(continueSem); + } + return result; } /*++ @@ -1688,7 +2050,6 @@ OpenProcess( IPalObject *pobjProcessRegistered = NULL; IDataLock *pDataLock; CProcProcessLocalData *pLocalData; - CProcSharedData *pSharedData; CObjectAttributes oa; HANDLE hProcess = NULL; @@ -1732,21 +2093,6 @@ OpenProcess( pLocalData->dwProcessId = dwProcessId; pDataLock->ReleaseLock(pThread, TRUE); - palError = pobjProcess->GetSharedData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast<void **>(&pSharedData) - ); - - if (NO_ERROR != palError) - { - goto OpenProcessExit; - } - - pSharedData->dwProcessId = dwProcessId; - pDataLock->ReleaseLock(pThread, TRUE); - palError = g_pObjectManager->RegisterObject( pThread, pobjProcess, @@ -1818,8 +2164,7 @@ EnumProcessModules( BOOL result = TRUE; DWORD count = 0; - - ProcessModules *listHead = CreateProcessModules(hProcess, &count); + ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count); if (listHead != NULL) { for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -1870,7 +2215,7 @@ GetModuleFileNameExW( DWORD result = 0; DWORD count = 0; - ProcessModules *listHead = CreateProcessModules(hProcess, &count); + ProcessModules *listHead = GetProcessModulesFromHandle(hProcess, &count); if (listHead != NULL) { for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -1889,7 +2234,7 @@ GetModuleFileNameExW( /*++ Function: - CreateProcessModules + GetProcessModulesFromHandle Abstract Returns a process's module list @@ -1899,7 +2244,7 @@ Return --*/ ProcessModules * -CreateProcessModules( +GetProcessModulesFromHandle( IN HANDLE hProcess, OUT LPDWORD lpCount) { @@ -1910,10 +2255,13 @@ CreateProcessModules( IDataLock *pDataLock = NULL; PAL_ERROR palError = NO_ERROR; DWORD dwProcessId = 0; + DWORD count = 0; + + _ASSERTE(lpCount != NULL); if (hPseudoCurrentProcess == hProcess) { - dwProcessId = gPID; + pobjProcess = g_pobjProcess; } else { @@ -1924,83 +2272,205 @@ CreateProcessModules( hProcess, &aotProcess, 0, - &pobjProcess - ); + &pobjProcess); if (NO_ERROR != palError) { + pThread->SetLastError(ERROR_INVALID_HANDLE); goto exit; } + } - palError = pobjProcess->GetProcessLocalData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast<void **>(&pLocalData) - ); + palError = pobjProcess->GetProcessLocalData( + pThread, + WriteLock, + &pDataLock, + reinterpret_cast<void **>(&pLocalData)); - if (NO_ERROR != palError) + _ASSERTE(NO_ERROR == palError); + + dwProcessId = pLocalData->dwProcessId; + listHead = pLocalData->pProcessModules; + count = pLocalData->cProcessModules; + + // If the module list hasn't been created yet, create it now + if (listHead == NULL) + { + listHead = CreateProcessModules(dwProcessId, &count); + if (listHead == NULL) { + pThread->SetLastError(ERROR_INVALID_PARAMETER); goto exit; } - dwProcessId = pLocalData->dwProcessId; - listHead = pLocalData->pProcessModules; + if (pLocalData != NULL) + { + pLocalData->pProcessModules = listHead; + pLocalData->cProcessModules = count; + } } - // If the module list hasn't been created yet, create it now - if (listHead == NULL) +exit: + if (NULL != pDataLock) { + pDataLock->ReleaseLock(pThread, TRUE); + } + if (NULL != pobjProcess) + { + pobjProcess->ReleaseReference(pThread); + } + + *lpCount = count; + return listHead; +} + +/*++ +Function: + CreateProcessModules + +Abstract + Returns a process's module list + +Return + ProcessModules * list + +--*/ +ProcessModules * +CreateProcessModules( + IN DWORD dwProcessId, + OUT LPDWORD lpCount) +{ + ProcessModules *listHead = NULL; + _ASSERTE(lpCount != NULL); + #if defined(__APPLE__) - // For OSx, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the - // command and read the relevant lines: - // - // ... - // ==== regions for process 347 (non-writable and writable regions are interleaved) - // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL - // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun - // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV - // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER - // ... - // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV - // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib - // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM - // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER - // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER - char *line = NULL; - size_t lineLen = 0; - int count = 0; - ssize_t read; - - char vmmapCommand[100]; - int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d", dwProcessId); - _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand)); - - FILE *vmmapFile = popen(vmmapCommand, "r"); - if (vmmapFile == NULL) - { - SetLastError(ERROR_INVALID_HANDLE); - return NULL; + // For OSx, the "vmmap" command outputs something similar to the /proc/*/maps file so popen the + // command and read the relevant lines: + // + // ... + // ==== regions for process 347 (non-writable and writable regions are interleaved) + // REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL + // __TEXT 000000010446d000-0000000104475000 [ 32K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // __DATA 0000000104475000-0000000104476000 [ 4K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // __LINKEDIT 0000000104476000-000000010447a000 [ 16K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/corerun + // Kernel Alloc Once 000000010447a000-000000010447b000 [ 4K] rw-/rwx SM=PRV + // MALLOC (admin) 000000010447b000-000000010447c000 [ 4K] r--/rwx SM=ZER + // ... + // MALLOC (admin) 00000001044ab000-00000001044ac000 [ 4K] r--/rwx SM=PRV + // __TEXT 00000001044ac000-0000000104c84000 [ 8032K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 0000000104c84000-0000000104c85000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 0000000104c85000-000000010513b000 [ 4824K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 000000010513b000-000000010513c000 [ 4K] rwx/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __TEXT 000000010513c000-000000010516f000 [ 204K] r-x/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __DATA 000000010516f000-00000001051ce000 [ 380K] rw-/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __DATA 00000001051ce000-00000001051fa000 [ 176K] rw-/rwx SM=PRV /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // __LINKEDIT 00000001051fa000-0000000105bac000 [ 9928K] r--/rwx SM=COW /Users/mikem/coreclr/bin/Product/OSx.x64.Debug/libcoreclr.dylib + // VM_ALLOCATE 0000000105bac000-0000000105bad000 [ 4K] r--/rw- SM=SHM + // MALLOC (admin) 0000000105bad000-0000000105bae000 [ 4K] r--/rwx SM=ZER + // MALLOC 0000000105bae000-0000000105baf000 [ 4K] rw-/rwx SM=ZER + char *line = NULL; + size_t lineLen = 0; + int count = 0; + ssize_t read; + + char vmmapCommand[100]; + int chars = snprintf(vmmapCommand, sizeof(vmmapCommand), "/usr/bin/vmmap -interleaved %d", dwProcessId); + _ASSERTE(chars > 0 && chars <= sizeof(vmmapCommand)); + + FILE *vmmapFile = popen(vmmapCommand, "r"); + if (vmmapFile == NULL) + { + goto exit; + } + + // Reading maps file line by line + while ((read = getline(&line, &lineLen, vmmapFile)) != -1) + { + void *startAddress, *endAddress; + char moduleName[PATH_MAX]; + int size; + + if (sscanf(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName) == 4) + { + bool dup = false; + for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) + { + if (strcmp(moduleName, entry->Name) == 0) + { + dup = true; + break; + } + } + + if (!dup) + { + int cbModuleName = strlen(moduleName) + 1; + ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); + if (entry == NULL) + { + DestroyProcessModules(listHead); + listHead = NULL; + count = 0; + break; + } + strcpy_s(entry->Name, cbModuleName, moduleName); + entry->BaseAddress = startAddress; + entry->Next = listHead; + listHead = entry; + count++; + } } + } - // Reading maps file line by line - while ((read = getline(&line, &lineLen, vmmapFile)) != -1) - { - void *startAddress, *endAddress; - char moduleName[PATH_MAX]; - int size; + *lpCount = count; + + free(line); // We didn't allocate line, but as per contract of getline we should free it + pclose(vmmapFile); + +#elif defined(HAVE_PROCFS_CTL) - if (sscanf(line, "__TEXT %p-%p [ %dK] %*[-/rwxsp] SM=%*[A-Z] %s\n", &startAddress, &endAddress, &size, moduleName) == 4) + // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says + // about a library we are looking for. This file looks something like this: + // + // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file + // + // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so + // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap] + // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so + // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so + + // Making something like: /proc/123/maps + char mapFileName[100]; + char *line = NULL; + size_t lineLen = 0; + int count = 0; + ssize_t read; + + INDEBUG(int chars = ) + snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId); + _ASSERTE(chars > 0 && chars <= sizeof(mapFileName)); + + FILE *mapsFile = fopen(mapFileName, "r"); + if (mapsFile == NULL) + { + goto exit; + } + + // Reading maps file line by line + while ((read = getline(&line, &lineLen, mapsFile)) != -1) + { + void *startAddress, *endAddress, *offset; + int devHi, devLo, inode; + char moduleName[PATH_MAX]; + + if (sscanf(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName) == 7) + { + if (inode != 0) { bool dup = false; for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) @@ -2018,7 +2488,6 @@ CreateProcessModules( ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); if (entry == NULL) { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); DestroyProcessModules(listHead); listHead = NULL; count = 0; @@ -2032,112 +2501,16 @@ CreateProcessModules( } } } + } - *lpCount = count; - - free(line); // We didn't allocate line, but as per contract of getline we should free it - pclose(vmmapFile); - -#elif defined(HAVE_PROCFS_CTL) - - // Here we read /proc/<pid>/maps file in order to parse it and figure out what it says - // about a library we are looking for. This file looks something like this: - // - // [address] [perms] [offset] [dev] [inode] [pathname] - HEADER is not preset in an actual file - // - // 35b1800000-35b1820000 r-xp 00000000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a1f000-35b1a20000 r--p 0001f000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a20000-35b1a21000 rw-p 00020000 08:02 135522 /usr/lib64/ld-2.15.so - // 35b1a21000-35b1a22000 rw-p 00000000 00:00 0 [heap] - // 35b1c00000-35b1dac000 r-xp 00000000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1dac000-35b1fac000 ---p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1fac000-35b1fb0000 r--p 001ac000 08:02 135870 /usr/lib64/libc-2.15.so - // 35b1fb0000-35b1fb2000 rw-p 001b0000 08:02 135870 /usr/lib64/libc-2.15.so - - // Making something like: /proc/123/maps - char mapFileName[100]; - - INDEBUG(int chars = ) - snprintf(mapFileName, sizeof(mapFileName), "/proc/%d/maps", dwProcessId); - _ASSERTE(chars > 0 && chars <= sizeof(mapFileName)); - - FILE *mapsFile = fopen(mapFileName, "r"); - if (mapsFile == NULL) - { - SetLastError(ERROR_INVALID_HANDLE); - return NULL; - } - - char *line = NULL; - size_t lineLen = 0; - int count = 0; - ssize_t read; - - // Reading maps file line by line - while ((read = getline(&line, &lineLen, mapsFile)) != -1) - { - void *startAddress, *endAddress, *offset; - int devHi, devLo, inode; - char moduleName[PATH_MAX]; - - if (sscanf(line, "%p-%p %*[-rwxsp] %p %x:%x %d %s\n", &startAddress, &endAddress, &offset, &devHi, &devLo, &inode, moduleName) == 7) - { - if (inode != 0) - { - bool dup = false; - for (ProcessModules *entry = listHead; entry != NULL; entry = entry->Next) - { - if (strcmp(moduleName, entry->Name) == 0) - { - dup = true; - break; - } - } - - if (!dup) - { - int cbModuleName = strlen(moduleName) + 1; - ProcessModules *entry = (ProcessModules *)InternalMalloc(sizeof(ProcessModules) + cbModuleName); - if (entry == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - DestroyProcessModules(listHead); - listHead = NULL; - count = 0; - break; - } - strcpy_s(entry->Name, cbModuleName, moduleName); - entry->BaseAddress = startAddress; - entry->Next = listHead; - listHead = entry; - count++; - } - } - } - } - - *lpCount = count; + *lpCount = count; - free(line); // We didn't allocate line, but as per contract of getline we should free it - fclose(mapsFile); + free(line); // We didn't allocate line, but as per contract of getline we should free it + fclose(mapsFile); #else - _ASSERTE(!"Not implemented on this platform"); + _ASSERTE(!"Not implemented on this platform"); #endif - if (pLocalData != NULL) - { - pLocalData->pProcessModules = listHead; - } - } exit: - if (NULL != pDataLock) - { - pDataLock->ReleaseLock(pThread, TRUE); - } - - if (NULL != pobjProcess) - { - pobjProcess->ReleaseReference(pThread); - } return listHead; } @@ -2147,8 +2520,10 @@ Function: Abstract Cleans up the process module table. + Return - TRUE if it succeeded, FALSE otherwise + None + --*/ void DestroyProcessModules(IN ProcessModules *listHead) @@ -2163,6 +2538,44 @@ DestroyProcessModules(IN ProcessModules *listHead) /*++ Function: + PROCShutdownProcess + + Calls the abort handler to do any shutdown cleanup. Call be called + from the unhandled native exception handler. + +(no return value) +--*/ +__attribute__((destructor)) +void PROCShutdownProcess() +{ + TRACE("PROCShutdownProcess %p\n", g_shutdownCallback.RawValue()); + + PSHUTDOWN_CALLBACK callback = InterlockedExchangePointer(&g_shutdownCallback, NULL); + if (callback != NULL) + { + callback(); + } +} + +/*++ +Function: + PROCAbort() + + Aborts the process after calling the shutdown cleanup handler. This function + should be called instead of calling abort() directly. + + Does not return +--*/ +PAL_NORETURN +void +PROCAbort() +{ + PROCShutdownProcess(); + abort(); +} + +/*++ +Function: InitializeFlushProcessWriteBuffers Abstract @@ -2200,7 +2613,7 @@ BOOL InitializeFlushProcessWriteBuffers() if (!(e)) \ { \ fprintf(stderr, "FATAL ERROR: " msg); \ - abort(); \ + PROCAbort(); \ } \ } \ while(0) @@ -2417,7 +2830,6 @@ CorUnix::CreateInitialProcessAndThreadObjects( IPalObject *pobjProcess = NULL; IDataLock *pDataLock; CProcProcessLocalData *pLocalData; - CProcSharedData *pSharedData; CObjectAttributes oa; HANDLE hProcess; @@ -2471,22 +2883,6 @@ CorUnix::CreateInitialProcessAndThreadObjects( pLocalData->ps = PS_RUNNING; pDataLock->ReleaseLock(pThread, TRUE); - palError = pobjProcess->GetSharedData( - pThread, - WriteLock, - &pDataLock, - reinterpret_cast<void **>(&pSharedData) - ); - - if (NO_ERROR != palError) - { - ASSERT("Unable to access shared data"); - goto CreateInitialProcessAndThreadObjectsExit; - } - - pSharedData->dwProcessId = gPID; - pDataLock->ReleaseLock(pThread, TRUE); - palError = g_pObjectManager->RegisterObject( pThread, pobjProcess, @@ -2790,36 +3186,38 @@ CorUnix::TerminateCurrentProcessNoExit(BOOL bTerminateUnconditionally) if (0 != old_terminator && GetCurrentThreadId() != old_terminator) { - /* another thread has already initiated the termination process. we - could just block on the PALInitLock critical section, but then - PROCSuspendOtherThreads would hang... so sleep forever here, we're - terminating anyway + /* another thread has already initiated the termination process. we + could just block on the PALInitLock critical section, but then + PROCSuspendOtherThreads would hang... so sleep forever here, we're + terminating anyway - Update: [TODO] PROCSuspendOtherThreads has been removed. Can this - code be changed? */ - - /* note that if *this* thread has already started the termination - process, we want to proceed. the only way this can happen is if a - call to DllMain (from ExitProcess) brought us here (because DllMain - called ExitProcess, or TerminateProcess, or ExitThread); - TerminateProcess won't call DllMain, so there's no danger to get - caught in an infinite loop */ - WARN("termination already started from another thread; blocking.\n"); - poll(NULL, 0, INFTIM); - } - - /* Try to lock the initialization count to prevent multiple threads from - terminating/initializing the PAL simultaneously */ - - /* note : it's also important to take this lock before the process lock, - because Init/Shutdown take the init lock, and the functions they call - may take the process lock. We must do it in the same order to avoid - deadlocks */ - locked = PALInitLock(); - if(locked && PALIsInitialized()) - { - PROCCleanupProcess(bTerminateUnconditionally); - } + Update: [TODO] PROCSuspendOtherThreads has been removed. Can this + code be changed? */ + + /* note that if *this* thread has already started the termination + process, we want to proceed. the only way this can happen is if a + call to DllMain (from ExitProcess) brought us here (because DllMain + called ExitProcess, or TerminateProcess, or ExitThread); + TerminateProcess won't call DllMain, so there's no danger to get + caught in an infinite loop */ + WARN("termination already started from another thread; blocking.\n"); + poll(NULL, 0, INFTIM); + } + + /* Try to lock the initialization count to prevent multiple threads from + terminating/initializing the PAL simultaneously */ + + /* note : it's also important to take this lock before the process lock, + because Init/Shutdown take the init lock, and the functions they call + may take the process lock. We must do it in the same order to avoid + deadlocks */ + + locked = PALInitLock(); + if(locked && PALIsInitialized()) + { + PROCShutdownProcess(); + PALCommonCleanup(); + } } /*++ @@ -3007,39 +3405,6 @@ PROCGetProcessStatusExit: return palError; } -void -ProcessCleanupRoutine( - CPalThread *pThread, - IPalObject *pObjectToCleanup, - bool fShutdown, - bool fCleanupSharedState - ) -{ - // - // Nothing to do -- no allocated data - // -} - -PAL_ERROR -ProcessInitializationRoutine( - CPalThread *pThread, - CObjectType *pObjectType, - void *pImmutableData, - void *pSharedData, - void *pProcessLocalData - ) -{ - PAL_ERROR palError = NO_ERROR; - CProcProcessLocalData *pProcLocalData = - reinterpret_cast<CProcProcessLocalData*>(pProcessLocalData); - CProcSharedData *pProcSharedData = - reinterpret_cast<CProcSharedData*>(pSharedData); - - pProcLocalData->dwProcessId = pProcSharedData->dwProcessId; - - return palError; -} - #ifdef _DEBUG void PROCDumpThreadList() { |