Source-Level Debugger v1.2 Purpose The MOO Source-Level Debugger is a server patch which makes available somewhat "internal" information from the MOO virtual machine. It includes a Direct Stack Access-interace compliant set of functions as well as several functions allowing step-by-step execution of verbs. The system is designed to provide an interface through which a MOO source-level debugger, containing many of the standard features of source-level debuggers, without violating the MOOcode abstraction. The access allowed by this interface does not provide any information about the system underlying the virtual machine executing the MOOcode. Overview The MOO Source-Level Debugger has two sub-parts. The first is interface-compatible with the Direct Stack Access extensions released previously by the author. The second is a set of verbs allowing fairly sophisticated control over the execution of a task. The Direct Stack Access extension provided a function which allowed a programmer to get a list of all variables in all frames of a running task. For those unfamiliar with the terminology, each time a verb begins executing, a "frame" is created for it on a "stack". Thus, if a command-line verb calls a second verb with calls a third verb, there are three frames on the stack for that particular task. All of the variables used by a verb are stored in its frame on the stack. Other functions allowed players to alter the values of variables in running tasks or to undefine variables in running tasks. The novel verbs in the Source-Level Debugger allow one task to resume a suspended task for one line of execution only. Each time the task is resumed in this way, it will execute one line of source code and re-suspend indefinitely. The task may also be resumed such that it will stop execution before entering any verb called by the current verb (a "step-in") or to complete the execution of the current verb and suspend before executing any code from the calling verb (a "step-out"). Interface task_frames() task_frames(INT) Both formats return lists of stack frames for a given task; the first format returns a stack frame list for the current task, while the second returns the stack frame list for a the task whose ID is passed in the first argument. The returned value is a list of lists. Each element of the returned list represents one stack frame, with the topmost frame of the stack being the first element of the list. Each stack frame is a list of three-member lists. The first element of each list is the name of the variable. The second is the type of the variable, with a 6 in this position representing the internal server type TYPE_NONE given to undefined variables. The third element is the current value of the variable. This third element is a random value of type INT for currently undefined variables. The list returned by task_frames() is similar to the one below: ;task_frames() => { { {"NUM", 0, 0}, {"OBJ", 0, 1}, ...}, { {"NUM", 0, 0}, ...}, ...}; task_frames() can also return E_INVARG if the specified task does not exist or E_PERM if the specified task is owned by another player and the player is not a wizard. alter_variable(INT, INT, INT, ANY) alter_variable(INT, INT, STR, ANY) The two formats of alter_variable() perform the same task. They alter the value of a variable in a given frame of a given stack. The stack is specified by the first argument, which is either a valid task ID or 0 to act on the current stack. The second argument specifies which frame of the stack to operate on. The currently-executing frame is 1, with verbs further down in the stack being represented by higher numbers. A given frame's level is its index in the list returned by task_frames(). The third argument is either a number or a string. If it is a number, then the function affects the n-th variable in that frame, counting in the order returned by task_frames(). If it is a string, the variable with that name is altered. The fourth argument is the value to which the specified variable should be set. In the above example, the following would all be valid, though perhaps not a wonderful idea: ;alter_variable(0, 1, 1, "Test...") ;alter_variable(0, 1, "NUM", "Test...") ;alter_variable(0, 2, "NUM", "Test...") alter_variable() can return E_PERM if the caller is not a wizard, E_VARNF if the specified variable index is out of range or the named variable does not exist, E_RANGE if the specified stack frame does not exist, or E_INVARG if the specified task does not exist. undef_variable(INT, INT, INT) undef_variable(INT, INT, STR) The two formats of undef_variable() undefine a given variable in a given frame of a given stack. The stack to be examined is specified by the first argument, which is either a valid task ID, in which case the given task's stack is affected, or 0, in which case the current stack is the target. The second argument is the index of the stack frame to affect, with 1 being the top and higher numbers representing lower levels of the stack. The third argument is either the integer index of the variable to be undefined, as returned in task_frames(), or a string representing the name of that variable. In the above example, the following would all be valid (though they probably ought to return E_BADIDEA...): ;undef_variable(0, 1, 2) ;undef_variable(0, 1, "OBJ") ;undef_variable(0, 2, "NUM") undef_variable() can return E_PERM if the caller is not a wizard, E_VARNF if the specified variable index is out of range or the named variable does not exist, E_RANGE if the specified stack frame does not exist, or E_INVARG if the specified task does not exist. task_debug(INT, INT) task_debug() returns the debugging state of a given frame of a given task. That is, if the verb executing in the specified task was +d when it was called, this returns true; otherwise it returns false. The first argument specifies the task to be examined. It must be either a valid task ID or 0, with the latter indicating that the current task is to be used. The second argument is the index of the stack frame to examine, with 1 being the top and higher numbers representing lower levels of the stack. task_debug can raise E_INVARG if the task ID specified does not exist, E_PERM if the caller does not own the task in question and is not a wizard, or E_RANGE if the frame specified does not exist. set_task_debug(INT, INT, ANY) set_task_debug() sets the debugging state of a givenframe of a given task. A given verb in a given task may be set to be +d or -d regardless of the debugging state of the verb being executed. As this could open security holes not immediately apparent to the author, it is restricted to wizards. The first argument specifies the task ID of the task to be altered, or may be 0 to alter the current task. The second arguent is the index of the stack frame to change, specified in the standard manner described above. set_task_debug can raise E_PERM is the caller is not a wizard, E_INVARG if the specified task does not exist, or E_RANGE if the frame specified does not exist. task_freeze(INT) task_freeze() alters a suspended task such that it will never continue execution unless un-suspended with a resume(), or one of the task_step() functions below. The first argument is the task ID of the suspended task. task_freeze can return E_PERM if the player is not a wizard and does not own the task in question. It can also return E_INVARG if the task ID specified does not exist. task_step(INT) task_step() causes a task to resume until it reaches the end of the current line of code. It will then suspend indefinitely. If the code was suspended with a suspend() built-in, the return value will be zero. The first argument specifies the task ID of the suspended task to be stepped. task_step can return E_PERM if the programmer is not a wizard and does not own the task in question. It can also return E_INVARG if the task ID specified does not represent a currently suspended task. task_step_in(INT) task_step_in() acts very much like task_step, except that it will not execute any verbs called by the current verb. Instead, it will prepare itself for the execution of the verb being called by the current verb and suspend before executing any code in the new verb. task_step_in can return E_PERM if the programmer is not a wizard and does not own the task in question. It can also return E_INVARG if the task ID specified does not represent a currently suspended task. task_step_out(INT) task_step_out() causes a task to resume and complete the execution of the current verb. It will then suspend after destroying the frame of the completed verb but before executing any code in the calling verb. If used on a command-line verb, it will cause the task to complete. task_step_out can return E_PERM if the programmer is not a wizard and does not own the task in question. It can also return E_INVARG if the task ID specified does not represent a currently suspended task. task_code(INT[, INT[, ANY[, ANY]]]) task_code() operates much like verb_code(). Its first argument specifies the task ID whose code is to be retreived. This may be zero, to specify the current task. The second argument specifies the frame of the task whose code is to be retreived. If this is omitted, the upper-most frame (the one currently executing) is selected. The third and fourth arguments control parentheses and indentation, respectively, as they do in verb_code(). task_code can return E_PERM if the programmer is not a wizard and does not own the task in question. It can also return E_RANGE if the specified frame does not exist. eval_suspended(STR) eval_suspended is in virtually every respect identical to the eval() built-in function. The only difference is that it causes the task to be infinitely suspended precisely before the point at which it would execute the code within the string to be evaluated. debugger_version() debugger_version returns a version string indicating the version of the MOO SLD being used. This string is identical to the version string displayed in the server log on server startup. Compatibility and Stability This server patch has been tested under a wide variety of conditions and on a wide variety of systems, including MkLinux DR2.1u4 (Linux 2.0.30) on a PowerPC, SusOS 5.5.1 on a Sparcstation 5, ULTRIX 4.1 on a Decstation 3100, HP-UX 9.03 on an HP-730 (numerous compilation warnings for the server itself, but it passed the tests--it may have been local header files), and Irix 5.3 on an SGI Indy. While it may not be bug-free, it should be stable enough for use in a production setting. Due to changes in the data structures for suspended tasks, the format of the database has been changed slightly. The databases are compatible under certain specific circumstances. All standard 1.8.0p6 databases can be read by the 1.8.0p6+debugger server. A 1.8.0p6+debugger database can be read by the 1.8.0p6 server provided no suspended tasks in the database were most recently suspended by task_step(), task_step_in(), or task_step_out(). The server normally suspends tasks at certain, narrowly-defined locations. The debugger suspends tasks at other locations. Tasks suspended at these other locations will prevent the database from being loaded by a non-debugging server. Contact Information The author, Nicholas Ingolia, may be reached by email at: ingolia at mit dot edu Information about this code can be found at http://web.mit.edu/ingolia/www/SLD.html Copyright Copyright (c) 1997 Nicholas Ingolia. This software is based on the LambdaMOO server. The copyright information for this server is included below: /****************************************************************************** Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved. Portions of this code were written by Stephen White, aka ghond. Use and copying of this software and preparation of derivative works based upon this software are permitted. Any distribution of this software or derivative works must comply with all applicable United States export control laws. This software is made available AS IS, and Xerox Corporation makes no warranty about the software, its performance or its conformity to any specification. Any person obtaining a copy of this software is requested to send their name and post office or electronic mail address to: Pavel Curtis Xerox PARC 3333 Coyote Hill Rd. Palo Alto, CA 94304 Pavel@Xerox.Com *****************************************************************************/ The copyright information for this code: This software may be freely distributed provided this information accompanies it. Derivative works based on this software may be freely created and distributed provided this copyright information is included and the derivitive work is clearly identified as such. This software is made freely available and the author makes no warranty, express or implied, about its performance, reliability, compliance with any standard, or fitness for any purpose.