Debugging

xerus comes with a number of functionalities to simplify debugging. The purpose of this page is to explain the most important ones.

LOG

The library uses a macro of the form XERUS_LOG(level, msg) to print messages to cout. It allows to use arbitrary log levels without any need to declare them beforehand and to use typical piping syntax for the messages.

XERUS_LOG(als_warning, "The ALS encountered a mishap " << variable_1 << " <= " << variable_2);

The following warning levels are predefined: fatal, critical, error, warning, info, debug. The default config file config.mk.default defines the preprocessor variable XERUS_LOG_INFO which causes all but the debug level to be printed. Per default any other log-level is printed. This can be turned off by including XERUS_SET_LOGGING(level, xerus::err::NO_LOGGING) inside a commonly included header.

The fatal loglevel is special in that it will not just print the message but also throw an exception including the message itself and a callstack in the .what() string.

With the additional option LOGGING += -D XERUS_LOG_BUFFER in the config.mk file, any not printed log message will be stored in a circular buffer and will be dumped to a file in the errors/ subfolder (if it exists) on any error, critical or fatal log message.

REQUIRE

The XERUS_REQUIRE(condition, additional_msg) macro replaces assertions in the xerus library. It uses above XERUS_LOG functionality and can thus use piping style messages just as the XERUS_LOG macro. It is equivalent to the following definition

XERUS_REQUIRE(condition, additional_msg) = if (!(condition)) { XERUS_LOG(fatal, additional_msg); }

There is a large number of such checks in the library. All of them can be turned off by defining DEBUG += -D XERUS_DISABLE_RUNTIME_CHECKS in the config.mk file.

Unit Tests

Compiling with make test creates an executable that includes all functions defined within xerus::UnitTest objects.

static xerus::misc::UnitTest objectName("Group", "Name", [](){
    // source code of the test
    // likely includes (several) tests of the form TEST(condition);
    TEST(true);
});

This will only include the unit tests in the library but can easily be extended should you wish to use it for your own executable. The created executable can be launched manually to perform individual tests (eg. ./XerusTest TTTensor:summation) or reperform all of them (./XerusTest all).

With the additional config.mk option DEBUG += -D XERUS_TEST_COVERAGE, the executable will track which XERUS_REQUIRE and XERUS_REQUIRE_TEST macros were executed during the run to advice about function that need additional testing.

Callstacks and XERUS_THROW

Unless XERUS_NO_FANCY_CALLSTACK = TRUE was declared in the config.mk file, the function xerus::misc::get_call_stack() will return a formatted string of the full stack trace including function name, source file and line numbers. This stack will in particular be included in the exceptions thrown by XERUS_LOG(fatal, ...) and XERUS_REQUIRE(...) macros to simplify the detection of errors.

The information of this callstack is only available if the application was compiled with the -g flag and the linking requires the binutils packages -lbfd -lz -ldl -liberty.

The exceptions used by xerus have the additional capability to accept piped information that will be included in the .what() string. To include a callstack it is thus possible to simply write

XERUS_THROW(xerus::misc::generic_error() << "callstack:\n" << xerus::misc::get_call_stack());

The used macro will additionally include the source file and line as well as the function name in which the exception was thrown.