Index: /trunk/include/iprt/fuzz.h
===================================================================
--- /trunk/include/iprt/fuzz.h	(revision 72648)
+++ /trunk/include/iprt/fuzz.h	(revision 72649)
@@ -297,4 +297,20 @@
 
 
+/**
+ * Fuzzing observer statistics.
+ */
+typedef struct RTFUZZOBSSTATS
+{
+    /** Number of fuzzed inputs per second. */
+    uint32_t                    cFuzzedInputsPerSec;
+    /** Number of overall fuzzed inputs. */
+    uint32_t                    cFuzzedInputs;
+    /** Number of observed hangs. */
+    uint32_t                    cFuzzedInputsHang;
+    /** Number of observed crashes. */
+    uint32_t                    cFuzzedInputsCrash;
+} RTFUZZOBSSTATS;
+/** Pointer to a fuzzing observer statistics record. */
+typedef RTFUZZOBSSTATS *PRTFUZZOBSSTATS;
 
 /**
@@ -324,4 +340,13 @@
  */
 RTDECL(int) RTFuzzObsQueryCtx(RTFUZZOBS hFuzzObs, PRTFUZZCTX phFuzzCtx);
+
+/**
+ * Queries the current statistics for the given fuzzing observer.
+ *
+ * @returns IPRT status code.
+ * @param   hFuzzObs            The fuzzing observer handle.
+ * @param   pStats              Where to store the statistics to.
+ */
+RTDECL(int) RTFuzzObsQueryStats(RTFUZZOBS hFuzzObs, PRTFUZZOBSSTATS pStats);
 
 /**
Index: /trunk/include/iprt/mangling.h
===================================================================
--- /trunk/include/iprt/mangling.h	(revision 72648)
+++ /trunk/include/iprt/mangling.h	(revision 72649)
@@ -1039,4 +1039,5 @@
 # define RTFuzzObsExecStop                              RT_MANGLER(RTFuzzObsExecStop)
 # define RTFuzzObsQueryCtx                              RT_MANGLER(RTFuzzObsQueryCtx)
+# define RTFuzzObsQueryStats                            RT_MANGLER(RTFuzzObsQueryStats)
 # define RTFuzzObsSetResultDirectory                    RT_MANGLER(RTFuzzObsSetResultDirectory)
 # define RTFuzzObsSetTestBinary                         RT_MANGLER(RTFuzzObsSetTestBinary)
Index: /trunk/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp	(revision 72648)
+++ /trunk/src/VBox/Runtime/common/fuzz/fuzz-observer.cpp	(revision 72649)
@@ -125,4 +125,11 @@
     /** Pointer to the array of observer thread states. */
     PRTFUZZOBSTHRD              paObsThreads;
+    /** Timestamp of the last stats query. */
+    uint64_t                    tsLastStats;
+    /** Last number of fuzzed inputs per second if we didn't gather enough data in between
+     * statistic queries. */
+    uint32_t                    cFuzzedInputsPerSecLast;
+    /** Fuzzing statistics. */
+    RTFUZZOBSSTATS              Stats;
 } RTFUZZOBSINT;
 
@@ -754,11 +761,20 @@
             RTPROCSTATUS ProcSts;
             rc = rtFuzzObsExecCtxClientRun(pThis, pExecCtx, &ProcSts);
+            ASMAtomicIncU32(&pThis->Stats.cFuzzedInputs);
+            ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsPerSec);
+
             if (RT_SUCCESS(rc))
             {
                 if (ProcSts.enmReason != RTPROCEXITREASON_NORMAL)
+                {
+                    ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsCrash);
                     rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
+                }
             }
             else if (rc == VERR_TIMEOUT)
+            {
+                ASMAtomicIncU32(&pThis->Stats.cFuzzedInputsHang);
                 rc = rtFuzzObsAddInputToResults(pThis, pObsThrd->hFuzzInput, pExecCtx);
+            }
             else
                 AssertFailed();
@@ -939,4 +955,9 @@
         pThis->cThreads      = 0;
         pThis->paObsThreads  = NULL;
+        pThis->tsLastStats   = RTTimeMilliTS();
+        pThis->Stats.cFuzzedInputsPerSec = 0;
+        pThis->Stats.cFuzzedInputs       = 0;
+        pThis->Stats.cFuzzedInputsHang   = 0;
+        pThis->Stats.cFuzzedInputsCrash  = 0;
         rc = RTFuzzCtxCreate(&pThis->hFuzzCtx);
         if (RT_SUCCESS(rc))
@@ -995,4 +1016,29 @@
 
 
+RTDECL(int) RTFuzzObsQueryStats(RTFUZZOBS hFuzzObs, PRTFUZZOBSSTATS pStats)
+{
+    PRTFUZZOBSINT pThis = hFuzzObs;
+    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+    AssertPtrReturn(pStats, VERR_INVALID_POINTER);
+
+    uint64_t tsStatsQuery = RTTimeMilliTS();
+    uint32_t cFuzzedInputsPerSec = ASMAtomicXchgU32(&pThis->Stats.cFuzzedInputsPerSec, 0);
+
+    pStats->cFuzzedInputsCrash  = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsCrash);
+    pStats->cFuzzedInputsHang   = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputsHang);
+    pStats->cFuzzedInputs       = ASMAtomicReadU32(&pThis->Stats.cFuzzedInputs);
+    uint64_t cPeriodSec = (tsStatsQuery - pThis->tsLastStats) / 1000;
+    if (cPeriodSec)
+    {
+        pStats->cFuzzedInputsPerSec    = cFuzzedInputsPerSec / cPeriodSec;
+        pThis->cFuzzedInputsPerSecLast = pStats->cFuzzedInputsPerSec;
+        pThis->tsLastStats             = tsStatsQuery;
+    }
+    else
+        pStats->cFuzzedInputsPerSec = pThis->cFuzzedInputsPerSecLast;
+    return VINF_SUCCESS;
+}
+
+
 RTDECL(int) RTFuzzObsSetTmpDirectory(RTFUZZOBS hFuzzObs, const char *pszTmp)
 {
@@ -1052,20 +1098,16 @@
             char **ppszOwn = pThis->papszArgs;
             const char * const *ppsz = papszArgs;
-            while (   *ppsz != NULL
-                   && RT_SUCCESS(rc))
+            for (unsigned i = 0; i < cArgs; i++)
             {
-                *ppszOwn = RTStrDup(*ppsz);
-                if (RT_UNLIKELY(!*ppszOwn))
+                pThis->papszArgs[i] = RTStrDup(papszArgs[i]);
+                if (RT_UNLIKELY(!pThis->papszArgs[i]))
                 {
-                    while (ppszOwn > pThis->papszArgs)
+                    while (i > 0)
                     {
-                        ppszOwn--;
-                        RTStrFree(*ppszOwn);
+                        i--;
+                        RTStrFree(pThis->papszArgs[i]);
                     }
                     break;
                 }
-
-                ppszOwn++;
-                ppsz++;
             }
 
Index: /trunk/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp
===================================================================
--- /trunk/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp	(revision 72648)
+++ /trunk/src/VBox/Runtime/common/fuzz/fuzzmastercmd.cpp	(revision 72649)
@@ -91,4 +91,8 @@
     /** Flag whether to shutdown. */
     bool                        fShutdown;
+    /** Flag whether to send a response along with the ACK. */
+    bool                        fAckResponse;
+    /** The response message. */
+    char                        aszResponse[_1K];
 } RTFUZZCMDMASTER;
 /** Pointer to a fuzzing master command state. */
