-
Notifications
You must be signed in to change notification settings - Fork 10
Home
When writing complex algorithms, it is important to be able to easily track its behavior and its current state.
To achieve that goal, one is tempted to use directly write to the standard output. This approach is not satisfying for several reasons:
- output cannot be turned on/off easily (debugging slows down algorithms!),
- not extensible nor modular,
- impossible to have clean runs (i.e. a released algorithm working fine should just not output anything).
This package provides:
- debugging levels (error, warning, notice, info)
- debugging outputs (console, journal)
Debugging levels:
- error: a problem lead to operation failure (will always be reported to the user),
- warning: a problem occurred but operation was not abandoned (will always be reported to the user),
- notice: no problem occurs but reports information which might be of interest for the end-user,
- info: debugging output, extremely verbose, intended for developer only (never to be shown to the end-user),
- benchmark: dedicated to performance testing, see next section for more information.
Debugging outputs:
-
JournalOutput
logs information on a file (default file name isjournal
), -
ConsoleOutput
logs information on the console (by defaultstd::cerr
).
Any debugging information should be written in the ${PREFIX}/var/log
directory (see the filesystem hierarchy standard for details).
For instance, if your installation prefix is /tmp/devel
then the logging directory is /tmp/devel/var/log/
.
Debugging files such as the journal is placed in the hpp
subdirectory, the others should be in a subdirectory matching the package name.
Example:
- journal file is placed in
/tmp/devel/var/log/hpp
as all HPP packages should write in it, - when planning a walk movement with
hpp-walkfootplanner
, trajectory is logged in/tmp/devel/var/log/hpp-walkfootplanner
.
Please, notice that if the HPP_LOGGINGDIR
environment variable is defined, all debugging files will be placed in this directory (without any subdirectories).
Important: when using CORBA servers through the hpp-corbaserver
package. CORBA log is sent to the HPP logging mechanims.
As such, if an error happens in omniORB, it will be logged by HPP. To enable verbose logging of omniORB, its configuration file omniORB.cfg
has to be modifies, see omniORB documentation for more information.
A simple benchmark tool is also provided to log computation time of algorithms sub-sections.
It is writing to the special channel benchmark
and is outputted in a specific journal called benchmark
.
This feature requires at least hpp-util v0.1.7.
Assertions can be used to detect errors at runtime. It is a simple mechanism which checks that a condition evaluates to true and make the software abort if it is not the case. This feature can also be disabled to avoid impacting the performance of the final software.
Making a program abort is sometimes unpleasant: the error leads to a full stop of the program without the
possibility to perform additional computations or free the memory. A less intrusive approach is throwing
an exception instead of aborting.
This mechanism is implemented by HPP_ASSERT
.
Two others macros are available:
-
HPP_PRECONDITION
checks that prerequisites of an algorithm is fulfilled. -
HPP_POSTCONDITION
checks a condition when exiting a scope. It is useful to check the result of an algorithm.
Debugging information is controlled by two compilation flags:
-
HPP_DEBUG
enabling all logging levels, -
HPP_ENABLE_BENCHMARK
enabling benchmarks computation.
To enable these options, define the symbol when calling cmake
:
cmake -DHPP_DEBUG=ON -DHPP_ENABLE_BENCHMARK=ON ..
Logging is realized through the hppDout
macro:
int x = 1 + 1;
hppDout (notice, "Computing 1 + 1");
if (x != 2) {
hppDout (error, "Oops... expected value of x is 2 and real value is " << x);
}
When writing a debugging file (foot steps, trajectory, etc.), one should place it in the ${PREFIX}/var/log/${SUBDIR}
directory where ${SUBDIR}
should be your package name.
To ease the path generation, hpp::debug::getFilename
should be used.
It takes two arguments:
- the file name including the extension such as
myfile.txt
, - the package name which should be either
hpp
is the debugging file is shared orPACKAGE_TARNAME
if not.
Example:
// To get PACKAGE_TARNAME definition:
#include "config.h"
// Required for I/O operations:
#include <iostream>
#include <fstream>
// Debugging tools:
#include <hpp/util/debug.hh>
void foo ()
{
std::string filename = hpp::debug::getFilename ("myfile.txt", PACKAGE_TARNAME);
std::ofstream debugFile (filename.c_str ());
debugFile << "This is my debugging file." << std::endl;
}
Benchmarking is handled through three macros:
-
hppStartBenchmark
which starts the timer -
hppStopBenchmark
which stops the timer -
hppDisplayBenchmark
which writes the result in thebenchmark
channel.
Example:
// Debugging tools:
#include <hpp/util/debug.hh>
void foo ()
{
hppStartBenchmark (MY_FIRST_COMPUTATION);
for (unsigned i = 0; i < 1000*1000; ++i)
hppDebug (info, "let's burn some cpu...");
hppStopBenchmark (MY_FIRST_COMPUTATION);
hppDisplayBenchmark (MY_FIRST_COMPUTATION);
}
Here MY_FIRST_COMPUTATION
is just an identifier for the benchmark. It can be any valid C++ identifier.
To be able to compile the previous examples in a package, it has to link against hpp-util
properly.
In your project CMakeLists.txt
:
# Allow compilation with -DHPP_DEBUG option
# Do not forget to select the option when calling cmake
SET (HPP_DEBUG FALSE CACHE BOOL "trigger hpp-util debug output")
IF (HPP_DEBUG)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHPP_DEBUG")
ENDIF()
# Declare dependency to hp-util package
ADD_REQUIRED_DEPENDENCY(hpp-util >= 0.4)
In your project 'src/CMakeLists.txt':
# Link against the library (here assuming your library is called `libhpp-MYPACKAGE.so`):
PKG_CONFIG_USE_DEPENDENCY(hpp-MYPACKAGE hpp-util)
The default behavior is given in the following table:
console | journal | benchmark journal | |
error | YES | YES | NO |
warning | YES | YES | NO |
notice | YES | YES | NO |
info | NO | YES | NO |
benchmark | NO | NO | YES |
This relation is currently ''compiled'' in hpp-util
(cf file src/util/debug.cc
):
// Global variables definitions.
#include <boost/assign/list_of.hpp>
namespace hpp
{
namespace debug
{
using boost::assign::list_of;
// Default output.
ConsoleOutput console;
JournalOutput journal ("journal");
JournalOutput benchmarkJournal ("benchmark");
// Default channels.
Channel error ("ERROR", list_of<Output*> (&journal) (&console));
Channel warning ("WARNING", list_of<Output*> (&journal) (&console));
Channel notice ("NOTICE", list_of<Output*> (&journal) (&console));
Channel info ("INFO", list_of<Output*> (&journal));
Channel benchmark ("BENCHMARK", list_of<Output*> (&benchmarkJournal));
} // end of namespace debug
} // end of namespace hpp
One enhancement would be to compute relations between channels and outputs from a configuration file or environment variable.