Everyone makes mistakes, but with NoBug you won't make them twice!

Nobug is a debugging library for instrumenting C and C++ programs inspired by ideas originating from Design-by-Contract.

Overview

The following features are provided by NoBug:

In contrast to traditional debuggers, NoBug is a non-interactive debugger which is linked to your application doing hard-coded tests in an efficient, low-overhead way.

What NoBug can not do

NoBug is a (macro-)library, it is not a C/C++ language extension. This means that code must be called at runtime to benefit from the set up contracts. Whats not tested is likely slipping through the net. Being part of the program itself it is affected by memory corruption, certain kinds of misuse may introduce new bugs (test expressions with side effects for example).

Building and Installing

Supported Platforms

NoBug has been developed on linux, using gcc. It should be possible to port it to any other POSIX compliant operating system. Platform/compiler specific things are kept optional. Currently Linux with a gcc that conforms to C99 is supported for both 32 and 64 bit architectures.

CPU OS State Notes
x86_64 Debian supported Reference Platform
x86 other Linux supported Please report distro specific problems
armel maemo5 supported check fails in SDK (emulator bug)
x86* MacOS X supported
x86 OpenSolaris mostly Builds, but target check fails
*BSD planned Need volunteer for testing

NoBug has few mandatory dependencies on other software and libraries, some things such as valgrind support are optional and should be automatially detected during the build, i.e., when ./configure is called. Nevertheless it requires pkg-config to be installed, you get some weird errors already at bootstrapping (autoreconf) when this is not available.

Release Tarballs

Releases are available on: http://www.pipapo.org/nobug-releases/

Gpg signed tarballs are being used for distribution. The first step involves checking the signature:

$ gpg nobug-VERSION.tar.gz.gpg

This will produce a nobug-VERSION.tar.gz and report if the signature could be validated.

Since they are built with gnu autotools, the usual build and install procedure will work:

$ tar xzvf nobug-VERSION.tar.gz
$ cd nobug-VERSION
$ mkdir -p build
$ cd build
$ ../configure
$ make
$ make check           # optional, run the testsuite
$ make install         # depending on distribution and setup, do this as root

Development Version via git

You can obtain a development version by using git. The git repository can be cloned via: git://git.pipapo.org/nobug or mirrored at repo.or.cz git://repo.or.cz/nobug.git.

Clone the git repository by:

$ git clone git://git.pipapo.org/nobug

After cloning the repository, then bootstrap the autotools:

$ cd nobug
$ autoreconf -i                # creates the configure file

Then the usual cd build && ../configure && make && make install (as above) will work. Careful users may run make check to run a testsuite before installing.

Keeping Git Up To Date

To update to any new revision, just enter the nobug dir and

$ git pull

After that you can build as above (cd build && ../configure && make && make install). This default pull will update from the master branch which is meant to be an on-going stable version (latest release + bugfixes).

What Is Installed

Currently, NoBug installs the following:

Generating This Documentation

There are Makefile targets for generating the documentation, Either one of the following does what you may expect:

$ make nobug_manual.txt nobug_manual.html nobug_manual.pdf

building the documentation has quite some more dependencies than building NoBug itself. Unless you are a packager you may want to refer to the online doc or the shipped README which is actually this full nobug manual. Generating the documentation requires: gawk, asciidoc, graphviz and some toolchains to further process docbook (dblatex, …).

Using NoBug

Your application will have to include the header file nobug.h before NoBug can be used:

#include <nobug.h>

Once you've included the NoBug API in your application, you'll then have to select a build-level. Build-levels are discussed later, c.f., buildlevel. Build-levels are used to define the amount of information NoBug provides to you. Maximum information is generally required while developing an application and the ALPHA build-level is most apropriate during this phase; whereas the released phase of an application will usually only require sparse information, for which the RELEASE build-level has been conceived.

A build-level must always be specified, otherwise the compiler will complain while attempting to compile your application. You can specifiy a build level in one of two ways: use a define statement in one of your modules, or pass the build-level using the -D flag to your compiler. Assuming we'd like to select the ALPHA build-level in your application, then your module would assume the following form:

#define EBUG_ALPHA
#include <nobug.h>

Subsequently you'll have to link the appropriate library to your application.

A number of different libraries are available to link depending on whether you require to statically or dynamically link, or whether your application is multi or single threaded. The link order is important on your link line. Link the NoBug library after your application's modules. Here's how to statically link, single-threaded applications:

gcc -o mybinary $(WHATEVER_FLAGS) mymodule1.o ... mymodulen.o  ..../libs/libnobug.a

However, for more flexibility in selecting a build-level, you might wish to define various targets in your makefile, one for each build-level. In such cases, the -D flag in your makefile is most appropriate; so your link line for an ALPHA build with multi-threaded support would look like the following:

gcc -o mybinary -DEBUG_ALPHA $(WHATEVER_FLAGS) mymodule1.o ... mymodulen.o  ..../libs/libnobugmt.a

Both libraries must be initialised before they can be used. There are a number of different ways to initialise the NoBug libraries. One of the easiest ways to initialise the NoBug libraries is to use the NOBUG_INIT macro, which must be used before any features can be used or any thread is created. This is discussed in more detail in the multithreading chapter.

So putting all this together, our application using NoBug might look something like the following:

#include <nobug.h>   /* Include the NoBug API */
#define EBUG_ALPHA   /* If we have not used the -D Flag in our makefile */

int main()
{
        NOBUG_INIT;  /* Initialise NoBug libs */

        ...
}

Many aspects of NoBug can be configured by overriding macros before nobug.h is included.

A project using NoBug can use autoconf to check for execinfo and valgrind:

AC_CHECK_HEADER([execinfo.h], AC_DEFINE(HAVE_EXECINFO_H))
PKG_HAVE_DEFINE_WITH_MODULES(VALGRIND, [valgrind])

For Multithreaded programs, you should also check for the availability of pthreads and flavour

ACX_PTHREAD

When the resulting HAVE_PTHREAD, HAVE_EXECINFO_H and HAVE_VALGRIND_H are defined by the configure script, the relevant features become available.

NoBug then defines NOBUG_USE_PTHREAD, NOBUG_USE_VALGRIND and NOBUG_USE_EXECINFO to 1. If you do not want to use any of these features in NoBug, you can override these macros by setting to 0 before including nobug.h.

If NVALGRIND is defined, there will be no support for valgrind.

There are many other macros which can be set and overridden by the user to control behavior. Your help would be appreciated in expanding this documentation if you find some features useful; or simply contact any of the authors.

Example: Using Nobug from a Project using autoconf
PKG_CHECK_MODULES(NOBUGMT_LUMIERA, [nobugmt >= 0.3rc1],
                                 AC_DEFINE(HAVE_NOBUGMT_H),
                                 AC_MSG_ERROR([NoBug pkg-config metadata missing])
)

Checking for Additional Tools

Various peripheral tools can be used by NoBug depending on the requirements of the application and the detail desired by the user. Such tools can provide additional, detailed information on the application and its behaviour. However, some applications may not require such detail and the associated overhead in information, and users may decide to omit excess information by excluding such tools.

At the moment NoBug supports the optional inclusion of gdb, valgrind and support for multi-threaded applications and the information that can be provided by these tools. However, support for other tools may be supplied in the future, e.g. the dbx debugger on OpenSolaris.

