Merge pull request #11243 from sdmaclea/PR-ARM64-LDP-ALLOW-GCREFS
[platform/upstream/coreclr.git] / src / debug / di / eventredirectionpipeline.cpp
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
6 // 
7
8 //
9 // Implement a native pipeline that redirects events.
10 //*****************************************************************************
11
12 #include "stdafx.h"
13 #include "nativepipeline.h"
14 #include "sstring.h"
15
16 #if defined(ENABLE_EVENT_REDIRECTION_PIPELINE)
17 #include "eventredirection.h"
18 #include "eventredirectionpipeline.h"
19
20
21 // Constructor
22 EventRedirectionPipeline::EventRedirectionPipeline()
23 {
24     m_pBlock = NULL;
25     m_dwProcessId = 0;
26
27     InitConfiguration();
28 }
29
30 // Dtor
31 EventRedirectionPipeline::~EventRedirectionPipeline()
32 {
33     CloseBlock();
34 }
35
36 // Call to free up the pipeline.
37 void EventRedirectionPipeline::Delete()
38 {
39     delete this;
40 }
41
42 //---------------------------------------------------------------------------------------
43 //
44 // Returns true if the Redirection is enabled.
45 //
46 // Arguments:
47 //    szOptions - specific Create/attach options to include in the overal format string
48 //    pidTarget - pid of real debuggeee.
49 //
50 // Return Value:
51 //    S_OK on success.
52 //
53 //
54 // Notes:
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.
57 //
58 //---------------------------------------------------------------------------------------
59 void EventRedirectionPipeline::InitConfiguration()
60 {
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);
66 }
67
68
69 // Implement INativeEventPipeline::DebugSetProcessKillOnExit
70 BOOL EventRedirectionPipeline::DebugSetProcessKillOnExit(bool fKillOnExit)
71 {
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.
74     return TRUE;
75 }
76
77 //---------------------------------------------------------------------------------------
78 //
79 // Attach a real debugger to the target.
80 //
81 // Arguments:
82 //    szOptions - specific Create/attach options to include in the overal format string
83 //    pidTarget - pid of real debuggeee.
84 //
85 // Return Value:
86 //    S_OK on success.
87 //
88 //
89 // Notes:
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.
92 //
93 //---------------------------------------------------------------------------------------
94 HRESULT EventRedirectionPipeline::AttachDebuggerToTarget(LPCWSTR szOptions, DWORD pidTarget)
95 {
96     SString s;
97
98     BOOL fRemap = false;
99
100     LPCWSTR lpApplicationName = NULL;
101     LPCWSTR lpCommandLine = NULL;
102
103     EX_TRY
104     {
105         m_pBlock = new (nothrow) RedirectionBlock(); // $$ make throwing   
106
107         ZeroMemory(m_pBlock, sizeof(RedirectionBlock));
108
109         // Initialize
110         m_pBlock->m_versionCookie = EVENT_REDIRECTION_CURRENT_VERSION;
111
112         s.Printf(m_CommonParams.Value(), GetCurrentProcessId(), m_pBlock, szOptions, pidTarget);
113         lpCommandLine = s.GetUnicode();
114
115         
116         lpApplicationName = m_DebuggerCmd.Value(); // eg, something like L"c:\\debuggers_amd64\\windbg.exe";
117
118         // Initialize events.
119         const BOOL kManualResetEvent = TRUE;
120         const BOOL kAutoResetEvent = FALSE;
121
122         m_pBlock->m_hEventAvailable = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL);
123         m_pBlock->m_hEventConsumed  = WszCreateEvent(NULL, kAutoResetEvent, FALSE, NULL);
124
125         m_pBlock->m_hDetachEvent = WszCreateEvent(NULL, kManualResetEvent, FALSE, NULL);
126
127         fRemap = true;
128     }EX_CATCH {}
129     EX_END_CATCH(RethrowTerminalExceptions);
130     
131     if (!fRemap)
132     {
133         return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
134     }
135
136     STARTUPINFO startupInfo = {0};
137     startupInfo.cb = sizeof (STARTUPINFOW);
138
139     PROCESS_INFORMATION procInfo = {0};
140
141     // Now create the debugger
142     BOOL fStatus = WszCreateProcess(
143         lpApplicationName,
144         lpCommandLine,
145         NULL,
146         NULL,
147         FALSE,
148         0, // flags
149         NULL,
150         NULL,
151         &startupInfo,
152         &procInfo);
153     
154     if (!fStatus)
155     {
156         return HRESULT_FROM_GetLastError();
157     }
158
159     CloseHandle(procInfo.hProcess);
160     CloseHandle(procInfo.hThread);
161
162     return S_OK;
163
164 }
165
166 //---------------------------------------------------------------------------------------
167 //
168 // Close the event block
169 //
170 // Notes:
171 //     This can be called multiple times.
172 //---------------------------------------------------------------------------------------
173 void EventRedirectionPipeline::CloseBlock()
174 {
175     if (m_pBlock == NULL)
176     {
177         return;
178     }
179
180     // Close our handle to the IPC events. When server closes its handles, OS will free the events.
181
182     // Setting the detach event signals the Server that this block is closing.
183     if (m_pBlock->m_hDetachEvent != NULL)
184     {
185         SetEvent(m_pBlock->m_hDetachEvent);
186         CloseHandle(m_pBlock->m_hDetachEvent);
187     }
188
189     if (m_pBlock->m_hEventAvailable != NULL)
190     {
191         CloseHandle(m_pBlock->m_hEventAvailable);
192     }
193
194     if (m_pBlock->m_hEventConsumed != NULL)
195     {
196         CloseHandle(m_pBlock->m_hEventConsumed);
197     }
198
199     delete m_pBlock;
200     m_pBlock = NULL;
201 }
202
203
204 // Wait for a debug event
205 BOOL EventRedirectionPipeline::WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess)
206 {
207     // Get debug event via Redirection from control block
208     DWORD res = WaitForSingleObject(m_pBlock->m_hEventAvailable, dwTimeout);
209     if (res == WAIT_TIMEOUT)
210     {
211         // No event is available.
212         return FALSE;
213     }
214
215     
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;
220     
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));
223
224     // We've got an event!
225     return TRUE;
226 }
227
228 // Continue a debug event
229 BOOL EventRedirectionPipeline::ContinueDebugEvent(
230   DWORD dwProcessId,
231   DWORD dwThreadId,
232   DWORD dwContinueStatus
233 )
234 {
235     m_pBlock->m_ContinuationStatus = dwContinueStatus;
236     m_pBlock->m_counterConsumed++;
237
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?). 
240
241
242     _ASSERTE(dwProcessId == m_pBlock->m_dwProcessId);
243     _ASSERTE(dwThreadId  == m_pBlock->m_dwThreadId);
244     _ASSERTE(m_pBlock->m_counterAvailable == m_pBlock->m_counterConsumed);
245
246     SetEvent(m_pBlock->m_hEventConsumed);
247
248     return TRUE;
249 }
250
251 // Create
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)
264 {
265     DWORD dwRealCreationFlags = dwCreationFlags;
266     dwRealCreationFlags |= CREATE_SUSPENDED;
267     dwRealCreationFlags &= ~(DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS);
268
269     // We must create the real process so that startup info and process information are correct.
270     BOOL fStatus = WszCreateProcess(
271             lpApplicationName,
272             lpCommandLine,
273             lpProcessAttributes,
274             lpThreadAttributes,
275             bInheritHandles,
276             dwRealCreationFlags,
277             lpEnvironment,
278             lpCurrentDirectory,
279             lpStartupInfo,
280             lpProcessInformation);
281     if (!fStatus)
282     {
283         return HRESULT_FROM_GetLastError();
284     }
285
286     // Attach the real debugger.    
287     AttachDebuggerToTarget(m_CreateParams.Value(), lpProcessInformation->dwProcessId);
288
289     m_dwProcessId = lpProcessInformation->dwProcessId;
290
291     return S_OK;
292 }
293
294
295 // Attach
296 HRESULT EventRedirectionPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId)
297 {
298     m_dwProcessId = processId;
299
300     // Use redirected pipeline
301     // Spin up debugger to attach to target.
302     return AttachDebuggerToTarget(m_AttachParams.Value(), processId);
303 }
304
305 // Detach
306 HRESULT EventRedirectionPipeline::DebugActiveProcessStop(DWORD processId)
307 {   
308     // Use redirected pipeline
309     SetEvent(m_pBlock->m_hDetachEvent);
310     CloseBlock();
311
312     // Assume detach can't fail (true on WinXP and above)
313     return S_OK;
314 }
315
316 // Return a handle for the debuggee process.
317 HANDLE EventRedirectionPipeline::GetProcessHandle()
318 {
319     _ASSERTE(m_dwProcessId != 0);
320
321     return ::OpenProcess(PROCESS_DUP_HANDLE        |
322                          PROCESS_QUERY_INFORMATION |
323                          PROCESS_TERMINATE         |
324                          PROCESS_VM_OPERATION      |
325                          PROCESS_VM_READ           |
326                          PROCESS_VM_WRITE          |
327                          SYNCHRONIZE,
328                          FALSE,
329                          m_dwProcessId);
330 }
331
332 // Terminate the debuggee process.
333 BOOL EventRedirectionPipeline::TerminateProcess(UINT32 exitCode)
334 {
335     _ASSERTE(m_dwProcessId != 0);
336
337     // Get a process handle for the process ID.
338     HANDLE hProc = OpenProcess(PROCESS_TERMINATE, FALSE, m_dwProcessId);
339
340     if (hProc == NULL)
341     {
342         return FALSE;
343     }
344
345     return ::TerminateProcess(hProc, exitCode);
346 }
347
348 #endif // ENABLE_EVENT_REDIRECTION_PIPELINE
349
350