Index: /trunk/src/VBox/Debugger/DBGPlugInLinux.cpp
===================================================================
--- /trunk/src/VBox/Debugger/DBGPlugInLinux.cpp	(revision 61557)
+++ /trunk/src/VBox/Debugger/DBGPlugInLinux.cpp	(revision 61558)
@@ -162,4 +162,5 @@
 };
 
+static const uint8_t g_abLinuxVersion[] = "Linux version ";
 
 /**
@@ -308,24 +309,148 @@
 
 /**
- * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
- */
-static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
-                                                            char *pszBuf, size_t cbBuf, size_t *pcbActual)
-{
-    PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg);
-
-    if (cMessages < 1)
-        return VERR_INVALID_PARAMETER;
-
-    /*
-     * Resolve the symbols we need and read their values.
-     */
-    RTDBGAS  hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
-    RTDBGMOD hMod;
-    int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod);
+ * Returns whether the log buffer is a simple ascii buffer or a record based implementation
+ * based on the kernel version found.
+ *
+ * @returns Flag whether the log buffer is the simple ascii buffer.
+ * @param   pThis               The Linux digger data.
+ * @param   pUVM                The user mode VM handle.
+ */
+static bool dbgDiggerLinuxLogBufferIsAsciiBuffer(PDBGDIGGERLINUX pThis, PUVM pUVM)
+{
+    char szTmp[128];
+    char const *pszVer = &szTmp[sizeof(g_abLinuxVersion) - 1];
+
+    RT_ZERO(szTmp);
+    int rc = DBGFR3MemReadString(pUVM, 0, &pThis->AddrLinuxBanner, szTmp, sizeof(szTmp) - 1);
+    if (    RT_SUCCESS(rc)
+        &&  RTStrVersionCompare(pszVer, "3.4") == -1)
+        return true;
+
+    return false;
+}
+
+/**
+ * Worker to get at the kernel log for pre 3.4 kernels where the log buffer was just a char buffer.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The Linux digger data.
+ * @param   pUVM        The VM user mdoe handle.
+ * @param   hMod        The debug module handle.
+ * @param   fFlags      Flags reserved for future use, MBZ.
+ * @param   cMessages   The number of messages to retrieve, counting from the
+ *                      end of the log (i.e. like tail), use UINT32_MAX for all.
+ * @param   pszBuf      The output buffer.
+ * @param   cbBuf       The buffer size.
+ * @param   pcbActual   Where to store the number of bytes actually returned,
+ *                      including zero terminator.  On VERR_BUFFER_OVERFLOW this
+ *                      holds the necessary buffer size.  Optional.
+ */
+static int dbgDiggerLinuxLogBufferQueryAscii(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
+                                             uint32_t fFlags, uint32_t cMessages,
+                                             char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+    int rc = VINF_SUCCESS;
+    RTGCPTR  GCPtrLogBuf;
+    uint32_t cbLogBuf;
+
+    struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
+    {
+        { &GCPtrLogBuf, sizeof(GCPtrLogBuf),    pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t),   "log_buf" },
+        { &cbLogBuf,    sizeof(cbLogBuf),       sizeof(cbLogBuf),                                      "log_buf_len" },
+    };
+    for (uint32_t i = 0; i < RT_ELEMENTS(aSymbols); i++)
+    {
+        RTDBGSYMBOL SymInfo;
+        rc = RTDbgModSymbolByName(hMod, aSymbols[i].pszSymbol, &SymInfo);
+        if (RT_SUCCESS(rc))
+        {
+            RT_BZERO(aSymbols[i].pvVar, aSymbols[i].cbHost);
+            Assert(aSymbols[i].cbHost >= aSymbols[i].cbGuest);
+            DBGFADDRESS Addr;
+            rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
+                               DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
+                               aSymbols[i].pvVar,  aSymbols[i].cbGuest);
+            if (RT_SUCCESS(rc))
+                continue;
+            Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Reading '%s' at %RGv: %Rrc\n", aSymbols[i].pszSymbol, Addr.FlatPtr, rc));
+        }
+        else
+            Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error looking up '%s': %Rrc\n", aSymbols[i].pszSymbol, rc));
+        rc = VERR_NOT_FOUND;
+        break;
+    }
+
     if (RT_FAILURE(rc))
