9 Kernel Debugger

under construction

This chapter tries to describe the kernel debugger functionality and interface.

9.1 Entering and Leaving the Kernel Debugger

There are several methods to enter kernel debugger. You can

For the last method you have to have a debug version of the L3 kernel.

You can leave the kernel debugger by pressing `g` (go).

9.2 Entering Commands

The kernel debugger provides several commands. They usually consist of a letter and some additional parameter. If a command consists of only one letter, the command is executed by pressing the letter on the keyboard.

If there are parameters for the command, you have to type the full number of characters, space or enter to execute the command. For instance the command `show thread control block` (t) can have no parameter and a thread number. The thread number can be a short one or a long one. So depending on the parameter you would have to type:

9.3 Tracing some Kernel Information

The kernel provides mechanisms for continously displaying of kernel events like page faults, messages, idle times and other things.

Enable/Disable autolog. This funtion provides continous information about kernel events on a statistical base.

Enable/Disable page fault display. This function provides a continous display of page faults.

Enable/Disable tracing. (I don't know, what this function really does, but you have to enable tracing to send output to the hercules screen.)

9.4 Information about kernel modules and addresses

The kernel debugger provides information about the L3 kernel modules, versions and associated addresses. You can use this information to set breakpoint base adresses or to find the associated address and module for a given address.

Show all modules with name, offset and version. The output has the format:
<offset> <modul name> <date>,<version>

Look for module and the associated address within that modul for the given address <address>. The output has the format:
<offset> <modul name> <date>,<version>

9.5 Breakpoints

The kernel debugger allows to set one (only one) breakpoint to an arbitrary address. There are three different kinds of breakpoints,

You can combine a breakpoint with an additional condition or restrict it to a distinct thread.

For ease of use you can use breakpoint offsets to set a breakpoint within a module. If there is an offset in use, it will be displayed while entering a breakpoint.

Show current breakpoint if there is one.

bb<break point address>
Set breakpoint base. This address serves as a base for all following breakpoints and allows easier handling of breakpoints within kernel modules. If you want to set a breakpoint within the kernel, you have to add 0xf0000000 to the output of command m or an address of kernel listings.

bi<breakpoint address>
Set an instruction breakpoint. The breakpoint is triggered before the instruction is executed.

bw, ba
Set a write/access breakpoint. The breakpoint is triggered after the instruction is executed. Breakpoints are attributed by size (1 (byte), 2 (word), 4 (dword)). The default size is byte.

You enter a breakpoint like follows:

ba/bw[<size> | <space>]at: [<breakpunkt base>+]<breakpoint address>

Disable Breakpoint.

bt<thread id>
Restrict breakpoint to a distinct thread with id <thread id>. To remove restriction enter bt0.

Add a condition to a breakpoint. The breakpoint is only triggered if the condition is true.


Conditions are related to contents of memory allowing monitoring of a distinct address. They are attributed with size. The breakpoint is triggered if the memory contains a value within or out of a specified range.

It depends on the values given to bx if the meaning is within or out of.

value1 < value2
The breakpoint is triggered if for the content of memory holds
<value1> <= <content of memory> <= <value2>.

value1 > value2
The breakpoint is triggered if for the contents of memory holds
<value2> <= <content of memory> || <content of memory> <= <value1>

9.6 Informationen about threads (tcb's, register contents, stack contents, ...)

The kernel debugger provides mechanisms to examine the state of threads. It supplies information like thread state, contents of wait queues, register contents, stack contents and more.

While visiting the kernel stack you can change the visible area by virtally changing the esp register using cursor keys. You can virtually set register contents to the state just before kernel entry using the <enter> key. The debugger executes a popa.

Show TCB of current thread.

t<thread id>
Show TCB of thread <thread id>

9.7 Inspecting Mappings

To fill up its own virtual memory a task has to map dataspaces. You can inspect these mappings using the kernel debugger. There should be at least three mappings, the mapping of the standard dataspace and two mappings supplied by the kernel.

Show mappings of task of current thread. Thsi command provides information about the memory mode of the task, about io permissions and current mappings. The display looks like follows:
task:<task no>             (<number> Threads, Root:<i don't know>)
mode:<mem mode, io mode>                            Limit:<number>
                           maphw base:<number>
                           hwmap max :<number>  wrlck max:<number>
                           prsent lnk:
[number] : [<start>,<end>] <map mode> ds:<number> mvec:<address>

Values of entries:

mem mode
Memory mode can have the following values:

  • resident
  • pageable

io mode
Every task has its own io priviledges. That can be:

  • unlimited io
  • <no entry> - no io priviledges

map mode
Dataspaces can be mapped with two different modes, read write and read only. There is an additional mode, which is specified for kernel supplied mappings. There are no map vectors for standard dataspace and kernel supplied mappings.

  • rw - read write
  • ro - read only
  • XX - kernel area

m<thread id>
Show mappings of task of thread <thread id>.

Cursor Keys
Scroll through mappings.

9.8 Inspecting Memory

There are several mechanisms to inspect memory. You can inspect address spaces, search for hex and ascii strings, change memory contents and calculate the associated physical address for a given virtual address.

There are two major modes for inspecting memory, disabled task switching and enabled task switching. While task switching is disabled you can only inspect virtual addresses, which are currently in physical memory. To inspect any virtual address or any dataspace you have to enable task switching to allow the pager to transfer the necessary information from disk to memory.

Enter inspecting memory. Using i you enter memory inspection with disabled task switching, using I with enabled task switching.

Commands for inspecting memory:

Specify a thread id. Format: <thread id>i<enter>

Specify a length to be displayed. Format: <length>l<enter>

Specify an address display starts with. Format: <address>p<enter>

Redisplay block.

Go forward in steps of length l.

Go backward in steps of length l.

Set base address. Format: <address>:<enter>

Cursor Keys
Move within the displayed block. Once you have reached the desired position you can change contents of memory entering arbitrary values. By pressing <enter> you go back to normal memory inspection.

Search for memory contents. You can search for hex or asci strings.

xh<hex value>{<space><hex value>}*<enter>
Search for hex string <hex value>{<space><hex value>}*

xc<ascii string><enter>
Search for ascii string.

Repeat last search operation.

Invoke nested kernel debugger, for instance to inspect a tcb without leaving memory inspection. You can leave the nested kernel debug using key h.

Invoke spvertest. You can leave spvertest with the key h.

Additional commands if task switch is enabled (I).

Specify a dataspace id. Format: <dataspace id>s<enter>

Load a block from backing storage. ???

Write a block to backing storage ???

Access task root ???

Marion Schalm, Jean Wolter, Michael Hohmuth
26.12.1995 (unfinished)