From e462a7a6d3c8c7ea52b3b7ed301b59f0b8807c49 Mon Sep 17 00:00:00 2001 From: Dmitry Gorbachev Date: Thu, 7 Jul 2011 19:18:16 +0000 Subject: [PATCH] [NTOSKRNL] 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 | 117 ++++++++- reactos/ntoskrnl/kdbg/kdb_cli.c | 423 +++++++++++++++++++++++++++++++- 2 files changed, 535 insertions(+), 5 deletions(-) diff --git a/reactos/ntoskrnl/kd/kdio.c b/reactos/ntoskrnl/kd/kdio.c index 769b5650ea8..c51a1b5cda8 100644 --- a/reactos/ntoskrnl/kd/kdio.c +++ b/reactos/ntoskrnl/kd/kdio.c @@ -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"); diff --git a/reactos/ntoskrnl/kdbg/kdb_cli.c b/reactos/ntoskrnl/kdbg/kdb_cli.c index 08c5f102981..3a0b98cf05f 100644 --- a/reactos/ntoskrnl/kdbg/kdb_cli.c +++ b/reactos/ntoskrnl/kdbg/kdb_cli.c @@ -42,6 +42,13 @@ #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.