summaryrefslogtreecommitdiffstats
path: root/src/VBox/VMM/VMMR3/DBGFR3Type.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/DBGFR3Type.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/DBGFR3Type.cpp1287
1 files changed, 1287 insertions, 0 deletions
diff --git a/src/VBox/VMM/VMMR3/DBGFR3Type.cpp b/src/VBox/VMM/VMMR3/DBGFR3Type.cpp
new file mode 100644
index 00000000..1be242aa
--- /dev/null
+++ b/src/VBox/VMM/VMMR3/DBGFR3Type.cpp
@@ -0,0 +1,1287 @@
+/* $Id: DBGFR3Type.cpp $ */
+/** @file
+ * DBGF - Debugger Facility, Type Management.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/** @page pg_dbgf_type DBGFType - Type Management
+ *
+ * The type management system is intended to ease retrieval of values from
+ * structures in the guest OS without having to take care of the size of pointers.
+ *
+ * @todo r=bird: We need to join this up with modules and address spaces. It
+ * cannot be standalone like this. Also, it must be comming from IPRT as
+ * there is no point in duplicating code (been there, done that with
+ * symbols and debug info already). This unfortunately means we need to
+ * find some common way of abstracting DWARF and Codeview type info so we
+ * can extend those debug info parsers to make type information available.
+ */
+
+
+/*********************************************************************************************************************************
+* 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/err.h>
+#include <VBox/log.h>
+
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/** Locks the type database for writing. */
+#define DBGF_TYPE_DB_LOCK_WRITE(pUVM) \
+ do { \
+ int rcSem = RTSemRWRequestWrite((pUVM)->dbgf.s.hTypeDbLock, RT_INDEFINITE_WAIT); \
+ AssertRC(rcSem); \
+ } while (0)
+
+/** Unlocks the type database after writing. */
+#define DBGF_TYPE_DB_UNLOCK_WRITE(pUVM) \
+ do { \
+ int rcSem = RTSemRWReleaseWrite((pUVM)->dbgf.s.hTypeDbLock); \
+ AssertRC(rcSem); \
+ } while (0)
+
+/** Locks the type database for reading. */
+#define DBGF_TYPE_DB_LOCK_READ(pUVM) \
+ do { \
+ int rcSem = RTSemRWRequestRead((pUVM)->dbgf.s.hTypeDbLock, RT_INDEFINITE_WAIT); \
+ AssertRC(rcSem); \
+ } while (0)
+
+/** Unlocks the type database after reading. */
+#define DBGF_TYPE_DB_UNLOCK_READ(pUVM) \
+ do { \
+ int rcSem = RTSemRWReleaseRead((pUVM)->dbgf.s.hTypeDbLock); \
+ AssertRC(rcSem); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * DBGF registered type.
+ */
+typedef struct DBGFTYPE
+{
+ /** String space core. */
+ RTSTRSPACECORE Core;
+ /** Pointer to the registration structure, NULL means builtin type. */
+ PCDBGFTYPEREG pReg;
+ /** How often the type is referenced by other types. */
+ volatile uint32_t cRefs;
+ /** Size of the type. */
+ size_t cbType;
+ /** Builtin type if pReg is NULL (otherwise it is invalid). */
+ DBGFTYPEBUILTIN enmTypeBuiltin;
+} DBGFTYPE;
+/** Pointer to a DBGF type. */
+typedef DBGFTYPE *PDBGFTYPE;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int dbgfR3TypeParseBufferByType(PUVM pUVM, PDBGFTYPE pType, uint8_t *pbBuf, size_t cbBuf,
+ PDBGFTYPEVAL *ppVal, size_t *pcbParsed);
+
+
+/**
+ * Looks up a type by the identifier.
+ *
+ * @returns Pointer to the type structure on success, NULL otherwise.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier.
+ */
+static PDBGFTYPE dbgfR3TypeLookup(PUVM pUVM, const char *pszType)
+{
+ PRTSTRSPACE pTypeSpace = &pUVM->dbgf.s.TypeSpace;
+ return (PDBGFTYPE)RTStrSpaceGet(pTypeSpace, pszType);
+}
+
+
+/**
+ * Calculate the size of the given type.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pType The type to calculate the size for.
+ * @param fCalcNested Flag whether to calculate the size for nested
+ * structs if the sizes are 0.
+ */
+static int dbgfR3TypeCalcSize(PUVM pUVM, PDBGFTYPE pType, bool fCalcNested)
+{
+ int rc = VINF_SUCCESS;
+
+ /* Builtin types are never recalculated. */
+ if (pType->pReg)
+ {
+ switch (pType->pReg->enmVariant)
+ {
+ case DBGFTYPEVARIANT_STRUCT:
+ {
+ size_t cbType = 0;
+
+ /* Go through the members and update size. */
+ for (uint32_t i = 0; i < pType->pReg->cMembers && RT_SUCCESS(rc); i++)
+ {
+ PCDBGFTYPEREGMEMBER pMember = &pType->pReg->paMembers[i];
+
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_POINTER)
+ {
+ /* Use the current pointer size. */
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, "ptr_t");
+ if (RT_LIKELY(pTypeMember))
+ {
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ cbType += pMember->cElements * pTypeMember->cbType;
+ else
+ cbType += pTypeMember->cbType;
+ }
+ }
+ else
+ {
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, pMember->pszType);
+ if (RT_LIKELY(pTypeMember))
+ {
+ if ( pTypeMember->cbType == 0
+ && fCalcNested)
+ rc = dbgfR3TypeCalcSize(pUVM, pTypeMember, fCalcNested);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ cbType += pMember->cElements * pTypeMember->cbType;
+ else
+ cbType += pTypeMember->cbType;
+ }
+ }
+ else
+ rc = VERR_INVALID_STATE;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ pType->cbType = cbType;
+ break;
+ }
+
+ case DBGFTYPEVARIANT_UNION:
+ {
+ /* Get size of the biggest member and use that one. */
+ size_t cbType = 0;
+
+ for (uint32_t i = 0; i < pType->pReg->cMembers && RT_SUCCESS(rc); i++)
+ {
+ PCDBGFTYPEREGMEMBER pMember = &pType->pReg->paMembers[i];
+
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_POINTER)
+ {
+ /* Use the current pointer size. */
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, "ptr_t");
+ if (RT_LIKELY(pTypeMember))
+ {
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ cbType = RT_MAX(cbType, pMember->cElements * pTypeMember->cbType);
+ else
+ cbType = RT_MAX(cbType, pTypeMember->cbType);
+ }
+ }
+ else
+ {
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, pMember->pszType);
+ if (RT_LIKELY(pTypeMember))
+ {
+ if ( pTypeMember->cbType == 0
+ && fCalcNested)
+ rc = dbgfR3TypeCalcSize(pUVM, pTypeMember, fCalcNested);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ cbType = RT_MAX(cbType, pMember->cElements * pTypeMember->cbType);
+ else
+ cbType = RT_MAX(cbType, pTypeMember->cbType);
+ }
+ }
+ else
+ rc = VERR_INVALID_STATE;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ pType->cbType = cbType;
+ break;
+ }
+
+ case DBGFTYPEVARIANT_ALIAS:
+ {
+ /* Get the size of the alias. */
+ PDBGFTYPE pAliased = dbgfR3TypeLookup(pUVM, pType->pReg->pszAliasedType);
+ if (RT_LIKELY(pAliased))
+ {
+ if ( pAliased->cbType == 0
+ && fCalcNested)
+ rc = dbgfR3TypeCalcSize(pUVM, pAliased, fCalcNested);
+
+ if (RT_SUCCESS(rc))
+ pType->cbType = pAliased->cbType;
+ }
+ else
+ rc = VERR_INVALID_STATE;
+ break;
+ }
+
+ default:
+ AssertMsgFailedReturn(("Invalid type variant: %d", pType->pReg->enmVariant), VERR_INVALID_STATE);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Callback for clearing the size of all non built-in types.
+ *
+ * @returns VBox status code.
+ * @param pStr The type structure.
+ * @param pvUser The user mode VM handle.
+ */
+static DECLCALLBACK(int) dbgfR3TypeTraverseClearSize(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ PDBGFTYPE pType = (PDBGFTYPE)pStr;
+
+ if (pType->pReg)
+ pType->cbType = 0;
+
+ NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Callback for calculating the size of all non built-in types.
+ *
+ * @returns VBox status code.
+ * @param pStr The type structure.
+ * @param pvUser The user mode VM handle.
+ */
+static DECLCALLBACK(int) dbgfR3TypeTraverseCalcSize(PRTSTRSPACECORE pStr, void *pvUser)
+{
+ PDBGFTYPE pType = (PDBGFTYPE)pStr;
+
+ if ( pType->pReg
+ && !pType->cbType)
+ dbgfR3TypeCalcSize((PUVM)pvUser, pType, true /* fCalcNested */);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Recalculate the sizes of all registered non builtin types.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ */
+static int dbgfR3TypeRecalculateAllSizes(PUVM pUVM)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Clear the sizes of all non builtin types to 0 first so we know which type we
+ * visited later on.
+ */
+ rc = RTStrSpaceEnumerate(&pUVM->dbgf.s.TypeSpace, dbgfR3TypeTraverseClearSize, pUVM);
+ if (RT_SUCCESS(rc))
+ {
+ /* Now recalculate the size. */
+ rc = RTStrSpaceEnumerate(&pUVM->dbgf.s.TypeSpace, dbgfR3TypeTraverseCalcSize, pUVM);
+ }
+
+ return rc;
+}
+
+/**
+ * Validates a given type registration.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pReg The type registration structure.
+ */
+static int dbgfR3TypeValidate(PUVM pUVM, PCDBGFTYPEREG pReg)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pReg->enmVariant)
+ {
+ case DBGFTYPEVARIANT_ALIAS:
+ if ( pReg->cMembers > 0
+ || pReg->paMembers
+ || !pReg->pszAliasedType)
+ rc = VERR_INVALID_PARAMETER;
+ else
+ {
+ PDBGFTYPE pAlias = dbgfR3TypeLookup(pUVM, pReg->pszAliasedType);
+ if (RT_UNLIKELY(!pAlias))
+ rc = VERR_NOT_FOUND;
+ }
+ break;
+ case DBGFTYPEVARIANT_STRUCT:
+ case DBGFTYPEVARIANT_UNION:
+ if (!pReg->pszAliasedType)
+ {
+ for (uint32_t i = 0; i < pReg->cMembers; i++)
+ {
+ PCDBGFTYPEREGMEMBER pMember = &pReg->paMembers[i];
+
+ /* Use the current pointer size. */
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, pMember->pszType);
+ if (RT_UNLIKELY(!pTypeMember))
+ {
+ rc = VERR_NOT_FOUND;
+ break;
+ }
+
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ {
+ if (pMember->cElements == 0)
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if (pMember->cElements != 0)
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ default:
+ AssertMsgFailedBreakStmt(("Invalid type variant: %d", pReg->enmVariant),
+ rc = VERR_INVALID_PARAMETER);
+ }
+
+ return rc;
+}
+
+/**
+ * Retains or releases the reference counters to referenced types for the given
+ * type registration structure.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pReg The type registration structure.
+ * @param fRetain Flag whether to retain or release references.
+ */
+static int dbgfR3TypeUpdateRefCnts(PUVM pUVM, PCDBGFTYPEREG pReg, bool fRetain)
+{
+ int rc = VINF_SUCCESS;
+
+ switch (pReg->enmVariant)
+ {
+ case DBGFTYPEVARIANT_ALIAS:
+ {
+ AssertPtr(pReg->pszAliasedType);
+
+ PDBGFTYPE pAlias = dbgfR3TypeLookup(pUVM, pReg->pszAliasedType);
+ AssertPtr(pAlias);
+
+ if (fRetain)
+ pAlias->cRefs++;
+ else
+ pAlias->cRefs--;
+ break;
+ }
+ case DBGFTYPEVARIANT_STRUCT:
+ case DBGFTYPEVARIANT_UNION:
+ {
+ for (uint32_t i = 0; i < pReg->cMembers; i++)
+ {
+ PCDBGFTYPEREGMEMBER pMember = &pReg->paMembers[i];
+
+ /* Use the current pointer size. */
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, pMember->pszType);
+ AssertPtr(pTypeMember);
+
+ if (fRetain)
+ pTypeMember->cRefs++;
+ else
+ pTypeMember->cRefs--;
+ }
+ break;
+ }
+ default:
+ AssertMsgFailedBreakStmt(("Invalid type variant: %d", pReg->enmVariant),
+ rc = VERR_INVALID_PARAMETER);
+ }
+
+ return rc;
+}
+
+
+/**
+ * Registers a single type in the database.
+ *
+ * @returns VBox status code.
+ * @retval VERR_ALREADY_EXISTS if the type exists already.
+ * @param pUVM The user mode VM handle.
+ * @param pReg The type registration structure.
+ */
+static int dbgfR3TypeRegister(PUVM pUVM, PCDBGFTYPEREG pReg)
+{
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("pUVM=%#p pReg=%#p{%s}\n", pUVM, pReg, pReg->pszType));
+
+ if (dbgfR3TypeLookup(pUVM, pReg->pszType) == NULL)
+ {
+ rc = dbgfR3TypeValidate(pUVM, pReg);
+ if (RT_SUCCESS(rc))
+ {
+ PDBGFTYPE pType = (PDBGFTYPE)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_TYPE, sizeof(DBGFTYPE));
+ if (RT_LIKELY(pType))
+ {
+ pType->Core.pszString = pReg->pszType;
+ pType->pReg = pReg;
+ pType->cRefs = 0;
+ pType->enmTypeBuiltin = DBGFTYPEBUILTIN_INVALID;
+ rc = dbgfR3TypeCalcSize(pUVM, pType, false /* fCalcNested */);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dbgfR3TypeUpdateRefCnts(pUVM, pReg, true /* fRetain */);
+ if (RT_SUCCESS(rc))
+ {
+ bool fSucc = RTStrSpaceInsert(&pUVM->dbgf.s.TypeSpace, &pType->Core);
+ Assert(fSucc);
+ if (!fSucc)
+ {
+ dbgfR3TypeUpdateRefCnts(pUVM, pReg, false /* fRetain */);
+ rc = VERR_ALREADY_EXISTS;
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ MMR3HeapFree(pType);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Registers a new built-in type
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param enmTypeBuiltin The builtin type enum.
+ * @param cbType Size of the type in bytes.
+ * @param pszType The type identifier for the builtin type.
+ */
+static int dbgfR3TypeRegisterBuiltin(PUVM pUVM, DBGFTYPEBUILTIN enmTypeBuiltin,
+ size_t cbType, const char *pszType)
+{
+ LogFlowFunc(("pUVM=%#p enmBuiltin=%d pszType=%s\n", pUVM, enmTypeBuiltin, pszType));
+
+ AssertReturn(!dbgfR3TypeLookup(pUVM, pszType), VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ PDBGFTYPE pType = (PDBGFTYPE)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_TYPE, sizeof(DBGFTYPE));
+ if (RT_LIKELY(pType))
+ {
+ pType->Core.pszString = pszType;
+ pType->pReg = NULL;
+ pType->cRefs = 0;
+ pType->cbType = cbType;
+ pType->enmTypeBuiltin = enmTypeBuiltin;
+ bool fSucc = RTStrSpaceInsert(&pUVM->dbgf.s.TypeSpace, &pType->Core);
+ Assert(fSucc);
+ if (!fSucc)
+ rc = VERR_ALREADY_EXISTS;
+
+ if (RT_FAILURE(rc))
+ MMR3HeapFree(pType);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Registers builtin types.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ */
+static int dbgfTypeRegisterBuiltinTypes(PUVM pUVM)
+{
+ int rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_UINT8, sizeof(uint8_t), "uint8_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_INT8, sizeof(int8_t), "int8_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_UINT16, sizeof(uint16_t), "uint16_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_INT16, sizeof(int16_t), "int16_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_UINT32, sizeof(uint32_t), "uint32_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_INT32, sizeof(int32_t), "int32_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_UINT64, sizeof(uint64_t), "uint64_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_INT64, sizeof(int64_t), "int64_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_PTR32, sizeof(uint32_t), "ptr32_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_PTR64, sizeof(uint64_t), "ptr64_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_PTR, 0, "ptr_t");
+ if (RT_SUCCESS(rc))
+ rc = dbgfR3TypeRegisterBuiltin(pUVM, DBGFTYPEBUILTIN_SIZE, 0, "size_t");
+
+ return rc;
+}
+
+
+/**
+ * Parses a single entry for a given type and assigns the value from the byte buffer
+ * to the value entry.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pMember The type member.
+ * @param pValEntry The value entry holding the value on success.
+ * @param pbBuf The raw byte buffer.
+ * @param cbBuf Size of the byte buffer.
+ * @param pcbParsed Where to store the amount of consumed bytes on success.
+ */
+static int dbgfR3TypeParseEntry(PUVM pUVM, PCDBGFTYPEREGMEMBER pMember, PDBGFTYPEVALENTRY pValEntry,
+ uint8_t *pbBuf, size_t cbBuf, size_t *pcbParsed)
+{
+ int rc = VINF_SUCCESS;
+ PDBGFTYPE pTypeMember = dbgfR3TypeLookup(pUVM, pMember->pszType);
+ uint32_t cValBufs = 1;
+ size_t cbParsed = 0;
+ PDBGFTYPEVALBUF pValBuf = &pValEntry->Buf.Val;
+
+ AssertPtrReturn(pTypeMember, VERR_INVALID_STATE);
+
+ if (pMember->fFlags & DBGFTYPEREGMEMBER_F_ARRAY)
+ {
+ cValBufs = pMember->cElements;
+ pValBuf = (PDBGFTYPEVALBUF)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_TYPE, cValBufs * sizeof(DBGFTYPEVALBUF));
+ if (RT_UNLIKELY(!pValBuf))
+ rc = VERR_NO_MEMORY;
+
+ pValEntry->Buf.pVal = pValBuf;
+ pValEntry->cEntries = cValBufs;
+ pValEntry->cbType = pTypeMember->cbType;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t iValBuf = 0; iValBuf < cValBufs && RT_SUCCESS(rc); iValBuf++)
+ {
+ size_t cbThisParsed = 0;
+
+ if (pTypeMember->pReg)
+ {
+ /* Compound or aliased type */
+ rc = dbgfR3TypeParseBufferByType(pUVM, pTypeMember, pbBuf, cbBuf,
+ &pValBuf->pVal, &cbThisParsed);
+ if (RT_SUCCESS(rc))
+ pValEntry->enmType = DBGFTYPEBUILTIN_COMPOUND;
+ }
+ else
+ {
+ void *pvVal = NULL;
+
+ switch (pTypeMember->enmTypeBuiltin)
+ {
+ case DBGFTYPEBUILTIN_UINT8:
+ pvVal = &pValBuf->u8;
+ cbThisParsed = 1;
+ break;
+ case DBGFTYPEBUILTIN_INT8:
+ pvVal = &pValBuf->i8;
+ cbThisParsed = 1;
+ break;
+ case DBGFTYPEBUILTIN_UINT16:
+ pvVal = &pValBuf->u16;
+ cbThisParsed = 2;
+ break;
+ case DBGFTYPEBUILTIN_INT16:
+ pvVal = &pValBuf->i16;
+ cbThisParsed = 2;
+ break;
+ case DBGFTYPEBUILTIN_UINT32:
+ pvVal = &pValBuf->u32;
+ cbThisParsed = 4;
+ break;
+ case DBGFTYPEBUILTIN_INT32:
+ pvVal = &pValBuf->i32;
+ cbThisParsed = 4;
+ break;
+ case DBGFTYPEBUILTIN_UINT64:
+ pvVal = &pValBuf->u64;
+ cbThisParsed = 8;
+ break;
+ case DBGFTYPEBUILTIN_INT64:
+ pvVal = &pValBuf->i64;
+ cbThisParsed = 8;
+ break;
+ case DBGFTYPEBUILTIN_PTR32:
+ pvVal = &pValBuf->GCPtr;
+ cbThisParsed = 4;
+ break;
+ case DBGFTYPEBUILTIN_PTR64:
+ pvVal = &pValBuf->GCPtr;
+ cbThisParsed = 8;
+ break;
+ case DBGFTYPEBUILTIN_PTR:
+ pvVal = &pValBuf->GCPtr;
+ cbThisParsed = pTypeMember->cbType;
+ break;
+ case DBGFTYPEBUILTIN_SIZE:
+ pvVal = &pValBuf->size;
+ cbThisParsed = pTypeMember->cbType;
+ break;
+ case DBGFTYPEBUILTIN_FLOAT32:
+ case DBGFTYPEBUILTIN_FLOAT64:
+ case DBGFTYPEBUILTIN_COMPOUND:
+ default:
+ AssertMsgFailedBreakStmt(("Invalid built-in type specified: %d\n", pTypeMember->enmTypeBuiltin),
+ rc = VERR_INVALID_STATE);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pValEntry->enmType = pTypeMember->enmTypeBuiltin;
+ if (cbBuf >= cbThisParsed)
+ memcpy(pvVal, pbBuf, cbThisParsed);
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+
+ pValBuf++;
+
+ cbParsed += cbThisParsed;
+ pbBuf += cbThisParsed;
+ cbBuf -= cbThisParsed;
+ }
+ }
+
+ if ( RT_FAILURE(rc)
+ && cValBufs > 1)
+ MMR3HeapFree(pValBuf);
+
+ if (RT_SUCCESS(rc))
+ {
+ pValEntry->cEntries = cValBufs;
+ *pcbParsed = cbParsed;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Parses the given byte buffer and returns the value based no the type information.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pType The type information.
+ * @param pbBuf The byte buffer to parse.
+ * @param cbBuf Size of the buffer.
+ * @param ppVal Where to store the pointer to the value on success.
+ * @param pcbParsed How many bytes of the buffer we consumed.
+ */
+static int dbgfR3TypeParseBufferByType(PUVM pUVM, PDBGFTYPE pType, uint8_t *pbBuf, size_t cbBuf,
+ PDBGFTYPEVAL *ppVal, size_t *pcbParsed)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t cEntries = pType->pReg ? pType->pReg->cMembers : 1;
+ PDBGFTYPEVAL pVal = (PDBGFTYPEVAL)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_TYPE,
+ RT_UOFFSETOF_DYN(DBGFTYPEVAL, aEntries[cEntries]));
+ if (RT_LIKELY(pVal))
+ {
+ size_t cbParsed = 0;
+
+ pVal->pTypeReg = pType->pReg;
+ for (uint32_t i = 0; i < cEntries && RT_SUCCESS(rc); i++)
+ {
+ PCDBGFTYPEREGMEMBER pMember = &pType->pReg->paMembers[i];
+ PDBGFTYPEVALENTRY pValEntry = &pVal->aEntries[i];
+ rc = dbgfR3TypeParseEntry(pUVM, pMember, pValEntry, pbBuf, cbBuf, &cbParsed);
+ if (RT_SUCCESS(rc))
+ {
+ pbBuf += cbParsed;
+ cbBuf -= cbParsed;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pVal->cEntries = cEntries;
+ *pcbParsed = cbParsed;
+ *ppVal = pVal;
+ }
+ else
+ MMR3HeapFree(pVal); /** @todo Leak for embedded structs. */
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+/**
+ * Dumps one level of a typed value.
+ *
+ * @returns VBox status code.
+ * @param pVal The value to dump.
+ * @param iLvl The current level.
+ * @param cLvlMax The maximum level.
+ * @param pfnDump The dumper callback.
+ * @param pvUser The opaque user data to pass to the dumper callback.
+ */
+static int dbgfR3TypeValDump(PDBGFTYPEVAL pVal, uint32_t iLvl, uint32_t cLvlMax,
+ PFNDBGFR3TYPEVALDUMP pfnDump, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PCDBGFTYPEREG pType = pVal->pTypeReg;
+
+ for (uint32_t i = 0; i < pVal->cEntries && rc == VINF_SUCCESS; i++)
+ {
+ PCDBGFTYPEREGMEMBER pTypeMember = &pType->paMembers[i];
+ PDBGFTYPEVALENTRY pValEntry = &pVal->aEntries[i];
+ PDBGFTYPEVALBUF pValBuf = pValEntry->cEntries > 1 ? pValEntry->Buf.pVal : &pValEntry->Buf.Val;
+
+ rc = pfnDump(0 /* off */, pTypeMember->pszName, iLvl, pValEntry->enmType, pValEntry->cbType,
+ pValBuf, pValEntry->cEntries, pvUser);
+ if ( rc == VINF_SUCCESS
+ && pValEntry->enmType == DBGFTYPEBUILTIN_COMPOUND
+ && iLvl < cLvlMax)
+ {
+ /* Print embedded structs. */
+ for (uint32_t iValBuf = 0; iValBuf < pValEntry->cEntries && rc == VINF_SUCCESS; iValBuf++)
+ rc = dbgfR3TypeValDump(pValBuf[iValBuf].pVal, iLvl + 1, cLvlMax, pfnDump, pvUser);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Dumps one level of a type.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pType The type to dump.
+ * @param iLvl The current level.
+ * @param cLvlMax The maximum level.
+ * @param pfnDump The dumper callback.
+ * @param pvUser The opaque user data to pass to the dumper callback.
+ */
+static int dbgfR3TypeDump(PUVM pUVM, PDBGFTYPE pType, uint32_t iLvl, uint32_t cLvlMax,
+ PFNDBGFR3TYPEDUMP pfnDump, void *pvUser)
+{
+ int rc = VINF_SUCCESS;
+ PCDBGFTYPEREG pTypeReg = pType->pReg;
+
+ switch (pTypeReg->enmVariant)
+ {
+ case DBGFTYPEVARIANT_ALIAS:
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ case DBGFTYPEVARIANT_STRUCT:
+ case DBGFTYPEVARIANT_UNION:
+ for (uint32_t i = 0; i < pTypeReg->cMembers && rc == VINF_SUCCESS; i++)
+ {
+ PCDBGFTYPEREGMEMBER pTypeMember = &pTypeReg->paMembers[i];
+ PDBGFTYPE pTypeResolved = dbgfR3TypeLookup(pUVM, pTypeMember->pszType);
+
+ rc = pfnDump(0 /* off */, pTypeMember->pszName, iLvl, pTypeMember->pszType,
+ pTypeMember->fFlags, pTypeMember->cElements, pvUser);
+ if ( rc == VINF_SUCCESS
+ && pTypeResolved->pReg
+ && iLvl < cLvlMax)
+ {
+ /* Print embedded structs. */
+ rc = dbgfR3TypeDump(pUVM, pTypeResolved, iLvl + 1, cLvlMax, pfnDump, pvUser);
+ }
+ }
+ break;
+ default:
+ AssertMsgFailed(("Invalid type variant: %u\n", pTypeReg->enmVariant));
+ rc = VERR_INVALID_STATE;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Initializes the type database.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ */
+DECLHIDDEN(int) dbgfR3TypeInit(PUVM pUVM)
+{
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = RTSemRWCreate(&pUVM->dbgf.s.hTypeDbLock);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dbgfTypeRegisterBuiltinTypes(pUVM);
+ if (RT_FAILURE(rc))
+ {
+ RTSemRWDestroy(pUVM->dbgf.s.hTypeDbLock);
+ pUVM->dbgf.s.hTypeDbLock = NIL_RTSEMRW;
+ }
+ }
+ pUVM->dbgf.s.fTypeDbInitialized = RT_SUCCESS(rc);
+ }
+ return rc;
+}
+
+
+/**
+ * Terminates the type database.
+ *
+ * @param pUVM The user mode VM handle.
+ */
+DECLHIDDEN(void) dbgfR3TypeTerm(PUVM pUVM)
+{
+ RTSemRWDestroy(pUVM->dbgf.s.hTypeDbLock);
+ pUVM->dbgf.s.hTypeDbLock = NIL_RTSEMRW;
+ pUVM->dbgf.s.fTypeDbInitialized = false;
+}
+
+
+/**
+ * Registers a new type for lookup.
+ *
+ * @returns VBox status code.
+ * @retval VERR_ALREADY_EXISTS if the type exists already.
+ * @param pUVM The user mode VM handle.
+ * @param cTypes Number of types to register.
+ * @param paTypes The array of type registration structures to register.
+ */
+VMMR3DECL(int) DBGFR3TypeRegister(PUVM pUVM, uint32_t cTypes, PCDBGFTYPEREG paTypes)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertReturn(cTypes > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(paTypes, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_WRITE(pUVM);
+ for (uint32_t i = 0; i < cTypes && RT_SUCCESS(rc); i++)
+ {
+ rc = dbgfR3TypeRegister(pUVM, &paTypes[i]);
+ if ( RT_FAILURE(rc)
+ && i > 0)
+ {
+ /* Deregister types in reverse order. */
+ do
+ {
+ int rc2 = DBGFR3TypeDeregister(pUVM, paTypes[i].pszType);
+ AssertRC(rc2);
+ i--;
+ } while (i > 0);
+
+ break;
+ }
+ }
+ DBGF_TYPE_DB_UNLOCK_WRITE(pUVM);
+
+ return rc;
+}
+
+
+/**
+ * Deregisters a previously registered type.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if the type is not known.
+ * @retval VERR_RESOURCE_IN_USE if the type is used by another type.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier to deregister.
+ */
+VMMR3DECL(int) DBGFR3TypeDeregister(PUVM pUVM, const char *pszType)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_WRITE(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ {
+ if (!pType->cRefs)
+ {
+
+ }
+ else
+ rc = VERR_RESOURCE_IN_USE;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_WRITE(pUVM);
+
+ return rc;
+}
+
+
+/**
+ * Return the type registration structure for the given type identifier.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if the type is not known.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier to get the registration structure from.
+ * @param ppTypeReg Where to store the type registration structure on success.
+ */
+VMMR3DECL(int) DBGFR3TypeQueryReg(PUVM pUVM, const char *pszType, PCDBGFTYPEREG *ppTypeReg)
+{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppTypeReg, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_READ(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ *ppTypeReg = pType->pReg;
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_READ(pUVM);
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Queries the size a given type would occupy in memory.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if the type is not known.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier.
+ * @param pcbType Where to store the amount of memory occupied in bytes.
+ */
+VMMR3DECL(int) DBGFR3TypeQuerySize(PUVM pUVM, const char *pszType, size_t *pcbType)
+{
+ LogFlowFunc(("pUVM=%#p pszType=%s pcbType=%#p\n", pUVM, pszType, pcbType));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbType, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_READ(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ *pcbType = pType->cbType;
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_READ(pUVM);
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Sets the size of the given type in bytes.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if the type is not known.
+ * @retval VERR_NOT_SUPPORTED if changing the size of this type is not supported.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier.
+ * @param cbType The size of the type in bytes.
+ *
+ * @note: This currently works only for the builtin pointer type without the explicit
+ * size (ptr_t or DBGFTYPEBUILTIN_PTR).
+ */
+VMMR3DECL(int) DBGFR3TypeSetSize(PUVM pUVM, const char *pszType, size_t cbType)
+{
+ LogFlowFunc(("pUVM=%#p pszType=%s cbType=%zu\n", pUVM, pszType, cbType));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertReturn(cbType > 0, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_WRITE(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ {
+ if ( !pType->pReg
+ && ( pType->enmTypeBuiltin == DBGFTYPEBUILTIN_PTR
+ || pType->enmTypeBuiltin == DBGFTYPEBUILTIN_SIZE))
+ {
+ if (pType->cbType != cbType)
+ {
+ pType->cbType = cbType;
+ rc = dbgfR3TypeRecalculateAllSizes(pUVM);
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_WRITE(pUVM);
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Dumps the type information of the given type.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszType The type identifier.
+ * @param fFlags Flags to control the dumping (reserved, MBZ).
+ * @param cLvlMax Maximum levels to nest.
+ * @param pfnDump The dumper callback.
+ * @param pvUser Opaque user data.
+ */
+VMMR3DECL(int) DBGFR3TypeDumpEx(PUVM pUVM, const char *pszType, uint32_t fFlags,
+ uint32_t cLvlMax, PFNDBGFR3TYPEDUMP pfnDump, void *pvUser)
+{
+ LogFlowFunc(("pUVM=%#p pszType=%s fFlags=%#x cLvlMax=%u pfnDump=%#p pvUser=%#p\n",
+ pUVM, pszType, fFlags, cLvlMax, pfnDump, pvUser));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnDump, VERR_INVALID_POINTER);
+ RT_NOREF_PV(fFlags);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_READ(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ rc = dbgfR3TypeDump(pUVM, pType, 0 /* iLvl */, cLvlMax, pfnDump, pvUser);
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_READ(pUVM);
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Returns the value of a memory buffer at the given address formatted for the given
+ * type.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NOT_FOUND if the type is not known.
+ * @param pUVM The user mode VM handle.
+ * @param pAddress The address to start reading from.
+ * @param pszType The type identifier.
+ * @param ppVal Where to store the pointer to the value structure
+ * on success.
+ */
+VMMR3DECL(int) DBGFR3TypeQueryValByType(PUVM pUVM, PCDBGFADDRESS pAddress, const char *pszType,
+ PDBGFTYPEVAL *ppVal)
+{
+ LogFlowFunc(("pUVM=%#p pAddress=%#p pszType=%s ppVal=%#p\n", pUVM, pAddress, pszType, ppVal));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pAddress, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppVal, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ if (!pUVM->dbgf.s.fTypeDbInitialized)
+ {
+ rc = dbgfR3TypeInit(pUVM);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ DBGF_TYPE_DB_LOCK_READ(pUVM);
+ PDBGFTYPE pType = dbgfR3TypeLookup(pUVM, pszType);
+ if (pType)
+ {
+ uint8_t *pbBuf = (uint8_t *)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF_TYPE, pType->cbType);
+ if (RT_LIKELY(pbBuf))
+ {
+ rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddress, pbBuf, pType->cbType);
+ if (RT_SUCCESS(rc))
+ {
+ /* Parse the buffer based on the type. */
+ size_t cbParsed = 0;
+ rc = dbgfR3TypeParseBufferByType(pUVM, pType, pbBuf, pType->cbType,
+ ppVal, &cbParsed);
+ }
+
+ MMR3HeapFree(pbBuf);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ DBGF_TYPE_DB_UNLOCK_READ(pUVM);
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Frees all acquired resources of a value previously obtained with
+ * DBGFR3TypeQueryValByType().
+ *
+ * @param pVal The value to free.
+ */
+VMMR3DECL(void) DBGFR3TypeValFree(PDBGFTYPEVAL pVal)
+{
+ AssertPtrReturnVoid(pVal);
+
+ for (uint32_t i = 0; i < pVal->cEntries; i++)
+ {
+ PDBGFTYPEVALENTRY pValEntry = &pVal->aEntries[i];
+ PDBGFTYPEVALBUF pValBuf = pValEntry->cEntries > 1 ? pValEntry->Buf.pVal : &pValEntry->Buf.Val;
+
+ if (pValEntry->enmType == DBGFTYPEBUILTIN_COMPOUND)
+ for (uint32_t iBuf = 0; iBuf < pValEntry->cEntries; iBuf++)
+ DBGFR3TypeValFree(pValBuf->pVal);
+
+ if (pValEntry->cEntries > 1)
+ MMR3HeapFree(pValEntry->Buf.pVal);
+ }
+
+ MMR3HeapFree(pVal);
+}
+
+
+/**
+ * Reads the guest memory with the given type and dumps the content of the type.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pAddress The address to start reading from.
+ * @param pszType The type identifier.
+ * @param fFlags Flags for tweaking (reserved, must be zero).
+ * @param cLvlMax Maximum number of levels to expand embedded structs.
+ * @param pfnDump The dumper callback.
+ * @param pvUser The opaque user data to pass to the callback.
+ */
+VMMR3DECL(int) DBGFR3TypeValDumpEx(PUVM pUVM, PCDBGFADDRESS pAddress, const char *pszType, uint32_t fFlags,
+ uint32_t cLvlMax, FNDBGFR3TYPEVALDUMP pfnDump, void *pvUser)
+{
+ LogFlowFunc(("pUVM=%#p pAddress=%#p pszType=%s fFlags=%#x pfnDump=%#p pvUser=%#p\n",
+ pUVM, pAddress, pszType, fFlags,pfnDump, pvUser));
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ AssertPtrReturn(pAddress, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszType, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnDump, VERR_INVALID_POINTER);
+ AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
+ AssertReturn(cLvlMax >= 1, VERR_INVALID_PARAMETER);
+
+ PDBGFTYPEVAL pVal = NULL;
+ int rc = DBGFR3TypeQueryValByType(pUVM, pAddress, pszType, &pVal);
+ if (RT_SUCCESS(rc))
+ {
+ rc = dbgfR3TypeValDump(pVal, 0 /* iLvl */, cLvlMax, pfnDump, pvUser);
+ DBGFR3TypeValFree(pVal);
+ }
+
+ LogFlowFunc(("-> rc=%Rrc\n", rc));
+ return rc;
+}
+