Abstraction based debugging is an experimental feature provided The Concert Debugger . The idea is that object oriented programs often contain one or more collections of objects that co-operate to provide some functionality through a well defined interface. A distributed priority queue implemented with an aggregate is a stock example of this notion; such groups of objects are called abstractions.
Localizing program errors is the name of the game in debugging, and abstraction provide an attractive, high-level way to do this. The correctness of an abstraction can be determined simply by observing the requests that come across its interface, and its responses to those requests. If an abstraction gives exclusively correct answers to requests, it is not the cause of whatever bug is being tracked; if it provides wrong answers, there must be a problem within the abstraction. Thus, if a program abstraction can be instrumented to display all its requests, paired with the responses to them, a bug can be quickly localized to inside or outside that abstraction.
The Concert Debugger provides such instrumentation with the concert abstraction family of commands; these provide the ability to define the interface to an abstraction, and observe the requests across that interface. It is not necessary to specify the entire abstraction; just the interface is enough to allow the debugger to monitor the abstraction. The following commands are available.
An example abstraction based debugging session is shown below. To make clear what monitoring the interface means, the source code for the buffer class is also show. Note how only interface methods are shown in the abstraction request trace, so isfull is not shown since it is used internally; also, while the reply to the request comes from a different object, abstraction monitoring finds it anyway. Further note that the numbers on the left side of the transcript are program output, not abstraction debugging messages.
;; retrieves an item if the buffer is filled
(method tiny_buffer get ()
(if (isfull self)
(conc
(forward (stuff (loc self)))
(set_full_flag self 0))
(reply EMPTY)))
;; inserts an item when the buffer is empty
(method tiny_buffer put (item)
(if (isfull self)
(reply FULL)
(let ((req requester))
(conc
(set_full_flag self 1)
(do (alter_value (loc self) item req))))))
(cadb) concert current
(4b2310) method osystem initial_message ()
at simple-buffer-example.ca:1211
buf = <object: 0,4b2210>
buf2 = <cwait: 0>
val2 = <object: 1,75b050>
(cadb) concert abstraction new buf
abstraction monitor 1 for (4b2210) object of class tiny_buffer
(cadb) c
Continuing.
request 1: (4b2410) method tiny_buffer put (item = 3) --> <int:3>
request 2: (4b2410) method tiny_buffer get () --> <int:3>
3request 3: (4b2410) method tiny_buffer put (item = 4) --> <int:4>
request 4: (4b2410) method tiny_buffer get () --> <int:4>
...
27request 52: (4b2410) method tiny_buffer put (item = 28) --> <int:28>
request 53: (4b2410) method tiny_buffer get () --> <int:28>
28request 54: (4b2410) method tiny_buffer put (item = 29) --> <int:29>
request 55: (4b2410) method tiny_buffer get () --> <int:29>
29request 56: (4b2410) method tiny_buffer put (item = 30) --> <int:30>
request 57: (4b2410) method tiny_buffer get () --> <int:30>
30request 58: (4b2410) method tiny_buffer put (item = 31) --> <int:31>
request 59: (4b2410) method tiny_buffer get () --> <int:31>
31request 60: (4b2410) method tiny_buffer put (item = 32) --> <int:32>
request 61: (4b2410) method tiny_buffer get () --> <int:32>
32<object: 2,d1e110>
done