Documentation for call_graph.pl

NAME

call_graph.pl

SYNOPSIS

Generates a call graph, including the worst case stack depth.

LIMITATIONS

Older compiler versions, when building with -g, prevent computing the stack depth. If you build with a compiler version >= than those shown here, then this limitation does NOT apply to you.

 Device Family         Minimum Compiler Version
 -------------         ------------------------
 ARM                   4.6.0
 C2000                 5.2.0
 C6000                 7.0.0
 MSP430                3.1.0

If you use an older compiler version, then none of the code can be built with the -g switch. If the code is built with -g, a call graph can still be built. But the stack depth information is inaccurate.

LIMITATIONS FOR C6000

LIMITATIONS FOR ARM (TMS470)

LIMITATIONS FOR C5500

USAGE

call_graph.pl [options] xml file

   ... OR ...

ofdXX -g -x file | perl call_graph.pl [options]

OPTIONS

 -h                   : Dump usage and quit
 --entry_point=<name> : Name root function for the call graph
 --i_cfg=<file>       : Configuration file for indirect function calls
 --verbose            : Enable warnings
 --func_info          : Show the file name and size for each function
 --stack_max          : Print the function which uses the most stack
 --cg_xml_version     : Print out the version of the cg_xml package in use

The module Getopt::Long is used to parse the options, thus you can substitute single letter versions of the options, i.e. -e for --entry_point. Except you can never abbreviate --cg_xml_version.

You can use --i_cfg multiple times to supply multiple configuration files.

C6000 specific ... To see a complete call graph starting at the application entry point, you may need to use -i=c60_rts_indirect.txt. Details below.

CREATING XML INPUT

OFD stands for Object File Display utility. Examples include ofd6x for C6000 and ofd55 for C5500.

Use the -x and -g options of OFD to create the XML file. Example ...

      ofd6x -xg -o=file.xml file.out

Alternatively, pipe the output from OFD into the script ...

      ofd6x -xg file.out | perl call_graph.pl

On Windows systems, piping the output from OFD tends to be slower; sometimes much slower.

The -g option is required. Otherwise, the XML does not contain the Dwarf debug information this script uses.

OFD OPTIONS

Recent releases of OFD support options for filtering the XML output down to what is strictly of interest. When processing a .out file, the best options to use in combination with this script are:

  -xg --xml_indent=0 --obj_display=none,header,optheader,symbols,battrs --dwarf_display=none,dinfo

When processing a library, symbols are not needed, so the best options to use are:

  -xg --xml_indent=0 --obj_display=none,header,optheader,battrs --dwarf_display=none,dinfo

Filtering the XML in this way reduces the amount of data processed by this script, thus making it run faster.

DESCRIPTION OF THE OUTPUT

The section presumes you are not using the --stack_max option.

MAIN CALL GRAPH

The first thing you see is the main call graph rooted at the entry point of the module. How the entry point is determined is described later. Here is an example ...

 Call Graph for main.obj
 **********************************************************************
 _main : wcs = 72
 |  _level1 : wcs = 64
 |  |  _level2 : wcs = 48

After each function name the worst case stack (wcs) depth is shown. That is the maximum amount stack memory used by that function and all the functions it calls. The values are in 8-bit bytes for C6000 and ARM, 16-bit words for C5500. As you can see, the child function(s) called by the parent are indented.

If you use the option --func_info, you additionally see the file name that supplies the function, and the size of the function.

 Call Graph for main.obj
 **********************************************************************
 _main : wcs = 72 : fn = main.c : sz = 32
 |  _level1 : wcs = 64 : fn = level.c : sz = 64
 |  |  _level2 : wcs = 48 : fn = level.c : sz = 64

Note the file name shown is not 100% reliable. In instances of function inlining, building with -pm, or linear assembly, you may see ??? instead of a file name, or an incorrect file name.

If the input file is a library, then there is no entry point and you do not see a main call graph.

REPEAT FUNCTION DISPLAY

For code like this:

 top_level()
 {
    first();
    second();
 }

 first()
 {
    big_tree();   // calls many functions
 }

 second()
 {
    big_tree();   // calls those same functions
 }

The output will look like this:

 top_level
 |  first
 |  |  big_tree
 |  |  |  many
 |  |  |  |  function
 |  |  |  |  calls
 |  |  |  appear
 |  |  |  |  here
 |  second
 |  |  big_tree
 |  |  |  <repeat ...>

All the functions that appear under "big_tree" are always the same, and always in the same order. Rather than print it out all again under "second", <repeat ...> appears instead. Printing it out again takes lots of space. For very large apps, this technique saves lots of execution time and output file space.

ORPHANED CALL GRAPHS

Followed by the main call graph are the orphaned call graphs. Functions which are never called directly are termed orphans because their parent cannot be found. There are two possible explanations for orphans. One, the function really is never called. Two, the function is called, but indirectly, and the parent that makes that indirect call is not noted in a configuration file described below. The full call graph of each orphan, including all the functions it calls, is shown.

Note interrupt functions are legitimate orphans.

FUNCTIONS THAT MAKE INDIRECT CALLS

