Index: trunk/src/VBox/VMM/VMMAll/EMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 72554)
+++ trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 72555)
@@ -401,4 +401,41 @@
 
 #endif /* IN_RING3 */
+
+
+/**
+ * Adds an exit to the history for this CPU.
+ *
+ * @returns Suggested action to take.
+ * @param   pVCpu           The corss context virtual CPU structure.
+ * @param   uFlagsAndType   Combined flags and type (see EMEXIT_MAKE_FLAGS_AND_TYPE).
+ * @param   uFlatPC         The flattened program counter (RIP).
+ * @param   uTimestamp      The TSC value for the exit, 0 if not available.
+ * @thread  EMT(pVCpu)
+ */
+VMM_INT_DECL(EMEXITACTION) EMHistoryAddExit(PVMCPU pVCpu, uint32_t uFlagsAndType, uint64_t uFlatPC, uint64_t uTimestamp)
+{
+    VMCPU_ASSERT_EMT(pVCpu);
+
+    /*
+     * Add the exit history entry.
+     */
+    AssertCompile(RT_ELEMENTS(pVCpu->em.s.aExitHistory) == 256);
+    PEMEXITENTRY pHistEntry = &pVCpu->em.s.aExitHistory[(uintptr_t)(pVCpu->em.s.iNextExit++) & 0xff];
+    pHistEntry->uFlatPC       = uFlatPC;
+    pHistEntry->uTimestamp    = uTimestamp;
+    pHistEntry->uFlagsAndType = uFlagsAndType;
+    pHistEntry->idxSlot       = UINT32_MAX;
+
+    /*
+     * If common exit type, we will insert/update the exit into the shared hash table.
+     */
+    if ((uFlagsAndType & EMEXIT_F_KIND_MASK) == EMEXIT_F_KIND_EM)
+    {
+        /** @todo later */
+    }
+
+    return EMEXITACTION_NORMAL;
+}
+
 
 /**
Index: trunk/src/VBox/VMM/VMMAll/NEMAllNativeTemplate-win.cpp.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/NEMAllNativeTemplate-win.cpp.h	(revision 72554)
+++ trunk/src/VBox/VMM/VMMAll/NEMAllNativeTemplate-win.cpp.h	(revision 72555)
@@ -1909,4 +1909,9 @@
      * Emulate the memory access, either access handler or special memory.
      */
+    EMHistoryAddExit(pVCpu,
+                       pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
+                     ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
+                     : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
+                     pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
     nemHCWinCopyStateFromX64Header(pVCpu, pCtx, &pMsg->Header);
     VBOXSTRICTRC rcStrict;
@@ -1998,4 +2003,10 @@
      * Emulate the memory access, either access handler or special memory.
      */
+    EMHistoryAddExit(pVCpu,
+                       pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
+                     ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_WRITE)
+                     : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MMIO_READ),
+                     pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
     nemR3WinCopyStateFromX64Header(pVCpu, pCtx, &pExit->VpContext);
     rc = nemHCWinCopyStateFromHyperV(pVM, pVCpu, pCtx, NEM_WIN_CPUMCTX_EXTRN_MASK_FOR_IEM | CPUMCTX_EXTRN_DS | CPUMCTX_EXTRN_ES);
@@ -2052,4 +2063,10 @@
          * Simple port I/O.
          */
+        EMHistoryAddExit(pVCpu,
+                           pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ),
+                         pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
         static uint32_t const s_fAndMask[8] =
         {   UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX,   UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX   };
@@ -2125,4 +2142,10 @@
          * the opcode bytes for possible evil prefixes.
          */
