Listtree.mcc: rename variables in Listtree_GetEntry to match the template of other...
[AROS.git] / tools / cxref / contrib / xrgr.pl
blobabf79f0804c81db899ab152700cc099031a01e6a
1 #! /usr/bin/perl -w
3 use strict;
4 use vars qw/$VERSION/;
5 BEGIN { $VERSION = '0.91' }
7 use Getopt::Long;
8 use Pod::Usage;
10 Getopt::Long::Configure ("bundling");
11 my %args;
12 GetOptions (\%args, 'h|?|help', 'V|version', 'd|debug',
13 'n|filename|file=s@',
14 'o|output=s',
15 'k|defined',
16 'v|verbose',
17 't|paper=s@',
18 'm|distance=s',
19 'l|label=s',
20 'c|cluster=s@',
21 'f|function|func=s@') or pod2usage(2);
23 pod2usage(-verbose => 2, -exitval => 1) if $args{'h'};
25 if ($args{'V'}) {
26 print STDERR "xrgr.pl Version $VERSION\n";
27 exit;
30 sub dumpargs {
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'};
43 #&dumpargs();
45 my %paper_sizes = (
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\";",
56 my %paper_ratios = (
57 'multi' => "ratio=auto;",
58 'single' => "ratio=fill;",
59 'auto' => "ratio=auto;",
60 'fill' => "ratio=fill;",
61 'compress' => "ratio=compress;",
64 my $paper_headers;
65 my $paper_size_set = 0;
66 my $paper_ratio_set = 0;
68 my ($papers, $pstring);
70 if ($args{'t'}) {
71 foreach $papers (@{$args{'t'}}) {
72 if (defined($pstring = $paper_sizes{$papers})) {
73 $paper_size_set = 1;
74 } elsif (defined($pstring = $paper_ratios{$papers})) {
75 $paper_ratio_set = 1;
76 } else {
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";
90 sub isDigits {
91 /\d+/ ? 1 : 0;
94 my $distance;
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') ;
98 my $verbose;
99 $verbose = 0 unless (defined($verbose = $args{'v'}));
102 local (*OUT);
103 if ($args{'o'}) {
104 open( OUT, ">$args{'o'}" ) or die ("Couldn't open output file $args{'o'}");
105 select(OUT);
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'};
119 my $xfilename;
120 local (*SRC);
122 if ($#ARGV < 0)
124 processlines(*STDIN, "stdin");
125 } else {
127 foreach $xfilename (@ARGV)
129 print STDERR "processing $xfilename\n";
130 if (!open(SRC, $xfilename)) {
131 print STDERR "Failed to open $xfilename : $!\n";
132 next;
134 processlines(*SRC, $xfilename);
135 close(SRC);
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
146 #local.
148 my %funcdef;
149 my %calls;
150 my %called;
151 my %tempcalled;
152 my %filedef;
153 my %simplefuncname;
154 my %globalmap;
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
165 sub processlines {
166 my ($infile, $fname) = @_;
167 my $lcount = 0;
168 my $calling;
169 my $simplename;
170 while(<$infile>) {
171 chomp;
172 $lcount++;
173 if (length($_) == 0) {
174 next;
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
185 $funcname = 'BODY';
187 $simplename = $funcname;
188 my $funcfilename = "$funcname" . "-$filename";
189 if ($scope < 2) {
190 $funcname .= "-$filename";
191 } else {
192 # global symbol
193 $globalmap{$funcname} = $funcfilename;
196 push(@{$simplefuncname{$simplename}}, $funcfilename);
198 $funcdef{$funcfilename} = $filename;
199 push (@{$filedef{$filename}}, $funcfilename);
200 foreach $calling (@slist) {
201 my $islocal = 0;
202 if ($calling =~ s/^%//) {
203 $islocal = 1;
205 if ($calling =~ s/^\&//) {
206 # a reference
208 if ($islocal) {
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";
220 sub fixfuncnames {
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);
226 if (!$filename) {
227 if (defined($fumap = $globalmap{$fukey})) {
228 $called{$fumap} = $tempcalled{$fukey};
229 } else {
230 $called{$fukey} = $tempcalled{$fukey};
232 } else {
233 $called{$fukey} = $tempcalled{$fukey};
236 foreach $fukey (keys(%calls)) {
237 foreach $furef (@{$calls{$fukey}}) {
238 my ($funcname, $filename) = split(/-/, $furef);
239 if (!$filename) {
240 if (defined($fumap = $globalmap{$furef})) {
241 $furef = $fumap;
248 fixfuncnames();
250 sub dumpxref {
251 my ($firef, $furef, $ref, $fulist);
252 foreach $firef (keys(%filedef)) {
253 my $lref = $filedef{$firef};
254 foreach $ref (@{$lref}) {
255 my $funcdname = $ref;
256 my $scope = 2;
257 my $junk = '';
258 ($funcdname, $junk) = split(/\-/, $ref);
259 if (defined($junk) && length($junk)) {
260 $scope = 1;
262 if ($funcdname eq 'BODY') {
263 $funcdname = '$';
264 $scope = 0;
266 print "$firef $funcdname $scope";
267 if (!defined($fulist = $calls{$ref})) {
268 print "\n";
269 next;
271 foreach $furef (@$fulist) {
272 my $funccname = $furef;
273 $junk = '';
274 $scope = 2;
275 ($funccname, $junk) = split(/\-/, $furef);
276 if (defined($junk) && length($junk)) {
277 $scope = 1;
278 $funccname = "\%$funccname";
280 print " $funccname";
282 print "\n";
287 my %seendnfun;
288 my %seenupfun;
289 my %seenfun;
291 sub seefun {
292 my ($fname) = @_;
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);
299 if ($verbose) {
300 print "\"$fname\" [label=\"$funcname\\n$filename\"];\n";
301 } else {
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;
313 seefun($fname);
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;
331 seefun($fname);
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);
344 sub dumpall {
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}) {
350 next;
352 $seendnfun{$furef} = 1;
353 my $funcdname;
354 my $junk = '';
355 if (!$args{'k'} || defined($filename = $funcdef{$furef})) {
356 # only interested in defined functions
357 if (!defined($fulist = $calls{$furef})) {
358 next;
360 foreach $fucref (@$fulist) {
361 if (!$args{'k'} || defined($filename = $funcdef{$fucref})) {
362 seefun($furef);
363 seefun($fucref);
364 print " \"$furef\" -> \"$fucref\";\n";
372 if ($args{'f'}) {
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);
394 } else {
396 &dumpall();
399 sub printcluster {
400 my ($cname) = @_;
401 my $first_elt = 1;
402 if (!defined(${filedef{$cname}})) {
403 return;
405 my $lref = $filedef{$cname};
406 my $furef;
407 foreach $furef (@{$lref}) {
408 if ($seenfun{$furef}) {
409 if ($first_elt) {
410 $first_elt = 0;
411 print "subgraph \"cluster_$cname\" { label=\"$cname\"\n";
413 print " \"$furef\";\n";
415 #my $funcdname;
416 #my $junk = '';
417 #($funcdname, $junk) = split(/\-/, $ref);
418 #print "$funcdname;\n";
420 if (!$first_elt) {
421 print "}\n";
425 # now, output any cluster definitions
426 if ($args{'c'}) {
427 my $cname;
428 foreach $cname (@{$args{'c'}}) {
429 if ($cname eq 'all') {
430 my $firef;
431 foreach $firef (keys(%filedef)) {
432 &printcluster($firef);
434 last;
435 } else {
436 &printcluster($cname);
441 print "}\n";
443 =head1 NAME
445 xrgr - cxref to graphviz processor
447 =head1 SYNOPSIS
449 B<xrgr> [B<-m> distance] [B<-c> cluster] [B<-n> filename] [B<-f> function] [filespec] [filespec...]
451 =head1 DESCRIPTION
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.
462 =head1 OPTIONS
464 =over 5
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
472 would be output.
474 Default value is B<all>, meaning B<all> possible called and calling functions
475 are output.
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
490 files as clusters.
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
521 multiple pages.
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.
543 =back
545 =head1 EXAMPLES
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.
557 =head1 INSTALLATION
559 Copy this script to a suitable place, e.g. /usr/local/bin or ~/bin.
561 =head1 COPYRIGHT
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.
568 =head1 AUTHOR
570 Jamie Honan
571 jhonan@optushome.com.au
573 =cut