5 BEGIN { $VERSION = '0.91' }
10 Getopt
::Long
::Configure
("bundling");
12 GetOptions
(\
%args, 'h|?|help', 'V|version', 'd|debug',
21 'f|function|func=s@') or pod2usage
(2);
23 pod2usage
(-verbose
=> 2, -exitval
=> 1) if $args{'h'};
26 print STDERR
"xrgr.pl Version $VERSION\n";
31 print "filenames " . join(' ', @
{$args{'n'}}) . "\n" if $args{'n'};
32 print "functions " . join(' ', @
{$args{'f'}}) . "\n" if $args{'f'};
33 print "cluster " . join(' ', @
{$args{'c'}}) . "\n" if $args{'c'};
34 print "paper $args{'t'}\n" if $args{'t'};
35 print "verbose $args{'v'}\n" if $args{'v'};
36 print "defined $args{'k'}\n" if $args{'k'};
37 print "distance $args{'m'}\n" if $args{'m'};
38 print "output $args{'o'}\n" if $args{'o'};
39 print "label $args{'l'}\n" if $args{'l'};
46 'a4' => "page=\"8.26,11.69\"; rotate=0; size=\"7.75,11.0\";",
47 'a4r' => "page=\"8.26,11.69\"; rotate=90;size=\"11.0,7.75\";",
48 'usr' => "page=\"8.5,11\"; rotate = 90; size = \"10.5,8.0\";",
49 'us' => "page=\"8.5,11\"; rotate=0; size=\"8.0,10.5\";" ,
50 'a1' => "page=\"594mm,841mm\"; rotate=0; size=\"575mm,820mm\";",
51 'a1r' => "page=\"594mm,841mm\"; rotate=90; size=\"820mm,575mm\";",
52 'a3' => "page=\"297mm,420mm\"; rotate=0; size=\"280mm,550mm\";",
53 'a3r' => "page=\"297mm,420mm\"; rotate=90; size=\"550mm,280mm\";",
57 'multi' => "ratio=auto;",
58 'single' => "ratio=fill;",
59 'auto' => "ratio=auto;",
60 'fill' => "ratio=fill;",
61 'compress' => "ratio=compress;",
65 my $paper_size_set = 0;
66 my $paper_ratio_set = 0;
68 my ($papers, $pstring);
71 foreach $papers (@
{$args{'t'}}) {
72 if (defined($pstring = $paper_sizes{$papers})) {
74 } elsif (defined($pstring = $paper_ratios{$papers})) {
77 die "Invalid -t paper type $papers";
79 $paper_headers .= " $pstring\n";
82 if (!$paper_size_set) {
83 $paper_headers .= " $paper_sizes{'a4r'}\n";
86 if (!$paper_ratio_set) {
87 $paper_headers .= " $paper_ratios{'single'}\n";
95 $distance = 'all' if (!defined($distance = $args{'m'}));
96 die "-m distance can only be a number or \'all\'" if (!($distance =~ /\d+/) && $distance ne 'all') ;
99 $verbose = 0 unless (defined($verbose = $args{'v'}));
104 open( OUT
, ">$args{'o'}" ) or die ("Couldn't open output file $args{'o'}");
108 print "digraph call {\n";
109 print " concentrate = true; remincross = true;\n";
110 print " $paper_headers";
111 print " fontname=\"helvetica\"\n";
112 print " fontsize = 12\n";
113 print " margin=\"0.25\"\n";
114 print " ranksep=\"0.5\"\n";
115 print " exact_ranksep=false\n";
116 print " center = 1\n";
117 print " label=\"$args{'l'}\"\n" if $args{'l'};
124 processlines
(*STDIN
, "stdin");
127 foreach $xfilename (@ARGV)
129 print STDERR
"processing $xfilename\n";
130 if (!open(SRC
, $xfilename)) {
131 print STDERR
"Failed to open $xfilename : $!\n";
134 processlines
(*SRC
, $xfilename);
140 #Format: filename funcname scope [[%][&]funcname1] [[%][&]funcname2] ...
141 #The function funcname in file filename calls or references functions
142 #funcname1, funcname2 ... ; those with a % are local, with a & are references.
144 #Format: filename $ 0 [[%]&funcname1] [[%]&funcname2] ...
145 #The file references functions funcname1, funcname2 ... ; those with a % are
156 # simplefuncname{simplefuncname} -> list of all functions with this name (could
157 # have multiple static definitions)
158 # funcdef{funcname} -> filename
159 # calls{funcname} -> list of called functions
160 # called{funcname} -> list of functions that call funcname directly
162 # filedef{filename} = list of functions defined in that file
163 # globalmap{globfun} = maps a global function name to a function-filename
166 my ($infile, $fname) = @_;
173 if (length($_) == 0) {
176 my @slist = split(/ /, $_);
177 if (scalar(@slist) < 3) {
178 die ("$fname has invalid line $lcount");
180 my ($filename) = shift(@slist);
181 my ($funcname) = shift(@slist);
182 my ($scope) = shift(@slist);
183 if ($funcname eq '$') {
184 next; # ignore for now
187 $simplename = $funcname;
188 my $funcfilename = "$funcname" . "-$filename";
190 $funcname .= "-$filename";
193 $globalmap{$funcname} = $funcfilename;
196 push(@
{$simplefuncname{$simplename}}, $funcfilename);
198 $funcdef{$funcfilename} = $filename;
199 push (@
{$filedef{$filename}}, $funcfilename);
200 foreach $calling (@slist) {
202 if ($calling =~ s/^%//) {
205 if ($calling =~ s/^\&//) {
209 $calling .= "-$filename";
211 push (@
{$calls{$funcfilename}}, $calling);
212 push (@
{$tempcalled{$calling}}, $funcfilename);
214 #print "$filename should eq $funcdef{$funcname}\n";
215 #print "function $funcname calling " . join(' ', @{$calls{$funcname}}) . "\n" if (defined($calls{$funcname}));
216 #print "calls " . join(' ', @{$tempcalled{$calling}}) . "\n";
221 # after reading in, the global names in called and calls must be fixed
222 # up to point to the correct file
223 my ($furef, $fukey, $fumap);
224 foreach $fukey (keys(%tempcalled)) {
225 my ($funcname, $filename) = split(/-/, $fukey);
227 if (defined($fumap = $globalmap{$fukey})) {
228 $called{$fumap} = $tempcalled{$fukey};
230 $called{$fukey} = $tempcalled{$fukey};
233 $called{$fukey} = $tempcalled{$fukey};
236 foreach $fukey (keys(%calls)) {
237 foreach $furef (@
{$calls{$fukey}}) {
238 my ($funcname, $filename) = split(/-/, $furef);
240 if (defined($fumap = $globalmap{$furef})) {
251 my ($firef, $furef, $ref, $fulist);
252 foreach $firef (keys(%filedef)) {
253 my $lref = $filedef{$firef};
254 foreach $ref (@
{$lref}) {
255 my $funcdname = $ref;
258 ($funcdname, $junk) = split(/\-/, $ref);
259 if (defined($junk) && length($junk)) {
262 if ($funcdname eq 'BODY') {
266 print "$firef $funcdname $scope";
267 if (!defined($fulist = $calls{$ref})) {
271 foreach $furef (@
$fulist) {
272 my $funccname = $furef;
275 ($funccname, $junk) = split(/\-/, $furef);
276 if (defined($junk) && length($junk)) {
278 $funccname = "\%$funccname";
293 my ($funcname, $junk, $filename);
294 if (!$seenfun{$fname}) {
295 $seenfun{$fname} = 1;
296 if (!$args{'k'} || defined($filename = $funcdef{$fname})) {
297 # only interested in defined functions
298 ($funcname, $junk) = split(/\-/, $fname);
300 print "\"$fname\" [label=\"$funcname\\n$filename\"];\n";
302 print "\"$fname\" [label=\"$funcname\"];\n";
308 sub tracednfunction
{
309 my ($fname, $recdepth) = @_;
310 my ($fulist, $funcname, $junk, $filename, $furef);
311 if (!$seendnfun{$fname}) {
312 $seendnfun{$fname} = 1;
314 if (($distance eq 'all' || $recdepth < $distance) &&
315 (defined($fulist = $calls{$fname}))) {
316 foreach $furef (@
$fulist) {
317 if (!$args{'k'} || defined($filename = $funcdef{$furef})) {
318 print " \"$fname\" -> \"$furef\";\n";
319 &tracednfunction
($furef, $recdepth+1);
326 sub traceupfunction
{
327 my ($fname, $recdepth) = @_;
328 my ($fulist, $funcname, $junk, $filename, $furef);
329 if (!$seenupfun{$fname}) {
330 $seenupfun{$fname} = 1;
332 if (($distance eq 'all' || $recdepth < $distance) &&
333 (defined($fulist = $called{$fname}))) {
334 foreach $furef (@
$fulist) {
335 if (!$args{'k'} || defined($filename = $funcdef{$furef})) {
336 print " \"$furef\" -> \"$fname\";\n";
337 &traceupfunction
($furef, $recdepth+1);
345 my ($firef, $furef, $fucref, $fulist, $filename );
346 foreach $firef (keys(%filedef)) {
347 my $lref = $filedef{$firef};
348 foreach $furef (@
{$lref}) {
349 if ($seendnfun{$furef}) {
352 $seendnfun{$furef} = 1;
355 if (!$args{'k'} || defined($filename = $funcdef{$furef})) {
356 # only interested in defined functions
357 if (!defined($fulist = $calls{$furef})) {
360 foreach $fucref (@
$fulist) {
361 if (!$args{'k'} || defined($filename = $funcdef{$fucref})) {
364 print " \"$furef\" -> \"$fucref\";\n";
373 # only interested in certain functions
374 my ($fusref, $funame, $furef);
375 foreach $funame (@
{$args{'f'}}) {
376 if (defined($fusref = $simplefuncname{$funame})) {
377 foreach $furef (@
$fusref) {
378 tracednfunction
($furef, 0);
379 traceupfunction
($furef, 0);
383 } elsif ($args{'n'}) {
384 # only interested in certain files
385 my ($fusref, $filename, $furef);
386 foreach $filename (@
{$args{'n'}}) {
387 if (defined($fusref = $filedef{$filename})) {
388 foreach $furef (@
$fusref) {
389 tracednfunction
($furef, 0);
390 traceupfunction
($furef, 0);
402 if (!defined(${filedef
{$cname}})) {
405 my $lref = $filedef{$cname};
407 foreach $furef (@
{$lref}) {
408 if ($seenfun{$furef}) {
411 print "subgraph \"cluster_$cname\" { label=\"$cname\"\n";
413 print " \"$furef\";\n";
417 #($funcdname, $junk) = split(/\-/, $ref);
418 #print "$funcdname;\n";
425 # now, output any cluster definitions
428 foreach $cname (@
{$args{'c'}}) {
429 if ($cname eq 'all') {
431 foreach $firef (keys(%filedef)) {
432 &printcluster
($firef);
436 &printcluster
($cname);
445 xrgr - cxref to graphviz processor
449 B<xrgr> [B<-m> distance] [B<-c> cluster] [B<-n> filename] [B<-f> function] [filespec] [filespec...]
453 Process entries produced by the cxref program from the cxref.function
454 file and produces a .dot file for use with the graphviz program 'dot'.
456 This can be used to produce a printable call tree graph diagram.
458 Graphviz can be obtained from http://www.research.att.com/sw/tools/graphviz/
460 Various options can be used to limit the view to a few functions or files.
466 =item B<-m> B<--distance> value
468 This controls the number of functions that will be printed out. This represents
469 the calling and called distance to and from the nominated function or
470 file. If the value B<1> was used, only the directly called functions,
471 and the functions that directly called the function or filename of interest
474 Default value is B<all>, meaning B<all> possible called and calling functions
477 =item B<-n> B<--filename> B<--file> filename
479 Specify the filename(s) of interest. Only functions contained in the
480 file(s) are to printed, together with any called and calling functions,
481 to a depth controlled by the B<-m> or B<--distance> switch.
483 Multiple B<-n> options can be used to specify multiple filenames.
485 =item B<-c> B<--cluster> filename
487 Output the functions in the nominated file(s) as belonging to a cluster.
489 The value B<all> can be used to place all functions in their respective
492 Multiple B<-c> options can be used to specify multiple filenames as clusters.
494 =item B<-f> B<--function> B<--func> function_name
496 Specify the function(s) of interest. Only these functions
497 are to printed, together with any called and calling functions,
498 to a depth controlled by the B<-m> or B<--distance> switch.
500 Multiple B<-f> options can be used to specify multiple functions.
502 =item B<-k> B<--defined>
504 Only functions that are defined in the group of files given to cxref
505 should be output. Otherwise all called
506 functions are included, e.g., stdio functions, etc.
508 =item B<-t> B<--paper> papersize
510 Set the paper size and orientation (a4r default), and whether multi page
511 or single page (single default);
513 Values are B<a4>, B<a4r>, B<us>, B<usr>, B<a1>, B<a1r>, B<a3>, B<a3r>
514 for page size and orientation.
516 B<single> for single page, B<multi> for multiple pages. You can
517 also use B<compress> to force the page to do a lot to fit, but you
518 probably wont be able to read the function names.
520 Use the B<-t> option twice if you wanted to specify a page size and
523 =item B<-v> B<--verbose>
525 Include the filename in the node when displaying the function name.
527 =item B<-l> B<--label> label
529 Specify the label to be printed with the graph.
531 =item B<-o> B<--output> filename
533 Specify the output filename rather than stdout.
535 =item B<-V> B<--version>
537 Version. Print out the current version of the script and exit.
539 =item B<-h> B<--help>
541 Help. Print out this help.
547 C<xrgr.pl -f main -o graph.out cxref.function>
549 Produces a graphviz dot file with every function calling or called by
550 B<main>, either directly or indirectly.
552 C<xrgr.pl -c main.c -o graph.out cxref.function>
554 Produce a graphviz dot file for every function. Cluster all the
555 functions in the module main.c into their own box.
559 Copy this script to a suitable place, e.g. /usr/local/bin or ~/bin.
563 Copyright (c) 2002 Jamie Honan.
565 This script is free software; you can redistribute it and/or modify it
566 under the same terms as Perl itself.
571 jhonan@optushome.com.au