1#!/usr/bin/perl
2# Copyright (c) 2000, 2001, 2006 MySQL AB, 2009 Sun Microsystems, Inc.
3# Use is subject to license terms.
4#
5# This library is free software; you can redistribute it and/or
6# modify it under the terms of the GNU Library General Public
7# License as published by the Free Software Foundation; version 2
8# of the License.
9#
10# This library is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13# Library General Public License for more details.
14#
15# You should have received a copy of the GNU Library General Public
16# License along with this library; if not, write to the Free
17# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18# MA 02110-1301, USA
19#
20# a little program to generate a table of results
21# just read all the RUN-*.log files and format them nicely
22# Made by Luuk de Boer
23# Patched by Monty
24
25use Getopt::Long;
26
27$opt_server="mysql";
28$opt_dir="output";
29$opt_machine=$opt_cmp="";
30$opt_relative=$opt_same_server=$opt_help=$opt_Information=$opt_skip_count=$opt_no_bars=$opt_verbose=0;
31
32GetOptions("Information","help","server=s","cmp=s","machine=s","relative","same-server","dir=s","skip-count","no-bars","html","verbose") || usage();
33
34usage() if ($opt_help || $opt_Information);
35
36$opt_cmp=lc(join(",",sort(split(',',$opt_cmp))));
37
38if ($opt_same_server)
39{
40  $files="$opt_dir/RUN-$opt_server*$opt_machine";
41}
42else
43{
44  $files="$opt_dir/RUN-*$opt_machine";
45}
46$files.= "-cmp-$opt_cmp" if (length($opt_cmp));
47
48#
49# Go trough all RUN files and gather statistics.
50#
51
52if ($#ARGV == -1)
53{
54  @ARGV=glob($files);
55  $automatic_files=1;
56}
57
58foreach (@ARGV)
59{
60  next if (!$opt_cmp && /-cmp-/ && $automatic_files || defined($found{$_}));
61  $prog=$filename = $_;
62  $found{$_}=1;			# Remove dupplicates
63  /RUN-(.*)$/;
64  $tot{$prog}{'version'}=$1;
65  push(@key_order,$prog);
66  $next = 0;
67  open(TMP, "<$filename") || die "Can't open $filename: $!\n";
68  while (<TMP>)
69  {
70    chomp;
71    if ($next == 0) {
72      if (/Server version:\s+(\S+.*)/i)
73      {
74	$tot{$prog}{'server'} = $1;
75      }
76      elsif (/Arguments:\s+(.+)/i)
77      {
78	$arguments= $1;
79	# Remove some standard, not informative arguments
80	$arguments =~ s/--force|--log|--use-old\S*|--server=\S+|--cmp=\S+|--user=\S+|--pass=\S+|--machine=\S+|--dir=\S+//g;
81	if (($tmp=index($arguments,"--comment")) >= 0)
82	{
83	  if (($end=index($arguments,$tmp+2,"--")) >= 0)
84	  {
85	    substr($arguments,$tmp,($end-$tmp))="";
86	  }
87	  else
88	  {
89	    $arguments=substr($arguments,0,$tmp);
90	  }
91	}
92	$arguments =~ s/\s+/ /g;
93	$tot{$prog}{'arguments'}=$arguments;
94      }
95      elsif (/Comments:\s+(.+)/i) {
96	$tot{$prog}{'comments'} = $1;
97      } elsif (/^(\S+):.*(estimated\s|)total\stime:\s+([\d.]+)\s+(wallclock\s|)secs/i)
98      {
99	$tmp = $1; $tmp =~ s/://;
100	$tot{$prog}{$tmp} = [ $3, (length($2) ? "+" : "")];
101	$op1{$tmp} = $tmp;
102      } elsif (/Totals per operation:/i) {
103	$next = 1;
104	next;
105      }
106    }
107    elsif ($next == 1)
108    {
109      if (/^(\S+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*([+|?])*/)
110      {
111	$tot1{$prog}{$1} = [$2,$6,$7];
112	$op{$1} = $1;
113      }
114    }
115  }
116}
117
118if (!%op)
119{
120  print "Didn't find any files matching: '$files'\n";
121  print "Use the --cmp=server,server option to compare benchmarks\n";
122  exit 1;
123}
124
125
126# everything is loaded ...
127# now we have to create a fancy output :-)
128
129# I prefer to redirect scripts instead to force it to file ; Monty
130#
131# open(RES, ">$resultfile") || die "Can't write to $resultfile: $!\n";
132# select(RES)
133#
134
135if ($opt_html) {
136  html_output();
137} else {
138  ascii_output();
139}
140exit 0;
141
142#
143# some output + format functions;
144#
145
146sub ascii_output {
147  print <<EOF;
148This is the result file of the different benchmark tests.
149
150The number in () after each tests shows how many SQL commands the particular
151test did.  As one test may have many different parameters this gives only
152a rough picture of what was done.  Check the source for more information :)
153
154Keep in mind that one can\'t compare benchmarks run with different --cmp
155options. The --cmp options sets the all limits according to the worst
156limit for all server in the benchmark.
157
158Numbers marked with '+' are estimated according to previous runs because
159the query took longer than a given time-limit to finish. The estimation
160shouldn\'t be far from the real result thought.
161
162Numbers marked with '?' contains results that gave wrong result. This can only
163be used as an indication of how long it took for the server to produce a wrong
164result :)
165
166Numbers marked with '*' are tests that was run a different number times
167than the test in the first column.  The reason for this is normally that the
168marked test was run with different options that affects the number of tests
169or that the test was from another version of the MySQL benchmarks.
170
171Hope this will give you some idea how each db is performing at what thing ....
172Hope you like it .... Luuk & Monty (1997)
173
174EOF
175
176  if ($opt_relative)
177  {
178    print "Column 1 is in seconds. All other columns are presented relative\n";
179    print "to this. 1.00 is the same, bigger numbers indicates slower\n\n";
180  }
181
182  if ($opt_verbose)
183  {
184    print "The test values is printed in the format 'time:number of tests'\n";
185  }
186
187  if (length($opt_cmp))
188  {
189    print "The test was run with limits from: $opt_cmp\n\n";
190  }
191  print "The result logs which where found and the options:\n";
192  $bar= $opt_no_bars ? " " : "|";
193
194  # Move $opt_server first in array if not filename on command line
195  if ($automatic_files)
196  {
197    @key_order=sort {$a cmp $b} keys %tot;
198    for ($i=0; $i <= $#key_order; $i++)
199    {
200      if ($tot{$key_order[$i]}{'version'} =~ /^$opt_server-/)
201      {
202	unshift(@key_order,$key_order[$i]);
203	splice(@key_order,$i+1,1);
204	last;
205      }
206    }
207  }
208  # Print header
209
210  $column_count=0;
211  foreach $key (@key_order)
212  {
213    $tmp=$tmp=$tot{$key}{'version'};
214    $tmp =~ s/-cmp-$opt_cmp// if (length($opt_cmp));
215    $column_count++;
216    printf "%2d %-40.40s: %s %s\n", $column_count, $tmp,
217    $tot{$key}{'server'}, $tot{$key}{'arguments'};
218    print "  $tot{$key}{'comments'}\n"
219      if ($tot{$key}{'comments'} =~ /\w+/);
220  }
221
222  print "\n";
223
224  $namewidth=($opt_skip_count && !$opt_verbose) ? 29 : 36;
225  $colwidth= $opt_relative ? 10 : 7;
226  $count_width=7;
227  $colwidth+=$count_width if ($opt_verbose);
228
229  print_sep("=");
230  printf "%-$namewidth.${namewidth}s${bar}", "Operation";
231  $count = 1;
232  foreach $key (@key_order)
233  {
234    printf "%${colwidth}d${bar}", $count;
235    $count++;
236  }
237  printf "\n%-$namewidth.${namewidth}s${bar}", "";
238  foreach $key (@key_order)
239  {
240    $ver=$tot{$key}{'version'};
241    $ver =~ s/-[a-zA-Z0-9_\.]+-cmp-$opt-cmp$//;
242    printf "%${colwidth}.${colwidth}s${bar}", $ver;
243  }
244  print "\n";
245  print_sep("-");
246  print_string($opt_relative ? "Relative results per test (First column is in seconds):" : "Results per test in seconds:");
247  print_sep("-");
248
249  foreach $key (sort {$a cmp $b} keys %op1)
250  {
251    printf "%-$namewidth.${namewidth}s${bar}", $key;
252    $first=undef();
253    foreach $server (@key_order)
254    {
255      print_value($first,$tot{$server}{$key}->[0],undef(),$tot{$server}{$key}->[1]);
256      $first=$tot{$server}{$key}->[0] if (!defined($first));
257    }
258    print "\n";
259  }
260
261  print_sep("-");
262  print_string("The results per operation:");
263  print_sep("-");
264
265  foreach $key (sort {$a cmp $b} keys %op)
266  {
267    next if ($key =~ /TOTALS/i);
268    $tmp=$key;
269    $count=$tot1{$key_order[0]}{$key}->[1];
270    $tmp.= " (" . $count .  ")" if (!$skip_count);
271    printf "%-$namewidth.${namewidth}s${bar}", $tmp;
272    $first=undef();
273    foreach $server (@key_order)
274    {
275      $tmp= $count != $tot1{$server}{$key}->[1] ? "*" : "";
276      print_value($first,$tot1{$server}{$key}->[0],$tot1{$server}{$key}->[1],
277		  $tot1{$server}{$key}->[2] . $tmp);
278      $first=$tot1{$server}{$key}->[0] if (!defined($first));
279    }
280    print "\n";
281  }
282
283  print_sep("-");
284  $key="TOTALS";
285  printf "%-$namewidth.${namewidth}s${bar}", $key;
286  $first=undef();
287  foreach $server (@key_order)
288  {
289    print_value($first,$tot1{$server}{$key}->[0],undef(),
290		$tot1{$server}{$key}->[2]);
291    $first=$tot1{$server}{$key}->[0] if (!defined($first));
292  }
293  print "\n";
294  print_sep("=");
295}
296
297
298sub html_output
299{
300  my $template="template.html";
301  my $title="MySQL | | Information | Benchmarks | Compare with $opt_cmp";
302  my $image="info.gif";
303  $bar="";
304
305  open(TEMPLATE, $template) || die;
306  while (<TEMPLATE>)
307  {
308    if (/<center>/)
309    {
310      print $_;
311      print "<!---- This is AUTOMATICALLY Generated. Do not edit here! ---->\n";
312    }
313    elsif (/TITLE:SUBTITLE/)
314    {
315      s|TITLE:SUBTITLE|$title|;
316      print $_;
317    }
318    elsif (/TITLE:COMPARE/)
319    {
320      s|TITLE:COMPARE|$opt_cmp|;
321      print $_;
322    }
323    elsif (/ subchapter name /)
324    {
325      # Nothing here for now
326      print $_;
327    }
328    elsif (/ text of chapter /)
329    {
330      print $_;
331      print_html_body();
332    }
333    else
334    {
335      print $_;
336    }
337  }
338  close(TEMPLATE);
339}
340
341
342sub print_html_body
343{
344  my ($title,$count,$key);
345  print <<EOF;
346<center>
347<font size=+4><b>MySQL Benchmark Results</b></font><br>
348<font size=+1><b>Compare with $opt_cmp</b></font><p><p>
349</center>
350This is the result file of the different benchmark tests.
351<p>
352
353The number in () after each tests shows how many SQL commands the particular
354test did.  As one test may have many different parameters this gives only
355a rough picture of what was done.  Check the source for more information.
356<p>
357Keep in mind that one can\'t compare benchmarks run with different --cmp
358options. The --cmp options sets the all limits according to the worst
359limit for all server in the benchmark.
360<p>
361Numbers marked with '+' are estimated according to previous runs because
362the query took longer than a given time-limit to finish. The estimation
363shouldn\'t be far from the real result thought.
364<p>
365Numbers marked with '?' contains results that gave wrong result. This can only
366be used as an indication of how long it took for the server to produce a wrong
367result :)
368<p>
369Hope this will give you some idea how each db is performing at what thing ....
370<br>
371Hope you like it .... Luuk & Monty (1997)
372<p><p>
373EOF
374
375  if ($opt_relative)
376  {
377    print "Column 1 is in seconds. All other columns are presented relative<br>\n";
378    print "to this. 1.00 is the same, bigger numbers indicates slower<p>\n\n";
379  }
380
381  if (length($opt_cmp))
382  {
383    print "The test was run with limits from: $opt_cmp\n\n";
384  }
385  print "The result logs which where found and the options:<br>\n";
386
387  # Move $opt_server first in array
388  if ($automatic_files)
389  {
390    @key_order=sort {$a cmp $b} keys %tot;
391    for ($i=0; $i <= $#key_order; $i++)
392    {
393      if ($tot{$key_order[$i]}{'version'} =~ /^$opt_server-/)
394      {
395	unshift(@key_order,$key_order[$i]);
396	splice(@key_order,$i+1,1);
397	last;
398      }
399    }
400  }
401  # Print header
402  print "<p><center><table border=1 width=100%>\n";
403  $column_count=0;
404  foreach $key (@key_order)
405  {
406    $tmp=$tot{$key}{'version'};
407    $tmp =~ s/-cmp-$opt_cmp// if (length($opt_cmp));
408    $column_count++;
409#    printf "<tr><td>%2d<td>%-36.36s<td>%s %s</tr>\n", $column_count, $tmp,
410    printf "<tr><td>%2d</td><td>%s</td><td>%s %s</td></tr>\n",
411    $column_count, $tmp, $tot{$key}{'server'}, $tot{$key}{'arguments'};
412    print "<tr><td colspan=3>$tot{$key}{'comments'}</td></tr>\n"
413      if ($tot{$key}{'comments'} =~ /\w+/);
414  }
415
416  print "</table></center><p><center><table border=1 width=100%>\n";
417
418  $namewidth=$opt_skip_count ? 22 :29;
419  $colwidth= $opt_relative ? 10 : 7;
420  $count_width=7;
421
422  printf "<tr><td><b>%s</b></td>\n", "Operation";
423  $count = 1;
424  foreach $key (@key_order)
425  {
426    $ver=$tot{$key}{'version'};
427    printf "<td align=center><b>%d", $count;
428    printf "<br>%${colwidth}.${colwidth}s</b></td>\n", substr($ver,0,index($ver,"-"));
429    $count++;
430  }
431  print "</tr>\n";
432  $title = $opt_relative ? "Relative results per test (First column is in seconds):" : "Results per test in seconds:";
433  printf "<tr><td colspan=%d><b>%s</b></td></tr>\n", $count, $title;
434
435  foreach $key (sort {$a cmp $b} keys %op1)
436  {
437    if (!$opt_html)
438    {
439      printf "<tr><td>%-$namewidth.${namewidth}s</td>", $key;
440    }
441    else
442    {
443      print "<tr><td>$key</td>";
444    }
445    $first=undef();
446    foreach $server (@key_order)
447    {
448      print_value($first,$tot{$server}{$key}->[0],undef(),
449		  $tot{$server}{$key}->[1]);
450      $first=$tot{$server}{$key}->[0] if (!defined($first));
451    }
452    print "</tr>\n";
453  }
454
455  $title = "The results per operation:";
456  printf "<tr><td colspan=%d><b>%s</b></td></tr>\n", $count, $title;
457
458  foreach $key (sort {$a cmp $b} keys %op)
459  {
460    next if ($key =~ /TOTALS/i);
461    $tmp=$key;
462    $tmp.= " (" . $tot1{$key_order[0]}{$key}->[1] . ")" if (!$skip_count);
463    if (!$opt_html)
464    {
465      printf "<tr><td>%-$namewidth.${namewidth}s</td>", $tmp;
466    }
467    else
468    {
469      print "<tr><td>$tmp</td>";
470    }
471    $first=undef();
472    foreach $server (@key_order)
473    {
474      print_value($first,$tot1{$server}{$key}->[0],
475		  $tot1{$server}{$key}->[1],
476		  $tot1{$server}{$key}->[2]);
477      $first=$tot1{$server}{$key}->[0] if (!defined($first));
478    }
479    print "</tr>\n";
480  }
481
482  $key="TOTALS";
483  printf "<tr><td><b>%-$namewidth.${namewidth}s</b></td>", $key;
484  $first=undef();
485  foreach $server (@key_order)
486  {
487    print_value($first,$tot1{$server}{$key}->[0],undef(),
488		$tot1{$server}{$key}->[2]);
489    $first=$tot1{$server}{$key}->[0] if (!defined($first));
490  }
491  print "</tr>\n</table>\n";
492}
493
494
495sub print_sep
496{
497  my ($sep)=@_;
498  print $sep x ($namewidth + (($colwidth+1) * $column_count)+1),"\n";
499}
500
501
502sub print_value
503{
504  my ($first,$value,$count,$flags)=@_;
505  my ($tmp,$width);
506
507  if (defined($value))
508  {
509    if (!defined($first) || !$opt_relative)
510    {
511      $tmp=sprintf("%.2f",$value);
512    }
513    else
514    {
515      $first=1 if ($first == 0); # Assume that it took one second instead of 0
516      $tmp= sprintf("%.2f",$value/$first);
517    }
518    if (defined($flags))
519    {
520      $tmp="+".$tmp if ($flags =~ /\+/);
521      $tmp="?".$tmp if ($flags =~ /\?/);
522      $tmp="*".$tmp if ($flags =~ /\*/);
523    }
524  }
525  else
526  {
527    $tmp="";
528  }
529  $width= ($opt_verbose ? $colwidth - $count_width : $colwidth);
530  if (!$opt_html)
531  {
532    $tmp= " " x ($width-length($tmp)) . $tmp if (length($tmp) < $width);
533  }
534  if ($opt_verbose)
535  {
536    if ($count)
537    {
538      $tmp.= ":" . " " x ($count_width-1-length($count)) . $count;
539    }
540    else
541    {
542      $tmp.= " " x ($count_width);
543    }
544  }
545
546  if (!$opt_html) {
547    print $tmp . "${bar}";
548  } else {
549    print "<td align=right>$tmp</td>";
550  }
551}
552
553
554sub print_string
555{
556  my ($str)=@_;
557  if (!$opt_html)
558  {
559    my ($width);
560    $width=$namewidth + ($colwidth+1)*$column_count;
561    $str=substr($str,1,$width) if (length($str) > $width);
562    print($str," " x ($width - length($str)),"${bar}\n");
563  }
564  else
565  {
566    print $str,"\n";
567  }
568}
569
570
571sub usage
572{
573    print <<EOF;
574$0  Ver 1.2
575
576This program parses all RUN files from old 'run-all-tests --log' scripts
577and makes a nice comparable table.
578
579$0 takes currently the following options:
580
581--help or --Information		
582  Shows this help
583
584--cmp=server,server,server (Default $opt_cmp)
585Compares all runs that are done with the same --cmp options to run-all-tests.
586The most normal options are '--cmp=mysql,pg,solid' and '--cmp ""'
587
588--dir=...  (Default $opt_dir)
589From which directory one should get the runs.  All runs made by
590run-all-tests --log is saved in the 'output' directory.
591In the 'results' directory you may have some example runs from different
592databases.
593
594--html
595  Print the table in html format.
596
597--machine='full-machine-name' (Default $opt_machine)
598Use only runs that match this machine.
599
600--relative
601Show all numbers in times of the first server where the time for the
602first server is 1.0
603
604--same-server
605Compare all runs for --server=....  The --machine is not used in this case
606This is nice to compare how the same server runs on different machines.
607
608--server='server name'  (Default $opt_server)
609Put this server in the first result column.
610
611--skip-count
612Do not write the number of tests after the test-name.
613
614--verbose
615Write the number of tests in each column. This is useful when some column
616is marked with '*'.
617EOF
618
619  exit(0);
620}
621