Debugging
This article explains how to debug Arx Libertatis.
Debug Views
F11
toggles between displays for FPS and other statistics
Scroll Lock
toggles between debug views: Entity|entities, paths and zones, pathfinding, lights, fogs, collision shapes, portals
Debug Log Output
The LogDebug()
macro can be used to add debug output that is disabled by default.
In a non-debug build (-DARX_DEBUG=0
), LogDebug()
is discarded completely.
In a debug build (-DARX_DEBUG=1
), the debug output is still disabled by default, but can be enabled by runtime settings. This is done with the ARXDEBUG
environment variable, the the --debug=
command-line parameter and/or the debug
setting in cfg.ini
under [misc]
. Each of those parameters specifies a comma-separated list of "component=level" or "component" entries.
Components can be any path component that is either the src
or "tools" directory or a subdirectory or a filename (not including the file extension). Settings for more specific components override more general settings.
Valid levels are:
Logger::Debug
: debug / d / D (default if level is omitted)Logger::Info
: info / i / ILogger::Warning
: warning / warn / w / WLogger::Error
: error / e / ELogger::None
: none / n / NLogger::Reset
: reset / r / R / - (nullifies any previous entry with exactly the same component name)
If the same component is occurs in multiple parameters, cfg.ini
takes precedence over the command-line parameter, which takes precedence over the environment variable.
Linux
Gdb
Start arx in the debugger by running gdb ./arx
and then entering run
run. You can pause arx and return to the gdb prompt at any time by sending arx the SIGINT signal (Ctrl+C
in the console).
Perhaps the most used function is to generate backtraces using the backtrace
(or shorter bt
) command in the gdb prompt.
To also include local variables and not just function arguments in the backtrace use bt full
By default, gdb will only print scalar function arguments in backtraces and replace classes with ...
. To print all function arguments use set print frame-arguments all
.
When printing objects, gdb will also include all static members of the class. This is often not needed and can be disabled with `set print static-members off`
Many stl classes check for error conditions when compiling in debug mode and throw an exception on failure. To get a backtrace for where the exception was thrown, add a breakpoint on the exception constructor. Enter this in the gdb prompt.
b std::logic_error::logic_error b std::runtime_error::runtime_error
If entering these commands into gdb on each run is to cumbersome, you can create a file called .gdbinit
that is loaded by gdb on startup and contains default settings for gdb:
set breakpoint pending on b std::logic_error::logic_error b std::runtime_error::runtime_error set print static-members off
The extra set breakpoint pending on
command is needed because .gdbinit
is loaded before any libraries and therefore gdb will not be able to find the breakpoints right away.
As already seen for stl exceptions, the b
command can be used to tell gdb to halt the execution if the program reaches a specific point.
You can then inspect the program state with the p <expression>
command.
More commands are discussed in this tutorial or the gdb documentation. A lot of common questions are answered in this gdb FAQ.
Attaching to existing processes
Sometimes it can be useful to debug a program that is already running. You can do this by running attach <pid>
instead of run
in the gdb prompt. You can get the pid of arx with the command ps -A | grep 'arx$' | sed 's/ [^0-9].*//'
Alternatively you can attach directly when starting gdb: gdb ./arx <pid>
or gdb ./arx `ps -A | grep 'arx$' | sed 's/ [^0-9].*//'`
Valgrind
Callgrind
Callgrind can be used to profile arx and generate a detailed call graph which can then be viewed using tools like KCachegrind. Like other valgrind tools, callgrind will considerably slow down the execution of the profiled program. Callgrind can be started using:
valgrind --tool=callgrind -- ./arx
Memcheck
Memcheck can detect memory access errors such as accessing unallocated/freed data, buffer overruns, using uninitialized values and memory leaks. To generate a very detailed report, run arx under valgrind with this command:
valgrind --tool=memcheck --error-limit=no --leak-check=full "--log-file=arx-valgrind-%p.log" -v --track-origins=yes -- ./arx
Memcheck will slow down the program even more than callgrind. Because the timing code in arx isn't very good, this can cause some cutscenes to fail (for example, the intro never finishes).
To make memcheck run faster, you can disable some of it's features that you don't need:
- Memory leaks: --leak-check=no
- Uninitialized values: --track-origins=no --undef-value-errors=no
For a full list of options see the memcheck manual
Because of bugs in libraries or because valgrind doesn't understand how some OpenGL implementations map HW buffers (notably the AMD binary drivers), there will most likely be a lot of false positives. Valgrind supports suppression files to handle bugs libraries that you don't want to debug: --suppressions=suppressions.txt
. This is not a very good solution for the HW buffers that valgrind doesn't understand as they are also accessed directly from arx code.
A starting point for a suppression file is:
# Valgrind doesn't understand how the ATI binrary drivers map HW buffers. # Some of these might be real bugs in the drivers! { fglrx_cond Memcheck:Cond ... obj:*fglrx_dri.so* } { fglrx_value4 Memcheck:Value4 ... obj:*fglrx_dri.so* } { fglrx_addr4 Memcheck:Addr4 ... obj:*fglrx_dri.so* } { fglrx_value1 Memcheck:Value1 ... obj:*fglrx_dri.so* } { fglrx_addr1 Memcheck:Addr1 ... obj:*fglrx_dri.so* } { fglrx_value2 Memcheck:Value2 ... obj:*fglrx_dri.so* } { fglrx_addr2 Memcheck:Addr2 ... obj:*fglrx_dri.so* } { fglrx_value8 Memcheck:Value8 ... obj:*fglrx_dri.so* } { fglrx_addr8 Memcheck:Addr8 ... obj:*fglrx_dri.so* } { fglrx_overlap Memcheck:Overlap ... obj:*fglrx_dri.so* } { fglrx_param Memcheck:Param ... obj:*fglrx_dri.so* } # Alsa is buggy, from http://winezeug.googlecode.com/svn/trunk/valgrind/valgrind-suppressions { suppress_libasound_overlap Memcheck:Overlap fun:memcpy obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_connect Memcheck:Param socketcall.connect(serv_addr..sun_path) obj:* obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_connect2 Memcheck:Cond fun:snd_pcm_direct_client_connect obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_bind Memcheck:Param socketcall.bind(my_addr..sun_path) obj:* obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_ioctl Memcheck:Param ioctl(arg) obj:* obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_ioctl2 Memcheck:Param ioctl(arg) obj:* fun:ioctl obj:/usr/lib*/libasound.so.2.0.0 } { suppress_libasound_semctl Memcheck:Param semctl(IPC_SET, arg.buf) obj:* obj:/usr/lib*/libasound.so.2.0.0 }
Apitrace
Apitrace can record all OpenGL calls made by arx. There is also a GUI that let's you inspect the generated trace and view the complete OpenGL state at each call. (including renderbuffer and textures)