MachineDebuggerImpl.cpp revision da6c95666f29b00894dfb9d110b74d9e1c4f1d46
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync/* $Id$ */
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync/** @file
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * VBox IMachineDebugger COM class implementation (VBoxC).
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync */
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync/*
c58f1213e628a545081c70e26c6b67a841cff880vboxsync * Copyright (C) 2006-2013 Oracle Corporation
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync *
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * available from http://www.virtualbox.org. This file is free software;
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * you can redistribute it and/or modify it under the terms of the GNU
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * General Public License (GPL) as published by the Free Software
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync */
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync/*******************************************************************************
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync* Header Files *
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync*******************************************************************************/
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include "MachineDebuggerImpl.h"
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include "Global.h"
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include "ConsoleImpl.h"
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include "AutoCaller.h"
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include "Logging.h"
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/em.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/patm.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/csam.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/uvm.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/tm.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/vmm/hm.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <VBox/err.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync#include <iprt/cpp/utils.h>
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync// constructor / destructor
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync/////////////////////////////////////////////////////////////////////////////
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsyncMachineDebugger::MachineDebugger()
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync : mParent(NULL)
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync{
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync}
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsyncMachineDebugger::~MachineDebugger()
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync{
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync}
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsyncHRESULT MachineDebugger::FinalConstruct()
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync{
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync unconst(mParent) = NULL;
14fd2cc4f028c3f5e41786f13f035762a35533cfvboxsync return BaseFinalConstruct();
}
void MachineDebugger::FinalRelease()
{
uninit();
BaseFinalRelease();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the machine debugger object.
*
* @returns COM result indicator
* @param aParent handle of our parent object
*/
HRESULT MachineDebugger::init (Console *aParent)
{
LogFlowThisFunc(("aParent=%p\n", aParent));
ComAssertRet(aParent, E_INVALIDARG);
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
AssertReturn(autoInitSpan.isOk(), E_FAIL);
unconst(mParent) = aParent;
for (unsigned i = 0; i < RT_ELEMENTS(maiQueuedEmExecPolicyParams); i++)
maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
mSingleStepQueued = ~0;
mRecompileUserQueued = ~0;
mRecompileSupervisorQueued = ~0;
mPatmEnabledQueued = ~0;
mCsamEnabledQueued = ~0;
mLogEnabledQueued = ~0;
mVirtualTimeRateQueued = ~0;
mFlushMode = false;
/* Confirm a successful initialization */
autoInitSpan.setSucceeded();
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;
unconst(mParent) = NULL;
mFlushMode = false;
}
// IMachineDebugger properties
/////////////////////////////////////////////////////////////////////////////
/**
* Returns the current singlestepping flag.
*
* @returns COM status code
* @param a_fEnabled Where to store the result.
*/
HRESULT MachineDebugger::getSingleStep(BOOL *aSingleStep)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/** @todo */
ReturnComNotImplemented();
}
return hrc;
}
/**
* Sets the singlestepping flag.
*
* @returns COM status code
* @param a_fEnable The new state.
*/
HRESULT MachineDebugger::setSingleStep(BOOL aSingleStep)
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/** @todo */
ReturnComNotImplemented();
}
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.
*/
HRESULT MachineDebugger::i_getEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL *pfEnforced)
{
CheckComArgOutPointerValid(pfEnforced);
AutoCaller autoCaller(this);
HRESULT hrc = autoCaller.rc();
if (SUCCEEDED(hrc))
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
*pfEnforced = maiQueuedEmExecPolicyParams[enmPolicy] == 1;
else
{
bool fEnforced = false;
Console::SafeVMPtrQuiet ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
EMR3QueryExecutionPolicy(ptrVM.rawUVM(), enmPolicy, &fEnforced);
*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.
*/
HRESULT MachineDebugger::i_setEmExecPolicyProperty(EMEXECPOLICY enmPolicy, BOOL fEnforce)
{
AutoCaller autoCaller(this);
HRESULT hrc = autoCaller.rc();
if (SUCCEEDED(hrc))
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
maiQueuedEmExecPolicyParams[enmPolicy] = fEnforce ? 1 : 0;
else
{
Console::SafeVMPtrQuiet ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = EMR3SetExecutionPolicy(ptrVM.rawUVM(), enmPolicy, fEnforce != FALSE);
if (RT_FAILURE(vrc))
hrc = setError(VBOX_E_VM_ERROR, tr("EMR3SetExecutionPolicy failed with %Rrc"), vrc);
}
}
}
return hrc;
}
/**
* Returns the current recompile user mode code flag.
*
* @returns COM status code
* @param a_fEnabled address of result variable
*/
HRESULT MachineDebugger::getRecompileUser(BOOL *aRecompileUser)
{
return i_getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aRecompileUser);
}
/**
* Sets the recompile user mode code flag.
*
* @returns COM status
* @param aEnable new user mode code recompile flag.
*/
HRESULT MachineDebugger::setRecompileUser(BOOL aRecompileUser)
{
LogFlowThisFunc(("enable=%d\n", aRecompileUser));
return i_setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING3, aRecompileUser);
}
/**
* Returns the current recompile supervisor code flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getRecompileSupervisor(BOOL *aRecompileSupervisor)
{
return i_getEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aRecompileSupervisor);
}
/**
* Sets the new recompile supervisor code flag.
*
* @returns COM status code
* @param aEnable new recompile supervisor code flag
*/
HRESULT MachineDebugger::setRecompileSupervisor(BOOL aRecompileSupervisor)
{
LogFlowThisFunc(("enable=%d\n", aRecompileSupervisor));
return i_setEmExecPolicyProperty(EMEXECPOLICY_RECOMPILE_RING0, aRecompileSupervisor);
}
/**
* Returns the current execute-all-in-IEM setting.
*
* @returns COM status code
* @param aEnabled Address of result variable.
*/
HRESULT MachineDebugger::getExecuteAllInIEM(BOOL *aExecuteAllInIEM)
{
return i_getEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
}
/**
* Changes the execute-all-in-IEM setting.
*
* @returns COM status code
* @param aEnable New setting.
*/
HRESULT MachineDebugger::setExecuteAllInIEM(BOOL aExecuteAllInIEM)
{
LogFlowThisFunc(("enable=%d\n", aExecuteAllInIEM));
return i_setEmExecPolicyProperty(EMEXECPOLICY_IEM_ALL, aExecuteAllInIEM);
}
/**
* Returns the current patch manager enabled flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getPATMEnabled(BOOL *aPATMEnabled)
{
#ifdef VBOX_WITH_RAW_MODE
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aPATMEnabled = PATMR3IsEnabled (ptrVM.rawUVM());
else
#endif
*aPATMEnabled = false;
return S_OK;
}
/**
* Set the new patch manager enabled flag.
*
* @returns COM status code
* @param aEnable new patch manager enabled flag
*/
HRESULT MachineDebugger::setPATMEnabled(BOOL aPATMEnabled)
{
LogFlowThisFunc(("enable=%d\n", aPATMEnabled));
#ifdef VBOX_WITH_RAW_MODE
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
{
// queue the request
mPatmEnabledQueued = aPATMEnabled;
return S_OK;
}
Console::SafeVMPtr ptrVM(mParent);
if (FAILED(ptrVM.rc()))
return ptrVM.rc();
int vrc = PATMR3AllowPatching(ptrVM.rawUVM(), RT_BOOL(aPATMEnabled));
if (RT_FAILURE(vrc))
return setError(VBOX_E_VM_ERROR, tr("PATMR3AllowPatching returned %Rrc"), vrc);
#else /* !VBOX_WITH_RAW_MODE */
if (aPATMEnabled)
return setError(VBOX_E_VM_ERROR, tr("PATM not present"), VERR_NOT_SUPPORTED);
#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
*/
HRESULT MachineDebugger::getCSAMEnabled(BOOL *aCSAMEnabled)
{
#ifdef VBOX_WITH_RAW_MODE
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aCSAMEnabled = CSAMR3IsEnabled(ptrVM.rawUVM());
else
#endif /* VBOX_WITH_RAW_MODE */
*aCSAMEnabled = false;
return S_OK;
}
/**
* Sets the new code scanner enabled flag.
*
* @returns COM status code
* @param aEnable new code scanner enabled flag
*/
HRESULT MachineDebugger::setCSAMEnabled(BOOL aCSAMEnabled)
{
LogFlowThisFunc(("enable=%d\n", aCSAMEnabled));
#ifdef VBOX_WITH_RAW_MODE
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
{
// queue the request
mCsamEnabledQueued = aCSAMEnabled;
return S_OK;
}
Console::SafeVMPtr ptrVM(mParent);
if (FAILED(ptrVM.rc()))
return ptrVM.rc();
int vrc = CSAMR3SetScanningEnabled(ptrVM.rawUVM(), aCSAMEnabled != FALSE);
if (RT_FAILURE(vrc))
return setError(VBOX_E_VM_ERROR, tr("CSAMR3SetScanningEnabled returned %Rrc"), vrc);
#else /* !VBOX_WITH_RAW_MODE */
if (aCSAMEnabled)
return setError(VBOX_E_VM_ERROR, tr("CASM not present"), VERR_NOT_SUPPORTED);
#endif /* !VBOX_WITH_RAW_MODE */
return S_OK;
}
/**
* Returns the log enabled / disabled status.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getLogEnabled(BOOL *aLogEnabled)
{
#ifdef LOG_ENABLED
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
const PRTLOGGER pLogInstance = RTLogDefaultInstance();
*aLogEnabled = pLogInstance && !(pLogInstance->fFlags & RTLOGFLAGS_DISABLED);
#else
*aLogEnabled = false;
#endif
return S_OK;
}
/**
* Enables or disables logging.
*
* @returns COM status code
* @param aEnabled The new code log state.
*/
HRESULT MachineDebugger::setLogEnabled(BOOL aLogEnabled)
{
LogFlowThisFunc(("aLogEnabled=%d\n", aLogEnabled));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
{
// queue the request
mLogEnabledQueued = aLogEnabled;
return S_OK;
}
Console::SafeVMPtr ptrVM(mParent);
if (FAILED(ptrVM.rc())) return ptrVM.rc();
#ifdef LOG_ENABLED
int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), aLogEnabled ? "enabled" : "disabled");
if (RT_FAILURE(vrc))
{
/** @todo handle error code. */
}
#endif
return S_OK;
}
HRESULT MachineDebugger::i_logStringProps(PRTLOGGER pLogger, PFNLOGGETSTR pfnLogGetStr,
const char *pszLogGetStr, Utf8Str astrSettings)
{
BSTR *a_pbstrSettings = new BSTR;
astrSettings.cloneTo(a_pbstrSettings);
/* Make sure the VM is powered up. */
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (FAILED(hrc))
return hrc;
/* Make sure we've got a logger. */
if (!pLogger)
{
Bstr bstrEmpty;
bstrEmpty.cloneTo(a_pbstrSettings);
return S_OK;
}
/* Do the job. */
size_t cbBuf = _1K;
for (;;)
{
char *pszBuf = (char *)RTMemTmpAlloc(cbBuf);
AssertReturn(pszBuf, E_OUTOFMEMORY);
int rc = pfnLogGetStr(pLogger, pszBuf, cbBuf);
if (RT_SUCCESS(rc))
{
try
{
Bstr bstrRet(pszBuf);
bstrRet.detachTo(a_pbstrSettings);
hrc = S_OK;
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
RTMemTmpFree(pszBuf);
return hrc;
}
RTMemTmpFree(pszBuf);
AssertReturn(rc == VERR_BUFFER_OVERFLOW, setError(VBOX_E_IPRT_ERROR, tr("%s returned %Rrc"), pszLogGetStr, rc));
/* try again with a bigger buffer. */
cbBuf *= 2;
AssertReturn(cbBuf <= _256K, setError(E_FAIL, tr("%s returns too much data"), pszLogGetStr));
}
}
HRESULT MachineDebugger::getLogDbgFlags(com::Utf8Str &aLogDbgFlags)
{
HRESULT hrc = i_logStringProps(RTLogGetDefaultInstance(), RTLogGetFlags, "RTGetFlags", aLogDbgFlags);
return hrc;
}
HRESULT MachineDebugger::getLogDbgGroups(com::Utf8Str &aLogDbgGroups)
{
HRESULT hrc = i_logStringProps(RTLogGetDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", aLogDbgGroups);
return hrc;
}
HRESULT MachineDebugger::getLogDbgDestinations(com::Utf8Str &aLogDbgDestinations)
{
HRESULT hrc = i_logStringProps(RTLogGetDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", aLogDbgDestinations);
return hrc;
}
HRESULT MachineDebugger::getLogRelFlags(com::Utf8Str &aLogRelFlags)
{
HRESULT hrc = i_logStringProps(RTLogRelDefaultInstance(), RTLogGetFlags, "RTGetFlags", aLogRelFlags);
return hrc;
}
HRESULT MachineDebugger::getLogRelGroups(com::Utf8Str &aLogRelGroups)
{
HRESULT hrc = i_logStringProps(RTLogRelDefaultInstance(), RTLogGetGroupSettings, "RTLogGetGroupSettings", aLogRelGroups);
return hrc;
}
HRESULT MachineDebugger::getLogRelDestinations(com::Utf8Str &aLogRelDestinations)
{
HRESULT hrc = i_logStringProps(RTLogRelDefaultInstance(), RTLogGetDestinations, "RTLogGetDestinations", aLogRelDestinations);
return hrc;
}
/**
* Returns the current hardware virtualization flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getHWVirtExEnabled(BOOL *aHWVirtExEnabled)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aHWVirtExEnabled = HMR3IsEnabled(ptrVM.rawUVM());
else
*aHWVirtExEnabled = false;
return S_OK;
}
/**
* Returns the current nested paging flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getHWVirtExNestedPagingEnabled(BOOL *aHWVirtExNestedPagingEnabled)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aHWVirtExNestedPagingEnabled = HMR3IsNestedPagingActive(ptrVM.rawUVM());
else
*aHWVirtExNestedPagingEnabled = false;
return S_OK;
}
/**
* Returns the current VPID flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getHWVirtExVPIDEnabled(BOOL *aHWVirtExVPIDEnabled)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aHWVirtExVPIDEnabled = HMR3IsVpidActive(ptrVM.rawUVM());
else
*aHWVirtExVPIDEnabled = false;
return S_OK;
}
/**
* Returns the current unrestricted execution setting.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getHWVirtExUXEnabled(BOOL *aHWVirtExUXEnabled)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
*aHWVirtExUXEnabled = HMR3IsUXActive(ptrVM.rawUVM());
else
*aHWVirtExUXEnabled = false;
return S_OK;
}
HRESULT MachineDebugger::getOSName(com::Utf8Str &aOSName)
{
LogFlowThisFunc(("\n"));
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Do the job and try convert the name.
*/
char szName[64];
int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), szName, sizeof(szName), NULL, 0);
if (RT_SUCCESS(vrc))
{
try
{
Bstr bstrName(szName);
aOSName = Utf8Str(bstrName);
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = setError(VBOX_E_VM_ERROR, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::getOSVersion(com::Utf8Str &aOSVersion)
{
LogFlowThisFunc(("\n"));
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Do the job and try convert the name.
*/
char szVersion[256];
int vrc = DBGFR3OSQueryNameAndVersion(ptrVM.rawUVM(), NULL, 0, szVersion, sizeof(szVersion));
if (RT_SUCCESS(vrc))
{
try
{
Bstr bstrVersion(szVersion);
aOSVersion = Utf8Str(bstrVersion);
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = setError(VBOX_E_VM_ERROR, tr("DBGFR3OSQueryNameAndVersion failed with %Rrc"), vrc);
}
return hrc;
}
/**
* Returns the current PAE flag.
*
* @returns COM status code
* @param aEnabled address of result variable
*/
HRESULT MachineDebugger::getPAEEnabled(BOOL *aPAEEnabled)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtrQuiet ptrVM(mParent);
if (ptrVM.isOk())
{
uint32_t cr4;
int rc = DBGFR3RegCpuQueryU32(ptrVM.rawUVM(), 0 /*idCpu*/, DBGFREG_CR4, &cr4); AssertRC(rc);
*aPAEEnabled = RT_BOOL(cr4 & X86_CR4_PAE);
}
else
*aPAEEnabled = false;
return S_OK;
}
/**
* Returns the current virtual time rate.
*
* @returns COM status code.
* @param a_puPct Where to store the rate.
*/
HRESULT MachineDebugger::getVirtualTimeRate(ULONG *aVirtualTimeRate)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
*aVirtualTimeRate = TMR3GetWarpDrive(ptrVM.rawUVM());
return hrc;
}
/**
* Returns the current virtual time rate.
*
* @returns COM status code.
* @param aPct Where to store the rate.
*/
HRESULT MachineDebugger::setVirtualTimeRate(ULONG aVirtualTimeRate)
{
HRESULT hrc = S_OK;
if (aVirtualTimeRate < 2 || aVirtualTimeRate > 20000)
return setError(E_INVALIDARG, tr("%u is out of range [2..20000]"), aVirtualTimeRate);
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
if (i_queueSettings())
mVirtualTimeRateQueued = aVirtualTimeRate;
else
{
Console::SafeVMPtr ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = TMR3SetWarpDrive(ptrVM.rawUVM(), aVirtualTimeRate);
if (RT_FAILURE(vrc))
hrc = setError(VBOX_E_VM_ERROR, tr("TMR3SetWarpDrive(, %u) failed with rc=%Rrc"), aVirtualTimeRate, 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.
*/
HRESULT MachineDebugger::getVM(LONG64 *aVM)
{
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
VMR3RetainUVM(ptrVM.rawUVM());
*aVM = (intptr_t)ptrVM.rawUVM();
}
/*
* Note! ptrVM protection provided by SafeVMPtr is no long effective
* after we return from this method.
*/
return hrc;
}
// IMachineDebugger methods
/////////////////////////////////////////////////////////////////////////////
HRESULT MachineDebugger::dumpGuestCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
{
if (aCompression.length())
return setError(E_INVALIDARG, tr("The compression parameter must be empty"));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = DBGFR3CoreWrite(ptrVM.rawUVM(), aFilename.c_str(), false /*fReplaceFile*/);
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
hrc = setError(E_FAIL, tr("DBGFR3CoreWrite failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::dumpHostProcessCore(const com::Utf8Str &aFilename, const com::Utf8Str &aCompression)
{
ReturnComNotImplemented();
}
/**
* Debug info string buffer formatter.
*/
typedef struct MACHINEDEBUGGERINOFHLP
{
/** The core info helper structure. */
DBGFINFOHLP Core;
/** Pointer to the buffer. */
char *pszBuf;
/** The size of the buffer. */
size_t cbBuf;
/** The offset into the buffer */
size_t offBuf;
/** Indicates an out-of-memory condition. */
bool fOutOfMemory;
} MACHINEDEBUGGERINOFHLP;
/** Pointer to a Debug info string buffer formatter. */
typedef MACHINEDEBUGGERINOFHLP *PMACHINEDEBUGGERINOFHLP;
/**
* @callback_method_impl{FNRTSTROUTPUT}
*/
static DECLCALLBACK(size_t) MachineDebuggerInfoOutput(void *pvArg, const char *pachChars, size_t cbChars)
{
PMACHINEDEBUGGERINOFHLP pHlp = (PMACHINEDEBUGGERINOFHLP)pvArg;
/*
* Grow the buffer if required.
*/
size_t const cbRequired = cbChars + pHlp->offBuf + 1;
if (cbRequired > pHlp->cbBuf)
{
if (RT_UNLIKELY(pHlp->fOutOfMemory))
return 0;
size_t cbBufNew = pHlp->cbBuf * 2;
if (cbRequired > cbBufNew)
cbBufNew = RT_ALIGN_Z(cbRequired, 256);
void *pvBufNew = RTMemRealloc(pHlp->pszBuf, cbBufNew);
if (RT_UNLIKELY(!pvBufNew))
{
pHlp->fOutOfMemory = true;
RTMemFree(pHlp->pszBuf);
pHlp->pszBuf = NULL;
pHlp->cbBuf = 0;
pHlp->offBuf = 0;
return 0;
}
pHlp->pszBuf = (char *)pvBufNew;
pHlp->cbBuf = cbBufNew;
}
/*
* Copy the bytes into the buffer and terminate it.
*/
memcpy(&pHlp->pszBuf[pHlp->offBuf], pachChars, cbChars);
pHlp->offBuf += cbChars;
pHlp->pszBuf[pHlp->offBuf] = '\0';
Assert(pHlp->offBuf < pHlp->cbBuf);
return cbChars;
}
/**
* @interface_method_impl{DBGFINFOHLP, pfnPrintfV}
*/
static DECLCALLBACK(void) MachineDebuggerInfoPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list va)
{
RTStrFormatV(MachineDebuggerInfoOutput, (void *)pHlp, NULL, NULL, pszFormat, va);
}
/**
* @interface_method_impl{DBGFINFOHLP, pfnPrintf}
*/
static DECLCALLBACK(void) MachineDebuggerInfoPrintf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
{
va_list va;
va_start(va, pszFormat);
MachineDebuggerInfoPrintfV(pHlp, pszFormat, va);
va_end(va);
}
/**
* Initializes the debug info string buffer formatter
*
* @param pHlp The help structure to init.
*/
static void MachineDebuggerInfoInit(PMACHINEDEBUGGERINOFHLP pHlp)
{
pHlp->Core.pfnPrintf = MachineDebuggerInfoPrintf;
pHlp->Core.pfnPrintfV = MachineDebuggerInfoPrintfV;
pHlp->pszBuf = NULL;
pHlp->cbBuf = 0;
pHlp->offBuf = 0;
pHlp->fOutOfMemory = false;
}
/**
* Deletes the debug info string buffer formatter.
* @param pHlp The helper structure to delete.
*/
static void MachineDebuggerInfoDelete(PMACHINEDEBUGGERINOFHLP pHlp)
{
RTMemFree(pHlp->pszBuf);
pHlp->pszBuf = NULL;
}
HRESULT MachineDebugger::info(const com::Utf8Str &aName, const com::Utf8Str &aArgs, com::Utf8Str &aInfo)
{
LogFlowThisFunc(("\n"));
/*
* Do the autocaller and lock bits.
*/
AutoCaller autoCaller(this);
HRESULT hrc = autoCaller.rc();
if (SUCCEEDED(hrc))
{
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Create a helper and call DBGFR3Info.
*/
MACHINEDEBUGGERINOFHLP Hlp;
MachineDebuggerInfoInit(&Hlp);
int vrc = DBGFR3Info(ptrVM.rawUVM(), aName.c_str(), aArgs.c_str(), &Hlp.Core);
if (RT_SUCCESS(vrc))
{
if (!Hlp.fOutOfMemory)
{
/*
* Convert the info string, watching out for allocation errors.
*/
try
{
Bstr bstrInfo(Hlp.pszBuf);
aInfo = bstrInfo;
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = E_OUTOFMEMORY;
}
else
hrc = setError(VBOX_E_VM_ERROR, tr("DBGFR3Info failed with %Rrc"), vrc);
MachineDebuggerInfoDelete(&Hlp);
}
}
return hrc;
}
HRESULT MachineDebugger::injectNMI()
{
LogFlowThisFunc(("\n"));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = DBGFR3InjectNMI(ptrVM.rawUVM(), 0);
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
hrc = setError(E_FAIL, tr("DBGFR3InjectNMI failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::modifyLogFlags(const com::Utf8Str &aSettings)
{
LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = DBGFR3LogModifyFlags(ptrVM.rawUVM(), aSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
hrc = setError(E_FAIL, tr("DBGFR3LogModifyFlags failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::modifyLogGroups(const com::Utf8Str &aSettings)
{
LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = DBGFR3LogModifyGroups(ptrVM.rawUVM(), aSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
hrc = setError(E_FAIL, tr("DBGFR3LogModifyGroups failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::modifyLogDestinations(const com::Utf8Str &aSettings)
{
LogFlowThisFunc(("aSettings=%s\n", aSettings.c_str()));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
int vrc = DBGFR3LogModifyDestinations(ptrVM.rawUVM(), aSettings.c_str());
if (RT_SUCCESS(vrc))
hrc = S_OK;
else
hrc = setError(E_FAIL, tr("DBGFR3LogModifyDestinations failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::readPhysicalMemory(LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::writePhysicalMemory(LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::readVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, std::vector<BYTE> &aBytes)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::writeVirtualMemory(ULONG aCpuId, LONG64 aAddress, ULONG aSize, const std::vector<BYTE> &aBytes)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::detectOS(com::Utf8Str &aOs)
{
LogFlowThisFunc(("\n"));
/*
* Do the autocaller and lock bits.
*/
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Do the job and try convert the name.
*/
/** @todo automatically load the DBGC plugins or this is a waste of time. */
char szName[64];
int vrc = DBGFR3OSDetect(ptrVM.rawUVM(), szName, sizeof(szName));
if (RT_SUCCESS(vrc) && vrc != VINF_DBGF_OS_NOT_DETCTED)
{
try
{
Bstr bstrName(szName);
aOs = Utf8Str(bstrName);
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = setError(VBOX_E_VM_ERROR, tr("DBGFR3OSDetect failed with %Rrc"), vrc);
}
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];
ssize_t cch = DBGFR3RegFormatValue(szHex, sizeof(szHex), a_pValue, a_enmType, true /*fSpecial*/);
if (RT_UNLIKELY(cch <= 0))
return E_UNEXPECTED;
*a_pbstr = szHex;
return S_OK;
}
HRESULT MachineDebugger::getRegister(ULONG aCpuId, const com::Utf8Str &aName, com::Utf8Str &aValue)
{
/*
* The prologue.
*/
LogFlowThisFunc(("\n"));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Real work.
*/
DBGFREGVAL Value;
DBGFREGVALTYPE enmType;
int vrc = DBGFR3RegNmQuery(ptrVM.rawUVM(), aCpuId, aName.c_str(), &Value, &enmType);
if (RT_SUCCESS(vrc))
{
try
{
Bstr bstrValue;
hrc = formatRegisterValue(&bstrValue, &Value, enmType);
if (SUCCEEDED(hrc))
aValue = Utf8Str(bstrValue);
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else if (vrc == VERR_DBGF_REGISTER_NOT_FOUND)
hrc = setError(E_FAIL, tr("Register '%s' was not found"), aName.c_str());
else if (vrc == VERR_INVALID_CPU_ID)
hrc = setError(E_FAIL, tr("Invalid CPU ID: %u"), aCpuId);
else
hrc = setError(VBOX_E_VM_ERROR,
tr("DBGFR3RegNmQuery failed with rc=%Rrc querying register '%s' with default cpu set to %u"),
vrc, aName.c_str(), aCpuId);
}
return hrc;
}
HRESULT MachineDebugger::getRegisters(ULONG aCpuId, std::vector<com::Utf8Str> &aNames, std::vector<com::Utf8Str> &aValues)
{
/*
* The prologue.
*/
LogFlowThisFunc(("\n"));
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
Console::SafeVMPtr ptrVM(mParent);
HRESULT hrc = ptrVM.rc();
if (SUCCEEDED(hrc))
{
/*
* Real work.
*/
size_t cRegs;
int vrc = DBGFR3RegNmQueryAllCount(ptrVM.rawUVM(), &cRegs);
if (RT_SUCCESS(vrc))
{
PDBGFREGENTRYNM paRegs = (PDBGFREGENTRYNM)RTMemAllocZ(sizeof(paRegs[0]) * cRegs);
if (paRegs)
{
vrc = DBGFR3RegNmQueryAll(ptrVM.rawUVM(), paRegs, cRegs);
if (RT_SUCCESS(vrc))
{
try
{
for (uint32_t iReg = 0; iReg < cRegs; iReg++)
{
Bstr bstrValue;
hrc = formatRegisterValue(&bstrValue, &paRegs[iReg].Val, paRegs[iReg].enmType);
AssertComRC(hrc);
aNames[iReg] = Utf8Str(paRegs[iReg].pszName);
aValues[iReg] = Utf8Str(bstrValue);
}
}
catch (std::bad_alloc)
{
hrc = E_OUTOFMEMORY;
}
}
else
hrc = setError(E_FAIL, tr("DBGFR3RegNmQueryAll failed with %Rrc"), vrc);
RTMemFree(paRegs);
}
else
hrc = E_OUTOFMEMORY;
}
else
hrc = setError(E_FAIL, tr("DBGFR3RegNmQueryAllCount failed with %Rrc"), vrc);
}
return hrc;
}
HRESULT MachineDebugger::setRegister(ULONG aCpuId, const com::Utf8Str &aName, const com::Utf8Str &aValue)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::setRegisters(ULONG aCpuId, const std::vector<com::Utf8Str> &aNames,
const std::vector<com::Utf8Str> &aValues)
{
ReturnComNotImplemented();
}
HRESULT MachineDebugger::dumpGuestStack(ULONG aCpuId, com::Utf8Str &aStack)
{
ReturnComNotImplemented();
}
/**
* Resets VM statistics.
*
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
*/
HRESULT MachineDebugger::resetStats(const com::Utf8Str &aPattern)
{
Console::SafeVMPtrQuiet ptrVM(mParent);
if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
STAMR3Reset(ptrVM.rawUVM(), aPattern.c_str());
return S_OK;
}
/**
* Dumps VM statistics to the log.
*
* @returns COM status code.
* @param aPattern The selection pattern. A bit similar to filename globbing.
*/
HRESULT MachineDebugger::dumpStats(const com::Utf8Str &aPattern)
{
Console::SafeVMPtrQuiet ptrVM(mParent);
if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
STAMR3Dump(ptrVM.rawUVM(), aPattern.c_str());
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.
*/
HRESULT MachineDebugger::getStats(const com::Utf8Str &aPattern, BOOL aWithDescriptions, com::Utf8Str &aStats)
{
Console::SafeVMPtrQuiet ptrVM (mParent);
if (!ptrVM.isOk())
return setError(VBOX_E_INVALID_VM_STATE, "Machine is not running");
char *pszSnapshot;
int vrc = STAMR3Snapshot(ptrVM.rawUVM(), aPattern.c_str(), &pszSnapshot, NULL,
!!aWithDescriptions);
if (RT_FAILURE(vrc))
return vrc == VERR_NO_MEMORY ? E_OUTOFMEMORY : E_FAIL;
/** @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. */
aStats = Utf8Str(pszSnapshot);
STAMR3SnapshotFree(ptrVM.rawUVM(), pszSnapshot);
return S_OK;
}
// public methods only for internal purposes
/////////////////////////////////////////////////////////////////////////////
void MachineDebugger::i_flushQueuedSettings()
{
mFlushMode = true;
if (mSingleStepQueued != ~0)
{
COMSETTER(SingleStep)(mSingleStepQueued);
mSingleStepQueued = ~0;
}
for (unsigned i = 0; i < EMEXECPOLICY_END; i++)
if (maiQueuedEmExecPolicyParams[i] != UINT8_MAX)
{
i_setEmExecPolicyProperty((EMEXECPOLICY)i, RT_BOOL(maiQueuedEmExecPolicyParams[i]));
maiQueuedEmExecPolicyParams[i] = UINT8_MAX;
}
if (mPatmEnabledQueued != ~0)
{
COMSETTER(PATMEnabled)(mPatmEnabledQueued);
mPatmEnabledQueued = ~0;
}
if (mCsamEnabledQueued != ~0)
{
COMSETTER(CSAMEnabled)(mCsamEnabledQueued);
mCsamEnabledQueued = ~0;
}
if (mLogEnabledQueued != ~0)
{
COMSETTER(LogEnabled)(mLogEnabledQueued);
mLogEnabledQueued = ~0;
}
if (mVirtualTimeRateQueued != ~(uint32_t)0)
{
COMSETTER(VirtualTimeRate)(mVirtualTimeRateQueued);
mVirtualTimeRateQueued = ~0;
}
mFlushMode = false;
}
// private methods
/////////////////////////////////////////////////////////////////////////////
bool MachineDebugger::i_queueSettings() const
{
if (!mFlushMode)
{
// check if the machine is running
MachineState_T machineState;
mParent->COMGETTER(State)(&machineState);
switch (machineState)
{
// queue the request
default:
return true;
case MachineState_Running:
case MachineState_Paused:
case MachineState_Stuck:
case MachineState_LiveSnapshotting:
case MachineState_Teleporting:
break;
}
}
return false;
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */