Index: /trunk/include/VBox/vmm/dbgf.h
===================================================================
--- /trunk/include/VBox/vmm/dbgf.h	(revision 35585)
+++ /trunk/include/VBox/vmm/dbgf.h	(revision 35586)
@@ -1274,4 +1274,7 @@
 typedef DBGFREGVAL const *PCDBGFREGVAL;
 
+VMMDECL(ssize_t) DBGFR3RegFormatValue(char *pszBuf, size_t cbBuf, PCDBGFREGVAL pValue, DBGFREGVALTYPE enmType, bool fSpecial);
+VMMDECL(ssize_t) DBGFR3RegFormatValueEx(char *pszBuf, size_t cbBuf, PCDBGFREGVAL pValue, DBGFREGVALTYPE enmType,
+                                        unsigned uBase, signed int cchWidth, signed int cchPrecision, uint32_t fFlags);
 
 /**
@@ -1452,6 +1455,6 @@
 VMMR3DECL(int) DBGFR3RegNmQueryAllCount(PVM pVM, size_t *pcRegs);
 VMMR3DECL(int) DBGFR3RegNmQueryAll( PVM pVM,                   PDBGFREGENTRYNM paRegs, size_t cRegs);
-VMMR3DECL(int) DBGFR3RegNmPrintf(   PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);
-VMMR3DECL(int) DBGFR3RegNmPrintfV(  PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);
+VMMR3DECL(int) DBGFR3RegNmPrintf(   PVM pVM, VMCPUID idDefCpu, char *pszBuf, size_t cbBuf, const char *pszFormat, ...);
+VMMR3DECL(int) DBGFR3RegNmPrintfV(  PVM pVM, VMCPUID idDefCpu, char *pszBuf, size_t cbBuf, const char *pszFormat, va_list va);
 
 VMMR3DECL(int) DBGFR3RegNmSetU8(    PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint8_t     u8);
Index: /trunk/src/VBox/Main/src-client/MachineDebuggerImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-client/MachineDebuggerImpl.cpp	(revision 35585)
+++ /trunk/src/VBox/Main/src-client/MachineDebuggerImpl.cpp	(revision 35586)
@@ -1053,101 +1053,12 @@
  * @param   a_enmType           The type of the value.
  */
-static HRESULT formatRegisterValue(Bstr *a_pbstr, PCDBGFREGVAL a_pValue, DBGFREGVALTYPE a_enmType)
-{
-    char szHex[128]; /* Must be big because RTStrFormatNumber is unsafe. */
-
-    switch (a_enmType)
-    {
-        case DBGFREGVALTYPE_U8:
-            RTStrFormatNumber(szHex, a_pValue->u8,  16,  2+2, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_8BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_U16:
-            RTStrFormatNumber(szHex, a_pValue->u16, 16,  2+4, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_16BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_U32:
-            RTStrFormatNumber(szHex, a_pValue->u32, 16,  2+8, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_32BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_U64:
-            RTStrFormatNumber(szHex, a_pValue->u64, 16, 2+16, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_64BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_U128:
-            RTStrFormatNumber(szHex, a_pValue->au64[1], 16, 2+16, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_64BIT);
-            RTStrFormatNumber(&szHex[2+16], a_pValue->au64[0], 16, 16, 0, RTSTR_F_ZEROPAD | RTSTR_F_64BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_R80:
-        {
-            char *pszHex = szHex;
-            if (a_pValue->r80.s.fSign)
-                *pszHex++ = '-';
-            else
-                *pszHex++ = '+';
-
-            if (a_pValue->r80.s.uExponent == 0)
-            {
-                if (   !a_pValue->r80.sj64.u63Fraction
-                    && a_pValue->r80.sj64.fInteger)
-                    *pszHex++ = '0';
-                /* else: Denormal, handled way below. */
-            }
-            else if (a_pValue->r80.sj64.uExponent == UINT16_C(0x7fff))
-            {
-                /** @todo Figure out Pseudo inf/nan... */
-                if (a_pValue->r80.sj64.fInteger)
-                    *pszHex++ = 'P';
-                if (a_pValue->r80.sj64.u63Fraction == 0)
-                {
-                    *pszHex++ = 'I';
-                    *pszHex++ = 'n';
-                    *pszHex++ = 'f';
-                }
-                else
-                {
-                    *pszHex++ = 'N';
-                    *pszHex++ = 'a';
-                    *pszHex++ = 'N';
-                }
-            }
-            if (pszHex != &szHex[1])
-                *pszHex = '\0';
-            else
-            {
-                *pszHex++ = a_pValue->r80.sj64.fInteger ? '1' : '0';
-                *pszHex++ = 'm';
-                pszHex += RTStrFormatNumber(pszHex, a_pValue->r80.sj64.u63Fraction, 16, 2+16, 0,
-                                            RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_64BIT);
-
-                *pszHex++ = 'e';
-                pszHex += RTStrFormatNumber(pszHex, (int32_t)a_pValue->r80.sj64.uExponent - 16383, 10, 0, 0,
-                                            RTSTR_F_ZEROPAD | RTSTR_F_32BIT | RTSTR_F_VALSIGNED);
-            }
-            *a_pbstr = szHex;
-            return S_OK;
-        }
-
-        case DBGFREGVALTYPE_DTR:
-            RTStrFormatNumber(szHex, a_pValue->dtr.u64Base, 16, 2+16, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD | RTSTR_F_64BIT);
-            szHex[2+16] = ':';
-            RTStrFormatNumber(&szHex[2+16+1], a_pValue->dtr.u32Limit, 16, 4, 0, RTSTR_F_ZEROPAD | RTSTR_F_32BIT);
-            *a_pbstr = szHex;
-            return S_OK;
-
-        case DBGFREGVALTYPE_INVALID:
-        case DBGFREGVALTYPE_END:
-        case DBGFREGVALTYPE_32BIT_HACK:
-            break;
-        /* no default */
-    }
-
-    return E_UNEXPECTED;
+DECLINLINE(HRESULT) formatRegisterValue(Bstr *a_pbstr, PCDBGFREGVAL a_pValue, DBGFREGVALTYPE a_enmType)
+{
+    char szHex[160];
+    ssize_t cch = DBGFR3RegFormatValue(szHex, sizeof(szHex), a_pValue, a_enmType, true /*fSpecial*/);
+    if (RT_UNLIKELY(cch <= 0))
+        return E_UNEXPECTED;
+    *a_pbstr = szHex;
+    return S_OK;
 }
 
Index: /trunk/src/VBox/VMM/VMMR3/DBGFReg.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/DBGFReg.cpp	(revision 35585)
+++ /trunk/src/VBox/VMM/VMMR3/DBGFReg.cpp	(revision 35586)
@@ -150,4 +150,50 @@
 /** Pointer to a const register lookup record. */
 typedef DBGFREGLOOKUP const *PCDBGFREGLOOKUP;
+
+
+/**
+ * Argument packet from DBGFR3RegNmQueryAll to dbgfR3RegNmQueryAllWorker.
+ */
+typedef struct DBGFR3REGNMQUERYALLARGS
+{
+    /** The output register array. */
+    PDBGFREGENTRYNM paRegs;
+    /** The number of entries in the output array. */
+    size_t          cRegs;
+    /** The current register number when enumerating the string space. */
+    size_t          iReg;
+} DBGFR3REGNMQUERYALLARGS;
+/** Pointer to a dbgfR3RegNmQueryAllWorker argument packet. */
+typedef DBGFR3REGNMQUERYALLARGS *PDBGFR3REGNMQUERYALLARGS;
+
+
+/**
+ * Argument packet passed by DBGFR3RegNmPrintfV to dbgfR3RegNmPrintfCbOutput
+ * and dbgfR3RegNmPrintfCbFormat.
+ */
+typedef struct DBGFR3REGNMPRINTFARGS
+{
+    /** The VM handle. */
+    PVM         pVM;
+    /** The target CPU. */
+    VMCPUID     idCpu;
+    /** The output buffer. */
+    char       *pszBuf;
+    /** The format string. */
+    const char *pszFormat;
+    /** The va list with format arguments. */
+    va_list     va;
+
+    /** The current buffer offset. */
+    size_t      offBuf;
+    /** The amount of buffer space left, not counting the terminator char. */
+    size_t      cchLeftBuf;
+    /** The status code of the whole operation.  First error is return,
+     * subsequent ones are suppressed. */
+    int         rc;
+} DBGFR3REGNMPRINTFARGS;
+/** Pointer to a DBGFR3RegNmPrintfV argument packet. */
+typedef DBGFR3REGNMPRINTFARGS *PDBGFR3REGNMPRINTFARGS;
+
 
 
@@ -1173,5 +1219,6 @@
 
 /**
- * On CPU worker for the register queries, used by dbgfR3RegNmQueryWorker.
+ * On CPU worker for the register queries, used by dbgfR3RegNmQueryWorker and
+ * dbgfR3RegNmPrintfCbFormat.
  *
  * @returns VBox status code.
@@ -1591,19 +1638,4 @@
 }
 
-/**
- * Argument packet from DBGFR3RegNmQueryAll to dbgfR3RegNmQueryAllWorker.
- */
-typedef struct DBGFR3REGNMQUERYALLARGS
-{
-    /** The output register array. */
-    PDBGFREGENTRYNM paRegs;
-    /** The number of entries in the output array. */
-    size_t          cRegs;
-    /** The current register number when enumerating the string space. */
-    size_t          iReg;
-} DBGFR3REGNMQUERYALLARGS;
-/** Pointer to a dbgfR3RegNmQueryAllWorker argument packet. */
-typedef DBGFR3REGNMQUERYALLARGS *PDBGFR3REGNMQUERYALLARGS;
-
 
 /**
@@ -1691,4 +1723,6 @@
     size_t const                cRegs  = pArgs->cRegs;
 
+    DBGF_REG_DB_LOCK_READ(pVM);
+
     /*
      * My CPU registers.
@@ -1713,4 +1747,5 @@
     }
 
+    DBGF_REG_DB_UNLOCK_READ(pVM);
     return VINF_SUCCESS; /* Ignore errors. */
 }
@@ -1743,5 +1778,345 @@
 
 
-/// @todo VMMR3DECL(int) DBGFR3RegNmPrintf(PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);
-/// @todo VMMR3DECL(int) DBGFR3RegNmPrintfV(PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);
-
+/**
+ * Internal worker for DBGFR3RegFormatValue, cbTmp is sufficent.
+ *
+ * @copydoc DBGFR3RegFormatValue
+ */
+DECLINLINE(ssize_t) dbgfR3RegFormatValueInt(char *pszTmp, size_t cbTmp, PCDBGFREGVAL pValue, DBGFREGVALTYPE enmType,
+                                            unsigned uBase, signed int cchWidth, signed int cchPrecision, uint32_t fFlags)
+{
+    switch (enmType)
+    {
+        case DBGFREGVALTYPE_U8:
+            return RTStrFormatU8(pszTmp, cbTmp, pValue->u8, uBase, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_U16:
+            return RTStrFormatU16(pszTmp, cbTmp, pValue->u16, uBase, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_U32:
+            return RTStrFormatU32(pszTmp, cbTmp, pValue->u32, uBase, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_U64:
+            return RTStrFormatU64(pszTmp, cbTmp, pValue->u64, uBase, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_U128:
+            return RTStrFormatU128(pszTmp, cbTmp, &pValue->u128, uBase, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_R80:
+            return RTStrFormatR80u2(pszTmp, cbTmp, &pValue->r80, cchWidth, cchPrecision, fFlags);
+        case DBGFREGVALTYPE_DTR:
+        {
+            ssize_t cch = RTStrFormatU64(pszTmp, cbTmp, pValue->dtr.u64Base,
+                                         16, 2+16, 0, RTSTR_F_SPECIAL | RTSTR_F_ZEROPAD);
+            AssertReturn(cch > 0, VERR_INTERNAL_ERROR_4);
+            pszTmp[cch++] = ':';
+            cch += RTStrFormatU64(&pszTmp[cch], cbTmp - cch, pValue->dtr.u32Limit,
+                                  16, 4, 0, RTSTR_F_ZEROPAD | RTSTR_F_32BIT);
+            return cch;
+        }
+
+        case DBGFREGVALTYPE_32BIT_HACK:
+        case DBGFREGVALTYPE_END:
+        case DBGFREGVALTYPE_INVALID:
+            break;
+        /* no default, want gcc warnings */
+    }
+
+    RTStrPrintf(pszTmp, cbTmp, "!enmType=%d!", enmType);
+    return VERR_INTERNAL_ERROR_5;
+}
+
+
+
+/**
+ * Format a register value, extended version.
+ *
+ * @returns The number of bytes returned, VERR_BUFFER_OVERFLOW on failure.
+ * @param   pszBuf          The output buffer.
+ * @param   cbBuf           The size of the output buffer.
+ * @param   pValue          The value to format.
+ * @param   enmType         The value type.
+ * @param   uBase           The base (ignored if not applicable).
+ * @param   cchWidth        The width if RTSTR_F_WIDTH is set, otherwise
+ *                          ignored.
+ * @param   cchPrecision    The width if RTSTR_F_PRECISION is set, otherwise
+ *                          ignored.
+ * @param   fFlags          String formatting flags, RTSTR_F_XXX.
+ */
+VMMDECL(ssize_t) DBGFR3RegFormatValueEx(char *pszBuf, size_t cbBuf, PCDBGFREGVAL pValue, DBGFREGVALTYPE enmType,
+                                        unsigned uBase, signed int cchWidth, signed int cchPrecision, uint32_t fFlags)
+{
+    /*
+     * Format to temporary buffer using worker shared with dbgfR3RegNmPrintfCbFormat.
+     */
+    char szTmp[160];
+    ssize_t cchOutput = dbgfR3RegFormatValueInt(szTmp, sizeof(szTmp), pValue, enmType, uBase, cchWidth, cchPrecision, fFlags);
+    if (cchOutput > 0)
+    {
+        if ((size_t)cchOutput < cbBuf)
+            memcpy(pszBuf, szTmp, cchOutput + 1);
+        else
+        {
+            if (cbBuf)
+            {
+                memcpy(pszBuf, szTmp, cbBuf - 1);
+                pszBuf[cbBuf - 1] = '\0';
+            }
+            cchOutput = VERR_BUFFER_OVERFLOW;
+        }
+    }
+    return cchOutput;
+}
+
+
+/**
+ * Format a register value as hexadecimal and with default width according to
+ * the type.
+ *
+ * @returns The number of bytes returned, VERR_BUFFER_OVERFLOW on failure.
+ * @param   pszBuf          The output buffer.
+ * @param   cbBuf           The size of the output buffer.
+ * @param   pValue          The value to format.
+ * @param   enmType         The value type.
+ * @param   fSpecial        Same as RTSTR_F_SPECIAL.
+ */
+VMMDECL(ssize_t) DBGFR3RegFormatValue(char *pszBuf, size_t cbBuf, PCDBGFREGVAL pValue, DBGFREGVALTYPE enmType, bool fSpecial)
+{
+    int cchWidth = 0;
+    switch (enmType)
+    {
+        case DBGFREGVALTYPE_U8:     cchWidth = 2  + fSpecial*2; break;
+        case DBGFREGVALTYPE_U16:    cchWidth = 4  + fSpecial*2; break;
+        case DBGFREGVALTYPE_U32:    cchWidth = 8  + fSpecial*2; break;
+        case DBGFREGVALTYPE_U64:    cchWidth = 16 + fSpecial*2; break;
+        case DBGFREGVALTYPE_U128:   cchWidth = 32 + fSpecial*2; break;
+        case DBGFREGVALTYPE_R80:    cchWidth = 0; break;
+        case DBGFREGVALTYPE_DTR:    cchWidth = 16+1+4 + fSpecial*2; break;
+
+        case DBGFREGVALTYPE_32BIT_HACK:
+        case DBGFREGVALTYPE_END:
+        case DBGFREGVALTYPE_INVALID:
+            break;
+        /* no default, want gcc warnings */
+    }
+    uint32_t fFlags = RTSTR_F_ZEROPAD;
+    if (fSpecial)
+        fFlags |= RTSTR_F_SPECIAL;
+    if (cchWidth != 0)
+        fFlags |= RTSTR_F_WIDTH;
+    return DBGFR3RegFormatValueEx(pszBuf, cbBuf, pValue, enmType, 16, cchWidth, 0, fFlags);
+}
+
+
+/**
+ * @callback_method_impl{FNSTRFORMAT}
+ */
+static DECLCALLBACK(size_t)
+dbgfR3RegNmPrintfCbFormat(void *pvArg, PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+                          const char **ppszFormat, va_list *pArgs, int cchWidth,
+                          int cchPrecision, unsigned fFlags, char chArgSize)
+{
+    PDBGFR3REGNMPRINTFARGS pThis = (PDBGFR3REGNMPRINTFARGS)pvArg;
+
+    /*
+     * Parse out the register bits of the register format type.  Noisily reject
+     * unknown format types.
+     */
+    const char *pszFormat = *ppszFormat;
+    if (    pszFormat[0] != 'V'
+        ||  pszFormat[1] != 'R')
+    {
+        AssertMsgFailed(("'%s'\n", pszFormat));
+        return 0;
+    }
+    unsigned uBase;
+    if (pszFormat[2] == '{')
+        uBase = 16;
+    else if (   pszFormat[2] == 'U'
+             && pszFormat[3] == '{')
+        uBase = 10;
+    else if (   pszFormat[2] == 'O'
+             && pszFormat[3] == '{')
+        uBase = 8;
+    else if (   pszFormat[2] == 'B'
+             && pszFormat[3] == '{')
+        uBase = 2;
+    else
+    {
+        AssertMsgFailed(("'%s'\n", pszFormat));
+        return 0;
+    }
+
+    const char * const  pachReg = &pszFormat[3];
+    const char         *pszEnd = strchr(&pachReg[3], '}');
+    AssertMsgReturn(pszEnd, ("Missing closing curly bracket: '%s'\n", pszFormat), 0);
+
+    size_t const cchReg = pachReg - pszEnd;
+
+    /*
+     * Look up the register - same as dbgfR3RegResolve, except for locking and
+     * input string termination.
+     */
+    char szTmp[DBGF_REG_MAX_NAME * 4 + 64];
+
+    /* Try looking up the name without any case folding or cpu prefixing. */
+    PCDBGFREGLOOKUP pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGetN(&pThis->pVM->dbgf.s.RegSpace, pachReg, cchReg);
+    if (!pLookupRec)
+    {
+        /* Lower case it and try again. */
+        ssize_t cchFolded = dbgfR3RegCopyToLower(pachReg, cchReg, szTmp, sizeof(szTmp) - DBGF_REG_MAX_NAME);
+        if (cchFolded > 0)
+            pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGet(&pThis->pVM->dbgf.s.RegSpace, szTmp);
+        if (   !pLookupRec
+            && cchFolded >= 0
+            && pThis->idCpu != VMCPUID_ANY)
+        {
+            /* Prefix it with the specified CPU set. */
+            size_t cchCpuSet = RTStrPrintf(szTmp, sizeof(szTmp), "cpu%u.", pThis->idCpu);
+            dbgfR3RegCopyToLower(pachReg, cchReg, &szTmp[cchCpuSet], sizeof(szTmp) - cchCpuSet);
+            pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGet(&pThis->pVM->dbgf.s.RegSpace, szTmp);
+        }
+    }
+    AssertMsgReturn(pLookupRec, ("'%s'\n", pszFormat), 0);
+    AssertMsgReturn(   pLookupRec->pSet->enmType != DBGFREGSETTYPE_CPU
+                    || pLookupRec->pSet->uUserArg.pVCpu->idCpu == pThis->idCpu,
+                    ("'%s' idCpu=%u, pSet/cpu=%u\n", pszFormat, pThis->idCpu, pLookupRec->pSet->uUserArg.pVCpu->idCpu),
+                    0);
+
+    /* Commit the format type parsing so we can return more freely below. */
+    *ppszFormat = pszFormat;
+
+    /*
+     * Get the register value.
+     */
+    DBGFREGVAL      Value;
+    DBGFREGVALTYPE  enmType;
+    int rc = dbgfR3RegNmQueryWorkerOnCpu(pThis->pVM, pLookupRec, DBGFREGVALTYPE_END, &Value, &enmType);
+    if (RT_FAILURE(rc))
+    {
+        PCRTSTATUSMSG pErr = RTErrGet(rc);
+        if (pErr)
+            return pfnOutput(pvArgOutput, pErr->pszDefine, strlen(pErr->pszDefine));
+        return pfnOutput(pvArgOutput, szTmp, RTStrPrintf(szTmp, sizeof(szTmp), "rc=%d", rc));
+    }
+
+    /*
+     * Format the value.
+     */
+    ssize_t cchOutput = dbgfR3RegFormatValueInt(szTmp, sizeof(szTmp), &Value, enmType, uBase, cchWidth, cchPrecision, fFlags);
+    if (RT_UNLIKELY(cchOutput <= 0))
+    {
+        AssertFailed();
+        return pfnOutput(pvArgOutput, "internal-error", sizeof("internal-error") - 1);
+    }
+    return pfnOutput(pvArgOutput, szTmp, cchOutput);
+}
+
+
+/**
+ * @callback_method_impl{FNRTSTROUTPUT}
+ */
+static DECLCALLBACK(size_t)
+dbgfR3RegNmPrintfCbOutput(void *pvArg, const char *pachChars, size_t cbChars)
+{
+    PDBGFR3REGNMPRINTFARGS  pArgs    = (PDBGFR3REGNMPRINTFARGS)pvArg;
+    size_t                  cbToCopy = cbChars;
+    if (cbToCopy >= pArgs->cchLeftBuf)
+    {
+        if (RT_SUCCESS(pArgs->rc))
+            pArgs->rc = VERR_BUFFER_OVERFLOW;
+        cbToCopy = pArgs->cchLeftBuf;
+    }
+    if (cbToCopy > 0)
+    {
+        memcpy(&pArgs->pszBuf[pArgs->offBuf], pachChars, cbChars);
+        pArgs->offBuf     += cbChars;
+        pArgs->cchLeftBuf -= cbChars;
+        pArgs->pszBuf[pArgs->offBuf] = '\0';
+    }
+    return cbToCopy;
+}
+
+
+/**
+ * On CPU worker for the register formatting, used by DBGFR3RegNmPrintfV.
+ *
+ * @returns VBox status code.
+ *
+ * @param   pArgs               The argument package and state.
+ */
+static DECLCALLBACK(int) dbgfR3RegNmPrintfWorkerOnCpu(PDBGFR3REGNMPRINTFARGS pArgs)
+{
+    DBGF_REG_DB_LOCK_READ(pArgs->pVM);
+    RTStrFormatV(dbgfR3RegNmPrintfCbOutput, pArgs, dbgfR3RegNmPrintfCbFormat, pArgs, pArgs->pszFormat, pArgs->va);
+    DBGF_REG_DB_UNLOCK_READ(pArgs->pVM);
+    return pArgs->rc;
+}
+
+
+/**
+ * Format a registers.
+ *
+ * This is restricted to registers from one CPU, that specified by @a idCpu.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   idCpu               The CPU ID of any CPU registers that may be
+ *                              printed, pass VMCPUID_ANY if not applicable.
+ * @param   pszBuf              The output buffer.
+ * @param   cbBuf               The size of the output buffer.
+ * @param   pszFormat           The format string.  Register names are given by
+ *                              %VR{name}, they take no arguments.
+ * @param   va                  Other format arguments.
+ */
+VMMR3DECL(int) DBGFR3RegNmPrintfV(PVM pVM, VMCPUID idCpu, char *pszBuf, size_t cbBuf, const char *pszFormat, va_list va)
+{
+    AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+    AssertReturn(cbBuf > 0, VERR_BUFFER_OVERFLOW);
+    *pszBuf = '\0';
+
+    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+    AssertReturn(idCpu < pVM->cCpus || idCpu == VMCPUID_ANY, VERR_INVALID_CPU_ID);
+    AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
+
+    /*
+     * Set up an argument package and execute the formatting on the
+     * specified CPU.
+     */
+    DBGFR3REGNMPRINTFARGS Args;
+    Args.pVM        = pVM;
+    Args.idCpu      = idCpu;
+    Args.pszBuf     = pszBuf;
+    Args.pszFormat  = pszFormat;
+    va_copy(Args.va, va);
+    Args.offBuf     = 0;
+    Args.cchLeftBuf = cbBuf - 1;
+    Args.rc         = VINF_SUCCESS;
+    int rc = VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegNmPrintfWorkerOnCpu, 1, &Args);
+    va_end(Args.va);
+    return rc;
+}
+
+
+/**
+ * Format a registers.
+ *
+ * This is restricted to registers from one CPU, that specified by @a idCpu.
+ *
+ * @returns VBox status code.
+ * @param   pVM                 The VM handle.
+ * @param   idCpu               The CPU ID of any CPU registers that may be
+ *                              printed, pass VMCPUID_ANY if not applicable.
+ * @param   pszBuf              The output buffer.
+ * @param   cbBuf               The size of the output buffer.
+ * @param   pszFormat           The format string.  Register names are given by
+ *                              %VR{name}, %VRU{name}, %VRO{name} and
+ *                              %VRB{name}, which are hexadecimal, (unsigned)
+ *                              decimal, octal and binary representation.  None
+ *                              of these types takes any arguments.
+ * @param   ...                 Other format arguments.
+ */
+VMMR3DECL(int) DBGFR3RegNmPrintf(PVM pVM, VMCPUID idCpu, char *pszBuf, size_t cbBuf, const char *pszFormat, ...)
+{
+    va_list va;
+    va_start(va, pszFormat);
+    int rc = DBGFR3RegNmPrintfV(pVM, idCpu, pszBuf, cbBuf, pszFormat, va);
+    va_end(va);
+    return rc;
+}
+
