Libcwd

Last updated

Libcwd is a C++ library, written by Carlo Wood, to add run-time debugging support for C++ applications, particularly for code developed with the GNU Compiler Collection. The functionality that the library adds to an application can be divided into three categories:

Contents

  1. Ostream-based debug output.
  2. Run-time access to debug information.
  3. Run-time access to memory allocation administration.

Supported platforms

Although the library code itself attempts to be strictly ISO C++, and conform to POSIX as much as possible, in order to achieve points 2 and 3, rather specialized code is needed, specific to the architecture the application runs on. Libcwd restricts itself to a narrow architecture for this reason: It has to be compiled with the GNU compiler, and demands the object code to be 32 or 64 bits ELF and the compiler generated debug information to be DWARF-2.

Compiling libcwd results in two libraries: one that is thread-safe (libcwd_r) and a version (libcwd) without thread support. The thread-safe version depends on even more architecture specific details (namely, the GNU C library). As a result, a full featured libcwd is basically only suitable for development on Linux platforms.

However, libcwd may be configured to drop thread support, memory allocation debugging and/or reading the ELF and DWARF-2 debugging information—until only the ostream debug output support is left. This way one can use it to develop an application on linux until it is robust, and still have the debug output on other (POSIX) platforms, even though a full-fledged libcwd isn't available there—provided no thread-safety is needed for the debug output on those platforms: two or more threads writing debug output to the same ostream might cause a rather messy output where the output of one line starts in the middle of another, without thread-support.

Ostream based debug output

Libcwd provides several macros that are easily extensible, allowing the user to basically do anything that one can normally do with ostreams. However, if one just wants to write debug output, two macros will suffice: Dout and DoutFatal. The latter is to be used for fatal debug output, after which the application needs to be terminated. For example:

if(error)DoutFatal(dc::fatal,"An unrecoverable error occurred.");

The difference with Dout is that when the application is compiled without debug code, the macro Dout() is replaced with nothing, while DoutFatal() is replaced with code that prints its output and terminates (in a way that the user can define).

Simple debug output is written by using Dout, as follows:

Dout(dc::notice,"called from "<<location_ct(CALL_ADDR));

where the second parameter is allowed to contain '<<' to write any type or object to the debug output stream (a location_ct in this case).

The 'dc::fatal' and 'dc::notice' are debug 'channels', which can be turned on or off. The user can create and use any number of custom debug channels, in addition to the default ones. It is also possible to create more than just the default debug output ostream object 'libcw_do', and thus to write output to more than one ostream. Each debug object, representing an ostream, can in turn be separately turned on and off.

Run-time access to debug information

This information includes the possibility to look up source file and line number locations, and function names. As a result, it is for example possible to write debug output that prints who the caller is of a given function, or to print the name of the current function, even if that function is a complex template. For example,

 PERSIST : PersistXML::serialize_builtin<std::string>("M_hostname", @0xbfc1f214) is called.

Run-time access to memory allocation administration

Libcwd keeps an internal administration of memory allocations. This allows one to do things like memory leak checking, printing out an overview of allocated memory (in a very powerful way, allowing one to filter on about anything: regular expressions for library names, function names (demangled or not) and/or time intervals during which allocations were made).

The library also provides a few global functions that can be called from within a debugger, like gdb, allowing the developer to quickly find out which allocation a given pointer is pointing at. For example,

 (gdb) call cwdebug_alloc(0x8a19450)  0x8a19450 points inside a memory allocation that starts at 0x8a19448        start: 0x8a19448         size: 12         type: char**  description: Array of commandline arguments passed to libcw_app_ct::options_done_event.     location: libcw_app.cc:304  in function: libcw_app_ct::libcw_init(int, char* const*)         when: 00:31:09.888760  (gdb) l libcw_app.cc:304

Related Research Articles

C (programming language) general-purpose programming language

C is a general-purpose, procedural computer programming language supporting structured programming, lexical variable scope, and recursion, while a static type system prevents unintended operations. By design, C provides constructs that map efficiently to typical machine instructions and has found lasting use in applications previously coded in assembly language. Such applications include operating systems and various application software for computers, from supercomputers to PLCs and embedded systems.

