[NTOSKRNL]
authorDmitry Gorbachev <gorbachev@reactos.org>
Thu, 7 Jul 2011 19:18:16 +0000 (19:18 +0000)
committerDmitry Gorbachev <gorbachev@reactos.org>
Thu, 7 Jul 2011 19:18:16 +0000 (19:18 +0000)
Minas Abrahamyan (minas \dot/ subs \at/ gmail \dot/ com):
- Allow to view already shown, logged debug messages on the screen (bug #6018).

svn path=/trunk/; revision=52556

reactos/ntoskrnl/kd/kdio.c
reactos/ntoskrnl/kdbg/kdb_cli.c

index 769b565..c51a1b5 100644 (file)
@@ -33,7 +33,15 @@ ULONG KdpPort;
 CHAR KdpScreenLineBuffer[KdpScreenLineLenght + 1] = "";
 ULONG KdpScreenLineBufferPos = 0, KdpScreenLineLength = 0;
 
-/* DEBUG LOG FUNCTIONS *******************************************************/
+const ULONG KdpDmesgBufferSize = 128 * 1024; // 512*1024; // 5*1024*1024;
+PCHAR KdpDmesgBuffer = NULL;
+volatile ULONG KdpDmesgCurrentPosition = 0;
+volatile ULONG KdpDmesgFreeBytes = 0;
+volatile ULONG KdbDmesgTotalWritten = 0;
+KSPIN_LOCK KdpDmesgLogSpinLock;
+extern volatile BOOLEAN KdbpIsInDmesgMode;
+
+/* FILE DEBUG LOG FUNCTIONS **************************************************/
 
 VOID
 NTAPI
@@ -49,8 +57,14 @@ KdpLoggerThread(PVOID Context)
         KeWaitForSingleObject(&KdpLoggerThreadEvent, 0, KernelMode, FALSE, NULL);
 
         /* Bug */
+        /* Keep KdpCurrentPosition and KdpFreeBytes values in local
+         * variables to avoid their possible change from Producer part,
+         * KdpPrintToLogFile function
+         */
         end = KdpCurrentPosition;
         num = KdpFreeBytes;
+
+        /* Now securely calculate values, based on local variables */
         beg = (end + num) % KdpBufferSize;
         num = KdpBufferSize - num;
 
@@ -148,6 +162,7 @@ KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
     IO_STATUS_BLOCK Iosb;
     HANDLE ThreadHandle;
     KPRIORITY Priority;
+    SIZE_T MemSizeMBs;
 
     if (!KdpDebugMode.File) return;
 
@@ -175,6 +190,8 @@ KdpInitDebugLog(PKD_DISPATCH_TABLE DispatchTable,
         /* Display separator + ReactOS version at start of the debug log */
         DPRINT1("---------------------------------------------------------------\n");
         DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
+        MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
+        DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
     }
     else if (BootPhase == 2)
     {
@@ -276,6 +293,7 @@ NTAPI
 KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
               ULONG BootPhase)
 {
+    SIZE_T MemSizeMBs;
     if (!KdpDebugMode.Serial) return;
 
     if (BootPhase == 0)
@@ -301,6 +319,8 @@ KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
         /* Display separator + ReactOS version at start of the debug log */
         DPRINT1("-----------------------------------------------------\n");
         DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
+        MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
+        DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
         DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
         DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
                                             KeLoaderBlock->NtHalPathName,
@@ -315,11 +335,19 @@ KdpSerialInit(PKD_DISPATCH_TABLE DispatchTable,
 
 /* SCREEN FUNCTIONS **********************************************************/
 
+/*
+ * Screen debug logger function KdpScreenPrint() writes text messages into
+ * KdpDmesgBuffer, using it as a circular buffer. KdpDmesgBuffer contents could
+ * be later (re)viewed using dmesg command of kdbg. KdpScreenPrint() protects
+ * KdpDmesgBuffer from simultaneous writes by use of KdpDmesgLogSpinLock.
+ */
 VOID
 NTAPI
 KdpScreenPrint(LPSTR Message,
                ULONG Length)
 {
+    ULONG beg, end, num;
+    KIRQL OldIrql;
     PCHAR pch = (PCHAR) Message;
 
     while (*pch)
@@ -354,16 +382,74 @@ KdpScreenPrint(LPSTR Message,
             KdpScreenLineBuffer[0] = '\0';
             KdpScreenLineLength = KdpScreenLineBufferPos = 0;
         }
-        
+
         ++pch;
     }
-    
+
     /* Print buffered characters */
     if(KdpScreenLineBufferPos != KdpScreenLineLength)
     {
         HalDisplayString(KdpScreenLineBuffer + KdpScreenLineBufferPos);
         KdpScreenLineBufferPos = KdpScreenLineLength;
     }
+
+    /* Dmesg: store Message in the buffer to show it later */
+    if (KdbpIsInDmesgMode)
+       return;
+
+    if (KdpDmesgBuffer == NULL)
+      return;
+
+    /* Acquire the printing spinlock without waiting at raised IRQL */
+    while (TRUE)
+    {
+        /* Wait when the spinlock becomes available */
+        while (!KeTestSpinLock(&KdpDmesgLogSpinLock));
+
+        /* Spinlock was free, raise IRQL */
+        KeRaiseIrql(HIGH_LEVEL, &OldIrql);
+
+        /* Try to get the spinlock */
+        if (KeTryToAcquireSpinLockAtDpcLevel(&KdpDmesgLogSpinLock))
+            break;
+
+        /* Someone else got the spinlock, lower IRQL back */
+        KeLowerIrql(OldIrql);
+    }
+
+    /* Invariant: always_true(KdpDmesgFreeBytes == KdpDmesgBufferSize);
+     * set num to min(KdpDmesgFreeBytes, Length).
+     */
+    num = (Length < KdpDmesgFreeBytes) ? Length : KdpDmesgFreeBytes; 
+    beg = KdpDmesgCurrentPosition;
+    if (num != 0)
+    {
+        end = (beg + num) % KdpDmesgBufferSize;
+        if (end > beg)
+        {
+            RtlCopyMemory(KdpDmesgBuffer + beg, Message, Length);
+        }
+        else
+        {
+            RtlCopyMemory(KdpDmesgBuffer + beg, Message, KdpDmesgBufferSize - beg);
+            RtlCopyMemory(KdpDmesgBuffer, Message + (KdpDmesgBufferSize - beg), end);
+        }
+        KdpDmesgCurrentPosition = end;
+
+        /* Counting the total bytes written */
+        KdbDmesgTotalWritten += num;
+    }
+
+    /* Release spinlock */
+    KiReleaseSpinLock(&KdpDmesgLogSpinLock);
+
+    /* Lower IRQL */
+    KeLowerIrql(OldIrql);
+
+    /* Optional step(?): find out a way to notify about buffer exhaustion,
+     * and possibly fall into kbd to use dmesg command: user will read
+     * debug messages before they will be wiped over by next writes.
+     */
 }
 
 VOID
@@ -371,6 +457,7 @@ NTAPI
 KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
               ULONG BootPhase)
 {
+    SIZE_T MemSizeMBs;
     if (!KdpDebugMode.Screen) return;
 
     if (BootPhase == 0)
@@ -382,6 +469,30 @@ KdpScreenInit(PKD_DISPATCH_TABLE DispatchTable,
         /* Register as a Provider */
         InsertTailList(&KdProviders, &DispatchTable->KdProvidersList);
     }
+    else if (BootPhase == 1)
+    {
+      /* Allocate a buffer for dmesg log buffer. +1 for terminating null,
+       * see kdbp_cli.c:KdbpCmdDmesg()/2
+       */
+      KdpDmesgBuffer = ExAllocatePool(NonPagedPool, KdpDmesgBufferSize + 1);
+      RtlZeroMemory(KdpDmesgBuffer, KdpDmesgBufferSize + 1);
+      KdpDmesgFreeBytes = KdpDmesgBufferSize;
+      KdbDmesgTotalWritten = 0;
+
+      /* Initialize spinlock */
+      KeInitializeSpinLock(&KdpDmesgLogSpinLock);
+
+      /* Display separator + ReactOS version at start of the debug log */
+      DPRINT1("-----------------------------------------------------\n");
+      DPRINT1("ReactOS "KERNEL_VERSION_STR" (Build "KERNEL_VERSION_BUILD_STR")\n");
+      MemSizeMBs = MmNumberOfPhysicalPages * PAGE_SIZE / 1024 / 1024;
+      DPRINT1("%u System Processor [%u MB Memory]\n", KeNumberProcessors, MemSizeMBs);
+      DPRINT1("Command Line: %s\n", KeLoaderBlock->LoadOptions);
+      DPRINT1("ARC Paths: %s %s %s %s\n", KeLoaderBlock->ArcBootDeviceName,
+                                          KeLoaderBlock->NtHalPathName,
+                                          KeLoaderBlock->ArcHalDeviceName,
+                                          KeLoaderBlock->NtBootPathName);
+    }
     else if (BootPhase == 2)
     {
         HalDisplayString("\n   Screen debugging enabled\n\n");
index 08c5f10..3a0b98c 100644 (file)
 #define KEY_SCAN_UP     72
 #define KEY_SCAN_DOWN   80
 
+/* Scan codes of keyboard keys: */
+#define KEYSC_END       0x004f
+#define KEYSC_PAGEUP    0x0049
+#define KEYSC_PAGEDOWN  0x0051
+#define KEYSC_HOME      0x0047
+#define KEYSC_ARROWUP   0x0048
+
 #define KDB_ENTER_CONDITION_TO_STRING(cond)                               \
                    ((cond) == KdbDoNotEnter ? "never" :                   \
                    ((cond) == KdbEnterAlways ? "always" :                 \
@@ -81,6 +88,7 @@ static BOOLEAN KdbpCmdBugCheck(ULONG Argc, PCHAR Argv[]);
 static BOOLEAN KdbpCmdFilter(ULONG Argc, PCHAR Argv[]);
 static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
 static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
+static BOOLEAN KdbpCmdDmesg(ULONG Argc, PCHAR Argv[]);
 
 /* GLOBALS *******************************************************************/
 
@@ -101,6 +109,17 @@ static LONG KdbNumberOfColsTerminal = -1;
 PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
 BOOLEAN KdbpBugCheckRequested = FALSE;
 
+/* Vars for dmesg */
+/* defined here, used in ../kd/kdio.c: */
+volatile BOOLEAN KdbpIsInDmesgMode = FALSE;
+
+/* defined in ../kd/kdio.c, declare here: */
+extern const ULONG KdpDmesgBufferSize;
+extern PCHAR KdpDmesgBuffer;
+extern volatile ULONG KdpDmesgCurrentPosition;
+extern volatile ULONG KdpDmesgFreeBytes;
+extern volatile ULONG KdbDmesgTotalWritten;
+
 static const struct
 {
     PCHAR Name;
@@ -150,6 +169,8 @@ static const struct
     { "bugcheck", "bugcheck", "Bugchecks the system.", KdbpCmdBugCheck },
     { "filter", "filter [error|warning|trace|info|level]+|-[componentname|default]", "Enable/disable debug channels", KdbpCmdFilter },
     { "set", "set [var] [value]", "Sets var to value or displays value of var.", KdbpCmdSet },
+    { "dmesg", "dmesg", "Display debug messages on screen, with navigation on pages.", KdbpCmdDmesg },
+    { "kmsg", "kmsg", "Kernel dmesg. Alias for dmesg.", KdbpCmdDmesg },
     { "help", "help", "Display help screen.", KdbpCmdHelp }
 };
 
@@ -1895,6 +1916,58 @@ KdbpCmdBugCheck(
     return FALSE;
 }
 
+VOID
+KdbpPager(
+    IN PCHAR Buffer,
+    IN ULONG BufLength);
+
+/*!\brief Display debug messages on screen, with paging.
+ *
+ * Keys for per-page view: Home, End, PageUp, Arrow Up, PageDown,
+ * all others are as PageDown.
+ */
+static BOOLEAN
+KdbpCmdDmesg(
+    ULONG Argc,
+    PCHAR Argv[])
+{
+  ULONG beg, end;
+
+  KdbpIsInDmesgMode = TRUE; /* Toggle logging flag */
+  if (!KdpDmesgBuffer)
+  {
+    KdbpPrint("Dmesg: error, buffer is not allocated! /DEBUGPORT=SCREEN kernel param required for dmesg.\n");
+    return TRUE;
+  }
+
+  KdbpPrint("*** Dmesg *** TotalWritten=%lu, BufferSize=%lu, CurrentPosition=%lu\n",
+            KdbDmesgTotalWritten, KdpDmesgBufferSize, KdpDmesgCurrentPosition);
+
+  // Pass data to the pager:
+  end = KdpDmesgCurrentPosition;
+  beg = (end + KdpDmesgFreeBytes) % KdpDmesgBufferSize;
+
+  // no roll-overs, and overwritten=lost bytes
+  if (KdbDmesgTotalWritten <= KdpDmesgBufferSize)
+  {
+    // show buffer (KdpDmesgBuffer + beg, num)
+    KdbpPager(KdpDmesgBuffer, KdpDmesgCurrentPosition);
+  }
+  else
+  {
+    // show 2 buffers: (KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg)
+    //            and: (KdpDmesgBuffer,       end)
+    KdbpPager(KdpDmesgBuffer + beg, KdpDmesgBufferSize - beg);
+    KdbpPrint("*** Dmesg: buffer rollup ***\n");
+    KdbpPager(KdpDmesgBuffer,       end);
+  }
+  KdbpPrint("*** Dmesg: end of output ***\n");
+
+  KdbpIsInDmesgMode = FALSE; /* Toggle logging flag */
+
+  return TRUE;
+}
+
 /*!\brief Sets or displays a config variables value.
  */
 static BOOLEAN
@@ -2110,6 +2183,7 @@ KdbpCmdHelp(
  *
  * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
  *       number of lines required to print a single line from the Buffer in the terminal.
+ *       Prints maximum 4096 chars, because of its buffer size.
  */
 VOID
 KdbpPrint(
@@ -2215,12 +2289,12 @@ KdbpPrint(
         if (KdbNumberOfRowsTerminal <= 0)
         {
             /* Set number of rows to the default. */
-            KdbNumberOfRowsTerminal = 24;
+            KdbNumberOfRowsTerminal = 23; //24; //Mna.: 23 for SCREEN debugport
         }
         else if (KdbNumberOfColsTerminal <= 0)
         {
             /* Set number of cols to the default. */
-            KdbNumberOfColsTerminal = 80;
+            KdbNumberOfColsTerminal = 75; //80; //Mna.: 75 for SCREEN debugport
         }
     }
 
@@ -2256,6 +2330,7 @@ KdbpPrint(
                 DbgPrint("\n");
 
             DbgPrint("--- Press q to abort, any other key to continue ---");
+            RowsPrintedByTerminal++; /* added by Mna. */
 
             if (KdbDebugState & KD_DEBUG_KDSERIAL)
                 c = KdbpGetCharSerial();
@@ -2337,6 +2412,350 @@ KdbpPrint(
     }
 }
 
+/** memrchr(), explicitly defined, since was absent in MinGW of RosBE. */
+/*
+ * Reverse memchr()
+ * Find the last occurrence of 'c' in the buffer 's' of size 'n'.
+ */
+void *
+memrchr(const void *s, int c, size_t n)
+{
+    const unsigned char *cp;
+
+    if (n != 0)
+    {
+        cp = (unsigned char *)s + n;
+        do
+        {
+            if (*(--cp) == (unsigned char)c)
+                return (void *)cp;
+        } while (--n != 0);
+    }
+    return NULL;
+}
+
+/*!\brief Calculate pointer position for N lines upper of current position.
+ *
+ * \param Buffer     Characters buffer to operate on.
+ * \param BufLength  Buffer size.
+ *
+ * \note Calculate pointer position for N lines upper of current displaying
+ *       position within the given buffer.
+ *
+ * Used by KdbpPager().
+ * Now N lines count is hardcoded to KdbNumberOfRowsTerminal.
+ */
+PCHAR
+CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
+{
+    PCHAR p;
+    // p0 is initial guess of Page Start
+    ULONG p0len = KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal;
+    PCHAR p0 = pCurPos - p0len;
+    PCHAR prev_p = p0, p1;
+    ULONG j;
+
+    if (pCurPos < Buffer)
+        pCurPos = Buffer;
+    ASSERT(pCurPos <= Buffer + BufLength);
+
+    p = memrchr(p0, '\n', p0len);
+    if (NULL == p)
+        p = p0;
+    for (j = KdbNumberOfRowsTerminal; j--; )
+    {
+        p1 = memrchr(p0, '\n', p-p0);
+        prev_p = p;
+        p = p1;
+        if (NULL == p)
+        {
+            p = prev_p;
+            if (NULL == p)
+                p = p0;
+            break;
+        }
+        int linesCnt = (KdbNumberOfColsTerminal+prev_p-p-2) / KdbNumberOfColsTerminal;
+        if (linesCnt > 1)
+            j -= linesCnt-1;
+    }
+
+    ASSERT(p != 0);
+    ++p;
+    return p;
+}
+
+/*!\brief Prints the given string with, page by page.
+ *
+ * \param Buffer     Characters buffer to print.
+ * \param BufferLen  Buffer size.
+ *
+ * \note Doesn't correctly handle \\t and terminal escape sequences when calculating the
+ *       number of lines required to print a single line from the Buffer in the terminal.
+ *       Maximum length of buffer is limited only by memory size.
+ *
+ * Note: BufLength should be greater then (KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal).
+ *
+ */
+VOID
+KdbpPager(
+    IN PCHAR Buffer,
+    IN ULONG BufLength)
+{
+    static CHAR InBuffer[4096];
+    static BOOLEAN TerminalInitialized = FALSE;
+    static BOOLEAN TerminalConnected = FALSE;
+    static BOOLEAN TerminalReportsSize = TRUE;
+    CHAR c = '\0';
+    PCHAR p, p2;
+    ULONG Length;
+    ULONG i, j;
+    LONG RowsPrintedByTerminal;
+    ULONG ScanCode;
+
+    if( BufLength == 0)
+      return;
+
+    /* Check if the user has aborted output of the current command */
+    if (KdbOutputAborted)
+        return;
+
+    /* Initialize the terminal */
+    if (!TerminalInitialized)
+    {
+        DbgPrint("\x1b[7h");      /* Enable linewrap */
+
+        /* Query terminal type */
+        /*DbgPrint("\x1b[Z");*/
+        DbgPrint("\x05");
+
+        TerminalInitialized = TRUE;
+        Length = 0;
+        KeStallExecutionProcessor(100000);
+
+        for (;;)
+        {
+            c = KdbpTryGetCharSerial(5000);
+            if (c == -1)
+                break;
+
+            InBuffer[Length++] = c;
+            if (Length >= (sizeof (InBuffer) - 1))
+                break;
+        }
+
+        InBuffer[Length] = '\0';
+        if (Length > 0)
+            TerminalConnected = TRUE;
+    }
+
+    /* Get number of rows and columns in terminal */
+    if ((KdbNumberOfRowsTerminal < 0) || (KdbNumberOfColsTerminal < 0) ||
+        (KdbNumberOfRowsPrinted) == 0) /* Refresh terminal size each time when number of rows printed is 0 */
+    {
+        if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
+        {
+            /* Try to query number of rows from terminal. A reply looks like "\x1b[8;24;80t" */
+            TerminalReportsSize = FALSE;
+            KeStallExecutionProcessor(100000);
+            DbgPrint("\x1b[18t");
+            c = KdbpTryGetCharSerial(5000);
+
+            if (c == KEY_ESC)
+            {
+                c = KdbpTryGetCharSerial(5000);
+                if (c == '[')
+                {
+                    Length = 0;
+
+                    for (;;)
+                    {
+                        c = KdbpTryGetCharSerial(5000);
+                        if (c == -1)
+                            break;
+
+                        InBuffer[Length++] = c;
+                        if (isalpha(c) || Length >= (sizeof (InBuffer) - 1))
+                            break;
+                    }
+
+                    InBuffer[Length] = '\0';
+                    if (InBuffer[0] == '8' && InBuffer[1] == ';')
+                    {
+                        for (i = 2; (i < Length) && (InBuffer[i] != ';'); i++);
+
+                        if (Buffer[i] == ';')
+                        {
+                            Buffer[i++] = '\0';
+
+                            /* Number of rows is now at Buffer + 2 and number of cols at Buffer + i */
+                            KdbNumberOfRowsTerminal = strtoul(InBuffer + 2, NULL, 0);
+                            KdbNumberOfColsTerminal = strtoul(InBuffer + i, NULL, 0);
+                            TerminalReportsSize = TRUE;
+                        }
+                    }
+                }
+                /* Clear further characters */
+                while ((c = KdbpTryGetCharSerial(5000)) != -1);
+            }
+        }
+
+        if (KdbNumberOfRowsTerminal <= 0)
+        {
+            /* Set number of rows to the default. */
+            KdbNumberOfRowsTerminal = 24;
+        }
+        else if (KdbNumberOfColsTerminal <= 0)
+        {
+            /* Set number of cols to the default. */
+            KdbNumberOfColsTerminal = 80;
+        }
+    }
+
+    /* Get the string */
+    p = Buffer;
+
+    while (p[0] != '\0')
+    {
+        if ( p > Buffer+BufLength)
+        {
+          DbgPrint("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer+BufLength));
+          return;
+        }
+        i = strcspn(p, "\n");
+
+        // Are we out of buffer?
+        if (p + i > Buffer + BufLength)
+          // Leaving pager function:
+          break;
+
+        /* Calculate the number of lines which will be printed in the terminal
+         * when outputting the current line
+         */
+        if (i > 0)
+            RowsPrintedByTerminal = (i + KdbNumberOfColsPrinted - 1) / KdbNumberOfColsTerminal;
+        else
+            RowsPrintedByTerminal = 0;
+
+        if (p[i] == '\n')
+            RowsPrintedByTerminal++;
+
+        /*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
+
+        /* Display a prompt if we printed one screen full of text */
+        if (KdbNumberOfRowsTerminal > 0 &&
+            (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
+        {
+            if (KdbNumberOfColsPrinted > 0)
+                DbgPrint("\n");
+
+            DbgPrint("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---");
+            RowsPrintedByTerminal++;
+
+            if (KdbDebugState & KD_DEBUG_KDSERIAL)
+                c = KdbpGetCharSerial();
+            else
+                c = KdbpGetCharKeyboard(&ScanCode);
+
+            if (c == '\r')
+            {
+                /* Try to read '\n' which might follow '\r' - if \n is not received here
+                 * it will be interpreted as "return" when the next command should be read.
+                 */
+                if (KdbDebugState & KD_DEBUG_KDSERIAL)
+                    c = KdbpTryGetCharSerial(5);
+                else
+                    c = KdbpTryGetCharKeyboard(&ScanCode, 5);
+            }
+
+            //DbgPrint("\n"); //Consize version: don't show pressed key
+            DbgPrint(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key
+
+            if (c == 'q')
+            {
+                KdbOutputAborted = TRUE;
+                return;
+            }
+            if (     ScanCode == KEYSC_END || c=='e')
+            {
+              PCHAR pBufEnd = Buffer + BufLength;
+              p = CountOnePageUp(Buffer, BufLength, pBufEnd);
+              i = strcspn(p, "\n");
+            }
+            else if (ScanCode == KEYSC_PAGEUP  || c=='u')
+            {
+              p = CountOnePageUp(Buffer, BufLength, p);
+              i = strcspn(p, "\n");
+            }
+            else if (ScanCode == KEYSC_HOME || c=='h')
+            {
+              p = Buffer;
+              i = strcspn(p, "\n");
+            }
+            else if (ScanCode == KEYSC_ARROWUP)
+            {
+              p = CountOnePageUp(Buffer, BufLength, p);
+              i = strcspn(p, "\n");
+            }
+
+            KdbNumberOfRowsPrinted = 0;
+            KdbNumberOfColsPrinted = 0;
+        }
+
+        /* Insert a NUL after the line and print only the current line. */
+        if (p[i] == '\n' && p[i + 1] != '\0')
+        {
+            c = p[i + 1];
+            p[i + 1] = '\0';
+        }
+        else
+        {
+            c = '\0';
+        }
+
+        /* Remove escape sequences from the line if there's no terminal connected */
+        if (!TerminalConnected)
+        {
+            while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
+            {
+                if (p2[1] == '[')
+                {
+                    j = 2;
+                    while (!isalpha(p2[j++]));
+                    strcpy(p2, p2 + j);
+                }
+                else
+                {
+                    strcpy(p2, p2 + 1);
+                }
+            }
+        }
+
+        // The main printing of the current line:
+        DbgPrint(p);
+
+        // restore not null char with saved:
+        if (c != '\0')
+            p[i + 1] = c;
+
+        /* Set p to the start of the next line and
+         * remember the number of rows/cols printed
+         */
+        p += i;
+        if (p[0] == '\n')
+        {
+            p++;
+            KdbNumberOfColsPrinted = 0;
+        }
+        else
+        {
+            ASSERT(p[0] == '\0');
+            KdbNumberOfColsPrinted += i;
+        }
+
+        KdbNumberOfRowsPrinted += RowsPrintedByTerminal;
+    }
+}
+
 /*!\brief Appends a command to the command history
  *
  * \param Command  Pointer to the command to append to the history.