Appendix - A Call-Graph Generator


Introduction

This appendix describes cgq (for call graph query), a shell script that generates who-calls-whom information. cgq differs from the other call-graph generator in three ways:
  1. cgi is a shell script; the other call-graph generator is an Icon program.
  2. cgi extracts its information from a realtional database containing the sbdump information; the other call-graph generator reads the sbdump files directly.
  3. cgi produces only the raw data for the call graph (that is, it produces the who-calls-whom list); the other call-graph generator produces dot code to draw the graph.

Implementation

<cgq>=

#!/bin/ksh

<shell boilerplate>
<process options>
<build the basic tables>
<filter the call-graph source nodes>
<filter the call-graph destination nodes>
<re-label the source call-graph nodes>
<re-label the destination call-graph nodes>
<write the call-graph data>

The analysis starts by constructing two tables: call_graph and containing_files. Both tables re-represent the information in the calling-information table in form more convenient for later analysis. Tbe call_graph table is the set of caller-called pairs; the containing_files table indicates which file contains a calling procedure. The shell variable ctable contains the name of the most recent version of the call-graph table; as analysis proceedes it will generate new versions of the call-graph table.

<build the basic tables>= (<-U)

ctable=call_graph

cat <<eot! | mysql mosaic
create table $ctable (
  caller varchar(40) not null,
  called varchar(40) not null
  );

insert into $ctable
  select distinct call_info.caller, call_info.calling
    from call_info
  ;

create table containing_files (
  file varchar(40) not null,
  proc varchar(40) not null
  );

insert into containing_files
  select distinct call_info.file, call_info.caller
    from call_info
  ;
eot!

If the cgq node-filter option -nf contains the string sf=pat, then only those procedures defined in files with names matching pat will be included in the call graph. For example, sf=%gui%, selects only those procedures defined in files with names matching %gui% (% is the SQL match-any metacharacter).

If a source-node filter was given, put the pattern in the shell variable sf, then construct a new call-graph table containing caller-called pairs in which the caller is defined in a file with a properly matching name.

<filter the call-graph source nodes>= (<-U)

sf=`expr "$nf" : '.*sf=\([^ ]*\)'`
if [ "$sf" ] ; then

otable=$ctable
ctable="${ctable}x"

cat <<eot! | mysql mosaic
create table $ctable (
  caller varchar(40) not null,
  called varchar(40) not null
  );

insert into $ctable
  select $otable.caller, $otable.called
    from containing_files, $otable
    where $otable.caller = containing_files.proc
    and   containing_files.file like '$sf'
  ;

drop table $otable;
eot!
fi

Do the same filtering on the destination nodes.

<filter the call-graph destination nodes>= (<-U)

df=`expr "$nf" : '.*df=\([^ ]*\)'`
if [ "$df" ] ; then
otable=$ctable
ctable="${ctable}x"
cat <<eot! | mysql mosaic
create table $ctable (
  caller varchar(40) not null,
  called varchar(40) not null
  );

insert into $ctable
  select $otable.caller, $otable.called
    from containing_files, $otable
    where $otable.called = containing_files.proc
    and   containing_files.file like '$df'
  ;

drop table $otable;
eot!
fi

A node in the call graph may be labeled in one of two ways: with a procedure name (the default) or with the name of the file containing the procedure. The -nl command-line option to cgq determines which label to use.

If source nodes should be labeled with a file name, create a new call-graph table containing the new labels.

<re-label the source call-graph nodes>= (<-U)

if [ `expr $nl : '^f'` = 1 ] ; then

otable=$ctable
ctable="${ctable}x"

cat <<eot! | mysql mosaic
create table $ctable (
  caller varchar(40) not null,
  called varchar(40) not null
  );

insert into $ctable
  select distinct containing_files.file, $otable.called
    from containing_files, $otable
    where $otable.caller = containing_files.proc
  ;

drop table $otable;
eot!
fi

Re-label the destiniation nodes too, if necessary.

<re-label the destination call-graph nodes>= (<-U)

if [ `expr $nl : '^.f'` = 2 ] ; then

otable=$ctable
ctable="${ctable}x"

cat <<eot! | mysql mosaic
create table $ctable (
  caller varchar(40) not null,
  called varchar(40) not null
  );

insert into $ctable
  select distinct $otable.caller, containing_files.file
    from containing_files, $otable
    where $otable.called = containing_files.proc
  ;

drop table $otable;
eot!
fi

Write the call-graph table to an external text file and delete the temporary tables.

<write the call-graph data>= (<-U)

ofile=call-graph
rm -f /tmp/mysql/mosaic/$ofile

cat <<eot! | mysql mosaic
select *
  from $ctable
  into outfile '$ofile'
  ;

drop table $ctable, containing_files;
eot!


The -nf options sets the node filter; the -nl option sets the node label.

<process options>= (<-U)

nl=pp
nf=''
while [ $# -gt 0 ]
do case $1 in
      -nf*)   x=`expr $1 : '-nf\(.*\)'`
              if [ ! "$x" ]
              then shift 1
                   x=$1
              fi
              [ "$x" ] || oops '-nf needs an argument'
              fi
              ;;

      -nl*)   x=`expr $1 : '-nl\(.*\)'`
              if [ ! "$x" ]
              then shift 1
                   x=$1
                   fi
              [ "$x" ] || oops '-nl needs an argument'
              nl=$x
              ;;

      *)      badcmd
              ;;
   esac
   shift 1
done

[ `expr "$nl" : '^[pf][pf]$'` -ne 0 ] || badcmd

Miscelanious functions and definitions.

<shell boilerplate>= (<-U)

function oops {
 
  # Print $1 to std-err and die.
 
  echo 1>&2 "$1."
  exit 1
  }
 
 
pgmname=`basename $0`

function badcmd {
 
  # Print a bad command message and die.
 
  oops "Command format is \"$pgmname [-nl arg] [-nf arg]\""
  }
 
alias mysql=/tmp/bin/mysql
alias mysqladmin=/tmp/bin/mysqladmin
alias mysqlshow=/tmp/bin/mysqlshow