VirtualBox

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

Last change on this file was 103516, checked in by vboxsync, 3 months ago

VMM/IEM,EM: Add all kind of exceptions to the exit history, adding a hack to supply the error code and cr2 when present. bugref:10376

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

© 2023 Oracle
ContactPrivacy policyTerms of Use