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