DBGF.cpp revision f588aa0c4cdc22831569bec5d6ce0546e44dba7f
/* $Id$ */
/** @file
* DBGF - Debugger Facility.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/** @page pg_dbgf DBGF - The Debugger Facility
*
* The purpose of the DBGF is to provide an interface for debuggers to
* manipulate the VMM without having to mess up the source code for each of
* them. The DBGF is always built in and will always work when a debugger
* attaches to the VM. The DBGF provides the basic debugger features, such as
* halting execution, handling breakpoints, single step execution, instruction
* disassembly, info querying, OS specific diggers, symbol and module
* management.
*
* The interface is working in a manner similar to the win32, linux and os2
* debugger interfaces. The interface has an asynchronous nature. This comes
* from the fact that the VMM and the Debugger are running in different threads.
* They are referred to as the "emulation thread" and the "debugger thread", or
* as the "ping thread" and the "pong thread, respectivly. (The last set of
* names comes from the use of the Ping-Pong synchronization construct from the
* RTSem API.)
*
* @see grp_dbgf
*
*
* @section sec_dbgf_scenario Usage Scenario
*
* The debugger starts by attaching to the VM. For practical reasons we limit the
* number of concurrently attached debuggers to 1 per VM. The action of
* attaching to the VM causes the VM to check and generate debug events.
*
*
* The waiting and polling is done by the DBGFEventWait() function. It will wait
* for the emulation thread to send a ping, thus indicating that there is an
* event waiting to be processed.
*
* An event can be a response to a command issued previously, the hitting of a
* the ping and must respond to the event at hand - the VMM is waiting. This
* usually means that the user of the debugger must do something, but it doesn't
* have to. The debugger is free to call any DBGF function (nearly at least)
* while processing the event.
*
* Typically the user will issue a request for the execution to be resumed, so
*
* When the user eventually terminates the debugging session or selects another
* VM, the debugger detaches from the VM. This means that breakpoints are
* disabled and that the emulation thread no longer polls for debugger commands.
*
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DBGF
#ifdef VBOX_WITH_REM
#endif
#include "DBGFInternal.h"
#include <iprt/semaphore.h>
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Sets the VMM Debug Command variable.
*
* @returns Previous command.
* @param pVM Pointer to the VM.
* @param enmCmd The command.
*/
{
if (enmCmd == DBGFCMD_NO_COMMAND)
{
}
else
{
AssertMsg(pVM->dbgf.s.enmVMMCmd == DBGFCMD_NO_COMMAND, ("enmCmd=%d enmVMMCmd=%d\n", enmCmd, pVM->dbgf.s.enmVMMCmd));
}
return rc;
}
/**
* Initializes the DBGF.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
return rc;
}
/**
* Terminates and cleans up resources allocated by the DBGF.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
int rc;
/*
* Send a termination event to any attached debugger.
*/
/* wait to become the speaker (we should already be that). */
/* now, send the event if we're the speaker. */
{
if (enmCmd == DBGFCMD_DETACH_DEBUGGER)
/* the debugger beat us to initiating the detaching. */
rc = VINF_SUCCESS;
else
{
/* ignore the command (if any). */
}
/*
* Process commands until we get a detached command.
*/
{
if (enmCmd != DBGFCMD_NO_COMMAND)
{
/* process command */
bool fResumeExecution;
}
else
{
/* wait for new command. */
if (RT_SUCCESS(rc))
}
}
}
/*
* Terminate the other bits.
*/
return VINF_SUCCESS;
}
/**
* Applies relocations to data and code managed by this
* component. This function will be called at init and
* whenever the VMM need to relocate it self inside the GC.
*
* @param pVM Pointer to the VM.
* @param offDelta Relocation delta relative to old location.
*/
{
}
/**
* Waits a little while for a debuggger to attach.
*
* @returns True is a debugger have attached.
* @param pVM Pointer to the VM.
* @param enmEvent Event.
*/
{
/*
* First a message.
*/
#ifndef RT_OS_L4
# if !defined(DEBUG) || defined(DEBUG_sandervl) || defined(DEBUG_frank) || defined(IEM_VERIFICATION_MODE)
int cWait = 10;
# else
&& ( enmEvent == DBGFEVENT_ASSERTION_HYPER
|| enmEvent == DBGFEVENT_FATAL_ERROR)
&& !RTEnvExist("VBOX_DBGF_WAIT_FOR_ATTACH")
? 10
: 150;
# endif
RTStrmPrintf(g_pStdErr, "DBGF: No debugger attached, waiting %d second%s for one to attach (event=%d)\n",
while (cWait > 0)
{
RTThreadSleep(100);
{
return true;
}
/* next */
if (!(cWait % 10))
{
}
cWait--;
}
#endif
return false;
}
/**
* Forced action callback.
* The VMM will call this from it's main loop when VM_FF_DBGF is set.
*
* The function checks and executes pending commands from the debugger.
*
* @returns VINF_SUCCESS normally.
* @returns VERR_DBGF_RAISE_FATAL_ERROR to pretend a fatal error happened.
* @param pVM Pointer to the VM.
*/
{
int rc = VINF_SUCCESS;
{
/*
* Commands?
*/
{
/*
* Process the command.
*/
bool fResumeExecution;
if (!fResumeExecution)
}
}
return rc;
}
/**
* Flag whether the event implies that we're stopped in the hypervisor code
* and have to block certain operations.
*
* @param pVM Pointer to the VM.
* @param enmEvent The event.
*/
{
switch (enmEvent)
{
case DBGFEVENT_STEPPED_HYPER:
break;
default:
break;
}
}
/**
* Try to determine the event context.
*
* @returns debug event context.
* @param pVM Pointer to the VM.
*/
{
/** @todo SMP support! */
switch (EMGetState(pVCpu))
{
case EMSTATE_RAW:
case EMSTATE_DEBUG_GUEST_RAW:
return DBGFEVENTCTX_RAW;
case EMSTATE_REM:
case EMSTATE_DEBUG_GUEST_REM:
return DBGFEVENTCTX_REM;
case EMSTATE_DEBUG_HYPER:
case EMSTATE_GURU_MEDITATION:
return DBGFEVENTCTX_HYPER;
default:
return DBGFEVENTCTX_OTHER;
}
}
/**
* The common event prologue code.
* It will set the 'stopped-in-hyper' flag, make sure someone is attached,
* and perhaps process any high priority pending actions (none yet).
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent The event to be sent.
*/
{
/** @todo SMP */
/*
* Check if a debugger is attached.
*/
{
return VERR_DBGF_NOT_ATTACHED;
}
/*
* Sync back the state from the REM.
*/
#ifdef VBOX_WITH_REM
#endif
/*
* Look thru pending commands and finish those which make sense now.
*/
//int rc = DBGFR3VMMForcedAction(pVM);
return VINF_SUCCESS;
}
/**
* Sends the event in the event buffer.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
if (RT_SUCCESS(rc))
/** @todo sync VMM -> REM after exitting the debugger. everything may change while in the debugger! */
return rc;
}
/**
* Send a generic debugger event which takes no data.
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent The event to send.
* @internal
*/
{
if (RT_FAILURE(rc))
return rc;
/*
* Send the event and process the reply communication.
*/
return dbgfR3SendEvent(pVM);
}
/**
* Send a debugger event which takes the full source file location.
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent The event to send.
* @param pszFile Source file.
* @param uLine Line number in source file.
* @param pszFunction Function name.
* @param pszFormat Message which accompanies the event.
* @param ... Message arguments.
* @internal
*/
VMMR3DECL(int) DBGFR3EventSrc(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszFile, unsigned uLine, const char *pszFunction, const char *pszFormat, ...)
{
return rc;
}
/**
* Send a debugger event which takes the full source file location.
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent The event to send.
* @param pszFile Source file.
* @param uLine Line number in source file.
* @param pszFunction Function name.
* @param pszFormat Message which accompanies the event.
* @param args Message arguments.
* @internal
*/
VMMR3DECL(int) DBGFR3EventSrcV(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszFile, unsigned uLine, const char *pszFunction, const char *pszFormat, va_list args)
{
if (RT_FAILURE(rc))
return rc;
/*
* Format the message.
*/
char *pszMessage = NULL;
char szMessage[8192];
{
pszMessage = &szMessage[0];
}
/*
* Send the event and process the reply communication.
*/
return dbgfR3SendEvent(pVM);
}
/**
* Send a debugger event which takes the two assertion messages.
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent The event to send.
* @param pszMsg1 First assertion message.
* @param pszMsg2 Second assertion message.
*/
VMMR3_INT_DECL(int) DBGFR3EventAssertion(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszMsg1, const char *pszMsg2)
{
if (RT_FAILURE(rc))
return rc;
/*
* Send the event and process the reply communication.
*/
return dbgfR3SendEvent(pVM);
}
/**
* Breakpoint was hit somewhere.
* Figure out which breakpoint it is and notify the debugger.
*
* @returns VBox status.
* @param pVM Pointer to the VM.
* @param enmEvent DBGFEVENT_BREAKPOINT_HYPER or DBGFEVENT_BREAKPOINT.
*/
{
if (RT_FAILURE(rc))
return rc;
/*
* Send the event and process the reply communication.
*/
/** @todo SMP */
if (iBp != ~0U)
else
{
/* REM breakpoints has be been searched for. */
#if 0 /** @todo get flat PC api! */
#else
/* @todo SMP support!! */
#endif
{
break;
}
}
return dbgfR3SendEvent(pVM);
}
/**
* Waits for the debugger to respond.
*
* @returns VBox status. (clearify)
* @param pVM Pointer to the VM.
*/
{
LogFlow(("dbgfR3VMMWait:\n"));
int rcRet = VINF_SUCCESS;
/*
* Waits for the debugger to reply (i.e. issue an command).
*/
for (;;)
{
/*
* Wait.
*/
uint32_t cPollHack = 1; /** @todo this interface is horrible now that we're using lots of VMR3ReqCall stuff all over DBGF. */
for (;;)
{
int rc;
{
if (RT_SUCCESS(rc))
break;
if (rc != VERR_TIMEOUT)
{
return rc;
}
}
{
cPollHack = 1;
}
{
LogFlow(("dbgfR3VMMWait: Processes requests...\n"));
if (rc == VINF_SUCCESS)
cPollHack = 1;
}
else
{
rc = VINF_SUCCESS;
if (cPollHack < 120)
cPollHack++;
}
{
switch (rc)
{
case VINF_EM_DBG_BREAKPOINT:
case VINF_EM_DBG_STEPPED:
case VINF_EM_DBG_STEP:
case VINF_EM_DBG_STOP:
break;
/* return straight away */
case VINF_EM_TERMINATE:
case VINF_EM_OFF:
return rc;
/* remember return code. */
default:
case VINF_EM_RESET:
case VINF_EM_SUSPEND:
case VINF_EM_HALT:
case VINF_EM_RESUME:
case VINF_EM_RESCHEDULE:
case VINF_EM_RESCHEDULE_REM:
case VINF_EM_RESCHEDULE_RAW:
break;
}
}
else if (RT_FAILURE(rc))
{
return rc;
}
}
/*
* Process the command.
*/
bool fResumeExecution;
if (fResumeExecution)
{
if (RT_FAILURE(rc))
else if ( rc >= VINF_EM_FIRST
&& rc <= VINF_EM_LAST
return rcRet;
}
}
}
/**
* Executes command from debugger.
* The caller is responsible for waiting or resuming execution based on the
* value returned in the *pfResumeExecution indicator.
*
* @returns VBox status. (clearify!)
* @param pVM Pointer to the VM.
* @param enmCmd The command in question.
* @param pCmdData Pointer to the command data.
* @param pfResumeExecution Where to store the resume execution / continue waiting indicator.
*/
{
bool fSendEvent;
bool fResume;
int rc = VINF_SUCCESS;
switch (enmCmd)
{
/*
* Halt is answered by an event say that we've halted.
*/
case DBGFCMD_HALT:
{
fSendEvent = true;
fResume = false;
break;
}
/*
* Resume is not answered we'll just resume execution.
*/
case DBGFCMD_GO:
{
fSendEvent = false;
fResume = true;
break;
}
/** @todo implement (and define) the rest of the commands. */
/*
* Disable breakpoints and stuff.
* Send an everythings cool event to the debugger thread and resume execution.
*/
case DBGFCMD_DETACH_DEBUGGER:
{
fSendEvent = true;
fResume = true;
break;
}
/*
* The debugger has detached successfully.
* There is no reply to this event.
*/
{
fSendEvent = false;
fResume = true;
break;
}
/*
* Single step, with trace into.
*/
case DBGFCMD_SINGLE_STEP:
{
Log2(("Single step\n"));
/** @todo SMP */
fSendEvent = false;
fResume = true;
break;
}
/*
* Default is to send an invalid command event.
*/
default:
{
fSendEvent = true;
fResume = false;
break;
}
}
/*
* Send pending event.
*/
if (fSendEvent)
{
if (RT_FAILURE(rc2))
{
*pfResumeExecution = true;
return rc2;
}
}
/*
* Return.
*/
return rc;
}
/**
* Attaches a debugger to the specified VM.
*
* Only one debugger at a time.
*
* @returns VBox status code.
* @param pUVM The user mode VM handle.
*/
{
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
/*
* Call the VM, use EMT for serialization.
*/
/** @todo SMP */
}
/**
* EMT worker for DBGFR3Attach.
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
{
Log(("dbgR3Attach: Debugger already attached\n"));
return VERR_DBGF_ALREADY_ATTACHED;
}
/*
* Create the Ping-Pong structure.
*/
/*
* Set the attached flag.
*/
return VINF_SUCCESS;
}
/**
* Detaches a debugger from the specified VM.
*
* Caller must be attached to the VM.
*
* @returns VBox status code.
* @param pUVM The user mode VM handle.
*/
{
LogFlow(("DBGFR3Detach:\n"));
int rc;
/*
* Check if attached.
*/
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
/*
* Try send the detach command.
* Keep in mind that we might be racing EMT, so, be extra careful.
*/
{
}
/*
* Wait for the OK event.
*/
/*
* Send the notification command indicating that we're really done.
*/
LogFlowFunc(("returns VINF_SUCCESS\n"));
return VINF_SUCCESS;
}
/**
* Wait for a debug event.
*
* @returns VBox status. Will not return VBOX_INTERRUPTED.
* @param pUVM The user mode VM handle.
* @param cMillies Number of millis to wait.
* @param ppEvent Where to store the event pointer.
*/
{
/*
* Check state.
*/
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
/*
* Wait.
*/
if (RT_SUCCESS(rc))
{
return VINF_SUCCESS;
}
return rc;
}
/**
* Halts VM execution.
*
* After calling this the VM isn't actually halted till an DBGFEVENT_HALT_DONE
* arrives. Until that time it's not possible to issue any new commands.
*
* @returns VBox status.
* @param pUVM The user mode VM handle.
*/
{
/*
* Check state.
*/
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
if ( enmSpeaker == RTPINGPONGSPEAKER_PONG
return VWRN_DBGF_ALREADY_HALTED;
/*
* Send command.
*/
return VINF_SUCCESS;
}
/**
* Checks if the VM is halted by the debugger.
*
* @returns True if halted.
* @returns False if not halted.
* @param pUVM The user mode VM handle.
*/
{
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
return enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED
|| enmSpeaker == RTPINGPONGSPEAKER_PONG;
}
/**
* Checks if the debugger can wait for events or not.
*
* This function is only used by lazy, multiplexing debuggers. :-)
*
* @returns True if waitable.
* @returns False if not waitable.
* @param pUVM The user mode VM handle.
*/
{
UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
VM_ASSERT_VALID_EXT_RETURN(pVM, false);
}
/**
* Resumes VM execution.
*
* There is no receipt event on this command.
*
* @returns VBox status.
* @param pUVM The user mode VM handle.
*/
{
/*
* Check state.
*/
/*
* Send the ping back to the emulation thread telling it to run.
*/
return rc;
}
/**
* Step Into.
*
* A single step event is generated from this command.
* The current implementation is not reliable, so don't rely on the event coming.
*
* @returns VBox status.
* @param pUVM The user mode VM handle.
* @param idCpu The ID of the CPU to single step on.
*/
{
/*
* Check state.
*/
/*
* Send the ping back to the emulation thread telling it to run.
*/
/** @todo SMP (idCpu) */
return rc;
}
/**
* Call this to single step programmatically.
*
* You must pass down the return code to the EM loop! That's
* where the actual single stepping take place (at least in the
* current implementation).
*
* @returns VINF_EM_DBG_STEP
*
* @param pVCpu Pointer to the VMCPU.
*
* @thread VCpu EMT
* @internal
*/
{
return VINF_EM_DBG_STEP;
}
/**
* Inject an NMI into a running VM (only VCPU 0!)
*
* @returns VBox status code.
* @param pVM Pointer to the VM.
*/
{
/** @todo Implement generic NMI injection. */
if (!HMIsEnabled(pVM))
return VERR_NOT_SUP_IN_RAW_MODE;
return VINF_SUCCESS;
}