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: WindowsPipeline.cpp
9 // Implements the native-pipeline on Windows OS.
10 //*****************************************************************************
13 #include "nativepipeline.h"
20 DWORD GetProcessId(const DEBUG_EVENT * pEvent)
22 return pEvent->dwProcessId;
24 DWORD GetThreadId(const DEBUG_EVENT * pEvent)
26 return pEvent->dwThreadId;
29 // Get exception event
30 BOOL IsExceptionEvent(const DEBUG_EVENT * pEvent, BOOL * pfFirstChance, const EXCEPTION_RECORD ** ppRecord)
32 if (pEvent->dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
34 *pfFirstChance = FALSE;
38 *pfFirstChance = pEvent->u.Exception.dwFirstChance;
39 *ppRecord = &(pEvent->u.Exception.ExceptionRecord);
44 //---------------------------------------------------------------------------------------
45 // Class serves as a connector to win32 native-debugging API.
46 class WindowsNativePipeline :
47 public INativeEventPipeline
50 WindowsNativePipeline()
52 // Default value for Win32.
57 // Call to free up the pipeline.
58 virtual void Delete();
60 virtual BOOL DebugSetProcessKillOnExit(bool fKillOnExit);
63 virtual HRESULT CreateProcessUnderDebugger(
64 MachineInfo machineInfo,
65 LPCWSTR lpApplicationName,
66 LPCWSTR lpCommandLine,
67 LPSECURITY_ATTRIBUTES lpProcessAttributes,
68 LPSECURITY_ATTRIBUTES lpThreadAttributes,
70 DWORD dwCreationFlags,
72 LPCWSTR lpCurrentDirectory,
73 LPSTARTUPINFOW lpStartupInfo,
74 LPPROCESS_INFORMATION lpProcessInformation);
77 virtual HRESULT DebugActiveProcess(MachineInfo machineInfo, DWORD processId);
80 virtual HRESULT DebugActiveProcessStop(DWORD processId);
82 virtual BOOL WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess);
84 virtual BOOL ContinueDebugEvent(
87 DWORD dwContinueStatus
90 // Return a handle for the debuggee process.
91 virtual HANDLE GetProcessHandle();
93 // Terminate the debuggee process.
94 virtual BOOL TerminateProcess(UINT32 exitCode);
96 // Resume any suspended threads
97 virtual HRESULT EnsureThreadsRunning();
100 void UpdateDebugSetProcessKillOnExit();
102 HRESULT IsRemoteDebuggerPresent(DWORD processId, BOOL* pfDebuggerPresent);
104 // Cached value from DebugSetProcessKillOnExit.
105 // This is thread-local, and impacts all debuggees on the thread.
111 // Allocate and return a pipeline object for this platform
112 INativeEventPipeline * NewPipelineForThisPlatform()
114 return new (nothrow) WindowsNativePipeline();
117 // Call to free up the pipeline.
118 void WindowsNativePipeline::Delete()
124 // set whether to kill outstanding debuggees when the debugger exits.
125 BOOL WindowsNativePipeline::DebugSetProcessKillOnExit(bool fKillOnExit)
127 // Can't call kernel32!DebugSetProcessKillOnExit until after the event thread
128 // has spawned a debuggee. So cache the value now and call it later.
129 // This bit is enforced in code:WindowsNativePipeline::UpdateDebugSetProcessKillOnExit
130 m_fKillOnExit = fKillOnExit;
134 // Enforces the bit set in code:WindowsNativePipeline::DebugSetProcessKillOnExit
135 void WindowsNativePipeline::UpdateDebugSetProcessKillOnExit()
137 #if !defined(FEATURE_CORESYSTEM)
138 // Late bind to DebugSetProcessKillOnExit - WinXP and above only
139 HModuleHolder hKernel32;
140 hKernel32 = WszLoadLibrary(W("kernel32"));
141 SIMPLIFYING_ASSUMPTION(hKernel32 != NULL);
142 if (hKernel32 == NULL)
145 typedef BOOL (*DebugSetProcessKillOnExitSig) (BOOL);
146 DebugSetProcessKillOnExitSig pDebugSetProcessKillOnExit =
147 reinterpret_cast<DebugSetProcessKillOnExitSig>(GetProcAddress(hKernel32, "DebugSetProcessKillOnExit"));
149 // If the API doesn't exist (eg. Win2k) - there isn't anything we can do, just
150 // silently ignore the request.
151 if (pDebugSetProcessKillOnExit == NULL)
154 BOOL ret = pDebugSetProcessKillOnExit(m_fKillOnExit);
156 // Not a good failure path here.
157 // 1) This shouldn't fail.
158 // 2) Even if it does, this is likely called after the debuggee
159 // has already been created, and if this API fails, most scenarios will
160 // be unaffected, so we don't want to fail the overall debugging session.
161 SIMPLIFYING_ASSUMPTION(ret);
164 // The API doesn't exit on CoreSystem, just return
169 // Create an process under the debugger.
170 HRESULT WindowsNativePipeline::CreateProcessUnderDebugger(
171 MachineInfo machineInfo,
172 LPCWSTR lpApplicationName,
173 LPCWSTR lpCommandLine,
174 LPSECURITY_ATTRIBUTES lpProcessAttributes,
175 LPSECURITY_ATTRIBUTES lpThreadAttributes,
176 BOOL bInheritHandles,
177 DWORD dwCreationFlags,
178 LPVOID lpEnvironment,
179 LPCWSTR lpCurrentDirectory,
180 LPSTARTUPINFOW lpStartupInfo,
181 LPPROCESS_INFORMATION lpProcessInformation)
183 // This is always doing Native-debugging at the OS-level.
184 dwCreationFlags |= (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS);
186 BOOL ret = ::WszCreateProcess(
196 lpProcessInformation);
199 return HRESULT_FROM_GetLastError();
202 m_dwProcessId = lpProcessInformation->dwProcessId;
203 UpdateDebugSetProcessKillOnExit();
207 // Attach the debugger to this process.
208 HRESULT WindowsNativePipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId)
211 BOOL ret = ::DebugActiveProcess(processId);
216 m_dwProcessId = processId;
217 UpdateDebugSetProcessKillOnExit();
221 hr = HRESULT_FROM_GetLastError();
223 // There are at least two scenarios in which DebugActiveProcess() returns E_INVALIDARG:
224 // 1) if the specified process doesn't exist, or
225 // 2) if the specified process already has a debugger atttached
226 // We need to distinguish these two cases in order to return the correct HR.
227 if (hr == E_INVALIDARG)
229 // Check whether a debugger is known to be already attached.
230 // Note that this API won't work on some OSes, in which case we err on the side of returning E_INVALIDARG
231 // even though a debugger may be attached. Another approach could be to assume that if
232 // OpenProcess succeeded, then DebugActiveProcess must only have failed because a debugger is
233 // attached. But I think it's better to only return the specific error code if we know for sure
235 BOOL fIsDebuggerPresent = FALSE;
236 if (SUCCEEDED(IsRemoteDebuggerPresent(processId, &fIsDebuggerPresent)))
238 if (fIsDebuggerPresent)
240 hr = CORDBG_E_DEBUGGER_ALREADY_ATTACHED;
249 // Determine (if possible) whether a debugger is attached to the target process
250 HRESULT WindowsNativePipeline::IsRemoteDebuggerPresent(DWORD processId, BOOL* pfDebuggerPresent)
252 #if !defined(FEATURE_CORESYSTEM)
254 // Get a process handle for the process ID.
255 HandleHolder hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId);
257 return HRESULT_FROM_GetLastError();
259 // Delay-bind to CheckRemoteDebuggerPresent - WinXP SP1 and above only
260 HModuleHolder hKernel32;
261 hKernel32 = WszLoadLibrary(W("kernel32"));
262 if (hKernel32 == NULL)
263 return HRESULT_FROM_GetLastError();
265 typedef BOOL (*CheckRemoteDebuggerPresentSig) (HANDLE, PBOOL);
266 CheckRemoteDebuggerPresentSig pCheckRemoteDebuggerPresent =
267 reinterpret_cast<CheckRemoteDebuggerPresentSig>(GetProcAddress(hKernel32, "CheckRemoteDebuggerPresent"));
268 if (pCheckRemoteDebuggerPresent == NULL)
269 return HRESULT_FROM_GetLastError();
271 // API exists - call it
272 if (!pCheckRemoteDebuggerPresent(hProc, pfDebuggerPresent))
273 return HRESULT_FROM_GetLastError();
278 //CoreSystem doesn't have this API
284 HRESULT WindowsNativePipeline::DebugActiveProcessStop(DWORD processId)
286 #if !defined(FEATURE_CORESYSTEM)
287 // Late-bind to DebugActiveProcessStop since it's WinXP and above only
288 HModuleHolder hKernel32;
289 hKernel32 = WszLoadLibrary(W("kernel32"));
290 if (hKernel32 == NULL)
291 return HRESULT_FROM_GetLastError();
293 typedef BOOL (*DebugActiveProcessStopSig) (DWORD);
294 DebugActiveProcessStopSig pDebugActiveProcessStop =
295 reinterpret_cast<DebugActiveProcessStopSig>(GetProcAddress(hKernel32, "DebugActiveProcessStop"));
297 // Win2K will fail here - can't find DebugActiveProcessStop
298 if (pDebugActiveProcessStop == NULL)
299 return HRESULT_FROM_GetLastError();
301 // Ok, the API exists, call it
302 if (!pDebugActiveProcessStop(processId))
304 // Detach itself failed
305 return HRESULT_FROM_GetLastError();
308 // The API exists, call it
309 if (!::DebugActiveProcessStop(processId))
311 // Detach itself failed
312 return HRESULT_FROM_GetLastError();
318 BOOL WindowsNativePipeline::WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess)
320 return ::WaitForDebugEvent(pEvent, dwTimeout);
323 BOOL WindowsNativePipeline::ContinueDebugEvent(
326 DWORD dwContinueStatus
329 return ::ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus);
332 // Return a handle for the debuggee process.
333 HANDLE WindowsNativePipeline::GetProcessHandle()
335 _ASSERTE(m_dwProcessId != 0);
337 return ::OpenProcess(PROCESS_DUP_HANDLE |
338 PROCESS_QUERY_INFORMATION |
340 PROCESS_VM_OPERATION |
348 // Terminate the debuggee process.
349 BOOL WindowsNativePipeline::TerminateProcess(UINT32 exitCode)
351 _ASSERTE(m_dwProcessId != 0);
353 // Get a process handle for the process ID.
354 HandleHolder hProc = OpenProcess(PROCESS_TERMINATE, FALSE, m_dwProcessId);
361 return ::TerminateProcess(hProc, exitCode);
364 // Resume any suspended threads (but just once)
365 HRESULT WindowsNativePipeline::EnsureThreadsRunning()
367 #ifdef FEATURE_CORESYSTEM
371 _ASSERTE(m_dwProcessId != 0);
373 // Take a snapshot of all running threads (similar to ShimProcess::QueueFakeThreadAttachEventsNativeOrder)
374 // Alternately we could return thread creation/exit in WaitForDebugEvent. But we expect this to be used
375 // very rarely, so no need to complicate more common codepaths.
376 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
379 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
380 if (hThreadSnap == INVALID_HANDLE_VALUE)
381 return HRESULT_FROM_GetLastError();
383 // HandleHolder doesn't deal with INVALID_HANDLE_VALUE, so we only assign if we have a legal value.
384 HandleHolder hSnapshotHolder(hThreadSnap);
386 // Fill in the size of the structure before using it.
387 te32.dwSize = sizeof(THREADENTRY32);
389 // Retrieve information about the first thread, and exit if unsuccessful
390 if (!Thread32First(hThreadSnap, &te32))
391 return HRESULT_FROM_GetLastError();
393 // Now walk the thread list of the system and attempt to resume any that are part of this process
394 // Ignore errors - this is a best effort (but ASSERT in CHK builds since we don't expect errors
395 // in practice - we expect the process to be frozen at a debug event, so no races etc.)
397 HRESULT hr = S_FALSE; // no thread was resumed
400 if (te32.th32OwnerProcessID == m_dwProcessId)
402 HandleHolder hThread = ::OpenThread(THREAD_SUSPEND_RESUME, FALSE, te32.th32ThreadID);
403 _ASSERTE(hThread != NULL);
406 // Resume each thread exactly once (if they were suspended multiple times,
407 // then EnsureThreadsRunning would need to be called multiple times until it
409 DWORD prevCount = ::ResumeThread(hThread);
410 _ASSERTE(prevCount >= 0);
412 hr = S_OK; // some thread was resumed
415 } while(Thread32Next(hThreadSnap, &te32));