⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 debugger.doc

📁 MMURTL(tm) Computer Operating System Ver x0.8, source code.
💻 DOC
字号:
Copyright (c) 1991-1993  R.A. Burgess


The MMURTL Debugger

The MMURTL Debugger is built into the operating system. It is not a separate run file.  It provides the following functions:

    Display of instructions (disassembled)
    Dumping of linear memory as Bytes or DWords
    Display of Exchanges (and messages or task waiting)
    Set/Clear breakpoints
    Full register display

USING THE DEBUGGER


Entering the Debugger
	The Debugger may be entered using the Debugger function key in the monitor, the Debug command in the CLI, or by placing and INT 03 command anywhere in your application's ASM file.

Loading the Symbol Table

    (Not implemented - yet)

Exiting the Debugger

    Press the Escape Key

Debugger Function Keys:

    F1  - Single step - this returns to the application and executes one instruction.

    F2  - 

    F3  -  

    F4  -

    F5  - Lists exchanges and messages or tasks that are waiting at them.

    F8  - This allows you to set the code address the debugger is displaying (disassembling)

    F9  - Dump Bytes
    F10 - Dump DWord
       These dump commands do almost the same thing which is to do a hex dump on a memory address.  The DWord format shows all data as DWords in the proper Intel byte order.

    F12 - Lists addresses of important structures used during OS development.

DEBUGGER THEORY

The 386/486 processors have very powerful debugging features.  They have internal debug registers that allow you to set code and data breakpoints.  What this means is that you can set the processor to execute an interrupt (actually a trap) whenever ANY linear memory location is accessed, or at the beginning of any instruction.  Of course you can still use the INT 03 instruction in your code just like the older processors if needed without having to fill in special registers.  There are only 4 debug address registers in the processor, which means you can only have 4 active breakpoints in your program if you only use the registers.   The INT 03 interrupt is just like any other on the system.  When it ocurrs, entry 3 in the interrupt table is used to "vector" to a procdure or task.  In MMURTL's case, it is an interrupt procedure (which will switch to the interrupt task after some cleanup).   

Debugging in a multitasking operating system is complicated to say the least.  The 386/486 processors makes it a lot easier because it lets you determine if the breakpoint is local to a single task or global to all tasks.  Remember that several tasks may be executing exactly the same piece of code.  If you set a breakpoint using the INT03 instruction, every task that executes it will produce the interrupt and enter the debugger.  

MMURTL's debugger is set up as a separate job.  It has its own Job Control Block (JCB) and one task (its own TSS).  The debugger is the highest priority process on the system (level 0).  No other task operates at that high a priority.  The debugger also has its own virtual screen (just like applications do).

When writing an operating system, the debugger becomes one of the most important tools you can have.  If you have problems before the debugger is initialized, it can mean hours of grueling debugging with very little indication of what's going on (usually a blank screen and nothing else).

Even though the debugger has its own JCB and looks like another application for the most part, it has special privleges that allow to it to operate outside of the direct control of the kernel.  It can remove the currently running task and make itself run, and do the reverse without the kernel even begin aware of what has happened (tricky little fellow).  The debugger must be able to do this to get its job done.

When the debug interrupt activates, we initially enter an interrupt procedure that places the running task in a hold status.  Hold status simply means that it is not placed on the ready queue as if it were a normal task switch. Instead, the current pRunTSS (pointer to the currently running TSS) is saved by the debugger, and the interrupt procedure does a task swtich to the debugger's task (the debugger becomes the active job).  This effectively replaces the running task with the debugger task.  

A few housekeeping chores must be done before the task switch is made.  These include copying the interrupted task's Page Directory Entry into the debugger's JCB, and the debugger's TSS.  This is so the debugger is using the tasks exact linear memory image. If we are going to debug a task, we must be able to access all of its memory (including allocated memory).

The debugger can NOT debug itself.  It's actually an orphan when it comes to being a program in it's own right.  It doesn't have it's own Page Directory or Page Tables like other jobs (programs). It almost (but not quite) becomes a task in the job it interrupted. 

The INT 03 interrupt is not the only way to enter MMURTL's debugger.  MMURTL's debugger is also entered when other fatal processor exceptions occur.  If you look through the 386/486 documentation, you will see that there are many interrupts (called exceptions or faults) that can occur.  Some of these are used by the OS to indicate something needs to be done, while others should not happen and are considered fatal for the task that caused them.  Until MMURTL uses demand page virtual memory, a page fault is fatal to a task.  It means it tried to access memory that didn't belong to it.  The exceptions that cause entry into the debugger include (F=Faults, T=Traps, S=Aborts):

No.	Type	Description
0*	F	Divide by zero
1	T/F	Debug Exception (debugger uses for single step) 
3	T	Breakpoint (set by debugger, or INT03 in code)
4	T	Overflow (INT0 instruction)
5*	F	Bounds Check (from Bound Instruction)
6*	F	Invalid Opcodes (reserved instructions)
7*	F	Coprocessor Not available (on ESC and WAIT)
8*	A	Double fault (real bad news...)
9
10*	F	Invalid TSS
11*	F	Segment Not Present
12*	F	Stack Fault
13*	F/T	General Protection Fault
14*	F	Page Fault
16	F	Coprocessor error

* - return address points to faulting instruction.

Some of the exceptions even place an error code on the stack after the return address.  MMURTL has a very small interrupt procedure for each of these exceptions.  These procedures  get the information off the stack before switching to the debugger. The debugger uses this information to help you find the problem.  When the debugger is entered it displays the registers from the task that was interrupted.  To do this it uses the values in the task's Task State Segment (TSS).  All of the values are just the way they were when the exception occurred except the CS and EIP.  These contain the address of the exception handler.  To fix this so that you see proper address of the code that was interrupted we must get the return address from the stack and place it back into the TSS.  This also has the effect of starting the task back at the instruction AFTER it was interrupted (the next instruction to execute).  This makes the debugger transparent (which is exactly what we want).  This also allows you to restart the application after a breakpoint at the proper place.  If the debugger was entered because of a fault, you will more than likely you will want to kill the offending Job and start again setting a breakpoint BEFORE the exception occurs and single step while watching the registers to see where the problem is.

Exiting the debugger is just about the reverse of entering it.  The debugger removes himself as the running task and returns the interrupted task to the running state with the values in the TSS.  This also includes switching the video back to the rightful owner.

While you are in the debugger, all of the registers from the task that was interrupted are displayed along with any error that may have been removed from the stack on a fault.


--------------- End of Debugger documentation ------------
   

 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -