#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" : \
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[]);
+
+BOOLEAN ExpKdbgExtPool(ULONG Argc, PCHAR Argv[]);
+
+#ifdef __ROS_DWARF__
+static BOOLEAN KdbpCmdPrintStruct(ULONG Argc, PCHAR Argv[]);
+#endif
/* GLOBALS *******************************************************************/
+static PKDBG_CLI_ROUTINE KdbCliCallbacks[10];
static BOOLEAN KdbUseIntelSyntax = FALSE; /* Set to TRUE for intel syntax */
static BOOLEAN KdbBreakOnModuleLoad = FALSE; /* Set to TRUE to break into KDB when a module is loaded */
static ULONG KdbNumberOfRowsPrinted = 0;
static ULONG KdbNumberOfColsPrinted = 0;
static BOOLEAN KdbOutputAborted = FALSE;
+static BOOLEAN KdbRepeatLastCommand = FALSE;
static LONG KdbNumberOfRowsTerminal = -1;
static LONG KdbNumberOfColsTerminal = -1;
PCHAR KdbInitFileBuffer = NULL; /* Buffer where KDBinit file is loaded into during initialization */
BOOLEAN KdbpBugCheckRequested = FALSE;
+/* Vars for dmesg */
+/* defined in ../kd/kdio.c, declare here: */
+extern volatile BOOLEAN KdbpIsInDmesgMode;
+extern const ULONG KdpDmesgBufferSize;
+extern PCHAR KdpDmesgBuffer;
+extern volatile ULONG KdpDmesgCurrentPosition;
+extern volatile ULONG KdpDmesgFreeBytes;
+extern volatile ULONG KdbDmesgTotalWritten;
+
static const struct
{
PCHAR Name;
{ "sregs", "sregs", "Display status registers.", KdbpCmdRegs },
{ "dregs", "dregs", "Display debug registers.", KdbpCmdRegs },
{ "bt", "bt [*frameaddr|thread id]", "Prints current backtrace or from given frame addr", KdbpCmdBackTrace },
+#ifdef __ROS_DWARF__
+ { "dt", "dt [mod] [type] [addr]", "Print a struct. Addr is optional.", KdbpCmdPrintStruct },
+#endif
/* Flow control */
{ NULL, NULL, "Flow control", NULL },
{ "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 },
- { "help", "help", "Display help screen.", KdbpCmdHelp }
+ { "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 },
+ { "!pool", "!pool [Address [Flags]]", "Display information about pool allocations.", ExpKdbgExtPool }
};
/* FUNCTIONS *****************************************************************/
return Ok;
}
+BOOLEAN
+NTAPI
+KdbpGetHexNumber(
+ IN PCHAR pszNum,
+ OUT ULONG_PTR *pulValue)
+{
+ char *endptr;
+
+ /* Skip optional '0x' prefix */
+ if ((pszNum[0] == '0') && ((pszNum[1] == 'x') || (pszNum[1] == 'X')))
+ pszNum += 2;
+
+ /* Make a number from the string (hex) */
+ *pulValue = strtoul(pszNum, &endptr, 16);
+
+ return (*endptr == '\0');
+}
+
/*!\brief Evaluates an expression and displays the result.
*/
static BOOLEAN
return TRUE;
}
+#ifdef __ROS_DWARF__
+
+/*!\brief Print a struct
+ */
+static VOID
+KdbpPrintStructInternal
+(PROSSYM_INFO Info,
+ PCHAR Indent,
+ BOOLEAN DoRead,
+ PVOID BaseAddress,
+ PROSSYM_AGGREGATE Aggregate)
+{
+ ULONG i;
+ ULONGLONG Result;
+ PROSSYM_AGGREGATE_MEMBER Member;
+ ULONG IndentLen = strlen(Indent);
+ ROSSYM_AGGREGATE MemberAggregate = {0 };
+
+ for (i = 0; i < Aggregate->NumElements; i++) {
+ Member = &Aggregate->Elements[i];
+ KdbpPrint("%s%p+%x: %s", Indent, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size, Member->Name ? Member->Name : "<anoymous>");
+ if (DoRead) {
+ if (!strcmp(Member->Type, "_UNICODE_STRING")) {
+ KdbpPrint("\"%wZ\"\n", ((PCHAR)BaseAddress) + Member->BaseOffset);
+ continue;
+ } else if (!strcmp(Member->Type, "PUNICODE_STRING")) {
+ KdbpPrint("\"%wZ\"\n", *(((PUNICODE_STRING*)((PCHAR)BaseAddress) + Member->BaseOffset)));
+ continue;
+ }
+ switch (Member->Size) {
+ case 1:
+ case 2:
+ case 4:
+ case 8: {
+ Result = 0;
+ if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
+ if (Member->Bits) {
+ Result >>= Member->FirstBit;
+ Result &= ((1 << Member->Bits) - 1);
+ }
+ KdbpPrint(" %lx\n", Result);
+ }
+ else goto readfail;
+ break;
+ }
+ default: {
+ if (Member->Size < 8) {
+ if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
+ ULONG j;
+ for (j = 0; j < Member->Size; j++) {
+ KdbpPrint(" %02x", (int)(Result & 0xff));
+ Result >>= 8;
+ }
+ } else goto readfail;
+ } else {
+ KdbpPrint(" %s @ %p {\n", Member->Type, ((PCHAR)BaseAddress) + Member->BaseOffset);
+ Indent[IndentLen] = ' ';
+ if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
+ KdbpPrintStructInternal(Info, Indent, DoRead, ((PCHAR)BaseAddress) + Member->BaseOffset, &MemberAggregate);
+ RosSymFreeAggregate(&MemberAggregate);
+ }
+ Indent[IndentLen] = 0;
+ KdbpPrint("%s}\n", Indent);
+ } break;
+ }
+ }
+ } else {
+ readfail:
+ if (Member->Size <= 8) {
+ KdbpPrint(" ??\n");
+ } else {
+ KdbpPrint(" %s @ %x {\n", Member->Type, Member->BaseOffset);
+ Indent[IndentLen] = ' ';
+ if (RosSymAggregate(Info, Member->Type, &MemberAggregate)) {
+ KdbpPrintStructInternal(Info, Indent, DoRead, BaseAddress, &MemberAggregate);
+ RosSymFreeAggregate(&MemberAggregate);
+ }
+ Indent[IndentLen] = 0;
+ KdbpPrint("%s}\n", Indent);
+ }
+ }
+ }
+}
+
+PROSSYM_INFO KdbpSymFindCachedFile(PUNICODE_STRING ModName);
+
+static BOOLEAN
+KdbpCmdPrintStruct(
+ ULONG Argc,
+ PCHAR Argv[])
+{
+ ULONG i;
+ ULONGLONG Result = 0;
+ PVOID BaseAddress = 0;
+ ROSSYM_AGGREGATE Aggregate = {0};
+ UNICODE_STRING ModName = {0};
+ ANSI_STRING AnsiName = {0};
+ CHAR Indent[100] = {0};
+ PROSSYM_INFO Info;
+
+ if (Argc < 3) goto end;
+ AnsiName.Length = AnsiName.MaximumLength = strlen(Argv[1]);
+ AnsiName.Buffer = Argv[1];
+ RtlAnsiStringToUnicodeString(&ModName, &AnsiName, TRUE);
+ Info = KdbpSymFindCachedFile(&ModName);
+
+ if (!Info || !RosSymAggregate(Info, Argv[2], &Aggregate)) {
+ DPRINT1("Could not get aggregate\n");
+ goto end;
+ }
+
+ // Get an argument for location if it was given
+ if (Argc > 3) {
+ ULONG len;
+ PCHAR ArgStart = Argv[3];
+ DPRINT1("Trying to get expression\n");
+ for (i = 3; i < Argc - 1; i++)
+ {
+ len = strlen(Argv[i]);
+ Argv[i][len] = ' ';
+ }
+
+ /* Evaluate the expression */
+ DPRINT1("Arg: %s\n", ArgStart);
+ if (KdbpEvaluateExpression(ArgStart, strlen(ArgStart), &Result)) {
+ BaseAddress = (PVOID)(ULONG_PTR)Result;
+ DPRINT1("BaseAddress: %p\n", BaseAddress);
+ }
+ }
+ DPRINT1("BaseAddress %p\n", BaseAddress);
+ KdbpPrintStructInternal(Info, Indent, !!BaseAddress, BaseAddress, &Aggregate);
+end:
+ RosSymFreeAggregate(&Aggregate);
+ RtlFreeUnicodeString(&ModName);
+ return TRUE;
+}
+#endif
+
/*!\brief Display list of active debug channels
*/
static BOOLEAN
while (Count > 0)
{
- if (!KdbSymPrintAddress((PVOID)Address))
+ if (!KdbSymPrintAddress((PVOID)Address, NULL))
KdbpPrint("<%x>:", Address);
else
KdbpPrint(":");
/* Disassemble */
while (Count-- > 0)
{
- if (!KdbSymPrintAddress((PVOID)Address))
+ if (!KdbSymPrintAddress((PVOID)Address, NULL))
KdbpPrint("<%08x>: ", Address);
else
KdbpPrint(": ");
else if (Argv[0][0] == 'c') /* cregs */
{
ULONG Cr0, Cr2, Cr3, Cr4;
- KDESCRIPTOR Gdtr, Idtr;
+ KDESCRIPTOR Gdtr = {0, 0, 0}, Idtr = {0, 0, 0};
USHORT Ldtr;
static const PCHAR Cr0Bits[32] = { " PE", " MP", " EM", " TS", " ET", " NE", NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
return TRUE;
}
+static BOOLEAN
+KdbpTrapFrameFromPrevTss(
+ PKTRAP_FRAME TrapFrame)
+{
+ ULONG_PTR Eip, Ebp;
+ KDESCRIPTOR Gdtr;
+ KGDTENTRY Desc;
+ USHORT Sel;
+ PKTSS Tss;
+
+ Ke386GetGlobalDescriptorTable(&Gdtr.Limit);
+ Sel = Ke386GetTr();
+
+ if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
+ (Sel < sizeof(KGDTENTRY)) ||
+ (Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
+ return FALSE;
+
+ if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
+ (PVOID)(Gdtr.Base + Sel),
+ sizeof(KGDTENTRY))))
+ return FALSE;
+
+ if (Desc.HighWord.Bits.Type != 0xB)
+ return FALSE;
+
+ Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
+ Desc.HighWord.Bytes.BaseMid << 16 |
+ Desc.HighWord.Bytes.BaseHi << 24);
+
+ if (!NT_SUCCESS(KdbpSafeReadMemory(&Sel,
+ (PVOID)&Tss->Backlink,
+ sizeof(USHORT))))
+ return FALSE;
+
+ if ((Sel & (sizeof(KGDTENTRY) - 1)) ||
+ (Sel < sizeof(KGDTENTRY)) ||
+ (Sel + sizeof(KGDTENTRY) - 1 > Gdtr.Limit))
+ return FALSE;
+
+ if (!NT_SUCCESS(KdbpSafeReadMemory(&Desc,
+ (PVOID)(Gdtr.Base + Sel),
+ sizeof(KGDTENTRY))))
+ return FALSE;
+
+ if (Desc.HighWord.Bits.Type != 0xB)
+ return FALSE;
+
+ Tss = (PKTSS)(ULONG_PTR)(Desc.BaseLow |
+ Desc.HighWord.Bytes.BaseMid << 16 |
+ Desc.HighWord.Bytes.BaseHi << 24);
+
+ if (!NT_SUCCESS(KdbpSafeReadMemory(&Eip,
+ (PVOID)&Tss->Eip,
+ sizeof(ULONG_PTR))))
+ return FALSE;
+
+ if (!NT_SUCCESS(KdbpSafeReadMemory(&Ebp,
+ (PVOID)&Tss->Ebp,
+ sizeof(ULONG_PTR))))
+ return FALSE;
+
+ TrapFrame->Eip = Eip;
+ TrapFrame->Ebp = Ebp;
+ return TRUE;
+}
+
+VOID __cdecl KiTrap02(VOID);
+VOID FASTCALL KiTrap03Handler(IN PKTRAP_FRAME);
+VOID __cdecl KiTrap08(VOID);
+VOID __cdecl KiTrap09(VOID);
+
+static BOOLEAN
+KdbpInNmiOrDoubleFaultHandler(
+ ULONG_PTR Address)
+{
+ return (Address > (ULONG_PTR)KiTrap02 && Address < (ULONG_PTR)KiTrap03Handler) ||
+ (Address > (ULONG_PTR)KiTrap08 && Address < (ULONG_PTR)KiTrap09);
+}
+
/*!\brief Displays a backtrace.
*/
static BOOLEAN
ULONGLONG Result = 0;
ULONG_PTR Frame = KdbCurrentTrapFrame->Tf.Ebp;
ULONG_PTR Address;
+ KTRAP_FRAME TrapFrame;
if (Argc >= 2)
{
KdbpPrint("Eip:\n");
/* Try printing the function at EIP */
- if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
+ if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip, &KdbCurrentTrapFrame->Tf))
KdbpPrint("<%08x>\n", KdbCurrentTrapFrame->Tf.Eip);
else
KdbpPrint("\n");
}
+ TrapFrame = KdbCurrentTrapFrame->Tf;
KdbpPrint("Frames:\n");
+
for (;;)
{
+ BOOLEAN GotNextFrame;
+
if (Frame == 0)
break;
break;
}
+ if ((GotNextFrame = NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR)))))
+ TrapFrame.Ebp = Frame;
+
/* Print the location of the call instruction */
- if (!KdbSymPrintAddress((PVOID)(Address - 5)))
+ if (!KdbSymPrintAddress((PVOID)(Address - 5), &TrapFrame))
KdbpPrint("<%08x>\n", Address);
else
KdbpPrint("\n");
+ if (KdbOutputAborted) break;
+
if (Address == 0)
break;
- if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame, (PVOID)Frame, sizeof (ULONG_PTR))))
+ if (KdbpInNmiOrDoubleFaultHandler(Address))
+ {
+ if ((GotNextFrame = KdbpTrapFrameFromPrevTss(&TrapFrame)))
+ {
+ Address = TrapFrame.Eip;
+ Frame = TrapFrame.Ebp;
+
+ if (!KdbSymPrintAddress((PVOID)Address, &TrapFrame))
+ KdbpPrint("<%08x>\n", Address);
+ else
+ KdbpPrint("\n");
+ }
+ }
+
+ if (!GotNextFrame)
{
KdbpPrint("Couldn't access memory at 0x%p!\n", Frame);
break;
if (strcmp(Argv[i+1], "IF") == 0) /* IF found */
{
ConditionArgIndex = i + 2;
- if (ConditionArgIndex >= Argc)
+ if ((ULONG)ConditionArgIndex >= Argc)
{
KdbpPrint("%s: IF requires condition expression.\n", Argv[0]);
return TRUE;
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
*
* \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(
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
}
}
if (KdbNumberOfRowsTerminal > 0 &&
(LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
{
+ KdbRepeatLastCommand = FALSE;
+
if (KdbNumberOfColsPrinted > 0)
DbgPrint("\n");
DbgPrint("--- Press q to abort, any other key to continue ---");
+ RowsPrintedByTerminal++; /* added by Mna. */
if (KdbDebugState & KD_DEBUG_KDSERIAL)
c = KdbpGetCharSerial();
}
}
+/** 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--; )
+ {
+ int linesCnt;
+ p1 = memrchr(p0, '\n', p-p0);
+ prev_p = p;
+ p = p1;
+ if (NULL == p)
+ {
+ p = prev_p;
+ if (NULL == p)
+ p = p0;
+ break;
+ }
+ 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)
+ {
+ KdbRepeatLastCommand = FALSE;
+
+ 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.
PCHAR Orig = Buffer;
ULONG ScanCode = 0;
BOOLEAN EchoOn;
- static CHAR LastCommand[1024] = "";
+ static CHAR LastCommand[1024];
static CHAR NextKey = '\0';
INT CmdHistIndex = -1;
INT i;
* Repeat the last command if the user presses enter. Reduces the
* risk of RSI when single-stepping.
*/
- if (Buffer == Orig)
+ if (Buffer != Orig)
{
- strncpy(Buffer, LastCommand, Size);
- Buffer[Size - 1] = '\0';
+ KdbRepeatLastCommand = TRUE;
+ *Buffer = '\0';
+ RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Orig);
}
+ else if (KdbRepeatLastCommand)
+ RtlStringCbCopyA(Buffer, Size, LastCommand);
else
- {
*Buffer = '\0';
- strncpy(LastCommand, Orig, sizeof (LastCommand));
- LastCommand[sizeof (LastCommand) - 1] = '\0';
- }
return;
}
}
}
+
+BOOLEAN
+NTAPI
+KdbRegisterCliCallback(
+ PVOID Callback,
+ BOOLEAN Deregister)
+{
+ ULONG i;
+
+ /* Loop all entries */
+ for (i = 0; i < _countof(KdbCliCallbacks); i++)
+ {
+ /* Check if deregistering was requested */
+ if (Deregister)
+ {
+ /* Check if this entry is the one that was registered */
+ if (KdbCliCallbacks[i] == Callback)
+ {
+ /* Delete it and report success */
+ KdbCliCallbacks[i] = NULL;
+ return TRUE;
+ }
+ }
+ else
+ {
+ /* Check if this entry is free */
+ if (KdbCliCallbacks[i] == NULL)
+ {
+ /* Set it and and report success */
+ KdbCliCallbacks[i] = Callback;
+ return TRUE;
+ }
+ }
+ }
+
+ /* Unsuccessful */
+ return FALSE;
+}
+
+/*! \brief Invokes registered CLI callbacks until one of them handled the
+ * Command.
+ *
+ * \param Command - Command line to parse and execute if possible.
+ * \param Argc - Number of arguments in Argv
+ * \param Argv - Array of strings, each of them containing one argument.
+ *
+ * \return TRUE, if the command was handled, FALSE if it was not handled.
+ */
+static
+BOOLEAN
+KdbpInvokeCliCallbacks(
+ IN PCHAR Command,
+ IN ULONG Argc,
+ IN PCH Argv[])
+{
+ ULONG i;
+
+ /* Loop all entries */
+ for (i = 0; i < _countof(KdbCliCallbacks); i++)
+ {
+ /* Check if this entry is registered */
+ if (KdbCliCallbacks[i])
+ {
+ /* Invoke the callback and check if it handled the command */
+ if (KdbCliCallbacks[i](Command, Argc, Argv))
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ /* None of the callbacks handled the command */
+ return FALSE;
+}
+
+
/*!\brief Parses command line and executes command if found
*
* \param Command Command line to parse and execute if possible.
ULONG i;
PCHAR p;
ULONG Argc;
+ // FIXME: for what do we need a 1024 characters command line and 256 tokens?
static PCH Argv[256];
static CHAR OrigCommand[1024];
- strncpy(OrigCommand, Command, sizeof(OrigCommand) - 1);
- OrigCommand[sizeof(OrigCommand) - 1] = '\0';
+ RtlStringCbCopyA(OrigCommand, sizeof(OrigCommand), Command);
Argc = 0;
p = Command;
}
}
+ /* Now invoke the registered callbacks */
+ if (KdbpInvokeCliCallbacks(Command, Argc, Argv))
+ {
+ return TRUE;
+ }
+
KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
return TRUE;
}
if (EnteredOnSingleStep)
{
- if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip))
+ if (!KdbSymPrintAddress((PVOID)KdbCurrentTrapFrame->Tf.Eip, &KdbCurrentTrapFrame->Tf))
{
KdbpPrint("<%x>", KdbCurrentTrapFrame->Tf.Eip);
}
/* Main loop */
do
{
+ /* Reset the number of rows/cols printed */
+ KdbNumberOfRowsPrinted = KdbNumberOfColsPrinted = 0;
+
/* Print the prompt */
KdbpPrint("kdb:> ");
/* Call the command */
Continue = KdbpDoCommand(Command);
+ KdbOutputAborted = FALSE;
}
while (Continue);
}
InitializeObjectAttributes(&ObjectAttributes, &FileName, 0, NULL, NULL);
/* Open the file */
- Status = ZwOpenFile(&hFile, FILE_READ_DATA, &ObjectAttributes, &Iosb, 0,
+ Status = ZwOpenFile(&hFile, FILE_READ_DATA | SYNCHRONIZE,
+ &ObjectAttributes, &Iosb, 0,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
FILE_NO_INTERMEDIATE_BUFFERING);
if (!NT_SUCCESS(Status))
*(KdpPromptString.Buffer + i));
}
+ if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
+ KbdDisableMouse();
+
/* Loop the whole string */
for (i = 0; i < OutStringLength; i++)
{
KdbpTryGetCharKeyboard(&DummyScanCode, 5);
}
- /*
+ /*
* Null terminate the output string -- documentation states that
* DbgPrompt does not null terminate, but it does
*/
/* Print a new line */
KdPortPutByteEx(&SerialPortInfo, '\r');
- KdPortPutByteEx(&SerialPortInfo, '\n');
+ KdPortPutByteEx(&SerialPortInfo, '\n');
/* Release spinlock */
KiReleaseSpinLock(&KdpSerialSpinLock);
KdPortPutByteEx(&SerialPortInfo, Response);
}
+ if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
+ KbdEnableMouse();
+
/* Print a new line */
KdPortPutByteEx(&SerialPortInfo, '\r');
KdPortPutByteEx(&SerialPortInfo, '\n');