+        EMHistoryAddExit(pVCpu,
+                           pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ),
+                         pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
         nemHCWinCopyStateFromX64Header(pVCpu, pCtx, &pMsg->Header);
         pCtx->fExtrn &= ~(  CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
@@ -2196,4 +2219,10 @@
          * Simple port I/O.
          */
+        EMHistoryAddExit(pVCpu,
+                           pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_READ),
+                         pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
         static uint32_t const s_fAndMask[8] =
         {   UINT32_MAX, UINT32_C(0xff), UINT32_C(0xffff), UINT32_MAX,   UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX   };
@@ -2249,4 +2278,10 @@
          * the opcode bytes for possible evil prefixes.
          */
+        EMHistoryAddExit(pVCpu,
+                           pExit->MemoryAccess.AccessInfo.AccessType == WHvMemoryAccessWrite
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_IO_PORT_STR_READ),
+                         pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
         nemR3WinCopyStateFromX64Header(pVCpu, pCtx, &pExit->VpContext);
         pCtx->fExtrn &= ~(  CPUMCTX_EXTRN_RAX | CPUMCTX_EXTRN_RCX | CPUMCTX_EXTRN_RDI | CPUMCTX_EXTRN_RSI
@@ -2320,4 +2355,7 @@
      * Just copy the state we've got and handle it in the loop for now.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
+                     pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
     nemHCWinCopyStateFromX64Header(pVCpu, pCtx, &pMsg->Header);
     Log4(("IntWinExit/%u: %04x:%08RX64/%s: %u IF=%d InterruptShadow=%d\n",
@@ -2354,4 +2392,7 @@
      * Just copy the state we've got and handle it in the loop for now.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_INTTERRUPT_WINDOW),
+                     pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
     nemR3WinCopyStateFromX64Header(pVCpu, pCtx, &pExit->VpContext);
     Log4(("IntWinExit/%u: %04x:%08RX64/%s: %u IF=%d InterruptShadow=%d\n",
@@ -2374,4 +2415,5 @@
  * @param   pMsg            The message.
  * @param   pCtx            The register context.
+ * @sa      nemR3WinHandleExitCpuId
  */
 NEM_TMPL_STATIC VBOXSTRICTRC nemHCWinHandleMessageCpuId(PVMCPU pVCpu, HV_X64_CPUID_INTERCEPT_MESSAGE const *pMsg, PCPUMCTX pCtx)
@@ -2385,4 +2427,7 @@
      *       function and make everyone use it.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
+                     pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
     /** @todo Combine implementations into IEMExecDecodedCpuId as this will
      *        only get weirder with nested VT-x and AMD-V support. */
@@ -2419,5 +2464,5 @@
  * @param   pExit           The VM exit information to handle.
  * @param   pCtx            The register context.
- * @sa      nemHCWinHandleMessageInterruptWindow
+ * @sa      nemHCWinHandleMessageCpuId
  */
 NEM_TMPL_STATIC VBOXSTRICTRC
@@ -2432,4 +2477,7 @@
      *       function and make everyone use it.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_CPUID),
+                     pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
     /** @todo Combine implementations into IEMExecDecodedCpuId as this will
      *        only get weirder with nested VT-x and AMD-V support. */
@@ -2487,4 +2535,10 @@
     if (pMsg->Header.ExecutionState.Cpl == 0)
     {
+        EMHistoryAddExit(pVCpu,
+                           pMsg->Header.InterceptAccessType == HV_INTERCEPT_ACCESS_WRITE
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
+                         pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
         /*
          * Get all the MSR state.  Since we're getting EFER, we also need to
@@ -2613,4 +2667,10 @@
          * get CR0, CR4 and CR3.
          */
+        EMHistoryAddExit(pVCpu,
+                           pExit->MsrAccess.AccessInfo.IsWrite
+                         ? EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_WRITE)
+                         : EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_EM, EMEXITTYPE_MSR_READ),
+                         pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
+
         nemR3WinCopyStateFromX64Header(pVCpu, pCtx, &pExit->VpContext);
         rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NULL, pCtx,
@@ -2873,4 +2933,7 @@
         case X86_XCPT_UD:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUd);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
+                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
+
             if (nemHcWinIsInterestingUndefinedOpcode(pMsg->InstructionByteCount, pMsg->InstructionBytes,
                                                      pMsg->Header.ExecutionState.EferLma && pMsg->Header.CsSegment.Long ))
@@ -2893,4 +2956,6 @@
         case X86_XCPT_DB:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionDb);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
+                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
             Log4(("XcptExit/%u: %04x:%08RX64/%s: #DB - TODO\n",
                   pVCpu->idCpu, pMsg->Header.CsSegment.Selector, pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header) ));
@@ -2899,4 +2964,6 @@
         case X86_XCPT_BP:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionBp);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
+                             pMsg->Header.Rip + pMsg->Header.CsSegment.Base, ASMReadTSC());
             Log4(("XcptExit/%u: %04x:%08RX64/%s: #BP - TODO - %u\n", pVCpu->idCpu, pMsg->Header.CsSegment.Selector,
                   pMsg->Header.Rip, nemHCWinExecStateToLogStr(&pMsg->Header), pMsg->Header.InstructionLength));
@@ -2971,4 +3038,6 @@
         case X86_XCPT_UD:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionUd);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_UD),
