Friday, May 29, 2020

Debugging Qt for WebAssembly


Debugging is often difficult in the best of times. Debugging Qt webassembly apps in the web browser is not something I want to do on a nice summer day, and certainly makes a person more appreciative of mature, gui based debugging. But sometimes you have to do what you have to do.

If you have a crash on your Qt for WebAssembly app, you would see a not very human readable backtrace in the console output. You can start by recompiling your app with CONFIG+=debug, which allows nice symbol names in the backtrace that appears in the dev console of the browser when an exception or crash happens. Many times this can lead you to find your bug fairly quickly.

NOTE: The Qt libraries do not need to be compiled in debug mode for you to just see symbols while debugging. Although, if you want to step through C++ source code using sourcemaps, you will need to compile Qt and your application in debug mode.

The easy way is to add qDebug output, run it and see what happens. I do this quite often. But just as often, it is not enough to find a particular bug.

One issue with that is linking emscripten based wasm files takes a long time (because that is where all the magic happen), so this method is rather time consuming. Thankfully, with Qt 5.15, we use Emscripten 1.39.8 which is based on upstream clang, transpiles directly to wasm binaries and skips the internediate step of outputting javascript, so link time is greatly reduced.

What if you need to step through the source code like a desktop debugger? QtCreator does not at this time, have support for debugging Qt WebAssembly apps. I suppose it could be implemented using the browsers remote debugging interface API 

Emscripten can be used to generate source maps for the browser debugger to utilize for debugging. In Qt, we use qmake to add the necessary linker lines to allow emscripten to generate these source maps. Qt should do this by default when you use CONFIG+=debug

There is a bug that was recently fixed regarding this. If you do not find any source maps files (.map), the workaround is to add QMAKE_LFLAGS_DEBUG += -g4 in your pro file.

By default, the source base in Qt is set to use http://localhost:8000/

You can change this by adding into your .pro file, something like:
QMAKE_WASM_SOURCE_MAP_BASE = http://myip:6931 /

Emrun by default uses port 6931.

If you are running a server of some sort, you can use the ip for that. 

Sources need to be in the directory tree seen by the server, so an in-source build is the easiest. In my experience, Chrome browser seems to be able to debug with sourcemaps better. If you are using Mac or Linux, you can simply symlink the source file path into the web server base.

This is what happens when you do not have this set up correctly:

Error while fetching an original source: request failed with status 404
Source URL: http://192.168.0.21:6931/src/wasm-windows/main.cpp

Or pehaps you see a message in the sources tab of the browser console such as:
Could not load content for http://localhost:8000/depot/qt/qt5/qtbase/examples/widgets/richtext/textedit/textedit.cpp (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)

It means it would not find the specific source file. You can symlink the sources directory.
I am using chrome, as I had better luck with it utilizing the sourcemaps. The actual procedure for firefox might be a bit different. It isn't working for me at this time.
  • emrun --browser chrome --port 8000 --hostname localhost --serve_after_close textedit.html 
    • (or wasmsourceserver.py as below)
  • open the web console
  • [chrome] Go into the debugger by clicking on 'sources'
  • [firefox] Uses the 'Debugger' tab
You should now see some entries to source files, including our main.cpp


You can now step in, step out, step over and even set breakpoints, just like a desktop gui debugger like QtCreator.



Note that these file paths are relative, and will create an error when you try debugging it, when it tries to step into a source file not local. This defeats the purpose of steping into code for the most part.

Morten has a work around on the QTBUG https://bugreports.qt.io/browse/QTBUG-72002 with a python web server named:
wasmsourceserver.py

By running this custom server, it will build a file map of all the files in the current directly reursively.


Another way to add a type of breakpoint, if you want to stop execution on a certain line and popup the browser debugger programmatically, you can add
 emscripten_debugger(); 
on the line you want it to stop and pop up. This is in the header emscripten.h, and recompile.




You can also read more about Mobile and Embedded development and Qt for WebAssembly in the book Hands-On Mobile and Embedded Development with Qt 5