User:Johnburger/Demo/Ints/Debug/Debug

From OSDev Wiki
Jump to: navigation, search

This module leverages the '386 debug facilities to implement a "debugger".

Contents

Design

The original IBM PC only entered this interrupt as a result of an INT 3 instruction in the code. The '386 added to this with the concept of "Hardware Debug Registers". These could be set to make an INT 3 occur on a number of different system events, such as executing an instruction at a particular address (without modifying that instruction to make it an INT 3 first) or, particularly usefully, writing to a particular memory location.

Implementation

But all that this "debugger" does is show the memory contents of (readable) Segments on the screen. This can be VERY useful! Directly after carefully crafting a system table in memory, it is handy to add a DEBUG macro (INT 3) so that you can examine the memory, and confirm that everything is as you designed it. The alternative could be a triple-fault...

Invocation

To invoke the debugger in code, merely call the DEBUG macro (which does an INT 3). Or, at runtime, press the <Pause/Break> key (get it?!). To show that the debugger is working, a Dingle(tm) is active in the top right corner.

Interaction

Once the memory dump is showing, you can Arrow or Page up and down, or choose other displayable Segments (Left and Right). You can also switch between showing Bytes and DWords by pressing <Del>.

Some Fun

For giggles, use the Right arrow to display the alias for the Screen, and then scroll down to see the Hex values for the changing Dingle(tm). Then scroll further to see the changes to the Screen that are showing the values for the changing Dingle(tm). Inception!

Resume

To exit the Debugger, press <Esc>. It will clear the screen, return to the executing code, and continue where it left off - minus the borders, which are only drawn once at the beginning of the User Task.

Recursion

One thing to note: while the Debugger is running, you can look at its operation by... invoking the Debugger. Press <Break> again. And again. And again. You'll have to exit the same number of times you entered. To prove it, go look at the Ring0 Stack that's active, and look at how deep it is. Press <Break> again. This will start a new Debugger at the beginning again, so you'll have to find the active Ring0 Stack again. Observe that it's deeper; so press <Break> again. And again. And... You had to do it, didn't you? Well, it's your own fault!

Stack Fault

