3 Nobug is a simple debugging library (only a single nobug.h header) similar to
4 gnu-nana and Design-by-Contract ideas.
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
26 Nobug uses different levels of checks:
29 + for expensive testing and logging while developing the software
31 + for testers and users who want to try the software
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.
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.
54 Here is a table which basic assertions gets checked on each level/scope
58 UNCHECKED Preconditions, Postconditions, Preconditions, compiling will
59 Invariants Postconditions abort
60 CHECKED Preconditions, Postconditions Preconditions
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
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.
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
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
94 depth depth for invariants and dumps
99 + Precondition (input) check
101 + Postcondition (progress/output) check
102 ASSERT(expr, what, ...)
104 INVARIANT(type, pointer, depth)
105 + Checking invariants
109 Logging is controlled by user defined flags and a log limit.
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.
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
147 * NOBUG_INIT_FLAG(flagname)
149 once at the start of your program for every flag.
151 Flags defined with NOBUG_DEFINE_FLAG(flagname) are initialized to -1 (nothing
152 gets logged), while NOBUG_DEFINE_FLAG_LIMIT(flagname, level) are initialized to
153 level. Calling NOBUG_INIT_FLAG(flagname) is optional for level initialized
156 NOBUG_INIT_FLAG(flagname) parses the environment variable '$NOBUG_LOG' which
157 should contain the flags which should be altered/enabled with an optional colon
158 separated level for the flag. If no level is given then the default
159 NOBUG_LOG_LIMIT level will be used, which can be changed with the $NOBUG_LIMIT
160 environment variable.
162 There is a predefined flag NOBUG_ON which is always enabled.
167 2 NOBUG_DEFINE_FLAG (test);
170 5 NOBUG_INIT_FLAG (test);
172 7 INFO (test, "Logging enabled");
173 8 INFO (NOBUG_ON, "Always on");
178 $ tcc -DEBUG_ALPHA -run example.c
179 DEBUG: main : Always on
180 $ NOBUG_LOG=test tcc -DEBUG_ALPHA -run example.c
181 DEBUG: main : Logging enabled
182 DEBUG: main : Always on
183 $ NOBUG_LIMIT=CRIT NOBUG_LOG=test tcc -DEBUG_ALPHA -run example.c
184 DEBUG: main : Always on
185 $ NOBUG_LIMIT=CRIT NOBUG_LOG=test:TRACE tcc -DEBUG_ALPHA -run example.c
186 DEBUG: main : Logging enabled
187 DEBUG: main : Always on
191 These macros log with implicit Log Level, note that there are no more servere
192 levels than LOG_ERROR since these should be handled by Assertions in a
195 * ERROR(flag, fmt, ...)
196 ERROR_IF(expr, rflag, fmt, ...)
197 + Application takes a error handling brach
200 WARN_IF(expr, flag, fmt, ...)
201 + Rare, handled but unexpected branch
204 INFO_IF(expr, flag, fmt, ...)
205 + Message about program progress
207 NOTICE(flag, fmt, ...)
208 NOTICE_IF(expr, flag, fmt, ...)
211 TRACE(flag, fmt, ...)
212 TRACE_IF(expr, flag, fmt, ...)
213 + Very fine grained messages
215 Note that TRACE correspondends to LOG_DEBUG, using 'DEBUG' could be ambiguous.
217 There is one generic LOG macro which takes the level explicitly:
219 * LOG(flag, lvl, fmt, ...)
220 LOG_IF(expr, flag, lvl, fmt, ...)
222 Dumping Datastructures
224 * DUMP(type, pointer, depth)
225 + Dump a datastructure
226 DUMP_IF(expr, type, pointer, depth)
227 + Dump datastructure if expr is true
229 How to write DUMP handlers
235 3 int INTEGER_MEMBER;
236 4 char * STRING_MEMBER;
237 5 struct STRUCTNAME* next;
240 then you define a function like:
243 2 nobug_STRUCTNAME_dump (const struct STRUCTNAME* self,
248 7 // check for self != NULL and that the depth
249 8 // limit did not exceed in recursive datastructures
252 11 // use DUMP_LOG not LOG to print the data
253 12 DUMP_LOG("STRUCTNAME %p: int is %d, string is %s", self,
254 13 self->INTEGER_MEMBER,
255 14 self->STRING_MEMBER);
256 15 // now recurse with decremented depth
257 16 nobug_STRUCTNAME_dump (self->next, depth-1, file, line);
261 now you can use the DUMP() macros within the code
265 3 struct STRUCTNAME foo;
267 5 DUMP (STRUCTNAME, &foo, 2);
272 One can tagging features as:
275 + important, not yet finished feature
277 + planned for future feature
279 + known bug to be fixed later
281 + enhancement to be done soon
283 + used to tag code-path which shall be never executed (defaults in switch
284 statements, else NOTREACHED after series of if's)
286 The advantage of this tagging over plain source comments is that we can take
287 some actions if we run in such a tag at compile or runtime:
289 the action to be taken when such a macro is hit depends on the build level:
292 UNIMPLEMENTED abort abort wont compile
293 BUG log abort wont compile
294 TODO log log wont compile
295 PLANNED log nothing nothing
296 NOTREACHED abort abort removed
300 * abort means first log and then abort
301 * log will only log once for each sourceline (not on each hit)
302 * wont compile will abort compilation with a error message
303 * nothing optimized out, sane way
304 * removed optimized out for performance reasons
315 <!> this section is very work in progress
320 + Write a testsuite, build your program with -O0 -g -DEBUG_ALPHA and run
321 the testsuite under valgrind control. Hack until the program fits the
322 requirements defined by the testsuite.
324 + Build with desired optimization level and -g -DEBUG_BETA and give the
325 program to your beta testers.
327 + Just build it with optimization and without -g -DEBUG_*
329 What and when to check
331 * Add REQUIRE checks on your interfaces (incoming parameters). Especially if
332 a argument might not cover the whole range of the underlying type.
333 * Don't waste your and your CPU's time with unessesary checks. The testsuite
334 should validate your program. NoBug aids in debugging. You can add
335 Postconditions (ENSURE) and Invariants when you have a bug somewhere and
336 want to nail it down.
337 * Added checks don't need to be removed.
338 * When you use the CHECKED/UNCHECKED features then don't forget C scoping
339 rules, tag things as CHECKED from the leaves to the root.
343 * TRACE(flagname) or TRACE_DBG(flagname) at the begin of every nontrivial
344 function will easily log the progress of your application.
345 * Trying a RELEASE build will abort on certain conditions (known BUG, TODO's,
346 UNCHECKED code), you can use this to find these spots.
348 This Documentation is maintained at:
350 * http://www.pipapo.org/pipawiki/NoBug/Documentation
352 NoBug/Documentation (last edited 2007-01-27 06:34:35 by ct)