[NTOS:KDBG] Deduplicate code between KdbpPrint() and KdbpPager().

This commit is contained in:
Hermès Bélusca-Maïto 2022-11-21 16:35:47 +01:00
parent 8940614a78
commit 9337ea6a3c
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0
2 changed files with 208 additions and 351 deletions

View file

@ -98,10 +98,15 @@ KdbpCliMainLoop(
VOID
KdbpCliInterpretInitFile(VOID);
VOID
KdbpPager(
_In_ PCHAR Buffer,
_In_ ULONG BufLength);
VOID
KdbpPrint(
IN PCHAR Format,
IN ... OPTIONAL);
_In_ PSTR Format,
_In_ ...);
VOID
KdbpPrintUnicodeString(

View file

@ -2526,12 +2526,6 @@ KdbpCmdReboot(
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,
@ -2787,266 +2781,9 @@ KdbpCmdHelp(
return TRUE;
}
/*!\brief Prints the given string with printf-like formatting.
*
* \param Format Format of the string/arguments.
* \param ... Variable number of arguments matching the format specified in \a Format.
*
* \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.
* Uses KdpDPrintf internally (NOT DbgPrint!). Callers must already hold the debugger lock.
*/
VOID
KdbpPrint(
IN PCHAR Format,
IN ... OPTIONAL)
{
static CHAR Buffer[4096];
static BOOLEAN TerminalInitialized = FALSE;
static BOOLEAN TerminalConnected = FALSE;
static BOOLEAN TerminalReportsSize = TRUE;
CHAR c = '\0';
PCHAR p, p2;
ULONG Length;
SIZE_T i, j;
LONG RowsPrintedByTerminal;
ULONG ScanCode;
va_list ap;
/* Check if the user has aborted output of the current command */
if (KdbOutputAborted)
return;
/* Initialize the terminal */
if (!TerminalInitialized)
{
KdpDprintf("\x1b[7h"); /* Enable linewrap */
/* Query terminal type */
/*DbgPrint("\x1b[Z");*/
KdpDprintf("\x05");
TerminalInitialized = TRUE;
Length = 0;
KeStallExecutionProcessor(100000);
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
Buffer[Length++] = c;
if (Length >= (sizeof(Buffer) - 1))
break;
}
Buffer[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);
KdpDprintf("\x1b[18t");
c = KdbpTryGetCharSerial(5000);
if (c == KEY_ESC)
{
c = KdbpTryGetCharSerial(5000);
if (c == '[')
{
Length = 0;
for (;;)
{
c = KdbpTryGetCharSerial(5000);
if (c == -1)
break;
Buffer[Length++] = c;
if (isalpha(c) || Length >= (sizeof(Buffer) - 1))
break;
}
Buffer[Length] = '\0';
if (Buffer[0] == '8' && Buffer[1] == ';')
{
for (i = 2; (i < Length) && (Buffer[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(Buffer + 2, NULL, 0);
KdbNumberOfColsTerminal = strtoul(Buffer + i, NULL, 0);
TerminalReportsSize = TRUE;
}
}
}
/* Clear further characters */
while ((c = KdbpTryGetCharSerial(5000)) != -1);
}
}
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 23; //24; //Mna.: 23 for SCREEN debugport
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 75; //80; //Mna.: 75 for SCREEN debugport
}
}
/* Get the string */
va_start(ap, Format);
Length = _vsnprintf(Buffer, sizeof(Buffer) - 1, Format, ap);
Buffer[Length] = '\0';
va_end(ap);
p = Buffer;
while (p[0] != '\0')
{
i = strcspn(p, "\n");
/* 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)
{
KdbRepeatLastCommand = FALSE;
if (KdbNumberOfColsPrinted > 0)
KdpDprintf("\n");
KdpDprintf("--- Press q to abort, any other key to continue ---");
RowsPrintedByTerminal++; /* added by Mna. */
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);
}
KdpDprintf("\n");
if (c == 'q')
{
KdbOutputAborted = TRUE;
return;
}
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 */
{
size_t len = strlen(p2);
if (p2[1] == '[')
{
j = 2;
while (!isalpha(p2[j++]));
memmove(p2, p2 + j, len + 1 - j);
}
else
{
memmove(p2, p2 + 1, len);
}
}
}
KdpDprintf("%s", p);
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;
}
}
VOID
KdbpPrintUnicodeString(
_In_ PCUNICODE_STRING String)
{
ULONG i;
if ((String == NULL) || (String->Buffer == NULL))
{
KdbpPrint("<NULL>");
return;
}
for (i = 0; i < String->Length / sizeof(WCHAR); i++)
{
KdbpPrint("%c", (CHAR)String->Buffer[i]);
}
}
/** memrchr(), explicitly defined, since was absent in MinGW of RosBE. */
/*
* memrchr(), explicitly defined, since absent in the CRT.
* Reverse memchr()
* Find the last occurrence of 'c' in the buffer 's' of size 'n'.
*/
@ -3078,14 +2815,17 @@ memrchr(const void *s, int c, size_t n)
* Used by KdbpPager().
* Now N lines count is hardcoded to KdbNumberOfRowsTerminal.
*/
PCHAR
CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
static PCHAR
CountOnePageUp(
_In_ PCCH Buffer,
_In_ ULONG BufLength,
_In_ PCCH pCurPos)
{
PCHAR p;
PCCH p;
// p0 is initial guess of Page Start
ULONG p0len = KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal;
PCHAR p0 = pCurPos - p0len;
PCHAR prev_p = p0, p1;
PCCH p0 = pCurPos - p0len;
PCCH prev_p = p0, p1;
ULONG j;
if (pCurPos < Buffer)
@ -3093,7 +2833,7 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
ASSERT(pCurPos <= Buffer + BufLength);
p = memrchr(p0, '\n', p0len);
if (NULL == p)
if (!p)
p = p0;
for (j = KdbNumberOfRowsTerminal; j--; )
{
@ -3101,10 +2841,10 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
p1 = memrchr(p0, '\n', p-p0);
prev_p = p;
p = p1;
if (NULL == p)
if (!p)
{
p = prev_p;
if (NULL == p)
if (!p)
p = p0;
break;
}
@ -3113,9 +2853,33 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
j -= linesCnt-1;
}
ASSERT(p != 0);
ASSERT(p != NULL);
++p;
return p;
return (PCHAR)p;
}
static VOID
KdpFilterEscapes(
_Inout_ PSTR String)
{
PCHAR p;
SIZE_T i;
size_t len;
while ((p = strrchr(String, '\x1b'))) /* Look for escape character */
{
len = strlen(p);
if (p[1] == '[')
{
i = 2;
while (!isalpha(p[i++]));
memmove(p, p + i, len + 1 - i);
}
else
{
memmove(p, p + 1, len);
}
}
}
/*!\brief Prints the given string with, page by page.
@ -3126,27 +2890,28 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
* \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.
* Uses KdpDprintf internally (NOT DbgPrint!). Callers must already hold the debugger lock.
*
* Note: BufLength should be greater then (KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal).
*
*/
VOID
KdbpPager(
IN PCHAR Buffer,
IN ULONG BufLength)
KdbpPagerInternal(
_In_ PCHAR Buffer,
_In_ ULONG BufLength,
_In_ BOOLEAN DoPage)
{
static CHAR InBuffer[4096];
static CHAR InBuffer[128];
static BOOLEAN TerminalInitialized = FALSE;
static BOOLEAN TerminalConnected = FALSE;
static BOOLEAN TerminalReportsSize = TRUE;
CHAR c = '\0';
PCHAR p, p2;
ULONG Length;
SIZE_T i, j;
LONG RowsPrintedByTerminal;
CHAR c;
ULONG ScanCode;
PCHAR p;
ULONG Length;
SIZE_T i;
LONG RowsPrintedByTerminal;
if( BufLength == 0)
if (BufLength == 0)
return;
/* Check if the user has aborted output of the current command */
@ -3156,13 +2921,14 @@ KdbpPager(
/* Initialize the terminal */
if (!TerminalInitialized)
{
TerminalInitialized = TRUE;
KdpDprintf("\x1b[7h"); /* Enable linewrap */
/* Query terminal type */
/*DbgPrint("\x1b[Z");*/
KdpDprintf("\x05");
TerminalInitialized = TRUE;
Length = 0;
KeStallExecutionProcessor(100000);
@ -3184,7 +2950,8 @@ KdbpPager(
/* 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 */
/* Refresh terminal size each time when number of rows printed is 0 */
(KdbNumberOfRowsPrinted) == 0)
{
if ((KdbDebugState & KD_DEBUG_KDSERIAL) && TerminalConnected && TerminalReportsSize)
{
@ -3235,36 +3002,45 @@ KdbpPager(
if (KdbNumberOfRowsTerminal <= 0)
{
/* Set number of rows to the default. */
KdbNumberOfRowsTerminal = 24;
/* Set number of rows to the default */
if (!DoPage)
KdbNumberOfRowsTerminal = 23; //Mna.: for SCREEN debugport
else
KdbNumberOfRowsTerminal = 24;
}
else if (KdbNumberOfColsTerminal <= 0)
{
/* Set number of cols to the default. */
KdbNumberOfColsTerminal = 80;
/* Set number of cols to the default */
if (!DoPage)
KdbNumberOfColsTerminal = 75; //Mna.: for SCREEN debugport
else
KdbNumberOfColsTerminal = 80;
}
}
/* Get the string */
/* Loop through the strings */
p = Buffer;
while (p[0] != '\0')
{
if ( p > Buffer+BufLength)
if (DoPage)
{
KdpDprintf("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer+BufLength));
return;
if (p > Buffer + BufLength)
{
KdpDprintf("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;
if (DoPage)
{
/* Are we out of buffer? */
if (p + i > Buffer + BufLength)
break; // Leaving pager function
}
/* Calculate the number of lines which will be printed in the terminal
* when outputting the current line.
*/
/* 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
@ -3273,7 +3049,7 @@ KdbpPager(
if (p[i] == '\n')
RowsPrintedByTerminal++;
/*DbgPrint("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);*/
//KdpDprintf("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal);
/* Display a prompt if we printed one screen full of text */
if (KdbNumberOfRowsTerminal > 0 &&
@ -3284,7 +3060,14 @@ KdbpPager(
if (KdbNumberOfColsPrinted > 0)
KdpDprintf("\n");
KdpDprintf("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---");
if (DoPage)
{
KdpDprintf("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---");
}
else
{
KdpDprintf("--- Press q to abort, any other key to continue ---");
}
RowsPrintedByTerminal++;
if (KdbDebugState & KD_DEBUG_KDSERIAL)
@ -3303,41 +3086,52 @@ KdbpPager(
c = KdbpTryGetCharKeyboard(&ScanCode, 5);
}
//DbgPrint("\n"); //Consize version: don't show pressed key
KdpDprintf(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key
if (DoPage)
{
//KdpDprintf("\n"); // Concise version: don't show pressed key
KdpDprintf(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key
}
else
{
KdpDprintf("\n");
}
if (c == 'q')
{
KdbOutputAborted = TRUE;
return;
}
if ( ScanCode == KEYSC_END || c=='e')
if (DoPage)
{
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");
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. */
/* Insert a NUL after the line and print only the current line */
if (p[i] == '\n' && p[i + 1] != '\0')
{
c = p[i + 1];
@ -3348,35 +3142,21 @@ KdbpPager(
c = '\0';
}
/* Remove escape sequences from the line if there's no terminal connected */
/* Remove escape sequences from the line if there is no terminal connected */
// FIXME: Dangerous operation since we modify the source string!!
if (!TerminalConnected)
{
while ((p2 = strrchr(p, '\x1b'))) /* Look for escape character */
{
size_t len = strlen(p2);
if (p2[1] == '[')
{
j = 2;
while (!isalpha(p2[j++]));
memmove(p2, p2 + j, len + 1 - j);
}
else
{
memmove(p2, p2 + 1, len);
}
}
}
KdpFilterEscapes(p);
// The main printing of the current line:
KdpDprintf(p);
/* Print the current line */
// KdpDprintf(p);
KdpDprintf("%s", p);
// restore not null char with saved:
/* 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
*/
* remember the number of rows/cols printed */
p += i;
if (p[0] == '\n')
{
@ -3393,6 +3173,78 @@ KdbpPager(
}
}
/*!\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.
* Uses KdpDprintf internally (NOT DbgPrint!). Callers must already hold the debugger lock.
*
* Note: BufLength should be greater then (KdbNumberOfRowsTerminal * KdbNumberOfColsTerminal).
*/
VOID
KdbpPager(
_In_ PCHAR Buffer,
_In_ ULONG BufLength)
{
/* Call the internal function */
KdbpPagerInternal(Buffer, BufLength, TRUE);
}
/*!\brief Prints the given string with printf-like formatting.
*
* \param Format Format of the string/arguments.
* \param ... Variable number of arguments matching the format specified in \a Format.
*
* \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(
_In_ PSTR Format,
_In_ ...)
{
static CHAR Buffer[4096];
ULONG Length;
va_list ap;
/* Check if the user has aborted output of the current command */
if (KdbOutputAborted)
return;
/* Build the string */
va_start(ap, Format);
Length = _vsnprintf(Buffer, sizeof(Buffer) - 1, Format, ap);
Buffer[Length] = '\0';
va_end(ap);
/* Actually print it */
KdbpPagerInternal(Buffer, Length, FALSE);
}
VOID
KdbpPrintUnicodeString(
_In_ PCUNICODE_STRING String)
{
ULONG i;
if ((String == NULL) || (String->Buffer == NULL))
{
KdbpPrint("<NULL>");
return;
}
for (i = 0; i < String->Length / sizeof(WCHAR); i++)
{
KdbpPrint("%c", (CHAR)String->Buffer[i]);
}
}
/*!\brief Appends a command to the command history
*
* \param Command Pointer to the command to append to the history.