summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/EMR3Dbg.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/VMM/VMMR3/EMR3Dbg.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/VMM/VMMR3/EMR3Dbg.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/EMR3Dbg.cpp350
1 files changed, 350 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/EMR3Dbg.cpp b/src/VBox/VMM/VMMR3/EMR3Dbg.cpp
new file mode 100644
index 00000000..71f8cad0
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/EMR3Dbg.cpp
@@ -0,0 +1,350 @@
+/* $Id: EMR3Dbg.cpp $ */
+/** @file
+ * EM - Execution Monitor / Manager, Debugger Related Bits.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_EM
+#include <VBox/vmm/em.h>
+#include <VBox/vmm/hm.h>
+#include <VBox/vmm/nem.h>
+#include <VBox/dbg.h>
+#include "EMInternal.h"
+#include <VBox/vmm/vm.h>
+#include <iprt/string.h>
+#include <iprt/ctype.h>
+
+
+/** @callback_method_impl{FNDBGCCMD,
+ * Implements the '.alliem' command. }
+ */
+static DECLCALLBACK(int) enmR3DbgCmdAllIem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
+{
+ int rc;
+ bool f;
+
+ if (cArgs == 0)
+ {
+ rc = EMR3QueryExecutionPolicy(pUVM, EMEXECPOLICY_IEM_ALL, &f);
+ if (RT_FAILURE(rc))
+ return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "EMR3QueryExecutionPolicy(,EMEXECPOLICY_IEM_ALL,");
+ DBGCCmdHlpPrintf(pCmdHlp, f ? "alliem: enabled\n" : "alliem: disabled\n");
+ }
+ else
+ {
+ rc = DBGCCmdHlpVarToBool(pCmdHlp, &paArgs[0], &f);
+ if (RT_FAILURE(rc))
+ return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToBool");
+ rc = EMR3SetExecutionPolicy(pUVM, EMEXECPOLICY_IEM_ALL, f);
+ if (RT_FAILURE(rc))
+ return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "EMR3SetExecutionPolicy(,EMEXECPOLICY_IEM_ALL,%RTbool)", f);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/** Describes a optional boolean argument. */
+static DBGCVARDESC const g_BoolArg = { 0, 1, DBGCVAR_CAT_ANY, 0, "boolean", "Boolean value." };
+
+/** Commands. */
+static DBGCCMD const g_aCmds[] =
+{
+ {
+ "alliem", 0, 1, &g_BoolArg, 1, 0, enmR3DbgCmdAllIem, "[boolean]",
+ "Enables or disabled executing ALL code in IEM, if no arguments are given it displays the current status."
+ },
+};
+
+
+/**
+ * Translates EMEXITTYPE into a name.
+ *
+ * @returns Pointer to read-only name, NULL if unknown type.
+ * @param enmExitType The exit type to name.
+ */
+VMM_INT_DECL(const char *) EMR3GetExitTypeName(EMEXITTYPE enmExitType)
+{
+ switch (enmExitType)
+ {
+ case EMEXITTYPE_INVALID: return "invalid";
+ case EMEXITTYPE_IO_PORT_READ: return "I/O port read";
+ case EMEXITTYPE_IO_PORT_WRITE: return "I/O port write";
+ case EMEXITTYPE_IO_PORT_STR_READ: return "I/O port string read";
+ case EMEXITTYPE_IO_PORT_STR_WRITE: return "I/O port string write";
+ case EMEXITTYPE_MMIO: return "MMIO access";
+ case EMEXITTYPE_MMIO_READ: return "MMIO read";
+ case EMEXITTYPE_MMIO_WRITE: return "MMIO write";
+ case EMEXITTYPE_MSR_READ: return "MSR read";
+ case EMEXITTYPE_MSR_WRITE: return "MSR write";
+ case EMEXITTYPE_CPUID: return "CPUID";
+ case EMEXITTYPE_RDTSC: return "RDTSC";
+ case EMEXITTYPE_MOV_CRX: return "MOV CRx";
+ case EMEXITTYPE_MOV_DRX: return "MOV DRx";
+ case EMEXITTYPE_VMREAD: return "VMREAD";
+ case EMEXITTYPE_VMWRITE: return "VMWRITE";
+
+ /* Raw-mode only: */
+ case EMEXITTYPE_INVLPG: return "INVLPG";
+ case EMEXITTYPE_LLDT: return "LLDT";
+ case EMEXITTYPE_RDPMC: return "RDPMC";
+ case EMEXITTYPE_CLTS: return "CLTS";
+ case EMEXITTYPE_STI: return "STI";
+ case EMEXITTYPE_INT: return "INT";
+ case EMEXITTYPE_SYSCALL: return "SYSCALL";
+ case EMEXITTYPE_SYSENTER: return "SYSENTER";
+ case EMEXITTYPE_HLT: return "HLT";
+ }
+ return NULL;
+}
+
+
+/**
+ * Translates flags+type into an exit name.
+ *
+ * @returns Exit name.
+ * @param uFlagsAndType The exit to name.
+ * @param pszFallback Buffer for formatting a numeric fallback.
+ * @param cbFallback Size of fallback buffer.
+ */
+static const char *emR3HistoryGetExitName(uint32_t uFlagsAndType, char *pszFallback, size_t cbFallback)
+{
+ const char *pszExitName;
+ switch (uFlagsAndType & EMEXIT_F_KIND_MASK)
+ {
+ case EMEXIT_F_KIND_EM:
+ pszExitName = EMR3GetExitTypeName((EMEXITTYPE)(uFlagsAndType & EMEXIT_F_TYPE_MASK));
+ break;
+
+ case EMEXIT_F_KIND_VMX:
+ pszExitName = HMGetVmxExitName( uFlagsAndType & EMEXIT_F_TYPE_MASK);
+ break;
+
+ case EMEXIT_F_KIND_SVM:
+ pszExitName = HMGetSvmExitName( uFlagsAndType & EMEXIT_F_TYPE_MASK);
+ break;
+
+ case EMEXIT_F_KIND_NEM:
+ pszExitName = NEMR3GetExitName( uFlagsAndType & EMEXIT_F_TYPE_MASK);
+ break;
+
+ case EMEXIT_F_KIND_XCPT:
+ switch (uFlagsAndType & EMEXIT_F_TYPE_MASK)
+ {
+ case X86_XCPT_DE: return "Xcpt #DE";
+ case X86_XCPT_DB: return "Xcpt #DB";
+ case X86_XCPT_NMI: return "Xcpt #NMI";
+ case X86_XCPT_BP: return "Xcpt #BP";
+ case X86_XCPT_OF: return "Xcpt #OF";
+ case X86_XCPT_BR: return "Xcpt #BR";
+ case X86_XCPT_UD: return "Xcpt #UD";
+ case X86_XCPT_NM: return "Xcpt #NM";
+ case X86_XCPT_DF: return "Xcpt #DF";
+ case X86_XCPT_CO_SEG_OVERRUN: return "Xcpt #CO_SEG_OVERRUN";
+ case X86_XCPT_TS: return "Xcpt #TS";
+ case X86_XCPT_NP: return "Xcpt #NP";
+ case X86_XCPT_SS: return "Xcpt #SS";
+ case X86_XCPT_GP: return "Xcpt #GP";
+ case X86_XCPT_PF: return "Xcpt #PF";
+ case X86_XCPT_MF: return "Xcpt #MF";
+ case X86_XCPT_AC: return "Xcpt #AC";
+ case X86_XCPT_MC: return "Xcpt #MC";
+ case X86_XCPT_XF: return "Xcpt #XF";
+ case X86_XCPT_VE: return "Xcpt #VE";
+ case X86_XCPT_SX: return "Xcpt #SX";
+ default:
+ pszExitName = NULL;
+ break;
+ }
+ break;
+
+ default:
+ AssertFailed();
+ pszExitName = NULL;
+ break;
+ }
+ if (pszExitName)
+ return pszExitName;
+ RTStrPrintf(pszFallback, cbFallback, "%#06x", uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_TYPE_MASK));
+ return pszFallback;
+}
+
+
+/**
+ * Displays the VM-exit history.
+ *
+ * @param pVM The cross context VM structure.
+ * @param pHlp The info helper functions.
+ * @param pszArgs Arguments, ignored.
+ */
+static DECLCALLBACK(void) emR3InfoExitHistory(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ NOREF(pszArgs);
+
+ /*
+ * Figure out target cpu and parse arguments.
+ */
+ PVMCPU pVCpu = VMMGetCpu(pVM);
+ if (!pVCpu)
+ pVCpu = pVM->apCpusR3[0];
+ bool fReverse = true;
+ uint32_t cLeft = RT_ELEMENTS(pVCpu->em.s.aExitHistory);
+
+ while (pszArgs && *pszArgs)
+ {
+ pszArgs = RTStrStripL(pszArgs);
+ if (!*pszArgs)
+ break;
+ if (RT_C_IS_DIGIT(*pszArgs))
+ {
+ /* The number to dump. */
+ uint32_t uValue = cLeft;
+ RTStrToUInt32Ex(pszArgs, (char **)&pszArgs, 0, &uValue);
+ if (uValue > 0)
+ cLeft = RT_MIN(uValue, RT_ELEMENTS(pVCpu->em.s.aExitHistory));
+ }
+ else if (RTStrCmp(pszArgs, "reverse") == 0)
+ {
+ pszArgs += 7;
+ fReverse = true;
+ }
+ else if (RTStrCmp(pszArgs, "ascending") == 0)
+ {
+ pszArgs += 9;
+ fReverse = false;
+ }
+ else if (RTStrCmp(pszArgs, "asc") == 0)
+ {
+ pszArgs += 3;
+ fReverse = false;
+ }
+ else
+ {
+ const char *pszStart = pszArgs;
+ while (*pszArgs && !RT_C_IS_SPACE(*pszArgs))
+ pszArgs++;
+ pHlp->pfnPrintf(pHlp, "Unknown option: %.*s\n", pszArgs - pszStart, pszArgs);
+ }
+ }
+
+ /*
+ * Do the job.
+ */
+ uint64_t idx = pVCpu->em.s.iNextExit;
+ if (idx == 0)
+ pHlp->pfnPrintf(pHlp, "CPU[%u]: VM-exit history: empty\n", pVCpu->idCpu);
+ else
+ {
+ /*
+ * Print header.
+ */
+ pHlp->pfnPrintf(pHlp,
+ "CPU[%u]: VM-exit history:\n"
+ " Exit No.: TSC timestamp / delta RIP (Flat/*) Exit Name\n"
+ , pVCpu->idCpu);
+
+ /*
+ * Adjust bounds if ascending order.
+ */
+ if (!fReverse)
+ {
+ if (idx > cLeft)
+ idx -= cLeft;
+ else
+ {
+ cLeft = idx;
+ idx = 0;
+ }
+ }
+
+ /*
+ * Print the entries.
+ */
+ uint64_t uPrevTimestamp = 0;
+ do
+ {
+ if (fReverse)
+ idx -= 1;
+ PCEMEXITENTRY const pEntry = &pVCpu->em.s.aExitHistory[(uintptr_t)idx & 0xff];
+
+ /* Get the exit name. */
+ char szExitName[16];
+ const char *pszExitName = emR3HistoryGetExitName(pEntry->uFlagsAndType, szExitName, sizeof(szExitName));
+
+ /* Calc delta (negative if reverse order, positive ascending). */
+ int64_t offDelta = uPrevTimestamp != 0 && pEntry->uTimestamp != 0 ? pEntry->uTimestamp - uPrevTimestamp : 0;
+ uPrevTimestamp = pEntry->uTimestamp;
+
+ char szPC[32];
+ if (!(pEntry->uFlagsAndType & (EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)))
+ RTStrPrintf(szPC, sizeof(szPC), "%016RX64 ", pEntry->uFlatPC);
+ else if (pEntry->uFlagsAndType & EMEXIT_F_UNFLATTENED_PC)
+ RTStrPrintf(szPC, sizeof(szPC), "%016RX64*", pEntry->uFlatPC);
+ else
+ RTStrPrintf(szPC, sizeof(szPC), "%04x:%08RX32* ", (uint32_t)(pEntry->uFlatPC >> 32), (uint32_t)pEntry->uFlatPC);
+
+ /* Do the printing. */
+ if (pEntry->idxSlot == UINT32_MAX)
+ pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %s %#07x %s\n",
+ idx, pEntry->uTimestamp, offDelta, szPC, pEntry->uFlagsAndType, pszExitName);
+ else
+ {
+ /** @todo more on this later */
+ pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %s %#07x %s slot=%#x\n",
+ idx, pEntry->uTimestamp, offDelta, szPC, pEntry->uFlagsAndType, pszExitName, pEntry->idxSlot);
+ }
+
+ /* Advance if ascending. */
+ if (!fReverse)
+ idx += 1;
+ } while (--cLeft > 0 && idx > 0);
+ }
+}
+
+
+int emR3InitDbg(PVM pVM)
+{
+ /*
+ * Register info dumpers.
+ */
+ const char *pszExitsDesc = "Dumps the VM-exit history. Arguments: Number of entries; 'asc', 'ascending' or 'reverse'.";
+ int rc = DBGFR3InfoRegisterInternalEx(pVM, "exits", pszExitsDesc, emR3InfoExitHistory, DBGFINFO_FLAGS_ALL_EMTS);
+ AssertLogRelRCReturn(rc, rc);
+ rc = DBGFR3InfoRegisterInternalEx(pVM, "exithistory", pszExitsDesc, emR3InfoExitHistory, DBGFINFO_FLAGS_ALL_EMTS);
+ AssertLogRelRCReturn(rc, rc);
+
+#ifdef VBOX_WITH_DEBUGGER
+ /*
+ * Register debugger commands.
+ */
+ rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
+ AssertLogRelRCReturn(rc, rc);
+#endif
+
+ return VINF_SUCCESS;
+}
+