MachineDebuggerImpl.cpp revision 0b413931f58fa8e259fdf0348aca9059f58eb620
/* $Id$ */
/** @file
* VBox IMachineDebugger COM class implementation (VBoxC).
*/
/*
* Copyright (C) 2006-2013 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include "MachineDebuggerImpl.h"
#include "Global.h"
#include "ConsoleImpl.h"
#include "AutoCaller.h"
#include "Logging.h"
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
{
}
{
}
{
return BaseFinalConstruct();
}
void MachineDebugger::FinalRelease()
{
uninit();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the machine debugger object.
*
* @returns COM result indicator
* @param aParent handle of our parent object
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
for (unsigned i = 0; i < RT_ELEMENTS(maiQueuedEmExecPolicyParams); i++)
mSingleStepQueued = ~0;
mRecompileUserQueued = ~0;
mPatmEnabledQueued = ~0;
mCsamEnabledQueued = ~0;
mLogEnabledQueued = ~0;
mVirtualTimeRateQueued = ~0;
mFlushMode = false;
/* Confirm a successful initialization */
return S_OK;
}
/**
* Uninitializes the instance and sets the ready flag to FALSE.
* Called either from FinalRelease() or by the parent when it gets destroyed.
*/
void MachineDebugger::uninit()
{
LogFlowThisFunc(("\n"));
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
mFlushMode = false;
}
// IMachineDebugger properties
/////////////////////////////////////////////////////////////////////////////
/**
* Returns the current singlestepping flag.
*
* @returns COM status code
* @param a_fEnabled Where to store the result.
*/
{
AutoCaller autoCaller(this);
{
{
/** @todo */
}
}
return hrc;
}
/**
* Sets the singlestepping flag.
*
* @returns COM status code
* @param a_fEnable The new state.
*/
{
AutoCaller autoCaller(this);
{
{
/** @todo */
}
}
return hrc;
}
/**
* Internal worker for getting an EM executable policy setting.
*
* @returns COM status code.
* @param enmPolicy Which EM policy.
* @param pfEnforced Where to return the policy setting.
*/
{
AutoCaller autoCaller(this);
{
if (queueSettings())
else
{
bool fEnforced = false;
*pfEnforced = fEnforced;
}
}
return hrc;
}
/**
* Internal worker for setting an EM executable policy.
*
* @returns COM status code.
* @param enmPolicy Which policy to change.
* @param fEnforce Whether to enforce the policy or not.
*/
{
AutoCaller autoCaller(this);
{
if (queueSettings())
else
{
{
if (RT_FAILURE(vrc))
}
}
}
return hrc;
}
/**
* Returns the current recompile user mode code flag.
*
* @returns COM status code
* @param a_fEnabled address of result variable
*/
{
}
/**
* Sets the recompile user mode code flag.
*
* @returns COM status
* @param aEnable new user mode code recompile flag.
*/
{
}
/**
* Returns the current recompile supervisor code flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
}
/**
* Sets the new recompile supervisor code flag.
*
* @returns COM status code
* @param aEnable new recompile supervisor code flag
*/
{
}
/**
* Returns the current execute-all-in-IEM setting.
*
* @returns COM status code
* @param aEnabled Address of result variable.
*/
{
}
/**
* Changes the execute-all-in-IEM setting.
*
* @returns COM status code
* @param aEnable New setting.
*/
{
}
/**
* Returns the current patch manager enabled flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
return autoCaller.rc();
#ifdef VBOX_WITH_RAW_MODE
else
#endif
*aEnabled = false;
return S_OK;
}
/**
* Set the new patch manager enabled flag.
*
* @returns COM status code
* @param aEnable new patch manager enabled flag
*/
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_RAW_MODE
if (queueSettings())
{
// queue the request
return S_OK;
}
if (RT_FAILURE(vrc))
#else /* !VBOX_WITH_RAW_MODE */
if (aEnable)
#endif /* !VBOX_WITH_RAW_MODE */
return S_OK;
}
/**
* Returns the current code scanner enabled flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_RAW_MODE
else
#endif /* VBOX_WITH_RAW_MODE */
*aEnabled = false;
return S_OK;
}
/**
* Sets the new code scanner enabled flag.
*
* @returns COM status code
* @param aEnable new code scanner enabled flag
*/
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_RAW_MODE
if (queueSettings())
{
// queue the request
return S_OK;
}
if (RT_FAILURE(vrc))
#else /* !VBOX_WITH_RAW_MODE */
if (aEnable)
#endif /* !VBOX_WITH_RAW_MODE */
return S_OK;
}
/**
* Returns the log enabled / disabled status.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
#ifdef LOG_ENABLED
#else
*aEnabled = false;
#endif
return S_OK;
}
/**
* Enables or disables logging.
*
* @returns COM status code
* @param aEnabled The new code log state.
*/
{
AutoCaller autoCaller(this);
if (queueSettings())
{
// queue the request
return S_OK;
}
#ifdef LOG_ENABLED
if (RT_FAILURE(vrc))
{
/** @todo handle error code. */
}
#endif
return S_OK;
}
{
/* Make sure the VM is powered up. */
return hrc;
/* Make sure we've got a logger. */
if (!pLogger)
{
return S_OK;
}
/* Do the job. */
for (;;)
{
if (RT_SUCCESS(rc))
{
try
{
}
{
hrc = E_OUTOFMEMORY;
}
return hrc;
}
AssertReturn(rc == VERR_BUFFER_OVERFLOW, setError(VBOX_E_IPRT_ERROR, tr("%s returned %Rrc"), pszLogGetStr, rc));
/* try again with a bigger buffer. */
cbBuf *= 2;
}
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
hrc = logStringProps(RTLogGetDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", a_pbstrSettings);
return hrc;
}
{
AutoCaller autoCaller(this);
hrc = logStringProps(RTLogGetDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", a_pbstrSettings);
return hrc;
}
{
AutoCaller autoCaller(this);
return hrc;
}
{
AutoCaller autoCaller(this);
hrc = logStringProps(RTLogRelDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", a_pbstrSettings);
return hrc;
}
{
AutoCaller autoCaller(this);
hrc = logStringProps(RTLogRelDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", a_pbstrSettings);
return hrc;
}
/**
* Returns the current hardware virtualization flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
else
*aEnabled = false;
return S_OK;
}
/**
* Returns the current nested paging flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
return autoCaller.rc();
else
*aEnabled = false;
return S_OK;
}
/**
* Returns the current VPID flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
return autoCaller.rc();
else
*aEnabled = false;
return S_OK;
}
/**
* Returns the current unrestricted execution setting.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
return autoCaller.rc();
else
*aEnabled = false;
return S_OK;
}
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
{
{
/*
* Do the job and try convert the name.
*/
char szName[64];
if (RT_SUCCESS(vrc))
{
try
{
}
{
hrc = E_OUTOFMEMORY;
}
}
else
}
}
return hrc;
}
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
{
{
/*
* Do the job and try convert the name.
*/
char szVersion[256];
if (RT_SUCCESS(vrc))
{
try
{
}
{
hrc = E_OUTOFMEMORY;
}
}
else
}
}
return hrc;
}
/**
* Returns the current PAE flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
{
AutoCaller autoCaller(this);
{
}
else
*aEnabled = false;
return S_OK;
}
/**
* Returns the current virtual time rate.
*
* @returns COM status code.
* @param a_puPct Where to store the rate.
*/
{
AutoCaller autoCaller(this);
{
}
return hrc;
}
/**
* Returns the current virtual time rate.
*
* @returns COM status code.
* @param aPct Where to store the rate.
*/
{
AutoCaller autoCaller(this);
{
if (queueSettings())
else
{
{
if (RT_FAILURE(vrc))
}
}
}
return hrc;
}
/**
* Hack for getting the user mode VM handle (UVM).
*
* This is only temporary (promise) while prototyping the debugger.
*
* @returns COM status code
* @param a_u64Vm Where to store the vm handle. Since there is no
* uintptr_t in COM, we're using the max integer.
* (No, ULONG is not pointer sized!)
* @remarks The returned handle must be passed to VMR3ReleaseUVM()!
* @remarks Prior to 4.3 this returned PVM.
*/
{
AutoCaller autoCaller(this);
{
{
}
/*
* Note! ptrVM protection provided by SafeVMPtr is no long effective
* after we return from this method.
*/
}
return hrc;
}
// IMachineDebugger methods
/////////////////////////////////////////////////////////////////////////////
{
if (a_bstrCompression && *a_bstrCompression)
AutoCaller autoCaller(this);
{
{
if (RT_SUCCESS(vrc))
else
}
}
return hrc;
}
STDMETHODIMP MachineDebugger::DumpHostProcessCore(IN_BSTR a_bstrFilename, IN_BSTR a_bstrCompression)
{
}
/**
* Debug info string buffer formatter.
*/
typedef struct MACHINEDEBUGGERINOFHLP
{
/** The core info helper structure. */
/** Pointer to the buffer. */
char *pszBuf;
/** The size of the buffer. */
/** The offset into the buffer */
/** Indicates an out-of-memory condition. */
bool fOutOfMemory;
/** Pointer to a Debug info string buffer formatter. */
/**
* @callback_method_impl{FNRTSTROUTPUT}
*/
static DECLCALLBACK(size_t) MachineDebuggerInfoOutput(void *pvArg, const char *pachChars, size_t cbChars)
{
/*
* Grow the buffer if required.
*/
{
return 0;
if (cbRequired > cbBufNew)
if (RT_UNLIKELY(!pvBufNew))
{
pHlp->fOutOfMemory = true;
return 0;
}
}
/*
* Copy the bytes into the buffer and terminate it.
*/
return cbChars;
}
/**
* @interface_method_impl{DBGFINFOHLP, pfnPrintfV}
*/
static DECLCALLBACK(void) MachineDebuggerInfoPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list va)
{
}
/**
* @interface_method_impl{DBGFINFOHLP, pfnPrintf}
*/
{
}
/**
* Initializes the debug info string buffer formatter
*
* @param pHlp The help structure to init.
*/
{
pHlp->fOutOfMemory = false;
}
/**
* Deletes the debug info string buffer formatter.
* @param pHlp The helper structure to delete.
*/
{
}
{
LogFlowThisFunc(("\n"));
/*
* Validate and convert input.
*/
try
{
}
{
return E_OUTOFMEMORY;
}
/*
* Do the autocaller and lock bits.
*/
AutoCaller autoCaller(this);
{
{
/*
* Create a helper and call DBGFR3Info.
*/
if (RT_SUCCESS(vrc))
{
if (!Hlp.fOutOfMemory)
{
/*
* Convert the info string, watching out for allocation errors.
*/
try
{
}
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = E_OUTOFMEMORY;
}
else
}
}
return hrc;
}
{
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
{
{
if (RT_SUCCESS(vrc))
else
}
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
{
if (RT_SUCCESS(vrc))
else
}
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
{
if (RT_SUCCESS(vrc))
else
}
}
return hrc;
}
{
AutoCaller autoCaller(this);
{
{
if (RT_SUCCESS(vrc))
else
}
}
return hrc;
}
STDMETHODIMP MachineDebugger::ReadPhysicalMemory(LONG64 a_Address, ULONG a_cbRead, ComSafeArrayOut(BYTE, a_abData))
{
}
STDMETHODIMP MachineDebugger::WritePhysicalMemory(LONG64 a_Address, ULONG a_cbRead, ComSafeArrayIn(BYTE, a_abData))
{
}
STDMETHODIMP MachineDebugger::ReadVirtualMemory(ULONG a_idCpu, LONG64 a_Address, ULONG a_cbRead, ComSafeArrayOut(BYTE, a_abData))
{
}
STDMETHODIMP MachineDebugger::WriteVirtualMemory(ULONG a_idCpu, LONG64 a_Address, ULONG a_cbRead, ComSafeArrayIn(BYTE, a_abData))
{
}
{
LogFlowThisFunc(("\n"));
/*
* Do the autocaller and lock bits.
*/
AutoCaller autoCaller(this);
{
{
/*
* Do the job and try convert the name.
*/
/** @todo automatically load the DBGC plugins or this is a waste of time. */
char szName[64];
{
try
{
}
{
hrc = E_OUTOFMEMORY;
}
}
else
}
}
return hrc;
}
/**
* Formats a register value.
*
* This is used by both register getter methods.
*
* @returns
* @param a_pbstr The output Bstr variable.
* @param a_pValue The value to format.
* @param a_enmType The type of the value.
*/
DECLINLINE(HRESULT) formatRegisterValue(Bstr *a_pbstr, PCDBGFREGVAL a_pValue, DBGFREGVALTYPE a_enmType)
{
char szHex[160];
if (RT_UNLIKELY(cch <= 0))
return E_UNEXPECTED;
return S_OK;
}
{
/*
* Validate and convert input.
*/
try
{
}
{
return E_OUTOFMEMORY;
}
/*
* The prologue.
*/
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
{
{
/*
* Real work.
*/
if (RT_SUCCESS(vrc))
{
try
{
}
{
hrc = E_OUTOFMEMORY;
}
}
else if (vrc == VERR_DBGF_REGISTER_NOT_FOUND)
else if (vrc == VERR_INVALID_CPU_ID)
else
tr("DBGFR3RegNmQuery failed with rc=%Rrc querying register '%s' with default cpu set to %u"),
}
}
return hrc;
}
STDMETHODIMP MachineDebugger::GetRegisters(ULONG a_idCpu, ComSafeArrayOut(BSTR, a_bstrNames), ComSafeArrayOut(BSTR, a_bstrValues))
{
/*
* The prologue.
*/
LogFlowThisFunc(("\n"));
AutoCaller autoCaller(this);
{
{
/*
* Real work.
*/
if (RT_SUCCESS(vrc))
{
if (paRegs)
{
if (RT_SUCCESS(vrc))
{
try
{
{
char szHex[128];
}
}
{
hrc = E_OUTOFMEMORY;
}
}
else
}
else
hrc = E_OUTOFMEMORY;
}
else
}
}
return hrc;
}
{
}
STDMETHODIMP MachineDebugger::SetRegisters(ULONG a_idCpu, ComSafeArrayIn(IN_BSTR, a_bstrNames), ComSafeArrayIn(IN_BSTR, a_bstrValues))
{
}
{
}
/**
* Resets VM statistics.
*
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
*/
{
return S_OK;
}
/**
* Dumps VM statistics to the log.
*
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
*/
{
return S_OK;
}
/**
* Get the VM statistics in an XML format.
*
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
* @param aWithDescriptions Whether to include the descriptions.
* @param aStats The XML document containing the statistics.
*/
{
char *pszSnapshot;
if (RT_FAILURE(vrc))
/** @todo this is horribly inefficient! And it's kinda difficult to tell whether it failed...
* Must use UTF-8 or ASCII here and completely avoid these two extra copy operations.
* Until that's done, this method is kind of useless for debugger statistics GUI because
* of the amount statistics in a debug build. */
return S_OK;
}
// public methods only for internal purposes
/////////////////////////////////////////////////////////////////////////////
void MachineDebugger::flushQueuedSettings()
{
mFlushMode = true;
if (mSingleStepQueued != ~0)
{
mSingleStepQueued = ~0;
}
for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
{
}
if (mPatmEnabledQueued != ~0)
{
mPatmEnabledQueued = ~0;
}
if (mCsamEnabledQueued != ~0)
{
mCsamEnabledQueued = ~0;
}
if (mLogEnabledQueued != ~0)
{
mLogEnabledQueued = ~0;
}
if (mVirtualTimeRateQueued != ~(uint32_t)0)
{
mVirtualTimeRateQueued = ~0;
}
mFlushMode = false;
}
// private methods
/////////////////////////////////////////////////////////////////////////////
bool MachineDebugger::queueSettings() const
{
if (!mFlushMode)
{
// check if the machine is running
switch (machineState)
{
// queue the request
default:
return true;
case MachineState_Running:
case MachineState_Paused:
case MachineState_Stuck:
case MachineState_Teleporting:
break;
}
}
return false;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */