summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/DBGFR3Flow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/DBGFR3Flow.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/DBGFR3Flow.cpp2260
1 files changed, 2260 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/DBGFR3Flow.cpp b/src/VBox/VMM/VMMR3/DBGFR3Flow.cpp
new file mode 100644
index 00000000..9365aebd
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/DBGFR3Flow.cpp
@@ -0,0 +1,2260 @@
+/* $Id: DBGFR3Flow.cpp $ */
+/** @file
+ * DBGF - Debugger Facility, Control Flow Graph Interface (CFG).
+ */
+
+/*
+ * Copyright (C) 2016-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_dbgf_cfg DBGFR3Flow - Control Flow Graph Interface
+ *
+ * The control flow graph interface provides an API to disassemble
+ * guest code providing the result in a control flow graph.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DBGF
+#include <VBox/vmm/dbgf.h>
+#include "DBGFInternal.h"
+#include <VBox/vmm/mm.h>
+#include <VBox/vmm/uvm.h>
+#include <VBox/vmm/vm.h>
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+#include <iprt/list.h>
+#include <iprt/mem.h>
+#include <iprt/sort.h>
+#include <iprt/strcache.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal control flow graph state.
+ */
+typedef struct DBGFFLOWINT
+{
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Internal reference counter for basic blocks. */
+ uint32_t volatile cRefsBb;
+ /** Flags during creation. */
+ uint32_t fFlags;
+ /** List of all basic blocks. */
+ RTLISTANCHOR LstFlowBb;
+ /** List of identified branch tables. */
+ RTLISTANCHOR LstBranchTbl;
+ /** Number of basic blocks in this control flow graph. */
+ uint32_t cBbs;
+ /** Number of branch tables in this control flow graph. */
+ uint32_t cBranchTbls;
+ /** The lowest addres of a basic block. */
+ DBGFADDRESS AddrLowest;
+ /** The highest address of a basic block. */
+ DBGFADDRESS AddrHighest;
+ /** String cache for disassembled instructions. */
+ RTSTRCACHE hStrCacheInstr;
+} DBGFFLOWINT;
+/** Pointer to an internal control flow graph state. */
+typedef DBGFFLOWINT *PDBGFFLOWINT;
+
+/**
+ * Instruction record
+ */
+typedef struct DBGFFLOWBBINSTR
+{
+ /** Instruction address. */
+ DBGFADDRESS AddrInstr;
+ /** Size of instruction. */
+ uint32_t cbInstr;
+ /** Disassembled instruction string. */
+ const char *pszInstr;
+} DBGFFLOWBBINSTR;
+/** Pointer to an instruction record. */
+typedef DBGFFLOWBBINSTR *PDBGFFLOWBBINSTR;
+
+
+/**
+ * A branch table identified by the graph processor.
+ */
+typedef struct DBGFFLOWBRANCHTBLINT
+{
+ /** Node for the list of branch tables. */
+ RTLISTNODE NdBranchTbl;
+ /** The owning control flow graph. */
+ PDBGFFLOWINT pFlow;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** The general register index holding the bracnh table base. */
+ uint8_t idxGenRegBase;
+ /** Start address of the branch table. */
+ DBGFADDRESS AddrStart;
+ /** Number of valid entries in the branch table. */
+ uint32_t cSlots;
+ /** The addresses contained in the branch table - variable in size. */
+ DBGFADDRESS aAddresses[1];
+} DBGFFLOWBRANCHTBLINT;
+/** Pointer to a branch table structure. */
+typedef DBGFFLOWBRANCHTBLINT *PDBGFFLOWBRANCHTBLINT;
+
+
+/**
+ * Internal control flow graph basic block state.
+ */
+typedef struct DBGFFLOWBBINT
+{
+ /** Node for the list of all basic blocks. */
+ RTLISTNODE NdFlowBb;
+ /** The control flow graph the basic block belongs to. */
+ PDBGFFLOWINT pFlow;
+ /** Reference counter. */
+ uint32_t volatile cRefs;
+ /** Basic block end type. */
+ DBGFFLOWBBENDTYPE enmEndType;
+ /** Start address of this basic block. */
+ DBGFADDRESS AddrStart;
+ /** End address of this basic block. */
+ DBGFADDRESS AddrEnd;
+ /** Address of the block succeeding.
+ * This is valid for conditional jumps
+ * (the other target is referenced by AddrEnd+1) and
+ * unconditional jumps (not ret, iret, etc.) except
+ * if we can't infer the jump target (jmp *eax for example). */
+ DBGFADDRESS AddrTarget;
+ /** The indirect branch table identified for indirect branches. */
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl;
+ /** Last status error code if DBGF_FLOW_BB_F_INCOMPLETE_ERR is set. */
+ int rcError;
+ /** Error message if DBGF_FLOW_BB_F_INCOMPLETE_ERR is set. */
+ char *pszErr;
+ /** Flags for this basic block. */
+ uint32_t fFlags;
+ /** Number of instructions in this basic block. */
+ uint32_t cInstr;
+ /** Maximum number of instruction records for this basic block. */
+ uint32_t cInstrMax;
+ /** Instruction records, variable in size. */
+ DBGFFLOWBBINSTR aInstr[1];
+} DBGFFLOWBBINT;
+/** Pointer to an internal control flow graph basic block state. */
+typedef DBGFFLOWBBINT *PDBGFFLOWBBINT;
+
+
+/**
+ * Control flow graph iterator state.
+ */
+typedef struct DBGFFLOWITINT
+{
+ /** Pointer to the control flow graph (holding a reference). */
+ PDBGFFLOWINT pFlow;
+ /** Next basic block to return. */
+ uint32_t idxBbNext;
+ /** Array of basic blocks sorted by the specified order - variable in size. */
+ PDBGFFLOWBBINT apBb[1];
+} DBGFFLOWITINT;
+/** Pointer to the internal control flow graph iterator state. */
+typedef DBGFFLOWITINT *PDBGFFLOWITINT;
+
+
+/**
+ * Control flow graph branch table iterator state.
+ */
+typedef struct DBGFFLOWBRANCHTBLITINT
+{
+ /** Pointer to the control flow graph (holding a reference). */
+ PDBGFFLOWINT pFlow;
+ /** Next branch table to return. */
+ uint32_t idxTblNext;
+ /** Array of branch table pointers sorted by the specified order - variable in size. */
+ PDBGFFLOWBRANCHTBLINT apBranchTbl[1];
+} DBGFFLOWBRANCHTBLITINT;
+/** Pointer to the internal control flow graph branch table iterator state. */
+typedef DBGFFLOWBRANCHTBLITINT *PDBGFFLOWBRANCHTBLITINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+static uint32_t dbgfR3FlowBbReleaseInt(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow);
+static void dbgfR3FlowBranchTblDestroy(PDBGFFLOWBRANCHTBLINT pFlowBranchTbl);
+
+
+/**
+ * Checks whether both addresses are equal.
+ *
+ * @returns true if both addresses point to the same location, false otherwise.
+ * @param pAddr1 First address.
+ * @param pAddr2 Second address.
+ */
+static bool dbgfR3FlowAddrEqual(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2)
+{
+ return pAddr1->Sel == pAddr2->Sel
+ && pAddr1->off == pAddr2->off;
+}
+
+
+/**
+ * Checks whether the first given address is lower than the second one.
+ *
+ * @returns true if both addresses point to the same location, false otherwise.
+ * @param pAddr1 First address.
+ * @param pAddr2 Second address.
+ */
+static bool dbgfR3FlowAddrLower(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2)
+{
+ return pAddr1->Sel == pAddr2->Sel
+ && pAddr1->off < pAddr2->off;
+}
+
+
+/**
+ * Checks whether the given basic block and address intersect.
+ *
+ * @returns true if they intersect, false otherwise.
+ * @param pFlowBb The basic block to check.
+ * @param pAddr The address to check for.
+ */
+static bool dbgfR3FlowAddrIntersect(PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddr)
+{
+ return (pFlowBb->AddrStart.Sel == pAddr->Sel)
+ && (pFlowBb->AddrStart.off <= pAddr->off)
+ && (pFlowBb->AddrEnd.off >= pAddr->off);
+}
+
+
+/**
+ * Returns the distance of the two given addresses.
+ *
+ * @returns Distance of the addresses.
+ * @param pAddr1 The first address.
+ * @param pAddr2 The second address.
+ */
+static RTGCUINTPTR dbgfR3FlowAddrGetDistance(PDBGFADDRESS pAddr1, PDBGFADDRESS pAddr2)
+{
+ if (pAddr1->Sel == pAddr2->Sel)
+ {
+ if (pAddr1->off >= pAddr2->off)
+ return pAddr1->off - pAddr2->off;
+ else
+ return pAddr2->off - pAddr1->off;
+ }
+ else
+ AssertFailed();
+
+ return 0;
+}
+
+
+/**
+ * Creates a new basic block.
+ *
+ * @returns Pointer to the basic block on success or NULL if out of memory.
+ * @param pThis The control flow graph.
+ * @param pAddrStart The start of the basic block.
+ * @param fFlowBbFlags Additional flags for this bascic block.
+ * @param cInstrMax Maximum number of instructions this block can hold initially.
+ */
+static PDBGFFLOWBBINT dbgfR3FlowBbCreate(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrStart, uint32_t fFlowBbFlags,
+ uint32_t cInstrMax)
+{
+ PDBGFFLOWBBINT pFlowBb = (PDBGFFLOWBBINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBBINT, aInstr[cInstrMax]));
+ if (RT_LIKELY(pFlowBb))
+ {
+ RTListInit(&pFlowBb->NdFlowBb);
+ pFlowBb->cRefs = 1;
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_INVALID;
+ pFlowBb->pFlow = pThis;
+ pFlowBb->fFlags = DBGF_FLOW_BB_F_EMPTY | fFlowBbFlags;
+ pFlowBb->AddrStart = *pAddrStart;
+ pFlowBb->AddrEnd = *pAddrStart;
+ pFlowBb->rcError = VINF_SUCCESS;
+ pFlowBb->pszErr = NULL;
+ pFlowBb->cInstr = 0;
+ pFlowBb->cInstrMax = cInstrMax;
+ pFlowBb->pFlowBranchTbl = NULL;
+ ASMAtomicIncU32(&pThis->cRefsBb);
+ }
+
+ return pFlowBb;
+}
+
+
+/**
+ * Creates an empty branch table with the given size.
+ *
+ * @returns Pointer to the empty branch table on success or NULL if out of memory.
+ * @param pThis The control flow graph.
+ * @param pAddrStart The start of the branch table.
+ * @param idxGenRegBase The general register index holding the base address.
+ * @param cSlots Number of slots the table has.
+ */
+static PDBGFFLOWBRANCHTBLINT
+dbgfR3FlowBranchTblCreate(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrStart, uint8_t idxGenRegBase, uint32_t cSlots)
+{
+ PDBGFFLOWBRANCHTBLINT pBranchTbl = (PDBGFFLOWBRANCHTBLINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBRANCHTBLINT,
+ aAddresses[cSlots]));
+ if (RT_LIKELY(pBranchTbl))
+ {
+ RTListInit(&pBranchTbl->NdBranchTbl);
+ pBranchTbl->pFlow = pThis;
+ pBranchTbl->idxGenRegBase = idxGenRegBase;
+ pBranchTbl->AddrStart = *pAddrStart;
+ pBranchTbl->cSlots = cSlots;
+ pBranchTbl->cRefs = 1;
+ }
+
+ return pBranchTbl;
+}
+
+
+/**
+ * Destroys a control flow graph.
+ *
+ * @returns nothing.
+ * @param pThis The control flow graph to destroy.
+ */
+static void dbgfR3FlowDestroy(PDBGFFLOWINT pThis)
+{
+ /* Defer destruction if there are still basic blocks referencing us. */
+ PDBGFFLOWBBINT pFlowBb;
+ PDBGFFLOWBBINT pFlowBbNext;
+ RTListForEachSafe(&pThis->LstFlowBb, pFlowBb, pFlowBbNext, DBGFFLOWBBINT, NdFlowBb)
+ {
+ dbgfR3FlowBbReleaseInt(pFlowBb, false /*fMayDestroyFlow*/);
+ }
+
+ Assert(!pThis->cRefs);
+ if (!pThis->cRefsBb)
+ {
+ /* Destroy the branch tables. */
+ PDBGFFLOWBRANCHTBLINT pTbl = NULL;
+ PDBGFFLOWBRANCHTBLINT pTblNext = NULL;
+ RTListForEachSafe(&pThis->LstBranchTbl, pTbl, pTblNext, DBGFFLOWBRANCHTBLINT, NdBranchTbl)
+ {
+ dbgfR3FlowBranchTblDestroy(pTbl);
+ }
+
+ RTStrCacheDestroy(pThis->hStrCacheInstr);
+ RTMemFree(pThis);
+ }
+}
+
+
+/**
+ * Destroys a basic block.
+ *
+ * @returns nothing.
+ * @param pFlowBb The basic block to destroy.
+ * @param fMayDestroyFlow Flag whether the control flow graph container
+ * should be destroyed when there is nothing referencing it.
+ */
+static void dbgfR3FlowBbDestroy(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow)
+{
+ PDBGFFLOWINT pThis = pFlowBb->pFlow;
+
+ RTListNodeRemove(&pFlowBb->NdFlowBb);
+ pThis->cBbs--;
+ for (uint32_t idxInstr = 0; idxInstr < pFlowBb->cInstr; idxInstr++)
+ RTStrCacheRelease(pThis->hStrCacheInstr, pFlowBb->aInstr[idxInstr].pszInstr);
+ uint32_t cRefsBb = ASMAtomicDecU32(&pThis->cRefsBb);
+ RTMemFree(pFlowBb);
+
+ if (!cRefsBb && !pThis->cRefs && fMayDestroyFlow)
+ dbgfR3FlowDestroy(pThis);
+}
+
+
+/**
+ * Destroys a given branch table.
+ *
+ * @returns nothing.
+ * @param pFlowBranchTbl The flow branch table to destroy.
+ */
+static void dbgfR3FlowBranchTblDestroy(PDBGFFLOWBRANCHTBLINT pFlowBranchTbl)
+{
+ RTListNodeRemove(&pFlowBranchTbl->NdBranchTbl);
+ RTMemFree(pFlowBranchTbl);
+}
+
+
+/**
+ * Internal basic block release worker.
+ *
+ * @returns New reference count of the released basic block, on 0
+ * it is destroyed.
+ * @param pFlowBb The basic block to release.
+ * @param fMayDestroyFlow Flag whether the control flow graph container
+ * should be destroyed when there is nothing referencing it.
+ */
+static uint32_t dbgfR3FlowBbReleaseInt(PDBGFFLOWBBINT pFlowBb, bool fMayDestroyFlow)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pFlowBb->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p %d\n", cRefs, pFlowBb, pFlowBb->enmEndType));
+ if (cRefs == 0)
+ dbgfR3FlowBbDestroy(pFlowBb, fMayDestroyFlow);
+ return cRefs;
+}
+
+
+/**
+ * Links the given basic block into the control flow graph.
+ *
+ * @returns nothing.
+ * @param pThis The control flow graph to link into.
+ * @param pFlowBb The basic block to link.
+ */
+DECLINLINE(void) dbgfR3FlowLink(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb)
+{
+ RTListAppend(&pThis->LstFlowBb, &pFlowBb->NdFlowBb);
+ pThis->cBbs++;
+}
+
+
+/**
+ * Links the given branch table into the control flow graph.
+ *
+ * @returns nothing.
+ * @param pThis The control flow graph to link into.
+ * @param pBranchTbl The branch table to link.
+ */
+DECLINLINE(void) dbgfR3FlowBranchTblLink(PDBGFFLOWINT pThis, PDBGFFLOWBRANCHTBLINT pBranchTbl)
+{
+ RTListAppend(&pThis->LstBranchTbl, &pBranchTbl->NdBranchTbl);
+ pThis->cBranchTbls++;
+}
+
+
+/**
+ * Returns the first unpopulated basic block of the given control flow graph.
+ *
+ * @returns The first unpopulated control flow graph or NULL if not found.
+ * @param pThis The control flow graph.
+ */
+DECLINLINE(PDBGFFLOWBBINT) dbgfR3FlowGetUnpopulatedBb(PDBGFFLOWINT pThis)
+{
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ if (pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY)
+ return pFlowBb;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Returns the branch table with the given address if it exists.
+ *
+ * @returns Pointer to the branch table record or NULL if not found.
+ * @param pThis The control flow graph.
+ * @param pAddrTbl The branch table address.
+ */
+DECLINLINE(PDBGFFLOWBRANCHTBLINT) dbgfR3FlowBranchTblFindByAddr(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrTbl)
+{
+ PDBGFFLOWBRANCHTBLINT pTbl;
+ RTListForEach(&pThis->LstBranchTbl, pTbl, DBGFFLOWBRANCHTBLINT, NdBranchTbl)
+ {
+ if (dbgfR3FlowAddrEqual(&pTbl->AddrStart, pAddrTbl))
+ return pTbl;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Sets the given error status for the basic block.
+ *
+ * @returns nothing.
+ * @param pFlowBb The basic block causing the error.
+ * @param rcError The error to set.
+ * @param pszFmt Format string of the error description.
+ * @param ... Arguments for the format string.
+ */
+static void dbgfR3FlowBbSetError(PDBGFFLOWBBINT pFlowBb, int rcError, const char *pszFmt, ...)
+{
+ va_list va;
+ va_start(va, pszFmt);
+
+ Assert(!(pFlowBb->fFlags & DBGF_FLOW_BB_F_INCOMPLETE_ERR));
+ pFlowBb->fFlags |= DBGF_FLOW_BB_F_INCOMPLETE_ERR;
+ pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_EMPTY;
+ pFlowBb->rcError = rcError;
+ pFlowBb->pszErr = RTStrAPrintf2V(pszFmt, va);
+ va_end(va);
+}
+
+
+/**
+ * Checks whether the given control flow graph contains a basic block
+ * with the given start address.
+ *
+ * @returns true if there is a basic block with the start address, false otherwise.
+ * @param pThis The control flow graph.
+ * @param pAddr The address to check for.
+ */
+static bool dbgfR3FlowHasBbWithStartAddr(PDBGFFLOWINT pThis, PDBGFADDRESS pAddr)
+{
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ if (dbgfR3FlowAddrEqual(&pFlowBb->AddrStart, pAddr))
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Splits a given basic block into two at the given address.
+ *
+ * @returns VBox status code.
+ * @param pThis The control flow graph.
+ * @param pFlowBb The basic block to split.
+ * @param pAddr The address to split at.
+ */
+static int dbgfR3FlowBbSplit(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddr)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t idxInstrSplit;
+
+ /* If the block is empty it will get populated later so there is nothing to split,
+ * same if the start address equals. */
+ if ( pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY
+ || dbgfR3FlowAddrEqual(&pFlowBb->AddrStart, pAddr))
+ return VINF_SUCCESS;
+
+ /* Find the instruction to split at. */
+ for (idxInstrSplit = 1; idxInstrSplit < pFlowBb->cInstr; idxInstrSplit++)
+ if (dbgfR3FlowAddrEqual(&pFlowBb->aInstr[idxInstrSplit].AddrInstr, pAddr))
+ break;
+
+ Assert(idxInstrSplit > 0);
+
+ /*
+ * Given address might not be on instruction boundary, this is not supported
+ * so far and results in an error.
+ */
+ if (idxInstrSplit < pFlowBb->cInstr)
+ {
+ /* Create new basic block. */
+ uint32_t cInstrNew = pFlowBb->cInstr - idxInstrSplit;
+ PDBGFFLOWBBINT pFlowBbNew = dbgfR3FlowBbCreate(pThis, &pFlowBb->aInstr[idxInstrSplit].AddrInstr,
+ 0 /*fFlowBbFlags*/, cInstrNew);
+ if (pFlowBbNew)
+ {
+ /* Move instructions over. */
+ pFlowBbNew->cInstr = cInstrNew;
+ pFlowBbNew->AddrEnd = pFlowBb->AddrEnd;
+ pFlowBbNew->enmEndType = pFlowBb->enmEndType;
+ pFlowBbNew->AddrTarget = pFlowBb->AddrTarget;
+ pFlowBbNew->fFlags = pFlowBb->fFlags & ~DBGF_FLOW_BB_F_ENTRY;
+ pFlowBbNew->pFlowBranchTbl = pFlowBb->pFlowBranchTbl;
+ pFlowBb->pFlowBranchTbl = NULL;
+
+ /* Move any error to the new basic block and clear them in the old basic block. */
+ pFlowBbNew->rcError = pFlowBb->rcError;
+ pFlowBbNew->pszErr = pFlowBb->pszErr;
+ pFlowBb->rcError = VINF_SUCCESS;
+ pFlowBb->pszErr = NULL;
+ pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_INCOMPLETE_ERR;
+
+ memcpy(&pFlowBbNew->aInstr[0], &pFlowBb->aInstr[idxInstrSplit], cInstrNew * sizeof(DBGFFLOWBBINSTR));
+ pFlowBb->cInstr = idxInstrSplit;
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND;
+ pFlowBb->AddrEnd = pFlowBb->aInstr[idxInstrSplit-1].AddrInstr;
+ pFlowBb->AddrTarget = pFlowBbNew->AddrStart;
+ DBGFR3AddrAdd(&pFlowBb->AddrEnd, pFlowBb->aInstr[idxInstrSplit-1].cbInstr - 1);
+ RT_BZERO(&pFlowBb->aInstr[idxInstrSplit], cInstrNew * sizeof(DBGFFLOWBBINSTR));
+
+ dbgfR3FlowLink(pThis, pFlowBbNew);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ AssertFailedStmt(rc = VERR_INVALID_STATE); /** @todo Proper status code. */
+
+ return rc;
+}
+
+
+/**
+ * Makes sure there is an successor at the given address splitting already existing
+ * basic blocks if they intersect.
+ *
+ * @returns VBox status code.
+ * @param pThis The control flow graph.
+ * @param pAddrSucc The guest address the new successor should start at.
+ * @param fNewBbFlags Flags for the new basic block.
+ * @param pBranchTbl Branch table candidate for this basic block.
+ */
+static int dbgfR3FlowBbSuccessorAdd(PDBGFFLOWINT pThis, PDBGFADDRESS pAddrSucc,
+ uint32_t fNewBbFlags, PDBGFFLOWBRANCHTBLINT pBranchTbl)
+{
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ /*
+ * The basic block must be split if it intersects with the given address
+ * and the start address does not equal the given one.
+ */
+ if (dbgfR3FlowAddrIntersect(pFlowBb, pAddrSucc))
+ return dbgfR3FlowBbSplit(pThis, pFlowBb, pAddrSucc);
+ }
+
+ int rc = VINF_SUCCESS;
+ pFlowBb = dbgfR3FlowBbCreate(pThis, pAddrSucc, fNewBbFlags, 10);
+ if (pFlowBb)
+ {
+ pFlowBb->pFlowBranchTbl = pBranchTbl;
+ dbgfR3FlowLink(pThis, pFlowBb);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Returns whether the parameter indicates an indirect branch.
+ *
+ * @returns Flag whether this is an indirect branch.
+ * @param pDisParam The parameter from the disassembler.
+ */
+DECLINLINE(bool) dbgfR3FlowBranchTargetIsIndirect(PDISOPPARAM pDisParam)
+{
+ bool fIndirect = true;
+
+ if ( pDisParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64)
+ || pDisParam->fUse & (DISUSE_IMMEDIATE8_REL | DISUSE_IMMEDIATE16_REL | DISUSE_IMMEDIATE32_REL | DISUSE_IMMEDIATE64_REL))
+ fIndirect = false;
+
+ return fIndirect;
+}
+
+
+/**
+ * Resolves the direct branch target address if possible from the given instruction address
+ * and instruction parameter.
+ *
+ * @returns VBox status code.
+ * @param pUVM The usermode VM handle.
+ * @param idCpu CPU id for resolving the address.
+ * @param pDisParam The parameter from the disassembler.
+ * @param pAddrInstr The instruction address.
+ * @param cbInstr Size of instruction in bytes.
+ * @param fRelJmp Flag whether this is a reltive jump.
+ * @param pAddrJmpTarget Where to store the address to the jump target on success.
+ */
+static int dbgfR3FlowQueryDirectBranchTarget(PUVM pUVM, VMCPUID idCpu, PDISOPPARAM pDisParam, PDBGFADDRESS pAddrInstr,
+ uint32_t cbInstr, bool fRelJmp, PDBGFADDRESS pAddrJmpTarget)
+{
+ int rc = VINF_SUCCESS;
+
+ Assert(!dbgfR3FlowBranchTargetIsIndirect(pDisParam));
+
+ /* Relative jumps are always from the beginning of the next instruction. */
+ *pAddrJmpTarget = *pAddrInstr;
+ DBGFR3AddrAdd(pAddrJmpTarget, cbInstr);
+
+ if (fRelJmp)
+ {
+ RTGCINTPTR iRel = 0;
+ if (pDisParam->fUse & DISUSE_IMMEDIATE8_REL)
+ iRel = (int8_t)pDisParam->uValue;
+ else if (pDisParam->fUse & DISUSE_IMMEDIATE16_REL)
+ iRel = (int16_t)pDisParam->uValue;
+ else if (pDisParam->fUse & DISUSE_IMMEDIATE32_REL)
+ iRel = (int32_t)pDisParam->uValue;
+ else if (pDisParam->fUse & DISUSE_IMMEDIATE64_REL)
+ iRel = (int64_t)pDisParam->uValue;
+ else
+ AssertFailedStmt(rc = VERR_NOT_SUPPORTED);
+
+ if (iRel < 0)
+ DBGFR3AddrSub(pAddrJmpTarget, -iRel);
+ else
+ DBGFR3AddrAdd(pAddrJmpTarget, iRel);
+ }
+ else
+ {
+ if (pDisParam->fUse & (DISUSE_IMMEDIATE8 | DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64))
+ {
+ if (DBGFADDRESS_IS_FLAT(pAddrInstr))
+ DBGFR3AddrFromFlat(pUVM, pAddrJmpTarget, pDisParam->uValue);
+ else
+ DBGFR3AddrFromSelOff(pUVM, idCpu, pAddrJmpTarget, pAddrInstr->Sel, pDisParam->uValue);
+ }
+ else
+ AssertFailedStmt(rc = VERR_INVALID_STATE);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Returns the CPU mode based on the given assembler flags.
+ *
+ * @returns CPU mode.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param fFlagsDisasm The flags used for disassembling.
+ */
+static CPUMMODE dbgfR3FlowGetDisasCpuMode(PUVM pUVM, VMCPUID idCpu, uint32_t fFlagsDisasm)
+{
+ CPUMMODE enmMode = CPUMMODE_INVALID;
+ uint32_t fDisasMode = fFlagsDisasm & DBGF_DISAS_FLAGS_MODE_MASK;
+ if (fDisasMode == DBGF_DISAS_FLAGS_DEFAULT_MODE)
+ enmMode = DBGFR3CpuGetMode(pUVM, idCpu);
+ else if ( fDisasMode == DBGF_DISAS_FLAGS_16BIT_MODE
+ || fDisasMode == DBGF_DISAS_FLAGS_16BIT_REAL_MODE)
+ enmMode = CPUMMODE_REAL;
+ else if (fDisasMode == DBGF_DISAS_FLAGS_32BIT_MODE)
+ enmMode = CPUMMODE_PROTECTED;
+ else if (fDisasMode == DBGF_DISAS_FLAGS_64BIT_MODE)
+ enmMode = CPUMMODE_LONG;
+ else
+ AssertFailed();
+
+ return enmMode;
+}
+
+
+/**
+ * Searches backwards in the given basic block starting the given instruction index for
+ * a mov instruction with the given register as the target where the constant looks like
+ * a pointer.
+ *
+ * @returns Flag whether a candidate was found.
+ * @param pFlowBb The basic block containing the indirect branch.
+ * @param idxRegTgt The general register the mov targets.
+ * @param cbPtr The pointer size to look for.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param fFlagsDisasm The flags to use for disassembling.
+ * @param pidxInstrStart The instruction index to start searching for on input,
+ * The last instruction evaluated on output.
+ * @param pAddrDest Where to store the candidate address on success.
+ */
+static bool dbgfR3FlowSearchMovWithConstantPtrSizeBackwards(PDBGFFLOWBBINT pFlowBb, uint8_t idxRegTgt, uint32_t cbPtr,
+ PUVM pUVM, VMCPUID idCpu, uint32_t fFlagsDisasm,
+ uint32_t *pidxInstrStart, PDBGFADDRESS pAddrDest)
+{
+ bool fFound = false;
+ uint32_t idxInstrCur = *pidxInstrStart;
+ uint32_t cInstrCheck = idxInstrCur + 1;
+
+ for (;;)
+ {
+ /** @todo Avoid to disassemble again. */
+ PDBGFFLOWBBINSTR pInstr = &pFlowBb->aInstr[idxInstrCur];
+ DBGFDISSTATE DisState;
+ char szOutput[_4K];
+
+ int rc = dbgfR3DisasInstrStateEx(pUVM, idCpu, &pInstr->AddrInstr, fFlagsDisasm,
+ &szOutput[0], sizeof(szOutput), &DisState);
+ if (RT_SUCCESS(rc))
+ {
+ if ( DisState.pCurInstr->uOpcode == OP_MOV
+ && (DisState.Param1.fUse & (DISUSE_REG_GEN16 | DISUSE_REG_GEN32 | DISUSE_REG_GEN64))
+ && DisState.Param1.Base.idxGenReg == idxRegTgt
+ /*&& DisState.Param1.cb == cbPtr*/
+ && DisState.Param2.cb == cbPtr
+ && (DisState.Param2.fUse & (DISUSE_IMMEDIATE16 | DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64)))
+ {
+ /* Found possible candidate. */
+ fFound = true;
+ if (DBGFADDRESS_IS_FLAT(&pInstr->AddrInstr))
+ DBGFR3AddrFromFlat(pUVM, pAddrDest, DisState.Param2.uValue);
+ else
+ DBGFR3AddrFromSelOff(pUVM, idCpu, pAddrDest, pInstr->AddrInstr.Sel, DisState.Param2.uValue);
+ break;
+ }
+ }
+ else
+ break;
+
+ cInstrCheck--;
+ if (!cInstrCheck)
+ break;
+
+ idxInstrCur--;
+ }
+
+ *pidxInstrStart = idxInstrCur;
+ return fFound;
+}
+
+
+/**
+ * Verifies the given branch table candidate and adds it to the control flow graph on success.
+ *
+ * @returns VBox status code.
+ * @param pThis The flow control graph.
+ * @param pFlowBb The basic block causing the indirect branch.
+ * @param pAddrBranchTbl Address of the branch table location.
+ * @param idxGenRegBase The general register holding the base address.
+ * @param cbPtr Guest pointer size.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ *
+ * @todo Handle branch tables greater than 4KB (lazy coder).
+ */
+static int dbgfR3FlowBranchTblVerifyAdd(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddrBranchTbl,
+ uint8_t idxGenRegBase, uint32_t cbPtr, PUVM pUVM, VMCPUID idCpu)
+{
+ int rc = VINF_SUCCESS;
+ PDBGFFLOWBRANCHTBLINT pBranchTbl = dbgfR3FlowBranchTblFindByAddr(pThis, pAddrBranchTbl);
+
+ if (!pBranchTbl)
+ {
+ uint32_t cSlots = 0;
+ uint8_t abBuf[_4K];
+
+ rc = DBGFR3MemRead(pUVM, idCpu, pAddrBranchTbl, &abBuf[0], sizeof(abBuf));
+ if (RT_SUCCESS(rc))
+ {
+ uint8_t *pbBuf = &abBuf[0];
+ while (pbBuf < &abBuf[0] + sizeof(abBuf))
+ {
+ DBGFADDRESS AddrDest;
+ RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t)
+ ? *(uint64_t *)pbBuf
+ : cbPtr == sizeof(uint32_t)
+ ? *(uint32_t *)pbBuf
+ : *(uint16_t *)pbBuf;
+ pbBuf += cbPtr;
+
+ if (DBGFADDRESS_IS_FLAT(pAddrBranchTbl))
+ DBGFR3AddrFromFlat(pUVM, &AddrDest, GCPtr);
+ else
+ DBGFR3AddrFromSelOff(pUVM, idCpu, &AddrDest, pAddrBranchTbl->Sel, GCPtr);
+
+ if (dbgfR3FlowAddrGetDistance(&AddrDest, &pFlowBb->AddrEnd) > _512K)
+ break;
+
+ cSlots++;
+ }
+
+ /* If there are any slots use it. */
+ if (cSlots)
+ {
+ pBranchTbl = dbgfR3FlowBranchTblCreate(pThis, pAddrBranchTbl, idxGenRegBase, cSlots);
+ if (pBranchTbl)
+ {
+ /* Get the addresses. */
+ for (unsigned i = 0; i < cSlots && RT_SUCCESS(rc); i++)
+ {
+ RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t)
+ ? *(uint64_t *)&abBuf[i * cbPtr]
+ : cbPtr == sizeof(uint32_t)
+ ? *(uint32_t *)&abBuf[i * cbPtr]
+ : *(uint16_t *)&abBuf[i * cbPtr];
+
+ if (DBGFADDRESS_IS_FLAT(pAddrBranchTbl))
+ DBGFR3AddrFromFlat(pUVM, &pBranchTbl->aAddresses[i], GCPtr);
+ else
+ DBGFR3AddrFromSelOff(pUVM, idCpu, &pBranchTbl->aAddresses[i],
+ pAddrBranchTbl->Sel, GCPtr);
+ rc = dbgfR3FlowBbSuccessorAdd(pThis, &pBranchTbl->aAddresses[i], DBGF_FLOW_BB_F_BRANCH_TABLE,
+ pBranchTbl);
+ }
+ dbgfR3FlowBranchTblLink(pThis, pBranchTbl);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+
+ if (pBranchTbl)
+ pFlowBb->pFlowBranchTbl = pBranchTbl;
+
+ return rc;
+}
+
+
+/**
+ * Checks whether the location for the branch target candidate contains a valid code address.
+ *
+ * @returns VBox status code.
+ * @param pThis The flow control graph.
+ * @param pFlowBb The basic block causing the indirect branch.
+ * @param pAddrBranchTgt Address of the branch target location.
+ * @param idxGenRegBase The general register holding the address of the location.
+ * @param cbPtr Guest pointer size.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param fBranchTbl Flag whether this is a possible branch table containing multiple
+ * targets.
+ */
+static int dbgfR3FlowCheckBranchTargetLocation(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PDBGFADDRESS pAddrBranchTgt,
+ uint8_t idxGenRegBase, uint32_t cbPtr, PUVM pUVM, VMCPUID idCpu, bool fBranchTbl)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!fBranchTbl)
+ {
+ union { uint16_t u16Val; uint32_t u32Val; uint64_t u64Val; } uVal;
+ rc = DBGFR3MemRead(pUVM, idCpu, pAddrBranchTgt, &uVal, cbPtr);
+ if (RT_SUCCESS(rc))
+ {
+ DBGFADDRESS AddrTgt;
+ RTGCUINTPTR GCPtr = cbPtr == sizeof(uint64_t)
+ ? uVal.u64Val
+ : cbPtr == sizeof(uint32_t)
+ ? uVal.u32Val
+ : uVal.u16Val;
+ if (DBGFADDRESS_IS_FLAT(pAddrBranchTgt))
+ DBGFR3AddrFromFlat(pUVM, &AddrTgt, GCPtr);
+ else
+ DBGFR3AddrFromSelOff(pUVM, idCpu, &AddrTgt, pAddrBranchTgt->Sel, GCPtr);
+
+ if (dbgfR3FlowAddrGetDistance(&AddrTgt, &pFlowBb->AddrEnd) <= _128K)
+ {
+ /* Finish the basic block. */
+ pFlowBb->AddrTarget = AddrTgt;
+ rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrTgt,
+ (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE),
+ pFlowBb->pFlowBranchTbl);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ }
+ else
+ rc = dbgfR3FlowBranchTblVerifyAdd(pThis, pFlowBb, pAddrBranchTgt,
+ idxGenRegBase, cbPtr, pUVM, idCpu);
+
+ return rc;
+}
+
+
+/**
+ * Tries to resolve the indirect branch.
+ *
+ * @returns VBox status code.
+ * @param pThis The flow control graph.
+ * @param pFlowBb The basic block causing the indirect branch.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param pDisParam The parameter from the disassembler.
+ * @param fFlagsDisasm Flags for the disassembler.
+ */
+static int dbgfR3FlowTryResolveIndirectBranch(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PUVM pUVM,
+ VMCPUID idCpu, PDISOPPARAM pDisParam, uint32_t fFlagsDisasm)
+{
+ Assert(dbgfR3FlowBranchTargetIsIndirect(pDisParam));
+
+ uint32_t cbPtr = 0;
+ CPUMMODE enmMode = dbgfR3FlowGetDisasCpuMode(pUVM, idCpu, fFlagsDisasm);
+
+ switch (enmMode)
+ {
+ case CPUMMODE_REAL:
+ cbPtr = sizeof(uint16_t);
+ break;
+ case CPUMMODE_PROTECTED:
+ cbPtr = sizeof(uint32_t);
+ break;
+ case CPUMMODE_LONG:
+ cbPtr = sizeof(uint64_t);
+ break;
+ default:
+ AssertMsgFailed(("Invalid CPU mode %u\n", enmMode));
+ }
+
+ if (pDisParam->fUse & DISUSE_BASE)
+ {
+ uint8_t idxRegBase = pDisParam->Base.idxGenReg;
+
+ /* Check that the used register size and the pointer size match. */
+ if ( ((pDisParam->fUse & DISUSE_REG_GEN16) && cbPtr == sizeof(uint16_t))
+ || ((pDisParam->fUse & DISUSE_REG_GEN32) && cbPtr == sizeof(uint32_t))
+ || ((pDisParam->fUse & DISUSE_REG_GEN64) && cbPtr == sizeof(uint64_t)))
+ {
+ /*
+ * Search all instructions backwards until a move to the used general register
+ * is detected with a constant using the pointer size.
+ */
+ uint32_t idxInstrStart = pFlowBb->cInstr - 1 - 1; /* Don't look at the branch. */
+ bool fCandidateFound = false;
+ bool fBranchTbl = RT_BOOL(pDisParam->fUse & DISUSE_INDEX);
+ DBGFADDRESS AddrBranchTgt;
+ do
+ {
+ fCandidateFound = dbgfR3FlowSearchMovWithConstantPtrSizeBackwards(pFlowBb, idxRegBase, cbPtr,
+ pUVM, idCpu, fFlagsDisasm,
+ &idxInstrStart, &AddrBranchTgt);
+ if (fCandidateFound)
+ {
+ /* Check that the address is not too far away from the instruction address. */
+ RTGCUINTPTR offPtr = dbgfR3FlowAddrGetDistance(&AddrBranchTgt, &pFlowBb->AddrEnd);
+ if (offPtr <= 20 * _1M)
+ {
+ /* Read the content at the address and check that it is near this basic block too. */
+ int rc = dbgfR3FlowCheckBranchTargetLocation(pThis, pFlowBb, &AddrBranchTgt, idxRegBase,
+ cbPtr, pUVM, idCpu, fBranchTbl);
+ if (RT_SUCCESS(rc))
+ break;
+ fCandidateFound = false;
+ }
+
+ if (idxInstrStart > 0)
+ idxInstrStart--;
+ }
+ } while (idxInstrStart > 0 && !fCandidateFound);
+ }
+ else
+ dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE,
+ "The base register size and selected pointer size do not match (fUse=%#x cbPtr=%u)",
+ pDisParam->fUse, cbPtr);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tries to resolve the indirect branch.
+ *
+ * @returns VBox status code.
+ * @param pThis The flow control graph.
+ * @param pFlowBb The basic block causing the indirect branch.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param pDisParam The parameter from the disassembler.
+ * @param fFlagsDisasm Flags for the disassembler.
+ */
+static int dbgfR3FlowBbCheckBranchTblCandidate(PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb, PUVM pUVM,
+ VMCPUID idCpu, PDISOPPARAM pDisParam, uint32_t fFlagsDisasm)
+{
+ int rc = VINF_SUCCESS;
+
+ Assert(pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE && pFlowBb->pFlowBranchTbl);
+
+ uint32_t cbPtr = 0;
+ CPUMMODE enmMode = dbgfR3FlowGetDisasCpuMode(pUVM, idCpu, fFlagsDisasm);
+
+ switch (enmMode)
+ {
+ case CPUMMODE_REAL:
+ cbPtr = sizeof(uint16_t);
+ break;
+ case CPUMMODE_PROTECTED:
+ cbPtr = sizeof(uint32_t);
+ break;
+ case CPUMMODE_LONG:
+ cbPtr = sizeof(uint64_t);
+ break;
+ default:
+ AssertMsgFailed(("Invalid CPU mode %u\n", enmMode));
+ }
+
+ if (pDisParam->fUse & DISUSE_BASE)
+ {
+ uint8_t idxRegBase = pDisParam->Base.idxGenReg;
+
+ /* Check that the used register size and the pointer size match. */
+ if ( ((pDisParam->fUse & DISUSE_REG_GEN16) && cbPtr == sizeof(uint16_t))
+ || ((pDisParam->fUse & DISUSE_REG_GEN32) && cbPtr == sizeof(uint32_t))
+ || ((pDisParam->fUse & DISUSE_REG_GEN64) && cbPtr == sizeof(uint64_t)))
+ {
+ if (idxRegBase != pFlowBb->pFlowBranchTbl->idxGenRegBase)
+ {
+ /* Try to find the new branch table. */
+ pFlowBb->pFlowBranchTbl = NULL;
+ rc = dbgfR3FlowTryResolveIndirectBranch(pThis, pFlowBb, pUVM, idCpu, pDisParam, fFlagsDisasm);
+ }
+ /** @todo else check that the base register is not modified in this basic block. */
+ }
+ else
+ dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE,
+ "The base register size and selected pointer size do not match (fUse=%#x cbPtr=%u)",
+ pDisParam->fUse, cbPtr);
+ }
+ else
+ dbgfR3FlowBbSetError(pFlowBb, VERR_INVALID_STATE,
+ "The instruction does not use a register");
+
+ return rc;
+}
+
+
+/**
+ * Processes and fills one basic block.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param pThis The control flow graph to populate.
+ * @param pFlowBb The basic block to fill.
+ * @param cbDisasmMax The maximum amount to disassemble.
+ * @param fFlags Combination of DBGF_DISAS_FLAGS_*.
+ */
+static int dbgfR3FlowBbProcess(PUVM pUVM, VMCPUID idCpu, PDBGFFLOWINT pThis, PDBGFFLOWBBINT pFlowBb,
+ uint32_t cbDisasmMax, uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t cbDisasmLeft = cbDisasmMax ? cbDisasmMax : UINT32_MAX;
+ DBGFADDRESS AddrDisasm = pFlowBb->AddrEnd;
+
+ Assert(pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY);
+
+ /*
+ * Disassemble instruction by instruction until we get a conditional or
+ * unconditional jump or some sort of return.
+ */
+ while ( cbDisasmLeft
+ && RT_SUCCESS(rc))
+ {
+ DBGFDISSTATE DisState;
+ char szOutput[_4K];
+
+ /*
+ * Before disassembling we have to check whether the address belongs
+ * to another basic block and stop here.
+ */
+ if ( !(pFlowBb->fFlags & DBGF_FLOW_BB_F_EMPTY)
+ && dbgfR3FlowHasBbWithStartAddr(pThis, &AddrDisasm))
+ {
+ pFlowBb->AddrTarget = AddrDisasm;
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND;
+ break;
+ }
+
+ pFlowBb->fFlags &= ~DBGF_FLOW_BB_F_EMPTY;
+
+ rc = dbgfR3DisasInstrStateEx(pUVM, idCpu, &AddrDisasm, fFlags,
+ &szOutput[0], sizeof(szOutput), &DisState);
+ if (RT_SUCCESS(rc))
+ {
+ cbDisasmLeft -= DisState.cbInstr;
+
+ if (pFlowBb->cInstr == pFlowBb->cInstrMax)
+ {
+ /* Reallocate. */
+ RTListNodeRemove(&pFlowBb->NdFlowBb);
+ PDBGFFLOWBBINT pFlowBbNew = (PDBGFFLOWBBINT)RTMemRealloc(pFlowBb,
+ RT_UOFFSETOF_DYN(DBGFFLOWBBINT, aInstr[pFlowBb->cInstrMax + 10]));
+ if (pFlowBbNew)
+ {
+ pFlowBbNew->cInstrMax += 10;
+ pFlowBb = pFlowBbNew;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTListAppend(&pThis->LstFlowBb, &pFlowBb->NdFlowBb);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ PDBGFFLOWBBINSTR pInstr = &pFlowBb->aInstr[pFlowBb->cInstr];
+
+ pInstr->AddrInstr = AddrDisasm;
+ pInstr->cbInstr = DisState.cbInstr;
+ pInstr->pszInstr = RTStrCacheEnter(pThis->hStrCacheInstr, &szOutput[0]);
+ pFlowBb->cInstr++;
+
+ pFlowBb->AddrEnd = AddrDisasm;
+ DBGFR3AddrAdd(&pFlowBb->AddrEnd, pInstr->cbInstr - 1);
+ DBGFR3AddrAdd(&AddrDisasm, pInstr->cbInstr);
+
+ /*
+ * Check control flow instructions and create new basic blocks
+ * marking the current one as complete.
+ */
+ if (DisState.pCurInstr->fOpType & DISOPTYPE_CONTROLFLOW)
+ {
+ uint16_t uOpc = DisState.pCurInstr->uOpcode;
+
+ if ( uOpc == OP_RETN || uOpc == OP_RETF || uOpc == OP_IRET
+ || uOpc == OP_SYSEXIT || uOpc == OP_SYSRET)
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_EXIT;
+ else if (uOpc == OP_JMP)
+ {
+ Assert(DisState.pCurInstr->fOpType & DISOPTYPE_UNCOND_CONTROLFLOW);
+
+ if (dbgfR3FlowBranchTargetIsIndirect(&DisState.Param1))
+ {
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP;
+
+ if (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE)
+ {
+ Assert(pThis->fFlags & DBGF_FLOW_CREATE_F_TRY_RESOLVE_INDIRECT_BRANCHES);
+
+ /*
+ * This basic block was already discovered by parsing a jump table and
+ * there should be a candidate for the branch table. Check whether it uses the
+ * same branch table.
+ */
+ rc = dbgfR3FlowBbCheckBranchTblCandidate(pThis, pFlowBb, pUVM, idCpu,
+ &DisState.Param1, fFlags);
+ }
+ else
+ {
+ if (pThis->fFlags & DBGF_FLOW_CREATE_F_TRY_RESOLVE_INDIRECT_BRANCHES)
+ rc = dbgfR3FlowTryResolveIndirectBranch(pThis, pFlowBb, pUVM, idCpu,
+ &DisState.Param1, fFlags);
+ else
+ dbgfR3FlowBbSetError(pFlowBb, VERR_NOT_SUPPORTED,
+ "Detected indirect branch and resolving it not being enabled");
+ }
+ }
+ else
+ {
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_UNCOND_JMP;
+
+ /* Create one new basic block with the jump target address. */
+ rc = dbgfR3FlowQueryDirectBranchTarget(pUVM, idCpu, &DisState.Param1, &pInstr->AddrInstr, pInstr->cbInstr,
+ RT_BOOL(DisState.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW),
+ &pFlowBb->AddrTarget);
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3FlowBbSuccessorAdd(pThis, &pFlowBb->AddrTarget,
+ (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE),
+ pFlowBb->pFlowBranchTbl);
+ }
+ }
+ else if (uOpc != OP_CALL)
+ {
+ Assert(DisState.pCurInstr->fOpType & DISOPTYPE_COND_CONTROLFLOW);
+ pFlowBb->enmEndType = DBGFFLOWBBENDTYPE_COND;
+
+ /*
+ * Create two new basic blocks, one with the jump target address
+ * and one starting after the current instruction.
+ */
+ rc = dbgfR3FlowBbSuccessorAdd(pThis, &AddrDisasm,
+ (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE),
+ pFlowBb->pFlowBranchTbl);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dbgfR3FlowQueryDirectBranchTarget(pUVM, idCpu, &DisState.Param1, &pInstr->AddrInstr, pInstr->cbInstr,
+ RT_BOOL(DisState.pCurInstr->fOpType & DISOPTYPE_RELATIVE_CONTROLFLOW),
+ &pFlowBb->AddrTarget);
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3FlowBbSuccessorAdd(pThis, &pFlowBb->AddrTarget,
+ (pFlowBb->fFlags & DBGF_FLOW_BB_F_BRANCH_TABLE),
+ pFlowBb->pFlowBranchTbl);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ dbgfR3FlowBbSetError(pFlowBb, rc, "Adding successor blocks failed with %Rrc", rc);
+
+ /* Quit disassembling. */
+ if ( uOpc != OP_CALL
+ || RT_FAILURE(rc))
+ break;
+ }
+ }
+ else
+ dbgfR3FlowBbSetError(pFlowBb, rc, "Increasing basic block failed with %Rrc", rc);
+ }
+ else
+ dbgfR3FlowBbSetError(pFlowBb, rc, "Disassembling the instruction failed with %Rrc", rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Populate all empty basic blocks.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param pThis The control flow graph to populate.
+ * @param pAddrStart The start address to disassemble at.
+ * @param cbDisasmMax The maximum amount to disassemble.
+ * @param fFlags Combination of DBGF_DISAS_FLAGS_*.
+ */
+static int dbgfR3FlowPopulate(PUVM pUVM, VMCPUID idCpu, PDBGFFLOWINT pThis, PDBGFADDRESS pAddrStart,
+ uint32_t cbDisasmMax, uint32_t fFlags)
+{
+ int rc = VINF_SUCCESS;
+ PDBGFFLOWBBINT pFlowBb = dbgfR3FlowGetUnpopulatedBb(pThis);
+ DBGFADDRESS AddrEnd = *pAddrStart;
+ DBGFR3AddrAdd(&AddrEnd, cbDisasmMax);
+
+ while (VALID_PTR(pFlowBb))
+ {
+ rc = dbgfR3FlowBbProcess(pUVM, idCpu, pThis, pFlowBb, cbDisasmMax, fFlags);
+ if (RT_FAILURE(rc))
+ break;
+
+ pFlowBb = dbgfR3FlowGetUnpopulatedBb(pThis);
+ }
+
+ return rc;
+}
+
+/**
+ * Creates a new control flow graph from the given start address.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param idCpu CPU id for disassembling.
+ * @param pAddressStart Where to start creating the control flow graph.
+ * @param cbDisasmMax Limit the amount of bytes to disassemble, 0 for no limit.
+ * @param fFlagsFlow Combination of DBGF_FLOW_CREATE_F_* to control the creation of the flow graph.
+ * @param fFlagsDisasm Combination of DBGF_DISAS_FLAGS_* controlling the style of the disassembled
+ * instructions.
+ * @param phFlow Where to store the handle to the control flow graph on success.
+ */
+VMMR3DECL(int) DBGFR3FlowCreate(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddressStart, uint32_t cbDisasmMax,
+ uint32_t fFlagsFlow, uint32_t fFlagsDisasm, PDBGFFLOW phFlow)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_CPU_ID);
+ AssertPtrReturn(pAddressStart, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlagsDisasm & ~DBGF_DISAS_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlagsDisasm & DBGF_DISAS_FLAGS_MODE_MASK) <= DBGF_DISAS_FLAGS_64BIT_MODE, VERR_INVALID_PARAMETER);
+
+ /* Create the control flow graph container. */
+ int rc = VINF_SUCCESS;
+ PDBGFFLOWINT pThis = (PDBGFFLOWINT)RTMemAllocZ(sizeof(DBGFFLOWINT));
+ if (RT_LIKELY(pThis))
+ {
+ rc = RTStrCacheCreate(&pThis->hStrCacheInstr, "DBGFFLOW");
+ if (RT_SUCCESS(rc))
+ {
+ pThis->cRefs = 1;
+ pThis->cRefsBb = 0;
+ pThis->cBbs = 0;
+ pThis->cBranchTbls = 0;
+ pThis->fFlags = fFlagsFlow;
+ RTListInit(&pThis->LstFlowBb);
+ RTListInit(&pThis->LstBranchTbl);
+ /* Create the entry basic block and start the work. */
+
+ PDBGFFLOWBBINT pFlowBb = dbgfR3FlowBbCreate(pThis, pAddressStart, DBGF_FLOW_BB_F_ENTRY, 10);
+ if (RT_LIKELY(pFlowBb))
+ {
+ dbgfR3FlowLink(pThis, pFlowBb);
+ rc = dbgfR3FlowPopulate(pUVM, idCpu, pThis, pAddressStart, cbDisasmMax, fFlagsDisasm);
+ if (RT_SUCCESS(rc))
+ {
+ *phFlow = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ ASMAtomicDecU32(&pThis->cRefs);
+ dbgfR3FlowDestroy(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Retains the control flow graph handle.
+ *
+ * @returns Current reference count.
+ * @param hFlow The control flow graph handle to retain.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowRetain(DBGFFLOW hFlow)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+/**
+ * Releases the control flow graph handle.
+ *
+ * @returns Current reference count, on 0 the control flow graph will be destroyed.
+ * @param hFlow The control flow graph handle to release.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowRelease(DBGFFLOW hFlow)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ dbgfR3FlowDestroy(pThis);
+ return cRefs;
+}
+
+
+/**
+ * Queries the basic block denoting the entry point into the control flow graph.
+ *
+ * @returns VBox status code.
+ * @param hFlow The control flow graph handle.
+ * @param phFlowBb Where to store the basic block handle on success.
+ */
+VMMR3DECL(int) DBGFR3FlowQueryStartBb(DBGFFLOW hFlow, PDBGFFLOWBB phFlowBb)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ if (pFlowBb->fFlags & DBGF_FLOW_BB_F_ENTRY)
+ {
+ *phFlowBb = pFlowBb;
+ return VINF_SUCCESS;
+ }
+ }
+
+ AssertFailed(); /* Should never get here. */
+ return VERR_INTERNAL_ERROR;
+}
+
+
+/**
+ * Queries a basic block in the given control flow graph which covers the given
+ * address.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if there is no basic block intersecting with the address.
+ * @param hFlow The control flow graph handle.
+ * @param pAddr The address to look for.
+ * @param phFlowBb Where to store the basic block handle on success.
+ */
+VMMR3DECL(int) DBGFR3FlowQueryBbByAddress(DBGFFLOW hFlow, PDBGFADDRESS pAddr, PDBGFFLOWBB phFlowBb)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pAddr, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFlowBb, VERR_INVALID_POINTER);
+
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pThis->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ if (dbgfR3FlowAddrIntersect(pFlowBb, pAddr))
+ {
+ DBGFR3FlowBbRetain(pFlowBb);
+ *phFlowBb = pFlowBb;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Queries a branch table in the given control flow graph by the given address.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if there is no branch table with the given address.
+ * @param hFlow The control flow graph handle.
+ * @param pAddr The address of the branch table.
+ * @param phFlowBranchTbl Where to store the handle to branch table on success.
+ *
+ * @note Call DBGFR3FlowBranchTblRelease() when the handle is not required anymore.
+ */
+VMMR3DECL(int) DBGFR3FlowQueryBranchTblByAddress(DBGFFLOW hFlow, PDBGFADDRESS pAddr, PDBGFFLOWBRANCHTBL phFlowBranchTbl)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pAddr, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFlowBranchTbl, VERR_INVALID_POINTER);
+
+ PDBGFFLOWBRANCHTBLINT pBranchTbl = dbgfR3FlowBranchTblFindByAddr(pThis, pAddr);
+ if (pBranchTbl)
+ {
+ DBGFR3FlowBranchTblRetain(pBranchTbl);
+ *phFlowBranchTbl = pBranchTbl;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Returns the number of basic blcoks inside the control flow graph.
+ *
+ * @returns Number of basic blocks.
+ * @param hFlow The control flow graph handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowGetBbCount(DBGFFLOW hFlow)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cBbs;
+}
+
+
+/**
+ * Returns the number of branch tables inside the control flow graph.
+ *
+ * @returns Number of basic blocks.
+ * @param hFlow The control flow graph handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowGetBranchTblCount(DBGFFLOW hFlow)
+{
+ PDBGFFLOWINT pThis = hFlow;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cBranchTbls;
+}
+
+
+/**
+ * Retains the basic block handle.
+ *
+ * @returns Current reference count.
+ * @param hFlowBb The basic block handle to retain.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBbRetain(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pFlowBb->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p %d\n", cRefs, pFlowBb, pFlowBb->enmEndType));
+ return cRefs;
+}
+
+
+/**
+ * Releases the basic block handle.
+ *
+ * @returns Current reference count, on 0 the basic block will be destroyed.
+ * @param hFlowBb The basic block handle to release.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBbRelease(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ if (!pFlowBb)
+ return 0;
+
+ return dbgfR3FlowBbReleaseInt(pFlowBb, true /* fMayDestroyFlow */);
+}
+
+
+/**
+ * Returns the start address of the basic block.
+ *
+ * @returns Pointer to DBGF adress containing the start address of the basic block.
+ * @param hFlowBb The basic block handle.
+ * @param pAddrStart Where to store the start address of the basic block.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetStartAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrStart)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, NULL);
+ AssertPtrReturn(pAddrStart, NULL);
+
+ *pAddrStart = pFlowBb->AddrStart;
+ return pAddrStart;
+}
+
+
+/**
+ * Returns the end address of the basic block (inclusive).
+ *
+ * @returns Pointer to DBGF adress containing the end address of the basic block.
+ * @param hFlowBb The basic block handle.
+ * @param pAddrEnd Where to store the end address of the basic block.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetEndAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrEnd)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, NULL);
+ AssertPtrReturn(pAddrEnd, NULL);
+
+ *pAddrEnd = pFlowBb->AddrEnd;
+ return pAddrEnd;
+}
+
+
+/**
+ * Returns the address the last instruction in the basic block branches to.
+ *
+ * @returns Pointer to DBGF adress containing the branch address of the basic block.
+ * @param hFlowBb The basic block handle.
+ * @param pAddrTarget Where to store the branch address of the basic block.
+ *
+ * @note This is only valid for unconditional or conditional branches and will assert
+ * for every other basic block type.
+ * @note For indirect unconditional branches using a branch table this will return the start address
+ * of the branch table.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetBranchAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrTarget)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, NULL);
+ AssertPtrReturn(pAddrTarget, NULL);
+ AssertReturn( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP
+ || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND
+ || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP,
+ NULL);
+
+ if ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP
+ && pFlowBb->pFlowBranchTbl)
+ *pAddrTarget = pFlowBb->pFlowBranchTbl->AddrStart;
+ else
+ *pAddrTarget = pFlowBb->AddrTarget;
+ return pAddrTarget;
+}
+
+
+/**
+ * Returns the address of the next block following this one in the instruction stream.
+ * (usually end address + 1).
+ *
+ * @returns Pointer to DBGF adress containing the following address of the basic block.
+ * @param hFlowBb The basic block handle.
+ * @param pAddrFollow Where to store the following address of the basic block.
+ *
+ * @note This is only valid for conditional branches and if the last instruction in the
+ * given basic block doesn't change the control flow but the blocks were split
+ * because the successor is referenced by multiple other blocks as an entry point.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBbGetFollowingAddress(DBGFFLOWBB hFlowBb, PDBGFADDRESS pAddrFollow)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, NULL);
+ AssertPtrReturn(pAddrFollow, NULL);
+ AssertReturn( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND
+ || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND,
+ NULL);
+
+ *pAddrFollow = pFlowBb->AddrEnd;
+ DBGFR3AddrAdd(pAddrFollow, 1);
+ return pAddrFollow;
+}
+
+
+/**
+ * Returns the type of the last instruction in the basic block.
+ *
+ * @returns Last instruction type.
+ * @param hFlowBb The basic block handle.
+ */
+VMMR3DECL(DBGFFLOWBBENDTYPE) DBGFR3FlowBbGetType(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, DBGFFLOWBBENDTYPE_INVALID);
+
+ return pFlowBb->enmEndType;
+}
+
+
+/**
+ * Get the number of instructions contained in the basic block.
+ *
+ * @returns Number of instructions in the basic block.
+ * @param hFlowBb The basic block handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBbGetInstrCount(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, 0);
+
+ return pFlowBb->cInstr;
+}
+
+
+/**
+ * Get flags for the given basic block.
+ *
+ * @returns Combination of DBGF_FLOW_BB_F_*
+ * @param hFlowBb The basic block handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBbGetFlags(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, 0);
+
+ return pFlowBb->fFlags;
+}
+
+
+/**
+ * Queries the branch table used if the given basic block ends with an indirect branch
+ * and has a branch table referenced.
+ *
+ * @returns VBox status code.
+ * @param hFlowBb The basic block handle.
+ * @param phBranchTbl Where to store the branch table handle on success.
+ *
+ * @note Release the branch table reference with DBGFR3FlowBranchTblRelease() when not required
+ * anymore.
+ */
+VMMR3DECL(int) DBGFR3FlowBbQueryBranchTbl(DBGFFLOWBB hFlowBb, PDBGFFLOWBRANCHTBL phBranchTbl)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, VERR_INVALID_HANDLE);
+ AssertReturn(pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_INDIRECT_JMP, VERR_INVALID_STATE);
+ AssertPtrReturn(pFlowBb->pFlowBranchTbl, VERR_INVALID_STATE);
+ AssertPtrReturn(phBranchTbl, VERR_INVALID_POINTER);
+
+ DBGFR3FlowBranchTblRetain(pFlowBb->pFlowBranchTbl);
+ *phBranchTbl = pFlowBb->pFlowBranchTbl;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Returns the error status and message if the given basic block has an error.
+ *
+ * @returns VBox status code of the error for the basic block.
+ * @param hFlowBb The basic block handle.
+ * @param ppszErr Where to store the pointer to the error message - optional.
+ */
+VMMR3DECL(int) DBGFR3FlowBbQueryError(DBGFFLOWBB hFlowBb, const char **ppszErr)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, VERR_INVALID_HANDLE);
+
+ if (ppszErr)
+ *ppszErr = pFlowBb->pszErr;
+
+ return pFlowBb->rcError;
+}
+
+
+/**
+ * Store the disassembled instruction as a string in the given output buffer.
+ *
+ * @returns VBox status code.
+ * @param hFlowBb The basic block handle.
+ * @param idxInstr The instruction to query.
+ * @param pAddrInstr Where to store the guest instruction address on success, optional.
+ * @param pcbInstr Where to store the instruction size on success, optional.
+ * @param ppszInstr Where to store the pointer to the disassembled instruction string, optional.
+ */
+VMMR3DECL(int) DBGFR3FlowBbQueryInstr(DBGFFLOWBB hFlowBb, uint32_t idxInstr, PDBGFADDRESS pAddrInstr,
+ uint32_t *pcbInstr, const char **ppszInstr)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, VERR_INVALID_POINTER);
+ AssertReturn(idxInstr < pFlowBb->cInstr, VERR_INVALID_PARAMETER);
+
+ if (pAddrInstr)
+ *pAddrInstr = pFlowBb->aInstr[idxInstr].AddrInstr;
+ if (pcbInstr)
+ *pcbInstr = pFlowBb->aInstr[idxInstr].cbInstr;
+ if (ppszInstr)
+ *ppszInstr = pFlowBb->aInstr[idxInstr].pszInstr;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Queries the successors of the basic block.
+ *
+ * @returns VBox status code.
+ * @param hFlowBb The basic block handle.
+ * @param phFlowBbFollow Where to store the handle to the basic block following
+ * this one (optional).
+ * @param phFlowBbTarget Where to store the handle to the basic block being the
+ * branch target for this one (optional).
+ */
+VMMR3DECL(int) DBGFR3FlowBbQuerySuccessors(DBGFFLOWBB hFlowBb, PDBGFFLOWBB phFlowBbFollow, PDBGFFLOWBB phFlowBbTarget)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, VERR_INVALID_POINTER);
+
+ if ( phFlowBbFollow
+ && ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND
+ || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND))
+ {
+ DBGFADDRESS AddrStart = pFlowBb->AddrEnd;
+ DBGFR3AddrAdd(&AddrStart, 1);
+ int rc = DBGFR3FlowQueryBbByAddress(pFlowBb->pFlow, &AddrStart, phFlowBbFollow);
+ AssertRC(rc);
+ }
+
+ if ( phFlowBbTarget
+ && ( pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP
+ || pFlowBb->enmEndType == DBGFFLOWBBENDTYPE_COND))
+ {
+ int rc = DBGFR3FlowQueryBbByAddress(pFlowBb->pFlow, &pFlowBb->AddrTarget, phFlowBbTarget);
+ AssertRC(rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Returns the number of basic blocks referencing this basic block as a target.
+ *
+ * @returns Number of other basic blocks referencing this one.
+ * @param hFlowBb The basic block handle.
+ *
+ * @note If the given basic block references itself (loop, etc.) this will be counted as well.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBbGetRefBbCount(DBGFFLOWBB hFlowBb)
+{
+ PDBGFFLOWBBINT pFlowBb = hFlowBb;
+ AssertPtrReturn(pFlowBb, 0);
+
+ uint32_t cRefsBb = 0;
+ PDBGFFLOWBBINT pFlowBbCur;
+ RTListForEach(&pFlowBb->pFlow->LstFlowBb, pFlowBbCur, DBGFFLOWBBINT, NdFlowBb)
+ {
+ if (pFlowBbCur->fFlags & DBGF_FLOW_BB_F_INCOMPLETE_ERR)
+ continue;
+
+ if ( pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_UNCOND
+ || pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_COND)
+ {
+ DBGFADDRESS AddrStart = pFlowBb->AddrEnd;
+ DBGFR3AddrAdd(&AddrStart, 1);
+ if (dbgfR3FlowAddrEqual(&pFlowBbCur->AddrStart, &AddrStart))
+ cRefsBb++;
+ }
+
+ if ( ( pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_UNCOND_JMP
+ || pFlowBbCur->enmEndType == DBGFFLOWBBENDTYPE_COND)
+ && dbgfR3FlowAddrEqual(&pFlowBbCur->AddrStart, &pFlowBb->AddrTarget))
+ cRefsBb++;
+ }
+ return cRefsBb;
+}
+
+
+/**
+ * Returns the basic block handles referencing the given basic block.
+ *
+ * @returns VBox status code.
+ * @retval VERR_BUFFER_OVERFLOW if the array can't hold all the basic blocks.
+ * @param hFlowBb The basic block handle.
+ * @param paFlowBbRef Pointer to the array containing the referencing basic block handles on success.
+ * @param cRef Number of entries in the given array.
+ */
+VMMR3DECL(int) DBGFR3FlowBbGetRefBb(DBGFFLOWBB hFlowBb, PDBGFFLOWBB paFlowBbRef, uint32_t cRef)
+{
+ RT_NOREF3(hFlowBb, paFlowBbRef, cRef);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Retains a reference for the given control flow graph branch table.
+ *
+ * @returns new reference count.
+ * @param hFlowBranchTbl The branch table handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBranchTblRetain(DBGFFLOWBRANCHTBL hFlowBranchTbl)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ AssertPtrReturn(pFlowBranchTbl, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pFlowBranchTbl->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pFlowBranchTbl));
+ return cRefs;
+}
+
+
+/**
+ * Releases a given branch table handle.
+ *
+ * @returns the new reference count of the given branch table, on 0 it is destroyed.
+ * @param hFlowBranchTbl The branch table handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBranchTblRelease(DBGFFLOWBRANCHTBL hFlowBranchTbl)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ if (!pFlowBranchTbl)
+ return 0;
+ AssertPtrReturn(pFlowBranchTbl, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pFlowBranchTbl->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pFlowBranchTbl));
+ if (cRefs == 0)
+ dbgfR3FlowBranchTblDestroy(pFlowBranchTbl);
+ return cRefs;
+}
+
+
+/**
+ * Return the number of slots the branch table has.
+ *
+ * @returns Number of slots in the branch table.
+ * @param hFlowBranchTbl The branch table handle.
+ */
+VMMR3DECL(uint32_t) DBGFR3FlowBranchTblGetSlots(DBGFFLOWBRANCHTBL hFlowBranchTbl)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ AssertPtrReturn(pFlowBranchTbl, 0);
+
+ return pFlowBranchTbl->cSlots;
+}
+
+
+/**
+ * Returns the start address of the branch table in the guest.
+ *
+ * @returns Pointer to start address of the branch table (pAddrStart).
+ * @param hFlowBranchTbl The branch table handle.
+ * @param pAddrStart Where to store the branch table address.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBranchTblGetStartAddress(DBGFFLOWBRANCHTBL hFlowBranchTbl, PDBGFADDRESS pAddrStart)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ AssertPtrReturn(pFlowBranchTbl, NULL);
+ AssertPtrReturn(pAddrStart, NULL);
+
+ *pAddrStart = pFlowBranchTbl->AddrStart;
+ return pAddrStart;
+}
+
+
+/**
+ * Returns one address in the branch table at the given slot index.
+ *
+ * @return Pointer to the address at the given slot in the given branch table.
+ * @param hFlowBranchTbl The branch table handle.
+ * @param idxSlot The slot the address should be returned from.
+ * @param pAddrSlot Where to store the address.
+ */
+VMMR3DECL(PDBGFADDRESS) DBGFR3FlowBranchTblGetAddrAtSlot(DBGFFLOWBRANCHTBL hFlowBranchTbl, uint32_t idxSlot, PDBGFADDRESS pAddrSlot)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ AssertPtrReturn(pFlowBranchTbl, NULL);
+ AssertPtrReturn(pAddrSlot, NULL);
+ AssertReturn(idxSlot < pFlowBranchTbl->cSlots, NULL);
+
+ *pAddrSlot = pFlowBranchTbl->aAddresses[idxSlot];
+ return pAddrSlot;
+}
+
+
+/**
+ * Query all addresses contained in the given branch table.
+ *
+ * @returns VBox status code.
+ * @retval VERR_BUFFER_OVERFLOW if there is not enough space in the array to hold all addresses.
+ * @param hFlowBranchTbl The branch table handle.
+ * @param paAddrs Where to store the addresses on success.
+ * @param cAddrs Number of entries the array can hold.
+ */
+VMMR3DECL(int) DBGFR3FlowBranchTblQueryAddresses(DBGFFLOWBRANCHTBL hFlowBranchTbl, PDBGFADDRESS paAddrs, uint32_t cAddrs)
+{
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl = hFlowBranchTbl;
+ AssertPtrReturn(pFlowBranchTbl, VERR_INVALID_HANDLE);
+ AssertPtrReturn(paAddrs, VERR_INVALID_POINTER);
+ AssertReturn(cAddrs > 0, VERR_INVALID_PARAMETER);
+
+ if (cAddrs < pFlowBranchTbl->cSlots)
+ return VERR_BUFFER_OVERFLOW;
+
+ memcpy(paAddrs, &pFlowBranchTbl->aAddresses[0], pFlowBranchTbl->cSlots * sizeof(DBGFADDRESS));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSORTCMP}
+ */
+static DECLCALLBACK(int) dbgfR3FlowItSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ PDBGFFLOWITORDER penmOrder = (PDBGFFLOWITORDER)pvUser;
+ PDBGFFLOWBBINT pFlowBb1 = *(PDBGFFLOWBBINT *)pvElement1;
+ PDBGFFLOWBBINT pFlowBb2 = *(PDBGFFLOWBBINT *)pvElement2;
+
+ if (dbgfR3FlowAddrEqual(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart))
+ return 0;
+
+ if (*penmOrder == DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST)
+ {
+ if (dbgfR3FlowAddrLower(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart))
+ return -1;
+ else
+ return 1;
+ }
+ else
+ {
+ if (dbgfR3FlowAddrLower(&pFlowBb1->AddrStart, &pFlowBb2->AddrStart))
+ return 1;
+ else
+ return -1;
+ }
+}
+
+
+/**
+ * Creates a new iterator for the given control flow graph.
+ *
+ * @returns VBox status code.
+ * @param hFlow The control flow graph handle.
+ * @param enmOrder The order in which the basic blocks are enumerated.
+ * @param phFlowIt Where to store the handle to the iterator on success.
+ */
+VMMR3DECL(int) DBGFR3FlowItCreate(DBGFFLOW hFlow, DBGFFLOWITORDER enmOrder, PDBGFFLOWIT phFlowIt)
+{
+ int rc = VINF_SUCCESS;
+ PDBGFFLOWINT pFlow = hFlow;
+ AssertPtrReturn(pFlow, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFlowIt, VERR_INVALID_POINTER);
+ AssertReturn(enmOrder > DBGFFLOWITORDER_INVALID && enmOrder < DBGFFLOWITORDER_BREADTH_FIRST,
+ VERR_INVALID_PARAMETER);
+ AssertReturn(enmOrder < DBGFFLOWITORDER_DEPTH_FRIST, VERR_NOT_IMPLEMENTED); /** @todo */
+
+ PDBGFFLOWITINT pIt = (PDBGFFLOWITINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWITINT, apBb[pFlow->cBbs]));
+ if (RT_LIKELY(pIt))
+ {
+ DBGFR3FlowRetain(hFlow);
+ pIt->pFlow = pFlow;
+ pIt->idxBbNext = 0;
+ /* Fill the list and then sort. */
+ uint32_t idxBb = 0;
+ PDBGFFLOWBBINT pFlowBb;
+ RTListForEach(&pFlow->LstFlowBb, pFlowBb, DBGFFLOWBBINT, NdFlowBb)
+ {
+ DBGFR3FlowBbRetain(pFlowBb);
+ pIt->apBb[idxBb++] = pFlowBb;
+ }
+
+ /* Sort the blocks by address. */
+ RTSortShell(&pIt->apBb[0], pFlow->cBbs, sizeof(PDBGFFLOWBBINT), dbgfR3FlowItSortCmp, &enmOrder);
+
+ *phFlowIt = pIt;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Destroys a given control flow graph iterator.
+ *
+ * @returns nothing.
+ * @param hFlowIt The control flow graph iterator handle.
+ */
+VMMR3DECL(void) DBGFR3FlowItDestroy(DBGFFLOWIT hFlowIt)
+{
+ PDBGFFLOWITINT pIt = hFlowIt;
+ AssertPtrReturnVoid(pIt);
+
+ for (unsigned i = 0; i < pIt->pFlow->cBbs; i++)
+ DBGFR3FlowBbRelease(pIt->apBb[i]);
+
+ DBGFR3FlowRelease(pIt->pFlow);
+ RTMemFree(pIt);
+}
+
+
+/**
+ * Returns the next basic block in the iterator or NULL if there is no
+ * basic block left.
+ *
+ * @returns Handle to the next basic block in the iterator or NULL if the end
+ * was reached.
+ * @param hFlowIt The iterator handle.
+ *
+ * @note If a valid handle is returned it must be release with DBGFR3FlowBbRelease()
+ * when not required anymore.
+ */
+VMMR3DECL(DBGFFLOWBB) DBGFR3FlowItNext(DBGFFLOWIT hFlowIt)
+{
+ PDBGFFLOWITINT pIt = hFlowIt;
+ AssertPtrReturn(pIt, NULL);
+
+ PDBGFFLOWBBINT pFlowBb = NULL;
+ if (pIt->idxBbNext < pIt->pFlow->cBbs)
+ {
+ pFlowBb = pIt->apBb[pIt->idxBbNext++];
+ DBGFR3FlowBbRetain(pFlowBb);
+ }
+
+ return pFlowBb;
+}
+
+
+/**
+ * Resets the given iterator to the beginning.
+ *
+ * @returns VBox status code.
+ * @param hFlowIt The iterator handle.
+ */
+VMMR3DECL(int) DBGFR3FlowItReset(DBGFFLOWIT hFlowIt)
+{
+ PDBGFFLOWITINT pIt = hFlowIt;
+ AssertPtrReturn(pIt, VERR_INVALID_HANDLE);
+
+ pIt->idxBbNext = 0;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNRTSORTCMP}
+ */
+static DECLCALLBACK(int) dbgfR3FlowBranchTblItSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
+{
+ PDBGFFLOWITORDER penmOrder = (PDBGFFLOWITORDER)pvUser;
+ PDBGFFLOWBRANCHTBLINT pTbl1 = *(PDBGFFLOWBRANCHTBLINT *)pvElement1;
+ PDBGFFLOWBRANCHTBLINT pTbl2 = *(PDBGFFLOWBRANCHTBLINT *)pvElement2;
+
+ if (dbgfR3FlowAddrEqual(&pTbl1->AddrStart, &pTbl2->AddrStart))
+ return 0;
+
+ if (*penmOrder == DBGFFLOWITORDER_BY_ADDR_LOWEST_FIRST)
+ {
+ if (dbgfR3FlowAddrLower(&pTbl1->AddrStart, &pTbl2->AddrStart))
+ return -1;
+ else
+ return 1;
+ }
+ else
+ {
+ if (dbgfR3FlowAddrLower(&pTbl1->AddrStart, &pTbl2->AddrStart))
+ return 1;
+ else
+ return -1;
+ }
+}
+
+
+/**
+ * Creates a new branch table iterator for the given control flow graph.
+ *
+ * @returns VBox status code.
+ * @param hFlow The control flow graph handle.
+ * @param enmOrder The order in which the basic blocks are enumerated.
+ * @param phFlowBranchTblIt Where to store the handle to the iterator on success.
+ */
+VMMR3DECL(int) DBGFR3FlowBranchTblItCreate(DBGFFLOW hFlow, DBGFFLOWITORDER enmOrder,
+ PDBGFFLOWBRANCHTBLIT phFlowBranchTblIt)
+{
+ int rc = VINF_SUCCESS;
+ PDBGFFLOWINT pFlow = hFlow;
+ AssertPtrReturn(pFlow, VERR_INVALID_POINTER);
+ AssertPtrReturn(phFlowBranchTblIt, VERR_INVALID_POINTER);
+ AssertReturn(enmOrder > DBGFFLOWITORDER_INVALID && enmOrder < DBGFFLOWITORDER_BREADTH_FIRST,
+ VERR_INVALID_PARAMETER);
+ AssertReturn(enmOrder < DBGFFLOWITORDER_DEPTH_FRIST, VERR_NOT_SUPPORTED);
+
+ PDBGFFLOWBRANCHTBLITINT pIt = (PDBGFFLOWBRANCHTBLITINT)RTMemAllocZ(RT_UOFFSETOF_DYN(DBGFFLOWBRANCHTBLITINT,
+ apBranchTbl[pFlow->cBranchTbls]));
+ if (RT_LIKELY(pIt))
+ {
+ DBGFR3FlowRetain(hFlow);
+ pIt->pFlow = pFlow;
+ pIt->idxTblNext = 0;
+ /* Fill the list and then sort. */
+ uint32_t idxTbl = 0;
+ PDBGFFLOWBRANCHTBLINT pFlowBranchTbl;
+ RTListForEach(&pFlow->LstBranchTbl, pFlowBranchTbl, DBGFFLOWBRANCHTBLINT, NdBranchTbl)
+ {
+ DBGFR3FlowBranchTblRetain(pFlowBranchTbl);
+ pIt->apBranchTbl[idxTbl++] = pFlowBranchTbl;
+ }
+
+ /* Sort the blocks by address. */
+ RTSortShell(&pIt->apBranchTbl[0], pFlow->cBranchTbls, sizeof(PDBGFFLOWBRANCHTBLINT), dbgfR3FlowBranchTblItSortCmp, &enmOrder);
+
+ *phFlowBranchTblIt = pIt;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Destroys a given control flow graph branch table iterator.
+ *
+ * @returns nothing.
+ * @param hFlowBranchTblIt The control flow graph branch table iterator handle.
+ */
+VMMR3DECL(void) DBGFR3FlowBranchTblItDestroy(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt)
+{
+ PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt;
+ AssertPtrReturnVoid(pIt);
+
+ for (unsigned i = 0; i < pIt->pFlow->cBranchTbls; i++)
+ DBGFR3FlowBranchTblRelease(pIt->apBranchTbl[i]);
+
+ DBGFR3FlowRelease(pIt->pFlow);
+ RTMemFree(pIt);
+}
+
+
+/**
+ * Returns the next branch table in the iterator or NULL if there is no
+ * branch table left.
+ *
+ * @returns Handle to the next basic block in the iterator or NULL if the end
+ * was reached.
+ * @param hFlowBranchTblIt The iterator handle.
+ *
+ * @note If a valid handle is returned it must be release with DBGFR3FlowBranchTblRelease()
+ * when not required anymore.
+ */
+VMMR3DECL(DBGFFLOWBRANCHTBL) DBGFR3FlowBranchTblItNext(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt)
+{
+ PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt;
+ AssertPtrReturn(pIt, NULL);
+
+ PDBGFFLOWBRANCHTBLINT pTbl = NULL;
+ if (pIt->idxTblNext < pIt->pFlow->cBranchTbls)
+ {
+ pTbl = pIt->apBranchTbl[pIt->idxTblNext++];
+ DBGFR3FlowBranchTblRetain(pTbl);
+ }
+
+ return pTbl;
+}
+
+
+/**
+ * Resets the given iterator to the beginning.
+ *
+ * @returns VBox status code.
+ * @param hFlowBranchTblIt The iterator handle.
+ */
+VMMR3DECL(int) DBGFR3FlowBranchTblItReset(DBGFFLOWBRANCHTBLIT hFlowBranchTblIt)
+{
+ PDBGFFLOWBRANCHTBLITINT pIt = hFlowBranchTblIt;
+ AssertPtrReturn(pIt, VERR_INVALID_HANDLE);
+
+ pIt->idxTblNext = 0;
+ return VINF_SUCCESS;
+}