source: trunk/SOURCE_TOOLS/profile_annotate.pl

Last change on this file was 18915, checked in by westram, 3 years ago
  • use uniform variable name 'cmd'.
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 5.9 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6# --------------------------------------------------------------------------------
7
8sub scanLine($) {
9  my ($line) = @_;
10  if ($line =~ /^[ ]*([0-9,]+) [ ]*/o) {
11    my ($num,$rest) = ($1,$');
12    $num =~ s/,//ig;
13    return ($num,$rest);
14  }
15  return (undef,undef);
16}
17
18sub prefix_percent($$$) {
19  my ($line,$total,$partial) = @_;
20  if ($partial>$total) { print "Invalid partial=$partial (total=$total)\n"; }
21  my $percent = int($partial/$total*1000+0.5)/10;
22  return sprintf("%5.1f",$percent).'% | '.$line;
23}
24
25sub percentize($$) {
26  my ($line,$totals) = @_;
27  my ($num,$rest) = scanLine($line);
28  if (defined $num) {
29    (prefix_percent($line,$totals,$num),$rest);
30  }
31  else {
32    ($line,$rest);
33  }
34}
35
36sub percentize_cluster(\@$) {
37  my ($cluster_r, $cluster_total) = @_;
38
39  eval {
40    if (defined $cluster_total) {
41      my @new = ();
42      foreach (@$cluster_r) {
43        if (/ \| /o) {
44          my ($prefix,$orgline) = ($`.$&, $');
45          my ($size,$rest) = scanLine($orgline);
46
47          push @new, prefix_percent($_,$cluster_total,$size);
48        }
49      }
50
51      @$cluster_r = @new;
52    }
53  };
54  if ($@) {
55    print "Error: $@\nat cluster:\n";
56    foreach (@$cluster_r) {
57      print "'$_'\n";
58    }
59  }
60}
61
62sub add_percentages(\@) {
63  my ($lines_r) = @_;
64
65  my @out       = ();
66  my $nr        = 0;
67  my $maxnr     = scalar(@$lines_r);
68  my $totals    = undef;
69  my $dummy;
70  my $separator = undef;
71
72  while (not defined $totals && $nr<$maxnr) {
73    my $line = $$lines_r[$nr++];
74    if (not defined($line)) {
75      print "Empty callgrind output?\n";
76      return;
77    }
78    push @out, $line;
79    if ($line =~ /PROGRAM TOTALS$/o) {
80      ($totals,$dummy) = scanLine($line);
81    }
82    elsif (not defined $separator) {
83      if ($line =~ /^-+$/o) { $separator = $line; }
84    }
85  }
86
87  if (not defined $totals) { die "Could not parse PROGRAM TOTALS"; }
88  if (not defined $separator) { die "No separator found"; }
89
90  my $last_line = pop @out;
91  ($last_line,$dummy) = percentize($last_line,$totals);
92  push @out, $last_line;
93  push @out, "";
94
95  my @rest          = ();
96  my @cluster       = ();
97  my $cluster_total = undef;
98  my $percentize_cluster = 1;
99
100  while ($nr<$maxnr) {
101    my $line = $$lines_r[$nr++];
102    my ($pline,$rest) = percentize($line,$totals);
103    if (defined $rest) { # percentage added
104      if ($rest =~ /^\*/o) {
105        push @out, $pline;
106        if ($pline =~ /\| /o) {
107          ($cluster_total,$dummy) = scanLine($');
108        }
109      }
110      push @cluster, $pline;
111    }
112    else {
113      if (scalar(@cluster)) {
114        if ($percentize_cluster==1) { percentize_cluster(@cluster,$cluster_total); }
115        push @rest, @cluster;
116        @cluster       = ();
117        $cluster_total = undef;
118      }
119
120      if ($line =~ /Auto-annotated source/o) { $percentize_cluster = 0; }
121      push @rest, $pline;
122    }
123  }
124
125  if (scalar(@cluster)) {
126    if ($percentize_cluster==1) { percentize_cluster(@cluster,$cluster_total); }
127    push @rest, @cluster;
128  }
129
130  @$lines_r = @out;
131  push @$lines_r, @rest;
132}
133
134# --------------------------------------------------------------------------------
135
136sub setModtime($$) {
137  my ($file,$modtime) = @_;
138  utime($modtime,$modtime,$file) || die "can't set modtime of '$file' (Reason: $!)";
139}
140
141sub getModtime($) {
142  my ($fileOrDir) = @_;
143  my $modtime = (stat($fileOrDir))[9];
144  return $modtime;
145}
146
147sub annotate_one($$$) {
148  my ($outfile,$force,$dir) = @_;
149  if (not -f $outfile) { die "No such file '$outfile'"; }
150  if (not $outfile =~ /^callgrind\.out\./o) {
151    die "Illegal name (expected 'callgrind.out.xxx' not '$outfile')";
152  }
153
154  my $annotated   = 'callgrind.annotated.'.$';
155  my $perform     = $force;
156  my $modtime_out = getModtime($outfile);
157
158  if (not -f $annotated) { $perform = 1; }
159  elsif ($modtime_out>getModtime($annotated)) { $perform = 1; }
160
161  if ($perform==1) {
162    print "* Updating $annotated\n";
163
164    my $cmd = "callgrind_annotate --tree=both --inclusive=yes ";
165    if (defined $dir) { $cmd .= '--auto=yes --include=./'.$dir.' '; }
166    $cmd .= $outfile;
167    my $line;
168    my @lines  = ();
169
170    open(CMD, $cmd.'|') || die "failed to fork '$cmd' (Reason: $!)";
171    while (defined ($line=<CMD>)) {
172      chomp($line);
173      push @lines, $line;
174    }
175    close(CMD) || die "failed to execute '$cmd' (Reason: $! exitcode=$?)";
176
177    add_percentages(@lines);
178
179    open(ANNO,'>'.$annotated) || die "can't write '$annotated' (Reason: $!)";
180    print ANNO "Command was '$cmd'\n\n";
181    foreach (@lines) {
182      print ANNO $_."\n";
183    }
184    close(ANNO);
185    # setModtime($annotated,$modtime_out+2);
186  }
187}
188
189sub annotate_all() {
190  opendir(DIR,".") || die "can't read directory '.' (Reason: $!)";
191  foreach (readdir(DIR)) {
192    if (/^callgrind\.out\./o) {
193      annotate_one($_,0, undef);
194    }
195    elsif (/^callgrind\.annotate\./o) {
196      my $out = 'callgrind.out.'.$';
197      if (not -f $out) {
198        print "* $out disappeared => remove $_\n";
199        unlink($_) || die "Can't unlink '$_' (Reason: $!)";
200      }
201    }
202  }
203  closedir(DIR);
204}
205
206# --------------------------------------------------------------------------------
207
208sub die_usage($) {
209  my ($err) = @_;
210
211  die("Usage: profile_annotate.pl  all | callgrind.out.xxx [DIR]\n".
212      "       Annotates all or one callgrind.out.xxx\n".
213      "       Annotations are written to callgrind.annotated.xxx\n".
214      "       If 'all' is specified, all callgrind.annotated.xxx files without source get deleted.\n".
215      "       If DIR is given it's used for auto source annotation.\n".
216      "Error: $err\n"
217       );
218}
219
220sub main() {
221  my $args = scalar(@ARGV);
222  if ($args<1 || $args>2) { die_usage "Wrong number of arguments"; }
223
224  my $arg = $ARGV[0];
225  if ($arg eq 'all') { annotate_all(); }
226  elsif (-f $arg) {
227    my $dir = undef;
228    if ($args==2) {
229      $dir = $ARGV[1];
230      if (not -d $dir) { die "No such directory '$dir'"; }
231    }
232    annotate_one($arg,1,$dir);
233  }
234  else { die_usage("No such file '$arg'"); }
235}
236
237main();
238
239
Note: See TracBrowser for help on using the repository browser.