CMPSC
311,
Introduction to Systems Programming
Program-Checking Tools
Reading
- CP:AMA
- p. 6, Use software tools to make programs more reliable.
- p. 30, How good is GCC at finding errors in programs?
- Use the option -Wextra
instead of -W
- Be careful not to confuse -W (uppercase) with -w (lowercase).
- Sec. 19.5, Design Issues for Abstract Data Types (suggestion
to
use
assert()
)
- Sec. 24.1, The
<assert.h>
header:
Diagnostics
- C:ARM, Sec. 19.1 (
assert
)
- applicable man pages
- Using the
GCC Compiler Collection
- specific sections are linked below
References
- The C Standard, C11
draft
dated April 12, 2011
- Sec. 6.7.10, Static assertions
- Sec.7.2,
Diagnostics
<assert.h>
More Advanced Reading
Language Features
assert()
- run-time checks
- implemented as a macro with possible function call
- in C89, C99, C11
static_assert(),_Static_assert()
- compile-time checks
- implemented (syntactically) as a declaration with the
appearance of a function call
- in C11
static_assert
is an object-like macro, defined
in <assert.h>,
that expands
to _Static_assert
_Static_assert
is a keyword in C11
Language Extensions implemented by GCC
- compiler-specific, not for use outside system-provided include
files and libraries
__builtin_expect(expression, value)
- Inform the compiler that you expect expression == value.
This is
used in code optimization for static branch prediction.
If your
expectation is wrong, your program might run more slowly, but
that's
all.
- Avoid this - there are better methods using
feedback-directed
optimization.
Compiler options
-v
(Sun)
-Wall -Wextra
(GCC)
-S
, to generate assembly code
-O
, to generate optimized code
Compiler options, language version, Sun
Compiler options, language version, GCC
-std=
standard
- standard can be one
of
...
c89
c99
- c1x or c11
- and some others that you probably don't need or want
Compiler options, warnings, GCC
- GCC Manual, "Options
to
Request or Suppress Warnings".
-pedantic
will cause complaints about anything
even
remotely non-standard.
- Be careful of the
-w
option (lower-case w
),
which
will
inhibit
all
warning
messages.
-Wall
will enable a large number of warnings.
- "This enables all the warnings about constructions that some
users consider questionable, and that are easy to avoid (or
modify to
prevent the warning), even in conjunction with macros."
-Wchar-subscripts
-Wcomment
-Wformat
-Wnonnull
-Wimplicit-int
-Wimplicit-function-declaration
-Wmain
-Wmissing-braces
-Wparentheses
-Wsequence-point
-Wreturn-type
-Wswitch
-Wtrigraphs
-Wunused-function
-Wunused-label
-Wunused-variable
-Wunused-value
-Wuninitialized
-Wstrict-aliasing
-Wall -Wextra
will enable additional warnings.
-Wunused-parameter
- For example, An empty body occurs in an
if
or
else
statement.
-Wshadow
- Warn whenever a local variable shadows another local
variable,
parameter or global variable or whenever a built-in function
is
shadowed. [This is a scope problem, perhaps.]
- Lots more!
Compiler-like tools
- Lint (Sun and others, but not Linux or Mac OS X)
- Splint, see above
Runtime information - test coverage per line of code - how often?
- (Sun)
tcov
man tcov
cc -xprofile=tcov -o foo foo.c
foo < bar
- The directory
foo.profile
will be generated
-
don't delete it yet.
tcov -x foo.profile foo.c
- Now look at the file
foo.c.tcov
- it's plain
text.
- (GNU)
gcov
gcc -fprofile-arcs -ftest-coverage -o foo foo.c
- The file
foo.gcno
will be generated - don't
delete it yet.
foo < bar
- The file
foo.gcda
will be generated - don't
delete it yet.
gcov foo
- Now look at the file
foo.c.gcov
-
it's plain text.
- There will also be
.gcov
files for
everything
you #include
'd.
-fprofile-arcs
- Add code so that program flow arcs are
instrumented. During
execution the program records how many times each branch and
call is
executed and how many times it is taken or returns.
When the
compiled
program exits it saves this data to a file called auxname.gcda
for each source file. The data may be used for
profile-directed optimizations (-fbranch-probabilities),
or
for
test
coverage
analysis
(-ftest-coverage).
Each
object
file's
auxname is generated from the name of the
output
file, if
explicitly specified and it is not the final executable,
otherwise it
is
the basename of the source file. In both cases any
suffix is
removed
(e.g. foo.gcda for
input file dir/foo.c,
or dir/foo.gcda
for output
file specified as -o dir/foo.o).
-ftest-coverage
- Produce a notes file that the gcov
code-coverage utility
can use to
show program coverage. Each source file's note file is
called auxname.gcno.
Refer
to the -fprofile-arcs
option
above for a description of auxname and
instructions on how
to
generate test coverage data. Coverage data will match
the source
files
more closely, if you do not optimize.
--coverage
- This option is used to compile and link code instrumented
for
coverage
analysis. The option is a synonym for -fprofile-arcs -ftest-coverage (when
compiling) and -lgcov
(when
linking). See the documentation for those options for
more
details.
- Compile the source files with -fprofile-arcs
plus optimization
and code generation options. For test coverage
analysis, use the
additional -ftest-coverage
option. You do not need to profile
every source file in a program.
- Link your object files with -lgcov
or -fprofile-arcs
(the latter implies the former).
- Run the program on a representative workload to generate
the arc profile
information. This may be repeated any number of
times. You
can run
concurrent instances of your program, and provided that
the file system
supports locking, the data files will be correctly
updated. Also
fork
calls are detected and correctly handled
(double counting
will not happen).
- For profile-directed optimizations, compile the source
files again with
the same optimization and code generation options plus -fbranch-probabilities
(see Options
that
Control
Optimization).
- For test coverage analysis, use gcov
to produce human readable
information from the .gcno
and .gcda
files. Refer to the gcov
documentation
for further information.
With -fprofile-arcs,
for
each
function
of
your
program
GCC
creates
a program flow graph, then finds a spanning tree for the
graph. Only arcs that are not on the spanning tree have to
be
instrumented: the
compiler adds code to count the number of times that these arcs
are
executed. When an arc is the only exit or only entrance to a
block, the
instrumentation code can be added to the block; otherwise, a new
basic
block must be created to hold the instrumentation code.
Runtime information - test coverage per function - how often? how
long?
prof
- compile with
gcc -p
- watch this space ...
gprof
- compile with
gcc -pg
- watch this space ...
Post-mortem debugger
Runtime debugger
Using the assert()
macro
Synopsis
- #include <assert.h>
- NDEBUG is used in
assert.h, but
is not defined there
- static_assert is
a
macro that
expands to _Static_assert
- void assert(scalar expression);
- assert is a
function-like macro, which expands to a void expression
- ((void)0) if
disabled, and a
possible function call if enabled
- disable/enable is controlled
by NDEBUG
- if enabled and triggered, assert()
prints a message and calls abort()
Example, from <assert.h>
on Solaris, indentation
added
#ifdef NDEBUG
#define assert(EX) ((void)0)
#else
#if defined(__STDC__)
#if __STDC_VERSION__ - 0 >= 199901L
#define assert(EX) (void)((EX) || \
(__assert_c99(#EX, __FILE__, __LINE__, __func__), 0))
#else
#define assert(EX) (void)((EX) || \
(__assert(#EX, __FILE__, __LINE__), 0))
#endif /* __STDC_VERSION__ - 0 >= 199901L */
#else
#define assert(EX) (void)((EX) || \
(_assert("EX", __FILE__, __LINE__), 0))
#endif /* __STDC__ */
#endif /* NDEBUG */
In the source file file.c,
#include
<assert.h>
In a function body, in file.c,
assert(some
expression you expect to be true or
nonzero);
When you compile the program, and you want to leave the
assertion-checking on,
gcc
file.c
gcc -UNDEBUG file.c
When you compile the program, and you want to turn the
assertion-checking off,
gcc
-DNDEBUG
file.c
For fine-grain control, you can do this, in file.c,
#undef
NDEBUG
#define NDEBUG
#include <assert.h>
// any use of assert() here
is disabled
#undef NDEBUG
#include <assert.h>
// any use of assert() here
is enabled
Using static assertions, C11 only
Synopsis
- #include <assert.h>
- static_assert is
a
macro that
expands to _Static_assert
- _Static_assert is
a
keyword
in C11
Anywhere a declaration is valid,
- _Static_assert(constant-expression, string-literal);
- static_assert(constant-expression, string-literal);
The constant-expression
must
evaluate to an integer. If it is not equal to 0, the
declaration
has no effect. Otherwise, the compiler issues a diagnostic
message using the string-literal.
Follow-up Exercises
(Solaris)
(GNU)
Last revised, 12 Feb. 2013