Jump to content

WIP: Javascript debugging - Web developer wanted


Recommended Posts

Have you ever wanted to step inside a Javascript function while tracking down a bug with a C++ debugger?

Did you miss the ability to single-step through Javascript code to figure out how something works?

Well, I did. I planned to look into the AI a bit and there were a lot of situations where I wished I had the same tools as with C++. Another recent situation convinced me to try figuring out how it could be realized.

EDIT: I've put the outdated information below into a spoiler.

Get the most up to date information and the newest version of the debugger from this wiki article (or read the whole thread).

http://trac.wildfire...scriptDebugging

Current status

I have a very basic and heavily WIP version that supports toggling breakpoints, continuing the execution and some additional things. It should work with all runtimes and contexts we are using (GUI, Simulation AI etc...), there might be some possible threading issues, thought.

Commands can be executed via a mongoose webserver that we already use for the profiler and maybe other things. There's no convenient UI, you have to know how to pass certain URLs. There's no way yet how to receive information about the content of variables at runtime.

As you can see it's not very useful yet, but now that the basics are implemented it should be possible to add new features and improve it. :)

More technical background

It uses the JSDBGAPI provided by Spidermonkey 1.8.5. Newer versions of Spidermonkey provide a simplified new API (along with memory leak fixes and more), but unfortunately Mozilla isn't interested in doing the work and publishing a independent build from Firefox. We had a discussion about that on IRC.

Supported commands at the moment (with example values)

Toggling (setting/removing) breakpoints:

http://127.0.0.1:9000/ToggleBreakpoint?filename=gui/gamesetup/gamesetup.js&line=1481

Continuing when the execution stops at a breakpoint:

http://127.0.0.1:9000/Continue

List all *.js files loaded into the vfs.

http://127.0.0.1:9000/EnumJsFilesPreExec

Get a File loaded into the vfs:

http://127.0.0.1:9000/GetFile?filename=globalscripts/Math.js

Get the names of all Scriptinterface instances:

http://127.0.0.1:9000/EnumScriptInterfaces

What's next / Web developer wanted

I thought it's time to think a bit about the UI before I implement too many additional features.

I have written some HTML a long time ago and I think it would be quite an effort to learn everything required to make a useful webinterface for calling these commands, displaying sourcecode, toggling breakpoints by clicking etc...

I would appreciate it if there was someone who's more used to it and could help creating such a UI.

This would allow me to focus on the server-side.

Here's the current WIP version (be warned, I haven't spent a single second cleaning it up or making it less ugly ;))

Feedback is always welcome!

javascript_debugging_WIP_v.0.1.diff

Link to comment
Share on other sites

Yves, I have applied your patch thusly: https://github.com/z...9d40a5444aea75d

How do you enable the debugging server? Should I make a debug build?

Yes at the moment it's only enabled in debug builds (#ifdef DEBUG). It should definitely not be enabled in release builds for reasons like performance, security and stability.

It's great to have someone working on this again! :) Have you seen #410?

No I haven't. :)

(BTW I think you should clarify this is not a web JS debugger but one specifically for 0 A.D., otherwise people will be suggesting stuff like Venkman...)

Yes that's an important note. It doesn't seem to be possible to use 3th party tools that are designed for other Javascript embedding applications like Firefox.

Ah, leper was right. I could make a simple UI for this. As long as it doesn't involve too much back-end :P

I'll be on IRC Saturday (GMT evening); sadly school has started again.

Sounds really handy. Maybe I can work with Geek377 on the UI. I don't really have experience with all that AJAX/HTML5 fanciness, but I'd be happy to work on the engine-facing stuff.

Thank you, that would be awesome! :)

If you have time, please check if the format returned is OK or if I should change anything so that once you start you don't have to worry about these things.

I thought it could be something similar to source/tools/profiler2/profiler2.html regarding the technologies used, but you're free about that.

Link to comment
Share on other sites

Yves, I get this when trying to do a debug build:

==== Building mocks_real (debug) ====
==== Building network (debug) ====
==== Building simulation2 (debug) ====
==== Building scriptinterface (debug) ====
==== Building engine (debug) ====
==== Building graphics (debug) ====
==== Building atlas (debug) ====
==== Building gui (debug) ====
==== Building lowlevel (debug) ====
DebuggingServer.cpp
==== Building mongoose (debug) ====
==== Building mocks_test (debug) ====
==== Building AtlasObject (debug) ====
==== Building AtlasScript (debug) ====
==== Building Collada (debug) ====
ScriptInterface.cpp
==== Building AtlasUI (debug) ====
GameSetup.cpp
==== Building ActorEditor (debug) ====
../../../source/scriptinterface/DebuggingServer.cpp: In function ‘void NewScriptHook(JSContext*, const char*, unsigned int, JSScript*, JSFunction*, void*)’:
../../../source/scriptinterface/DebuggingServer.cpp:132:47: fejl: could not convert ‘0’ from ‘int’ to ‘jsval’
make[1]: *** [obj/scriptinterface_Debug/DebuggingServer.o] Fejl 1
make[1]: *** Venter på uafsluttede job....
Linking engine
make: *** [scriptinterface] Fejl 2