Common Lisp (CL) is a dialect of the Lisp programming language, published in ANSI standard document ANSI INCITS 226-1994 (R2004). The Common Lisp HyperSpec, a hyperlinked HTML version, has been derived from the ANSI Common Lisp standard.

GNU Debugger source-level debugger

The GNU Debugger (GDB) is a portable debugger that runs on many Unix-like systems and works for many programming languages, including Ada, C, C++, Objective-C, Free Pascal, Fortran, Go, and partially others.

Integrated development environment Software application used to develop software

An integrated development environment (IDE) is a software application that provides comprehensive facilities to computer programmers for software development. An IDE normally consists of at least a source code editor, build automation tools and a debugger. Some IDEs, such as NetBeans and Eclipse, contain the necessary compiler, interpreter, or both; others, such as SharpDevelop and Lazarus, do not.

The C preprocessor or cpp is the macro preprocessor for the C, Objective-C and C++ computer programming languages. The preprocessor provides the ability for the inclusion of header files, macro expansions, conditional compilation, and line control.

The C standard library or libc is the standard library for the C programming language, as specified in the ANSI C standard. It was developed at the same time as the C library POSIX specification, which is a superset of it. Since ANSI C was adopted by the International Organization for Standardization, the C standard library is also called the ISO C library.

C dynamic memory allocation refers to performing manual memory management for dynamic memory allocation in the C programming language via a group of functions in the C standard library, namely malloc, realloc, calloc and free.

errno.h is a header file in the standard library of the C programming language. It defines macros for reporting and retrieving error conditions using the symbol errno.

The GNU toolchain is a broad collection of programming tools produced by the GNU Project. These tools form a toolchain used for developing software applications and operating systems.

bc, for basic calculator, is "an arbitrary-precision calculator language" with syntax similar to the C programming language. bc is typically used as either a mathematical scripting language or as an interactive mathematical shell.

Stack-based memory allocation

Stacks in computing architectures are regions of memory where data is added or removed in a last-in-first-out (LIFO) manner.

In the C programming language, Obstack is a memory-management GNU extension to the C standard library. An "obstack" is a "stack" of "objects" which is dynamically managed. It implements a region-based memory management scheme.

Dynamic program analysis is the analysis of computer software that is performed by executing programs on a real or virtual processor. For dynamic program analysis to be effective, the target program must be executed with sufficient test inputs to cover almost all possible outputs. Use of software testing measures such as code coverage helps ensure that an adequate slice of the program's set of possible behaviors has been observed. Also, care must be taken to minimize the effect that instrumentation has on the execution of the target program. Dynamic analysis is in contrast to static program analysis. Unit tests, integration tests, system tests and acceptance tests use dynamic testing.

Intel C++ Compiler, also known as icc or icl, is a group of C and C++ compilers from Intel available for Windows, Mac, Linux, FreeBSD and Intel-based Android devices.

mtrace is the memory debugger included in the GNU C Library.

Intel Fortran Compiler, also known as IFORT, is a group of Fortran compilers from Intel for Windows, OS X, and Linux.

Intel Inspector is a memory and thread checking and debugging tool to increase the reliability, security, and accuracy of C/C++ and Fortran applications.

Bionic is the standard C library developed by Google for its Android operating system. Bionic is a BSD-licensed C library for use with the Linux kernel. This differs from other BSD C libraries which require a BSD kernel, and from the GNU C Library (glibc) which uses the GNU Lesser General Public License.

Nim (programming language) programming language

Nim is an imperative, general-purpose, multi-paradigm, statically typed, systems, compiled programming language designed and developed by Andreas Rumpf. It is designed to be "efficient, expressive, and elegant", supporting metaprogramming, functional, message passing, procedural, and object-oriented programming styles by providing several features such as compile time code generation, algebraic data types, a foreign function interface (FFI) with C and C++, and compiling to C, C++, Objective-C, and JavaScript.