Help Topics > Table of Contents

# The TADS Debugger

## Overview

This is an overview of the TADS Debugger for Windows. The Debugger on Windows is part of the TADS Workbench, which integrates the TADS compiler and debugger into a single environment.

The Windows version of the debugger is simply a port of the original TADS Debugger ("tdb") engine to Windows, so it has essentially the same features as the DOS version. However, the Windows version has a substantially updated user interface that takes advantage of Windows; the graphical UI makes the debugger easier to use and more flexible than the character-mode DOS version.

In addition, the Windows debugger works with the HTML-enabled TADS interpreter, so you can debug games that take advantage of HTML features. Of course, you can also use the Windows debugger with traditional TADS games; this version of the debugger (like HTML TADS itself) is fully compatible with TADS games that don't use HTML.

This overview is intended primarily for people who already know how to use another version of the TADS Debugger, such as the DOS version. The core functionality of the Windows debugger is the same as that of the debugger on other platforms, and the concepts are all the same.

If you're not already familiar with the TADS Debugger, the TADS Author's Manual describes the debugger in detail in Appendix G.

## Summary of Windows features

For the most part, the Windows debugger works like the DOS version. However, the Windows debugger has a few features not found in the DOS version:

- A graphical user interface
- Per-game configuration saving to keep track of your window layout, breakpoints, and watch expressions between sessions
- Continuous updating of the call stack display as you step through code
- A local variables window that shows the current set of local variables and their values, continuously updated as you step through code
- Expandable variable displays that let you open a composite value (such as a list or an object) to see the contents of the value. You can even open a composite object contained within another composite object, to almost any depth that you desire.
- Type-in variable updating. To change the value of a variable, you can simply click on the value display (in the local variables or watch expressions windows) and type in a new value.
- Tooltip evaluation. When you let the mouse cursor hover for a moment over a variable in a source code window, the debugger automatically shows you the value of the variable in a little floating window.
- The ability to reload the game without exiting and restarting the debugger. This allows you to correct a problem that you find in your game using the debugger, edit your source code to correct the problem, recompile your game, and then test the fix, all without having to start a new debugger session.
- The ability to load another game into the debugger without exiting and restarting the debugger.
- Docking windows to customize the display layout.

## Display Layout

The DOS version of the debugger divided the screen into two main panels: the source area at the top of the screen, and the command line area at the bottom of the screen. An additional panel appeared in the middle of the screen to show watch expressions when needed. This layout was fixed: you couldn't change the arrangement of windows.

In addition, the DOS version switched the entire display back and forth between the debugger and the game screen. When the game was active, the game completely took over the display screen; when the debugger was active, the game screen was entirely replaced with the debugger screen. The debugger also had a command that let you manually switch to the game screen while the debugger was active.

The Windows version of the debugger uses windows (naturally) for the different display areas. These windows come and go as needed, and you can move and resize them to lay out the screen as you like. The game window and the debugger windows can share the screen, so the screen switching that the DOS version did is unnecessary in the Windows version.

The debugger uses the following types of windows:

- The game window. This is the normal window that displays your game as it executes.
- The control window. This is the main debugger window; it has a menu and a toolbar. Most of the debugger commands are available in the control window's menu. The toolbar has buttons for the most common commands.
- A debug log window. This window shows error messages from the HTML parser, and messages from the debugger.
- One or more source windows. Unlike the DOS version of the debugger, which can show only one source file at a time, the Windows version can show as many source files as you want; each file is opened in its own window.
- A stack window. This window displays the current call stack, and is kept updated as you step through your game's code. With the DOS debugger, you had to ask for the current call stack using the "k" command; with the Windows version, you never have to ask for a stack listing, because it's always kept up-to-date in the stack window.
- A local variables window. This window shows the current local variables and their values, and is updated automatically as you step through your code. In addition, when a variable contains a composite value (such as a list or an object), you can expand the variable's display to show the contents. This window also has a separate panel, which you can access through the control tabs at the bottom of the window, that shows the current "self" object and its contents.
- A watch expressions window. This window is similar to the watchpoint window in the DOS version, in that it displays a set of variables and their values that you want to keep updated as you step through code. The watch window actually has four separate panels, which you can switch among using the tabs at the bottom of the window. The panels are provided so that you can organize the variables in the watch window in whatever arrangement is most convenient to you. For example, you might find yourself debugging two or three different functions in the course of a debugging session, each each function may have its own set of expressions that you're interested in watching while stepping through that function. You could use the watch window tabs to group these sets of variables, and select the appropriate tab in the watch window for the current function.
- A call history window. This windows shows the call history that the debugger collects if you turn on call tracing.