+        return rc;
+
+    /*
+     * Check if the values make sense.
+     */
+    if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
+    {
+        Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
         return VERR_NOT_FOUND;
-    RTDbgAsRelease(hAs);
-
+    }
+    if (   cbLogBuf < 4096
+        || !RT_IS_POWER_OF_TWO(cbLogBuf)
+        || cbLogBuf > 16*_1M)
+    {
+        Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf_len' value %#x is not valid.\n", cbLogBuf));
+        return VERR_NOT_FOUND;
+    }
+
+    /*
+     * Read the whole log buffer.
+     */
+    uint8_t *pbLogBuf = (uint8_t *)RTMemAlloc(cbLogBuf);
+    if (!pbLogBuf)
+    {
+        Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Failed to allocate %#x bytes for log buffer\n", cbLogBuf));
+        return VERR_NO_MEMORY;
+    }
+    DBGFADDRESS Addr;
+    rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, GCPtrLogBuf), pbLogBuf, cbLogBuf);
+    if (RT_FAILURE(rc))
+    {
+        Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: Error reading %#x bytes of log buffer at %RGv: %Rrc\n",
+             cbLogBuf, Addr.FlatPtr, rc));
+        RTMemFree(pbLogBuf);
+        return VERR_NOT_FOUND;
+    }
+
+    /** @todo: Try to parse where the single messages start to make use of cMessages. */
+    memcpy(&pszBuf[0], pbLogBuf, RT_MIN(cbBuf, cbLogBuf));
+
+    /* Done with the buffer. */
+    RTMemFree(pbLogBuf);
+
+    /* Set return size value. */
+    if (pcbActual)
+        *pcbActual = RT_MIN(cbBuf, cbLogBuf);
+
+    return cbBuf <= cbLogBuf ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
+}
+
+/**
+ * Worker to get at the kernel log for post 3.4 kernels where the log buffer contains records.
+ *
+ * @returns VBox status code.
+ * @param   pThis       The Linux digger data.
+ * @param   pUVM        The VM user mdoe handle.
+ * @param   hMod        The debug module handle.
+ * @param   fFlags      Flags reserved for future use, MBZ.
+ * @param   cMessages   The number of messages to retrieve, counting from the
+ *                      end of the log (i.e. like tail), use UINT32_MAX for all.
+ * @param   pszBuf      The output buffer.
+ * @param   cbBuf       The buffer size.
+ * @param   pcbActual   Where to store the number of bytes actually returned,
+ *                      including zero terminator.  On VERR_BUFFER_OVERFLOW this
+ *                      holds the necessary buffer size.  Optional.
+ */
+static int dbgDiggerLinuxLogBufferQueryRecords(PDBGDIGGERLINUX pThis, PUVM pUVM, RTDBGMOD hMod,
+                                               uint32_t fFlags, uint32_t cMessages,
+                                               char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+    int rc = VINF_SUCCESS;
     RTGCPTR  GCPtrLogBuf;
     uint32_t cbLogBuf;
@@ -335,5 +460,5 @@
     struct { void *pvVar; size_t cbHost, cbGuest; const char *pszSymbol; } aSymbols[] =
     {
-        { &GCPtrLogBuf, sizeof(GCPtrLogBuf),    pData->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t),   "log_buf" },
+        { &GCPtrLogBuf, sizeof(GCPtrLogBuf),    pThis->f64Bit ? sizeof(uint64_t) : sizeof(uint32_t),   "log_buf" },
         { &cbLogBuf,    sizeof(cbLogBuf),       sizeof(cbLogBuf),                                      "log_buf_len" },
         { &idxFirst,    sizeof(idxFirst),       sizeof(idxFirst),                                      "log_first_idx" },
@@ -350,5 +475,5 @@
             DBGFADDRESS Addr;
             rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/,
-                               DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pData->AddrKernelBase.FlatPtr),
+                               DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value + pThis->AddrKernelBase.FlatPtr),
                                aSymbols[i].pvVar,  aSymbols[i].cbGuest);
             if (RT_SUCCESS(rc))
@@ -372,9 +497,5 @@
         idxFirst = 0;
         idxNext = 0;
-        rc = dbgDiggerLinuxQueryLogBufferPtrs(pData, pUVM, hMod, &GCPtrLogBuf, &cbLogBuf);
-
-        /* Release the module in any case. */
-        RTDbgModRelease(hMod);
-
+        rc = dbgDiggerLinuxQueryLogBufferPtrs(pThis, pUVM, hMod, &GCPtrLogBuf, &cbLogBuf);
         if (RT_FAILURE(rc))
             return rc;
