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()