Skip to content
Fernbach Pierre edited this page Mar 11, 2016 · 1 revision

hpp-util - Debugging tools for the Humanoid Path Planner project

Resources

Concepts

Logging

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 is journal),
  • ConsoleOutput logs information on the console (by default std::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.

Benchmark

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.

Assertions and contract programming

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.

Configuration

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 ..

Using debugging tools in your code

Logging

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);
}

Writing a debugging file

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 or PACKAGE_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

Benchmarking is handled through three macros:

  • hppStartBenchmark which starts the timer
  • hppStopBenchmark which stops the timer
  • hppDisplayBenchmark which writes the result in the benchmark 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.

Adding a dependency toward hpp-util

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)

Changing relations between channels and outputs

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.