Merge pull request #2270 from swgillespie/gc_performance_tests
[platform/upstream/coreclr.git] / src / debug / di / shimremotedatatarget.cpp
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4 //
5 //*****************************************************************************
6
7 // 
8 // File: ShimRemoteDataTarget.cpp
9 //
10 //*****************************************************************************
11 #include "stdafx.h"
12 #include "safewrap.h"
13
14 #include "check.h" 
15
16 #include <limits.h>
17
18 #include "shimpriv.h"
19 #include "shimdatatarget.h"
20
21 #include "dbgtransportsession.h"
22 #include "dbgtransportmanager.h"
23
24
25 class ShimRemoteDataTarget : public ShimDataTarget
26 {
27 public:
28     ShimRemoteDataTarget(DWORD processId, DbgTransportTarget * pProxy, DbgTransportSession * pTransport);
29
30     virtual ~ShimRemoteDataTarget();
31
32     virtual void Dispose();
33
34     //
35     // ICorDebugMutableDataTarget.
36     //
37
38     virtual HRESULT STDMETHODCALLTYPE GetPlatform( 
39         CorDebugPlatform *pPlatform);
40
41     virtual HRESULT STDMETHODCALLTYPE ReadVirtual( 
42         CORDB_ADDRESS address,
43         BYTE * pBuffer,
44         ULONG32 request,
45         ULONG32 *pcbRead);
46
47     virtual HRESULT STDMETHODCALLTYPE WriteVirtual( 
48         CORDB_ADDRESS address,
49         const BYTE * pBuffer,
50         ULONG32 request);
51
52     virtual HRESULT STDMETHODCALLTYPE GetThreadContext(
53         DWORD dwThreadID,
54         ULONG32 contextFlags,
55         ULONG32 contextSize,
56         BYTE * context);
57
58     virtual HRESULT STDMETHODCALLTYPE SetThreadContext(
59         DWORD dwThreadID,
60         ULONG32 contextSize,
61         const BYTE * context);
62
63     virtual HRESULT STDMETHODCALLTYPE ContinueStatusChanged(
64         DWORD dwThreadId,
65         CORDB_CONTINUE_STATUS dwContinueStatus);
66
67     virtual HRESULT STDMETHODCALLTYPE VirtualUnwind(
68         DWORD threadId, ULONG32 contextSize, PBYTE context);
69
70 private:
71     DbgTransportTarget  * m_pProxy;
72     DbgTransportSession * m_pTransport;
73 };
74
75
76 // Helper macro to check for failure conditions at the start of data-target methods.
77 #define ReturnFailureIfStateNotOk() \
78     if (m_hr != S_OK) \
79     { \
80         return m_hr; \
81     }
82
83 //---------------------------------------------------------------------------------------
84 //
85 // This is the ctor for ShimRemoteDataTarget. 
86 //
87 // Arguments:
88 //      processId  - pid of live process on the remote machine
89 //      pProxy     - connection to the debugger proxy
90 //      pTransport - connection to the debuggee process
91 //
92
93 ShimRemoteDataTarget::ShimRemoteDataTarget(DWORD processId, 
94                                            DbgTransportTarget * pProxy, 
95                                            DbgTransportSession * pTransport)
96 {
97     m_ref = 0;
98
99     m_processId = processId;
100     m_pProxy = pProxy;
101     m_pTransport = pTransport;
102
103     m_hr = S_OK;
104
105     m_fpContinueStatusChanged = NULL;
106     m_pContinueStatusChangedUserData = NULL;
107 }
108
109 //---------------------------------------------------------------------------------------
110 //
111 // dtor for ShimRemoteDataTarget
112 //
113
114 ShimRemoteDataTarget::~ShimRemoteDataTarget()
115 {
116     Dispose();
117 }
118
119 //---------------------------------------------------------------------------------------
120 //
121 // Dispose all resources and neuter the object.
122 //
123 // Notes:
124 //    Release all resources (such as the connections to the debugger proxy and the debuggee process).
125 //    May be called multiple times.
126 //    All other non-trivial APIs (eg, not IUnknown) will fail after this.
127 //
128
129 void ShimRemoteDataTarget::Dispose()
130 {
131     if (m_pTransport != NULL)
132     {
133         m_pProxy->ReleaseTransport(m_pTransport);
134     }
135
136     m_pTransport = NULL;
137     m_hr = CORDBG_E_OBJECT_NEUTERED;
138 }
139
140 //---------------------------------------------------------------------------------------
141 //
142 // Construction method for data-target
143 //
144 // Arguments:
145 //      machineInfo  - (input) the IP address of the remote machine and the port number of the debugger proxy
146 //      processId    - (input) live OS process ID to build a data-target for.
147 //      ppDataTarget - (output) new data-target instance. This gets addreffed.
148 //
149 // Return Value:
150 //    S_OK on success.
151 //
152 // Assumptions:
153 //    pid is for a process on the remote machine specified by the IP address in machineInfo
154 //    Caller must release *ppDataTarget.
155 //
156
157 HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo,
158                                         DWORD processId, 
159                                         ShimDataTarget ** ppDataTarget)
160 {
161     HandleHolder hDummy;
162     HRESULT hr = E_FAIL;
163
164     ShimRemoteDataTarget * pRemoteDataTarget = NULL;
165     DbgTransportTarget *   pProxy = g_pDbgTransportTarget;
166     DbgTransportSession *  pTransport = NULL;
167
168     hr = pProxy->GetTransportForProcess(processId, &pTransport, &hDummy);
169     if (FAILED(hr))
170     {
171         goto Label_Exit;
172     }
173
174     if (!pTransport->WaitForSessionToOpen(10000))
175     {
176         hr = CORDBG_E_TIMEOUT;
177         goto Label_Exit;
178     }
179
180     pRemoteDataTarget = new (nothrow) ShimRemoteDataTarget(processId, pProxy, pTransport);
181     if (pRemoteDataTarget == NULL)
182     {
183         hr = E_OUTOFMEMORY;
184         goto Label_Exit;
185     }
186
187     _ASSERTE(SUCCEEDED(hr));
188     *ppDataTarget = pRemoteDataTarget;
189     pRemoteDataTarget->AddRef(); // must addref out-parameters
190
191 Label_Exit:
192     if (FAILED(hr))
193     {
194         if (pRemoteDataTarget != NULL)
195         {
196             // The ShimRemoteDataTarget has ownership of the proxy and the transport, 
197             // so we don't need to clean them up here.
198             delete pRemoteDataTarget;
199         }
200         else
201         {
202             if (pTransport != NULL)
203             {
204                 pProxy->ReleaseTransport(pTransport);
205             }
206         }
207     }
208
209     return hr;
210 }
211
212 // impl of interface method ICorDebugDataTarget::GetPlatform
213 HRESULT STDMETHODCALLTYPE
214 ShimRemoteDataTarget::GetPlatform( 
215         CorDebugPlatform *pPlatform)
216 {
217 #ifdef FEATURE_PAL
218      #if defined(DBG_TARGET_X86)
219          *pPlatform = CORDB_PLATFORM_POSIX_X86;
220      #elif defined(DBG_TARGET_AMD64)
221          *pPlatform = CORDB_PLATFORM_POSIX_AMD64;
222      #elif defined(DBG_TARGET_ARM)
223          *pPlatform = CORDB_PLATFORM_POSIX_ARM;
224      #elif defined(DBG_TARGET_ARM64)
225          *pPlatform = CORDB_PLATFORM_POSIX_ARM64;
226      #else
227          #error Unknown Processor.
228      #endif
229 #else
230     #if defined(DBG_TARGET_X86)
231         *pPlatform = CORDB_PLATFORM_WINDOWS_X86;
232     #elif defined(DBG_TARGET_AMD64)
233         *pPlatform = CORDB_PLATFORM_WINDOWS_AMD64;
234     #elif defined(DBG_TARGET_ARM)
235         *pPlatform = CORDB_PLATFORM_WINDOWS_ARM;
236     #elif defined(DBG_TARGET_ARM64)
237         *pPlatform = CORDB_PLATFORM_WINDOWS_ARM64;
238     #else
239         #error Unknown Processor.
240     #endif
241 #endif
242
243     return S_OK;
244 }
245
246 // impl of interface method ICorDebugDataTarget::ReadVirtual
247 HRESULT STDMETHODCALLTYPE
248 ShimRemoteDataTarget::ReadVirtual( 
249     CORDB_ADDRESS address,
250     PBYTE pBuffer,
251     ULONG32 cbRequestSize,
252     ULONG32 *pcbRead)
253 {
254     ReturnFailureIfStateNotOk();
255
256     HRESULT hr = E_FAIL;
257     hr = m_pTransport->ReadMemory(reinterpret_cast<BYTE *>(CORDB_ADDRESS_TO_PTR(address)), 
258                                   pBuffer, 
259                                   cbRequestSize);
260     if (pcbRead != NULL)
261     {
262         *pcbRead = (SUCCEEDED(hr) ? cbRequestSize : 0);
263     }
264     return hr;
265 }
266
267 // impl of interface method ICorDebugMutableDataTarget::WriteVirtual
268 HRESULT STDMETHODCALLTYPE
269 ShimRemoteDataTarget::WriteVirtual( 
270     CORDB_ADDRESS pAddress,
271     const BYTE * pBuffer,
272     ULONG32 cbRequestSize)
273 {
274     ReturnFailureIfStateNotOk();
275
276     HRESULT hr = E_FAIL;
277     hr = m_pTransport->WriteMemory(reinterpret_cast<BYTE *>(CORDB_ADDRESS_TO_PTR(pAddress)), 
278                                    const_cast<BYTE *>(pBuffer), 
279                                    cbRequestSize);
280     return hr;
281 }
282
283 // impl of interface method ICorDebugMutableDataTarget::GetThreadContext
284 HRESULT STDMETHODCALLTYPE
285 ShimRemoteDataTarget::GetThreadContext(
286     DWORD dwThreadID,
287     ULONG32 contextFlags,
288     ULONG32 contextSize,
289     BYTE * pContext)
290 {
291     ReturnFailureIfStateNotOk();
292         
293     // GetThreadContext() is currently not implemented in ShimRemoteDataTarget, which is used with our pipe transport 
294     // (FEATURE_DBGIPC_TRANSPORT_DI). Pipe transport is used on POSIX system, but occasionally we can turn it on for Windows for testing,
295     // and then we'd like to have same behavior as on POSIX system (zero context).
296     //
297     // We don't have a good way to implement GetThreadContext() in ShimRemoteDataTarget yet, because we have no way to convert a thread ID to a 
298     // thread handle.  The function to do the conversion is OpenThread(), which is not implemented in PAL. Even if we had a handle, PAL implementation 
299     // of GetThreadContext() is very limited and doesn't work when we're not attached with ptrace. 
300     // Instead, we just zero out the seed CONTEXT for the stackwalk.  This tells the stackwalker to
301     // start the stackwalk with the first explicit frame.  This won't work when we do native debugging, 
302     // but that won't happen on the POSIX systems since they don't support native debugging.
303     ZeroMemory(pContext, contextSize);
304     return E_NOTIMPL;
305 }
306
307 // impl of interface method ICorDebugMutableDataTarget::SetThreadContext
308 HRESULT STDMETHODCALLTYPE
309 ShimRemoteDataTarget::SetThreadContext(
310     DWORD dwThreadID,
311     ULONG32 contextSize,
312     const BYTE * pContext)
313 {
314     ReturnFailureIfStateNotOk();
315
316     // ICorDebugDataTarget::GetThreadContext() and ICorDebugDataTarget::SetThreadContext() are currently only 
317     // required for interop-debugging and inspection of floating point registers, both of which are not 
318     // implemented on Mac.
319     _ASSERTE(!"The remote data target doesn't know how to set a thread's CONTEXT.");
320     return E_NOTIMPL;
321 }
322
323 // Public implementation of ICorDebugMutableDataTarget::ContinueStatusChanged
324 HRESULT STDMETHODCALLTYPE
325 ShimRemoteDataTarget::ContinueStatusChanged(
326     DWORD dwThreadId,
327     CORDB_CONTINUE_STATUS dwContinueStatus)
328 {
329     ReturnFailureIfStateNotOk();
330
331     _ASSERTE(!"ShimRemoteDataTarget::ContinueStatusChanged() is called unexpectedly");
332     if (m_fpContinueStatusChanged != NULL)
333     {
334         return m_fpContinueStatusChanged(m_pContinueStatusChangedUserData, dwThreadId, dwContinueStatus);
335     }
336     return E_NOTIMPL;
337 }
338
339 //---------------------------------------------------------------------------------------
340 //
341 // Unwind the stack to the next frame.
342 //
343 // Return Value: 
344 //     context filled in with the next frame
345 //
346 HRESULT STDMETHODCALLTYPE 
347 ShimRemoteDataTarget::VirtualUnwind(DWORD threadId, ULONG32 contextSize, PBYTE context)
348 {
349     return m_pTransport->VirtualUnwind(threadId, contextSize, context);
350 }