This section shows some simple examples of using the debugger included in the Concert emulator (see [6] for a complete listing of the debugger functionalities).
When running a CA program using the emulator, the emulator invokes the debugger under the following four situations.
The debugger has its own debugger> prompt, as shown below.
Running the program... User defined-break in (FUNCTION . break) debugger>
Once inside the debugger, you can use the help command to display the available commands.
debugger> help Debugger commands object - prints current object goto-local - sets current object to specified local variable goto-param - sets current object to specified parameter goto-ivar - sets current object to specified instance variable goto-sibling - moves to a sibling goto-array - goes to an specified array element local - prints letbound vars current - display current executing method parent - prints the parent context children - prints all the children up - goes up the context tree down - goes down the context tree backtrace - lists all the predecessors of the current context original - go to position at which debugger was entered blocked - list all blocked messages goto-blocked - goto a blocked context unprocessed - list all unprocessed messages goto-message - goto an unprocessed message waiting - list all waiting contexts goto-waiting - goto a waiting context waiting-for - displays the method the current context is waiting for continue, c - continue execution top-level - go to top-level untrace-all - disable all tracing trace-all - enable tracing of all functions exit, quit - terminate execution & go to top-level debugger> exit concert>
The exit command terminates the current program and exits the debugger. To correct an error and continue execution, you can use the top-level command to return control temporarily to the emulator top level, make the corrections, and use the exit command from the top level to return to the debugger. The continue command resumes the program execution.
A common error made by beginner CA programmers is omitting the reply form in a method. We show an example of using the emulator debugger to detect such an error. The program shown below, for example, does not reply from the initial_message method.
(method osystem initial_message () (HELLO (global console)))
Because the initial_message method does not reply, its caller is permanently waiting for a reply. Thus, when running the program, the emulator detects a deadlock and enters the debugger.
Running the program... HELLO Deadlocked with 0 blocked messages and 1 waiting contexts debugger>
Blocked messages are method invocations trying to gain exclusive access to objects; waiting contexts are contexts waiting for replies. We can use the debugger commands current and waiting to display the current method invocation context and all method invocations that are waiting for replies, respectively.
debugger> current
initial_message <|_osystem| {182C93FD}>
debugger> waiting
0 ::_main <|_rootclass| {1824FC15}>
debugger> goto-waiting
Waiting method :0
_main <|_rootclass| {1824FC15}>
debugger> waiting-for
Waiting for initial_message
debugger>
As shown above, we used goto-waiting to jump to the single waiting context - an invocation of the main method, a builtin method that start the program execution and invokes initial_message. We verified that it is waiting for a reply from the initial_message method. This gives us sufficient hints to check if initial_message has a reply. We can return to the emulator prompt by the top-level command.
In this section, we use a simple aggregate example to illustrate some more emulator commands. The following program allocates an aggregate of seven representatives and then increments the value of all representatives by 1.
(aggregate int-set value
(parameters size)
(initial size
(forall i from 0 below groupsize
(set_value (sibling group i) i))))
(handler int-set inc ()
(let ((old (value self)))
(set_value self (+ 1 old))
(OLD_VALUE (global console) old)
(reply old)))
(handler osystem initial_message ()
(let ((myset (new int-set 7)))
(forall i from 0 below 7
(inc (sibling myset i)))
(reply STARTED)))
Running the program using the emulator produces the following output.
Running the program... OLD_VALUE 0 OLD_VALUE 1 OLD_VALUE 2 OLD_VALUE 3 OLD_VALUE 4 OLD_VALUE 5 OLD_VALUE 6 STARTED Evaluation took: 4.23 seconds of real time 1.82 seconds of user run time 0.25 seconds of system run time 2 page faults and 924800 bytes consed. concert>
To check the intermediate state of objects, we can insert a (break) function in the method inc and load the modified function into the emulator at the concert> prompt.
concert> (method int-set inc ()
(let ((old (value self)))
(set_value self (+ 1 old))
(break)
(OLD_VALUE (global console) old)
(reply old)))
Input filename: "/tmp/concert2971374993"
Reading: /tmp/concert2971374993...
...Closing: /tmp/concert2971374993
Defining method inc for int-set
concert>
The program now encounters breakpoints when restarted using the go command.
concert> go
Building the automatic functions....
Defining method _main for rootclass
Running the program...
User defined-break in (FUNCTION . break)
debugger> current
(FUNCTION . break) <|_int-set| {1036AEAD}>
debugger>
When the break function is encountered, execution suspends within the break function. As shown below, we can move to the calling frame of the break function using up. up also displays the message associated with the new context, in this case, an invocation of the inc method on a representative of the inc-set aggregate. Once we are at the parent context, local displays all the values of local variables. In our example, local shows the value of the local variable old is 0. To verify the operation of our program, we use the object command to display all instance variables of the current representative. For a representative of an aggregate, groupsize is the size of the aggregate to which the current object belongs, and myindex is the index of the current representative. We can continue the program execution via the c command.
debugger> parent
inc <|_int-set| {1036AEAD}>
debugger> up
inc <|_int-set| {1036AEAD}>
debugger> local
old :: 0
debugger> object
group :: <AGGREGATE {1036AC8D}>
groupsize :: 7
myindex :: 0
value :: 1
debugger> c
OLD_VALUE 0
User defined-break in (FUNCTION . break)
We can also move to a different aggregate representative using the goto-sibling command, which prompts for an index (starts with 0). As shown below, we moved to representative 5 and 0 and displayed their instance variables. Notice that the current context does not change after the goto-sibling command.
debugger> up
inc <|_int-set| {1036B0DD}>
debugger> local
old :: 1
debugger> object
group :: <AGGREGATE {1036AC8D}>
groupsize :: 7
myindex :: 1
value :: 1
debugger> goto-sibling
sibling number: 5
debugger> object
group :: <AGGREGATE {1036AC8D}>
groupsize :: 7
myindex :: 5
value :: 5
debugger> local
old :: 1
debugger> goto-sibling
sibling number: 0
debugger> object
group :: <AGGREGATE {1036AC8D}>
groupsize :: 7
myindex :: 0
value :: 1
debugger> c
OLD_VALUE 1
User defined-break in (FUNCTION . break)
...
The emulator is designed to withstand errors in user programs; however, an unanticipated error in user programs may crash the the emulator. When this happens, the emulator enters the CMU lisp debugger, as shown below.
Error in function UNIX::SIGBUS-HANDLER: Bus Error at #x314ADC.
Restarts:
0: [CONTINUE] Return from BREAK.
Debug (type H for help)
(UNIX::SIGINT-HANDLER #<unavailable-arg>
#<unavailable-arg>
#.(SYSTEM:INT-SAP #xEFFFDDF8))
0]
If this does happen, you can use the command (QUIT) (notice all capitals) to abort the emulator. It is also appreciated if you mail the program that caused the crash to concert-bugs@red-herring.cs.uiuc.edu (see section 10 for using the bug reporting package).