Such tools can be easily queried on the system and if they are available on a particular system, they can be used by NoBug to provide even more information on the application using NoBug. If such tools are not available or are not required by the user for one reason or other, then NoBug will happily function as usual, just without the extra information.

Testing the availability of such tools on a particular system can be achieved using autoconf, as illustrated in the following:

NOBUG_USE_VALGRIND
1 Use valgrind
0 Do not use valgrind
NOBUG_USE_PTHREAD
1 Support for multi-thread applications
0 Single-threaded applications
NOBUG_USE_EXECINFO
1 Backtrace information
0 No backtrace information

These macros are then automatically defined when the configuration system provides the associated HAVE_* macros, but can then be overridden by the user, depending on the user's requirements.

Finally, the appropriate library (for either single or multi-threaded applications) is linked to the project.

libnobug Link-in this library for single threaded applications.
libnobugmt Link with this library for multi-threaded applications.

NoBug installed static and dynamic libraries. When your application uses multiple dynamic libraries which use NoBug or you build a dynamic library, then you have to link against the dynamic library.

You can use the pkg-config tool to gather information about NoBug in your build system.

Release builds remove all assertions, but logging is still kept. We make the assumption that bugs which were not covered in alpha and beta builds will not easily show up in releases because the assertions there were not sufficient. Furthermore, end users are not test bunnies and will not provide good bug reports anyway. If there is a problem in a release build, try to track down the cause using a beta build from the same source.

Initialization

Global init

Before anything from NoBug can be used, NoBug must be initialised. This is performed by calling one of the NOBUG_INIT_ macros.

The simpliest such macro among the initialising set is the following:

NOBUG_INIT()

NOBUG_INIT can be called more than once, subsequent calls will be a no-op, thus initialising in main and in libraries won't interfere with one another.

In other words, NOBUG_INIT is usually the first call to NoBug.

Destroying NoBug

Since NoBug is intended to be available throughout its whole lifetime, destroying it is not to be advised. Nevertheless, there is a destroy function void nobug_destroy (void)

to shutdown NoBug, and this frees all resources associated with it. This is mostly used in the NoBug testsuite itself to check for leaks, and it might be useful for other programs which employ some kind of leak checker.

Init logging Flags

If you want to use environment variable controlled debuging, then you have to initialize each defined flag with

NOBUG_INIT_FLAG(flagname)

or

NOBUG_INIT_FLAG_LIMIT(flagname, default)

or one of the C++ compatibility macros.

This is documented later in the logging configuration chapter.

Threads

In Multithreaded programs you should assign an identifier to each thread. A thread identifier is a string which will be automatically appended with an underscore and a incrementing integer. It is is created with:

NOBUG_THREAD_ID_SET(name)

Calling NOBUG_THREAD_ID_SET("worker") will yield in a thread identifier worker_1 for example.

If you don't set an identifier, then NoBug will assign an automatic one. This is further documented in the multi threading section of this manual.

Example: Initialization
#include "nobug.h"
NOBUG_DEFINE_FLAG(example);

...

int main()
{
    NOBUG_INIT();
    NOBUG_THREAD_ID_SET("main");
    NOBUG_INIT_FLAG(example);

    ...
}

Debugging Information Granuality: The Build Levels

There are three different levels of debugging information available: alpha, beta and release. One of these levels must be specified before compiling, otherwise an error while compiling will occur.

ALPHA

This debugging level is envisaged for the development phase of a project where exhaustive testing and logging are required.

BETA

This debugging level is more appropriate for projects beyond the development phase and ready for trials in the field and users willing to test the software.

RELEASE

This level is for final, end-users.

Select a Build Level

A logging level can be selected by either using a define in one of the applications' modules, or by passing the appropriate level using the -D switch to the compiler:

