| 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 | |