@@ -384,5 +505,5 @@
      * Check if the values make sense.
      */
-    if (pData->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
+    if (pThis->f64Bit ? !LNX64_VALID_ADDRESS(GCPtrLogBuf) : !LNX32_VALID_ADDRESS(GCPtrLogBuf))
     {
         Log(("dbgDiggerLinuxIDmsg_QueryKernelLog: 'log_buf' value %RGv is not valid.\n", GCPtrLogBuf));
@@ -543,4 +664,52 @@
         *pcbActual = offDst;
 
+    if (offDst <= cbBuf)
+        return VINF_SUCCESS;
+    else
+        return VERR_BUFFER_OVERFLOW;
+}
+
+/**
+ * @interface_method_impl{DBGFOSIDMESG,pfnQueryKernelLog}
+ */
+static DECLCALLBACK(int) dbgDiggerLinuxIDmsg_QueryKernelLog(PDBGFOSIDMESG pThis, PUVM pUVM, uint32_t fFlags, uint32_t cMessages,
+                                                            char *pszBuf, size_t cbBuf, size_t *pcbActual)
+{
+    PDBGDIGGERLINUX pData = RT_FROM_MEMBER(pThis, DBGDIGGERLINUX, IDmesg);
+
+    if (cMessages < 1)
+        return VERR_INVALID_PARAMETER;
+
+    /*
+     * Resolve the symbols we need and read their values.
+     */
+    RTDBGAS  hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
+    RTDBGMOD hMod;
+    int rc = RTDbgAsModuleByName(hAs, "vmlinux", 0, &hMod);
+    if (RT_FAILURE(rc))
+        return VERR_NOT_FOUND;
+    RTDbgAsRelease(hAs);
+
+    size_t cbActual;
+    /*
+     * Check whether the kernel log buffer is a simple char buffer or the newer
+     * record based implementation.
+     * The record based implementation was presumably introduced with kernel 3.4,
+     * see: http://thread.gmane.org/gmane.linux.kernel/1284184
+     */
+    if (dbgDiggerLinuxLogBufferIsAsciiBuffer(pData, pUVM))
+        rc = dbgDiggerLinuxLogBufferQueryAscii(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
+    else
+        rc = dbgDiggerLinuxLogBufferQueryRecords(pData, pUVM, hMod, fFlags, cMessages, pszBuf, cbBuf, &cbActual);
+
+    /* Release the module in any case. */
+    RTDbgModRelease(hMod);
+
+    if (RT_FAILURE(rc) && rc != VERR_BUFFER_OVERFLOW)
+        return rc;
+
+    if (pcbActual)
+        *pcbActual = cbActual;
+
     /*
      * All VBox strings are UTF-8 and bad things may in theory happen if we
@@ -549,7 +718,8 @@
      * no defined code set in reality.
      */
-    if (offDst <= cbBuf)
-    {
-        pszBuf[offDst - 1] = '\0';
+    if (   RT_SUCCESS(rc)
+        && cbActual <= cbBuf)
+    {
+        pszBuf[cbActual - 1] = '\0';
         RTStrPurgeEncoding(pszBuf);
         return VINF_SUCCESS;
@@ -1330,11 +1500,10 @@
         DBGFR3AddrFromFlat(pUVM, &KernelAddr, g_au64LnxKernelAddresses[i]);
         DBGFADDRESS HitAddr;
-        static const uint8_t s_abLinuxVersion[] = "Linux version ";
         int rc = DBGFR3MemScan(pUVM, 0, &KernelAddr, LNX_MAX_KERNEL_SIZE, 1,
-                               s_abLinuxVersion, sizeof(s_abLinuxVersion) - 1, &HitAddr);
+                               g_abLinuxVersion, sizeof(g_abLinuxVersion) - 1, &HitAddr);
         if (RT_SUCCESS(rc))
         {
             char szTmp[128];
-            char const *pszX = &szTmp[sizeof(s_abLinuxVersion) - 1];
+            char const *pszX = &szTmp[sizeof(g_abLinuxVersion) - 1];
             rc = DBGFR3MemReadString(pUVM, 0, &HitAddr, szTmp, sizeof(szTmp));
             if (    RT_SUCCESS(rc)