ALPHA -DEBUG_ALPHA (#define EBUG_ALPHA)
BETA -DEBUG_BETA (#define EBUG_BETA)
RELEASE -DNDEBUG (#define NDEBUG)

If none of the above switches has been set, NoBug will abort the compilation with an error.

Logging

Nearly all NoBug Macros emit some log message. NoBug gives the user fine grained control over these log messages to display only interesting information without loosing details.

Log messages can be routed to various destinations. The following destintaions are available:

RINGBUFFER

The underlying storage backend. Messages are appended to the end of the buffer, overwriting older messages at the front of the buffer. NoBug comes with a highly efficient ringbuffer implementation. This ringbuffer is temporary by default but can be made persistent on disk which can be inspected with the nobug_rbdump tool.

CONSOLE

This is either just stderr, or, if running under a supported debugger, the debuggers facilities to print messages will be used.

FILE

The user can open files for log messages.

SYSLOG

Messages are sent to the standard system logging daemon.

APPLICATION

There are hooks which allow the programmer to catch logmessages and display them in an application which are defined by the application.

Each logmessage has a priority describing its severity in the same way as syslog messages do.

All non-fatal messages are associated with a programmer defined flag describing the source of the message (subsystem, module, …).

Putting this all together: A user can define which source/flag will be logged at what priority level and to which destination. To make this all easier, NoBug tries to provide reasonable defaults.

Configuration

Log Levels

Each log macro has an explicit or implicit log-level which correspondends to syslog levels. Logging is only emitted when the message is more severe or the same as a defined limit.

Table: Default levels for logging
ALPHA BETA RELEASE
ringbuffer TRACE INFO NOTICE ringbuffer must always be most verbose
console INFO NOTICE -1 no log to console in release
file TRACE NOTICE WARNING
syslog -1 NOTICE WARNING no syslog for test runs
application INFO WARNING ERROR

Depending on the build level, there is a default logging target and a default limit which is selected when the user doesn't specify one.

The following default limits are available:

The default targets are:

You can override all these values with your own values. As an alternative, NOBUG_LOG_LIMIT and NOBUG_LOG_TARGET can be defined before including "nobug.h" to override all defaults.

Log Flags

Flags are used to inform NoBug about subsystems/modules or even finer grained sections of the code. These are referred to as channels in other logging libraries.

A flag should be declared in a headerfile using the following mechanism:

NOBUG_DECLARE_FLAG(flagname)

It is advisable to do so in one of your header files.

Furthermore, the flag must be defined in some implementation file by using one of the following schemes:

NOBUG_DEFINE_FLAG(flagname)

or:

NOBUG_DEFINE_FLAG_LIMIT(flagname, limit)

Moreover, macros are available that accept a parent flag as a parameter, which is then used to initialize the defaults from another flag:

NOBUG_DEFINE_FLAG_PARENT(flagname, parent)

or

NOBUG_DEFINE_FLAG_PARENT_LIMIT(flagname, parent, limit)

This can be used to create hierachies of flags

C support, C logflags

Additional macros are available for applications written in C++:

NOBUG_CPP_DEFINE_FLAG(name)
NOBUG_CPP_DEFINE_FLAG_PARENT(name, parent)
NOBUG_CPP_DEFINE_FLAG_LIMIT(name, default)
NOBUG_CPP_DEFINE_FLAG_PARENT_LIMIT(name, parent, default)

These macros statically initialize the flags when they are defined, there is no need to call NOBUG_INIT_FLAG() (see below).

Force declarations only

When the the following preprocessor constant is defined to be 1:

NOBUG_DECLARE_ONLY

then all definitions here (NOBUG_DEFINE_*) become declarations only. When this is defined to be 0 (which is the default) then all definitions behave as described. This can be used to construct a headerfile which only contains definitions, but, by default, yield only declarations. This provides one convenient single point to maintain flag configurations.

Example: Maintaining flags in a single header flags.h
#include <nobug.h>

/*
 if not included from flags.c then declare the flags,
 else define them
 */
#ifndef FLAGS_C
#define NOBUG_DECLARE_ONLY 1
#endif

/* use only DEFINE_FLAG here */
NOBUG_DEFINE_FLAG(example);

/*
 Reset it to 0 to cause no trouble
 */
#ifndef FLAGS_C
#undef NOBUG_DECLARE_ONLY
#define NOBUG_DECLARE_ONLY 0
#endif
Example: flags.c
#define FLAGS_C
#include "flags.h"
...
Logging Flag Initialization

Next you should call

NOBUG_INIT_FLAG(flagname)

or

NOBUG_INIT_FLAG_LIMIT(flagname, default)

once at the start of your program for each flag.

For flags defined with NOBUG_DEFINE_FLAG(flagname) the defaults are initialized as in the table above, while NOBUG_DEFINE_FLAG_LIMIT(flagname, level) is used to initialize the default target (depending on build level) to level.

Control what gets logged

The NOBUG_INIT_FLAG… calls parsing the environment variable NOBUG_LOG to configure what gets logged at runtime. The syntax is as following:

Example: Formal Syntax for log control
 logdecl_list --> logdecl, any( ',' logdecl_list).

 logdecl --> flag, opt(limitdecl, any(targetdecl)).

 flag --> "identifier of a flag".

 limitdecl --> ':', "LIMITNAME".

 targetdecl --> '@', "targetname", opt(targetopts).

 targetopts --> '(', "options for target", ')', opt(targetopts).

Roughly speaking, NOBUG_LOG contains a comma separated list of declarations for flags which are the name of the flag followed by a limit which is written in all uppercase letters and preceeded by a colon, followed by target declarations which are names of the targets, introduced by a at sign. Target declarations can have option, described in the next section. Limit and target declarations are optional and then choosen from the defaults table above. These defaults are currently just an guess what should be useable and might be redefined in future.

Targets and Options

The Following options are available:

@ringbuffer
(file=filename) set filename backing the ringbuffer
(size=nnn) set size of the ringbuffer
(append) don't erase existing ringbuffer
(keep) keep file after application end
(temp) unlink file instantly at creation
@console
(fd=n) redirect console output to fd n
@file
(name=filename) log to filename
(append) append to (existing) log
@syslog
(ident=name) global prefix for syslog
(cons) log to system console if syslog is down
(pid) include pid in log
(perror) log to stderr as well
Example: How the NOBUG_LOG is used
# set the limit of the default target a default limit (see table above)
NOBUG_LOG='flag,other'

# set the limit of the default target to DEBUG
NOBUG_LOG='flag:DEBUG'

# set console and syslog limits for flag to DEBUG
NOBUG_LOG='flag:DEBUG@console@syslog'

# trace 'other' to a persistent ringbuffer
NOBUG_LOG='other:TRACE@ringbuffer(file=log.rb)(size=8192)(keep)'
Example: Using log flags (example.c)
#include "nobug.h"

NOBUG_DEFINE_FLAG (test);

int main()
{
   /* NOBUG_INIT;  // not needed because of NOBUG_INIT_FLAG */
   NOBUG_INIT_FLAG (test);

   TRACE (test, "Logging enabled");
   TRACE (NOBUG_ON, "Always on");
}
Example: test it:
$ cc -DEBUG_ALPHA -lnobug example.c
$ ./a.out
0000000002: TRACE: example.c:11: main: Always on

$ NOBUG_LOG=test:TRACE ./a.out
0000000001: TRACE: example.c:10: main: Logging enabled
0000000002: TRACE: example.c:11: main: Always on

Predefined Flags

There are some debugging flags which are predefined by NoBug.

NOBUG_ON

The flag NOBUG_ON is always enabled at LOG_DEBUG level. This is static and can not be changed.

NOBUG_ANN

The flag NOBUG_ANN is used for the source annotations. This is static and can not be changed. It differs from NOBUG_ON as in never logging to syslog and only define a LOG_WARNING limit for the application callback.

nobug (flag)

Actions on NoBug itself will be logged under the nobug flag itself. When you want to see whats going on (useful to check if you call NOBUG_INIT_FLAG() on all flags) you can enable it with NOBUG_LOG=nobug:TRACE.

Macros

The NoBug interface is almost completely implemented using preprocessor macros. This is required because NoBug uses the __FILE__, __LINE__ and __func__ macros to log information on the current file, line number and function. Moreover, all the flat namespace uppercase identifiers make it ease to recognise the macros in source code.

All macros are available without condition with a NOBUG_… prefix. Many macros (the common cases) are also available without this prefix as a convenience, however macros without this prefix must not have been previously defined. When NOBUG_DISABLE_SHORTNAMES is defined before including nobug.h, then only the NOBUG_ prefixed macros are available and the short forms will never be defined.

A set of macros are provided by NoBug that are postfixed by …_IF. These macros have the following form:

They perform the desired action only if when is true. For example:

The assertion will only be performed if foo is non NULL.

NoBug also also contains a facility to pass the source context (file, line, function) around, this can be used to write functions which handle things where one is more interested in the context of the caller than the location where the macros appears.

This macros are postfixed with …_CTX and take an extra context parameter (usually at last but before the logging format specifier and any variable argument list). The context parameter must be of type const struct nobug_context.

When the _CTX context form is used together with the conditional _IF form then the suffix of the macros is always …_IF_CTX.

The macros which take a context have no short form and must always be prefixed with NOBUG_….

Parameters types

We use names for parameters which describe their type. These names are orthogonal through all macro definitions.

when Assertion is only performed if expression when is true at runtime
expr Test without side effects
flag Flag to enable custom logging groups
type Data type to be checked as a single identifier name
pointer Pointer to type
lvl Log level
depth Depth for invariants and dumps
context Source context of type struct nobug_context
printf-like format string followed by its arguments

Source Contexts

NOBUG_CONTEXT
NOBUG_CONTEXT_NOFUNC

NoBug passes information about the source location of a given statement in const struct nobug_context structures. These can be generated with NOBUG_CONTEXT or NOBUG_CONTEXT_NOFUNC. The later one doesn't define a function name and must be used when the function context is not available like in static initialization etc..

Assertions

CHECK
CHECK(expr, ...)
CHECK_IF(when, expr, ...)

This assertion is never optimized out. Its main purpose is for implementing testsuites where one want to assert tests independent of the build level

REQUIRE
REQUIRE(expr, ...)
REQUIRE_IF(when, expr, ...)
NOBUG_REQUIRE_CTX(expr, context,...)
NOBUG_REQUIRE_IF_CTX(when, expr, context, ...)

Precondition (input) check. Use these macros to validate input a function receives. The checks are enabled in ALPHA and BETA builds and optimized out in RELEASE builds.

ENSURE
ENSURE(expr, ...)
ENSURE_IF(when, expr, ...)
NOBUG_ENSURE_CTX(expr, context, ...)
NOBUG_ENSURE_IF_CTX(when, expr, context, ...)

Postcondition (progress/output) check. Use these macros to validate the data a function produces (example: return value). ENSURE is enabled unconditionally in ALPHA builds and optimized out in BETA builds for scopes which are tagged as CHECKED.

The ENSURE_IF variants are enabled in ALPHA and BETA builds.

In RELEASE builds this checks are always optimized out, scopes tagged as UNCHECKED are not permitted.

ASSERT
ASSERT(expr, ...)
ASSERT_IF(when, expr, ...)
NOBUG_ASSERT_CTX(expr, context, ...)
NOBUG_ASSERT_IF_CTX(when, expr, context, ...)

Generic check. Use these macros when you want to validate something which doesn't fall into one of the above categories. A example is when a library function can return a unexpected result (scanf with syntax error in the formatstring, when a constant/literal formatstring is expected). The checks are enabled in ALPHA and BETA builds and optimized out in RELEASE builds.

assert
assert(expr)

NoBug overrides the standard assert macro, using NOBUG_ASSERT. This is just a compatibility feature, its use is not suggested.

INVARIANT
INVARIANT(type, pointer, depth)
INVARIANT_IF(when,type, pointer, depth)
INVARIANT_ASSERT(expr, ...)

Checking invariants. You can provide more complex checking functions which test the validity of datastructures. Invariants are only enabled in ALPHA builds for scopes which are not tagged as CHECKED and otherwise optimized out.

TODO: describe how to create invariant checks

Logging Macros

Logging targets a flag (except for ECHO) and is done at a log-level relating to syslog levels.

Note
there is no logging macro for LOG_EMERG, this is only used by the assertions as fatal message
ECHO
ECHO(...)

Never optimized out, logs at LOG_NOTICE level. Its main purpose is for implementing testsuites where one want to print and log messages independent of the build level

ALERT
ALERT(flag, ...)
ALERT_IF(when, flag, ...)
NOBUG_ALERT_CTX(flag, context, ...)
NOBUG_ALERT_IF_CTX(when, flag, context, ...)

This is the most critical condition an application might log. This might be used if an error occurs which can not be handled except a safe shutdown for example.

CRITICAL
CRITICAL(flag, ...)
CRITICAL_IF(when, flag, ...)
NOBUG_CRITICAL_CTX(flag, context, ...)
NOBUG_CRITICAL_IF_CTX(when, flag, context, ...)

An error which can not be handled occured but the application does not need to be shutdowen, perhaps waiting for an operator to fix the cause.

ERROR
ERROR(flag, ...)
ERROR_IF(when, flag, ...)
NOBUG_ERROR_CTX(flag, context, ...)
NOBUG_ERROR_IF_CTX(when, flag, context, ...)

Application takes a error handling brach

WARN
WARN(flag, ...)
WARN_IF(when, flag, ...)
NOBUG_WARN_CTX(flag, context, ...)
NOBUG_WARN_IF_CTX(when, flag, context, ...)

Rare, handled but unexpected branch

INFO
INFO(flag, ...)
INFO_IF(when, flag, ...)
NOBUG_INFO_CTX(flag, context, ...)
NOBUG_INFO_IF_CTX(when, flag, context, ...)

Message about program progress

NOTICE
NOTICE(flag, ...)
NOTICE_IF(when, flag, ...)
NOBUG_NOTICE_CTX(flag, context, ...)
NOBUG_NOTICE_IF_CTX(when, flag, context, ...)

More detailed progress message

TRACE
TRACE(flag, ...)
TRACE_IF(when, flag, ...)
NOBUG_TRACE_CTX(flag, context, ...)
NOBUG_TRACE_IF_CTX(when, flag, context, ...)

Very fine grained messages

Note
that TRACE corresponds to LOG_DEBUG, because using DEBUG could be ambiguous.
LOG
NOBUG_LOG_CTX(flag, lvl, context, ...)
NOBUG_LOG_IF_CTX(when, flag, lvl, context, ...)

Generic logging macro which takes the level explicitly, avoid this, unless you implement your own logging facilities.

LOG_BASELIMIT
NOBUG_LOG_BASELIMIT_ALPHA
NOBUG_LOG_BASELIMIT_BETA
NOBUG_LOG_BASELIMIT_RELEASE
NOBUG_LOG_BASELIMIT

anything more detailed than this base limits will be optimized out. This is used to reduce the logging overhead for RELEASE builds. By default the limit is set to LOG_DEBUG for ALPHA and BETA builds, so all logging is retained and LOG_NOTICE in RELEASE builds to log the application progress only coarsely then.

This macros can be defined before including nobug.h to some other log level (as defined in syslog.h).

Dumping Datastructures

TODO How to write DUMP handlers

One can write functions for dumping complex datastructures using the NoBug facilities. This is done by writing a custom function for each datastructure to be dumped which may recursively call other dumping functions. There are macros for logging within such a dumper function and for initiating a dump of a given datastructure.

A dump function has the prototype:

void
nobug_NAME_dump (const struct NAME* POINTER,
                 const int DEPTH,
                 const struct nobug_context CONTEXT,
                 void* EXTRA);

where NAME is the identifier for what you want to dump, POINTER is a pointer to the data to be dumped, DEPTH is a integer which will be decremented when recursing into the datastructure dumper (your dump function does that, see below) to limit the recursion depth, CONTEXT is a source context generated by nobug when you call DUMP() and EXTRA is a pointer transparently passed around you can use to store some extra state. // src/nobug.h:341 // .DUMP DUMP(flag, type, pointer, depth, extra) DUMP_IF(when, flag, type, pointer, depth, extra)

This macros call a datastructure dump of the object (pointer) in question. DUMP is only available in ALPHA and BETA builds, DUMP_IF is also enabled for the RELEASE builds.

extra is a void* which is transparently passed around and can be used to pass some state around. NoBug does not touch it.

DUMP_LOG
DUMP_LOG(...)
DUMP_LOG_IF(when, ...)

Any output from DUMP handlers should be done by these macros.

Dumping is by default done on level LOG_DEBUG, this can be overridden by defining NOBUG_DUMP_LEVEL to some other level.

Example: How to use the DUMP facilities
struct STRUCTNAME
{
  int INTEGER_MEMBER;
  char * STRING_MEMBER;
  struct STRUCTNAME* next;
}

then you define a function like:

void
nobug_STRUCTNAME_dump (const struct STRUCTNAME* self,
                       const int depth,
                       const struct nobug_context context,
                       void* extra)
{
  // check for self != NULL and that the depth
  // limit did not exceed in recursive datastructures
  if (self && depth)
  {
    // you may or may not do something with the extra parameter here
    // extra is transparently passed around
    (void) extra;

    // use DUMP_LOG not LOG to print the data
    DUMP_LOG("STRUCTNAME %p: int is %d, string is %s", self,
                             self->INTEGER_MEMBER,
                             self->STRING_MEMBER);

    // now recurse with decremented depth
    nobug_STRUCTNAME_dump (self->next, depth-1, context, extra);
  }
}

now you can use the DUMP() macros within the code

example()
{
  struct STRUCTNAME foo;
  init(&foo);

  // extra can be anything, NULL is suggested when you don't use it
  DUMP (my_flag, STRUCTNAME, &foo, 2, NULL);
}

Source Annotations

One can tag features as:

DEPRECATED
DEPRECATED(...)

Something which shouldn't be used in future

UNIMPLEMENTED
UNIMPLEMENTED(...)

not yet finished feature

FIXME
FIXME(...)

known bug to be fixed later

TODO
TODO(...)

enhancement to be done soon

PLANNED
PLANNED(...)

future enhancement

NOTREACHED
NOTREACHED(...)

used to tag code-path which shall be never executed.

ELSE_NOTREACHED
ELSE_NOTREACHED(...)

same as else NOTREACHED(), but wholly optimized out in release builds.

The advantage of this tagging over plain source comments is that we can take some actions if we run in such a tag at compile or runtime:

the action to be taken when such a macro is hit depends on the build level:

ALPHA BETA RELEASE
DEPRECATED log nothing wont compile
UNIMPLEMENTED abort abort wont compile
FIXME log wont compile wont compile
TODO log log wont compile
PLANNED log nothing nothing
NOTREACHED abort abort removed

Legend:

Scope Checks

The programmer can tag any scope as UNCHECKED or CHECKED. In ALPHA and BETA builds, a global UNCHECKED is implied. In RELEASE builds, UNCHECKED scopes are not allowed.

Table: Assertions active depending on Build level and Scope
ALPHA BETA RELEASE
UNCHECKED Preconditions, Postconditions, Invariants Preconditions, Postconditions compiling will abort
CHECKED Preconditions, Postconditions Preconditions

Fault injection

NoBug has some macros which can be used to simulate errorneous behaviour:

INJECT_GOODBAD
INJECT_GOODBAD(expr, good, bad)

substitutes to an expression and returns good when expr is false and bad when expr is true. In BETA and RELEASE builds good is always returned.

INJECT_FAULT
INJECT_FAULT(expr, bad)

substitutes to a statement which executes bad when expr is true. Optimitzed out in BETA and RELEASE builds.

INJECT_LEVEL

In both cases, when a fault is injected it will be logged at NOBUG_INJECT_LEVEL (default: LOG_NOTICE). This can be defined before including nobug.h to override it.

Resource Tracking

With little effort, NoBug can watch all kinds of resources a program uses. This becomes useful for resources which are distributed over a multithreaded program. Resource tracking includes logging actions on resource and checking locking policies over acquired resources. Resource logging is active in ALPHA and BETA builds when NOBUG_RESOURCE_LOGGING is defined to 1 (the default). The resource tracker which supervises locking policies is only enabled in ALPHA builds.

Concepts

Resources are abstracted, NoBug has little knowledge about the semantics of a resource, it only keeps records of resources and the code using it and ensures basic constraints. Detailed usage checks of resource have to be done with other NoBug facilities.

Resources are identified by a arbitrary identifier which is just a pointer. Additionally a name, the type and the source locations which announced the resource are stored.

Code which wants to use a resource calls a enter macro with its own identifier and state, then might alter the state and finally a leave macro when finished with it.

When a resource is used one has to pass one of this states:

Possible state transitions:

resource-transistinons.png

Notes

There are small race conditions between logging and checking resource tracking and the actual call to the resource using function. This is a design decision there is no way to account for this exactly when the function call may block.

The Resource Tracker relies on proper announce/forget and enter/leave are properly pairing. The programmer should ensure that this is done right, otherwise the results are unpredictable.

Resource tracking macros

Unless the user defines NOBUG_RESOURCE_LOGGING to 0 each of the above macros will emit a log message at NOBUG_RESOURCE_LOG_LEVEL which defaults to LOG_DEBUG.

RESOURCE_HANDLE
RESOURCE_HANDLE(name)
RESOURCE_HANDLE_INIT(name)
RESOURCE_USER(name)
RESOURCE_USER_INIT(name)

Define and initialize handles for to track resources.

name

identifer to be used for the handle

There are two kinds of handles, each resource itself is abstracted with a RESOURCE_HANDLE and every access to this resources is tracked through a RESOURCE_USER handle. These macros takes care that the declaration is optimized out in the same manner as the rest of the resource tracker would be disabled. You can still instantiate handles as struct nobug_resource_record* or struct nobug_resource_user* in structures which must have a constant size unconditional of the build level. The two *_INIT macros can be used to initialize resource handles and are optimized out when the resource tracker gets disabled.

RESOURCE_ANNOUNCE
RESOURCE_ANNOUNCE(flag, type, name, identifier, handle)
NOBUG_RESOURCE_ANNOUNCE_RAW(flagptr, type, name, ptr, handle)
NOBUG_RESOURCE_ANNOUNCE_RAW_CTX(flagptr, type, name, ptr, handle, context)

Publishes resources.

flag

the NoBug flag name which turns logging on for this macro

type

a string which should denote the domain of the resource, examples are "file", "mutex", "lock", "database" and so on

name

the actual name of a named resource this as string which together with type forms a unique identifier of the resource. type and name must be available through the entire lifetime of the resource, using literal strings is recommended

identifier

a pointer which should be unique for this resource, any kind of pointer will suffice, it is only used for identification. In multithreaded applications the thread identifier becomes an additional identifier

handle

a NOBUG_RESOURCE_HANDLE which will be initialized to point to the newly created resource.

Resources must be unique, it is a fatal error when a resource it tried to be announced more than one time.

RESOURCE_ANNOUNCE() acts like the head of a C loop statement, it ties to the following (block-) statement. Leaving and the user defined following statement are atomic. This statement must not be left by break, return or any other kind of jump.

RESOURCE_FORGET
RESOURCE_FORGET(flag, handle)
NOBUG_RESOURCE_FORGET_RAW(flagptr, handle)
NOBUG_RESOURCE_FORGET_RAW_CTX(flagptr, handle, context)

Removes resources that have become unavailable from the registry.

flag

the NoBug flag which turns logging on for this macro

handle

the NOBUG_RESOURCE_HANDLE used to track this resource

The resource must still exist and no users must be attached to it, else a fatal error is raised.

RESOURCE_FORGET() acts like the head of a C loop statement, it ties to the following (block-) statement. Leaving and the user defined following statement are atomic. This statement must not be left by break, return or any other kind of jump.

RESOURCE_ENTER
RESOURCE_ENTER(flag, announced, user, state, handle)
NOBUG_RESOURCE_ENTER_CTX(flag, resource, user, state, handle, context)

Acquire a resource.

flag

nobug flag which turns logging on for this macro

announced

the handle set by RESOURCE_ANNOUNCE

user

a free-form identifier

state

the initial state, one of NOBUG_RESOURCE_WAITING, NOBUG_RESOURCE_TRYING, NOBUG_RESOURCE_EXCLUSIVE, NOBUG_RESOURCE_RECURSIVE or NOBUG_RESOURCE_SHARED

handle

a NOBUG_RESOURCE_HANDLE which will be initialized to the entering node

RESOURCE_ENTER() acts like the head of a C loop statement, it ties to the following (block-) statement. Leaving and the user defined following statement are atomic. This statement must not be left by break, return or any other kind of jump.

RESOURCE_WAIT
RESOURCE_WAIT(flag, resource, user, handle)
NOBUG_RESOURCE_WAIT_CTX(flag, resource, user, handle, context)

This is just an alias for RESOURCE_ENTER(flag, resource, user, NOBUG_RESOURCE_WAITING, handle)

Example: How to use it
RESOURCE_WAIT(flag, resource, user, handle);
if (lock_my_resource() == ERROR)
  NOBUG_RESOURCE_LEAVE(flag, handle);
else
  RESOURCE_STATE(flag, NOBUG_RESOURCE_EXCLUSIVE, handle);
RESOURCE_TRY
RESOURCE_TRY(flag, resource, user, handle)
NOBUG_RESOURCE_TRY_CTX(flag, resource, user, handle, context)

This is just an alias for RESOURCE_ENTER(flag, resource, user, NOBUG_RESOURCE_TRYING, handle). Trying on a resource is similar to waiting but will not trigger a deadlock check. This can be used when a deadlock is expected at runtime and one handles this otherwise (by a timed wait or something like that).

RESOURCE_STATE
RESOURCE_STATE(flag, entered, state)
NOBUG_RESOURCE_STATE_CTX(flag, state, entered, context)
NOBUG_RESOURCE_STATE_RAW(flagptr, state, entered)
NOBUG_RESOURCE_STATE_RAW_CTX(flagptr, nstate, entered, context)

Changes resource's state.

flag

is nobug flag which turns logging on for this macro

state

the new state Note that only certain state transitions are allowed, see discussion/diagram above

entered

the handle set by RESOURCE_ENTER

RESOURCE_STATE() acts like the head of a C loop statement, it ties to the following (block-) statement. Leaving and the user defined following statement are atomic. This statement must not be left by break, return or any other kind of jump.

RESOURCE_LEAVE
RESOURCE_LEAVE(flag, handle){}
NOBUG_RESOURCE_LEAVE_RAW(flagptr, handle){}
NOBUG_RESOURCE_LEAVE_RAW_CTX(flagptr, handle, context){}

Disconnect from a resource identified with its handle.

flag

nobug flag which turns logging on for this macro

handle

the handle you got while entering the resource

RESOURCE_LEAVE() acts like the head of a C loop statement, it ties to the following (block-) statement. Leaving and the user defined following statement are atomic. This statement must not be left by break, return or any other kind of jump.

Example: How to use it
NOBUG_RESOURCE_LEAVE(flag, handle)
  {
    unlock_my_resource();
  }
RESOURCE_ASSERT_STATE
RESOURCE_ASSERT_STATE(resource, state)
RESOURCE_ASSERT_STATE_IF(when, resource, state)
NOBUG_RESOURCE_ASSERT_STATE_CTX(resource, state, context)
NOBUG_RESOURCE_ASSERT_STATE_IF_CTX(when, resource, state, context)

Assert that we have a resource in a given state. For multithreaded programms the topmost state of the calling thread is checked, for non threadeded programs the most recent state on resource is used.

when

Condition which must be true for testing the assertion

resource

Resource handle

state

The expected state

RESOURCE_DUMP
NOBUG_RESOURCE_DUMP(flag, handle)
NOBUG_RESOURCE_DUMP_IF(when, flag, handle)

Dump the state of a single resource.

when

Condition which must be true to dump the resource

flag

Nobug flag for the log channel

handle

handle of the resource to be dumped

RESOURCE_DUMPALL
NOBUG_RESOURCE_DUMPALL(flag)
NOBUG_RESOURCE_DUMPALL_IF(when, flag)

Dump the state of all resources.

when

Condition which must be true to dump the resources

flag

Nobug flag for the log channel

RESOURCE_LIST
NOBUG_RESOURCE_LIST(flag)
NOBUG_RESOURCE_LIST_IF(when, flag)

List all registered resources.

when

Condition which must be true to list the resources

flag

Nobug flag for the log channel

Example: How to use the Resourcetracker
NOBUG_DEFINE_FLAG_LIMIT(test, LOG_DEBUG);

void example()
{
  // define a mutex and announce it
  pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
  RESOURCE_HANDLE(resource);

  // 'example' is just a pointer to this function which suffices as unique id
  RESOURCE_ANNOUNCE(test, "mutex", "my_mutex", example, resource);

  // the following would be done in a different thread in a real program
  // define a handle
  RESOURCE_HANDLE(enter);

  // announce that we want to use the resource
  // &enter also suffices as unique pointer, which is all we need as identifer here
  RESOURCE_WAIT(flag, resource, &enter, enter)
    {
      // lock() might block
      pthread_mutex_lock (&my_mutex);
      // assume no errors, got it, change the state
      RESOURCE_STATE(test, NOBUG_RESOURCE_EXCLUSIVE, enter);
    }

  ////////////////////////////////////////
  // program does something useful here //
  ////////////////////////////////////////

  // we don't need it anymore
  RESOURCE_LEAVE(test, enter)  // << no semicolon
    pthread_mutex_unlock (&my_mutex);

  // back in the main thread
  RESOURCE_FORGET(test, resource);                         // remove the resource from the public registry
}

Deadlock Detection

The Resource Tracker is able to detect potential deadlocks. This is done by learning the relations between locks (precedence). A possible deadlock results in a log message and a fatal abort. Note that only waiting on resources can lead to a deadlock. Deadlock detection is implemented in the Resource Tracker and active in ALPHA builds and optimized out on any other build level.

For details about the deadlock detection algorithm see Appendix: Resource Tracking Alorithm.

Callbacks

NoBug provides callbacks, applications can use these to present logging information in some custom way or hook some special processing in. The callbacks are initialized to NULL and never modified by NoBug, its the solve responsibility of the user to manage them.

Caution
There are certain constraints what and what not can be done in callbacks documented below which must be followed.
type of logging callbacks
typedef void (*nobug_logging_cb)(const struct nobug_flag* flag, int priority, const char *log, void* data)

used for the logging callbacks

flag

Flag structure which defines the logging configuration for this event

priority

Log level of the current event

log

Pointing to the current log line in the ringbuffer or NULL

data

Global pointer defined by the user, passed arround (see below)

type of abort callback
typedef void (*nobug_abort_cb)(void* data)

used for the abort callback

data

Global data defined by the user, passed arround (see below)

passing data to callbacks
void* nobug_callback_data

This global variable is initialized to NULL and will never be touched by NoBug. One can use it to pass extra data to the callback functions.

callback when logging
nobug_logging_cb nobug_logging_callback

This callback gets called when something gets logged. NoBug will still hold its mutexes when calling this hook, calling NoBug logging or resource tracking functions from here recursively will deadlock and must be avoided. The log parameter points to the logging message in the ringbuffer. Unlike other logging targets it is not automatically limited to the log level configured in the flag but called unconditionally. The callback should implement its own limiting.

When one wants to do complex calls which may include recursion into logging and resource tracking functions, the intended way is to pass contextual information possibly including a copy of the log parameter in NOBUG_THREAD_DATA to the postlogging callback (see below). Other internal NoBug facilties, like the ringbuffer etc, are protected by the mutexes and may be accessed from this function.

callback after logging
nobug_logging_cb nobug_postlogging_callback

This callback gets called after something got logged. The log parameter is always NULL and all NoBug mutexes are released. This means that this function may call any complex things, including calling logging and resource tracking, but may not call internal NoBug facilities. Contextual created in the nobug_logging_callback and stored in NOBUG_THREAD_DATA can be retrieved here and may need to be cleaned up here.

callback for aborting
nobug_abort_cb nobug_abort_callback

This callback gets called when the application shall be terminated due an error. It can be used to hook exceptions or similar things in. When it returns, abort() is called.

Important
Errors detected by NoBug are always fatal. If one handles and possible throws an exception here, the application must shut down as soon as possible. Most causes for aborts are optimitzed out in RELEASE builds.

Tool Macros

NOBUG_FLAG_RAW
NOBUG_FLAG_RAW(ptr)

Using this macro one can pass a direct pointer to a flag where a name would be expected. This is sometimes convinient when flag pointers are passed around in management strutures and one wants to tie logging to dynamic targets.

NOBUG_DEFINE_FLAG(myflag);
...
struct nobug_flag* ptr = &NOBUG_FLAG(myflag);
TRACE(NOBUG_FLAG_RAW(ptr), "Passed flag by pointer")
Backtraces
BACKTRACE
NOBUG_BACKTRACE_CTX(context)

The backtrace macro logs a stacktrace using the NoBug facilities. This is automatically called when NoBug finds an error and is due to abort. But one might call it manually too.

Aborting
NOBUG_ABORT_

This is the default implementation for aborting the program, it first syncs all ringbuffers to disk, then calls the abort callback if defined and then abort().

NOBUG_ABORT

If not overridden, evaluates to NOBUG_ABORT_. One can override this before including nobug.h to customize abortion behaviour. This will be local to the translation unit then.

NOBUG_ALPHA_COMMA
NOBUG_ALPHA_COMMA(something)
NOBUG_ALPHA_COMMA_NULL

Sometimes it is useful to have initializer code only in ALPHA builds, for example when you conditionally include resource handles only in ALPHA versions. An initializer can then use this macros to append a comman and something else only in ALPHA builds as in: struct foo = {"foo", "bar" NOBUG_ALPHA_COMMA_NULL };

NOBUG_IF_*
NOBUG_IF_ALPHA(...)
NOBUG_IF_NOT_ALPHA(...)
NOBUG_IF_BETA(...)
NOBUG_IF_NOT_BETA(...)
NOBUG_IF_RELEASE(...)
NOBUG_IF_NOT_RELEASE(...)

This macros allow one to conditionally include the code in (…) only if the criteria on the build level is met. If not, nothing gets substituted. Mostly used internally, but can also be used for custom things.

Multithreading

It is important that NoBug protects certain operations with locks in multithreaded programs. You have to ensure that HAVE_PTHREAD_H is defined by the configuration system and use the libnobugmt library for linking. It is particular important that libraries using NoBug are compiled with HAVE_PTHREAD_H enabled when they are intended to be used in multithreaded programs.

When Multithreading is used, log messages contain a identifier of the originating thread. This identifier should be set by

NOBUG_THREAD_ID_SET
NOBUG_THREAD_ID_SET(name)
name

New name for the thread

Nobug will assemble a unique identifier by appending a underscore and a number to name, for example NOBUG_THREAD_ID_SET("gui") will result in a identifier like "gui_5". When you don't set a thread identifier, then NoBug assigns one automatically with the name thread preprended if needed. Thread identifiers may be reset with a new call to this macro.

NOBUG_THREAD_ID_GET
NOBUG_THREAD_ID_GET

Will return a const char* of the thread id in multithreaded programs and a pointer to a literal empty string in singlethreaded programs.

NOBUG_THREAD_DATA
NOBUG_THREAD_DATA

Evaluates to a variable of type void* which can be used to store thread local information. This is useable for callbacks which may prepare context information to be reused later.

This macro is also available in singlethreaded programs, refering to a single global variable.

Nobug initializes this variable to NULL and then touches it never again.

Dumping Persistent Ringbuffers

NoBug installs the nobug_rbdump tool for dumping the content of a persistent ringbuffer. It is invoked with the filename of the ringbuffer, the content is then printed to stdout.

Testsuite

TODO Documentation to be written, use the source Luke!

NoBug maintains a test.sh script which drives extensive testsuites. Look at into the tests/ folder about how to apply this.

Best Practices

Note
this section is very work in progress
Workflow
  1. Development

    • Write a testsuite, build your program with -O0 -g -DEBUG_ALPHA and run the testsuite under valgrind control. Hack until the program mets the specifications defined by the testsuite.

  2. Beta Test

    • Build with desired optimization level and -g -DEBUG_BETA and give the program to your beta testers.

  3. Release

    • Build it with optimization and without -g -DEBUG_*

What and when to check
Tips & Tricks

Appendix

The Resource Tracking Algorithm

Each resource registers a global resource_record.

Every new locking path discovered is stored as resource_node structures which refer to the associated resource_record.

Threads keep a trail of resource_user strcutures for each resource entered. This resource_user struct refer to the resource_nodes and thus indirectly to the associated resource_record.

The deadlock checker uses this information to test if the acqusition of a new resource would yield a potential deadlock.

Entering Resources

In multithreaded programs, whenever a thread wants to wait for a resource_record the deadlock checker jumps in.

The deadlock checking algorithm is anticipatory as it will find and abort on conditions which may lead to a potential deadlock by violating the locking order learned earlier.

Each thread holds a stack (list) of each resource_user it created. Leaving a resource will remove it from this stacklist.

Each resource_record stores the trail which other resource_records are already entered. This relations are implemented with the resource_node helper structure.

First we find out if there is already a node from the to be acquired resource back to the topmost node of the current threads user stack.

  struct nobug_resource_user* user = NULL;
  struct nobug_resource_node* node = NULL;

  if (!llist_is_empty (&tls->res_stack))
    {
      user = LLIST_TO_STRUCTP (llist_tail (&tls->res_stack),
                               struct nobug_resource_user,
                               res_stack);

      struct nobug_resource_node templ =
        {
         ...
          user->current->resource,
         ...
        };

      node = (struct nobug_resource_node*)
        llist_ufind (&resource->nodes,
                     &templ.node,
                     nobug_resource_node_resource_cmpfn,
                     NULL);
    }
  ...

Deadlock checking is only done when the node is entered in WAITING state and only available in multithreaded programs.

  if (state == NOBUG_RESOURCE_WAITING)
    {
#if NOBUG_USE_PTHREAD
      ...

If node was found above, then this locking path is already validated and no deadlock can happen, else, if this stack already holds a resource (user is set) we have to go on with checking.

      if (!node && user)
        {
          ...

If not then its checked that the resource to be entered is not on any parent trail of the current topmost resource, if it is then this could be a deadlock which needs to be further investigated.

          LLIST_FOREACH (&user->current->resource->nodes, n)
            {
              for (struct nobug_resource_node* itr =
                     ((struct nobug_resource_node*)n)->parent;
                   itr;
                   itr = itr->parent)
                {
                  if (itr->resource == resource)
                    {
                      ...

if the resource was on the trail, we search if there is a common ancestor before the resource on the trail and the threads current chain, if yes then this ancestor protects against deadlocks and we can continue.

                      for (struct nobug_resource_node* itr2 = itr->parent;
                           itr2;
                           itr2 = itr2->parent)
                        {
                          LLIST_FOREACH_REV (&tls->res_stack, p)
                            {
                              struct nobug_resource_user* user =
                                LLIST_TO_STRUCTP (p,
                                                  struct nobug_resource_user,
                                                  res_stack);
                              if (user->current->resource == itr2->resource)
                                goto done;
                            }

If no ancestor found, we finally abort with a potential deadlock condition.

                          nobug_resource_error = "possible deadlock detected";
                          return NULL;
                          ...

Leaving Resources

store the tail and next aside, we need it later

#if NOBUG_USE_PTHREAD
      struct nobug_resource_user* tail =
        LLIST_TO_STRUCTP (llist_tail (&user->thread->res_stack),
                          struct nobug_resource_user,
                          res_stack);
      struct nobug_resource_user* next =
        LLIST_TO_STRUCTP (llist_next (&user->res_stack),
                          struct nobug_resource_user,
                          res_stack);

remove user struct from thread stack The res_stack is now like it is supposed to look like with the user removed. We now need to fix the node tree up to match this list.

      llist_unlink_fast_ (&user->res_stack);

When the the user node was not the tail or only node of the thread stack, we have to check (and possibly construct) a new node chain for it. No valdation of this chain needs to be done, since it was already validated when entering the resources first.

      if (user != tail && !llist_is_empty (&user->thread->res_stack))
        {
          struct nobug_resource_user* parent = NULL;
          if (llist_head (&user->thread->res_stack) != &next->res_stack)
            {
              parent =
                LLIST_TO_STRUCTP (llist_prev (&next->res_stack),
                                  struct nobug_resource_user,
                                  res_stack);
            }

iterate over all users following the removed node, finding nodes pointing to this users or create new nodes.

          LLIST_FORRANGE (&next->res_stack, &user->thread->res_stack, n)
            {
              struct nobug_resource_user* cur =
                LLIST_TO_STRUCTP (n,
                                  struct nobug_resource_user,
                                  res_stack);

find the node pointing back to parent, create a new one if not found, rinse repeat

              struct nobug_resource_node templ =
                {
                  ...
                  NULL,
                  ...
                };

              struct nobug_resource_node* node = (struct nobug_resource_node*)
                llist_ufind (&resource->nodes,
                             &templ.node,
                             nobug_resource_node_parent_cmpfn,
                             NULL);

              if (!node)
                {
                  node = nobug_resource_node_new (resource,
                                                  parent?parent->current:NULL);
                  if (!node)
                    {
                      nobug_resource_error = "internal allocation error";
                      return 0;
                    }
                }

              parent = cur;
            }
        }

Index

Aborting abort the program
callback for aborting hook to handle a termination
type of abort callback type of a abort callback function
ALERT about to die
ASSERT generic assertion
assert C standard assertion
Backtraces generate a backtrace
Build Levels selecting the build level
passing data to callbacks data to be passed to callbacks
CHECK unnconditional assertion for testsuites
CHECKED, Scope tag scope as reviewed
C support, C logflags C++ support for log flags
CRITICAL can not continue
The Resource Tracking Algorithm how resources are tracked
DECLARE_FLAG declaring a flag
DECLARE_ONLY force flag declarations only
DEFINE_FLAG defining a flag
DEFINE_FLAG_LIMIT defining a flag w/ log limit
DEFINE_FLAG_PARENT defining a flag hierarchy
DEFINE_FLAG_PARENT_LIMIT defining a flag hierarchy, w/ log limit
DEPRECATED to be discarded in future
DUMP dumping datastructures
DUMP_LOG logging helper for dumping
ECHO unconditional logging for tests
ELSE_NOTREACHED alternative never taken
ENSURE postconditions (computation outcomes)
ERROR something gone wrong
FIXME known bug
INFO progress message
INJECT_FAULT fault injection statement
INJECT_GOODBAD fault injection expression
INJECT_LEVEL log level for fault injection
INVARIANT validate invariant state
LOG generic logging
LOG_BASELIMIT minimum compliled-in logging limit
Log Flags define hierarchies for logging output
callback when logging hook when something get logged
type of logging callbacks type of a logging callback function
Multithreading using NoBug in multithreaded programs
NOBUG_ALPHA_COMMA append something after a comma in ALPHA builds
NOBUG_ANN log flag for annotations
Source Contexts pass information about the source location
Control what gets logged environment variable for loging control
nobug (flag) log flag used to show nobug actions
NOBUG_FLAG_RAW pass direct flag pointer
NOBUG_IF_* include code conditionally on build level
NOBUG_ON log flag which is always enabled
Entering Resources deadlock check on enter
Leaving Resources fix resource lists
NOTICE detailed progress message
NOTREACHED code path never taken
PLANNED ideas for future
callback after logging hook after something get logged
Dumping Persistent Ringbuffers dumping persistent ringbuffers
REQUIRE preconditions (input parameters)
RESOURCE_ANNOUNCE publish new resources
RESOURCE_ASSERT_STATE assert the state of a resource
RESOURCE_DUMP dump the state of a single resource
RESOURCE_DUMPALL dump the state of all resources
RESOURCE_ENTER claim a resource
RESOURCE_FORGET remove resources
RESOURCE_HANDLE define resource handles
RESOURCE_LEAVE relinquish a claimed resource
RESOURCE_LIST enumerate all registered resources
RESOURCE_LOG_LEVEL select the log level for resource logging
RESOURCE_LOGGING switch resource logging on and off
RESOURCE_STATE change the state of a resource
RESOURCE_TRY wait for a resource to become available
RESOURCE_WAIT wait for a resource to become available
NOBUG_THREAD_DATA thread local data for application use
NOBUG_THREAD_ID_GET query thread id
NOBUG_THREAD_ID_SET set or reset thread id
TODO things to be done
TRACE debugging level message
UNCHECKED, Scope tag scope as unreviewed
UNIMPLEMENTED not yet implemented
WARN unexpected fixable error

License

NoBug
Copyright (C) 2009          Christian Thäter <ct@pipapo.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
License Rationale

NoBug is released under the "GNU General Public License version 2 or any later" to protect its freedom. If one wants to use NoBug in a propietary program, please contact the main author for acknowledging relicensing terms.

For BSD license style Free Software, this means you can not distribute binaries linking NoBug without making its source available. To make this compatible, it is suggested that you dual-license your software with your prefered BSD like license and the GPL. As long as it uses NoBug, the GPL will take over and you have to make the source available, while one can ship a BSD or LGPL Licensed headerfile which defines all NoBug macros as empty macros and remove libnobug from the linking, then NoBug isn't used anymore and you may apply BSD license terms for resulting binaries.

Contributor Agreement

Improvements and patches must be licensed as "GPL v2 or any later" to be acceptable. Further a contributor must either assign his copyright to the main NoBug author or agree with the possibility that NoBug can be relicensed for propietary use:

Independent of the GPL license as stated above, The main author of
NoBug explicitly reserve the right to relicense NoBug under
different, even propietary terms. Any contributor agrees to such
a possiblility by sending his contribution to be included into
the official releases.
This agreement is bilateral, every contributor who worked on a
substantial part of NoBug has the right to relicense it after
negotiation with the NoBug main author. Exact terms of such
relicensing are worked out on a per case base.

The intention is that anyone who worked on NoBug should be able to benefit from his work. This means one should be able to use it at his workplace, to gain a job or as well as relicense it for a customer. Unlike other projects which simply ask for transfering the copyright to the main author, NoBug tries to make it possible to retain the copyright by anyone who helped the project.

This additional agreement has no impact on the GPL, it's sole purpose is to define relicensing policies between the NoBug main author and contributors. When you recieve NoBug it will be licensed under GPL unless you personally acknowledged other terms with the NoBug main author (or any other main contributor).

If anyone feels he is not credited in the AUTHORS file or in any copyright notice, please contact the main author for inclusion.