## Entering Commands

With the DOS version, you entered commands to the debugger by typing them on the debugger command line. In addition, certain commands could be entered using function keys.

The Windows version, in contrast, has no command line. Instead, you enter commands using the control window's menu or toolbar, or using function keys, or using context menus.

Here's a brief listing of the Windows version's equivalents of the commands from the DOS version.

Ctrl-Home (show next line to be executed). Use the "Show Next Line" item on the "Debug" menu. The debugger will bring the appropriate window to the front and show the correct source line, with a little arrow in the left margin pointing to the line. You can also double-click on the current line (which is always the first line) in the stack window.

You can also go to a line in an enclosing stack frame by double-clicking on the line in the stack window. This will show the source line with a triangular green arrow (to indicate that it's the current line in an enclosing frame, not in the active frame), and will also activate the enclosing frame's context, which means that you can evaluate expressions using the frame's local variables, and that the debugger will display the frame's local variables in the "Locals" window.

g (Go).. Use the "Go" item on the "Debug" menu in the control window, or use the F5 key, or use the "Go" button in the control window toolbar.

t (Trace). Use the "Debug/Step into" menu item, or use the F11 key, or use the "Step into" button in the control window toolbar.

p (Procedure trace, or step over). Use the "Debug/Step over" menu item, or use the F10 key, or use the "Step over" button in the control window toolbar.

e (Evaluate) To evaluate an expression, select the "Evaluate" item on the "Debug" menu, or press Ctrl+E. The debugger will open a dialog that lets you type in an expression to evaluate. Type the expression and hit return, and the debugger will show you its value in the right column of the list box in the dialog. To type in a new expression, click on the expression in the left half of the list box; this opens the expression for editing. Type in a new expression and hit return, and the debugger will update the value displayed with the value of your new expression. Press the "Close" button when you're done with the dialog.

To make expression evaluation faster, the debugger automatically types in an expression for you according to the selected text in the active source window before opening the dialog. Activate a source window, use the mouse to place the caret on the name of a variable you want to evaluate, and press Ctrl+E (or use the menu) to open the dialog. Or, use the mouse to select a range of text containing an expression you want to evaluate, then open the dialog. The dialog will start off showing the expression you selected in the source window.

If the expression evaluates to an aggregate value, such as an object or a list, a small box with a plus sign will appear to the left of the expression. Click this to expand the object or list. When expanded, the list will show each property of the object or each item in the list.

If an expression can be assigned into (for example, if the expression is a local variable or a property of an object), you can edit the value of the expression. Click on the right half of the window, where the value is showing; the debugger will open the value for editing. Type in the new value that you want to set and press return; the debugger will immediately set the new value. This works both in the main expression and in the contents of an aggregate value.

The debugger also has something called "tooltip evaluation." This works like the little tooltip messages that appear in many Windows applications when you let the mouse hover over controls such as toolbar buttons. With tooltip evaluation, the debugger automatically displays the value of a variable or other expression when you let the mouse cursor hover for a few moments over the variable name in a source window.

By default, the debugger tries to be smart about picking the expression to evaluate:

- If you hold the mouse over a parenthesis ("()") or square bracket ("[]"), the debugger looks for the matching delimiter, and uses the whole stretch between (and including) the delimiters as the source text to evaluate.
- If the text found with the first rule begins with "[", and the preceding character isn't an operator of some kind, the debugger extends the expression to include the part before the "[", using the first rule above to figure out its limits. (So, for example, a parenthesized expression just before the "[" will be included in whole.) Otherwise, if the character before the result so far is ".", the debugger extends the expression to include the part before the ".", again using the first rule.
- The second rule is then applied repeatedly as long as there are more prefix expressions to include.

The point of the second rule - we call it the "prefix" rule - is that the "suffix" part of an index expression ("lst[i]") or object property evaluation ("obj.prop") is usually uninteresting on its own - you usually want to see the result of the entire "lst[i]" or "obj.prop" expression rather than just the "i" or "prop" part. The prefix rule makes it easy to evaluate the whole expression. In the case of an index expression, point to either of the brackets, and the debugger will evaluate the whole "lst[i]" expression. In the case of an object property expression, point to the property name, and the debugger will evaluate the whole "obj.prop" value.

The nice thing about working "backwards" from the suffix part of this kind of expression is that you can still easily evaluate the prefix part on its own, just by pointing to it. The prefix part of these expressions is often as interesting on its own as the whole expression is. The prefix rule makes it easy to evaluate the prefix on its own, or the entire expression with the suffix.

Note that you can override the "smart" behavior to evaluate any expression you want, simply by using the mouse to select the text you want to evaluate and then letting the mouse hover over the selection for a moment. The debugger will ignore its standard rules and instead use the entire highlighted range you selected.

Note that tooltip evaluation will never evaluate an expression involving an assignment or a method or function call; this is because it would be too dangerous to have the debugger changing your game state just because you were moving the mouse over a source window. Tooltip evaluation is also limited by the fact that you can only use it to look at expressions that are already in your source files. It's not a substitute for the other evaluation mechanisms, but tooltip evaluation is often a convenient way to get a quick look at a variable or two in the current function.

ws, wd (set/delete watch expression). Watch expressions work much as they did in the DOS version, but it's simpler to add, change, and remove expressions. Rather than using commands, you can edit the expressions directly in the watch window itself.

To add an expression, go to the "Watch Expressions" window, and click on the blank line at the bottom of the list. This will highlight the line. Now click again in the left column; this will activate the column so that you can enter an expression. Hit return when you're done typing in the expression, and the debugger will immediately show you the value in the right column.

To remove an expression, click on the line containing the expression to highlight it, then press the Delete or Backspace key. The expression will disappear from the list.

To modify an expression, click on the line containing the expression, then click on the left column to activate the expression for editing. You can now change the expression or type in an entirely new expression.

Note that the watch window has the same features as the expression evaluation dialog, so you can open aggregate values to see their contents, and you can directly edit the values of the expressions.

In addition to the watch window, the debugger provides a "Locals" window, which shows the set of local variables for the current function or method. This window works very much like the watch window, except that the debugger automatically determines the current set of local variables, so you don't have to add them manually as expressions.

c (Call history). You can turn call history tracing on and off using the "Collect Call Trace" item in the "Debug" menu. When the debugger is collecting call history information, it will show this menu item as checked; selecting the menu item will switch it from off to on or from on to off. You can clear the contents of the call history log by selecting the "Clear Trace Log" item in the "Debug" menu.

Use the "Call Trace History" item in the "View" menu to open the history window. This window shows the current call history log; the debugger updates this window automatically as you step through your code.

bp, bc (Set/clear breakpoint). The Windows debugger provides three ways of setting or clearing a breakpoint on a source line:

- Open the source window containing the code where want to set the breakpoint, click on the line where you want the breakpoint set, and then use the "Set/clear breakpoint" item from the window's context menu (which you can access by clicking the right mouse button).
- Click on the line where want the breakpoint, and press the F9 key or select the "Set/clear breakpoint" item from the main frame window's "Debug" menu.
- Click in the gray left margin area of the source line.

To set a global breakpoint (a breakpoint that's not associated with a line of code, but instead stops execution when a condition becomes true), select the "Edit breakpoints" item from the "Debug" menu. This will show the breakpoints dialog, which lists all of the breakpoints you currently have set. Click on the "New global" button, and type in the global expression condition that you want to use for your global breakpoint.

be, bd (Enable/disable breakpoint). Click on the line containing the breakpoint to enable or disable, and use the "Enable/disable breakpoint" item from the window's context menu.

You can also enable and disable breakpoints from within the breakpoints dialog. Select "Edit breakpoints" from the "Debug" menu; this will open a dialog that shows the list of breakpoints currently set. Select a breakpoint in the list, and click the "Enable" or "Disable" button (note that this is a single button -- it changes its function depending on whether the selected breakpoint is already enabled or disabled, so that you can reverse that status).

bl (List breakpoints). For the most part, you can see breakpoints directly (each one is marked by a red dot, or a white dots when it's disabled, in the left margin of the source window showing the file with the breakpoint). However, global breakpoints (breakpoints that are set only on expressions) obviously don't show up this way, and it's sometimes handy to be able to see all code breakpoints in one place, so the debugger still lets you get a list of breakpoints.

To see all current breakpoints, select the "Edit Breakpoints" item from the "Debug" menu. This will display a dialog that lists all of the breakpoints.

Code breakpoints are shown with the file that contains the breakpoint and the character offset within the file of the line with the breakpoint. (Note that the offset is given in terms of characters, not line numbers.) Global breakpoints are shown simply as conditional expressions.

fl (List source files). The Windows debugger doesn't have an explicit command to list source files. Instead, you can simply bring up the File menu, and select the "Open Source File" item; this item will show a submenu that lists all of the source files in your game. To open one of these files, simply select the file from the submenu.

fv (View file). To open one of the source files in your game, bring up the File menu, select the "Open Source File" item, and select the file you want to open from the submenu. To open any other text file, select "Open..." from the File menu, and select the file you want to open using the normal Windows file dialog.

quit (Terminate debugging session). Select the "File/Quit" item from the control window's menu, or simply close the control window. The debugger will prompt for confirmation, since this will terminate the game and the debugger session.

k (Show call stack). The call stack is always displayed in the stack window, and the display is updated automatically as you step through your code, so the Windows debugger doesn't need a command to show the current stack. However, the "View/Stack" item on the control window's menu lets you re-open the stack window if you closed it, or simply bring the stack window to the foreground if you've lost it behind other windows.

\ (Show game screen). Since the game window and the debugger windows share the screen, there's no screen switching as there was in the DOS version. However, the "View/Game window" item on the control window's menu lets you bring the game window to the foreground if you've lost it behind other windows.

h (Toggle hidden output). Use the "Show hidden output" item on the "Debug" menu to control hidden output. When this menu item is checked, TADS will show hidden output in the game window, with special marker lines that indicate that the output was meant to be hidden. When this item is not checked, TADS keeps hidden output invisible, as it would during normal play.

/ (Search). Select the window that you want to search, then select the "Find" item on the "Edit" menu to bring up the search dialog. Type the string you want to find and click the "Find Next" button. The debugger will find the text and highlight in the source window.

To search again for the same string, use the "Find Next" item on the "Edit" menu (or just press function key F3). This will search again for the same string, starting at the current selection point in the window.

u, d (Change stack context levels up/down). To set the expression evaluation context, go to the stack window and double-click on the line in the stack that contains the context you want to set. The debugger will automatically open the source file containing that call context, and display a green arrow pointing at the line of code at which execution will resume when execution returns to that context level. (The green arrow will also be displayed in the stack window next to the same context level.) Expressions will now be evaulated in the context of the code at the green arrow, so you can access its local variables. Note that the evaluation context always reverts to the current line when you step to a new line.

When you've selected an enclosing frame, any expressions you evaluate will be evaluated in the selected frame's context, which means that you can access its local variables. In addition, the "Locals" window will show the selected frame's local variables, and the "Watch Expressions" window will evaluate its expressions in the selected frame's context.

## New Commands

The Windows version of the debugger has several commands that the DOS version does not provide. Here's a brief explanation of the new commands.

Break into Debugger. This command (on the "Debug" menu), which is only active when the game is running, lets you switch back to the debugger without trying to coerce the game into reaching a breakpoint or a call to debugTrace(). This can be especially useful if you haven't coded a debugTrace() call in your game.

Run to Cursor. Position the cursor on a line of source code, then select this command (which you can access through the "Debug" menu, the right mouse button context menu in the source window, or by pressing function key F4). Execution will resume until it reaches the line of code that you selected (or until it reaches a breakpoint). This command is equivalent to setting a breakpoint on the selected line, resuming execution with the "Go" command, then clearing the breakpoint, but is obviously more convenient.

Set Next Statement. This command is accessed through the "Debug" menu, or through the context menu on the right mouse button in a source window. You can use this command to move the execution point, if you want to intervene in the sequence of execution of your code. This can be useful when you want to experiment with a slight change in the execution path, or when you want to avoid executing a line of code that will cause a run-time error. Move the caret to the line where you want execution to resume, then select the "Set Next Statement" command; the execution point will move to the new line you selected. Note that you can never move execution outside of the bounds of the function or method containing the current execution point.

Restart Game. This command (on the "File" menu) allows you to start the game over from the beginning from within the debugger. This command is provided mostly for convenience, since it's roughly the same as entering a "restart" command on the game's command line, but it does differ from the normal "restart" command within the game in that it completely clears the output window, including any prior pages that may have been displayed.

Abort Current Command. This command (on the "Debug" menu) has the same effect as executing the TADS `abort` statement in your game: it immediately terminates the current player command and starts the next turn in the game. This command can be useful when you're stepping through some code and decide that you don't want to let the code for the current command go any further (due to a bug in the code, for example). When you execute this command, the debugger will take control again at the first source line that's processed for the new turn, which usually means that the debugger will stop in the status line code (since the game will display its status line as part of setting up for the next turn).

Terminate Game. This command (on the "File" menu) quits the game, as though you had typed a QUIT command into the game's command line. After the game is terminated, the debugger unloads the game. Use the "Reload Game File" item on the "File" menu to load and run the game again.

Reload Game File. This command (on the "File" menu) terminates the current game and reloads the game file from disk.

Load New Game. This command (on the "File" menu) terminates the current game and loads a new game file from disk.

## Run-time Error Handling

The Windows version of the debugger differs slightly from the DOS version in its handling of run-time errors.

With the DOS version, when a run-time error occurred in the course of executing your game, TADS stopped your game and activated the debugger at the source line containing the error. You could inspect variables, the call stack, and other conditions related to the error. When you resumed execution (with "go" or one of the "step" commands), the debugger aborted the current command and started a new turn.

The Windows debugger behaves mostly the same way, but has one substantial difference: the Windows debugger does not abort the current command. Instead, it resumes execution at the start of the source line containing the error. This gives you a lot more flexibility in debugging code that encounters a run-time error: you can correct any variable values that led to the error, and try executing the line again with the new values; or, you can move the instruction pointer to a new line (for example, skipping the line with the error), and resume execution from that point; or, you can abort the command entirely using the "Abort Current Command" debugger command (which is what the old debugger would have done).

The purpose of this error-handling behavior is to give you more flexibility while debugging; this flexibility is made possible by the extra commands available in the Windows debugger. The DOS debugger doesn't let you move the execution pointer, so if it allowed you to resume execution at the start of a source line containing an error, you would find yourself stuck in an infinite loop without any way of moving past the error. Since the Windows debugger lets you move the execution pointer and lets you explicitly abort a player command, there's no danger that the game will get stuck re-executing an erroneous line of code: you can simply move past the line by moving the execution pointer, or you can abort the command entirely.

## Quitting the Game

The Windows debugger differs slightly from the DOS version in the way the debugger handles game termination. In the DOS version, quitting the game immediately terminates the debugger. If you want to start the game over, you must start a new debugging session.

In the Windows version, in contrast, the debugger remains running when the game quits. At this point, you can't perform any debugging operations on the game, since the game is no longer loaded; however, you can load the game again, by selecting the "Reload Game File" item on the "File" menu.

You can at any time restart the game without reloading the game file, by selecting "Restart Game" from the "File" menu. Using this command does not load the game file again, but merely starts over with the existing game file.

You can terminate the game at any time by selecting the "Terminate Game" item on the "File" menu. This commands stops the game and unloads the game file. You can start over by using the "Reload Game File" command.

Note that you cannot recompile a game as long as it's running in the debugger; you must unload the game file before you can recompile it. The debugger must keep the game file locked as long as the game is running, because the debugger might need to load data from the file in the course of executing the game. Because the file is locked, the compiler cannot overwrite it. If you want to make a change in your source code and recompile your game, you must first terminate the game (either by quitting the game normally or by using the "Terminate Game" command in the debugger). After terminating the game, you can recompile it. Finally, after you recompile the game, you can reload the game into the debugger (using the "Reload Game File" command), at which point you can run the modified version of the game.

## Loading and Reloading the Game

The Windows debugger lets you load a game again after it terminates. To run the game again, simply use the "Go" or one of the "Step" commands to begin execution. The debugger will re-load the file from disk and start over.

The "Restart Game" item on the "File" menu differs from "Reload Game File" in that "Restart" merely starts the current game over without loading it again from disk. "Reload" terminates the game and reloads the game file from disk.

You can also load a different game into the debugger. Select the "Load New Game" item from the "File" menu to debug a different game. This will terminate and unload the current game.

Help Topics > Table of Contents
Copyright ©1999, 2007 by Michael J. Roberts.