Changes between Version 13 and Version 14 of JavascriptDebugging


Ignore:
Timestamp:
Mar 3, 2013, 4:17:29 PM (11 years ago)
Author:
Yves
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • JavascriptDebugging

    v13 v14  
    33== Introduction ==
    44A 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.
    5 The web based 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 tool for 0 A.D. and the Pyrogenesis engine being developed at the moment that should hopefully be ready to use soon.
    6 
    7 == Technical background ==
    8 The DebuggingServer component makes debugging commands like (ToggleBreakpoint, Step, Continue etc...) available using a Mongoose webserver.
    9 The format used for return values is JSON, which should be easy to parse from a web-GUI.
    10  
    11 The Debugger uses the JSDBGAPI provided by Spidermonkey 1.8.5. Newer versions of Spidermonkey provide a simplified new API (along with memory leak fixes, performance improvements and more), but unfortunately Mozilla isn't interested in doing the work and publishing a independent build from Firefox at the moment.
    12 We had a [http://irclogs.wildfiregames.com/2013-01-02-QuakeNet-%230ad-dev.log discussion about that on IRC].
    13 We're thinking about upgrading Spidermonkey nevertheless, but that could be a bigger task.
    14 
    15 The JSDBGAPI isn't thread-safe and also our current implementation of ScriptInterfaces (which are basically abstractions of Spidermonkey) isn't thread-safe either.
    16 As a general rule, all Javascript objects are tied to one ScriptInterface which represents one Javascript runtime and one Javascript context and can only be used in one thread (although multiple ScriptInterface can be used in one thread). We do use multiple threads, but they don't share ScriptInterfaces.
    17 One major difficulty about implementing a debugger is not to violate these constraints.
    18 
    19 The idea is that there's one CThreadDebugger class which is responsible for tracking hooks, callbacks, breakpoints and general state information for each of those ScriptInterfaces. The class CDebuggingServer manages multiple ScriptInterfaces (which are potentially multiple threads) using multiple objects of CThreadDebugger.
    20 It passes commands from the Mongoose webserver (triggered by the user) to the CThreadDebugger objects and retrieves state information from these objects and returns it to the user as JSON. The DebuggingServer makes sure not to access any Spidermonkey functions or Javascript objects directly (because they belong to the ScriptInterface's thread).
    21 The DebuggingServer itself should also be thread-safe to accept multiple request asynchronously.
    22 
    23 
    24 == Available commands ==
    25 This section lists all currently implemented commands and gives some examples of data they return.
    26 
    27 === Setting: Simultaneously breaking threads ===
    28 This setting specifies the behaviour when a breakpoint is hit.
    29 If enabled, all other threads executing Javascript code will halt too as soon as possible and their status (ThreadInBreak) will change to true.
    30 Keep in mind that other ScriptInterfaces being executed in the same thread will not halt because their code will only be reached when you continue the execution in the current ScriptInterface. Another limitation is that threads can only be halted by this debugger when they execute Javascript code!
    31 You can set breakpoints with your favourite c++ debugger at the right places if you need seamless debugging between c++ and Javascript.
    32 
    33 ==== Get ====
    34 {{{
    35 http://127.0.0.1:9000/GetSettingSimultaneousThreadBreak
    36 }}}
    37 
    38 Returns:
    39 {{{
    40 #!js
    41 { "Enabled" : true }
    42 }}}
    43 
    44 ==== Set ====
    45 
    46 {{{
    47 http://127.0.0.1:9000/SetSettingSimultaneousThreadBreak?Enabled=false
    48 }}}
    49 Arguments:
    50  * '''Enabled''': If the setting should be enabled (true or false).
    51 
    52 Returns:
    53 Nothing
    54 
    55 
    56 === Toggling (setting/removing) breakpoints: ===
    57 {{{
    58 http://127.0.0.1:9000/ToggleBreakpoint?filename=gui/gamesetup/gamesetup.js&line=1502
    59 }}}
    60 Returns:
    61 Nothing
    62 
    63 === Get the status of all threads ===
    64 {{{
    65 http://127.0.0.1:9000/GetThreadDebuggerStatus
    66 }}}
    67 
    68 Returns:
    69 {{{
    70 #!js
    71 [ { "ThreadDebuggerID" : 1,"ScriptInterfaceName" : "GUI","ThreadInBreak" : true,"BreakFileName" : "gui/gamesetup/gamesetup.js","BreakLine" : 1502 } ]
    72 }}}
    73 Note:
    74 BreakFileName and BreakLine should be considered "undefined" if ThreadInBreak is false!
    75 
    76 === Continue ===
    77 Continue the execution until the next breakpoint is hit.
    78 {{{
    79 http://127.0.0.1:9000/Continue?threadDebuggerID=1
    80 }}}
    81 Arguments:
    82  * '''threadDebuggerID''': The ThreadDebuggerID of the thread that should be continued. You can get this ID by calling GetThreadDebuggerStatus. If you pass 0, all threads will be continued.
    83 
    84 Returns:
    85 Nothing
    86 
    87 Note: The command has no effect if the thread is not stopped.
    88 
    89 === Step ===
    90 Steps to the next line of code and will step over functions that are called on the current line.
    91 {{{
    92 http://127.0.0.1:9000/Step?threadDebuggerID=1
    93 }}}
    94 Arguments:
    95  * '''threadDebuggerID''': The ThreadDebuggerID of the thread that should be stepped. You can get this ID by calling GetThreadDebuggerStatus.
    96 
    97 Returns:
    98 Nothing
    99 
    100 === Step into ===
    101 Steps into function calls on the current line of code or steps to the next line if there aren't any function calls.
    102 {{{
    103 http://127.0.0.1:9000/StepInto?threadDebuggerID=1
    104 }}}
    105 Arguments:
    106  * '''threadDebuggerID''': The ThreadDebuggerID of the thread that should be stepped. You can get this ID by calling GetThreadDebuggerStatus.
    107 
    108 Returns:
    109 Nothing
    110 
    111 === Step out ===
    112 Steps out of the current function and stops the execution in the parent function.
    113 {{{
    114 http://127.0.0.1:9000/StepOut?threadDebuggerID=1
    115 }}}
    116 Arguments:
    117  * '''threadDebuggerID''': The ThreadDebuggerID of the thread that should be stepped. You can get this ID by calling GetThreadDebuggerStatus.
    118 
    119 Returns:
    120 Nothing
    121 
    122 
    123 === Getting Callstacks ===
    124 Returns the callstacks of all threads that are in break mode.
    125 {{{
    126 http://127.0.0.1:9000/GetAllCallstacks
    127 }}}
    128 Returns:
    129 {{{
    130 #!js
    131 { "CallStacks" : [{"ThreadDebuggerID" : 1, "CallStack" : ["keywordTestOR","annonymous","testFilter","initMapNameList","selectMapType","__eventhandler31 (selectionchange)","initMain","onTick","__eventhandler28 (tick)"]}] }
    132 }}}
    133 keywordTestOR is the innermost function which got called by an annonymous function, which got called by "testFilter" etc...
    134 You also get the ThreadDebuggerID to know which thread is meant.
    135 
    136 === Getting a stack frame ===
    137 Returns all variables and their content of the specified thread and nesting level.
    138 {{{
    139 http://127.0.0.1:9000/GetStackFrame?nestingLevel=0&threadDebuggerID=1
    140 }}}
    141 Returns:
    142 {{{
    143 ... need to find a shorter example ...
    144 }}}
    145 Arguments:
    146  * '''nestingLevel''' If you look at the function above (GetCallstack), nestingLevel=0 would point to the innermost stackframe (keywordTestOR). Native functions as "__eventhandler31" don't return any data.
    147  * '''threadDebuggerID''' The thread you want to get the a stack frame from.
    148 
    149 
    150 === Getting a list of .js files ===
    151 Returns all full Vfs paths ending with *.js loaded into the Vfs.
    152 {{{
    153 http://127.0.0.1:9000/EnumVfsJSFiles
    154 }}}
    155 Returns (shortened)
    156 {{{
    157 #!js
    158 ["globalscripts/Math.js","globalscripts/Technologies.js","hwdetect/hwdetect.js","hwdetect/test.js","gui/aiconfig/aiconfig.js","gui/civinfo/civinfo.js"]
    159 }}}
    160 
    161 === Getting a file ===
    162 Returns a whole file without any formatting (no JSON).
    163 {{{
    164 http://127.0.0.1:9000/GetFile?filename=globalscripts/Math.js
    165 }}}
    166 Returns:
    167 A file as plaintext.
    168 
    169 Arguments:
    170  * '''filename''' A full Vfs path to a file
    171 
    172 
    173 
    174 == Known issues ==
    175  * Error messages can be displayed caused by StringifyJSON and objects that don't support serialization
    176  * Getting a stack frame and getting the callstack doesn't always work. This will have to be checked more closely soon.
    177  * Some functionality is still missing. Further testing, proper error handling and correcting/completing comments is also required.
     5Existing 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.
     6This 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 [wiki:JavascriptDebuggingServer Javascript debugging server].