@@ -813,6 +817,28 @@
 static int rtFuzzCmdMasterProcessJsonReqQueryStats(PRTFUZZCMDMASTER pThis, RTJSONVAL hJsonRoot, PRTERRINFO pErrInfo)
 {
-    RT_NOREF(pThis, hJsonRoot, pErrInfo);
-    return VERR_NOT_IMPLEMENTED;
+    PRTFUZZRUN pFuzzRun;
+    int rc = rtFuzzCmdMasterQueryFuzzRunFromJson(pThis, hJsonRoot, "Id", pErrInfo, &pFuzzRun);
+    if (RT_SUCCESS(rc))
+    {
+        RTFUZZOBSSTATS Stats;
+        rc = RTFuzzObsQueryStats(pFuzzRun->hFuzzObs, &Stats);
+        if (RT_SUCCESS(rc))
+        {
+            const char s_szStats[] = "{ \"FuzzedInputsPerSec\": %u\n"
+                                     "  \"FuzzedInputs\":       %u\n"
+                                     "  \"FuzzedInputsHang\":   %u\n"
+                                     "  \"FuzzedInputsCrash\":   %u\n}";
+            ssize_t cch = RTStrPrintf2(&pThis->aszResponse[0], sizeof(pThis->aszResponse), s_szStats, Stats.cFuzzedInputsPerSec,
+                                       Stats.cFuzzedInputs, Stats.cFuzzedInputsHang, Stats.cFuzzedInputsCrash);
+            if (cch > 0)
+                pThis->fAckResponse = true;
+            else
+                rc = rtFuzzCmdMasterErrorRc(pErrInfo, VERR_BUFFER_OVERFLOW, "Request error: Response data buffer overflow", rc);
+        }
+        else
+            rc = rtFuzzCmdMasterErrorRc(pErrInfo, rc, "Request error: Failed to query fuzzing statistics with %Rrc", rc);
+    }
+
+    return rc;
 }
 
@@ -902,9 +928,21 @@
  * @returns nothing.
  * @param   hSocket             The socket handle to send the ACK to.
- */
-static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket)
+ * @param   pszResponse         Additional response data.
+ */
+static void rtFuzzCmdMasterTcpSendAck(RTSOCKET hSocket, const char *pszResponse)
 {
     const char s_szSucc[] = "{ \"Status\": \"ACK\" }\n";
-    RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
+    const char s_szSuccResp[] = "{ \"Status\": \"ACK\"\n  \"Response\":\n    %s\n }\n";
+    if (pszResponse)
+    {
+        char szTmp[_1K];
+        ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szSuccResp, pszResponse);
+        if (cchResp > 0)
+            RTTcpWrite(hSocket, szTmp, cchResp);
+        else
+            RTTcpWrite(hSocket, s_szSucc, strlen(s_szSucc));
+    }
+    else
+        RTTcpWrite(hSocket, s_szSucc, sizeof(s_szSucc));
 }
 
@@ -924,5 +962,5 @@
     if (pErrInfo)
     {
-        char szTmp[1024];
+        char szTmp[_1K];
         ssize_t cchResp = RTStrPrintf2(szTmp, sizeof(szTmp), s_szFailInfo, pErrInfo->pszMsg);
         if (cchResp > 0)
@@ -973,4 +1011,6 @@
                     RTErrInfoInitStatic(&ErrInfo);
 
+                    pThis->fAckResponse = false;
+                    RT_ZERO(pThis->aszResponse);
                     rc = RTJsonParseFromBuf(&hJsonReq, pbReq, cbReq, &ErrInfo.Core);
                     if (RT_SUCCESS(rc))
@@ -978,5 +1018,5 @@
                         rc = rtFuzzCmdMasterProcessJsonReq(pThis, hJsonReq, &ErrInfo.Core);
                         if (RT_SUCCESS(rc))
-                            rtFuzzCmdMasterTcpSendAck(hSocket);
+                            rtFuzzCmdMasterTcpSendAck(hSocket, pThis->fAckResponse ? pThis->aszResponse : NULL);
                         else
                             rtFuzzCmdMasterTcpSendNAck(hSocket, &ErrInfo.Core);
