VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/EMR3Dbg.cpp@ 107307

Last change on this file since 107307 was 107227, checked in by vboxsync, 2 months ago

VMM: Cleaning up ARMv8 / x86 split. jiraref:VBP-1470

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.5 KB
Line 
1/* $Id: EMR3Dbg.cpp 107227 2024-12-04 15:20:14Z vboxsync $ */
2/** @file
3 * EM - Execution Monitor / Manager, Debugger Related Bits.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_EM
33#include <VBox/vmm/em.h>
34#include <VBox/vmm/hm.h>
35#include <VBox/vmm/iem.h>
36#include <VBox/vmm/nem.h>
37#include <VBox/dbg.h>
38#include "EMInternal.h"
39#include <VBox/vmm/vm.h>
40#include <iprt/string.h>
41#include <iprt/ctype.h>
42
43
44#ifdef VBOX_WITH_DEBUGGER
45
46/**
47 * Common worker for the '.alliem' and '.iemrecompiled' commands.
48 */
49static int emR3DbgCmdSetPolicyCommon(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs,
50 EMEXECPOLICY enmPolicy, const char *pszPolicy)
51{
52 int rc;
53 bool f;
54
55 if (cArgs == 0)
56 {
57 rc = EMR3QueryExecutionPolicy(pUVM, enmPolicy, &f);
58 if (RT_FAILURE(rc))
59 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "EMR3QueryExecutionPolicy(,%s,", pszPolicy);
60 DBGCCmdHlpPrintf(pCmdHlp, f ? "%s: enabled\n" : "%s: disabled\n", pszPolicy);
61 }
62 else
63 {
64 rc = DBGCCmdHlpVarToBool(pCmdHlp, &paArgs[0], &f);
65 if (RT_FAILURE(rc))
66 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "DBGCCmdHlpVarToBool");
67 rc = EMR3SetExecutionPolicy(pUVM, enmPolicy, f);
68 if (RT_FAILURE(rc))
69 return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "EMR3SetExecutionPolicy(,%s,%RTbool)", pszPolicy, f);
70 }
71 return VINF_SUCCESS;
72}
73
74
75/** @callback_method_impl{FNDBGCCMD,
76 * Implements the '.alliem' command. }
77 */
78static DECLCALLBACK(int) emR3DbgCmdAllIem(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
79{
80 return emR3DbgCmdSetPolicyCommon(pCmd, pCmdHlp, pUVM, paArgs, cArgs, EMEXECPOLICY_IEM_ALL, "EMEXECPOLICY_IEM_ALL");
81}
82
83
84/** @callback_method_impl{FNDBGCCMD,
85 * Implements the '.iemrecompiled' command. }
86 */
87static DECLCALLBACK(int) emR3DbgCmdIemRecompiled(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
88{
89 return emR3DbgCmdSetPolicyCommon(pCmd, pCmdHlp, pUVM, paArgs, cArgs, EMEXECPOLICY_IEM_RECOMPILED, "EMEXECPOLICY_IEM_RECOMPILED");
90}
91
92
93/** Describes a optional boolean argument. */
94static DBGCVARDESC const g_BoolArg = { 0, 1, DBGCVAR_CAT_ANY, 0, "boolean", "Boolean value." };
95
96/** Commands. */
97static DBGCCMD const g_aCmds[] =
98{
99 {
100 "alliem", 0, 1, &g_BoolArg, 1, 0, emR3DbgCmdAllIem, "[boolean]",
101 "Enables or disables executing ALL code in IEM, if no arguments are given it displays the current status."
102 },
103 {
104 "iemrecompiled", 0, 1, &g_BoolArg, 1, 0, emR3DbgCmdIemRecompiled, "[boolean]",
105 "Enables or disables recompiled ALL-in-IEM execution, if no arguments are given it displays the current status."
106 },
107};
108
109#endif /* VBOX_WITH_DEBUGGER */
110
111
112/**
113 * Translates EMEXITTYPE into a name.
114 *
115 * @returns Pointer to read-only name, NULL if unknown type.
116 * @param enmExitType The exit type to name.
117 */
118VMM_INT_DECL(const char *) EMR3GetExitTypeName(EMEXITTYPE enmExitType)
119{
120 switch (enmExitType)
121 {
122 case EMEXITTYPE_INVALID: return "invalid";
123 case EMEXITTYPE_IO_PORT_READ: return "I/O port read";
124 case EMEXITTYPE_IO_PORT_WRITE: return "I/O port write";
125 case EMEXITTYPE_IO_PORT_STR_READ: return "I/O port string read";
126 case EMEXITTYPE_IO_PORT_STR_WRITE: return "I/O port string write";
127 case EMEXITTYPE_MMIO: return "MMIO access";
128 case EMEXITTYPE_MMIO_READ: return "MMIO read";
129 case EMEXITTYPE_MMIO_WRITE: return "MMIO write";
130 case EMEXITTYPE_MSR_READ: return "MSR read";
131 case EMEXITTYPE_MSR_WRITE: return "MSR write";
132 case EMEXITTYPE_CPUID: return "CPUID";
133 case EMEXITTYPE_RDTSC: return "RDTSC";
134 case EMEXITTYPE_MOV_CRX: return "MOV CRx";
135 case EMEXITTYPE_MOV_DRX: return "MOV DRx";
136 case EMEXITTYPE_VMREAD: return "VMREAD";
137 case EMEXITTYPE_VMWRITE: return "VMWRITE";
138
139 /* Raw-mode only: */
140 case EMEXITTYPE_INVLPG: return "INVLPG";
141 case EMEXITTYPE_LLDT: return "LLDT";
142 case EMEXITTYPE_RDPMC: return "RDPMC";
143 case EMEXITTYPE_CLTS: return "CLTS";
144 case EMEXITTYPE_STI: return "STI";
145 case EMEXITTYPE_INT: return "INT";
146 case EMEXITTYPE_SYSCALL: return "SYSCALL";
147 case EMEXITTYPE_SYSENTER: return "SYSENTER";
148 case EMEXITTYPE_HLT: return "HLT";
149 }
150 return NULL;
151}
152
153
154/**
155 * Translates flags+type into an exit name.
156 *
157 * @returns Exit name.
158 * @param uFlagsAndType The exit to name.
159 * @param pszFallback Buffer for formatting a numeric fallback.
160 * @param cbFallback Size of fallback buffer.
161 */
162static const char *emR3HistoryGetExitName(uint32_t uFlagsAndType, char *pszFallback, size_t cbFallback)
163{
164 const char *pszExitName;
165 switch (uFlagsAndType & EMEXIT_F_KIND_MASK)
166 {
167 case EMEXIT_F_KIND_EM:
168 pszExitName = EMR3GetExitTypeName((EMEXITTYPE)(uFlagsAndType & EMEXIT_F_TYPE_MASK));
169 break;
170
171#if defined(VBOX_VMM_TARGET_X86) && defined(VBOX_WITH_HWVIRT)
172 case EMEXIT_F_KIND_VMX:
173 pszExitName = HMGetVmxExitName(uFlagsAndType & EMEXIT_F_TYPE_MASK);
174 break;
175
176 case EMEXIT_F_KIND_SVM:
177 pszExitName = HMGetSvmExitName(uFlagsAndType & EMEXIT_F_TYPE_MASK);
178 break;
179#endif
180
181 case EMEXIT_F_KIND_NEM:
182 pszExitName = NEMR3GetExitName(uFlagsAndType & EMEXIT_F_TYPE_MASK);
183 break;
184
185 case EMEXIT_F_KIND_IEM:
186 pszExitName = IEMR3GetExitName(uFlagsAndType & EMEXIT_F_TYPE_MASK);
187 break;
188
189#ifdef VBOX_VMM_TARGET_X86
190 case EMEXIT_F_KIND_XCPT:
191 switch (uFlagsAndType & EMEXIT_F_TYPE_MASK)
192 {
193 case X86_XCPT_DE: return "Xcpt #DE";
194 case X86_XCPT_DB: return "Xcpt #DB";
195 case X86_XCPT_NMI: return "Xcpt #NMI";
196 case X86_XCPT_BP: return "Xcpt #BP";
197 case X86_XCPT_OF: return "Xcpt #OF";
198 case X86_XCPT_BR: return "Xcpt #BR";
199 case X86_XCPT_UD: return "Xcpt #UD";
200 case X86_XCPT_NM: return "Xcpt #NM";
201 case X86_XCPT_DF: return "Xcpt #DF";
202 case X86_XCPT_CO_SEG_OVERRUN: return "Xcpt #CO_SEG_OVERRUN";
203 case X86_XCPT_TS: return "Xcpt #TS";
204 case X86_XCPT_NP: return "Xcpt #NP";
205 case X86_XCPT_SS: return "Xcpt #SS";
206 case X86_XCPT_GP: return "Xcpt #GP";
207 case X86_XCPT_PF: return "Xcpt #PF";
208 case X86_XCPT_MF: return "Xcpt #MF";
209 case X86_XCPT_AC: return "Xcpt #AC";
210 case X86_XCPT_MC: return "Xcpt #MC";
211 case X86_XCPT_XF: return "Xcpt #XF";
212 case X86_XCPT_VE: return "Xcpt #VE";
213 case X86_XCPT_CP: return "Xcpt #CP";
214 case X86_XCPT_VC: return "Xcpt #VC";
215 case X86_XCPT_SX: return "Xcpt #SX";
216
217 case X86_XCPT_DF | EMEXIT_F_XCPT_ERRCD: return "Xcpt #DF ErrCd as PC";
218 case X86_XCPT_TS | EMEXIT_F_XCPT_ERRCD: return "Xcpt #TS ErrCd as PC";
219 case X86_XCPT_NP | EMEXIT_F_XCPT_ERRCD: return "Xcpt #NP ErrCd as PC";
220 case X86_XCPT_SS | EMEXIT_F_XCPT_ERRCD: return "Xcpt #SS ErrCd as PC";
221 case X86_XCPT_GP | EMEXIT_F_XCPT_ERRCD: return "Xcpt #GF ErrCd as PC";
222 case X86_XCPT_PF | EMEXIT_F_XCPT_ERRCD: return "Xcpt #PF ErrCd as PC";
223 case X86_XCPT_AC | EMEXIT_F_XCPT_ERRCD: return "Xcpt #AC ErrCd as PC";
224
225 case X86_XCPT_PF | EMEXIT_F_XCPT_CR2: return "Xcpt #PF CR2 as PC";
226
227 default:
228 pszExitName = NULL;
229 break;
230 }
231 break;
232#endif
233
234 default:
235 AssertFailed();
236 pszExitName = NULL;
237 break;
238 }
239 if (pszExitName)
240 return pszExitName;
241 RTStrPrintf(pszFallback, cbFallback, "%#06x", uFlagsAndType & (EMEXIT_F_KIND_MASK | EMEXIT_F_TYPE_MASK));
242 return pszFallback;
243}
244
245
246/**
247 * Displays the VM-exit history.
248 *
249 * @param pVM The cross context VM structure.
250 * @param pHlp The info helper functions.
251 * @param pszArgs Arguments, ignored.
252 */
253static DECLCALLBACK(void) emR3InfoExitHistory(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
254{
255 NOREF(pszArgs);
256
257 /*
258 * Figure out target cpu and parse arguments.
259 */
260 PVMCPU pVCpu = VMMGetCpu(pVM);
261 if (!pVCpu)
262 pVCpu = pVM->apCpusR3[0];
263 bool fReverse = true;
264 uint32_t cLeft = RT_ELEMENTS(pVCpu->em.s.aExitHistory);
265
266 while (pszArgs && *pszArgs)
267 {
268 pszArgs = RTStrStripL(pszArgs);
269 if (!*pszArgs)
270 break;
271 if (RT_C_IS_DIGIT(*pszArgs))
272 {
273 /* The number to dump. */
274 uint32_t uValue = cLeft;
275 RTStrToUInt32Ex(pszArgs, (char **)&pszArgs, 0, &uValue);
276 if (uValue > 0)
277 cLeft = RT_MIN(uValue, RT_ELEMENTS(pVCpu->em.s.aExitHistory));
278 }
279 else if (RTStrCmp(pszArgs, "reverse") == 0)
280 {
281 pszArgs += 7;
282 fReverse = true;
283 }
284 else if (RTStrCmp(pszArgs, "ascending") == 0)
285 {
286 pszArgs += 9;
287 fReverse = false;
288 }
289 else if (RTStrCmp(pszArgs, "asc") == 0)
290 {
291 pszArgs += 3;
292 fReverse = false;
293 }
294 else
295 {
296 const char *pszStart = pszArgs;
297 while (*pszArgs && !RT_C_IS_SPACE(*pszArgs))
298 pszArgs++;
299 pHlp->pfnPrintf(pHlp, "Unknown option: %.*s\n", pszArgs - pszStart, pszArgs);
300 }
301 }
302
303 /*
304 * Do the job.
305 */
306 uint64_t idx = pVCpu->em.s.iNextExit;
307 if (idx == 0)
308 pHlp->pfnPrintf(pHlp, "CPU[%u]: VM-exit history: empty\n", pVCpu->idCpu);
309 else
310 {
311 /*
312 * Print header.
313 */
314 pHlp->pfnPrintf(pHlp,
315 "CPU[%u]: VM-exit history:\n"
316 " Exit No.: TSC timestamp / delta RIP (Flat/*) Exit Name\n"
317 , pVCpu->idCpu);
318
319 /*
320 * Adjust bounds if ascending order.
321 */
322 if (!fReverse)
323 {
324 if (idx > cLeft)
325 idx -= cLeft;
326 else
327 {
328 cLeft = idx;
329 idx = 0;
330 }
331 }
332
333 /*
334 * Print the entries.
335 */
336 uint64_t uPrevTimestamp = 0;
337 do
338 {
339 if (fReverse)
340 idx -= 1;
341 PCEMEXITENTRY const pEntry = &pVCpu->em.s.aExitHistory[(uintptr_t)idx & 0xff];
342
343 /* Get the exit name. */
344 char szExitName[16];
345 const char *pszExitName = emR3HistoryGetExitName(pEntry->uFlagsAndType, szExitName, sizeof(szExitName));
346
347 /* Calc delta (negative if reverse order, positive ascending). */
348 int64_t offDelta = uPrevTimestamp != 0 && pEntry->uTimestamp != 0 ? pEntry->uTimestamp - uPrevTimestamp : 0;
349 uPrevTimestamp = pEntry->uTimestamp;
350
351 char szPC[32];
352 if (!(pEntry->uFlagsAndType & (EMEXIT_F_CS_EIP | EMEXIT_F_UNFLATTENED_PC)))
353 RTStrPrintf(szPC, sizeof(szPC), "%016RX64 ", pEntry->uFlatPC);
354 else if (pEntry->uFlagsAndType & EMEXIT_F_UNFLATTENED_PC)
355 RTStrPrintf(szPC, sizeof(szPC), "%016RX64*", pEntry->uFlatPC);
356 else
357 RTStrPrintf(szPC, sizeof(szPC), "%04x:%08RX32* ", (uint32_t)(pEntry->uFlatPC >> 32), (uint32_t)pEntry->uFlatPC);
358
359 /* Do the printing. */
360 if (pEntry->idxSlot == UINT32_MAX)
361 pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %s %#07x %s\n",
362 idx, pEntry->uTimestamp, offDelta, szPC, pEntry->uFlagsAndType, pszExitName);
363 else
364 {
365 /** @todo more on this later */
366 pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %s %#07x %s slot=%#x\n",
367 idx, pEntry->uTimestamp, offDelta, szPC, pEntry->uFlagsAndType, pszExitName, pEntry->idxSlot);
368 }
369
370 /* Advance if ascending. */
371 if (!fReverse)
372 idx += 1;
373 } while (--cLeft > 0 && idx > 0);
374 }
375}
376
377
378int emR3InitDbg(PVM pVM)
379{
380 /*
381 * Register info dumpers.
382 */
383 const char *pszExitsDesc = "Dumps the VM-exit history. Arguments: Number of entries; 'asc', 'ascending' or 'reverse'.";
384 int rc = DBGFR3InfoRegisterInternalEx(pVM, "exits", pszExitsDesc, emR3InfoExitHistory, DBGFINFO_FLAGS_ALL_EMTS);
385 AssertLogRelRCReturn(rc, rc);
386 rc = DBGFR3InfoRegisterInternalEx(pVM, "exithistory", pszExitsDesc, emR3InfoExitHistory, DBGFINFO_FLAGS_ALL_EMTS);
387 AssertLogRelRCReturn(rc, rc);
388
389#ifdef VBOX_WITH_DEBUGGER
390 /*
391 * Register debugger commands.
392 */
393 rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
394 AssertLogRelRCReturn(rc, rc);
395#endif
396
397 return VINF_SUCCESS;
398}
399
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette