Debugging Code in Keykos

In classic programming environments I am accustomed to a debugger that displays the stack and understands the name spaces defined by each of the stack frames. This pattern does not work for the Keykos application that is distributed in many Keykos objects running in separate address spaces and with no conceptual stack known to the system or debugger. A further problem in Keykos is that the programmer who is debugging code may not be authorized to see the implementation of other objects involved with the current state where the problem is manifested. OO languages typically assume that encapsulation is merely for limiting access by programs and that a programmer can ignore such barriers while debugging the code. In Keykos we took the opposite view and assumed that encapsulation was really to keep secrets, even from programmers, debuggers and DDT (gdb). We took this attitude with some trepidation, but saw no alternative.

We designed, wrote and named a debugger after Peter Deutsch’s DDT, an ancestor of gdb. The semantics was a good fit but the syntax was arcane. An instance of DDT recognized just one domain and did its work by holding the domain key to that domain. The domain key gives DDT all of the authority normally wielded over a subject program. DDT runs outside the address space of its subject. We will call the person who is debugging the code with DDT the user in this note. There is an order on the DDT object to produce a synthetic domain key that will serve most purposes.

DDT can start, stop and single step the program. The 370 has convenient hardware to set break points without modifying memory, and several other execution surveillance hooks. These are supported by the kernel and available via the domain key. DDT makes these functions available to the user in a convenient way.

There are two ways that a DDT can come into existence for a domain. The fundamental way is by a creator that would take as arguments:

The DDT begins to run and reports on the state of the domain without impacting it in any way as it may be well enough to server most of its clients.

Alternatively a production domain is likely to have a Virtual Domain Keeper (VDK) whose invocation causes a DDT instance to be created upon a domain fault. The window that this DDT gets depends on how the VDK was created. No programmer with authority to examine this code and state may be immediately available and at a terminal. Keykos is persistent and the situation would last until a suitable programmer appeared.

This hard nose attitude toward security was ameliorated by a strong bias towards unit test. The programmer could debug his new code assuming that the objects that it employed were older, well tested and performed to spec. Most objects upon which new application are built have orders (methods) to query state without changing it. DDT has commands to invoke objects available to the subject code. When foundation objects appeared to be broken one recourse is to send mail to the implementer of the object along with the start key to the domain holding the state in dispute. The programmer with legitimate access to the suspect domain would have access to its domain creator and, with synergy, derive the domain key, create a DDT and peer inside, perhaps to find a bug or explain why the state was valid.

This pattern was often too inconvenient and we invented families of domain creators for each of which there is a can opener. A can opener will try to open a domain by invoking each domain creator in the family. If it succeeds it instals a DDT and reports the real type (brander) of the opened object.

Features

DDT has two basic modes, synchronous and async, controlled by DTT upon command by the user. While in synchronous mode only the DDT or the domain runs at one moment. In async mode they might run simultaneously. In async mode the user can repeatedly display the program counter and observe it changing as the domain runs. To single step, the user must first command DDT to enter synchronous state.

DDT can place the domain is a state where it stops upon each key invocation. A canned command sequence will then display the message of the invocation. Single stepping the domain at that point allows display of the returned message.

DDT includes a rudimentary yet general command system. It can invoke a directory that it was born with which might be the user’s personal directory. DDT provides a few general slots to keys for use that the user may wish.

Problems

Domain keys are not designed to be shared. Few domains require use of domain keys except during creation, destruction and debugging. OS emulators use domain keys and typically make arrangements with DDT to share the domain key thru some ad-hoc protocol. Some domains invoke their own domain keys to create start keys, for example. These uses do not conflict with the DDT’s use.

Debugging Factory Yields

Novel problems arise while fixing bugs in confined programs. These are inherent in the proprietary considerations that lead to using factories. When the user of the code is unwilling to reveal the data that breaks the code, he should not allow the programmer access to the state of the broken object. When the user can find similar input that is not secret and that breaks the program, he may send his object handle to the programmer. This handle is typically a start key to the object which is a capability used by the user to deliver messages to the confined object. The programmer now has effective access to the state of the object. The programmer retains the builder’s key to the factory which he can invoke passing the start key. Synergy allows the factory to reproduce the original domain key to the inner object that executes the instructions of the confined program. This domain key is the tool of debuggers as described above.

The introduction to a note on layering covers some of the debugging patterns we used.