Last, is a list of functions known to make indirect function calls, but which have no information in the configuration file described below.

To distinguish orphans and functions that make indirect calls, here is an example ...

 - Consider two functions _top_level and _next_level
 - _top_level is called directly from _main
 - _top_level makes an indirect call to _next_level
 - There are no other calls to _next_level
 - No configuration file about indirect calls is used

In that case, _top_level is listed as making an indirect call, and _next_level is an orphan.

WHEN GRAPHING A LIBRARY

When the input file is a library, all of the code in the library is examined and combined into a single call graph with many entry points. The entry points to the graph are all the functions which are not called directly. The file boundaries in the library are not considered. Suppose file1.c has a function named _level1, which calls a function in file2.c named _level2. Suppose further there are no other calls to _level1 or _level2. Then, _level1 is an entry point into the graph, and the corresponding graph will show the call to _level2. Neither function will appear elsewhere in the output.

Note it is possible that an entry point into the graph is called indirectly. That will occur when that indirect call is not listed in the configuration file provided with the i_cfg=file option.

FUNCTION SYMBOL ALIASING

One optimization performed by the compiler is called Function Symbol Aliasing. For this code ...

 int bbb(int arg1, char *arg2);

 int aaa(int n, char *str)
 {
    return bbb(n, str);
 }

The compiler makes aaa an alias of bbb. The symbols aaa and bbb have the same address.

This is represented in the call graph with ...

 |  |  main : wcs = 1356
 |  |  |  ( aaa
            bbb ) : wcs = 1356
 |  |  |  |  printf : wcs = 1348

And it appears in the orphan function listing ...

 one_orphan
 ( aaa bbb )
 another_orphan

If you use the --verbose option, a table of Function Symbol Aliases is output to stderr.

 Function Symbol Aliases
 -----------------------
 ( aaa bbb )
 ( level1 level2 )

Function symbol aliases are represented internally with a name like "aaa:bbb". Thus, you may see names of that form in diagnostics.

FUNCTION SYMBOL ALIASING AND CONFIGURATION FILES

Consider the example above with the functions aaa and bbb. Suppose your call_graph configuration file contains ...

   aaa : bbb

This indicates that the function aaa calls bbb. Now that call_graph combines aaa and bbb together in the graph, this is the same as writing ...

   (aaa bbb) : (aaa bbb)

The configuration entry, in effect, says that this function calls itself. In nearly all such cases, that is not true.

To find this situation, run call_graph with --verbose (or just -v) and look for ( aaa bbb ) in the Function Symbol Aliases table. That entry means aaa and bbb cannot be written to call each other in the configuration file.

DESCRIPTION OF --stack_max OUTPUT

Under the option --stack_max the output is only one line like this ...

 _name_of_function : wcs = 1172

The function name is the root of the call graph, whether the main call graph or an orphan call graph, that uses the most stack.

This option is intended to support automatically finding maximum stack usage. Care must be taken, however. There is no guarantee that the number output is, in all cases, the maximum amount of stack used. These two factors must be considered: the effect of possible indirect calls, and possible nesting of interrupts.

EFFECT OF INDIRECT CALLS

It is possible that some function in the maximum stack call graph makes an indirect call to some other function, thus increasing stack usage even more than what is reported. To know this is not occuring, you have to run without --stack_max, and insure that the last section of output is empty. The last section lists the functions known to make indirect calls but have no configuration file information about those calls. Note, further, the information in the configuration file(s) about those indirect calls must be complete and correct.

NESTING OF INTERRUPTS

If building the call graph for a library, this section can be ignored. When building the call graph for a complete application, you have to consider the possibility of nesting interrupts that use the same stack. In most applications, interrupts that could nest use separate stacks. But it is possible for nested interrupts to use the same stack. The case not addressed in the --stack_max output is the possiblity that while the functions in the maximum stack graph are executing, an interrupt is taken, that interrupt uses the same stack, and thus causes the stack to grow even more.

CANNOT COMBINE --stack_max AND --func_info

When the options combination --stack_max and --func_info is attempted, a warning message is issued, and --func_info is ignored.

ARM SUPPORT DETAILS

This table summarizes ARM support ...

                                      All
 ABI         |   ARM7   ARM9   ARM11  Cortex
 ------------+------------------------------
 TIABI       |   Y      NA     NA     NA
 TI_ARM9_ABI |   N      4.4.3  4.4.3  4.4.3
 EABI        |   N      4.4.3  4.4.3  4.4.3

First, a general issue which affects all ARM devices is described. The table entries are described next.

FALSE INDIRECT CALLS

Due to a bug in the compiler, this script may incorrectly display an indirect call in a function which makes no indirect function calls. The ClearQuest entry is SDSCM00020700. If you know you have no indirect calls in your code, then you can presume any indirect calls found are these false ones.

Y

Generally supported, even when builds are done with older toolsets.

N

Not supported. When the combination is detected, a diagnostic is issued. The script tries to execute anyway. But the results could be incorrect, and there is no easy way detect any errors.

