dump fixes
[nobug.git] / README
blob71bed7816b3cbfdf6969c8e09c0bc7b969b9f519
1 NOBUG Documentation
3 Nobug is a simple debugging library (only a single nobug.h header) similar to
4 gnu-nana and Design-by-Contract ideas.
6 Overview
8 Nobug provides you with:
10   * Three different levels for checks (in depth to final no-overhead)
11   * Scope tags (tell whenever a function or loop is considered to be bug free)
12   * Pre-, Postcondition and Invariant checks, generic Assertions
13   * Debugger support (actions are only executed while running under a
14     debugger), currently only valgrind
15   * Dumping of your datastructures
16   * Logging your application's activities
17   * Runtime customizable logging via an enviromnet variable
18   * Different logging targets (stderr, syslog, debugger...)
19   * Annotation of your sourcecode about known bugs, things to do and planned
20     things
22 Debug Control
24 Build Levels
26 Nobug uses different levels of checks:
28   * ALPHA
29       + for expensive testing and logging while developing the software
30     BETA
31       + for testers and users who want to try the software
32     RELEASE
33       + finished version for end users
35 Release builds remove all assertions (but some logging is kept) we make the
36 assumption that bugs which where not covered in alpha and beta builds won't be
37 easily show up in releases because the assertions there where not sufficent.
38 Further end users are not test bunnies and wont provide good bug reports
39 anyways. If there is a problem in a release build, then try to investigate the
40 cause with a beta build from the same source.
42 To define a debug level for compilation just use '-DEBUG_ALPHA', '-DEBUG_BETA'
43 for Debug Builds or the standard '-DNDEBUG' for a release build. Nobug will
44 complain if none of this is defined.
46 Scope Checks
48 The programmer can tag any Scope as UNCHECKED or CHECKED. In ALPHA and BETA
49 builds a global UNCHECKED is implied, while in RELEASE builds a global CHECKED
50 is used and UNCHECKED Scopes are not allowed.
52 Test Matrix
54 Here is a table which basic assertions gets checked on each level/scope
55 combination:
57           ALPHA                          BETA                   RELEASE
58 UNCHECKED Preconditions, Postconditions, Preconditions,         compiling will
59           Invariants                     Postconditions         abort
60 CHECKED   Preconditions, Postconditions  Preconditions
62 Macros
64 Nobug is almost completely implemented in preprocessor macros, this is needed
65 because it uses the __FILE__ and __LINE__ macros for logging. All its flat
66 namespace uppercase identifiers make it good recognizeable in a source too.
68 All macros are unconditionally available with NOBUG_ prefixed, for convinience
69 there are also macros without this prefix unless such a macro was already
70 defined. For each Assertion and Logging macro there is a form with _DBG as
71 suffix which will be only active under a debugger. The _DBG versions are only
72 enabled in alpha builds.
74 There are also assertions with a _IF suffix taking a 'when' parameter as first
75 argument example:
77   * REQUIRE_IF(foo!=NULL, foo->something == constrained)
79 they only perform the assertion when when is true. The debugger versions are
80 available as _IF_DBG prefixed macros.
82 Parameters types:
84 when    assertion is only performed if expression 'when' is true at runtime
85 expr    test without side effects
86 fmt     printf like formatstring
87 ...     if not preceeded by 'fmt' then printf like formatstring else only its
88         arguments
89 what    reason for a failure as string literal
90 flag    flag for enabling custom logging groups
91 type    type of the to be checked data as single identifier name
92 pointer pointer to type
93 lvl     log level
94 depth   depth for invariants and dumps
96 Assertions
98   * REQUIRE(expr, ...)
99       + Precondition (input) check
100     ENSURE(expr, ...)
101       + Postcondition (progress/output) check
102     ASSERT(expr, what, ...)
103       + Generic check
104     INVARIANT(type, pointer, depth)
105       + Checking invariants
107 Logging
109 Logging is controlled by user defined flags and a log limit.
111 Log Levels
113 Each Log macro has a explicit or implicit Log-Level which correspondends to
114 syslog levels. Logging is only emitted when the current NOBUG_LOG_LIMIT is more
115 servere than the one of the Log message.
117   * In ALPHA builds, NOBUG_LOG_LIMIT_ALPHA is used which defaults to LOG_INFO
118   * In BETA builds, NOBUG_LOG_LIMIT_BETA is used and defaults to LOG_WARNING
119   * In RELEASE builds, NOBUG_LOG_LIMIT_RELEASE is used and defaults to LOG_CRIT
121 You can override all of those three with your own preference. Alternatively
122 NOBUG_LOG_LIMIT can be defined by the user to override all defaults.
124 Further the NOBUG_LOG_LIMIT can be initialized in ALPHA and BETA builds by the
125 NOBUG_LIMIT enviroment variable. The NOBUG_INIT_FLAG below takes care of this.
127 Log Flags
129 Flag is just a C identifier and should be declared with
131   * NOBUG_DECLARE_FLAG(flagname)
133 preferably in one of your headers
135 Further it must be defined with
137   * NOBUG_DEFINE_FLAG(flagname)
141   * NOBUG_DEFINE_FLAG_LIMIT(flagname, level)
143 in one of your source files
145 Next you should call
147   * NOBUG_INIT_FLAG(flagname)
149 once at the start of your program for every flag defined with NOBUG_DEFINE_FLAG
151 Flags are initially set to -1 (nothing gets logged), NOBUG_INIT_FLAG(flagname)
152 checks if flagname is present in the environment variable 'NOBUG_LOG' and then
153 sets the flag to the current limit. You can query the flag state with
154 NOBUG_FLAG(flagname).
156 There is a predefined flag NOBUG_ON which is always enabled.
158 Example code:
160    1 #include "nobug.h"
161    2 NOBUG_DEFINE_FLAG (test);
162    3
163    4 int main() {
164    5   NOBUG_INIT_FLAG (test);
165    6
166    7   INFO (test, "Logging enabled");
167    8   INFO (NOBUG_ON, "Always on");
168    9 }
170 test it:
172 $ tcc -DEBUG_ALPHA -run example.c
173 DEBUG: main : Always on
174 $ NOBUG_LOG=test tcc -DEBUG_ALPHA -run example.c
175 DEBUG: main : Logging enabled
176 DEBUG: main : Always on
177 $ NOBUG_LIMIT=CRIT NOBUG_LOG=test tcc -DEBUG_ALPHA -run example.c
178 DEBUG: main : Always on
180 The Logging Macros
182 These macros log with implicit Log Level, note that there are no more servere
183 levels than LOG_ERROR since these should be handled by Assertions in a
184 debugging library.
186   * ERROR(flag, fmt, ...)
187     ERROR_IF(expr, rflag, fmt, ...)
188       + Application takes a error handling brach
190     WARN(flag, fmt, ...)
191     WARN_IF(expr, flag, fmt, ...)
192       + Rare, handled but unexpected branch
194     INFO(flag, fmt, ...)
195     INFO_IF(expr, flag, fmt, ...)
196       + Message about program progress
198     NOTICE(flag, fmt, ...)
199     NOTICE_IF(expr, flag, fmt, ...)
200       + More detailed
202     TRACE(flag, fmt, ...)
203     TRACE_IF(expr, flag, fmt, ...)
204       + Very fine grained messages
206 Note that TRACE correspondends to LOG_DEBUG, using 'DEBUG' could be ambiguous.
208 There is one generic LOG macro which takes the level explicitly:
210   * LOG(flag, lvl, fmt, ...)
211     LOG_IF(expr, flag, lvl, fmt, ...)
213 Dumping Datastructures
215   * DUMP(type, pointer, depth)
216       + Dump a datastructure
217     DUMP_IF(expr, type, pointer, depth)
218       + Dump datastructure if expr is true
220 How to write DUMP handlers
222 if you have a
224    1 struct STRUCTNAME
225    2 {
226    3   int INTEGER_MEMBER;
227    4   char * STRING_MEMBER;
228    5   struct STRUCTNAME* next;
229    6 }
231 then you define a function like:
233    1 void
234    2 nobug_STRUCTNAME_dump (const struct STRUCTNAME* self,
235    3                        const int depth,
236    4                        const char* file,
237    5                        const int line)
238    6 {
239    7   // check for self != NULL and that the depth
240    8   // limit did not exceed in recursive datastructures
241    9   if (self && depth)
242   10   {
243   11     // use DUMP_LOG not LOG to print the data
244   12     DUMP_LOG("STRUCTNAME %p: int is %d, string is %s", self,
245   13                              self->INTEGER_MEMBER,
246   14                              self->STRING_MEMBER);
247   15     // now recurse with decremented depth
248   16     nobug_STRUCTNAME_dump (self->next, depth-1, file, line);
249   17   }
250   18 }
252 now you can use the DUMP() macros within the code
254    1 example()
255    2 {
256    3   struct STRUCTNAME foo;
257    4   init(&foo);
258    5   DUMP (STRUCTNAME, &foo, 2);
259    6 }
261 Source Annotations
263 One can tagging features as:
265   * UNIMPLEMENTED(...)
266       + important, not yet finished feature
267     PLANNED(...)
268       + planned for future feature
269     BUG(...)
270       + known bug to be fixed later
271     TODO(...)
272       + enhancement to be done soon
273     NOTREACHED
274       + used to tag code-path which shall be never executed (defaults in switch
275         statements, else NOTREACHED after series of if's)
277 The advantage of this tagging over plain source comments is that we can take
278 some actions if we run in such a tag at compile or runtime:
280 the action to be taken when such a macro is hit depends on the build level:
282               ALPHA BETA    RELEASE
283 UNIMPLEMENTED abort abort   wont compile
284 BUG           log   abort   wont compile
285 TODO          log   log     wont compile
286 PLANNED       log   nothing nothing
287 NOTREACHED    abort abort   removed
289 Legend:
291   * abort means first log and then abort
292   * log will only log once for each sourceline (not on each hit)
293   * wont compile will abort compilation with a error message
294   * nothing optimized out, sane way
295   * removed optimized out for performance reasons
297 Tool Macros
299   * BACKTRACE(...)
300       + Log a stacktrace
302 This Documentation is maintained at:
304   * http://www.pipapo.org/pipawiki/NoBug/Documentation
306 NoBug/Documentation (last edited 2007-01-25 18:23:45 by ct)