The JS debugger is temporarily disabled during the SpiderMonkey upgrade (check ticket #2348 for details)

Introduction

A large part of the game like components, GUI scripts or the AI are written in Javascript. If you have programmed C++ or used Javascript for websites you probably know how important a good debugger can be and how much it helps to find bugs or learn how a part of code works. Existing tools like Firebug can't be used for 0 A.D. because the debugger has to be very closely connected to the Javascript runtime. However, there's a dedicated debugging tool for 0 A.D. and the Pyrogenesis engine which is not as sophisticated as Firebug but offers some basic debugging functionality. This article describes the usage of the Pyrogenesis script debugger. If you are interested in technical documentation of the debugger and in changing the debugger itself, please refer to Javascript debugging server.

Overview

The debugger requires two components to work. First, there's a webserver built into the engine and second, there's a web GUI that can be launched from anywhere by opening a html file in a web browser. All important commands can be executed from the web GUI and it displays script code and information like active breakpoints or running threads.

Enabling the debugger

The debugger can run both in release and in debug builds. However, the debugging server is disabled by default for performance and security reasons. You can enable it by adding this line to your local.cfg.

jsdebugger.enable = true                   ; Enable Javascript debugging (default off for security/performance) 

If you only need the debugger for a single session, you can use a command line argument:

./pyrogenesis -conf=jsdebugger.enable:true

A yellow warning message is displayed on startup of the game if the debugger is enabled.

Using the debugger

Starting

First start the game and make sure that the debugger is enabled. Then open 0ad/source/tools/jsdebugger/index.html in a web browser.

The debugger will load a list of script files loaded by the engine (1). Because no file is loaded at the moment, the file panel in the middle (2) will be empty. On the right side (3) you see all (or most) commands that are available. On the bottom left side (4) there's a list of currently running script interface instances. They can all be different threads, but that's not necessarily so. Because the scripts are running and we haven't triggered a breakpoint yet, the callstack window (5) is empty. The values window (6) contains the three root nodes for values you can watch (locals, this, global) but they are empty at the moment.

The following sections describe all these panels in more detail.

Files (1)

This is a list of all .js files currently loaded into the vfs by the engine. You can type something in the search field and it will only display those files that match the pattern. Double-clicking a file will open it in the file panel in the middle.

File panel (2)

The file panel displays a file with line numbers, syntax highlighting and even some basic syntax validations. However, it only displays the file and you can't edit it there directly. You see breakpoints and can toggle breakpoints by clicking left beside the line numbers. The yellow line shows you at which line the execution is currently halted.

Actions (3)

The actions are basically the same as with other debuggers. You don't change anything at the code or what happens when it's executed. These actions are just commands to stop and continue the execution in a certain way.

Be aware that the debugger is only capable of stepping through JS code and has no control over native (c++) code.

Step

Step (or step over) continues the execution until the next line of code is reached. If there is a function call on the current line, it does not stop the execution after it entered the function (it steps over it).

Step into

If there is a function call on the current line, it steps into that function and stops the execution at the first line of that function. Otherwise it just stops the execution at the next line of code.

Step out

Continues until the current function is completed and then stops the execution in the parent function.

Continue

Continue the execution of all threads.

Continue thread

Continue the execution of the currently selected thread.

Break

Stop the execution of each thread as soon as possible (basically as soon as the next JS code is executed).

Threads (4)

Actually the term "thread" is not quite accurate. All ScriptInterface? instances are listed there. A ScriptInterface? represents a JS runtime and a JS context. Multiple ScriptInterface? instances can be used in one or more threads. Important to know is that stopping one thread will sometimes make it impossible to stop another one without continuing the first again. However, if two threads are really running in parallel and independent from each other, the debugger will try to stop the second thread too if you stopped the fist one.

If one thread is in break state, you can double-click on it to switch to that thread. The debugger will try to open the file the thread is currently executing and it will display the associated callstack and variables.

Call stack (5)

If you look at this example, keywordTestOR is the innermost function which got called by an anonymous function, which got called by "testFilter" etc... Often the debugger will only show anonymous functions because we are working with prototypes a lot and don't use named functions often.

You can double-click on one function name to display the variables of that scope.

Variables (6)

There are three main types of values that can be displayed.

  1. Locals

Locals are locals variables in the current scope.

  1. This

This is Javascript's "this" object. If you are a Javascript developer you should know what it is, it's quite difficult to explain. Note that we are using the "strict" mode which means that sometimes the "this" object will be empty.

  1. Global

This is the current global object.

Sometimes retrieving these values from the game via JSON can take quite a while (in rare cases up to 15 seconds). If you want to quickly step through code you should collapse all root nodes (or at least the ones with a lot of values) because their content is only loaded when they are expanded. This will make stepping a lot faster.

The web GUI remembers expanded nodes and they will stay expanded the next time values are loaded from the service. Sub nodes will not be collapsed if you collapse their parent node (they will become invisible but they will be in expanded state if you expand the parent again).

Breakpoints from code

You can use this code to trigger breakpoints from JS code:

throw "Breakpoint";

The debugger will clear the exception and stop the execution as if you used "break" or set a breakpoint in the debugger's web GUI. However, these code breakpoints will have to be removed again for final versions of code because the exception will not be cleared if the debugger is not enabled! Maybe we will add this to some kind of "assert" function which will not affect release builds.

Debugger settings

The debugger supports some settings that allow you to change its behaviour. These settings are not included in the web GUI at the moment and you have to type some URLs in your browser manually to change them.

Simultaneous thread break

It's enabled by default and means that if one thread stops (because of a breakpoint for example), the debugger will try to stop all other threads too. Note: Setting it to false should work but isn't very well tested.

http://127.0.0.1:9000/SetSettingSimultaneousThreadBreak?Enabled=false

Break on exception

It's enabled by default and means that the debugger will stop script execution if any exception is thrown. Even if you disable it, the debugger will still trigger breakpoints exceptions with the message "Breakpoint".

http://127.0.0.1:9000/SetSettingBreakOnException?Enabled=false
Last modified 10 months ago Last modified on Jan 4, 2014 8:33:46 PM

Attachments (5)

Download all attachments as: .zip