Am I missing something?

Link to comment
Share on other sites

I have created a new version which also changes the part of code that can cause the compile error mentioned above.

Now I get this:

==== Building mocks_real (debug) ====
==== Building network (debug) ====
==== Building simulation2 (debug) ====
==== Building scriptinterface (debug) ====
==== Building engine (debug) ====
==== Building graphics (debug) ====
==== Building atlas (debug) ====
==== Building gui (debug) ====
DebuggingServer.cpp
==== Building lowlevel (debug) ====
==== Building mongoose (debug) ====
==== Building mocks_test (debug) ====
==== Building AtlasObject (debug) ====
==== Building AtlasScript (debug) ====
==== Building Collada (debug) ====
ScriptInterface.cpp
==== Building AtlasUI (debug) ====
GameSetup.cpp
==== Building ActorEditor (debug) ====
../../../source/scriptinterface/DebuggingServer.cpp: In function ‘JSTrapStatus TrapHandler_(JSContext*, JSScript*, jsbytecode*, jsval*, jsval)’:
../../../source/scriptinterface/DebuggingServer.cpp:13:38: fejl: invalid cast from type ‘jsval’ to type ‘CDebuggingServer*’
../../../source/scriptinterface/DebuggingServer.cpp: In member function ‘void CDebuggingServer::NewScriptHook(JSContext*, const char*, unsigned int, JSScript*, JSFunction*, void*)’:
../../../source/scriptinterface/DebuggingServer.cpp:130:54: fejl: no matching function for call to ‘jsval_layout::jsval_layout(CDebuggingServer* const)’
../../../source/scriptinterface/DebuggingServer.cpp:130:54: bemærk: candidates are:
In file included from ../../../libraries/spidermonkey/include-unix/js/jspubtd.h:47:0,
from ../../../libraries/spidermonkey/include-unix/js/jsapi.h:49,
from ../../../source/scriptinterface/ScriptTypes.h:81,
from ../../../source/scriptinterface/ScriptInterface.h:25,
from ../../../source/scriptinterface/DebuggingServer.h:6,
from ../../../source/scriptinterface/DebuggingServer.cpp:3:
../../../libraries/spidermonkey/include-unix/js/jsval.h:294:15: bemærk: jsval_layout::jsval_layout()
../../../libraries/spidermonkey/include-unix/js/jsval.h:294:15: bemærk: candidate expects 0 arguments, 1 provided
../../../libraries/spidermonkey/include-unix/js/jsval.h:294:15: bemærk: jsval_layout::jsval_layout(const jsval_layout&)
../../../libraries/spidermonkey/include-unix/js/jsval.h:294:15: bemærk: no known conversion for argument 1 from ‘CDebuggingServer* const’ to ‘const jsval_layout&’
../../../source/scriptinterface/DebuggingServer.cpp: In function ‘JSTrapStatus TrapHandler_(JSContext*, JSScript*, jsbytecode*, jsval*, jsval)’:
../../../source/scriptinterface/DebuggingServer.cpp:14:1: advarsel: kontrol når til slutningen af ikke-void funktion [-Wreturn-type]
make[1]: *** [obj/scriptinterface_Debug/DebuggingServer.o] Fejl 1
make[1]: *** Venter på uafsluttede job....
Linking engine
make: *** [scriptinterface] Fejl 2
make: *** Venter på uafsluttede job....

Any hope of persuading you to push your changes to a GitHub branch? This would make it less likely that I screw up the patching process.

Link to comment
Share on other sites

Any hope of persuading you to push your changes to a GitHub branch? This would make it less likely that I screw up the patching process.

It doesn't look like you've done anything wrong with the patching.

Please try compiling just DebuggingServer.cpp with the same options as I did (maybe you have to adjust some library directories):


g++ -g -Wall -DDEBUG -DLIB_STATIC_LINK -DUSING_PCH -DWITH_SYSTEM_MOZJS185 -Wno-switch -Wno-reorder -Wno-invalid-offsetof -Wextra -Wno-missing-field-initializers -Wunused-parameter -Wredundant-decls -Wnon-virtual-dtor -Wundef -fstack-protector-all -D_FORTIFY_SOURCE=2 -fstrict-aliasing -fno-omit-frame-pointer -fpch-preprocess -msse -fvisibility=hidden -I/usr/include/nspr -I/usr/include/js -Winvalid-pch -include "../../../source/pch/scriptinterface/precompiled.h" -I/usr/X11R6/include/X11 -I/usr/X11R6/include -I/usr/include/X11 -I../../../source/pch/scriptinterface -I../../../source -I../../../libraries/valgrind/include -c ../../../source/scriptinterface/DebuggingServer.cpp -o ./DebuggingServer.o

EDIT: I've figured out why it happens, but not yet how to resolve it.