Why? The compiler has a bug where an indirect call sequence is not encoded as a call in the Dwarf debug information. This means an indirect call is ignored by this script. The ClearQuest entry is SDSCM00020565.

If you know you have no indirect calls in your code, then you can ignore the warning.

NA

Not applicable. You cannot build code for TIABI while specifying any target other than ARM7. Note, however, you can execute ARM7/TIABI code on other ARM devices.

4.4.3

Supported only if all the code is built with tools version 4.4.3 or later. The script cannot detect the version of the tools used to do the build.

C64+ FUNCTION CALL OPTIMIZATION LIMITS OUTPUT

A code size optimization, performed when building for C64P with the build options -ms2 or -ms3, changes how some functions are called. The net effect is that a direct function call at source level is changed to an indirect function call in assembly. The Dwarf information used to build the call graph shows such a call as an indirect function call. Such a call will appear in the call graph similar to this ...

 _user_func1 : wcs = 0
 |  __local_call_stub : wcs = 0
 |  |  __call_stub : wcs = 0

In this case, _user_func1 does a direct call to _user_func2. The optimization, however, changes it to an indirect function call performed by the two functions _local_call_stub and __call_stub. In many cases, _user_func2 will show up later in the call graph output as an orphan.

CONFIGURATION FILE FOR INDIRECT FUNCTION CALLS

Supply information about what functions are called through indirection with configuration files. The option -i_cfg=file gives the name of the configuration file. You can use the option multiple times to supply, for instance, one configuration file for each library you use.

SYNTAX OF THE CONFIGURATION FILE

General syntax ...

   parent : child1 child2 child3 ...

Where the "parent" function may make one or more indirect function calls to the listed "child" functions. It must all appear on one line. Multiple lines that start with the same parent are OK. Comments begin with '#'. Blank lines are ignored. All other space is normalized.

The function names must be written as they appear in *assembly*, not C. For all targets except MSP430, when building with the default COFF ABI, this means the function name must begin with an '_'. For MSP430 or when building with EABI, the C function name and the assembly name is the same, which means a leading '_' is usually not present. At this writing, only the ARM and C6000 compilers provide support for EABI. Other TI compilers will add EABI support over time.

CONFIGURATION FILES FOR COMPILER RTS FUNCTIONS

The compiler runtime support library code makes indirect function calls. The configuration files ti_rts_indirect.txt, c60_rts_indirect.txt, arm_rts_indirect.txt, and c55_rts_indirect.txt supply information for these indirect function calls. The file ti_rts_indirect.txt supplies information that is common across those different RTS libs. For full correctness, it must be used in combination with a target specific file. For example ...

   call_graph.pl -i=ti_rts_indirect.txt -i=c60_rts_indirect.txt c60_app.xml

These files also serve as examples of how you can write your own such configuration files.

DIAGNOSTICS

These diagnostics are emitted only under the option --verbose.

If you name a parent function for which there is no information on the main source of the function (i.e. declarations don't count), then you get a warning message and the parent function, and all the children listed, are ignored.

If you name a child function for which there is similarly no information, you get a diagnostic. But, since this could legitimately occur in cases where you are processing only part of an application, or the routine is written in assembly, the child function is still entered in the call graph.

ASSEMBLY FUNCTIONS

The call graph is developed entirely from Dwarf debug information. The assembler will encode proper Dwarf debug information for an assembly function if you follow these steps.

 - Use the .asmfunc and .endasmfunc directives
 - Indicate stack usage with the stack_usage(<num>) argument
   to .asmfunc.  Specify the number of bytes for C6000 and ARM.  Specify
   the number of words for C5500.
 - For C6000, use the CALL and RET mnemonics instead of simple B

Here is a C6000 assembly example ...

 in_asm:     .asmfunc    stack_usage(16)     ; uses 16 bytes of stack
             ...
 loop:
             ...
             b     loop    ; normal branches need no special handling
             ...
             call  func    ; instead of b   func
             ...
             ret   b3      ; instead of b   b3
             .endasmfunc

RECURSION

Recursion is detected. It is stopped in the graph with the first function that begins the cycle of recursion. Under --verbose, you get a warning message about how the script cannot compute worst case stack depth in this case.

ENTRY POINT DETAILS

This section does not apply to libraries, but only to full application builds.

The script attempts to automatically find the entry point of the application, and root the call graph there. This doesn't always work.

In COFF files, the entry point is given in the optional file header. The optional file header is not always present. Simple object (.obj) files created by the assembler do not have an optional file header.

In C6000, there is an additional issue. In compiler version v7.2.0, there is a bug in the Dwarf information created by the compiler. The chain of calls from the entry point to main is: _c_int00 -> args_main -> main. Because of the bug, the Dwarf information about the call from args_main to main is not present. The bug is SDSCM00039952 in the ClearQuest system. There are entries in the configuration file c60_rts_indirect.txt to make up for this bug. Thus the option ...

  -i=c60_rts_indirect.txt

is a workaround for the problem.

PERL VERSION DETAILS

This script was written using Perl version 5.8.3. It may not work with earlier revisions of Perl.

 Documentation for call_graph.pl