Synchronize with trunk revision 59636 (just before Alex's CreateProcess revamp).
[reactos.git] / ntoskrnl / kdbg / kdb_cli.c
index 8e842f8..c40c282 100644 (file)
@@ -90,12 +90,15 @@ static BOOLEAN KdbpCmdSet(ULONG Argc, PCHAR Argv[]);
 static BOOLEAN KdbpCmdHelp(ULONG Argc, PCHAR Argv[]);
 static BOOLEAN KdbpCmdDmesg(ULONG Argc, PCHAR Argv[]);
 
-#ifdef __ROS_CMAKE__
+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 */
 
@@ -107,6 +110,7 @@ static LONG KdbCommandHistoryIndex = 0;
 static ULONG KdbNumberOfRowsPrinted = 0;
 static ULONG KdbNumberOfColsPrinted = 0;
 static BOOLEAN KdbOutputAborted = FALSE;
+static BOOLEAN KdbRepeatLastCommand = FALSE;
 static LONG KdbNumberOfRowsTerminal = -1;
 static LONG KdbNumberOfColsTerminal = -1;
 
@@ -139,7 +143,7 @@ static const struct
     { "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_CMAKE__
+#ifdef __ROS_DWARF__
     { "dt", "dt [mod] [type] [addr]", "Print a struct.  Addr is optional.", KdbpCmdPrintStruct },
 #endif
 
@@ -176,7 +180,8 @@ static const struct
     { "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 }
+    { "help", "help", "Display help screen.", KdbpCmdHelp },
+    { "!pool", "!pool [Address [Flags]]", "Display information about pool allocations.", ExpKdbgExtPool }
 };
 
 /* FUNCTIONS *****************************************************************/
@@ -401,6 +406,24 @@ KdbpEvaluateExpression(
     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
@@ -460,23 +483,23 @@ KdbpCmdEvalExpression(
     return TRUE;
 }
 
-#ifdef __ROS_CMAKE__
+#ifdef __ROS_DWARF__
 
 /*!\brief Print a struct
  */
 static VOID
 KdbpPrintStructInternal
-(PROSSYM_INFO Info, 
- PCHAR Indent, 
- BOOLEAN DoRead, 
- PVOID BaseAddress, 
+(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 = { };
+    ROSSYM_AGGREGATE MemberAggregate = {0 };
 
     for (i = 0; i < Aggregate->NumElements; i++) {
         Member = &Aggregate->Elements[i];
@@ -508,7 +531,7 @@ KdbpPrintStructInternal
             default: {
                 if (Member->Size < 8) {
                     if (NT_SUCCESS(KdbpSafeReadMemory(&Result, ((PCHAR)BaseAddress) + Member->BaseOffset, Member->Size))) {
-                        int j;
+                        ULONG j;
                         for (j = 0; j < Member->Size; j++) {
                             KdbpPrint(" %02x", (int)(Result & 0xff));
                             Result >>= 8;
@@ -551,24 +574,26 @@ KdbpCmdPrintStruct(
     ULONG Argc,
     PCHAR Argv[])
 {
-    int i;
+    ULONG i;
     ULONGLONG Result = 0;
     PVOID BaseAddress = 0;
-    ROSSYM_AGGREGATE Aggregate = { };
-    UNICODE_STRING ModName = { };
-    ANSI_STRING AnsiName = { };
-    CHAR Indent[100] = { };
+    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);
-    PROSSYM_INFO Info = KdbpSymFindCachedFile(&ModName);
+    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;
@@ -579,7 +604,7 @@ KdbpCmdPrintStruct(
             len = strlen(Argv[i]);
             Argv[i][len] = ' ';
         }
-        
+
         /* Evaluate the expression */
         DPRINT1("Arg: %s\n", ArgStart);
         if (KdbpEvaluateExpression(ArgStart, strlen(ArgStart), &Result)) {
@@ -861,7 +886,7 @@ KdbpCmdRegs(
     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,
@@ -945,6 +970,86 @@ KdbpCmdRegs(
     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
@@ -1052,6 +1157,20 @@ KdbpCmdBackTrace(
         if (Address == 0)
             break;
 
+        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);
@@ -1330,7 +1449,7 @@ KdbpCmdBreakPoint(ULONG Argc, PCHAR Argv[])
         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;
@@ -2467,6 +2586,8 @@ KdbpPrint(
         if (KdbNumberOfRowsTerminal > 0 &&
             (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
         {
+            KdbRepeatLastCommand = FALSE;
+
             if (KdbNumberOfColsPrinted > 0)
                 DbgPrint("\n");
 
@@ -2605,6 +2726,7 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
         p = p0;
     for (j = KdbNumberOfRowsTerminal; j--; )
     {
+        int linesCnt;
         p1 = memrchr(p0, '\n', p-p0);
         prev_p = p;
         p = p1;
@@ -2615,7 +2737,7 @@ CountOnePageUp(PCHAR Buffer, ULONG BufLength, PCHAR pCurPos)
                 p = p0;
             break;
         }
-        int linesCnt = (KdbNumberOfColsTerminal+prev_p-p-2) / KdbNumberOfColsTerminal;
+        linesCnt = (KdbNumberOfColsTerminal+prev_p-p-2) / KdbNumberOfColsTerminal;
         if (linesCnt > 1)
             j -= linesCnt-1;
     }
@@ -2786,6 +2908,8 @@ KdbpPager(
         if (KdbNumberOfRowsTerminal > 0 &&
             (LONG)(KdbNumberOfRowsPrinted + RowsPrintedByTerminal) >= KdbNumberOfRowsTerminal)
         {
+            KdbRepeatLastCommand = FALSE;
+
             if (KdbNumberOfColsPrinted > 0)
                 DbgPrint("\n");
 
@@ -2984,7 +3108,7 @@ KdbpReadCommand(
     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;
@@ -3056,15 +3180,16 @@ KdbpReadCommand(
              * Repeat the last command if the user presses enter. Reduces the
              * risk of RSI when single-stepping.
              */
-            if (Buffer == Orig)
-            {
-                RtlStringCbCopyA(Buffer, Size, LastCommand);
-            }
-            else
+            if (Buffer != Orig)
             {
+                KdbRepeatLastCommand = TRUE;
                 *Buffer = '\0';
                 RtlStringCbCopyA(LastCommand, sizeof(LastCommand), Orig);
             }
+            else if (KdbRepeatLastCommand)
+                RtlStringCbCopyA(Buffer, Size, LastCommand);
+            else
+                *Buffer = '\0';
 
             return;
         }
@@ -3163,6 +3288,82 @@ KdbpReadCommand(
     }
 }
 
+
+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.
@@ -3177,6 +3378,7 @@ KdbpDoCommand(
     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];
 
@@ -3217,6 +3419,12 @@ KdbpDoCommand(
         }
     }
 
+    /* Now invoke the registered callbacks */
+    if (KdbpInvokeCliCallbacks(Command, Argc, Argv))
+    {
+        return TRUE;
+    }
+
     KdbpPrint("Command '%s' is unknown.\n", OrigCommand);
     return TRUE;
 }
@@ -3370,7 +3578,8 @@ KdbpCliInit()
     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))
@@ -3486,6 +3695,9 @@ KdpPrompt(IN LPSTR InString,
                         *(KdpPromptString.Buffer + i));
     }
 
+    if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
+        KbdDisableMouse();
+
     /* Loop the whole string */
     for (i = 0; i < OutStringLength; i++)
     {
@@ -3528,7 +3740,7 @@ KdpPrompt(IN LPSTR InString,
                 KdbpTryGetCharKeyboard(&DummyScanCode, 5);
             }
 
-            /* 
+            /*
              * Null terminate the output string -- documentation states that
              * DbgPrompt does not null terminate, but it does
              */
@@ -3536,7 +3748,7 @@ KdpPrompt(IN LPSTR InString,
 
             /* Print a new line */
             KdPortPutByteEx(&SerialPortInfo, '\r');
-            KdPortPutByteEx(&SerialPortInfo, '\n');         
+            KdPortPutByteEx(&SerialPortInfo, '\n');
 
             /* Release spinlock */
             KiReleaseSpinLock(&KdpSerialSpinLock);
@@ -3553,6 +3765,9 @@ KdpPrompt(IN LPSTR InString,
         KdPortPutByteEx(&SerialPortInfo, Response);
     }
 
+    if (!(KdbDebugState & KD_DEBUG_KDSERIAL))
+        KbdEnableMouse();
+
     /* Print a new line */
     KdPortPutByteEx(&SerialPortInfo, '\r');
     KdPortPutByteEx(&SerialPortInfo, '\n');