+                             pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
             if (nemHcWinIsInterestingUndefinedOpcode(pExit->VpException.InstructionByteCount, pExit->VpException.InstructionBytes,
                                                      pExit->VpContext.ExecutionState.EferLma && pExit->VpContext.Cs.Long ))
@@ -2994,4 +3063,6 @@
         case X86_XCPT_DB:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionDb);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_DB),
+                             pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
             Log4(("XcptExit/%u: %04x:%08RX64/%s: #DB - TODO\n",
                   pVCpu->idCpu, pExit->VpContext.Cs.Selector, pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext) ));
@@ -3000,4 +3071,6 @@
         case X86_XCPT_BP:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitExceptionBp);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_XCPT_BP),
+                             pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
             Log4(("XcptExit/%u: %04x:%08RX64/%s: #BP - TODO - %u\n", pVCpu->idCpu, pExit->VpContext.Cs.Selector,
                   pExit->VpContext.Rip, nemR3WinExecStateToLogStr(&pExit->VpContext), pExit->VpContext.InstructionLength));
@@ -3057,4 +3130,6 @@
      * Let IEM decide whether this is really it.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
+                     pMsgHdr->Rip + pMsgHdr->CsSegment.Base, ASMReadTSC());
     nemHCWinCopyStateFromX64Header(pVCpu, pCtx, pMsgHdr);
     VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, pGVCpu, pCtx,
@@ -3112,4 +3187,6 @@
      * Let IEM decide whether this is really it.
      */
+    EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_UNRECOVERABLE_EXCEPTION),
+                     pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
     nemR3WinCopyStateFromX64Header(pVCpu, pCtx, &pExit->VpContext);
     VBOXSTRICTRC rcStrict = nemHCWinImportStateIfNeededStrict(pVCpu, NULL, pCtx,
@@ -3181,4 +3258,6 @@
             case HvMessageTypeX64Halt:
                 STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitHalt);
+                EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_HALT),
+                                 pMsg->X64InterceptHeader.Rip + pMsg->X64InterceptHeader.CsSegment.Base, ASMReadTSC());
                 Log4(("HaltExit\n"));
                 return VINF_EM_HALT;
@@ -3263,4 +3342,6 @@
         case WHvRunVpExitReasonX64Halt:
             STAM_REL_COUNTER_INC(&pVCpu->nem.s.StatExitHalt);
+            EMHistoryAddExit(pVCpu, EMEXIT_MAKE_FLAGS_AND_TYPE(EMEXIT_F_KIND_NEM, NEMEXITTYPE_HALT),
+                             pExit->VpContext.Rip + pExit->VpContext.Cs.Base, ASMReadTSC());
             Log4(("HaltExit\n"));
             return VINF_EM_HALT;
Index: trunk/src/VBox/VMM/VMMR3/EMR3Dbg.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/EMR3Dbg.cpp	(revision 72554)
+++ trunk/src/VBox/VMM/VMMR3/EMR3Dbg.cpp	(revision 72555)
@@ -22,6 +22,11 @@
 #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>
 
 
@@ -66,14 +71,230 @@
 };
 
+VMM_INT_DECL(const char *) EMR3GetExitTypeName(uint32_t uExitType)
+{
+    switch ((EMEXITTYPE)uExitType)
+    {
+        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_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";
+    }
+    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(uFlagsAndType & EMEXIT_F_TYPE_MASK);
+            break;
+
+        case EMEXIT_F_KIND_VMX:
+            pszExitName = HMR3GetVmxExitName( uFlagsAndType & EMEXIT_F_TYPE_MASK);
+            break;
+
+        case EMEXIT_F_KIND_SVM:
+            pszExitName = HMR3GetSvmExitName( 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->aCpus[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", pVCpu->idCpu);
+        pHlp->pfnPrintf(pHlp, "   Exit No.:      TSC timestamp / delta    Flat RIP         Exit   Name\n");
+
+        /*
+         * 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];
+            if (!fReverse)
+                idx += 1;
+
+            /* Get the exit name. */
+            char        szExitName[16];
+            const char *pszExitName = emR3HistoryGetExitName(pEntry->uFlagsAndType, szExitName, sizeof(szExitName));
+
+            int64_t offDelta = uPrevTimestamp != 0 && pEntry->uTimestamp != 0 ? pEntry->uTimestamp - uPrevTimestamp : 0;
+            uPrevTimestamp = pEntry->uTimestamp;
+
+            if (pEntry->idxSlot == UINT32_MAX)
+                pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %016RX64 %#06x %s\n",
+                                idx, pEntry->uTimestamp, offDelta, pEntry->uFlatPC, pEntry->uFlagsAndType, pszExitName);
+            else
+            {
+                /** @todo more on this later */
+                pHlp->pfnPrintf(pHlp, " %10RU64: %#018RX64/%+-9RI64 %016RX64 %#06x %s slot=%#x\n",
+                                idx, pEntry->uTimestamp, offDelta, pEntry->uFlatPC, pEntry->uFlagsAndType, pszExitName,
+                                pEntry->idxSlot);
+            }
+        } while (--cLeft > 0 && idx > 0);
+    }
+}
+
 
 int emR3InitDbg(PVM pVM)
 {
-    RT_NOREF_PV(pVM);
-    int rc = VINF_SUCCESS;
+    /*
+     * Register info dumpers.
+     */
+    int rc = DBGFR3InfoRegisterInternalEx(pVM, "exits", "Dumps the VM-exit history.",
+                                          emR3InfoExitHistory, DBGFINFO_FLAGS_ALL_EMTS);
+    AssertLogRelRCReturn(rc, rc);
+
 #ifdef VBOX_WITH_DEBUGGER
+    /*
+     * Register debugger commands.
+     */
     rc = DBGCRegisterCommands(&g_aCmds[0], RT_ELEMENTS(g_aCmds));
-    AssertLogRelRC(rc);
+    AssertLogRelRCReturn(rc, rc);
 #endif
-    return rc;
-}
-
+
+    return VINF_SUCCESS;
+}
+
Index: trunk/src/VBox/VMM/VMMR3/HM.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 72554)
+++ trunk/src/VBox/VMM/VMMR3/HM.cpp	(revision 72555)
@@ -3646,4 +3646,32 @@
 
 /**
+ * Gets the name of a VT-x exit code.
+ *
+ * @returns Pointer to read only string if @a uExit is known, otherwise NULL.
+ * @param   uExit               The VT-x exit to name.
+ */
+VMMR3DECL(const char *) HMR3GetVmxExitName(uint32_t uExit)
+{
+    if (uExit < RT_ELEMENTS(g_apszVTxExitReasons))
+        return g_apszVTxExitReasons[uExit];
+    return NULL;
+}
+
+
+/**
+ * Gets the name of an AMD-V exit code.
+ *
+ * @returns Pointer to read only string if @a uExit is known, otherwise NULL.
+ * @param   uExit               The AMD-V exit to name.
+ */
+VMMR3DECL(const char *) HMR3GetSvmExitName(uint32_t uExit)
+{
+    if (uExit < RT_ELEMENTS(g_apszAmdVExitReasons))
+        return g_apszAmdVExitReasons[uExit];
+    return hmSvmGetSpecialExitReasonDesc(uExit);
+}
+
+
+/**
  * Displays the guest VM-exit history.
  *
Index: trunk/src/VBox/VMM/VMMR3/NEMR3.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/NEMR3.cpp	(revision 72554)
+++ trunk/src/VBox/VMM/VMMR3/NEMR3.cpp	(revision 72555)
@@ -327,4 +327,27 @@
 
 
+/**
+ * Gets the name of a generic NEM exit code.
+ *
+ * @returns Pointer to read only string if @a uExit is known, otherwise NULL.
+ * @param   uExit               The NEM exit to name.
+ */
+VMMR3DECL(const char *) NEMR3GetExitName(uint32_t uExit)
+{
+    switch ((NEMEXITTYPE)uExit)
+    {
+        case NEMEXITTYPE_UNRECOVERABLE_EXCEPTION:       return "NEM unrecoverable exception";
+        case NEMEXITTYPE_INVALID_VP_REGISTER_VALUE:     return "NEM invalid vp register value";
+        case NEMEXITTYPE_INTTERRUPT_WINDOW:             return "NEM interrupt window";
+        case NEMEXITTYPE_HALT:                          return "NEM halt";
+        case NEMEXITTYPE_XCPT_UD:                       return "NEM #UD";
+        case NEMEXITTYPE_XCPT_DB:                       return "NEM #DB";
+        case NEMEXITTYPE_XCPT_BP:                       return "NEM #BP";
+        case NEMEXITTYPE_CANCELED:                      return "NEM canceled";
+    }
+
+    return NULL;
+}
+
 
 VMMR3_INT_DECL(VBOXSTRICTRC) NEMR3RunGC(PVM pVM, PVMCPU pVCpu)
Index: trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 72554)
+++ trunk/src/VBox/VMM/VMMR3/NEMR3Native-win.cpp	(revision 72555)
@@ -1445,5 +1445,5 @@
          */
 
-#if 1 && defined(DEBUG_bird)
+#if 0 && defined(DEBUG_bird)
         /*
          * Poke and probe a little.
Index: trunk/src/VBox/VMM/include/EMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/EMInternal.h	(revision 72554)
+++ trunk/src/VBox/VMM/include/EMInternal.h	(revision 72555)
@@ -292,4 +292,36 @@
 
 /**
+ * Exit history entry.
+ *
+ * @remarks We could perhaps trim this down a little bit by assuming uFlatPC
+ *          only needs 48 bits (currently true but will change) and stuffing
+ *          the flags+type in the available 16 bits made available.  The
+ *          timestamp could likewise be shortened to accomodate the index, or
+ *          we might skip the index entirely.  However, since we will have to
+ *          deal with 56-bit wide PC address before long, there's not point.
+ *
+ *          On the upside, there are unused bits in both uFlagsAndType and the
+ *          idxSlot fields if needed for anything.
+ */
+typedef struct EMEXITENTRY
+{
+    /** The flat PC (CS:EIP/RIP) address of the exit. */
+    uint64_t        uFlatPC;
+    /** The EMEXIT_MAKE_FLAGS_AND_TYPE */
+    uint32_t        uFlagsAndType;
+    /** The index into the exit slot hash table.
+     * UINT32_MAX if too many collisions and not entered into it. */
+    uint32_t        idxSlot;
+    /** The TSC timestamp of the exit.
+     * This is 0 if not timestamped. */
+    uint64_t        uTimestamp;
+} EMEXITENTRY;
+/** Pointer to an exit history entry. */
+typedef EMEXITENTRY *PEMEXITENTRY;
+/** Pointer to a const exit history entry. */
+typedef EMEXITENTRY const *PCEMEXITENTRY;
+
+
+/**
  * Converts a EM pointer into a VM pointer.
  * @returns Pointer to the VM structure the EM is part of.
@@ -464,4 +496,12 @@
     uint64_t                padding1;
 #endif
+
+    /** Where to store the next exit history entry.
+     * Since aExitHistory is 256 items longs, we'll just increment this and
+     * mask it when using it.  That help the readers detect whether we've
+     * wrapped around or not.  */
+    uint64_t                iNextExit;
+    /** Exit history table (6KB). */
+    EMEXITENTRY             aExitHistory[256];
 } EMCPU;
 /** Pointer to EM VM instance data. */
Index: trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- trunk/src/VBox/VMM/include/NEMInternal.h	(revision 72554)
+++ trunk/src/VBox/VMM/include/NEMInternal.h	(revision 72555)
@@ -102,4 +102,26 @@
 # define NEM_TMPL_STATIC
 #endif
+
+
+/**
+ * Generic NEM exit type enumeration for use with EMHistoryAddExit.
+ *
+ * On windows we've got two different set of exit types and they are both jumping
+ * around the place value wise, so EM can use their values.
+ *
+ * @note We only have exit types for exits not covered by EM here.
+ */
+typedef enum NEMEXITTYPE
+{
+    /* windows: */
+    NEMEXITTYPE_UNRECOVERABLE_EXCEPTION = 1,
+    NEMEXITTYPE_INVALID_VP_REGISTER_VALUE,
+    NEMEXITTYPE_INTTERRUPT_WINDOW,
+    NEMEXITTYPE_HALT,
+    NEMEXITTYPE_XCPT_UD,
+    NEMEXITTYPE_XCPT_DB,
+    NEMEXITTYPE_XCPT_BP,
+    NEMEXITTYPE_CANCELED
+} NEMEXITTYPE;
 
 
