diff options
Diffstat (limited to 'src/debug/di/eventredirectionpipeline.cpp')
-rw-r--r-- | src/debug/di/eventredirectionpipeline.cpp | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/src/debug/di/eventredirectionpipeline.cpp b/src/debug/di/eventredirectionpipeline.cpp new file mode 100644 index 0000000000..7cda8ff66e --- /dev/null +++ b/src/debug/di/eventredirectionpipeline.cpp @@ -0,0 +1,351 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +//***************************************************************************** +// File: EventRedirectionPipeline.cpp +// + +// +// Implement a native pipeline that redirects events. +//***************************************************************************** + +#include "stdafx.h" +#include "nativepipeline.h" +#include "sstring.h" + +#if defined(ENABLE_EVENT_REDIRECTION_PIPELINE) +#include "eventredirection.h" +#include "eventredirectionpipeline.h" + + +// Constructor +EventRedirectionPipeline::EventRedirectionPipeline() +{ + m_pBlock = NULL; + m_dwProcessId = 0; + + InitConfiguration(); +} + +// Dtor +EventRedirectionPipeline::~EventRedirectionPipeline() +{ + CloseBlock(); +} + +// Call to free up the pipeline. +void EventRedirectionPipeline::Delete() +{ + delete this; +} + +//--------------------------------------------------------------------------------------- +// +// Returns true if the Redirection is enabled. +// +// Arguments: +// szOptions - specific Create/attach options to include in the overal format string +// pidTarget - pid of real debuggeee. +// +// Return Value: +// S_OK on success. +// +// +// Notes: +// This will spin up an auxillary debugger (windbg) and attach it to the existing +// process. If this is a create case, then we're attaching to a create-suspended process. +// +//--------------------------------------------------------------------------------------- +void EventRedirectionPipeline::InitConfiguration() +{ + // We need some config strings. See header for possible values. + m_DebuggerCmd.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectApplication); + m_AttachParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectAttachCmd); + m_CreateParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCreateCmd); + m_CommonParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCommonCmd); +} + + +// Implement INativeEventPipeline::DebugSetProcessKillOnExit +BOOL EventRedirectionPipeline::DebugSetProcessKillOnExit(bool fKillOnExit) +{ + // Not implemented for redirection pipeline. That's ok. Redirection pipeline doesn't care. + // Return success so caller can assert for other pipeline types. + return TRUE; +} + +//--------------------------------------------------------------------------------------- +// +// Attach a real debugger to the target. +// +// Arguments: +// szOptions - specific Create/attach options to include in the overal format string +// pidTarget - pid of real debuggeee. +// +// Return Value: +// S_OK on success. +// +// +// Notes: +// This will spin up an auxillary debugger (windbg) and attach it to the existing +// process. If this is a create case, then we're attaching to a create-suspended process. +// +//--------------------------------------------------------------------------------------- +HRESULT EventRedirectionPipeline::AttachDebuggerToTarget(LPCWSTR szOptions, DWORD pidTarget) +{ + SString s; + + BOOL fRemap = false; + + LPCWSTR lpApplicationName = NULL; + LPCWSTR lpCommandLine = NULL; + + EX_TRY + { + m_pBlock = new (nothrow) RedirectionBlock(); // $$ make throwing + + ZeroMemory(m_pBlock, sizeof(RedirectionBlock)); + + // Initialize + m_pBlock->m_versionCookie = EVENT_REDIRECTION_CURRENT_VERSION; + + s.Printf(m_CommonParams.Value(), GetCurrentProcessId(), m_pBlock, szOptions, pidTarget); + lpCommandLine = s.GetUnicode(); + + + lpApplicationName = m_DebuggerCmd.Value(); // eg, something like L"c:\\debuggers_amd64\\windbg.exe"; + + // Initialize events. + const BOOL kManualResetEvent = TRUE; + const BOOL kAutoResetEvent = FALSE; + + m_pBlock->m_hEventAvailable = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL); + m_pBlock->m_hEventConsumed = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL); + + m_pBlock->m_hDetachEvent = WszCreateEvent(NULL, kManualResetEvent, FALSE, NULL); + + fRemap = true; + }EX_CATCH {} + EX_END_CATCH(RethrowTerminalExceptions); + + if (!fRemap) + { + return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY); + } + + STARTUPINFO startupInfo = {0}; + startupInfo.cb = sizeof (STARTUPINFOW); + + PROCESS_INFORMATION procInfo = {0}; + + // Now create the debugger + BOOL fStatus = WszCreateProcess( + lpApplicationName, + lpCommandLine, + NULL, + NULL, + FALSE, + 0, // flags + NULL, + NULL, + &startupInfo, + &procInfo); + + if (!fStatus) + { + return HRESULT_FROM_GetLastError(); + } + + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + + return S_OK; + +} + +//--------------------------------------------------------------------------------------- +// +// Close the event block +// +// Notes: +// This can be called multiple times. +//--------------------------------------------------------------------------------------- +void EventRedirectionPipeline::CloseBlock() +{ + if (m_pBlock == NULL) + { + return; + } + + // Close our handle to the IPC events. When server closes its handles, OS will free the events. + + // Setting the detach event signals the Server that this block is closing. + if (m_pBlock->m_hDetachEvent != NULL) + { + SetEvent(m_pBlock->m_hDetachEvent); + CloseHandle(m_pBlock->m_hDetachEvent); + } + + if (m_pBlock->m_hEventAvailable != NULL) + { + CloseHandle(m_pBlock->m_hEventAvailable); + } + + if (m_pBlock->m_hEventConsumed != NULL) + { + CloseHandle(m_pBlock->m_hEventConsumed); + } + + delete m_pBlock; + m_pBlock = NULL; +} + + +// Wait for a debug event +BOOL EventRedirectionPipeline::WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess) +{ + // Get debug event via Redirection from control block + DWORD res = WaitForSingleObject(m_pBlock->m_hEventAvailable, dwTimeout); + if (res == WAIT_TIMEOUT) + { + // No event is available. + return FALSE; + } + + + pEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + pEvent->dwProcessId = m_pBlock->m_dwProcessId; + pEvent->dwThreadId = m_pBlock->m_dwThreadId; + pEvent->u.Exception.dwFirstChance = m_pBlock->m_dwFirstChance; + + _ASSERTE(sizeof(m_pBlock->m_record) == sizeof(pEvent->u.Exception.ExceptionRecord)); + memcpy(&pEvent->u.Exception.ExceptionRecord, &m_pBlock->m_record, sizeof(m_pBlock->m_record)); + + // We've got an event! + return TRUE; +} + +// Continue a debug event +BOOL EventRedirectionPipeline::ContinueDebugEvent( + DWORD dwProcessId, + DWORD dwThreadId, + DWORD dwContinueStatus +) +{ + m_pBlock->m_ContinuationStatus = dwContinueStatus; + m_pBlock->m_counterConsumed++; + + // Sanity check the block. If these checks fail, then the block is corrupted (perhaps a issue in the + // extension dll feeding us the events?). + + + _ASSERTE(dwProcessId == m_pBlock->m_dwProcessId); + _ASSERTE(dwThreadId == m_pBlock->m_dwThreadId); + _ASSERTE(m_pBlock->m_counterAvailable == m_pBlock->m_counterConsumed); + + SetEvent(m_pBlock->m_hEventConsumed); + + return TRUE; +} + +// Create +HRESULT EventRedirectionPipeline::CreateProcessUnderDebugger( + MachineInfo machineInfo, + LPCWSTR lpApplicationName, + LPCWSTR lpCommandLine, + LPSECURITY_ATTRIBUTES lpProcessAttributes, + LPSECURITY_ATTRIBUTES lpThreadAttributes, + BOOL bInheritHandles, + DWORD dwCreationFlags, + LPVOID lpEnvironment, + LPCWSTR lpCurrentDirectory, + LPSTARTUPINFOW lpStartupInfo, + LPPROCESS_INFORMATION lpProcessInformation) +{ + DWORD dwRealCreationFlags = dwCreationFlags; + dwRealCreationFlags |= CREATE_SUSPENDED; + dwRealCreationFlags &= ~(DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS); + + // We must create the real process so that startup info and process information are correct. + BOOL fStatus = WszCreateProcess( + lpApplicationName, + lpCommandLine, + lpProcessAttributes, + lpThreadAttributes, + bInheritHandles, + dwRealCreationFlags, + lpEnvironment, + lpCurrentDirectory, + lpStartupInfo, + lpProcessInformation); + if (!fStatus) + { + return HRESULT_FROM_GetLastError(); + } + + // Attach the real debugger. + AttachDebuggerToTarget(m_CreateParams.Value(), lpProcessInformation->dwProcessId); + + m_dwProcessId = lpProcessInformation->dwProcessId; + + return S_OK; +} + + +// Attach +HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId) +{ + m_dwProcessId = processId; + + // Use redirected pipeline + // Spin up debugger to attach to target. + return AttachDebuggerToTarget(m_AttachParams.Value(), processId); +} + +// Detach +HRESULT EventRedirectionPipeline::DebugActiveProcessStop(DWORD processId) +{ + // Use redirected pipeline + SetEvent(m_pBlock->m_hDetachEvent); + CloseBlock(); + + // Assume detach can't fail (true on WinXP and above) + return S_OK; +} + +// Return a handle for the debuggee process. +HANDLE EventRedirectionPipeline::GetProcessHandle() +{ + _ASSERTE(m_dwProcessId != 0); + + return ::OpenProcess(PROCESS_DUP_HANDLE | + PROCESS_QUERY_INFORMATION | + PROCESS_TERMINATE | + PROCESS_VM_OPERATION | + PROCESS_VM_READ | + PROCESS_VM_WRITE | + SYNCHRONIZE, + FALSE, + m_dwProcessId); +} + +// Terminate the debuggee process. +BOOL EventRedirectionPipeline::TerminateProcess(UINT32 exitCode) +{ + _ASSERTE(m_dwProcessId != 0); + + // Get a process handle for the process ID. + HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, m_dwProcessId); + + if (hProc == NULL) + { + return FALSE; + } + + return ::TerminateProcess(hProc, exitCode); +} + +#endif // ENABLE_EVENT_REDIRECTION_PIPELINE + + |