RTFL Documentation

Contents

Overview

RTFL, which stands for Read The Figurative Logfile, is a both a protocol for structured debug messages, as well as a collection of programs (currently two, rtfl-objcount and rtfl-objview) displaying these debug messages in a semi-graphical way.

Programs are prepared to print these special debug messages to standard output (typically controllable via #ifdef); for C++, there is already a header file which provides convenient macros. By passing the messages to a viewer program (rtfl-objcount, rtfl-objview, or, in the future, similar programs), it becomes simpler to determine what the debugged program does.

Synopsis

To use RTFL, prepare the program which is to be tested (see Preparing the tested program), and run

tested-program | rtfl-objcount
tested-program | rtfl-objview [options]

or, in the future, other programs, which will become part of RTFL. The prepared, tested program will print special debug messages to standard output, which will then be read by rtfl-objcount or rtfl-objview via pipe. Of course, using files instead of pipes is also possible.

Rtfl-objcount does not yet support options, the ones of rtfl-objview are described in the section Command line options of rtfl-objview.

Details on using rtfl-objcount and rtfl-objview can be found in the sections Using rtfl-objcount and Using rtfl-objview, respectively. Furthermore, there is a useful filter, rtfl-tee, which is described in Using rtfl-tee.

The filter rtfl-findrepeat has been moved to the attic.

Preparing the tested program

The program to be tested must print special commands to standard output. The file debug_rtfl.hh, included in RTFL, provides some convenient macros. Both commands and macros are described in the section Protocol and macros. The macros are active only when the pre-processor variable DBG_RTFL is defined. Furthermore, the full paths of the source file names should be included into the commands; this variant prefixes __FILE__ with CUR_WORKING_DIR, which has to be defined.

It is best to copy debug_rtfl.hh in your project, so that there is no direct dependencies to RTFL. This file itself is public domain, so there are no restrictions on using RTFL.

See the tests directory for some examples. (But notice that explicitly passing -DDBG_RTFL, as in tests/Makefile.am is not “comme il faut”; instead, provide a way to let the user decide.)

Using RTFL with autoconf and automake

The file configure.ac of RTFL itself shows how to handle this best by an option --enable-rtfl. First, define this option:

AC_ARG_ENABLE(rtfl, [--enable-rtfl Build with RTFL messages])

Later, a pre-processor variable has to be defined:

if test "x$enable_rtfl" = "xyes" ; then
  CXXFLAGS="$CXXFLAGS -DDBG_RTFL"
fi

Furthermore, we need CUR_WORKING_DIR; this is defined later; here we define:

BASE_CUR_WORKING_DIR=`pwd`

and:

AC_SUBST(BASE_CUR_WORKING_DIR)

Finally, all instances of Makefile.am have to be prepared. Any file foo/Makefile.am should contain:

AM_CPPFLAGS = -DCUR_WORKING_DIR='"@BASE_CUR_WORKING_DIR@/foo"'

Protocol and macros

This section describes both the commands which are printed to standard output, as well as the macros from debug_rtfl.hh.

The whole protocol is divided into modules (currently only one). Each protocol module has a version number consisting of a positive major number and a minor number, which may be 0. The major number changes when the protocol module changes in an incompatible way, while compatible changes affect the minor number.[1][2]

Generally, the colon (:) is used to divide different parts of a command. To use the colon in a literal way, not for division,it can be quoted using the backslash (\). Likewise, a literally used backslash must be quoted (\\).

In the detailed descriptions below, commands are shortened. All commands begin with

[rtfl-module-major version.minor version]file name:line number:process identifier:

where the prefix [rtfl-module-major version.minor version] is used to filter RTFL messages from other messages printed to standard output. Major version and minor version refer to the version of the protocol module.[3] Then follows the file name and the line number, where this message is triggered (this may be used for extensions, like simple navigation through the source code). The last part is a process (or thread) identifier, e. g. as returned by getpid(2). Then follows (not shown here) the command identifier, which defines basically what to do.

It is common to combine protocol module and command identifier, separated by a hyphen. For example, obj-create refers to the command create of the module obj.

The following descriptions only give the part following this common prefix.[4]

General module

The general module is identified by gen, the current version is 1.0.

The general module contains messages intended for use with other modules.

Command: time:usecs
Macro: DBG_GEN_TIME()

Define the point in time of the previous command, in micro seconds since a defined, but unspecified, origin. Can be used for simple profiling.

Objects module

The object module is identified by obj, the current version is 1.0.

All commands from this module are related to an object, which is typically the first argument following the command identifier. For C++, the value of this, as printed by printf("%p", this) is most suitable. The first occurrence of a specific object will "define" the object; a specific command is not needed (but see create).

For most macros dealing with this, there is an additional macro with the suffix _O, which expects the object explicitly. Use these in special cases when this is not suitable. They are not mentioned here; look into debug_rtfl.hh for details.

Command: msg:object:aspect:priority:msg
Macros:
DBG_OBJ_MSG(aspect, priority, msg)
DBG_OBJ_MSGF(aspect, priority, fmt ...)

Display a message (msg) related to an object. The macro DBG_OBJ_MSG expects a simple text, while DBG_OBJ_MSGF can be used to use printf(3) like formatting.

Aspects are arbitrary keywords, which can be used to focus on different parts of a program (which may affect the same objects). Priorities are positive numbers defining how important a message should be regarded, with 0 denoting the most important message. Both can be filtered by the viewer programs, see Filtering messages in rtfl-objview.

A very simple variant of HTML can be used in the messages, the tags <i> and <b> display italics and bold text, respectively.

Command: mark:object:aspect:priority:mark
DBG_OBJ_MARK(aspect, priority, mark)
DBG_OBJ_MARKF(aspect, priority, fmt ...)

Very similar to msg, but marks are handled in a privileged way; rtfl-objview displays them in a seperate menu, so it is simple to select them in a fast way.

Marks are used to mark special positions within the streams of RTFL commands; although there are macros, they are typically added by filters, which scan RTFL messages for notable occurences. An example is rtfl-stracktraces with the option -m.

Commands: msg-start:object and msg-end:object
Macros: DBG_OBJ_MSG_START() and DBG_OBJ_MSG_END()

Define the start and the end of a section, like a function. Especially useful for recursions, where these commands are nested.

Commands: enter:object:aspect:priority:funname:args and leave:object or leave:object:vals
Macros:
DBG_OBJ_ENTER0(aspect, priority, funname) (for functions/methods with no arguments)
DBG_OBJ_ENTER(aspect, priority, funname, fmt ...)
DBG_OBJ_LEAVE()
DBG_OBJ_LEAVE_VAL(fmt ...)
DBG_OBJ_LEAVE_VAL0(val)

States that a function or a method has been entered or left, respectively. Aspect and priority have the same meaning as for msg. The printf(3) like format fmt (macros DBG_OBJ_ENTER and DBG_OBJ_LEAVE_VAL) must match the following arguments. Any leave refers to the last “open” enter.

Leave supports optional return values, the macro DBG_OBJ_LEAVE_VAL should be used in this case. Multiple values are allowed; either with languages which support this, or indirectly by returning values by passing references as arguments. For a literal text value, use DBG_OBJ_LEAVE_VAL0.

Command: create:object:class
Macro: DBG_OBJ_CREATE(class)

Assigns a class to an object. This is not really necessary, but without this command, the class of an object is not known, and for the user of RTFL, difficult to guess.

When multiple classes are assigned to one object, the last class is valid. This way, calling DBG_OBJ_CREATE() in both constructors, the one of the base class (which is called first) and the one of the sub class (which is called second), will work properly, by eventually assigning the class of the sub class.

For C++, it is recommended to use the fully qualified class name (path::to::namespace::ClassName).

Command: delete:object
Macro: DBG_OBJ_DELETE()

Deletes an object. Can be called multiple times for one object, when each destructor the class hierarchy prints this command.

After delete has been called as often as create before, the object identifier must be regarded as unassigned, i. e., if it is used again, it is regarded as identifying a new object.

Command: ident:object1:object2
Macro: DBG_OBJ_BASECLASS(class)

This command defines two objects as identical, so that all commands for object1 and object2 will be treated in the same way, in a non-distinguishable way.

The reasoning behind this command is shown by the macro, and lies in the way how C++ handles multiple inheritance. Assume a class C which has two super-classes, A and B. Typically, when regarding instances of C as A (cast to A*), the value of this will not change. However, when regarded as B, the value will be different, typically sizeof(A) bytes larger.

For this reason, DBG_OBJ_CREATE should, at least for cases of multiple inheritance, be followed by DBG_OBJ_BASECLASS:

DBG_OBJ_CREATE(C);
DBG_OBJ_BASECLASS(A);
DBG_OBJ_BASECLASS(B);

Command: noident

This command tells the processing program that obj-ident is never used, which makes it unnecessary to wait until the (non-)identity of object has been clarified.

Command: assoc:parent:child
Macros:
DBG_OBJ_ASSOC(parent, child)
DBG_OBJ_ASSOC_PARENT(parent)
DBG_OBJ_ASSOC_CHILD(child)

Creates an association between two objects. Objects have some kind of hierarchy, not limited to a tree, not even a DAG, but still not symmetric: DBG_OBJ_ASSOC(x, y) is to be distinguished from DBG_OBJ_ASSOC(y, x).

Command: set:object:var:val
Macros:
DBG_OBJ_SET_TYPE(var, val)
DBG_OBJ_ARRSET_TYPE(var, ind, val)
DBG_OBJ_ARRATTRSET_TYPE(var, ind, attr, val)

Set or change an attribute of an object. The name can be divided by using the dot (.), both for sub structures and for arrays. The different macros are used to format different types:

TYPE C++ type of val Output
NUM int val
SYM char* val
BOOL bool true or false
STR char* "val"
PTR void* val
COL int (0xRRGGBB, interpreted as RGB color) #RRGGBB

Furthermore, variants use the dot notation for array elements. The last variant is used for attributes of structures, which are elements of an array. (For even more complex notations, you can, of course, define new macros.)

Command: class-color:class:color[5]
Macro: DBG_OBJ_CLASS_COLOR(class, color)[6]

Defines a color for groups of class names. Classes takes the form of patterns as defined by fnmatch(3), e. .g. path::to::namespace::*; color is given as #RGB or #RRGGBB.

When more than one pattern matches, the most specific is applied.[7][8]

Command: object-color:object:color

Defines a color for single objects. Like in class-color, color is given as #RGB or #RRGGBB.

Object colors are preferred over class colors.

Using rtfl-objcount

Rtfl-objcount reads RTFL commands from the objects module via standard input, simply counts the number of instances of each class, and shows them in a table, which is especially useful to examine memory problems. Use New snapshot from the Snapshot menu to preserve a current state: a column is added, and new changes are only applied to the most right column.

Like rtfl-objbase, rtfl-objcount first reads commands from a file .rtfl in the current directory, which typically contains commands for the program to be debugged.

Using rtfl-objview

Rtfl-objview reads RTFL commands from the objects module via standard input, and displays them as a diagram, with all commands related to one object as a box, and showing the relations between the objects.

Like rtfl-objbase, rtfl-objview first reads commands from a file .rtfl in the current directory, which typically contains commands for the program to be debugged. (See also option -B.)

Basic usage

A typical object box

All messages related to an object will be displayed in a box showing

At different levels, parts of a box can be toggled between visible and invisible (or, instead, replaced by a much smaller part, as e. g. used for attributes). This is indicated in the usual way, by “+” (to show) and “−” (to hide) in the upper left corner.

For attributes, the structure based on the dot notation (see set) creates a tree whose parts can be toggled between visible and invisible. The complete history of values is displayed, but can be hidden by instead only showing the recent value.

In the messages part, msg-start and msg-end change the indentation of the messaged between, and also insert the messages start and end.

Associations lead to a respective positioning of the object boxes and connections by arrows, as well as a message in the messages part.

(Many aspects can be configured, see Filtering by types.)

Command line options

Rtfl-objview supports these options:

-a aspect, -A aspect
Show (-a) or hide (-A) an aspect (see Filtering messages). This is equivalent to turning on/off an aspect from the Aspects menu. The aspect “*” refers to all, so this option is then equivalent to Show/Hide all aspects. Both options can be used multiple times, and are processed in the order in which they are given; so -A "*" -a foo will only show messages with the aspect “foo”.
-b, -B
Do (-b) or do not (-B) apply .rtfl and filtering identities (what rtfl-objbase does).
-m, -M
Show (-m) or hide (-M) the messages of all object boxes. Hiding (-M) is useful when examining attributes; the messages can be shown by clicking on “+”. (Since showing is the default, the option -m only exists for reasons of symmetry.)
-o, -O
Show (-o) or hide (-O) the contents of all object boxes. Hiding (-O) can be useful to get an overview over the relations, without initially caring about the contents; the contents can be shown by clicking on “+”. (Since showing is the default, the option -o only exists for reasons of symmetry.)
-p priority
Set the priority of messages (see Filtering messages). This is equivalent to choosing the respective value in the Priorities menu. The priority “*” refers to No limit.
-t types, -T types
Show (-t) or hide (-T) certain command types (see Filtering by types). Types is a sequence of any of these characters: “c” for creations, “i” for indentations, “m” for messages, “a” for marks, “f” for functions, “s” for association, “t” for attributes, and “d” for deletions. This is equivalent to turning on/off the respective types in the Commands menu.
-v viewer
Set the program called for viewing code (see Navigation). Viewer is a command (passed to system(3)), which may contain these variables: “%n” is replaced by the line number, “%p” is replaced by the path of the file. Notice that you should append “&” to avoid blocking. The default is “xterm -e 'vi +%n %p' &”; another nice option is “emacsclient -n %n %p”. (Also notice that there is currently no quoting done for expanding “%p”, so it is best to keep your file names as simple as possible.)

Navigation

The commands (from the object module) msg-start, msg-end, enter, leave, msg, mark, set, assoc, create, and delete are navigable. They are assigned a serial numbers (starting with 0) which is preceding the actual message; furthermore, they can be selected (by clicking on the respective message) and navigated with the commands Previous and Next from the Command menu, or the respective keyboard accelerators. Furthermore, View Code calls an external (and configurable) viewer or editor to show the line in the code where the selected message was printed.

Both preceding the serial number on one hand, and selection and highlighting on the other, refers to the respective message in the lower part of the object box (msg-start, msg-end, msg, mark, assoc, create, delete), or (set) for the message in the attribute value history (click “+” to show), respectively

Some navigable commands are related; by activating Switch between related from the Commands menu switches between them. Currently enter and leave are regarded, as well as msg-start and msg-end.

Hiding commands

Displaying of navigable commands can be limited to any region. Select a command (mouse or navigating) and choose from the Command menu:

Notice that the serial numbers are recalculated, so that the first visible command has the number 0. This is also valid for commands filtered out (by type, or by aspect and priority).

Stack traces

Based on enter and leave, the command Show stack trace from the Command menu opens a new window showing all enter commands leading to the selected command. As long as these are not hidden (which is represented by italics), you can navigate to these by clicking on Jump to.

Marks

All marks are added to the Marks menu, from where they can be directly selected, as long as they are not hidden.

Filtering by type

All navigable commands can be filtered by types, by using entries in the Command menu. In all cases, this refers to the navigable part of the command; other effects (indentation itself, current value of an attribute, arrows for associations) are not affected.

By default, indentation messages are turned off, all others are turned on.

Filtering messages

Message commands can be filtered by aspects and priorities.

All aspects defined by the program are visible in the Aspects menu, alphabetically sorted, and can be toggled individually. Show all and Hide all not only affect all aspects already shown in the Aspects menu, but also define the default value for newly added aspects (initially turned on, or initially turned off, respectively).

Likewise, the Priorities menu contains all priorities, numerically sorted. Choosing one defines the maximal priority number (and so minimal importance); anything above this number (and so less important) is not shown. No limit will not filter any messages by priority.

Note: Both aspects and priorities should be sorted (aspects alphabetically, priorities numerically), which is not yet the case. Especially for priorities, this can become confusing. Also, version 0.0.7 contains a bug that No limit is not selected initially, when it should be. (This is only a visualization problem, filtering works.)

Using rtfl-objbase

Rtfl-objbase is a filter which reads RTFL commands from the objects module and writes them again to standard output, after

Rtfl-objbase is usefully for scripts.

Using rtfl-tee

Rtfl-tee is a simple program which works similar to tee(1): it copies standard input to standard output as well as to a new pipe which is connected to standard input of another process. The exact syntax is:

rtfl-tee [-b] [--] command [arguments ...]

All arguments refer to command.

The option -b enables the bypass mode, in which the standard output of command is piped to rtfl-tee, which prints both its own standard input and the standard output of command line by line, so that the lines from both streams are preserved. Use this when command is a filter and you want to mix both the original and the filtered stream in a simple way. Notice that no order of the lines is guaranteed.

Everything after -- is regarded as command. Use this for commands starting with “-”.

A typical (simple) example:

tested-program | rtfl-tee rtf-objview -O | rtfl-objcount

This will start tested-program in a way that the output is passed both to rtfl-objview (started with option -O) and rtfl-objcount.

An example for the bypass mode is given in the section Transformation of attributes in Tips and tricks.

Scripts

Part of the RTFL package are some scripts:

See the comments at the respective scripts for more information on usage.

The scripts rtfl-filter-out-classes, rtfl-objfilter, rtfl-objtail, and (with options -e and -m) rtfl-stacktraces are used as filters: if e. g. you want to use rtfl-objview but ignore all classes of the package some::package, run

tested-program | rtfl-filter-out-classes "some::package::*" | rtfl-objview [options]

N. b. that since versioning and escaping of protocols, parsing is incorrect:

Furthermore, rtfl-objbase should be used by all scripts to achieve a uniform handling of obj-ident and obj-delete, as well as to fix some parsing problems. Currently, this has been done for rtfl-filter-out-classes and rtfl-objtail.

See also


[1] Some notes and definitions:

Backward compatibility means than a parser can focus on one new version and ignore differences to older versions. Backward compatibility is not generally given, only within a major version; instead, a parser must be aware of older versions. However, changes will be made in a way making this as simple as possible; how this is done will be decided from case to case.

Forward compatibility means than a parser implementing an older version can deal with newer versions. Forward compatibility is given within a major version, by defining the following rules:

This implies that there are two ways how a protocol module can be extended in a forward compatible way (leading to a new minor version):

For simplicity, a parser may simply check the module and major version, and, for supported modules and major versions, ignore new commands and new arguments. New forward-compatible changes can be implemented by and by.

[2] Currently, there is no version for the general part of the protocol, which is independent of the modules. This may change when the general protocol changes.

[3] Since a stream may consist of command lines of different protocol modules, it can consist of command lines with different versions. It is even allowed to mix different versions of the same protocol module.

[4] An old version of the protocol, called “pre-version protocol”, is still supported for the objects module, albeit deprecated. Lines start with [rtfl], with neither module name nor version; the module name is put before the command (example: obj-create). No escaping mechanism is defined, so that only the last part may contain colons. If not otherwise stated, the specific commands are equivalent

[5] In the “pre-version protocol”, obj-color is still a (deprecated) synonym of obj-class-color.

[6] In the “pre-version protocol”, the order of the arguments is obj-class-color:color:class, since class typically contains colons, as used for C++ namespaces, which are only allowed in the last argument. As for the macro, there are different versions, so you have to be cautious. (As opposed to the protocols, old versions of macros are not supported, since the file debug_rtfl.hh is typically copied into the tested program, and so the maintainer of the tested program is responsible.)

[7] The exact definition of the specifity of a pattern: number of “?” − 100 ∙ number of “*” + 100 ∙ number of other characters.

[8] In the “pre-version protocol”, the rule was different (the first pattern was choosen), but for simplicity, newer versions of RTFL treat the “pre-version protocol” like the new version.