[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nana is a library that provides support for assertion checking and logging in a space and time efficient manner. The aim is to put common good practice(1) into a library that can be reused rather than writing this stuff every time you begin a new project.
In addition assertion checking and logging code can be implemented using a debugger rather than as inline code with a large saving in code space.
Nana aims to solve the following problems:
The code space and time costs of having assertion checking and detailed logging code in a program can be high. Normally people construct two versions of the program, one with checking code for testing and one without checking code for production use.
With nana one version of the executable can be built for both testing and release since debugger based checking has negligible space and time impact.
This is an old embedded systems trick and is very useful for production systems. The time cost of logging into memory is not large and when your production system in the field has problems you can then see what was happening in the minutes before its unfortunate demise rather than asking some user what was happening before it died.
For example the GNU `assert.h' implementation uses 53 bytes for `assert(i>=0)' on a i386. The nana version using the i386 `stp' instruction on assert fail uses 10 bytes. If you're willing to accept the time penalty this can be reduced to 0 or 1 byte by using debugger based assertions.
Specifications are often written in terms of the state of variables before and after an operation. For example the `isempty' operation on a stack should leave the stack unchanged. To verify this in nana we could use:
bool isempty(){ /* true iff stack is empty */ DS($s = s); /* copy s into $s in the debugger */ ...; /* code to do the operation */ DI($s == s); /* verify that s hasn't been changed */ } |
These `$..' variables are called convenience variables and are implemented by gdb. They have a global scope and are dynamically typed and initialised automatically to 0.
In addition a C only version of before and after state is provided. For example:
bool isempty() { /* true iff stack is empty */ ID(int olds); /* declare variable to hold old value */ IS(olds = s); /* copy s into $s in the debugger */ ...; /* code to do the operation */ I(olds == s); /* verify that s hasn't been changed */ } |
Nana provides some support for universal (forall) and existential (exists one or more) quantification. For example to specify that the string v contains only lower case letters we could use:
I(A(char *p = v, *p != '\0', p++, islower(*p))); |
These macros can be nested and used as normal boolean values in control constructs as well as assertions. Unfortunately they depend on the GNU CC statement value extensions and so are not portable. The following macros are defined in `Q.h':
A
For all values the expression must be true.
E
There exists one or more values for which the expression is true.
E1
There exists a single value for which the expression is true.
C
Returns the number of times the expression is true.
S
Returns the sum of the expressions.
P
Returns the product of the expressions.
The shortform of a program consists of the function headers together with their preconditions(2) and postconditions(3)
A small package which measures the time and space overhead of code fragments is provided. This is used to analyse the space/time requirements of the nana library and could be used for other types of measurement.
As well as using nana to verify timings with assertions using a hardware supported timer you can also a simulator (e.g. the PSIM power pc simulator by Cagney) with gdb. These simulators can model time and provide a register called `$cycles' which represents the current cycle count of the program. This can be used to check that timing constraints are being meet.
void process_events() { for(;;){ DS($start = $cycles); switch(get_event()){ case TOO_HOT: ...; DI($cycles - $start <= 120); break; case TOO_COLD: ...; DI($cycles - $start <= 240); break; } } } |
The intended audience for Nana includes:
1.1 Related work | ||
1.2 Assert.h considered harmful | ||
1.3 Scope of this document |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The Nana project was inspired by some other projects, in particular:
Anna is a language for formally specifying the intended behaviour of Ada programs. It extends Ada with various different kinds of specification constructs from ones as simple as assertions, to as complex as algebraic specifications. A tool set has been implemented at Stanford for Anna, including:
- standard DIANA extension packages, parsers, pretty-printers;
- a semantic checker;
- a specification analyser;
- an annotation transformer; and
- a special debugger that allows program debugging based on formal specifications
All tools have been developed in Ada and are therefore extremely portable. Anna has thus been ported to many platforms. For more information send e-mail to "anna-request@anna.stanford.edu". Before down loading the huge Anna release, you may wish to copy and read some Anna LaTeX reports.
Anna is available from: ftp://anna.stanford.edu/pub/anna
Eiffel is a pure object-oriented language featuring multiple inheritance, polymorphism, static typing and dynamic binding, genericity (constrained and unconstrained), a disciplined exception mechanism, systematic use of assertions to promote programming by contract, and deferred classes for high-level design and analysis.
See "A Practical Approach to Programming with Assertions" in Vol 21, No. 1, January 1995 of IEEE Transactions on Software Engineering for an interesting paper describing APP. Unfortunately the APP tool doesn't seem to be freely available (I'm willing to be corrected on this). Note that any similarity between my examples and David's are due to morphic resonance.
To quote from http://www.sunlabs.com/research/adl/
:
ADL (Assertion Definition Language) is a specification language for programming interfaces. It can be used to describe the programmer's interface to any C-callable function, library or system call.
The Practical Specification Language.
ADL is the world's most practical specification language because:
- Even partial specifications are useful
- Test programs can be automatically generated from ADL specifications
- Specifications are external to the implementation of the interface, so that they are vendor-independent.
An Automated Test Generator.
An ADL specification is not just a paper document. It can be compiled by ADLT (the ADL translator). ADLT generates:
- Header files, that can be used in an implementation
- Test programs, that ensure that any implementation meets the specification
- Natural-language documentation, derived directly from the specification
ADLT can be used:
As a test generator, to create tests for existing software or for existing standards As a development tool, to ensure that documentation, software, and tests are aligned, and to enable concurrent work on all three aspects of software production.
Nana is essentially a poor mans implementation of some of these ideas which works for C and C++. Ideally in the best of all possible worlds you might want to look at Eiffel or in the military world Ada and Anna. If you use TCL/TK you might also be interested in Jon Cook's `AsserTCL' package.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Most C programmers become familiar with assertions from the the
assert.h
header. As such its a very good thing and has a nice
simple implementation. However it is also inefficient and leads some
people to the conclusion that assertion checking is an expensive luxury.
The implementation of assert.h
as distributed with gcc
looks like the following (after a bit of editing):
# ifndef NDEBUG # define _assert(ex) {if (!(ex)) \ {(void)fprintf(stderr, \ "Assertion failed: file \"%s\", line %d\n", \ __FILE__, __LINE__);exit(1);}} # define assert(ex) _assert(ex) # else # define _assert(ex) # define assert(ex) # endif |
There are are two main problems with this:
assert.h
had library code support we could make the implementation
much more space efficient, e.g. by calling a single function on error
detection.
Of course everyone merely rewrites their own `assert' macro so these are not significant objections. The only problem is if the author uses the libraries without modification.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This document aims to both describe the library and provide a tutorial in its use. Further work is required, particularly on the tutorial sections. If anyone has any suggestions please send them to me.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nana uses the normal GNU install method in the same way as `gcc' and `gdb'. To install nana in the default location `/usr/local/{bin,lib,include}' you would use:
% gzcat nana-1.10.tar.gz | tar xvf - % cd nana-1.10 % ./configure % make % make install % make check % make check-mail % make subscribe |
If you wish to produce space and time efficient code then replace the `./configure' with:
% I_DEFAULT=fast ./configure |
If you are using a Pentium compatiable CPU which supports the `RDTSC' instruction you may wish to enable cycle level timing in `cycles.h' by using:
% ./configure --enable-rdtsc |
The check-mail and subscribe targets both send e-mail. If you need to change the mailer used try something like:
% make MAILER=elm subscribe |
Note: we need to install nana before running the `make check' target. The `check-mail' target sends the test report via e-mail to the `gnuware@cs.ntu.edu.au'.
Of course things are never that simple. If you want to install Nana in a different location or change the behaviour on error detection see Configure.
Each of the sub-directories nana can be compiled and installed separately, e.g. if you don't need the documentation you can just compile and install from the `src' sub-directory after doing the configure statement.
Note that some of the subdirectories contain code that you may wish to install, improve or inspect. In particular:
2.1 Required Software | ||
2.2 Optional Software | ||
2.3 Configure | ||
2.4 Variables for ./configure | ||
2.5 Supported Platforms | ||
2.6 Supported Debuggers | ||
2.7 Known Problems | ||
2.8 Bug Reports | ||
2.9 New Versions |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The following software is possibly required to run nana.
gcc-2.7.2
Nana makes use of two GNU extensions in its library so you really should be using `gcc'. Some of the code can be used with any C compiler, though it may not be worth the bother. The dependencies on gcc are in `Q.h' which uses the statement value extension and in `L.h' which uses the variable number of arguments extension to `cpp'.
gdb-4.16+
A recent version of `gdb' is worthwhile, some early 4.?? versions had problems setting a large number of breakpoints. Note that `gdb-4.17' is available and has a few improvement which are useful for some parts of this package including the tools in `gdb'.
gmake
The `configure' script and `Makefiles' are generated using the `automake' and `autoconf' programs. They should be reasonably portable but if you have problems try using GNU make. For example on some old DEC boxes we have had strange behaviour using the system make.
For a listing of porting results including software versions see:
`http://www.cs.ntu.edu.au/homepages/pjm/nana-bug/'
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
In addition to the required software you might also be interested in:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nana uses a standard GNU autoconf
generated configure
script. The configure
script checks the setup on your machine and
then generates the appropriate Makefiles. Some of the things checked by
configure include:
-lposix
flag to the linker to build programs
on your machine.
unistd.h
available on this machine.
In addition `configure' uses the host architecture and operating system to generate the `nana-config.h' file. This file contains some macro definitions which define how nana works on particular operating systems and hardware architectures.
For example on `i386' machines we would use the `asm("hlt")' instruction whenever an assertion fails, on a `sparc' we would use `asm("unimp")'. Otherwise we would default to a plain C call to `abort()' If `configure' does not recognise your machine it uses plain C code.
You may wish to change these defaults on installation, one method is to edit
a local copy of the `nana-config.h' file. Alternately you can define
the code yourself in the call to `configure'. For example
to redefine the action we take when an error is detected by the I
macro we can use:
I_DEFAULT_HANDLER="restart_system()" ./configure |
As well as simple calls to routines various other bits of information are passed off to the `I_DEFAULT_HANDLER' such as the expression that failure and a failure code. For example:
% I_DEFAULT_HANDLER="restart(line,file,param)" ./configure |
The default for `I_DEFAULT_HANDLER' calls a function which prints a message and then dumps core. Different behaviour on failure can be organised by setting the `I_DEFAULT' to `fast', i.e. plain core dump or `verbose' which prints an error messsage and then does the core dump.
% I_DEFAULT=fast ./configure |
For nana the following examples may be useful:
./configure
Accept the default values for everything. In particular the files will be installed in:
`/usr/local/{bin,include,lib,man,info}'
./configure --prefix=~project/tools
Install the files into:
`~project/tools/{bin,include,lib,man,info}'
./configure --bindir=~project/bin --libdir=~/project/lib \
--includedir=~/project/headers --infodir=/usr/local/info \
--mandir=~/project/doc
The install directory for program (`bin'), etc can all be set with command line arguments to `configure'.
CC=xacc LIBS=-lposix ./configure sun3
If the defaults chosen by `configure' are not correct you can
override them by setting variables such as CC
before calling
`configure'. The `sun3' argument is used to identify the
machine we are running on and may be necessary on some machines.
./configure --help
And of course when in doubt ask for help.
For even more details see the file `INSTALL.con' which contains the generic instructions for use with `autoconf' generated `configure' scripts.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The configure program uses the following shell variables to change various defaults. Another method is simply to edit the `nana-config.h' file. Most of these values should be auto detected, so you can ignore this section until your need to save a few bytes of store by using `asm("hlt")' instead of a call to `abort()'.
DI_MAKE_VALID_BREAKPOINT
This text is inserted when the `DI.h' library needs to set a breakpoint in the generated code. It should ideally update all variables which being kept in registers etc so that gdb gets the correct values for each variable.
Possible values include:
This is the default.
DL_MAKE_VALID_BREAKPOINT
Used for the same purpose as `DI_MAKE_VALID_BREAKPOINT' for `DL.h'. It also defaults to `asm("nop")'.
I_DEFAULT_HANDLER
The code called when `I.h' detects an error.
asm("hlt")
Some machines use a `hlt' instruction.
asm("unimp")
And other machines use a `unimp' instruction.
abort()
Or we could use a call to `abort' which is at least standard C. On some machines this is significantly larger than a single `hlt' instruction.
restart()
Or a call to a function which attempts to restart the system.
ALWAYS_INCLUDE_MALLOC
This is a dodgey for some versions of Linux which don't seem to include `malloc' when you include `stdio.h' and use `print'. This causes problems for `gdb' since it uses `malloc' in the executable to implement parts of its functionality.
This kludge should be removed!
GDB
This is the pathname of the version of GDB you wish to use. For example on my FreeBSD box we have gdb-4.16 installed in `/usr/bin' and gdb-4.17 in `/usr/local/bin/' to optimise confusion. If you wish nana to use 4.17 try something like:
GDB=/usr/local/bin/gdb ./configure |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nana has been tested on the following platforms:
The `alpha-dec-osf3.2', `mips-sgi-irix5.3' and `powerpc-ibm-aix3.2.5' implementations have problems when you compile with `-O2' or `-O3' optimisation. This causes some errors in the the debugger based assertion and logging code since variables can be removed or changed by optimisation. At `-O' everything passes. Regardless of optimisation the C based checking code passes all tests on these platforms.
If you use nana on a new platform please send the report file to me via the `make check-mail' command. A machine generated list of this information is available at:
`http://www.cs.ntu.edu.au/homepages/gnuware/nana'
(Warning this page is out of date and may be fixed shortly)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Currently Nana works with the GNU GDB debugger which is available on a wide range of platforms including embedded systems and even provides support for remote debugging. Porting to any reasonable debugger with conditional breakpoints and commands is not very difficult.
As an example of an unreasonable debugger, Nana has been ported to
work with the Microsoft CodeView debugger. The port is small (60 lines of
code) but suffers from a problem with variable scoping in CodeView. If
a breakpoint is set at a point in the code the expressions are not
evaluated from that particular scope. For example setting a breakpoint
in the function f
cannot access a variable local to f
directly. CodeView has a unique (expletive deleted) scope operator
which you must use to set the scope `{...}'. This makes the
interface somewhat less than beautiful.
Another good thing about CodeView is to try a debug command which prints a message which contains a single open `{'. This of course causes it to hang and was the main problem during the porting to CodeView which took a whole day.(4)
If anyone is interested I may release the CodeView implementation, please contact me if you are interested. Of course a better bet is probably to move to the `gdbserver' system. I think `gdb' has been released as a native even for some Microsoft operating systems.
Other debuggers like DBX don't seem to be worth the trouble since gdb works on those machines. A redesign of the nana internals may also be useful if we decide portability between debuggers is actually useful.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Nana has the following known features (or perhaps problems):
DI
or DL
should be on lines by themselves. If you
mix code and nana macros on the same line you will get errors, e.g:
main(){ int x; x = 5; x--; DI(x == 4); } |
This doesn't work since breakpoints are set at line boundaries rather than statement ones. Of course anyone who writes code like this deserves whatever happens to them.
DI(x + 10 > 30); |
A few backslashes may solve this particular problem.
The `configure' script automatically detects the target operating system and architecture and then generates `nana-config.h'. If the options selected in `nana-config.h' are incorrect they can be edited by hand and installed in the usual include directory. The easiest method is simply to delete all macros in `nana-config.h' since the system defaults to more portable (and less efficient) implementations. If you wish to do this from the configure script you can try giving a unsupported machine type, e.g.
% ./configure pdp11-dec-ultrix |
configure
script detecting vsnprintf
. If configure
doesn't find it
and it does exist then simply define it in `nana-config.h' as per
the previous question.
If vsnprintf
really doesn't exist then get a new C library,
possibly the GNU libc.
vsprintf
opens a security hole since no
bounds checking is done by it. Nana attempts to use vsnprintf
which is safe when it exists but it will resort to vsprintf
if it can't find vsnprintf
. All careful people should make
sure that they have a library with vsnprintf
.
Qstl.h
doesn't work since the STL library has not
been installed along with C++. This can of course be fixed by installing
STL. See:
`http://www.stl.org'
STL
header file errors due to nana.
The C++ STL
header files for version 3.0 at least must
be included before the Q.h
file.
The problem is caused by the STL files using S
as a template
argument. Of course Q.h
uses S
for summing a
series. As usual namespace pollution strikes again.
(Thanks to Han Holl for this particular problem).
DI.h
or DL.h
on code that has not
been compiled with -g
then misery follows.
(Thanks to Eugen Dedu for this one)
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If you think you have found a bug in the Nana library, please investigate it and report it.
If your bug report is good, we will do our best to help you to get a corrected version of the library; if the bug report is poor, we won't do anything about it (apart from asking you to send better bug reports).
Send your bug report to:
`nana-bug@cs.ntu.edu.au'
Copies of bug reports will be kept at:
`http://www.cs.ntu.edu.au/homepages/pjm/nana-bug/'
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
New versions of nana will be made available at:
`ftp://ftp.cs.ntu.edu.au/pub/nana/'
If you wish to be informed about new releases of nana then subscribe to the nana mailing list. Send a message containing `subscribe' <your e-mail address> to:
`mailto:nana-request@it.ntu.edu.au'.
A hypermail archive of this list is kept at:
`http://www.cs.ntu.edu.au/hypermail/nana-archive'
If you wish to send a message to the list send it to `mailto:nana@it.ntu.edu.au'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The functions defined by Nana are implemented either as pure C code or as a set of commands which are generated for the debugger. To use the C based support for assertion checking you would use something like:
#include <nana.h> /* this file includes the other nana .h files */ int floor_sqrt(int i) { /* returns floor(sqrt(i) */ int answer; I(i >= 0); /* assert(i >= 0) if i -ve then exit */ ...; /* code to calculate sqrt(i) */ L("floor_sqrt(%d) == %d\n", i, answer); /* logs a printf style message */ } |
To compile and link the previous code you may need to use the `-Ipath' or `-lnana' flags with the compiler. For example:
% gcc toy.c -lnana |
If the nana headers have been installed in a strange location you may need to do something like:
% gcc -I<strange location>/include toy.c -L<strange location>/lib -lnana |
The next example uses the debugger versions of `L' and `I'. If the code is run under the debugger these checks will occur, otherwise they take up a negligible amount of space and time.
#include <nana.h> /* this includes the other nana .h files */ int floor_sqrt(int i){ int answer; DI(i >= 0); /* assert(i >= 0) if i -ve then exit */ ...; /* code to calculate sqrt(i) */ DL("floor_sqrt(%d) == %d\n", i, answer); /* logs a printf style message */ } |
To generate the debugger commands from the C source we just run the `nana' filter over the program and then execute the commands under gdb using the `source' command. You also need to compile the program with the `-g' option so the debugger works. So something like:
% gcc -g sqrt.c % nana sqrt.c >sqrt.gdb % gdb a.out (gdb) source sqrt.gdb breakpoint insert: ... (gdb) run ... (gdb) quit |
Note that any C preprocessor flags which you use must be passed off to the `nana' command. The best way to do this of course is in a Makefile. Something like the following works for GNU Make:
%.nana: %.c nana $(CFLAGS) $< >$@ |
The `nana' filter can also be run over multiple source files in a single run if thats more convenient.
For convenience a number of other simple scripts are provided, in particular to:
nana-run
Run a program under the debugger without prompting, etc. For example:
% nana-run a.out -x main.gdb output from program |
nana-clg
Compiles the program, generates the debugger commands and the runs the program using `nana-run'. For example:
% nana-clg -O3 main.c output from program |
You can change the compiler invoked by `nana-clg' by redefining the `NANACC' environment variable. For example:
% NANACC=g++ nana-clg -O3 main.cc |
The installation also `nana-c++lg' which compiles your code using a GNU C++ compiler.
nana-trace
Generates a line by line trace of the execution of a program using GDB. For example:
% nana-trace a.out 54 printf("main()\n"); 55 x = distance(5,-5); distance (i=5, j=-5) at test.c:47 47 i = -i; 48 j = -j; ... |
The arguments to `nana-trace' are passed directly to GDB. If you wish display variables or call procedures on each line then could use something like:
% nana-trace -x mycommands.gdb a.out |
Where the `mycommands.gdb' contains the GDB commands such as `display x' which causes `x' to be printed every time the debugger gets control of the program.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This section describes the details of the interface to nana library.
All of the files can be included multiple times without ill-effect since they use the C preprocessor to make sure the header declarations are only seen the first by the compiler. Each of the files can also be included individually.
If any of the following routines have an internal problem (e.g. malloc fails due to lack of memory) they will call the `nana_error' function defined in `nana_error.c'. By default this function prints a message and dumps core using `abort'. If you wish to override this behaviour you should define your own handler before linking in the nana library.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The `nana.h' file includes most of the other files in the library. In particular it `#include's' the following files:
I.h
DI.h
L.h
DL.h
Q.h
GDB.h
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
If you wish to disable all nana code you can `#define' the `WITHOUT_NANA' macro. This selects versions of the macros defined in `I.h',`L.h', etc which map to `/* empty */'.
So if you are using nana for your development but don't wish to force your customers to use it you can add an option to your `configure' script to define/undefine `WITHOUT_NANA'. In addition you will need to distribute copies of the nana header files with your package to get the stubs.
Note that the `L.h' and `DL.h' macros use the macro variable number of arguments extension provided by GNU C. If you wish your code to be portable you should use the macros `VL((..))', etc rather than `L(..)' to avoid problems with non GNU C preprocessors which only take a fixed number of arguments.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This implements the C based invariant checking code and is a replacement for `assert.h'. The first two macros are the normal user interface; the remainder are used for configuring the behaviour on failure, etc.
The exprn should always be true if the program is correct. If the exprn is false a message will be printed, followed by core dump.(6)
Checking can be enabled and disabled by using the I_LEVEL and I_DEFAULT_GUARD macros. See the definitions below for these macros for further details.
Note that exprn should have no side-effects(7) since disabling checking shouldn't change your programs behaviour.
I(z != 0); x = y / z; |
The opposite of `I', i.e. the expression must never ever be true if
the program is working properly. It is equivelant to I(!(e))
and
exists as a piece of syntactic sugar which may be helpful for complicated
boolean expressions.
char* strdup(char *s) { N(s == NULL); ...; } |
The `I_LEVEL' macro is used to globally enable and disable checking by the macros in this file. It can take on one of three values:
0
Disable all checking. Regardless of anything else no code will be
generated for I
, N
, etc.
1
Enable checking only if the corresponding guard condition is true. The guard condition can be used to enable and disable checking at compile and run time.
2
Enable all checking regardless of guard conditions.
I_LEVEL
defaults to 1
.
The I_DEFAULT_GUARD
is used to selectively enable or disable
checking at compile or run time.
I_DEFAULT_GUARD
defaults to TRUE
, i.e. always enabled.
A user would typically define I_DEFAULT_GUARD
to be global or local
variable which is used to turn checking on or off at run-time. For
example:
#define I_DEFAULT_GUARD i_guard > 0 extern int i_guard; |
This is passed off to the I_DEFAULT_HANDLER
and defaults to
nothing, it is just some text and is intended to pass failure codes
(e.g. IEH303
) or requests (e.g. HW_DEAD
) information off
to the handler.
I_DEFAULT_PARAMS
defaults to nothing.
When an error is detected the I_DEFAULT_HANDLER
will be called to
handle the error. The arguments are:
exprn
A string representation of the expression that has failed, e.g. "I(i>=0)"
.
file
The file that this error occurred in, i.e. __FILE__
.
line
The line number for the error, i.e. __LINE__
.
param
An optional parameter which can be passed across which defaults to
I_DEFAULT_PARAMS
. This can be used to pass failure codes or other
information from the checking code to the handler.
All of the remaining macros are used to individually override the default values defined above. Normally these macros would be used in a system wide header file to define macros appropriate for the application. For example you might use `IH' to define different checking macros for hardware and software faults.
We also provide support for referring to previous values of variables in
postconditions. The ID
macro is used to create variables to
save the old state in. The IS
and ISG
macros are to
set these values.
For example:
void ex(int &r) { ID(int oldr = r); /* save parameter */ g(r); I(oldr == r); /* check r is unchanged */ while(more()) { IS(oldr = r); /* assign r to oldr */ h(r); I(oldr == r * r); } } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This implements the debugger based invariant checking code. The first two macros are the normal user interface; the remainder are used for configuring the behaviour on failure, etc. Note that these macros have no effect unless you run your program under the debugger and read in the commands generated by the `nana' command. You also need to compile the program with the `-g' option.
The exprn should always be true if the program is working. If it is true then nothing happens otherwise the code given by `DI_DEFAULT_HANDLER' will be called which by default prints a message and dies just like `assert.h'.
The checking using DI can be enabled and disabled by using the DI_LEVEL and DI_DEFAULT_GUARD macros. See the definitions below for these macros for further details.
Note that exprn should have no side-effects(8) since disabling the checking shouldn't change your programs behaviour.
The opposite of `DI', i.e. the expression must never ever be true if
the program is working properly. It is equivelant to I(!(e))
and
exists as piece of syntactic sugar which is helpful for complicated
boolean expressions.
The `DI_LEVEL' macro is used to globally enable and disable checking, in particular it can take on one of three values:
0
Disable all checking. Regardless of anything else no code will be
generated for DI
, DN
, etc.
1
Enable checking only if the corresponding guard condition is true. The guard condition can be used to enable and disable checking at compile and run time.
2
Enable all checking regardless of guard conditions, etc.
DI_LEVEL
defaults to 1
.
The DI_DEFAULT_GUARD
is used to selectively enable or disable
checking at compile or run time.
DI_DEFAULT_GUARD
defaults to TRUE
, i.e. always enabled.
A user would typically define DI_DEFAULT_GUARD
to be global or local
variable which is used to turn checking on or off at run-time. For
example:
#define DI_DEFAULT_GUARD (i_guard) extern int i_guard; |
This is passed off to the DI_DEFAULT_HANDLER
and defaults to
nothing, it is just some text and is intended to pass failure codes
(e.g. IEH303
) or requests (e.g. HW_DEAD
) information off
to the handler.
DI_DEFAULT_PARAMS
defaults to nothing.
When an error is detected the DI_DEFAULT_HANDLER
will be called to
handle the error. The arguments are:
exprn
A string representation of the expression that has failed, e.g. "I(i>=0)"
.
file
The file that this error occurred in, i.e. __FILE__
.
line
The line number for the error, i.e. __LINE__
.
param
An optional parameter which can be passed across which defaults to
DI_DEFAULT_PARAMS
. This can be used to pass failure codes or other
information from the checking code to the handler.
This macro is used to ensure that a breakpoint can be set at the
location we are checking using DI
, etc. It defaults to
asm("nop")
and can be redefined by the user.
All of these macros are used to individually override the default values defined above. Normally these macros would be used in a system wide header file to define macros appropriate for the application.
These macros are used to assign values to convenience variables in the
debugger. Convenience variables are dynamically typed, global in scope
and initialised to 0. They start with a single $
and can be used be
used for saving the state of program or for counting events. The
`DS' macro executes e under the same rules as DI
.
The `DSG' macro executes e only if the the expression
g is true.
Note that `DS' and `DSG' can also be used for modifying C variables and calling functions.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
These routines are used to provide logging functions. Messages can be divided into classes and separately enabled and disabled.
Used to log a message in a similar way to printf.
Defaults to a using fprintf
on stderr
.
And all of the special functions.
The macros such as `L' depend on the GNU CC variable number of arguments to macros extension. If you wish to compile your code on other systems you might wish to use the following variations on `L', etc.
Each of these macros calls the corresponding function from the previous group, i.e. by default `VLG' is the same as a call to `LG'. If you define `WITHOUT_NANA' all these macros are translated to `/* empty */'.
Thus you can have nana under GCC whilst the code is still portable to other compilers. However debugging information will not be available on other platforms.
Note: the argument list is surrounded by two sets of brackets. For example:
VL(("haze in darwin = %d\n", 3.4)); |
Used to enable and disable logging independently of guard expressions.
2
Always print message
1
Print message only if the guard expression is true.
0
Never print any messages.
Defaults to 1
.
The default handler for printing which is simply the name of the logging function or macro.
Defaults to fprintf
The default guard condition for logging.
Defaults to TRUE
.
The default parameter passed off to the logging function or macro.
Defaults to stderr
If defined then display the time in front of each message.
A format string for the time stamp in the log. By default it prints the time out in seconds.
The name of a function that returns the time for the time stamp. This defaults to the `now' function from `now.h'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A traditional embedded systems trick is to log messages to a circular buffer in core. This has the following benefits:
Used to define buffer variables, it is similar to `FILE*' type in `stdio.h'. To create an instance use `L_buffer_create'.
These are used to create or delete a buffer which can contain size characters.
L_BUFFER *lbuffer; lbuffer = L_buffer_create(32*1024); /* create a 32K buffer */ ...; L_buffer_delete(lbuffer); /* and delete it after use */ |
A buffer created by `L_buffer_create' is set up so that the new messages will overwrite the older messages in the buffer. If you wish to disable this overwriting, e.g. to keep the first 32K bytes of your system startup messages you should use `L_buffer_wraparound'. For example:
L_BUFFER *lb = L_buffer_create(32*1024); L_buffer_wraparound(lb, 0); /* disable wraparound */ |
These are the routines which do that actual printing to the buffer.
L_buffer_printf(lbuffer, "U: user input %c\n", c); L_buffer_puts(lbuffer, "warning: its too hot"); L_buffer_putchar(lbuffer, '*'); |
Note: a null pointer passed to the `L_buffer_puts' function prints as `(null)'. (9)
Clear the log, i.e. remove all messages and start again.
Dump the contents of the log *b to the file descriptor *fp. Typically *fp would be `stderr'.
Note that this does not change the contents of the buffer. This is important since we may have a hardware or software problem part of the way through the dump operation and you don't want to loose anything.
To reset the buffer after a successful dump use `L_buffer_clear'.
The output of `L_buffer_dump' consists of a starting message followed by the contents of the log. If a character in the log is not printable we print it out in hex on a line by itself.
* L_buffer_dump = log message and another * non-printable character 0x1 more log messages * end of dump |
You also need to be able to integrate these functions into your design. See `examples/ott.c' for a complicated example. Here we will provide a simplified version which implements a new logging macro called `LFAST' which does a `printf' to the `log_buffer'. If you want to have all messages going to a `L_BUFFER' then you can redefine `L_DEFAULT_HANDLER'.
/* project.h - the project wide include file */ #include <nana.h> #include <L_buffer.h> /* LFAST(char *, ...) - log a message to the log_buffer */ /* ##f translates as the rest of the arguments to LFAST */ #define LFAST(f...) LHP(L_buffer_printf,log_buffer,##f) extern L_BUFFER *log_buffer; /* the log buffer */ |
The main program merely creates the log_buffer and eventually calls `L_buffer_dump' to print out the buffer when the system dies.
/* main.c - initialise the system and start things */ #include <project.h> L_BUFFER *log_buffer; main() { log_buffer = L_buffer_create(16000); if(log_buffer == NULL) { /* not enough store */ ... } LFAST("system starting at %f\n", now()); ...; } void fatal_error() { /* called on fatal errors */ FILE *f = fopen("project.errors","w"); L_buffer_dump(b, stderr); /* print log to stderr */ L_buffer_dump(b, f); /* print log to file */ } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This component is used to record events and times with a lower time and space overhead than the `L_buffer.h' component. Instead of using a `printf' style string we simply record the time and a pointer to a string identifying the event in a circular buffer.
Note 0: the string arguments should not be modified after a call since we record pointers to the strings rather than the strings themselves.
Note 1: there is no printf style formatting, e.g. `%d' in this package.
Used to define buffers, it is similar to `FILE*' type in `stdio.h'. To create an instance use `L_times_create'.
These are used to create or delete a buffer which can contain size messages.
A buffer created by `L_times_create' is set up so that the new messages will overwrite the oldest messages in the buffer. If you wish to disable this overwriting, e.g. to keep the first few messages messages you could use `L_times_wraparound(b,0)'.
Add an event identified by message m at time t to b. The type NANA_TIME defaults to `double'.
Dump the contents of the buffer out.
Clear all the messages from b.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
These routines are used to provide logging functions. Messages can be divided into classes and separately enabled and disabled. Note that these macros have no effect unless you run your program under the debugger and read in the commands generated by the `nana' command. You also need to compile the program with the `-g' option.
Used to log a message.
Defaults to a using fprintf
on stdout
.
And all of the special functions.
The macros such as `DL' depend on the GNU CC variable number of arguments to macros extension. If you wish to compile your code on other systems you might wish to use the following variations on `DL', etc.
Each of these macros calls the corresponding function from the previous group, i.e. by default `VDL' is equivelant to a call to `DL'. If `WITHOUT_NANA' is defined then the call too `VDL' is equivelant to `/* empty */'.
Thus you can have debugging under GCC whilst the code is still portable to other compilers. However debugging information will not be available on other platforms.
Note: the argument list is surrounded by two sets of brackets. For example:
VDL(("haze in darwin = %d\n", 3.4)); |
Used to enable and disable logging independently of guard expressions.
2
Always print message
1
Print message only if the guard expression is true.
0
Never print any messages.
Defaults to 1
.
The default handler for printing which is simply the name of the printing function.
Defaults to fprintf
Defaults to TRUE
.
Defaults to stdout
Each message can be given an individual time stamp by defining
DL_SHOW_TIME
. This causes the _L_gettime
routine to be
called before each message which generates the timestamp. A default
version is provided by the nana library.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
`GDB.h' provides macros for generating user specified commands in the output of the `nana' command. They are not included by default in the `nana.h' file. Note that these macros have no effect unless you run your program under the debugger and read in the commands generated by the `nana' command. You also need to compile the program with the `-g' option.
Emit a single line command when running this file through `nana'. Note that each line must be passed off separately to the `GDB' macro.
This could be used to set debugger options or to define procedures inside `gdb', e.g.
GDB(define checkstack); GDB( if 0 <= n && n <= 10); GDB( print "stack ok"); GDB( else); GDB( print "stack corrupted"); GDB( end); GDB(end); |
Causes a single gdb command to be executed whenever control passes through this line of code. After the user's command is executed control automatically returns to the program.
GDBCALL(set memory_check = 1) |
These macros could used for instrumenting code or setting up test harnesses, e.g.
GDB(set $siocall = 0); GDB(set $sioerr = 0); void sio_driver() { GDBCALL(set $siocall++) if(SIO_REQ & 0x010) { GDBCALL(set $sioerr++); ... } } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
`Q.h' provides support for the quantifiers of predicate logic. For example to check that all elements in a data structure have some property we would use universal (forall, upside down A) quantification. To check that one or more values in a data structure have some property we would use existential (exists, back the front E) quantification. For example:
/* all values in a[] must be between 0 and 10 */ I(A(int i = 0, i < n_array, i++, 0 <= a[i] && a[i] <= 10)); /* there exists a value in linked list l which is smaller than 10 */ I(E(node *p = l, p != NULL, p = p->next, p->data <= 10)); |
The first three arguments to `A' and `E' are similar to a C `for' loop which iterates over the values we wish to check. The final argument is the expression that must be true.
The only minor difference from the C `for' loop is that variables may be declared at the start of the loop, even if you are using C rather than C++ which already supports this.(10)
The `Q.h' macros can also be nested and used anywhere a boolean value is required. For example:
if(A(int i = 0, i < MAXX, i++, A(int j = 0, j < MAXY, j++, m[i][j] == (i == j ? 1 : 0)))) { /* identity matrix, i.e. all 0's except for 1's on */ /* the diagonal */ ... } else { /* not an identity matrix */ ... } |
The results from these macros can also be combined using boolean operations, e.g.
/* the values in a[i] are either ALL positive or ALL negative */ I(A(int i = 0, i < MAX, i++, a[i] >= 0) || A(int i = 0, i < MAX, i++, a[i] < 0)); |
Portability: note the macros in this file require the GNU CC/C++ statement expression extension of GCC to work. If you're not using GNU CC then for now you are out of luck. At some time in the future we may implement a method which will work for standard C++, standard C is a bit of a challenge.
Portability: unfortunately these macros do not work for the `DI' and `DL' macros since the statement expression extension has not been implemented in GDB.
For all values generated by `for(int;condition;next)' the exprn must be true.
I(A(int i = 0, i < MAX, i++, a[i] >= 0)); /* all a[i] are +ve */ |
There exists at least one value for exprn generated by `for (int;condition;next)' which is true.
/* one or more a[i] >= 0 */ I(E(int i = 0, i < MAX, i++, a[i] >= 0)); |
Returns the number of times the exprn is true over the values generated by `for(int;condition;next)'.
/* 3 elements of a[] are +ve */ I(C(int i = 0, i < MAX, i++, a[i] >= 0) == 3); |
There exists only one value generated by `for(int;condition;next)' for which the exprn is true.
/* a single elements of a[] is +ve */ I(E1(int i = 0, i < MAX, i++, a[i] >= 0)); |
Sum the values generated by exprn for all values given by `for(int;condition;next)'. The type of the value returned is given by the type of the exprn.(11)
/* sum of a[] is 10 */ I(S(int i = 0, i < MAX, i++, a[i]) == 10); /* sum of all +ve numbers in a[] is 10 */ I(S(int i = 0, i < MAX, i++, a[i] >= 0 ? a[i] : 0) == 10); |
Returns the product of the values generated by exprn for all values given by `for(int;condition;next)'. The type returned is the type of the expression.
/* product of all the values in a[] is 10 */ I(P(int i = 0, i < MAX, i++, a[i]) == 10); /* a = x^y i.e. x*x..*x y times */ I(P(int i = 0, i < y, i++, x) == a); |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The Standard Template Library (STL) is a library for C++ that makes extensive use of templates to implement the standard container classes and much more. Each of the container classes provides an interface to iterate over all the objects in the container, e.g.
// MAP is an associate array from location(lat,long) onto the name. typedef map<location,string,locationlt> MAP; void print_map_names(MAP& m) { // print out all the names in the map for(MAP::iterator i = m.begin(); i != m.end(); ++i) { cout << (*i).second << "\n"; } } |
`Qstl.h' provides the same facilities as `Q.h' but uses the standard STL iterator protocol shown above. The names in `Qstl.h' are generated by appending a `O' (O not zero!) to the names in `Q.h'. In particular:
For all values in the container class the predicate must be true. The predicate refers to individual values using name. See the STL documentation for more details. Another way of putting this is forall name in container the predicate must be true.
map<int,char *,ltint> m; // all keys (or indexes) into m are positive I(AO(i, m, (*i).first >= 0)); |
There exists one or more values in the container class for which the predicate is true.
map<int,char,ltint> m; // one or more characters in m are '$' I(EO(i, m, (*i).second == '$')); |
There exists one value in the container for which the predicate is true.
map<int,char,ltint> m; // one characters in m is a '$' I(E1O(i, m, (*i).second == '$')); |
Returns the number of times the predicate was true for all values in the container.
map<int,char,ltint> m; int nalpha; // count the number of alphabetic chars in the map nalpha = CO(i, m, isalpha((*i).second)); |
Sum the exprn for all values in the container.
map<int,float,ltint> m; float sum; // sum all the values in m sum = SO(i, m, (*i).second); |
Take the product of the exprn for all values in the container.
map<int,float,ltint> m; float product; // multiply all the values in m product = PO(i, m, (*i).second); |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The `now.h' file provides some simple time measurement routines. It is not included in `nana.h' so you must include this file separately.
It uses the `gettimeofday' system call and has an accuracy of between 1us and 10ms depending on the operating system and hardware configuration.
See the IPM package if you require better measurement tools.(12)
Returns the time in seconds since the beginning of time as defined by your system. If you call `now_reset' the time will start again at 0.
Reset the times returned by `now' to 0.
Returns the elapsed time between *dp and now(). It then sets *dp to now, thus giving a delta time between particular events.
t = now(); for(;;) { ...; /* code that must finish in 50ms */ I(now_delta(&t) <= 0.050); } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Some modern CPU's provide user accessible registers or special intstructions which can access a counter driven directly by the CPU clock. The `cycles.h' library provides access to these instructions together with some calibration and utility routines.
Currently we only provide support for Pentium/Cyrix machines using the `RDTSC' instruction. If you want to use these routines you need to run the `configure' script with the `--enable-rdtsc' option. Other machine architectures will be supported as time goes on.
The CPU cycle measurement type, typically a 64 bit unsigned integer.
Returns the current value for the cycle counter.
Returns an estimate of the number of cycles per second using the `now.h' library. The measurement is taken n times using a measurement period of t seconds for each measurement. The minimum and maximum values for the measurement are set by each call to `cycles_per_second' and are available from the next two functions.
Return the minimum or maximum of the measurements carried out by the previous call to `cycles_per_second'.
Returns the time difference between start and stop cycles in seconds as a double. As usual there are a few requirements:
4.13.1 RDTSC: cycle timing for Pentium, Cyrix, etc |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The RDTSC instruction is used for cycle timing on Pentiums and other compatible CPUs such as the Cyrix chip set. Note that this instruction does not exist on earlier CPUs in the series.
We could of course try to discover the CPU type at compile or run time and then use the appropriate instruction. This has all sorts of problems, e.g. if we compile on a i586 does that mean it will be run on the same CPU (no of course not....).
For now we use the `--enable-rdtsc' option for `./configure'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Eiffel is a very nice language which provides the assertion checking facilities of nana inside the language itself. The `eiffel.h' library is intended to provide a similar setup to Eiffel in the C++ language.
4.14.1 EIFFEL_CHECK: enabling and disabling checking. | ||
4.14.2 DOEND: adding DO ... END | ||
4.14.3 REQUIRE, ENSURE, CHECK, etc. |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Assertion checking is controlled by the EIFFEL_CHECK macro which can take on any of the following values:
CHECK_NO
Disable all checking.
CHECK_REQUIRE
Check the preconditions for each method.
CHECK_ENSURE
And also check the postconditions.
CHECK_INVARIANT
And also check the class invariant before and after each method is called. The programmer should provide a class method called `invariant' which returns `true' if the object is consistent, `false' otherwise.
CHECK_LOOP
And also check the loop invariants.
CHECK_ALL
And also check any assertions using the `CHECK' instruction.
Note that the default value for EIFFEL_CHECK
is
CHECK_REQUIRE
, i.e. check preconditions only.
A typical compile flag to the compile might be:
% g++ -c -DEIFFEL_CHECK=CHECK_ALL play.cc |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
At the suggestion of Bertrand Meyer (Eiffel's author) the DO
and
END
macros have been added to `eiffel.h'. Note that these
are only available if you define the EIFFEL_DOEND
macro. To use
these macros each of your methods should use DO
... END
as
their outermost brackets. For example:
// compiled with EIFFEL_DOEND defined void Stack::push(int n) DO // checks the class invariant + { ... END // check the class invariant + } |
If you do not define the EIFFEL_DOEND
macro then `eiffel.h'
reverts to its old behaviour where `REQUIRE' and `ENSURE' also
check the class invariant. Thus to check the class invariant when you
are not using DO
and END
you would need to call
REQUIRE
and ENSURE
, for example:
// compile with EIFFEL_DOEND undefined (i.e. old behaviour) void Stack::push(int n) { REQUIRE(true); // checks the invariant as well as the precondition ENSURE(true); // checks the invariant as well as the postcondition } |
As for which one to option to pick, Bertrand Meyer is in favour of the
DO
... END
solution.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Here are the individual checking macros:
Called at the beginning of each method to check its precondition (requirements). For example:
void Stack::push(int n) { REQUIRE(!full()); // stack has space for push ... } |
If EIFFEL_DOEND
is not defined this also checks
the class invariant.
Called at the end of each method. This checks the postcondition for a method and the class invariant.
void Stack::push(int n) { ... ENSURE(!empty()); // it can't be empty after a push! } |
If EIFFEL_DOEND
is not defined this also checks
the class invariant.
Used to check a loop invariant.
Used for any other inline assertions. For example:
CHECK(z != 0); x = y / z; |
And finally a small example:
#include <eiffel.h> class example { int nobjects; map<location,string,locationlt> layer; public: bool invariant(); // is this object consistent void changeit(location l); }; bool example::invariant() { return AO(i,layer,valid_location((*i).first)) && nobjects >= 0; } void example::changeit(string n, location l) { REQUIRE(E1O(i,layer,(*i).second == n)); ...; while(..) { INVARIANT(...); ... INVARIANT(...); } ... CHECK(x == 5); ... ENSURE(layer[l] == n); } |
Note that the invariant checking macro `example::invariant' is called automatically on function entry/exit using the `REQUIRE' and `ENSURE' macros if `EIFFEL_CHECK' is not defined.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A drop in replacement for `assert.h' is provided in the `src' directory. It is not installed by default. If you wish to use it then you need to copy it to your include directory by hand.
This might be of use if you are already using `assert.h' and wish to save some code space since the nana implementation is more space efficient.
Calls to `assert' are translated to calls to `I' and can be disabled by defining `NDEBUG'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The `calls' module implements a simple list of functions which can be modified and executed at run-time. It is similar in spirit to the ANSI C `atexit' function. It is intended to be used for:
For example each module could register a self checking function which uses the rest of the nana library. All of these functions would then be called using `calls.h' to check that the entire system is consistent.
A pointer to a `void' function which takes a single `void*' argument. The `void *' argument is intended to be used to pass information such as arguments or pointers to objects (e.g. `this' in C++). All of the checking/printing functions must be of this type, e.g.
void print_object(void *f) { ...; } |
This structure represents a single call to a function, i.e. a function pointer (`FUNC') and the `void*' argument.
CALL *head = 0; |
Adds a call to function `fp' with argument `arg' to the list pointed to by `head'.
CALL *global_checks = 0; calls_add(&global_checks,complex_ok,(void *)x); |
Execute all/some of the calls in the list given by `head'. The arguments `fp' and `arg' must both match for each individual call. The null pointer (`0') matches anything whilst any other value requires an exact match between the `CALL' and the arguments to `calls_exec'. For example:
calls_exec(&l,0,0); /* execute all functions in l */ calls_exec(&l,complex_print,0); /* calls complex_print(*) in l */ calls_exec(&l,0,(void*) &b); /* calls *(&b) in l */ calls_exec(&l,f,(void*) &b); /* calls f(&b) in l */ |
Delete all/some of the calls in the list given by `head'. The arguments `fp' and `arg' must both match for each individual call. The null pointer (`0') matches anything whilst any other value requires an exact match between the `CALL' and the arguments to `calls_delete'. For example:
calls_delete(&l,0,0); /* delete all functions in l */ calls_delete(&l,complex_print,0); /* delete complex_print(*) in l */ calls_delete(&l,0,(void*) &b); /* delete *(&b) in l */ calls_delete(&l,f,(void*) &b); /* delete f(&b) in l */ |
Note: that calls are added to the head of the list rather than the tail. This means that the most recently added call will be executed first (as in a stack).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The Eiffel language provides a shortform of a class which consists of the exported methods and their pre and post conditions. The private part of the class such as the code is hidden in this form leaving only:
This is useful to provide a summary of what the code does and how to use it rather than how it works.
Nana provides a similar service which can be used to generated a HTML version of the short form of your program automatically. The code for this is kept in `shortform'. Do a `make example' to build an example document.(14)
Consider the following program:
/* smallex.c - a small example */ #include <stdio.h> #include <math.h> #include <eiffel.h> void sort(int *v, int n) { int i; REQUIRE(v != NULL && 0 <= n); for(i = 0; < n; i++) { /* at last, an O(n) sort! */ v[i] = i; } /* And no, this isn't what most people think of as sorting */ ENSURE(A(int i = 0, i < n - 1, i++, v[i] <= v[i+1])); } |
Its short form can be generated by using the `nana-sfg'(15) program which generates:
% nana-sfg smallex.c ... #include <stdio.h> #include <math.h> #include <eiffel.h> ... void sort(int *v, int n) { ... REQUIRE(v != NULL && n >= 0); ... ENSURE(A(int i = 0, i < n, i++, v[i] <= v[i+1])); } % |
The `nana-sfg' program is a small AWK program which processes its arguments into shortform and always writes to the standard output. If it is passed no arguments it works as a normal UNIX filter reading from the standard input.
It is suggested that a copy of `nana-sfg' be kept in each projects `bin' directory so that it can be modified for local taste. The user will probably wish to modify the rules for short form generation. For example you might add rules such as:
/^\/\// { emit(); } # print out C++ comments in column 1 /^\/\*\+/,/\*\// { emit(); } # print out multi-line /*+ ... */ comments |
Of course for a real project you need to run `nana-sfg' over the entire source tree. To do this you can use the `nana-sfdir' program.
% nana-sfdir |
This command simply creates a copy of the source tree in the current directory under `NANASF' using the `nana-sfg' program. You can then run a source code to HTML translator over the `NANASF' directory. Currently we are using the GLOBAL package which was written by Shigio Yamaguchi which available from:
The alert reader will perhaps be asking themselves why we did not simply modify GLOBAL. Well that was the original idea, however after a bit of thinking it seemed better to separate the generation of the short form of the code from the generation of the HTML. This gives us the ability to use other translators and other tools. It also simplifies the interaction between nana and GLOBAL. For information on other translators see:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A tool is provided for measuring code/time requirements for arbitrary code fragments. This is kept in the `perf' directory and is not built by default. If you wish to use this tool use the following targets:
% cd perf % make perf |
The output is `perf.tex', `perf.dvi' and `perf/index.html'.
Note that the measurement requires the following:
As an indication of the values you can expect here is part of the results for `make perf' on a 200Mhz Cyrix MMX (i386) chip which runs at about 200 BogoMips under FreeBSD 2.2.6 with `-O'.
`assert(i >= 2);' 28 bytes, 19ns.
`TRAD_assert(i >= 2);' 47 bytes, 20ns.(16)
`I(i >= 2);' 9 bytes, 18ns.
`DI(i >= 2);' 1 byte, 147.4us.
`I(A(int i=0, i!=10, i++, a[i]>=0));' 28 bytes, 287ns.
`d = now();' 8 bytes, 3.1us.
`printf("helloworld\n");' 13 bytes, 9.1us.
`L("helloworld\n");' 18 bytes, 8.9us.
`DL("helloworld\n");' 1 byte, 26.4us.
Note that these measurements were on a system that was configured with `I_DEFAULT=fast ./configure'. The default output of `./configure' produces nice error messages at the cost of increased code space.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
A few tools for execution tracing and logging are available in the `gdb' directory and are installed by default. They are simple shell scripts and may be of some use in testing/development. Note that `gdb-4.17' may be required on some machines for this stuff to work properly.
7.1 Statement level tracing | ||
7.2 Library tracing |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
The `nana-trace' executes a program and generates a message for each line of code executed (a statement trace). The statement level trace is useful for things such as:
For example the `make ex-trace' command in `gdb' generates:
% make ex-trace gcc -g test.c sh ./nana-trace a.out 47 setbuf(stdout, NULL); /* disable buffering */ 49 printf("** main()\n"); ** main() 50 printf("** 1: %d\n", distance(1,-5)); distance (i=1, j=-5) at test.c:43 43 return abs(i - j); abs (i=6) at test.c:35 35 if(i >= 0) { 36 return i; 40 } distance (i=1, j=-5) at test.c:44 44 } ** 1: 6 main () at test.c:51 51 printf("** 2: %d\n", distance(twice(1),-5)); twice (i=1) at test.c:29 29 i = i * 2; 31 return i ; 32 } distance (i=2, j=-5) at test.c:43 43 return abs(i - j); abs (i=7) at test.c:35 35 if(i >= 0) { 36 return i; 40 } distance (i=2, j=-5) at test.c:44 44 } ** 2: 7 main () at test.c:52 52 printf("** 3: %d\n", distance(3,-5)); distance (i=3, j=-5) at test.c:43 43 return abs(i - j); abs (i=8) at test.c:35 35 if(i >= 0) { 36 return i; 40 } distance (i=3, j=-5) at test.c:44 44 } ** 3: 8 main () at test.c:53 53 } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
On most UNIX machines there exists a tool for tracing the system calls executed by a program. If you haven't used one of these tools (e.g. ktrace) then now is a good time to learn. Being able to trace the User/Kernel interface is an excellent way to understand complex applications.
The `nana-libtrace' facility generalises this idea to provide a GDB based function call tracer for all functions in which are defined in a particular library. For example the `make ex-libtrace' command in the `gdb' directory produces a trace of all calls to `libc'.
% gcc -g -static test.c -lm % ./nana-libtrace a.out >test.gdb printf write % cat test.gdb break printf command $bpnum silent where 1 cont end break write command $bpnum silent where 1 cont end % nana-run a.out -x test.gdb Breakpoint 1 at 0x1483: file /usr/.../libc/stdio/printf.c, line 65. Breakpoint 2 at 0x8c78 #0 printf (fmt=0x1116 "** main()\n") at /usr/src/lib/libc/../libc/stdio/printf.c:65 #0 0x8c78 in write () ** main() #0 printf (fmt=0x1121 "** 1: %d\n") at /usr/src/lib/libc/../libc/stdio/printf.c:65 #0 0x8c78 in write () ** 1: 6 #0 printf (fmt=0x112b "** 2: %d\n") at /usr/src/lib/libc/../libc/stdio/printf.c:65 #0 0x8c78 in write () ** 2: 7 #0 printf (fmt=0x1135 "** 3: %d\n") at /usr/src/lib/libc/../libc/stdio/printf.c:65 #0 0x8c78 in write () ** 3: 8 Program exited with code 010. |
#0 printf (fmt=0x1135 "** 3: %d\n") at /usr/src/lib/libc/../libc/stdio/printf.c:65 #0 vfprintf (fp=0xa1d4, fmt0=0x1135 "** 3: %d\n", ap=0xefbfd888 "\b") at /usr/src/lib/libc/../libc/stdio/vfprintf.c:428 #0 vfprintf (fp=0xefbfd5b8, fmt0=0x1135 "** 3: %d\n", ap=0xefbfd888 "\b") at /usr/src/lib/libc/../libc/stdio/vfprintf.c:428 #0 __sfvwrite (fp=0xefbfd5b8, uio=0xefbfd184) at /usr/src/lib/libc/../libc/stdio/fvwrite.c:68 #0 0x8ff8 in memcpy () #0 0x8ff8 in memcpy () #0 __sfvwrite (fp=0xefbfd5b8, uio=0xefbfd184) at /usr/src/lib/libc/../libc/stdio/fvwrite.c:68 #0 0x8ff8 in memcpy () #0 fflush (fp=0xefbfd5b8) at /usr/src/lib/libc/../libc/stdio/fflush.c:60 #0 __sflush (fp=0xefbfd5b8) at /usr/src/lib/libc/../libc/stdio/fflush.c:84 #0 __swrite (cookie=0xa1d4, buf=0xefbfd1b8 "** 3: 8\nain()\n", n=8) at /usr/src/lib/libc/../libc/stdio/stdio.c:78 #0 0x8c78 in write () ** 3: 8 |
Note that your library code must be compiled with `-g' to put in the debugging information if you wish to have the arguments displayed symbolically. This is fairly easy on most systems, e.g on FreeBSD you edit the global compile options `/etc/make.conf' and rebuild/install the entire system.
Ideally we would also like to be able to trace only entry calls and not internal calls to libc. This can be done by inserting a counter which is incremented on call and decremented on return. We print the trace messages iff the counter is 1. This extension (perhaps to GNU libc) may be written if people are interested.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter is intended to provide some hopefully useful examples of Nana. If any of the users of this library would be so kind as to contribute a section on their usage I would be obliged.
This section is under development
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As a nice simple, indeed trivial example consider the following:
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
`syslog' is a comprehensive logging system written by Eric Allman which is available on most UNIX systems. It provides facilities for sorting messages by source or severity and mechanisms for sending messages off to files, terminals or other machines. See chapter 12 of the "UNIX System Administration Handbook (2nd Edition)" by Nemeth, Snyder, Seebass and Hein for an excellent tutorial on `syslog'.
The rules used by `syslog' are normally defined in `/etc/syslog.conf'. Each line specifies the destination for messages with particular sources and priorities. For example:
# log mail messages at priority info to mailinfo mail.info /var/log/mailinfo # log all ftp information to syslog on a remote machine ftp.* @remote-machine.cs.ntu.edu.au # all critical errors to console *.crit /dev/console |
To use `syslog' we merely redefine the default handlers and parameter values for `nana'. For example:
#include <syslog.h> #define L_DEFAULT_HANDLER syslog /* replaces fprintf(3) by syslog(3) */ #define L_DEFAULT_PARAMS LOG_USER /* default priority for syslog info */ #include <nana.h> int main() { openlog("nana", /* identifier for these log messages */ LOG_PID, /* also log the process id */ LOG_DAEMON /* facility */ ); L("temperature is falling to %d", 35); /* logged at LOG_USER priority */ LP(LOG_CRIT, "the snow is falling in Darwin"); /* LOG_CRIT message */ closelog(); return 0; } |
This program results in the following addition to `/var/adm/messages':
Jun 12 16:04:46 chingiz nana[2671]: the temperature is falling to 35 Jun 12 16:04:46 chingiz nana[2671]: the snow is falling in Darwin |
In addition the `LOG_CRIT' message about snow falling in Darwin is sent to the console for immediate action.
Note: `syslog' normally uses `UDP' for its network communications and that `UDP' does not guarantee delivery.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Imagine you(19) are building a GNU program. Ideally it should run on systems without any other software including GCC, GDB and even Nana.
To achieve this noble goal you can provide your configure script with a `--without-nana'(20) flag which then `#define's' `WITHOUT_NANA'. You should also use the `VL' macro rather than `L' macro since `L' takes a variable number of arguments and will break non GNU C preprocessors.
int half(int n) { /* Don't use L(...) since it takes a variable number of args! */ VL(("hello world = %d\n", 10)); /* Note the doubled (( */ ...; } |
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
One of the uses (in fact the original use) for nana is in the testing of non-deterministic systems. These systems behave in a different way each time they are run because of timing or measurement noise. Thus you can't just `diff' the results against a known good run since they run differently each time.
Instead you can use a series of programs which execute over a the results of the program and check that the behaviour meets the specification automatically.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Assertions for the timing behaviour of you system can be done in nana nicely. You might also consider using an instruction level simulator such as PSIM and use the `cycles' variable to test realtime constraints.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Ah databases, business boffins using assertions. It would nice to get a real example for this one!
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
One nice way to use `L', `DL', etc is to punt off the messages off to a program which displays the information. You would use the `L_DEFAULT_PARAM' argument to send commands off to a pipe with gnuplot or TCL/TK interpreter running at the end.
For a small example of this, see `tcl/status.c'.
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
This chapter is intended to answer some general questions about the usage of Nana. Most of the material (perhaps) has been presented elsewhere, my apologies for repeating myself.
See the license details in the file `COPYING'. In summary GNU Nana is Free software which has been released under a license that allows commercial use provided the copyright is maintained. It is not under the GPL.
Set the `I_LEVEL' command to `0' using `-D' or `#define' before including `nana.h'.
Subscribe to the nana mailing list by using the `make subscribe' target in the nana root directory.
`nana-bug@it.ntu.edu.au' - bug archive
`nana@it.ntu.edu.au' - mailing list
`pjm@gnu.org' - the author
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
As usual, there is more work to be done than time to do it in. Workers or test pilots are invited to apply for the following tasks which may get down in the fullness of time minister. I'm particularly interested in which projects people think are worthwhile (or rubbish).
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Jump to: | )
0
1
2
A C D E F G I L N P Q R S T V |
---|
Jump to: | )
0
1
2
A C D E F G I L N P Q R S T V |
---|
[Top] | [Contents] | [Index] | [ ? ] |
Which is unfortunately quite uncommon in the authors experience.
Precondition: a boolean expression which must be true if the operation is to succeed. For example the `sort(int *v, int n)' might have have precondition that `v != NULL && n >= 0'.
Postcondition: a boolean expression that must be true if the operation is correct (and the precondition was true on entry).
And about 60 reset cycles where the machine went off into hyperspace.
This allows a variable number of arguments to C preprocessor macros.
If you don't want a core dump then look at stopping the core
dumps with ulimit
rather than changing the handler.
Side-effects include such operations as input/output or assignments, e.g. `x++'.
Side-effects include operations like input/output or assignments.
This was suggested by Phil Blecker.
ANSI C does not allow variable declarations at the beginning of loops unlike C++. The `Q.h' macros get around this by starting each loop with a new scope.
This uses yet another GNU CC extension, however since we are already using statement expressions we might as well use `typeof' as well.
In the fullness of time, we may integrate these routines in here.
Famous last words I know but: (2^64)/(1e9*60*60*24*365) = 584 yrs.
Note you need to install the GLOBAL package first. This is installed by default on FreeBSD systems. If you do not have the GLOBAL package read on.
The name `nana-sfg' stands for either Nana Short Form Generator or Nana Science Fiction Generator. Personally I prefer the later derivation.
This is the traditional assert which uses `fprintf' and `exit' in a macro. The BSD `assert' macro used in FreeBSD is a bit smarter and calls a function to do the message printing and exiting. Note that the real cost of this function is even higher since we are only measuring the code space requirements, not the space required for the message strings.
If possible we should replace the call to `nm' with a call to something which can print all the symbols for a dynamically linked library. Unfortunately GDB gets upset if you try to set a breakpoint for a function that does not exist. I suppose we could use gdb to print the symbol list out.
Of course you also need to use the gdb commands generated by the `nana' command, perhaps using `nana-clg'.
Gordon Matzigkeit contributed some of the ideas presented here and raised this problem.
Or add a `--with-nana' flag to configure that does the opposite.
[Top] | [Contents] | [Index] | [ ? ] |
[Top] | [Contents] | [Index] | [ ? ] |
[Top] | [Contents] | [Index] | [ ? ] |
This document was generated by Build Daemon on December, 13 2006 using texi2html 1.76.
The buttons in the navigation panels have the following meaning:
Button | Name | Go to | From 1.2.3 go to |
---|---|---|---|
[ < ] | Back | previous section in reading order | 1.2.2 |
[ > ] | Forward | next section in reading order | 1.2.4 |
[ << ] | FastBack | beginning of this chapter or previous chapter | 1 |
[ Up ] | Up | up section | 1.2 |
[ >> ] | FastForward | next chapter | 2 |
[Top] | Top | cover (top) of document | |
[Contents] | Contents | table of contents | |
[Index] | Index | index | |
[ ? ] | About | about (help) |
where the Example assumes that the current position is at Subsubsection One-Two-Three of a document of the following structure:
This document was generated by Build Daemon on December, 13 2006 using texi2html 1.76.