From f620ce7705225f7ece92427db09a1659758f3c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Wed, 29 Mar 2023 19:38:10 +0200 Subject: [PATCH] [NTOS:KDBG] Introduce local versions of KdpPrintString, KdpDprintf and KdpPromptString... ... since the original ones are internal to the kernel and won't be available once KDBG is moved out of it. Use these functions in the pager/prompt support. --- ntoskrnl/kd/kdio.c | 7 +- ntoskrnl/kdbg/kdb.h | 33 +++++++ ntoskrnl/kdbg/kdb_cli.c | 65 +++++++------ ntoskrnl/kdbg/kdb_print.c | 191 ++++++++++++++++++++++++++++++++++++++ ntoskrnl/ntos.cmake | 1 + 5 files changed, 259 insertions(+), 38 deletions(-) create mode 100644 ntoskrnl/kdbg/kdb_print.c diff --git a/ntoskrnl/kd/kdio.c b/ntoskrnl/kd/kdio.c index 3e94c1f0ac6..82a1ade1ed7 100644 --- a/ntoskrnl/kd/kdio.c +++ b/ntoskrnl/kd/kdio.c @@ -597,7 +597,7 @@ KdIoPrintf( } -extern STRING KdbPromptString; +extern const CSTRING KdbPromptStr; VOID NTAPI @@ -706,7 +706,6 @@ KdReceivePacket( _Inout_ PKD_CONTEXT Context) { #ifdef KDBG - STRING NewLine = RTL_CONSTANT_STRING("\n"); STRING ResponseString; PDBGKD_DEBUG_IO DebugIo; CHAR MessageBuffer[512]; @@ -768,8 +767,8 @@ KdReceivePacket( /* The prompt string has been printed by KdSendPacket; go to * new line and print the kdb prompt -- for SYSREG2 support. */ - KdpPrintString(&NewLine); - KdpPrintString(&KdbPromptString); // Alternatively, use "Input> " + KdIoPrintString("\n", 1); + KdIoPuts(KdbPromptStr.Buffer); // Alternatively, use "Input> " if (!(KdbDebugState & KD_DEBUG_KDSERIAL)) KbdDisableMouse(); diff --git a/ntoskrnl/kdbg/kdb.h b/ntoskrnl/kdbg/kdb.h index 4e2c04e7e3c..2170be54f9f 100644 --- a/ntoskrnl/kdbg/kdb.h +++ b/ntoskrnl/kdbg/kdb.h @@ -287,3 +287,36 @@ KbdDisableMouse(VOID); VOID KbdEnableMouse(VOID); + + +/* From kdb_print.c */ + +VOID +KdbPrintString( + _In_ const CSTRING* Output); + +USHORT +KdbPromptString( + _In_ const CSTRING* PromptString, + _Inout_ PSTRING ResponseString); + +VOID +KdbPutsN( + _In_ PCCH String, + _In_ USHORT Length); + +VOID +KdbPuts( + _In_ PCSTR String); + +VOID +__cdecl +KdbPrintf( + _In_ PCSTR Format, + ...); + +SIZE_T +KdbPrompt( + _In_ PCSTR Prompt, + _Out_ PCHAR Buffer, + _In_ SIZE_T Size); diff --git a/ntoskrnl/kdbg/kdb_cli.c b/ntoskrnl/kdbg/kdb_cli.c index 4dde1487a08..7ad4cc275ee 100644 --- a/ntoskrnl/kdbg/kdb_cli.c +++ b/ntoskrnl/kdbg/kdb_cli.c @@ -145,7 +145,7 @@ static volatile ULONG KdbDmesgTotalWritten = 0; static volatile BOOLEAN KdbpIsInDmesgMode = FALSE; static KSPIN_LOCK KdpDmesgLogSpinLock; -STRING KdbPromptString = RTL_CONSTANT_STRING("kdb:> "); +const CSTRING KdbPromptStr = RTL_CONSTANT_STRING("kdb:> "); // // Debug Filter Component Table @@ -491,7 +491,7 @@ KdbpCmdEvalExpression( } /* Evaluate the expression */ - Ok = KdbpEvaluateExpression(Argv[1], KdbPromptString.Length + (Argv[1]-Argv[0]), &Result); + Ok = KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result); if (Ok) { if (Result > 0x00000000ffffffffLL) @@ -849,7 +849,7 @@ KdbpCmdDisassembleX( /* Evaluate the expression */ if (Argc > 1) { - if (!KdbpEvaluateExpression(Argv[1], KdbPromptString.Length + (Argv[1]-Argv[0]), &Result)) + if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result)) return TRUE; if (Result > (ULONGLONG)(~((ULONG_PTR)0))) @@ -1271,7 +1271,7 @@ KdbpCmdBackTrace( Argv[1]++; /* Evaluate the expression */ - if (!KdbpEvaluateExpression(Argv[1], KdbPromptString.Length + (Argv[1]-Argv[0]), &Result)) + if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result)) return TRUE; if (Result > (ULONGLONG)(~((ULONG_PTR)0))) @@ -1680,7 +1680,7 @@ KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[]) /* Evaluate the address expression */ if (!KdbpEvaluateExpression(Argv[AddressArgIndex], - KdbPromptString.Length + (Argv[AddressArgIndex]-Argv[0]), + KdbPromptStr.Length + (Argv[AddressArgIndex]-Argv[0]), &Result)) { return TRUE; @@ -2046,7 +2046,7 @@ KdbpCmdMod( Argv[Argc][strlen(Argv[Argc])] = ' '; /* Evaluate the expression */ - if (!KdbpEvaluateExpression(Argv[1], KdbPromptString.Length + (Argv[1]-Argv[0]), &Result)) + if (!KdbpEvaluateExpression(Argv[1], KdbPromptStr.Length + (Argv[1]-Argv[0]), &Result)) { return TRUE; } @@ -2880,7 +2880,7 @@ KdpFilterEscapes( * \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. + * Uses KdbPrintf internally. * * Note: BufLength should be greater than (KdTermSize.cx * KdTermSize.cy). */ @@ -2925,7 +2925,7 @@ KdbpPagerInternal( { if (p > Buffer + BufLength) { - KdpDprintf("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer + BufLength)); + KdbPrintf("Dmesg: error, p > Buffer+BufLength,d=%d", p - (Buffer + BufLength)); return; } } @@ -2948,39 +2948,33 @@ KdbpPagerInternal( if (p[i] == '\n') RowsPrintedByTerminal++; - //KdpDprintf("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal); + //KdbPrintf("!%d!%d!%d!%d!", KdbNumberOfRowsPrinted, KdbNumberOfColsPrinted, i, RowsPrintedByTerminal); /* Display a prompt if we printed one screen full of text */ if (KdTermSize.cy > 0 && (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdTermSize.cy) { + PCSTR Prompt; + /* Disable the repetition of previous command with long many-page output */ KdbRepeatLastCommand = FALSE; if (KdbNumberOfColsPrinted > 0) - KdpDprintf("\n"); + KdbPuts("\n"); if (DoPage) - { - KdpDprintf("--- Press q to abort, e/End,h/Home,u/PgUp, other key/PgDn ---"); - } + Prompt = "--- 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++; + Prompt = "--- Press q to abort, any other key to continue ---"; + KdbPuts(Prompt); c = KdpReadTermKey(&ScanCode); - - if (DoPage) - { - //KdpDprintf("\n"); // Concise version: don't show pressed key - KdpDprintf(" '%c'/scan=%04x\n", c, ScanCode); // Shows pressed key - } + if (DoPage) // Show pressed key + KdbPrintf(" '%c'/scan=%04x\n", c, ScanCode); else - { - KdpDprintf("\n"); - } + KdbPuts("\n"); + + RowsPrintedByTerminal++; if (c == 'q') { @@ -3030,7 +3024,7 @@ KdbpPagerInternal( KdpFilterEscapes(p); /* Print the current line */ - KdpDprintf("%s", p); + KdbPuts(p); /* Restore not null char with saved */ if (c != '\0') @@ -3062,7 +3056,7 @@ KdbpPagerInternal( * \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. + * Uses KdbPrintf internally. * * Note: BufLength should be greater than (KdTermSize.cx * KdTermSize.cy). */ @@ -3300,15 +3294,18 @@ KdbpCliMainLoop( /* Reset the number of rows/cols printed */ KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0; - /* Print the prompt */ - KdpDprintf(KdbPromptString.Buffer); - /* - * Read a command. Repeat the last one if the user pressed Enter. + * Print the prompt and read a command. + * Repeat the last one if the user pressed Enter. * This reduces the risk of RSI when single-stepping! */ - CmdLen = KdIoReadLine(Command, sizeof(Command)); - if (CmdLen > 0) // i.e. (*Command != ANSI_NULL) + CmdLen = KdbPrompt(KdbPromptStr.Buffer, Command, sizeof(Command)); + if (CmdLen == 0) + { + /* Nothing received but the user didn't press Enter, retry */ + continue; + } + else if (CmdLen > 1) // i.e. (*Command != ANSI_NULL) { /* Save this new last command */ KdbRepeatLastCommand = TRUE; diff --git a/ntoskrnl/kdbg/kdb_print.c b/ntoskrnl/kdbg/kdb_print.c new file mode 100644 index 00000000000..257b37e1225 --- /dev/null +++ b/ntoskrnl/kdbg/kdb_print.c @@ -0,0 +1,191 @@ +/* + * PROJECT: ReactOS KDBG Kernel Debugger + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: IO interface for KDBG. Provides local KDBG versions + * of KdpPrintString, KdpPromptString and KdpDprintf. + * COPYRIGHT: Copyright 2023 Hermès Bélusca-Maïto + */ + +/* INCLUDES ******************************************************************/ + +#include +#include "kdb.h" + +/* FUNCTIONS *****************************************************************/ + +static VOID +KdbPrintStringWorker( + _In_ const CSTRING* Output, + _In_ ULONG ApiNumber, + _Inout_ PDBGKD_DEBUG_IO DebugIo, + _Inout_ PSTRING Header, + _Inout_ PSTRING Data) +{ + USHORT Length; + C_ASSERT(PACKET_MAX_SIZE >= sizeof(*DebugIo)); + + ASSERT((ApiNumber == DbgKdPrintStringApi) || + (ApiNumber == DbgKdGetStringApi)); + + /* Make sure we don't exceed the KD Packet size */ + Length = min(Output->Length, PACKET_MAX_SIZE - sizeof(*DebugIo)); + + /* Build the packet header */ + DebugIo->ApiNumber = ApiNumber; + DebugIo->ProcessorLevel = 0; // (USHORT)KeProcessorLevel; + DebugIo->Processor = KeGetCurrentPrcb()->Number; + + if (ApiNumber == DbgKdPrintStringApi) + DebugIo->u.PrintString.LengthOfString = Length; + else // if (ApiNumber == DbgKdGetStringApi) + DebugIo->u.GetString.LengthOfPromptString = Length; + + Header->Length = sizeof(*DebugIo); + Header->Buffer = (PCHAR)DebugIo; + + /* Build the data */ + Data->Length = Length; + Data->Buffer = (PCHAR)Output->Buffer; + + /* Send the packet */ + KdSendPacket(PACKET_TYPE_KD_DEBUG_IO, Header, Data, &KdpContext); +} + +VOID +KdbPrintString( + _In_ const CSTRING* Output) +{ + DBGKD_DEBUG_IO DebugIo; + STRING Header, Data; + + KdbPrintStringWorker(Output, DbgKdPrintStringApi, + &DebugIo, &Header, &Data); +} + +static BOOLEAN +KdbPromptStringWorker( + _In_ const CSTRING* PromptString, + _Inout_ PSTRING ResponseString) +{ + DBGKD_DEBUG_IO DebugIo; + STRING Header, Data; + ULONG Length; + KDSTATUS Status; + + /* Print the prompt */ + // DebugIo.u.GetString.LengthOfPromptString = Length; + DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength; + KdbPrintStringWorker(PromptString, DbgKdGetStringApi, + &DebugIo, &Header, &Data); + + /* Set the maximum lengths for the receive */ + Header.MaximumLength = sizeof(DebugIo); + Data.MaximumLength = ResponseString->MaximumLength; + /* Build the data */ + Data.Buffer = ResponseString->Buffer; + + /* Enter receive loop */ + do + { + /* Get our reply */ + Status = KdReceivePacket(PACKET_TYPE_KD_DEBUG_IO, + &Header, + &Data, + &Length, + &KdpContext); + + /* Return TRUE if we need to resend */ + if (Status == KdPacketNeedsResend) + return TRUE; + + /* Loop until we succeed */ + } while (Status != KdPacketReceived); + + /* Don't copy back a larger response than there is room for */ + Length = min(Length, ResponseString->MaximumLength); + + /* We've got the string back; return the length */ + ResponseString->Length = (USHORT)Length; + + /* Success; we don't need to resend */ + return FALSE; +} + +USHORT +KdbPromptString( + _In_ const CSTRING* PromptString, + _Inout_ PSTRING ResponseString) +{ + /* Enter prompt loop: send the prompt and receive the response */ + ResponseString->Length = 0; + while (KdbPromptStringWorker(PromptString, ResponseString)) + { + /* Loop while we need to resend */ + } + return ResponseString->Length; +} + + +VOID +KdbPutsN( + _In_ PCCH String, + _In_ USHORT Length) +{ + CSTRING Output; + + Output.Buffer = String; + Output.Length = Output.MaximumLength = Length; + KdbPrintString(&Output); +} + +VOID +KdbPuts( + _In_ PCSTR String) +{ + KdbPutsN(String, (USHORT)strnlen(String, MAXUSHORT - sizeof(ANSI_NULL))); +} + +VOID +__cdecl +KdbPrintf( + _In_ PCSTR Format, + ...) +{ + va_list ap; + SIZE_T Length; + CHAR Buffer[1024]; + + /* Format the string */ + va_start(ap, Format); + Length = _vsnprintf(Buffer, + sizeof(Buffer), + Format, + ap); + Length = min(Length, MAXUSHORT - sizeof(ANSI_NULL)); + va_end(ap); + + /* Send it to the debugger directly */ + KdbPutsN(Buffer, (USHORT)Length); +} + +SIZE_T +KdbPrompt( + _In_ PCSTR Prompt, + _Out_ PCHAR Buffer, + _In_ SIZE_T Size) +{ + CSTRING PromptString; + STRING ResponseBuffer; + + PromptString.Buffer = Prompt; + PromptString.Length = PromptString.MaximumLength = + (USHORT)strnlen(Prompt, MAXUSHORT - sizeof(ANSI_NULL)); + + ResponseBuffer.Buffer = Buffer; + ResponseBuffer.Length = 0; + ResponseBuffer.MaximumLength = (USHORT)min(Size, MAXUSHORT); + + return KdbPromptString(&PromptString, &ResponseBuffer); +} + +/* EOF */ diff --git a/ntoskrnl/ntos.cmake b/ntoskrnl/ntos.cmake index 8f3b6c8abbf..60d4436a438 100644 --- a/ntoskrnl/ntos.cmake +++ b/ntoskrnl/ntos.cmake @@ -407,6 +407,7 @@ if(NOT _WINKD_) ${REACTOS_SOURCE_DIR}/ntoskrnl/kdbg/kdb_cli.c ${REACTOS_SOURCE_DIR}/ntoskrnl/kdbg/kdb_cmdhist.c ${REACTOS_SOURCE_DIR}/ntoskrnl/kdbg/kdb_expr.c + ${REACTOS_SOURCE_DIR}/ntoskrnl/kdbg/kdb_print.c ${REACTOS_SOURCE_DIR}/ntoskrnl/kdbg/kdb_symbols.c) endif()