1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //*****************************************************************************
5 // File: EventRedirectionPipeline.cpp
9 // Implement a native pipeline that redirects events.
10 //*****************************************************************************
13 #include "nativepipeline.h"
16 #if defined(ENABLE_EVENT_REDIRECTION_PIPELINE)
17 #include "eventredirection.h"
18 #include "eventredirectionpipeline.h"
22 EventRedirectionPipeline::EventRedirectionPipeline()
31 EventRedirectionPipeline::~EventRedirectionPipeline()
36 // Call to free up the pipeline.
37 void EventRedirectionPipeline::Delete()
42 //---------------------------------------------------------------------------------------
44 // Returns true if the Redirection is enabled.
47 // szOptions - specific Create/attach options to include in the overal format string
48 // pidTarget - pid of real debuggeee.
55 // This will spin up an auxillary debugger (windbg) and attach it to the existing
56 // process. If this is a create case, then we're attaching to a create-suspended process.
58 //---------------------------------------------------------------------------------------
59 void EventRedirectionPipeline::InitConfiguration()
61 // We need some config strings. See header for possible values.
62 m_DebuggerCmd.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectApplication);
63 m_AttachParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectAttachCmd);
64 m_CreateParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCreateCmd);
65 m_CommonParams.Init_DontUse_(CLRConfig::EXTERNAL_DbgRedirectCommonCmd);
69 // Implement INativeEventPipeline::DebugSetProcessKillOnExit
70 BOOL EventRedirectionPipeline::DebugSetProcessKillOnExit(bool fKillOnExit)
72 // Not implemented for redirection pipeline. That's ok. Redirection pipeline doesn't care.
73 // Return success so caller can assert for other pipeline types.
77 //---------------------------------------------------------------------------------------
79 // Attach a real debugger to the target.
82 // szOptions - specific Create/attach options to include in the overal format string
83 // pidTarget - pid of real debuggeee.
90 // This will spin up an auxillary debugger (windbg) and attach it to the existing
91 // process. If this is a create case, then we're attaching to a create-suspended process.
93 //---------------------------------------------------------------------------------------
94 HRESULT EventRedirectionPipeline::AttachDebuggerToTarget(LPCWSTR szOptions, DWORD pidTarget)
100 LPCWSTR lpApplicationName = NULL;
101 LPCWSTR lpCommandLine = NULL;
105 m_pBlock = new (nothrow) RedirectionBlock(); // $$ make throwing
107 ZeroMemory(m_pBlock, sizeof(RedirectionBlock));
110 m_pBlock->m_versionCookie = EVENT_REDIRECTION_CURRENT_VERSION;
112 s.Printf(m_CommonParams.Value(), GetCurrentProcessId(), m_pBlock, szOptions, pidTarget);
113 lpCommandLine = s.GetUnicode();
116 lpApplicationName = m_DebuggerCmd.Value(); // eg, something like L"c:\\debuggers_amd64\\windbg.exe";
118 // Initialize events.
119 const BOOL kManualResetEvent = TRUE;
120 const BOOL kAutoResetEvent = FALSE;
122 m_pBlock->m_hEventAvailable = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL);
123 m_pBlock->m_hEventConsumed = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL);
125 m_pBlock->m_hDetachEvent = WszCreateEvent(NULL, kManualResetEvent, FALSE, NULL);
129 EX_END_CATCH(RethrowTerminalExceptions);
133 return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
136 STARTUPINFO startupInfo = {0};
137 startupInfo.cb = sizeof (STARTUPINFOW);
139 PROCESS_INFORMATION procInfo = {0};
141 // Now create the debugger
142 BOOL fStatus = WszCreateProcess(
156 return HRESULT_FROM_GetLastError();
159 CloseHandle(procInfo.hProcess);
160 CloseHandle(procInfo.hThread);
166 //---------------------------------------------------------------------------------------
168 // Close the event block
171 // This can be called multiple times.
172 //---------------------------------------------------------------------------------------
173 void EventRedirectionPipeline::CloseBlock()
175 if (m_pBlock == NULL)
180 // Close our handle to the IPC events. When server closes its handles, OS will free the events.
182 // Setting the detach event signals the Server that this block is closing.
183 if (m_pBlock->m_hDetachEvent != NULL)
185 SetEvent(m_pBlock->m_hDetachEvent);
186 CloseHandle(m_pBlock->m_hDetachEvent);
189 if (m_pBlock->m_hEventAvailable != NULL)
191 CloseHandle(m_pBlock->m_hEventAvailable);
194 if (m_pBlock->m_hEventConsumed != NULL)
196 CloseHandle(m_pBlock->m_hEventConsumed);
204 // Wait for a debug event
205 BOOL EventRedirectionPipeline::WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess)
207 // Get debug event via Redirection from control block
208 DWORD res = WaitForSingleObject(m_pBlock->m_hEventAvailable, dwTimeout);
209 if (res == WAIT_TIMEOUT)
211 // No event is available.
216 pEvent->dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
217 pEvent->dwProcessId = m_pBlock->m_dwProcessId;
218 pEvent->dwThreadId = m_pBlock->m_dwThreadId;
219 pEvent->u.Exception.dwFirstChance = m_pBlock->m_dwFirstChance;
221 _ASSERTE(sizeof(m_pBlock->m_record) == sizeof(pEvent->u.Exception.ExceptionRecord));
222 memcpy(&pEvent->u.Exception.ExceptionRecord, &m_pBlock->m_record, sizeof(m_pBlock->m_record));
224 // We've got an event!
228 // Continue a debug event
229 BOOL EventRedirectionPipeline::ContinueDebugEvent(
232 DWORD dwContinueStatus
235 m_pBlock->m_ContinuationStatus = dwContinueStatus;
236 m_pBlock->m_counterConsumed++;
238 // Sanity check the block. If these checks fail, then the block is corrupted (perhaps a issue in the
239 // extension dll feeding us the events?).
242 _ASSERTE(dwProcessId == m_pBlock->m_dwProcessId);
243 _ASSERTE(dwThreadId == m_pBlock->m_dwThreadId);
244 _ASSERTE(m_pBlock->m_counterAvailable == m_pBlock->m_counterConsumed);
246 SetEvent(m_pBlock->m_hEventConsumed);
252 HRESULT EventRedirectionPipeline::CreateProcessUnderDebugger(
253 MachineInfo machineInfo,
254 LPCWSTR lpApplicationName,
255 LPCWSTR lpCommandLine,
256 LPSECURITY_ATTRIBUTES lpProcessAttributes,
257 LPSECURITY_ATTRIBUTES lpThreadAttributes,
258 BOOL bInheritHandles,
259 DWORD dwCreationFlags,
260 LPVOID lpEnvironment,
261 LPCWSTR lpCurrentDirectory,
262 LPSTARTUPINFOW lpStartupInfo,
263 LPPROCESS_INFORMATION lpProcessInformation)
265 DWORD dwRealCreationFlags = dwCreationFlags;
266 dwRealCreationFlags |= CREATE_SUSPENDED;
267 dwRealCreationFlags &= ~(DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS);
269 // We must create the real process so that startup info and process information are correct.
270 BOOL fStatus = WszCreateProcess(
280 lpProcessInformation);
283 return HRESULT_FROM_GetLastError();
286 // Attach the real debugger.
287 AttachDebuggerToTarget(m_CreateParams.Value(), lpProcessInformation->dwProcessId);
289 m_dwProcessId = lpProcessInformation->dwProcessId;
296 HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId)
298 m_dwProcessId = processId;
300 // Use redirected pipeline
301 // Spin up debugger to attach to target.
302 return AttachDebuggerToTarget(m_AttachParams.Value(), processId);
306 HRESULT EventRedirectionPipeline::DebugActiveProcessStop(DWORD processId)
308 // Use redirected pipeline
309 SetEvent(m_pBlock->m_hDetachEvent);
312 // Assume detach can't fail (true on WinXP and above)
316 // Return a handle for the debuggee process.
317 HANDLE EventRedirectionPipeline::GetProcessHandle()
319 _ASSERTE(m_dwProcessId != 0);
321 return ::OpenProcess(PROCESS_DUP_HANDLE |
322 PROCESS_QUERY_INFORMATION |
324 PROCESS_VM_OPERATION |
332 // Terminate the debuggee process.
333 BOOL EventRedirectionPipeline::TerminateProcess(UINT32 exitCode)
335 _ASSERTE(m_dwProcessId != 0);
337 // Get a process handle for the process ID.
338 HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, m_dwProcessId);
345 return ::TerminateProcess(hProc, exitCode);
348 #endif // ENABLE_EVENT_REDIRECTION_PIPELINE