// 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;
}
Beispiel #3
0
// 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);
}
Beispiel #4
0
// 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);
}
Beispiel #5
0
// 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;
}
Beispiel #9
0
// 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;
}
Beispiel #10
0
// 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;
}
Beispiel #11
0
// 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;
}