If I use the bundled version of spidermonkey, I get the same errors.

Link to comment
Share on other sites

It works :) This will be very useful. And JSON is clearly the right output format. Maybe you want to add some status outputs on those commands that don't have it yet, e.g. "{status: true}" when a breakpoint is set and "{status: false}" when it is unset (or something along those lines).

Link to comment
Share on other sites

The next version is ready and you can now examine variable content at runtime.

New functions

Both functions are only available when the execution is halted because you have triggered a breakpoint or used step after triggering a breakpoint.

Getting the callstack

http://127.0.0.1:9000/GetCallstack

It will return something like that:


["keywordTestOR","annonymous","testFilter","initMapNameList","selectMapType","__eventhandler31 (selectionchange)","initMain","onTick","__eventhandler28 (tick)"]

keywordTestOR is the innermost function which got called by an annonymous function, which got called by "testFilter" etc...

Getting all variables from a stack frame.

http://127.0.0.1:9000/GetStackFrame?nestingLevel=0

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.

Other modifications

  • I've uncommented the most annoying LOGERROR calls that spammed the screen.
  • EnumJsFilesPreExec returns a slightly different output format (I hope that's better).

javascript_debugging_WIP_v.0.4.diff

Link to comment
Share on other sites

Yves, I'm very keen to help with the UI on this. What would be nice is if, in your path, when you hit the root path (http://localhost:9000) it would load an index.html file (which can be blank for now). That way I know which file to modify. Then I'll link in JQuery from googles JS CDN and get to work implementing some buttons which execute the url asynchronously and then updates the page with results.

Link to comment
Share on other sites

I can't get the breakpoints to work. What happens when a breakpoint is hit?

I've always used the example from the first post for testing. Maybe there are some issues I haven't discovered yet with other files and lines.


http://127.0.0.1:9000/ToggleBreakpoint?filename=gui/gamesetup/gamesetup.js&line=1481

  1. Start the game. there should be a yellow warning message on the upper left corner that the javascript debugging is enabled.
  2. Enter the URL in your browser to toggle a breakpoint.
  3. Select "Singleplayer->Matches"
  4. Now the UI should "freeze" because the breakpoint callback is called and loops until you continue/step.
  5. The breakpoint will be triggered multiple times. You can either remove the breakpoint and call Continue once or call Continue multiple times.

As you see it only stops one thread which is one thing that could cause issues.

Yves, I'm very keen to help with the UI on this.

Good :)

What would be nice is if, in your path, when you hit the root path (http://localhost:9000) it would load an index.html file (which can be blank for now). That way I know which file to modify. Then I'll link in JQuery from googles JS CDN and get to work implementing some buttons which execute the url asynchronously and then updates the page with results.

I thought we could do it similar to 0ad/source/tools/profiler2.html.

It uses a local file which can be opened in a browser, modified or placed wherever you want. Why would we need to provide the index.html via webserver?

Link to comment
Share on other sites

I thought we could do it similar to 0ad/source/tools/profiler2.html.

It uses a local file which can be opened in a browser, modified or placed wherever you want. Why would we need to provide the index.html via webserver?

Ah, ok. I'll have to look into how it does it.

Edit: Oh yup. That looks very doable. Attempting to compile the game in debug with the patch. Will let you know where I get.

Link to comment
Share on other sites

I've always used the example from the first post for testing. Maybe there are some issues I haven't discovered yet with other files and lines.


http://127.0.0.1:9000/ToggleBreakpoint?filename=gui/gamesetup/gamesetup.js&line=1481

  1. Start the game. there should be a yellow warning message on the upper left corner that the javascript debugging is enabled.
  2. Enter the URL in your browser to toggle a breakpoint.
  3. Select "Singleplayer->Matches"
  4. Now the UI should "freeze" because the breakpoint callback is called and loops until you continue/step.
  5. The breakpoint will be triggered multiple times. You can either remove the breakpoint and call Continue once or call Continue multiple times.

As you see it only stops one thread which is one thing that could cause issues.

I try those steps and I get the debugging enabled message, some of the other commands work but not breakpoints. The UI doesn't freeze when entering match setup, which is what I was expecting. Could it be something about how the Spidermonkey lib was built on Windows?

Link to comment
Share on other sites

I try those steps and I get the debugging enabled message, some of the other commands work but not breakpoints. The UI doesn't freeze when entering match setup, which is what I was expecting. Could it be something about how the Spidermonkey lib was built on Windows?

I can confirm this on Windows. I'll try to figure it out and edit this post or add a new one when I know more.

EDIT: I forgot to initalize a boolean variable. Apparently it becomes false by default on Linux but not on Windows.

There was also another problem with an uninitialized jsval which should be solved now.

This version is now tested on Windows and Linux.

A known Issue is that it crashes when exiting the game, which I think is related to using Javascript contexts in multiple threads and will require some more thinking.

Here's the updated patch:

javascript_debugging_WIP_v.0.5.diff

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...