That red lump of text has an Int of 0C, which corresponds to a Stack fault. Sure enough, the Stack underflowed - the ESP value is at the limit of the stack. (If you don't see the red lump of text, press <Esc> a few times.) You cannot go back from here: you've caused a Stack Fault, and nothing can un-underflow it. All you can do is invoke the Debugger again. And again. And again. Want to know how deep you are? Check the SS:ESP value as you go. This time the value to watch out for (if you haven't changed the Stack Fault Stack Size) is FFFFF000. As you get nearer to this value, you get closer to underflowing the stack again. Quick: remember the values for SS and Task!

Double Fault

If you keep hitting <Break>, you'll underflow the Stack Fault handler stack, causing another Stack Fault. The CPU will vector through the Stack Fault IDT entry, which is a Task Gate, to attempt to switch to the defined Stack Fault TSS. Unfortunately, it's marked as Busy: with us! A Fault while trying to react to a Fault is usually a Double Fault, which has its own handler, INT 08. That also has a Task Gate, to a different TSS, so a whole new context is switched in, with its own new Stack - check the SS and Task values. The important thing to realise about a Double Fault isn't that it "remembers" it was in the middle of exception handler code when another exception happens: what triggers a Double Fault is if, while the CPU is trying to start the handler for an exception, another exception occurs.

Triple Fault

So, let's tempt fate and underflow this stack too. By pressing <Break> a few more times, the Stack will underflow again, causing a Stack Fault - but that TSS is busy, so it will generate a Double Fault. Unfortunately, even that TSS is busy, so the CPU starts a triple-fault - which simply shuts down the CPU. The PC (or virtualisation environment) detects this state, and reboots the CPU.

Demo/Ints/Debug/Debug.inc

;
; Ints/Debug.inc
;
 
; This module leverages the '386 debug facilities to implement a "debugger".
;
; The original IBM PC only entered this interrupt as a result of an INT 3
; instruction. The '386 added to this with the concept of "Hardware Debug
; Registers". These could be set to make an INT 3 occur on a number of different
; system events: executing an instruction at a particular address (without
; modifying that instruction to make it an INT 3 first); or, particularly
; useful, writing to a particular memory location.
;
; But all that this "debugger" does is show the memory contents of (readable)
; Segments on the screen. This can be VERY useful! Directly after carefully
; crafting a system table in memory, it is handy to add a DEBUG (INT 3) so that
; you can examine the memory, and confirm that everything is as you designed it.
; The alternative could be a triple-fault...
;
; To invoke the debugger in code, merely call the "DEBUG" macro (which does an
; INT 3). Or, at runtime, press the Pause/Break key (by default: get it?!). To
; show that the debugger is working, a Dingle(tm) is active in the top right
; corner.
;
; Once the memory dump is showing, you can Scroll or Page up and down, or choose
; other displayable Segments (Left and Right). For giggles, display the alias
; for the Screen, and scroll to see the changing Dingle(tm). Then scroll further
; to see the changes to the Screen that are showing the Dingle(tm). Inception!
;
; To exit the Debugger, press Esc (by default). It will return and continue
; where it left off. One thing to note: while the Debugger is running, you can
; look at it by... invoking the Debugger. Press Break again. And again. And
; again. You'll have to exit the same number of times you entered. To prove it,
; go look at the Ring0 Stack that's active, and watch it get deeper, and deeper,
; and... You had to do it, didn't you? Well, it's your own fault!
 
; Here are the colours for the different fields of the display
Debug.Colour.Addr   EQU         Dev.VGA.Grey  ; Colour of Address field
Debug.Colour.Bytes  EQU         Dev.VGA.White ; Colour of Hex Bytes field
Debug.Colour.DWords EQU         Dev.VGA.White ; Colour of Hex DWords field
Debug.Colour.ASCII  EQU         Dev.VGA.Grey  ; Colour of ASCII field
Debug.Colour.Label  EQU         Dev.VGA.White ; Colour of Labels
Debug.Colour.Regs   EQU         Dev.VGA.Grey  ; Colour of Regs field
Debug.Colour.Blank  EQU         Dev.VGA.Grey  ; Colour when there's nothing there
 
; Here are the scan-codes for the different functions that the debugger can do
Debug.Key.Quit   EQU            Dev.Key.Break | Dev.Key.Esc ; Quit on Esc UP
Debug.Key.Up     EQU            Dev.Key.Up    ; Scroll up (earlier in memory)
Debug.Key.Down   EQU            Dev.Key.Down  ; Scroll down (later in memory)
Debug.Key.PgUp   EQU            Dev.Key.PgUp  ; Lots of .Ups
Debug.Key.PgDn   EQU            Dev.Key.PgDn  ; Lots of .Downs
Debug.Key.Format EQU            Dev.Key.Del   ; Toggle between Bytes and DWords
Debug.Key.Prev   EQU            Dev.Key.Left  ; Previous Segment
Debug.Key.Next   EQU            Dev.Key.Right ; Next Segment
 
; Screen coordinates to use
Debug.VGA.Top    EQU            (VGA.Rows-Debug.VGA.Height+1)/2
Debug.VGA.Left   EQU            (VGA.Cols-Debug.VGA.Width+1)/2
Debug.VGA.Width  EQU            Debug.Width.Addr+Debug.Width.Byte
Debug.VGA.Height EQU            VGA.Rows-1
Debug.Show.Height EQU           Debug.VGA.Height-Debug.Regs.Rows
Debug.Regs.Rows  EQU            4
 
; These constants could be hard to change...
Debug.Width.Addr  EQU           4+1+8+3
Debug.Width.Byte  EQU           (2+1)*Debug.BytesPerRow+Debug.BytesPerRow ; Chars/Byte + ASCII
Debug.Width.DWord EQU           (8+2)*Debug.BytesPerRow/4  ; Chars/DWord
Debug.BytesPerRow EQU           16
 
;-------------------------------------------------------------------------------
Ints.Debug:
                PUSH            0               ; Duplicate Fault's stack
                PUSH            3               ; Duplicate Fault's stack
 
                PUSHAD
                CALL            Ints.Fault.Frame.Create
 
                CALL            .Init           ; Initialise everything
                PUSH            EAX             ; Remember TSS to switch back
 
                CALL            .Regs           ; Display registers
 
                XOR             AX,AX           ; Starting Descriptor
                MOV             DS,AX
                CALL            .Desc.Next      ; Get first (real) Descriptor to Show
.Refresh:
                INC     BYTE    [ES:(VGA.Cols-1)*2] ; Display Dingle(tm)
 
                CALL            .Show           ; Display memory
                CALL            .Key            ; Look at keys
                CMP             AL,Debug.Key.Quit ; Finished?
                JNZ             .Refresh        ; Not yet, so re-display
.Finish:
                POP             EBX             ; Get TSS back
                CALL            .Done
 
                CALL            Ints.Fault.Frame.Destroy
                POPAD
                ADD             ESP,8           ; Ignore Int and "Error" values
                IRETD           ; Return to next instruction
;-------------------------------------------------------------------------------
%include        "Ints/Debug/Init.inc"   ; Initialisation functions
;-------------------------------------------------------------------------------
%include        "Ints/Debug/Regs.inc"   ; Display registers functions
;-------------------------------------------------------------------------------
%include        "Ints/Debug/Show.inc"   ; Display memory functions
;-------------------------------------------------------------------------------
%include        "Ints/Debug/Key.inc"    ; Key functions
Personal tools
Namespaces
Variants
Actions
Navigation
About
Toolbox