Next: Preprocessing, Previous: Recursive Calls, Up: Top
An alert reader has already noticed something strange in the
above output: the function _exit
is missing, although according
to the source file it is called twice by printdir
. It is
because by default cflow omits from its output all symbols
beginning with underscore character. To include these symbols as well,
specify -i _ (or --include _) command line option.
Continuing our example:
$ cflow --number -i _ d.c 1 main() <int main (int argc,char **argv) at d.c:85>: 2 fprintf() 3 atoi() 4 printdir() <void printdir (int level,char *name) at d.c:42> (R): 5 getcwd() 6 perror() 7 _exit() 8 chdir() 9 opendir() 10 readdir() 11 printf() 12 ignorent() <int ignorent (char *name) at d.c:28>: 13 strcmp() 14 isdir() <int isdir (char *name) at d.c:12>: 15 stat() 16 perror() 17 S_ISDIR() 18 putchar() 19 printdir() <void printdir (int level,char *name) at d.c:42> (recursive: see 4) 20 closedir()
In general, --include takes an argument specifying a list of symbol classes. Default option behavior is to include the requested classes to the output. If the argument begins with a minus or caret sign, this behavior is reversed and the requested symbol classes are excluded from the output.
The symbol class ‘_’ includes symbols whose names begin with an
underscore. Another useful symbol class is ‘s’, representing
static functions or data. By default, static functions are
always included in the output. To omit them, one can give
-i ^s (or -i -s1)
command line option. Our sample program d.c defines static
function isdir
, running cflow -i ^s, completely omits
this function and its callees from the resulting graph:
$ cflow --number -i ^s d.c 1 main() <int main (int argc,char **argv) at d.c:85>: 2 fprintf() 3 atoi() 4 printdir() <void printdir (int level,char *name) at d.c:42> (R): 5 getcwd() 6 perror() 7 chdir() 8 opendir() 9 readdir() 10 printf() 11 ignorent() <int ignorent (char *name) at d.c:28>: 12 strcmp() 13 putchar() 14 printdir() <void printdir (int level,char *name) at d.c:42> (recursive: see 4) 15 closedir()
Actually, the exclusion sign (‘^’ or ‘-’) can be used any place in -i argument, not only at the beginning. Thus, option -i _^s means “include symbols, beginning with underscore and exclude static functions”. Several -i options accumulate, so the previous example can also be written as -i _ -i ^s.
It is important to notice that by default cflow graphs contain only functions. You can, however, request displaying variables as well, by using symbol class ‘x’. This class contains all data symbols, both global and static, so to include these in the output, use option -i x. For example:
$ cflow --number -i x d.c 1 main() <int main (int argc,char **argv) at d.c:85>: 2 fprintf() 3 stderr 4 max_level <int max_level at d.c:37> 5 atoi() 6 printdir() <void printdir (int level,char *name) at d.c:42> (R): 7 DIR 8 dir 9 getcwd() 10 perror() 11 chdir() 12 opendir() 13 readdir() 14 printf() 15 ignorent() <int ignorent (char *name) at d.c:28>: 16 ignored_names <char *ignored_names[] at d.c:24> 17 strcmp() 18 isdir() <int isdir (char *name) at d.c:12>: 19 stat() 20 perror() 21 S_ISDIR() 22 NULL 23 max_level <int max_level at d.c:37> 24 putchar() 25 printdir() <void printdir (int level,char *name) at d.c:42> (recursive: see 6) 26 closedir()
Now, lines 3, 4, 16 and 23 show data symbols, with their
definitions when available. Notice, however, lines 7 and 8. Why both
type name DIR
and automatic variable dir
are listed as
data?
To answer this question, let's first describe the cflow
notion of symbols. The program keeps its symbol tables, which
are initially filled with C
predefined keywords. When parsing
input files, cflow updates these tables. In particular, upon
encountering a typedef
, it registers the defined symbol as a
type.
Now, DIR
is not declared in d.c, so cflow
has no way of knowing it is a data type. So, it supposes it is a
variable. But then the input:
DIR *dir;
is parsed as an expression, meaning “multiply DIR
by
dir
”.
Of course, it is wrong. There are two ways to help
cflow out of this confusion. You can either explicitly
declare DIR
as data type, or let cflow run
preprocessor, so it sees the contents of the include files and
determines it by itself. Running preprocessor is covered by the next
chapter (see Preprocessing). In the present chapter we will
concentrate on the first method.
Command line option --symbol (-s) declares a type of the symbol. Its argument consists of two strings separated by a colon:
--symbol sym:t
The first string, sym is a C
identifier to be recorded in
the symbol table. The second string, t, specifies a type to
be associated with this symbol. If t is a string ‘type’,
the symbol sym will be recorded as a C
type
definition. Thus, to fix the above output, run:
$ cflow --number -i x --symbol DIR:type d.c
Another important symbol type is a parameter wrapper. It is
a kind of a macro, often used in sources that are meant to be
compatible with pre-ANSI compilers to protect parameter
declarations in function prototypes. For example, in the declaration
below, taken from /usr/include/resolv.h, __P
is a
parameter wrapper:
void res_npquery __P((const res_state, const u_char *, int, FILE *));
For cflow to be able to process such declarations,
declare __P
as a wrapper, for example:
cflow --symbol __P:wrapper *.c
Another usage for wrapper
symbol type is to declare
special attributes often used with gcc. For example,
the following declaration:
void fatal_exit (void) __attribute__ ((noreturn));
will confuse cflow. To correctly process it, use option --symbol __attribute__:wrapper.
For the complete list of --symbol supported types, See symbol types.
Notice, finally, that when using preprocess mode, there is no need to use --symbol, since in this mode cflow is able to correctly determine all symbol types by itself.
[1] Notice that -i -s
is a single option, in spite of -s
beginning with a minus sign.
Since this might be confusing, we prefer using ‘^’ instead of
‘-’ to denote symbol exclusion.