The Whiteboard System
The BSD debugger is build around a whiteboard system (WBS). A WBS is closely related to a blackboard system used in artificial intelligence applications.
Metaphor
A group of specialists are seated in a room with a large blackboard. They work as a team to brainstorm a solution to a problem, using the blackboard as the workplace for cooperatively developing the solution. The session begins when the problem specifications are written onto the blackboard. The specialists all watch the blackboard, looking for an opportunity to apply their expertise to the developing solution. When someone writes something on the blackboard that allows another specialist to apply their expertise, the second specialist records their contribution on the blackboard, hopefully enabling other specialists to then apply their expertise. This process of adding contributions to the blackboard continues until the problem has been solved.
Why a WBS?
A debugger needs to collect a lot of information and while information is structured in most cases, there's a lot of variation.
- Executables are always ELF in FreeBSD nowadays, but this is may not necessarily be true in the future. What if you want to extend the debugger so that you can debug shell scripts or any other of the numerous interpreted languages? What if ELF is being replaced? What if you want to support COFF or AOUT (again)?
- Executables come in various shapes and forms: different architectures, different ABIs, different operating systems, statically linked, shared linked, etc. Some can be run on the host, while others can only be debugged by examining saved state. How do you code all these combinations? Do you want to code them all?
- Is source-level debugging possible? Do we have debug information? In what form do we have it?
- How to unwind the stack? What about signals or traps? How to do it for highly-optimized code?
- What does a context look like?
- What is stored in a core file?
- Threads? Static or dynamic TLS?
The WBS is an attempt to avoid having to know everything up-front and also to avoid that everything is entangled and thus that everything is hard-coded in software. Instead, the WBS records objects and associations and pieces of code get called whenever a change is made that is of interest to that code.
Example
The user opens the debugger and specifies "helloworld" as the program to debug. The WBS records the arrival of a new debuggee. The ELF file handler has previously announced it is interested in new debuggees, and as such gets called. The ELF file handler checks the target and finds it to be an ELF file and updates the WBS that an ELF file is being used. This triggers the execution of numerous ELF specific handlers, such as symbol table handlers, segment readers, section readers, DWARF parsers, unwind papsers. When a core file is loaded, specific handlers that know about the architecture and OS specifics get to take a look and take appropriate action by creating thread and context information, adding new ELF object files, etc...
None of this is explicitly coded. It's all based on updates to the WBS and having those updates trigger calls to functions/routines that can further update the WBS. With nothing hardcoded, everything can be supported by using plugins or extensions.