3 * Copyright (C) 2001-2004 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/dbg/kdb.c
23 * PURPOSE: Kernel debugger
24 * PROGRAMMER: David Welch (welch@mcmail.com)
29 /* INCLUDES ******************************************************************/
35 #include <internal/debug.h>
37 /* TYPES *********************************************************************/
39 /* GLOBALS *******************************************************************/
44 BOOL KbdEchoOn
= TRUE
;
53 } KDB_ACTIVE_BREAKPOINT
;
55 #define KDB_MAXIMUM_BREAKPOINT_COUNT (255)
57 static ULONG KdbBreakPointCount
= 0;
58 static KDB_ACTIVE_BREAKPOINT
59 KdbActiveBreakPoints
[KDB_MAXIMUM_BREAKPOINT_COUNT
];
61 static BOOLEAN KdbHandleUmode
= FALSE
;
62 static BOOLEAN KdbHandleHandled
= FALSE
;
63 static BOOLEAN KdbIgnoreNextSingleStep
= FALSE
;
65 static ULONG KdbLastSingleStepFrom
= 0xFFFFFFFF;
66 static BOOLEAN KdbEnteredOnSingleStep
= FALSE
;
68 ULONG KdbEntryCount
= 0;
72 PsDumpThreads(BOOLEAN System
);
74 DbgContCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
76 DbgStopCondition(ULONG Aargc
, PCH Argv
[], PKTRAP_FRAME Tf
);
78 DbgEchoToggle(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
80 DbgRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
82 DbgDRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
84 DbgCRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
86 DbgBugCheckCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
88 DbgBackTraceCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
90 DbgAddrCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
92 DbgXCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
94 DbgScriptCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
96 DbgThreadListCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
98 DbgProcessListCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
100 DbgProcessHelpCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
102 DbgShowFilesCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
104 DbgEnableFileCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
106 DbgDisableFileCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
108 DbgDisassemble(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
110 DbgSetBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
112 DbgDeleteBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
114 DbgSetMemoryBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
116 DbgStep(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
118 DbgStepOver(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
120 DbgFinish(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
127 ULONG (*Fn
)(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
);
128 } DebuggerCommands
[] = {
129 {"cont", "cont", "Exit the debugger", DbgContCommand
},
130 {"echo", "echo", "Toggle serial echo", DbgEchoToggle
},
131 {"condition", "condition [all|umode|kmode]", "Kdbg enter condition", DbgStopCondition
},
133 {"regs", "regs", "Display general purpose registers", DbgRegsCommand
},
134 {"dregs", "dregs", "Display debug registers", DbgDRegsCommand
},
135 {"cregs", "cregs", "Display control registers", DbgCRegsCommand
},
136 {"bugcheck", "bugcheck", "Bugcheck the system", DbgBugCheckCommand
},
137 {"bt", "bt [*frame-address]|[thread-id]","Do a backtrace", DbgBackTraceCommand
},
138 {"addr", "addr <address>", "Displays symbol info", DbgAddrCommand
},
139 {"x", "x <addr> <words>", "Displays <addr> for <words>", DbgXCommand
},
140 {"plist", "plist", "Display processes in the system", DbgProcessListCommand
},
141 {"tlist", "tlist [sys]", "Display threads in the system", DbgThreadListCommand
},
142 {"sfiles", "sfiles", "Show files that print debug prints", DbgShowFilesCommand
},
143 {"efile", "efile <filename>", "Enable debug prints from file", DbgEnableFileCommand
},
144 {"dfile", "dfile <filename>", "Disable debug prints from file", DbgDisableFileCommand
},
145 {"js", "js", "Script mode", DbgScriptCommand
},
146 {"disasm", "disasm <address>", "Disables 10 instructions at <address> or "
147 "eip", DbgDisassemble
},
148 {"bp", "bp <address>", "Sets an int3 breakpoint at a given address",
150 {"bc", "bc <breakpoint number>", "Deletes a breakpoint",
151 DbgDeleteBreakPoint
},
152 {"ba", "ba <debug register> <access type> <length> <address>",
153 "Sets a breakpoint using a debug register", DbgSetMemoryBreakPoint
},
154 {"t", "t", "Steps forward a single instructions", DbgStep
},
155 {"p", "p", "Steps forward a single instructions skipping calls",
157 {"finish", "finish", "Runs until the current function exits", DbgFinish
},
158 {"help", "help", "Display help screen", DbgProcessHelpCommand
},
162 static const char *ExceptionTypeStrings
[] =
169 "BOUND range exceeded",
171 "No Math Coprocessor",
175 "Segment Not Present",
176 "Stack Segment Fault",
177 "General Protection",
186 volatile DWORD x_dr0
= 0, x_dr1
= 0, x_dr2
= 0, x_dr3
= 0, x_dr7
= 0;
188 extern LONG
KdbDisassemble(ULONG Address
);
189 extern LONG
KdbGetInstLength(ULONG Address
);
191 /* FUNCTIONS *****************************************************************/
194 * Convert a string to an unsigned long integer.
196 * Ignores `locale' stuff. Assumes that the upper and lower case
197 * alphabets and digits are each contiguous.
200 strtoul(const char *nptr
, char **endptr
, int base
)
202 const char *s
= nptr
;
205 unsigned long cutoff
;
206 int neg
= 0, any
, cutlim
;
209 * See strtol for comments as to the logic used.
213 } while (isspace(c
));
221 if ((base
== 0 || base
== 16) &&
222 c
== '0' && (*s
== 'x' || *s
== 'X'))
229 base
= c
== '0' ? 8 : 10;
230 cutoff
= (unsigned long)ULONG_MAX
/ (unsigned long)base
;
231 cutlim
= (unsigned long)ULONG_MAX
% (unsigned long)base
;
232 for (acc
= 0, any
= 0;; c
= *s
++)
237 c
-= isupper(c
) ? 'A' - 10 : 'a' - 10;
242 if (any
< 0 || acc
> cutoff
|| (acc
== cutoff
&& c
> cutlim
))
257 *endptr
= any
? (char *)s
- 1 : (char *)nptr
;
263 strpbrk(const char* s
, const char* accept
)
266 for (; (*s
) != 0; s
++)
268 for (i
= 0; accept
[i
] != 0; i
++)
270 if (accept
[i
] == (*s
))
282 KdbpSafeReadMemory(PVOID dst
, PVOID src
, INT size
)
287 page_end
= (((ULONG_PTR
)src
+ size
) / PAGE_SIZE
);
288 for (page
= ((ULONG_PTR
)src
/ PAGE_SIZE
); page
<= page_end
; page
++)
290 if (!MmIsPagePresent(NULL
, (PVOID
)(page
* PAGE_SIZE
)))
291 return STATUS_UNSUCCESSFUL
;
295 RtlCopyMemory(dst
, src
, size
);
296 return STATUS_SUCCESS
;
301 KdbpSafeWriteMemory(PVOID dst
, PVOID src
, INT size
)
303 return KdbpSafeWriteMemory(dst
, src
, size
);
306 /* check destination */
307 page_end
= (((ULONG_PTR
)dst
+ size
) / PAGE_SIZE
);
308 for (page
= ((ULONG_PTR
)dst
/ PAGE_SIZE
); page
<= page_end
; page
++)
310 if (!MmIsPagePresent(NULL
, (PVOID
)(page
* PAGE_SIZE
)))
311 return STATUS_UNSUCCESSFUL
;
315 RtlCopyMemory(dst
, src
, size
);
316 return STATUS_SUCCESS
;
322 KdbGetCommand(PCH Buffer
)
326 static CHAR LastCommand
[256] = "";
328 static CHAR LastKey
= '\0';
330 KbdEchoOn
= !((KdDebugState
& KD_DEBUG_KDNOECHO
) != 0);
334 if (KdDebugState
& KD_DEBUG_KDSERIAL
)
335 while ((Key
= KdbTryGetCharSerial()) == -1);
337 while ((Key
= KdbTryGetCharKeyboard(&ScanCode
)) == -1);
339 if (Key
== '\n' && LastKey
== '\r')
341 /* Ignore this key... */
343 else if (Key
== '\r' || Key
== '\n')
347 Repeat the last command if the user presses enter. Reduces the
348 risk of RSI when single-stepping.
352 strcpy(Buffer
, LastCommand
);
357 strcpy(LastCommand
, Orig
);
362 else if (Key
== BS
|| Key
== DEL
)
369 DbgPrint("%c %c", BS
, BS
);
374 else if (ScanCode
== 72)
377 while (Buffer
> Orig
)
382 DbgPrint("%c %c", BS
, BS
);
386 for (i
= 0; LastCommand
[i
] != 0; i
++)
389 DbgPrint("%c", LastCommand
[i
]);
390 *Buffer
= LastCommand
[i
];
407 KdbDecodeAddress(PCHAR Buffer
, PULONG Address
)
409 while (isspace(*Buffer
))
413 if (Buffer
[0] == '<')
415 PCHAR ModuleName
= Buffer
+ 1;
416 PCHAR AddressString
= strpbrk(Buffer
, ":");
417 extern LIST_ENTRY ModuleTextListHead
;
418 PLIST_ENTRY current_entry
;
419 MODULE_TEXT_SECTION
* current
= NULL
;
420 static WCHAR ModuleNameW
[256];
423 if (AddressString
== NULL
)
425 DbgPrint("Address %x is malformed.\n", Buffer
);
430 while (isspace(*AddressString
))
435 for (i
= 0; ModuleName
[i
] != 0 && !isspace(ModuleName
[i
]); i
++)
437 ModuleNameW
[i
] = (WCHAR
)ModuleName
[i
];
441 /* Find the module. */
442 current_entry
= ModuleTextListHead
.Flink
;
444 while (current_entry
!= &ModuleTextListHead
&&
445 current_entry
!= NULL
)
448 CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
, ListEntry
);
449 if (wcscmp(ModuleNameW
, current
->Name
) == 0)
453 current_entry
= current_entry
->Flink
;
455 if (current_entry
== NULL
|| current_entry
== &ModuleTextListHead
)
457 DbgPrint("Couldn't find module %s.\n", ModuleName
);
460 *Address
= current
->Base
;
461 *Address
+= strtoul(AddressString
, NULL
, 16);
466 *Address
= strtoul(Buffer
, NULL
, 0);
472 KdbOverwriteInst(ULONG Address
, PUCHAR PreviousInst
, UCHAR NewInst
)
476 /* Get the protection for the address. */
477 Protect
= MmGetPageProtect(PsGetCurrentProcess(), (PVOID
)PAGE_ROUND_DOWN(Address
));
478 /* Return if that page isn't present. */
479 if (Protect
& PAGE_NOACCESS
)
481 return(STATUS_MEMORY_NOT_ALLOCATED
);
483 if (Protect
& (PAGE_READONLY
|PAGE_EXECUTE
|PAGE_EXECUTE_READ
))
485 MmSetPageProtect(PsGetCurrentProcess(), (PVOID
)PAGE_ROUND_DOWN(Address
),
486 (Protect
& ~(PAGE_READONLY
|PAGE_EXECUTE
|PAGE_EXECUTE_READ
)) | PAGE_READWRITE
);
488 /* Copy the old instruction back to the caller. */
489 if (PreviousInst
!= NULL
)
491 Status
= KdbpSafeReadMemory(PreviousInst
, (PUCHAR
)Address
, 1);
492 if (!NT_SUCCESS(Status
))
494 if (Protect
& (PAGE_READONLY
|PAGE_EXECUTE
|PAGE_EXECUTE_READ
))
496 MmSetPageProtect(PsGetCurrentProcess(), (PVOID
)PAGE_ROUND_DOWN(Address
), Protect
);
501 /* Copy the new instruction in its place. */
502 Status
= KdbpSafeWriteMemory((PUCHAR
)Address
, &NewInst
, 1);
503 if (Protect
& (PAGE_READONLY
|PAGE_EXECUTE
|PAGE_EXECUTE_READ
))
505 MmSetPageProtect(PsGetCurrentProcess(), (PVOID
)PAGE_ROUND_DOWN(Address
), Protect
);
512 KdbRenableBreakPoints(VOID
)
515 for (i
= 0; i
< KDB_MAXIMUM_BREAKPOINT_COUNT
; i
++)
517 if (KdbActiveBreakPoints
[i
].Assigned
&&
518 !KdbActiveBreakPoints
[i
].Enabled
)
520 KdbActiveBreakPoints
[i
].Enabled
= TRUE
;
521 (VOID
)KdbOverwriteInst(KdbActiveBreakPoints
[i
].Address
,
522 &KdbActiveBreakPoints
[i
].SavedInst
,
529 KdbIsBreakPointOurs(PKTRAP_FRAME Tf
)
532 for (i
= 0; i
< KDB_MAXIMUM_BREAKPOINT_COUNT
; i
++)
534 if (KdbActiveBreakPoints
[i
].Assigned
&&
535 KdbActiveBreakPoints
[i
].Address
== (Tf
->Eip
- 1))
544 KdbDeleteBreakPoint(ULONG BreakPointNr
)
546 KdbBreakPointCount
--;
547 KdbActiveBreakPoints
[BreakPointNr
].Assigned
= FALSE
;
551 KdbInsertBreakPoint(ULONG Address
, BOOLEAN Temporary
)
556 if (KdbBreakPointCount
== KDB_MAXIMUM_BREAKPOINT_COUNT
)
558 return(STATUS_UNSUCCESSFUL
);
560 for (i
= 0; i
< KDB_MAXIMUM_BREAKPOINT_COUNT
; i
++)
562 if (!KdbActiveBreakPoints
[i
].Assigned
)
567 Status
= KdbOverwriteInst(Address
, &SavedInst
, 0xCC);
568 if (!NT_SUCCESS(Status
))
572 KdbActiveBreakPoints
[i
].Assigned
= TRUE
;
573 KdbActiveBreakPoints
[i
].Enabled
= TRUE
;
574 KdbActiveBreakPoints
[i
].Address
= Address
;
575 KdbActiveBreakPoints
[i
].Temporary
= Temporary
;
576 KdbActiveBreakPoints
[i
].SavedInst
= SavedInst
;
577 return(STATUS_SUCCESS
);
581 DbgSetBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
588 DbgPrint("Need an address to set the breakpoint at.\n");
591 /* Stitch the remaining arguments back into a single string. */
592 for (i
= 2; i
< Argc
; i
++)
596 if (!KdbDecodeAddress(Argv
[1], &Addr
))
600 DbgPrint("Setting breakpoint at 0x%X\n", Addr
);
601 if (!NT_SUCCESS(Status
= KdbInsertBreakPoint(Addr
, FALSE
)))
603 DbgPrint("Failed to set breakpoint (Status %X)\n", Status
);
609 DbgDeleteBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
614 DbgPrint("Need a breakpoint number to delete.\n");
617 BreakPointNr
= strtoul(Argv
[1], NULL
, 10);
618 DbgPrint("Deleting breakpoint %d.\n", BreakPointNr
);
619 KdbDeleteBreakPoint(BreakPointNr
);
624 DbgSetMemoryBreakPoint(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
628 ULONG Length
, Address
;
631 if (Argc
!= 2 && Argc
< 5)
633 DbgPrint("ba <0-3> <r|w|e> <1|2|4> <address>\n");
636 DebugRegNr
= strtoul(Argv
[1], NULL
, 10);
639 DbgPrint("Debug register number should be between 0 and 3.\n");
644 /* Clear the breakpoint. */
645 Tf
->Dr7
&= ~(0x3 << (DebugRegNr
* 2));
646 if ((Tf
->Dr7
& 0xFF) == 0)
649 If no breakpoints are enabled then
650 clear the exact match flags.
652 Tf
->Dr7
&= 0xFFFFFCFF;
656 BreakType
= Argv
[2][0];
657 if (BreakType
!= 'r' && BreakType
!= 'w' && BreakType
!= 'e')
659 DbgPrint("Access type to break on should be either 'r', 'w' or 'e'.\n");
662 Length
= strtoul(Argv
[3], NULL
, 10);
663 if (Length
!= 1 && Length
!= 2 && Length
!= 4)
665 DbgPrint("Length of the breakpoint should be one, two or four.\n");
668 if (Length
!= 1 && BreakType
== 'e')
670 DbgPrint("The length of an execution breakpoint should be one.\n");
673 /* Stitch the remaining arguments back into a single string. */
674 for (i
= 4; i
< Argc
; i
++)
678 if (!KdbDecodeAddress(Argv
[4], &Address
))
682 if ((Address
& (Length
- 1)) != 0)
684 DbgPrint("The breakpoint address should be aligned to a multiple of "
685 "the breakpoint length.\n");
689 /* Set the breakpoint address. */
692 case 0: Tf
->Dr0
= Address
; break;
693 case 1: Tf
->Dr1
= Address
; break;
694 case 2: Tf
->Dr2
= Address
; break;
695 case 3: Tf
->Dr3
= Address
; break;
697 /* Enable the breakpoint. */
698 Tf
->Dr7
|= (0x3 << (DebugRegNr
* 2));
699 /* Enable the exact match bits. */
700 Tf
->Dr7
|= 0x00000300;
701 /* Clear existing state. */
702 Tf
->Dr7
&= ~(0xF << (16 + (DebugRegNr
* 4)));
703 /* Set the breakpoint type. */
706 case 'r': Rw
= 3; break;
707 case 'w': Rw
= 1; break;
708 case 'e': Rw
= 0; break;
710 Tf
->Dr7
|= (Rw
<< (16 + (DebugRegNr
* 4)));
711 /* Set the breakpoint length. */
712 Tf
->Dr7
|= ((Length
- 1) << (18 + (DebugRegNr
* 4)));
718 DbgStep(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
720 /* Set the single step flag and return to the interrupted code. */
721 Tf
->Eflags
|= (1 << 8);
722 KdbIgnoreNextSingleStep
= FALSE
;
723 KdbLastSingleStepFrom
= Tf
->Eip
;
728 DbgStepOver(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
733 if (!NT_SUCCESS(KdbpSafeReadMemory(Mem
, (PVOID
)Tf
->Eip
, sizeof (Mem
))))
735 DbgPrint("Couldn't access memory at 0x%x\n", (UINT
)Tf
->Eip
);
740 /* Check if the current instruction is a call. */
741 while (Eip
[0] == 0x66 || Eip
[0] == 0x67)
745 if (Eip
[0] == 0xE8 || Eip
[0] == 0x9A || Eip
[0] == 0xF2 || Eip
[0] == 0xF3 ||
746 (Eip
[0] == 0xFF && (Eip
[1] & 0x38) == 0x10))
748 ULONG NextInst
= Tf
->Eip
+ KdbGetInstLength(Tf
->Eip
);
749 KdbLastSingleStepFrom
= Tf
->Eip
;
750 KdbInsertBreakPoint(NextInst
, TRUE
);
755 return(DbgStep(Argc
, Argv
, Tf
));
760 DbgFinish(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
762 PULONG Ebp
= (PULONG
)Tf
->Ebp
;
765 PKTHREAD CurrentThread
;
767 /* Check that ebp points onto the stack. */
768 CurrentThread
= KeGetCurrentThread();
769 if (CurrentThread
== NULL
||
770 !(Ebp
>= (PULONG
)CurrentThread
->StackLimit
&&
771 Ebp
<= (PULONG
)CurrentThread
->StackBase
))
773 DbgPrint("This function doesn't appear to have a valid stack frame.\n");
777 /* Get the address of the caller. */
778 Status
= KdbpSafeReadMemory(&ReturnAddress
, Ebp
+ 1, sizeof(ULONG
));
779 if (!NT_SUCCESS(Status
))
781 DbgPrint("Memory access error (%X) while getting return address.\n",
786 /* Set a temporary breakpoint at that location. */
787 Status
= KdbInsertBreakPoint(ReturnAddress
, TRUE
);
788 if (!NT_SUCCESS(Status
))
790 DbgPrint("Couldn't set a temporary breakpoint at %X (Status %X)\n",
791 ReturnAddress
, Status
);
796 Otherwise start running again and with any luck we will break back into
797 the debugger when the current function returns.
803 DbgDisassemble(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
809 /* Stitch the remaining arguments back into a single string. */
810 for (i
= 2; i
< Argc
; i
++)
814 if (!KdbDecodeAddress(Argv
[1], &Address
))
823 for (i
= 0; i
< 10; i
++)
825 if (!KdbSymPrintAddress((PVOID
)Address
))
827 DbgPrint("<%x>", Address
);
830 InstLen
= KdbDisassemble(Address
);
833 DbgPrint("<INVALID>\n");
844 DbgProcessHelpCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
848 DbgPrint("Kernel debugger commands:\n");
849 for (i
= 0; DebuggerCommands
[i
].Name
!= NULL
; i
++)
851 DbgPrint(" %s", DebuggerCommands
[i
].Syntax
);
852 len
= strlen(DebuggerCommands
[i
].Syntax
);
855 for (j
= 0; j
< 35 - len
; j
++)
860 DbgPrint(" - %s\n", DebuggerCommands
[i
].Help
);
866 DbgThreadListCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
869 if (Argc
== 2 && (!strcmp(Argv
[1], "sys") || !strcmp(Argv
[1], "SYS")))
872 PsDumpThreads(System
);
877 DbgProcessListCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
879 extern LIST_ENTRY PsProcessListHead
;
880 PLIST_ENTRY current_entry
;
884 if (PsProcessListHead
.Flink
== NULL
)
886 DbgPrint("No processes.\n");
890 DbgPrint("Process list: ");
891 current_entry
= PsProcessListHead
.Flink
;
892 while (current_entry
!= &PsProcessListHead
)
894 current
= CONTAINING_RECORD(current_entry
, EPROCESS
, ProcessListEntry
);
895 DbgPrint("%d %.8s", current
->UniqueProcessId
,
896 current
->ImageFileName
);
902 current_entry
= current_entry
->Flink
;
908 DbgPrintBackTrace(PULONG Frame
, ULONG_PTR StackBase
, ULONG_PTR StackLimit
)
912 DbgPrint("Frames: ");
913 while (Frame
!= NULL
&& (ULONG_PTR
)Frame
>= StackLimit
&&
914 (ULONG_PTR
)Frame
< StackBase
) /* FIXME: why limit this to StackBase/StackLimit? */
916 if (!NT_SUCCESS(KdbpSafeReadMemory(&Address
, Frame
+ 1, sizeof (Address
))))
918 DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT
)(Frame
+ 1));
921 KdbSymPrintAddress(Address
);
923 if (!NT_SUCCESS(KdbpSafeReadMemory(&Frame
, Frame
, sizeof (Frame
))))
925 DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT
)Frame
);
932 DbgAddrCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME tf
)
938 Addr
= (PVOID
)strtoul(Argv
[1], NULL
, 0);
939 KdbSymPrintAddress(Addr
);
946 DbgXCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME tf
)
954 Addr
= (PDWORD
)strtoul(Argv
[1], NULL
, 0);
956 Items
= (DWORD
)strtoul(Argv
[2], NULL
, 0);
961 for (i
= 0; i
< Items
; i
++)
967 DbgPrint("%08x:", (int)(&Addr
[i
]));
969 if (!NT_SUCCESS(KdbpSafeReadMemory(&Item
, Addr
+ i
, sizeof (Item
))))
971 DbgPrint("\nCouldn't access memory at 0x%x!\n", (UINT
)(Addr
+ i
));
974 DbgPrint("%08x ", Item
);
980 static int KjsReadRegValue( void *context
,
984 PVOID
*context_list
= context
;
985 PKJS kjs
= (PKJS
)context_list
[0];
986 JSVirtualMachine
*vm
= kjs
->vm
;
988 RTL_QUERY_REGISTRY_TABLE QueryTable
[2] = { { 0 } };
989 UNICODE_STRING NameString
;
990 UNICODE_STRING PathString
;
991 UNICODE_STRING DefaultString
;
992 UNICODE_STRING ValueResult
;
993 ANSI_STRING AnsiResult
;
995 if (args
->u
.vinteger
!= 2 ||
996 args
[1].type
!= JS_STRING
|| args
[2].type
!= JS_STRING
) {
997 return JS_PROPERTY_FOUND
;
1000 RtlInitUnicodeString(&PathString
,NULL
);
1001 RtlInitUnicodeString(&NameString
,NULL
);
1003 cp
= js_string_to_c_string (vm
, &args
[1]);
1004 RtlCreateUnicodeStringFromAsciiz(&PathString
,cp
);
1006 cp
= js_string_to_c_string (vm
, &args
[2]);
1007 RtlCreateUnicodeStringFromAsciiz(&NameString
,cp
);
1010 RtlInitUnicodeString(&ValueResult
,NULL
);
1011 RtlInitUnicodeString(&DefaultString
,L
"");
1012 RtlInitAnsiString(&AnsiResult
,NULL
);
1014 QueryTable
->EntryContext
= 0;
1016 RTL_QUERY_REGISTRY_REQUIRED
| RTL_QUERY_REGISTRY_DIRECT
;
1017 QueryTable
->Name
= NameString
.Buffer
;
1018 QueryTable
->DefaultType
= REG_SZ
;
1019 QueryTable
->DefaultData
= &DefaultString
;
1020 QueryTable
->EntryContext
= &ValueResult
;
1021 Status
= RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE
,
1027 RtlFreeUnicodeString(&NameString
);
1028 RtlFreeUnicodeString(&PathString
);
1030 if (NT_SUCCESS(Status
)) {
1031 RtlInitAnsiString(&AnsiResult
,NULL
);
1032 RtlUnicodeStringToAnsiString(&AnsiResult
,
1035 js_vm_make_string (vm
, result
, AnsiResult
.Buffer
,
1036 strlen(AnsiResult
.Buffer
));
1037 RtlFreeAnsiString(&AnsiResult
);
1039 result
->type
= JS_INTEGER
;
1040 result
->u
.vinteger
= Status
;
1043 return JS_PROPERTY_FOUND
;
1046 static int KjsGetRegister( void *context
,
1049 PVOID
*context_list
= context
;
1050 if( args
->u
.vinteger
== 1 && args
->type
== JS_INTEGER
) {
1051 DWORD Result
= ((DWORD
*)context_list
[1])[args
[1].u
.vinteger
];
1052 result
->type
= JS_INTEGER
;
1053 result
->u
.vinteger
= Result
;
1056 return JS_PROPERTY_FOUND
;
1059 static int KjsGetNthModule( void *context
,
1062 PVOID
*context_list
= context
;
1063 PKJS kjs
= (PKJS
)context_list
[0];
1064 JSVirtualMachine
*vm
= kjs
->vm
;
1065 PLIST_ENTRY current_entry
;
1066 MODULE_TEXT_SECTION
*current
= NULL
;
1067 extern LIST_ENTRY ModuleTextListHead
;
1070 if (args
->u
.vinteger
!= 1 || args
[1].type
!= JS_INTEGER
) {
1071 return JS_PROPERTY_FOUND
;
1074 current_entry
= ModuleTextListHead
.Flink
;
1076 while (current_entry
!= &ModuleTextListHead
&&
1077 current_entry
!= NULL
&&
1078 n
<= args
[1].u
.vinteger
) {
1079 current
= CONTAINING_RECORD(current_entry
, MODULE_TEXT_SECTION
,
1081 current_entry
= current_entry
->Flink
;
1085 if (current_entry
&& current
) {
1086 ANSI_STRING NameStringNarrow
;
1087 UNICODE_STRING NameUnicodeString
;
1089 RtlInitUnicodeString( &NameUnicodeString
, current
->Name
);
1090 RtlUnicodeStringToAnsiString( &NameStringNarrow
,
1094 js_vm_make_array (vm
, result
, 2);
1096 js_vm_make_string (vm
,
1097 &result
->u
.varray
->data
[0],
1098 NameStringNarrow
.Buffer
,
1099 NameStringNarrow
.Length
);
1101 RtlFreeAnsiString(&NameStringNarrow
);
1103 result
->u
.varray
->data
[1].type
= JS_INTEGER
;
1104 result
->u
.varray
->data
[1].u
.vinteger
= (DWORD
)current
->Base
;
1105 result
->type
= JS_ARRAY
;
1106 return JS_PROPERTY_FOUND
;
1108 result
->type
= JS_UNDEFINED
;
1109 return JS_PROPERTY_FOUND
;
1112 static BOOL
FindJSEndMark( PCHAR Buffer
) {
1115 for( i
= 0; Buffer
[i
] && Buffer
[i
+1]; i
++ ) {
1116 if( Buffer
[i
] == ';' && Buffer
[i
+1] == ';' ) return TRUE
;
1122 DbgScriptCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME tf
)
1126 static void *interp
= 0;
1127 void *script_cmd_context
[2];
1129 if( !interp
) interp
= kjs_create_interp(NULL
);
1130 if( !interp
) return 1;
1132 BufferStart
= Buffer
= ExAllocatePool( NonPagedPool
, 4096 );
1133 if( !Buffer
) return 1;
1135 script_cmd_context
[0] = interp
;
1136 script_cmd_context
[1] = &tf
;
1138 kjs_system_register( interp
, "regs", script_cmd_context
,
1140 kjs_system_register( interp
, "regread", script_cmd_context
,
1142 kjs_system_register( interp
, "getmodule", script_cmd_context
,
1148 "'\\\\Registry\\\\Machine\\\\System\\\\"
1149 "CurrentControlSet\\\\Control\\\\Kdb',"
1152 DbgPrint("\nKernel Debugger Script Interface (JavaScript :-)\n");
1153 DbgPrint("Terminate input with ;; and end scripting with .\n");
1156 if( Buffer
!= BufferStart
)
1160 KdbGetCommand( BufferStart
);
1161 if( BufferStart
[0] == '.' ) {
1162 if( BufferStart
!= Buffer
) {
1163 DbgPrint("Input Aborted.\n");
1164 BufferStart
= Buffer
;
1166 /* Single dot input -> exit */
1170 if( FindJSEndMark( Buffer
) ) {
1171 kjs_eval( interp
, Buffer
);
1172 BufferStart
= Buffer
;
1175 BufferStart
= BufferStart
+ strlen(BufferStart
);
1180 ExFreePool( Buffer
);
1182 kjs_system_unregister( interp
, script_cmd_context
, KjsGetRegister
);
1183 kjs_system_unregister( interp
, script_cmd_context
, KjsReadRegValue
);
1184 kjs_system_unregister( interp
, script_cmd_context
, KjsGetNthModule
);
1190 DbgBackTraceCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1192 ULONG_PTR StackBase
, StackLimit
;
1193 extern unsigned int init_stack
, init_stack_top
;
1195 /* Without an argument we print the current stack. */
1198 if (PsGetCurrentThread() != NULL
)
1200 StackBase
= (ULONG_PTR
)PsGetCurrentThread()->Tcb
.StackBase
;
1201 StackLimit
= PsGetCurrentThread()->Tcb
.StackLimit
;
1205 StackBase
= (ULONG_PTR
)&init_stack_top
;
1206 StackLimit
= (ULONG_PTR
)&init_stack
;
1208 DbgPrintBackTrace((PULONG
)&Tf
->DebugEbp
, StackBase
, StackLimit
);
1211 * If there are two arguments and the second begins with a asterik treat it
1212 * as the address of a frame to start printing the back trace from.
1214 else if (Argc
== 2 && Argv
[1][0] == '*')
1217 Frame
= (PULONG
)strtoul(&Argv
[1][1], NULL
, 0);
1218 DbgPrintBackTrace(Frame
, ULONG_MAX
, 0);
1221 * Otherwise treat the argument as the id of a thread whose back trace is to
1231 DbgPrintCr0(ULONG Cr0
)
1252 if (!(Cr0
& (1 << 4)))
1260 for (i
= 6; i
< 16; i
++)
1264 DbgPrint(" BIT%d", i
);
1267 if (Cr0
& (1 << 16))
1271 if (Cr0
& (1 << 17))
1275 if (Cr0
& (1 << 18))
1279 for (i
= 19; i
< 29; i
++)
1283 DbgPrint(" BIT%d", i
);
1286 if (Cr0
& (1 << 29))
1290 if (Cr0
& (1 << 30))
1294 if (Cr0
& (1 << 31))
1302 DbgCRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1304 ULONG Cr0
, Cr1
, Cr2
, Cr3
, Cr4
;
1308 __asm__
__volatile__ ("movl %%cr0, %0\n\t" : "=d" (Cr0
));
1309 /* __asm__ __volatile__ ("movl %%cr1, %0\n\t" : "=d" (Cr1)); */
1311 __asm__
__volatile__ ("movl %%cr2, %0\n\t" : "=d" (Cr2
));
1312 __asm__
__volatile__ ("movl %%cr3, %0\n\t" : "=d" (Cr3
));
1313 __asm__
__volatile__ ("movl %%cr4, %0\n\t" : "=d" (Cr4
));
1314 __asm__
__volatile__ ("str %0\n\t" : "=d" (Tr
));
1315 __asm__
__volatile__ ("sldt %0\n\t" : "=d" (Ldtr
));
1317 DbgPrint("CR1 %.8x CR2 %.8x CR3 %.8x CR4 %.8x TR %.8x LDTR %.8x\n",
1318 Cr1
, Cr2
, Cr3
, Cr4
, (ULONG
)Tf
, Ldtr
);
1323 DbgDRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1325 DbgPrint("Trap : DR0 %.8x DR1 %.8x DR2 %.8x DR3 %.8x DR6 %.8x DR7 %.8x\n",
1326 Tf
->Dr0
, Tf
->Dr1
, Tf
->Dr2
, Tf
->Dr3
, Tf
->Dr6
, Tf
->Dr7
);
1331 DbgContCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1333 /* Not too difficult. */
1338 DbgStopCondition(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1341 if( KdbHandleHandled
) DbgPrint("all\n");
1342 else if( KdbHandleUmode
) DbgPrint("umode\n");
1343 else DbgPrint("kmode\n");
1345 else if( !strcmp(Argv
[1],"all") )
1346 { KdbHandleHandled
= TRUE
; KdbHandleUmode
= TRUE
; }
1347 else if( !strcmp(Argv
[1],"umode") )
1348 { KdbHandleHandled
= FALSE
; KdbHandleUmode
= TRUE
; }
1349 else if( !strcmp(Argv
[1],"kmode") )
1350 { KdbHandleHandled
= FALSE
; KdbHandleUmode
= FALSE
; }
1356 DbgEchoToggle(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1358 KbdEchoOn
= !KbdEchoOn
;
1363 DbgPrintEflags(ULONG Eflags
)
1365 DbgPrint("EFLAGS:");
1366 if (Eflags
& (1 << 0))
1370 if (!(Eflags
& (1 << 1)))
1374 if (Eflags
& (1 << 2))
1378 if (Eflags
& (1 << 3))
1382 if (Eflags
& (1 << 4))
1386 if (Eflags
& (1 << 5))
1390 if (Eflags
& (1 << 6))
1394 if (Eflags
& (1 << 7))
1398 if (Eflags
& (1 << 8))
1402 if (Eflags
& (1 << 9))
1406 if (Eflags
& (1 << 10))
1410 if (Eflags
& (1 << 11))
1414 if ((Eflags
& ((1 << 12) | (1 << 13))) == 0)
1418 else if ((Eflags
& ((1 << 12) | (1 << 13))) == 1)
1422 else if ((Eflags
& ((1 << 12) | (1 << 13))) == 2)
1426 else if ((Eflags
& ((1 << 12) | (1 << 13))) == 3)
1430 if (Eflags
& (1 << 14))
1434 if (Eflags
& (1 << 15))
1438 if (Eflags
& (1 << 16))
1442 if (Eflags
& (1 << 17))
1446 if (Eflags
& (1 << 18))
1450 if (Eflags
& (1 << 19))
1454 if (Eflags
& (1 << 20))
1458 if (Eflags
& (1 << 21))
1462 if (Eflags
& (1 << 22))
1466 if (Eflags
& (1 << 23))
1470 if (Eflags
& (1 << 24))
1474 if (Eflags
& (1 << 25))
1478 if (Eflags
& (1 << 26))
1482 if (Eflags
& (1 << 27))
1486 if (Eflags
& (1 << 28))
1490 if (Eflags
& (1 << 29))
1494 if (Eflags
& (1 << 30))
1498 if (Eflags
& (1 << 31))
1506 DbgRegsCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1508 DbgPrint("CS:EIP %.4x:%.8x, EAX %.8x EBX %.8x ECX %.8x EDX %.8x\n",
1509 Tf
->Cs
& 0xFFFF, Tf
->Eip
, Tf
->Eax
, Tf
->Ebx
, Tf
->Ecx
, Tf
->Edx
);
1510 DbgPrint("ESI %.8x EDI %.8x EBP %.8x SS:ESP %.4x:%.8x\n",
1511 Tf
->Esi
, Tf
->Edi
, Tf
->Ebp
, Tf
->Ss
& 0xFFFF, Tf
->Esp
);
1512 DbgPrintEflags(Tf
->Eflags
);
1517 DbgBugCheckCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1519 KEBUGCHECK(0xDEADDEAD);
1524 DbgShowFilesCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1531 DbgEnableFileCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1535 if (strlen(Argv
[1]) > 0)
1537 DbgEnableFile(Argv
[1]);
1544 DbgDisableFileCommand(ULONG Argc
, PCH Argv
[], PKTRAP_FRAME Tf
)
1548 if (strlen(Argv
[1]) > 0)
1550 DbgDisableFile(Argv
[1]);
1557 KdbCreateThreadHook(PCONTEXT Context
)
1559 Context
->Dr0
= x_dr0
;
1560 Context
->Dr1
= x_dr1
;
1561 Context
->Dr2
= x_dr2
;
1562 Context
->Dr3
= x_dr3
;
1563 Context
->Dr7
= x_dr7
;
1567 KdbDoCommand(PCH CommandLine
, PKTRAP_FRAME Tf
)
1572 static PCH Argv
[256];
1574 static CHAR OrigCommand
[256];
1576 strcpy(OrigCommand
, CommandLine
);
1580 while ((s1
= strpbrk(s
, "\t ")) != NULL
)
1590 for (i
= 0; DebuggerCommands
[i
].Name
!= NULL
; i
++)
1592 if (strcmp(DebuggerCommands
[i
].Name
, Argv
[0]) == 0)
1594 return(DebuggerCommands
[i
].Fn(Argc
, Argv
, Tf
));
1597 DbgPrint("Command '%s' is unknown.", OrigCommand
);
1602 KdbMainLoop(PKTRAP_FRAME Tf
)
1607 if (!KdbEnteredOnSingleStep
)
1609 DbgPrint("\nEntered kernel debugger (type \"help\" for a list of commands)\n");
1613 if (!KdbSymPrintAddress((PVOID
)Tf
->Eip
))
1615 DbgPrint("<%x>", Tf
->Eip
);
1618 if (KdbDisassemble(Tf
->Eip
) < 0)
1620 DbgPrint("<INVALID>");
1622 KdbEnteredOnSingleStep
= FALSE
;
1623 KdbLastSingleStepFrom
= 0xFFFFFFFF;
1628 DbgPrint("\nkdb:> ");
1630 KdbGetCommand(Command
);
1631 s
= KdbDoCommand(Command
, Tf
);
1636 KdbInternalEnter(PKTRAP_FRAME Tf
)
1638 __asm__
__volatile__ ("cli\n\t");
1640 if (KdDebugState
& KD_DEBUG_SCREEN
)
1642 HalReleaseDisplayOwnership();
1644 (VOID
)KdbMainLoop(Tf
);
1646 __asm__
__volatile__("sti\n\t");
1650 KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord
,
1651 KPROCESSOR_MODE PreviousMode
,
1653 PKTRAP_FRAME TrapFrame
,
1654 BOOLEAN AlwaysHandle
)
1657 ULONG ExpNr
= (ULONG
)TrapFrame
->DebugArgMark
;
1659 if (ExpNr
!= 1 && ExpNr
!= 3)
1661 DbgPrint(":KDBG:Entered:%s:%s\n",
1662 PreviousMode
==KernelMode
? "kmode" : "umode",
1663 AlwaysHandle
? "always" : "if-unhandled");
1666 /* If we aren't handling umode exceptions then return */
1667 if (PreviousMode
== UserMode
&& !KdbHandleUmode
&& !AlwaysHandle
)
1672 /* If the exception would be unhandled (and we care) then handle it */
1673 if (PreviousMode
== KernelMode
&& !KdbHandleHandled
&& !AlwaysHandle
)
1678 /* Exception inside the debugger? Game over. */
1679 if (KdbEntryCount
> 0)
1681 return(kdHandleException
);
1685 /* Clear the single step flag. */
1686 TrapFrame
->Eflags
&= ~(1 << 8);
1688 Reenable any breakpoints we disabled so we could execute the breakpointed
1691 KdbRenableBreakPoints();
1692 /* Silently ignore a debugger initiated single step. */
1693 if (ExpNr
== 1 && KdbIgnoreNextSingleStep
)
1695 KdbIgnoreNextSingleStep
= FALSE
;
1699 /* If we stopped on one of our breakpoints then let the user know. */
1700 if (ExpNr
== 3 && (BreakPointNr
= KdbIsBreakPointOurs(TrapFrame
)) >= 0)
1702 DbgPrint("Entered debugger on breakpoint %d.\n", BreakPointNr
);
1704 The breakpoint will point to the next instruction by default so
1705 point it back to the start of original instruction.
1709 ..and restore the original instruction.
1711 (VOID
)KdbOverwriteInst(TrapFrame
->Eip
, NULL
,
1712 KdbActiveBreakPoints
[BreakPointNr
].SavedInst
);
1714 If this was a breakpoint set by the debugger then delete it otherwise
1715 flag to enable it again after we step over this instruction.
1717 if (KdbActiveBreakPoints
[BreakPointNr
].Temporary
)
1719 KdbActiveBreakPoints
[BreakPointNr
].Assigned
= FALSE
;
1720 KdbBreakPointCount
--;
1721 KdbEnteredOnSingleStep
= TRUE
;
1725 KdbActiveBreakPoints
[BreakPointNr
].Enabled
= FALSE
;
1726 TrapFrame
->Eflags
|= (1 << 8);
1727 KdbIgnoreNextSingleStep
= TRUE
;
1730 else if (ExpNr
== 1)
1732 if ((TrapFrame
->Dr6
& 0xF) != 0)
1734 DbgPrint("Entered debugger on memory breakpoint(s) %s%s%s%s.\n",
1735 (TrapFrame
->Dr6
& 0x1) ? "1" : "",
1736 (TrapFrame
->Dr6
& 0x2) ? "2" : "",
1737 (TrapFrame
->Dr6
& 0x4) ? "3" : "",
1738 (TrapFrame
->Dr6
& 0x8) ? "4" : "");
1740 else if (KdbLastSingleStepFrom
!= 0xFFFFFFFF)
1742 KdbEnteredOnSingleStep
= TRUE
;
1746 DbgPrint("Entered debugger on unexpected debug trap.\n");
1751 const char *ExceptionString
=
1752 (ExpNr
< (sizeof (ExceptionTypeStrings
) / sizeof (ExceptionTypeStrings
[0]))) ?
1753 (ExceptionTypeStrings
[ExpNr
]) :
1754 ("Unknown/User defined exception");
1755 DbgPrint("Entered debugger on exception number %d (%s)\n", ExpNr
, ExceptionString
);
1757 KdbInternalEnter(TrapFrame
);
1759 if (ExpNr
!= 1 && ExpNr
!= 3)
1761 return(kdHandleException
);
1765 /* Clear dr6 status flags. */
1766 TrapFrame
->Dr6
&= 0xFFFF1F00;
1767 /* Set the RF flag to we don't trigger the same breakpoint again. */
1770 TrapFrame
->Eflags
|= (1 << 16);