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 business plans allowed no alternative.

We designed, wrote and named a debugger after Peter Deutsch’s DDT, an ancestor of gdb. The semantics was a good fit. An instance of DDT recognized just one domain and did its job by holding the domain service key to that domain. The domain service key gives DDT all of the authority normally wielded over a subject program and most DDT commands translate directly to operations on the domain via the domain service key. DDT runs outside the address space of its subject. DDT syntax provides implicit access to the address segment of the domain in support of simple memory access of earlier debuggers. Also provided is simple syntactic access to the general key slots of the domain.

We will call the person who is debugging the code with DDT the user in this note.

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 serve 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 service 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. We invented the can opener.

Features

DDT has two modes between which the user chooses:
concurrent
where DDT and the domain are both running. In this mode you can repeatedly display the program counter and see it change.
sequential
where there is just one process between DDT and its subject. In this mode you can single step the code.
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 to DDT via the domain service key. DDT makes these functions conveniently available to the user.

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 separate key slots for arbitrary use by the user.

Problems

Domain service keys are not designed to be shared. Few domains require use of domain service keys except during creation, destruction and debugging. OS emulators use domain service keys and typically make arrangements with DDT to share the domain service key thru some ad-hoc protocol. Some domains invoke their own domain serve keys to create start keys, for example. These uses do not conflict with the DDT’s use. There is an order on the DDT object to produce a synthetic domain service key that will serve most purposes of the real domain service key.

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 service key to the inner object that executes the instructions of the confined program. This domain service key is the tool of debuggers as described above.

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


Here is some more contemporary information.
There was an Augment document giving the precise details of our debugger for the 370. It was lost.