Next: , Previous: Recursive Calls, Up: Top

6 Controlling Symbol Types

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.

The command line option --symbol (-s) declares a syntactic class of the symbol. Its argument consists of two strings separated by a colon:

       --symbol sym:class

The first string, sym is a C identifier to be recorded in the symbol table. The second string, class, specifies a class to be associated with this symbol. In particular, if class is ‘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

In both examples above the reason for using the --symbol option was that cflow was unable to determine what the given symbol was, either because it did not see the type definition, as it was in case with ‘DIR’, or because the macro definition was not expanded. Both cases are better solved by using preprocess mode, described in the next chapter. Nevertheless, even with preprocessor, the --symbol option remains useful, as shown in the following sections.


[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.