// Block and wait for the next debug event from the debuggee process. BOOL DbgTransportPipeline::WaitForDebugEvent(DEBUG_EVENT * pEvent, DWORD dwTimeout, CordbProcess * pProcess) { if (!IsTransportRunning()) { return FALSE; } // We need to wait for a debug event from the transport and the process termination event. // On Windows, process termination is communicated via a debug event as well, but that's not true for // the Mac debugging transport. DWORD cWaitSet = 2; HANDLE rghWaitSet[2]; rghWaitSet[0] = m_pTransport->GetDebugEventReadyEvent(); rghWaitSet[1] = m_hProcess; DWORD dwRet = ::WaitForMultipleObjectsEx(cWaitSet, rghWaitSet, FALSE, dwTimeout, FALSE); if (dwRet == WAIT_OBJECT_0) { // The Mac debugging transport actually transmits IPC events and not debug events. // We need to convert the IPC event to a debug event and pass it back to the caller. m_pTransport->GetNextEvent(m_pIPCEvent, CorDBIPC_BUFFER_SIZE); pEvent->dwProcessId = m_pIPCEvent->processId; _ASSERTE(m_dwProcessId == m_pIPCEvent->processId); // We are supposed to return a thread ID in the DEBUG_EVENT back to our caller. // However, we don't actually store the thread ID in the DebuggerIPCEvent anymore. Instead, // we just get a VMPTR_Thread, and so we need to find the thread ID associated with the VMPTR_Thread. pEvent->dwThreadId = 0; HRESULT hr = S_OK; EX_TRY { if (!m_pIPCEvent->vmThread.IsNull()) { pEvent->dwThreadId = pProcess->GetDAC()->TryGetVolatileOSThreadID(m_pIPCEvent->vmThread); } } EX_CATCH_HRESULT(hr); if (FAILED(hr)) { return FALSE; } // The Windows implementation stores the target address of the IPC event in the debug event. // We can do that for Mac debugging, but that would require the caller to do another cross-machine // ReadProcessMemory(). Since we have all the data in-proc already, we just store a local address. // // @dbgtodo Mac - We are using -1 as a dummy base address right now. // Currently Mac remote debugging doesn't really support multi-instance. InitEventForDebuggerNotification(pEvent, PTR_TO_CORDB_ADDRESS(reinterpret_cast<LPVOID>(-1)), m_pIPCEvent); return TRUE; }
HRESULT BuildPlatformSpecificDataTarget(MachineInfo machineInfo, DWORD processId, ShimDataTarget ** ppDataTarget) { HandleHolder hDummy; HRESULT hr = E_FAIL; ShimRemoteDataTarget * pRemoteDataTarget = NULL; DbgTransportTarget * pProxy = g_pDbgTransportTarget; DbgTransportSession * pTransport = NULL; hr = pProxy->GetTransportForProcess(processId, &pTransport, &hDummy); if (FAILED(hr)) { goto Label_Exit; } if (!pTransport->WaitForSessionToOpen(10000)) { hr = CORDBG_E_TIMEOUT; goto Label_Exit; } pRemoteDataTarget = new (nothrow) ShimRemoteDataTarget(processId, pProxy, pTransport); if (pRemoteDataTarget == NULL) { hr = E_OUTOFMEMORY; goto Label_Exit; } _ASSERTE(SUCCEEDED(hr)); *ppDataTarget = pRemoteDataTarget; pRemoteDataTarget->AddRef(); // must addref out-parameters Label_Exit: if (FAILED(hr)) { if (pRemoteDataTarget != NULL) { // The ShimRemoteDataTarget has ownership of the proxy and the transport, // so we don't need to clean them up here. delete pRemoteDataTarget; } else { if (pTransport != NULL) { pProxy->ReleaseTransport(pTransport); } } } return hr; }
// Send an IPC event to the LS. // // virtual HRESULT RemoteEventChannel::SendEventToLeftSide(DebuggerIPCEvent * pEvent, SIZE_T eventSize) { _ASSERTE(eventSize <= CorDBIPC_BUFFER_SIZE); // Delegate to the transport. The event size is ignored. return m_pTransport->SendEvent(pEvent); }
// Update the entire RS copy of the debugger control block by reading the LS copy. // // virtual HRESULT RemoteEventChannel::UpdateRightSideDCB() { _ASSERTE(m_pDCBBuffer != NULL); // Ask the transport to read the DCB from the Ls. return m_pTransport->GetDCB(m_pDCBBuffer); }
// Update a single field with a value stored in the RS copy of the DCB. // // virtual HRESULT RemoteEventChannel::UpdateLeftSideDCBField(void * rsFieldAddr, SIZE_T size) { _ASSERTE(m_pDCBBuffer != NULL); // Ask the transport to update the LS DCB. return m_pTransport->SetDCB(m_pDCBBuffer); }
// Attach the debugger to this process. HRESULT DbgTransportPipeline::DebugActiveProcess(MachineInfo machineInfo, DWORD processId) { // INativeEventPipeline has a 1:1 relationship with CordbProcess. _ASSERTE(!IsTransportRunning()); HRESULT hr = E_FAIL; m_pProxy = g_pDbgTransportTarget; // Establish a connection to the actual runtime to be debugged. hr = m_pProxy->GetTransportForProcess(processId, &m_pTransport, &m_hProcess); if (SUCCEEDED(hr)) { // TODO: Pass this timeout as a parameter all the way from debugger // Wait for the connection to become useable (or time out). if (!m_pTransport->WaitForSessionToOpen(10000)) { hr = CORDBG_E_TIMEOUT; } else { if (!m_pTransport->UseAsDebugger(&m_ticket)) { hr = CORDBG_E_DEBUGGER_ALREADY_ATTACHED; } } } if (SUCCEEDED(hr)) { m_dwProcessId = processId; m_fRunning = TRUE; } else { Dispose(); } return hr; }
// impl of interface method ICorDebugMutableDataTarget::WriteVirtual HRESULT STDMETHODCALLTYPE ShimRemoteDataTarget::WriteVirtual( CORDB_ADDRESS pAddress, const BYTE * pBuffer, ULONG32 cbRequestSize) { ReturnFailureIfStateNotOk(); HRESULT hr = E_FAIL; hr = m_pTransport->WriteMemory(reinterpret_cast<BYTE *>(CORDB_ADDRESS_TO_PTR(pAddress)), const_cast<BYTE *>(pBuffer), cbRequestSize); return hr; }
// impl of interface method ICorDebugDataTarget::ReadVirtual HRESULT STDMETHODCALLTYPE ShimRemoteDataTarget::ReadVirtual( CORDB_ADDRESS address, PBYTE pBuffer, ULONG32 cbRequestSize, ULONG32 *pcbRead) { ReturnFailureIfStateNotOk(); HRESULT hr = E_FAIL; hr = m_pTransport->ReadMemory(reinterpret_cast<BYTE *>(CORDB_ADDRESS_TO_PTR(address)), pBuffer, cbRequestSize); if (pcbRead != NULL) { *pcbRead = (SUCCEEDED(hr) ? cbRequestSize : 0); } return hr; }
// Allocate and return an old-style event channel object for this target platform. HRESULT NewEventChannelForThisPlatform(CORDB_ADDRESS pLeftSideDCB, ICorDebugMutableDataTarget * pMutableDataTarget, DWORD dwProcessId, MachineInfo machineInfo, IEventChannel ** ppEventChannel) { // @dbgtodo Mac - Consider moving all of the transport logic to one place. // Perhaps add a new function on DbgTransportManager. HandleHolder hDummy; HRESULT hr = E_FAIL; RemoteEventChannel * pEventChannel = NULL; DebuggerIPCControlBlock * pDCBBuffer = NULL; DbgTransportTarget * pProxy = g_pDbgTransportTarget; DbgTransportSession * pTransport = NULL; hr = pProxy->GetTransportForProcess(dwProcessId, &pTransport, &hDummy); if (FAILED(hr)) { goto Label_Exit; } if (!pTransport->WaitForSessionToOpen(10000)) { hr = CORDBG_E_TIMEOUT; goto Label_Exit; } pDCBBuffer = new (nothrow) DebuggerIPCControlBlock; if (pDCBBuffer == NULL) { hr = E_OUTOFMEMORY; goto Label_Exit; } pEventChannel = new (nothrow) RemoteEventChannel(pDCBBuffer, pProxy, pTransport); if (pEventChannel == NULL) { hr = E_OUTOFMEMORY; goto Label_Exit; } _ASSERTE(SUCCEEDED(hr)); *ppEventChannel = pEventChannel; Label_Exit: if (FAILED(hr)) { if (pEventChannel != NULL) { // The IEventChannel has ownership of the proxy and the transport, // so we don't need to clean them up here. delete pEventChannel; } else { if (pTransport != NULL) { pProxy->ReleaseTransport(pTransport); } if (pDCBBuffer != NULL) { delete pDCBBuffer; } } } return hr; }
// Get the reply from the LS for a previously sent IPC event. // // virtual HRESULT RemoteEventChannel::GetReplyFromLeftSide(DebuggerIPCEvent * pReplyEvent, SIZE_T eventSize) { // Delegate to the transport. m_pTransport->GetNextEvent(pReplyEvent, (DWORD)eventSize); return S_OK; }
// Get a handle to wait on after sending an IPC event to the LS. The caller should call NeedToWaitForAck() // // virtual HANDLE RemoteEventChannel::GetRightSideEventAckHandle() { // Delegate to the transport which does the real work. return m_pTransport->GetIPCEventReadyEvent(); }
// Create an process under the debugger. HRESULT DbgTransportPipeline::CreateProcessUnderDebugger( MachineInfo machineInfo, LPCWSTR lpApplicationName, LPCWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) { // INativeEventPipeline has a 1:1 relationship with CordbProcess. _ASSERTE(!IsTransportRunning()); // We don't support interop-debugging on the Mac. _ASSERTE(!(dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS))); // When we're using a transport we can't deal with creating a suspended process (we need the process to // startup in order that it can start up a transport thread and reply to our messages). _ASSERTE(!(dwCreationFlags & CREATE_SUSPENDED)); // Connect to the debugger proxy on the remote machine and ask it to create a process for us. HRESULT hr = E_FAIL; m_pProxy = g_pDbgTransportTarget; hr = m_pProxy->CreateProcess(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); if (SUCCEEDED(hr)) { // Establish a connection to the actual runtime to be debugged. hr = m_pProxy->GetTransportForProcess(lpProcessInformation->dwProcessId, &m_pTransport, &m_hProcess); if (SUCCEEDED(hr)) { // Wait for the connection to become useable (or time out). if (!m_pTransport->WaitForSessionToOpen(10000)) { hr = CORDBG_E_TIMEOUT; } else { if (!m_pTransport->UseAsDebugger(&m_ticket)) { hr = CORDBG_E_DEBUGGER_ALREADY_ATTACHED; } } } } if (SUCCEEDED(hr)) { _ASSERTE((m_hProcess != NULL) && (m_hProcess != INVALID_HANDLE_VALUE)); m_dwProcessId = lpProcessInformation->dwProcessId; // For Mac remote debugging, we don't actually have a process handle to hand back to the debugger. // Instead, we return a handle to an event as the "process handle". The Win32 event thread also waits // on this event handle, and the event will be signaled when the proxy notifies us that the process // on the remote machine is terminated. However, normally the debugger calls CloseHandle() immediately // on the "process handle" after CreateProcess() returns. Doing so causes the Win32 event thread to // continue waiting on a closed event handle, and so it will never wake up. // (In fact, in Whidbey, we also duplicate the process handle in code:CordbProcess::Init.) if (!DuplicateHandle(GetCurrentProcess(), m_hProcess, GetCurrentProcess(), &(lpProcessInformation->hProcess), 0, // ignored since we are going to pass DUPLICATE_SAME_ACCESS FALSE, DUPLICATE_SAME_ACCESS)) { hr = HRESULT_FROM_GetLastError(); } } if (SUCCEEDED(hr)) { m_fRunning = TRUE; } else { Dispose(); } return hr; }