Changeset 27643

Show
Ignore:
Timestamp:
18.06.2013 10:30:13 (6 years ago)
Author:
jmt12
Message:

Changed the script generator so it can recurse through directories and generate several charts at once. Added a few more tidbits of information, such as average IO time per file

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • gs2-extensions/parallel-building/trunk/src/bin/script/generate_gantt.pl

    r27590 r27643  
    11#!/usr/bin/perl 
    22 
     3# Pragma 
    34use strict; 
    45use warnings; 
    5  
     6use 5.012; # so readdir assigns to $_ in a lone while test 
     7 
     8# Modules 
    69use Sort::Naturally; 
    710use POSIX qw(floor strftime); 
     
    912print "\n===== Generate Timing (GANTT) =====\n"; 
    1013 
    11 # 0. Configuration 
     14# 0. Init 
     15# - configurables 
     16my $chart_width = 1600; 
    1217my $debug = 0; 
    13 my $import_dir; 
    14  
    15 # 1. Initialization 
    16 if (!defined $ARGV[0] || !-d $ARGV[0]) 
    17 { 
    18   &printUsage('Directory not provided or doesn\'t exist'); 
    19 } 
    20 my $dir = $ARGV[0]; 
    21 my $timing_csv_path = &filenameCat($dir, 'timing.csv'); 
    22 if (!-e $timing_csv_path) 
    23 { 
    24   &printUsage('Directory doesn\'t contain timing.csv: ' . $dir); 
    25 } 
    26 print 'Timing File: ' . $timing_csv_path . "\n"; 
    27 my $chart_width = 1024; 
    28 if (defined $ARGV[1]) 
    29 { 
    30   if ($ARGV[1] !~ /^\d+$/) 
    31   { 
    32     &printUsage('Chart width not a number'); 
    33   } 
    34   $chart_width = $ARGV[1]; 
     18# - globals 
     19my $chart_count = 0; 
     20my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); 
     21 
     22# 1. Parse options 
     23while (defined $ARGV[0] && $ARGV[0] =~ /^-/) 
     24{ 
     25  my $option = shift(@ARGV); 
     26  if ($option eq '-debug') 
     27  { 
     28    $debug = 1; 
     29  } 
     30  elsif ($option eq '-width') 
     31  { 
     32    if (!defined $ARGV[0]) 
     33    { 
     34      &printUsage('Error! No width value specified'); 
     35    } 
     36    my $value = shift(@ARGV); 
     37    if ($value !~ /^\d+$/) 
     38    { 
     39      &printUsage('Error! Chart width not a number'); 
     40    } 
     41    $chart_width = $value; 
     42  } 
     43  else 
     44  { 
     45    &printUsage('Error! Unknown option: ' . $option); 
     46  } 
    3547} 
    3648print "Chart Width: " . $chart_width . "px\n"; 
    37  
    38 my ($epoc) = $dir =~ /(\d+)$/; 
    39 my $gantt_path = $dir . '/' . $epoc . '-gantt.html'; 
    40 print "Gantt Chart: " . $gantt_path . "\n"; 
    41  
     49print "Debug? " . ($debug ? 'Yes' : 'No') . "\n"; 
    4250print "===================================\n\n"; 
    4351 
    44 # Read in timing.csv and parse information into data structure 
    45 my $timing_data = {}; 
    46 my $id_2_worker_id = {}; 
    47 if (open(TIN, '<:utf8', $timing_csv_path)) 
    48 { 
    49   my $line; 
    50   while ($line = <TIN>) 
    51   { 
    52     my @parts = split(/,/, $line); 
    53     if ($parts[1] eq 'M0') 
    54     { 
    55       $timing_data->{'M'} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4]}; 
    56     } 
    57     elsif ($parts[1] =~ /W\d+/) 
    58     { 
    59       $timing_data->{$parts[1]} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4], 'F'=>{}}; 
    60       $id_2_worker_id->{$parts[0]} = $parts[1]; 
    61     } 
    62     elsif ($parts[1] =~ /T\d+/) 
    63     { 
    64       my $worker_id = $id_2_worker_id->{$parts[7]}; 
    65       my $stop = $parts[4]; 
    66       my $filepath = $parts[8]; 
    67       $filepath =~ s/^\s+|\s+$//g; 
    68       $import_dir = &longestCommonPath($filepath, $import_dir); 
    69       $timing_data->{$worker_id}->{'F'}->{$parts[3]} = {'FN'=>$filepath, 'PS'=>($stop - $parts[5]), 'PE'=>$stop, 'E'=>$stop, 'DL'=>$parts[6]}; 
    70     } 
    71   } 
    72   close(TIN); 
    73 } 
    74 else 
    75 { 
    76   die('Error! Failed to open file for reading: ' . $timing_csv_path); 
    77 } 
    78 my $number_of_workers = scalar(keys(%{$id_2_worker_id}));; 
    79  
    80 # 3. Produce pretty HTML chart of timing information including jobs 
    81 print " * Generating timing information as HTML... "; 
    82 open(HTMLOUT, '>:utf8', $gantt_path) or die('Error! Failed to open file for writing: gantt.html'); 
    83 print HTMLOUT "<html>\n"; 
    84 print HTMLOUT '<head>' . "\n"; 
    85 print HTMLOUT '<style type="text/css">' . "\n"; 
    86 print HTMLOUT 'div.thread {position:relative}' . "\n"; 
    87 print HTMLOUT 'div.master {border:1px solid gray;color:white;font-weight:bold}' . "\n"; 
    88 print HTMLOUT 'div.worker {border:1px solid black;background-color:green;color:white;font-weight:bold}' . "\n"; 
    89 print HTMLOUT 'div.time {font-size:smaller;font-weight:normal}' . "\n"; 
    90 print HTMLOUT 'div.job {background-color:transparent;color:black;border:1px solid black;display:block;font-size:smaller;position:relative;text-align:center;overflow:hidden}' . "\n"; 
    91 print HTMLOUT 'span.process {z-index:-1;background-color:#C7C7C7;position:absolute}' . "\n"; 
    92 print HTMLOUT 'span.label {z-index:1;background-color:transparent;overflow:hidden;white-space:nowrap;}' . "\n"; 
    93 print HTMLOUT "th {text-align:left}\n"; 
    94 print HTMLOUT '</style>' . "\n"; 
    95 print HTMLOUT '</head>' . "\n"; 
    96 print HTMLOUT "<body>\n"; 
    97 print HTMLOUT "<h2>Statistics</h2>\n"; 
    98 print HTMLOUT "<table>\n"; 
    99  
    100 my $total_duration = $timing_data->{'M'}->{'E'} - $timing_data->{'M'}->{'S'}; 
    101 my $file_count = 0; 
    102 my $data_locality = 0; 
    103 my $total_io_time = 0; 
    104 my $total_process_time = 0; 
    105 my $fastest_file = 0; 
    106 my $slowest_file = 0; 
    107 my $problem_files = 0; 
    108 foreach my $worker_id (keys %{$timing_data}) 
    109 { 
    110   if ($worker_id ne 'M') 
    111   { 
    112     foreach my $job_start ( keys %{$timing_data->{$worker_id}->{'F'}} ) 
    113     { 
    114       my $process_start = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PS'}; 
    115       my $process_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PE'}; 
    116       my $job_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'E'}; 
    117       if ($process_start == 0 || $process_end == 0 || $job_end == 0) 
    118       { 
    119         $problem_files++; 
    120       } 
    121       else 
    122       { 
    123         my $io_duration = ($process_start - $job_start) + ($job_end - $process_end); 
    124         my $process_duration = $process_end - $process_start; 
    125         my $total_duration = $io_duration + $process_duration; 
    126         &debugPrint("filename: " . $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}); 
    127         &debugPrint("start: $job_start ps: $process_start pe: $process_end end: $job_end"); 
    128         &debugPrint("io: $io_duration process: $process_duration duration: $total_duration"); 
    129         # Running stats 
    130         $total_io_time += $io_duration; 
    131         $total_process_time += $process_duration; 
    132         if ($fastest_file == 0 || $total_duration < $fastest_file) 
     52# 2. Search for valid directories (containing timing.csv) 
     53while (defined $ARGV[0]) 
     54{ 
     55  my $dir = shift(@ARGV); 
     56  if (!-d $dir) 
     57  { 
     58    &printUsage('Error! Not a directory: ' . $dir); 
     59  } 
     60  &searchForTimingCSV($dir); 
     61} 
     62 
     63# 3. Done 
     64print "Complete!\n\n"; 
     65print "===================================\n"; 
     66print 'Generated ' . $chart_count . " charts\n"; 
     67print "===================================\n\n"; 
     68exit; 
     69## main() ## 
     70 
     71 
     72## @function searchForTimingCSV() 
     73# 
     74sub searchForTimingCSV 
     75{ 
     76  my $dir = shift(@_); 
     77  # For every directory where we find a timing.csv we generate a gantt chart 
     78  my $timing_path = &filenameCat($dir, 'timing.csv'); 
     79  if (-e $timing_path) 
     80  { 
     81    &generateChart($dir, $timing_path); 
     82  } 
     83  # We also recursively search for other directories containing timing.csv's 
     84  opendir(my $dh, $dir) or &printError('Failed to open directory for reading: ' . $dir); 
     85  while (readdir($dh)) 
     86  { 
     87    my $file = $_; 
     88    if ($file !~ /^\./) 
     89    { 
     90      my $path = &filenameCat($dir, $file); 
     91      if (-d $path) 
     92      { 
     93        &searchForTimingCSV($path); 
     94      } 
     95    } 
     96  } 
     97} 
     98## searchForTimingCSV() ## 
     99 
     100 
     101## @function generateChart() 
     102# 
     103sub generateChart 
     104{ 
     105  my $dir = shift(@_); 
     106  my $timing_csv_path = shift(@_); 
     107  my $import_dir; 
     108  my ($epoc) = $dir =~ /(\d+)$/; 
     109  my $gantt_path = $dir . '/' . $epoc . '-gantt.html'; 
     110 
     111  print ' * Generating chart for: ' . $dir . "\n"; 
     112  print ' - timing file: ' . $timing_csv_path . "\n"; 
     113  print ' - gantt chart: ' . $gantt_path . "\n"; 
     114 
     115  # Read in timing.csv and parse information into data structure 
     116  print ' - parsing timing.csv... '; 
     117  my $timing_data = {}; 
     118  my $id_2_worker_id = {}; 
     119  if (open(TIN, '<:utf8', $timing_csv_path)) 
     120  { 
     121    my $line; 
     122    while ($line = <TIN>) 
     123    { 
     124      my @parts = split(/,/, $line); 
     125      if ($parts[1] eq 'M0') 
     126      { 
     127        $timing_data->{'M'} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4]}; 
     128      } 
     129      elsif ($parts[1] =~ /W\d+/) 
     130      { 
     131        $timing_data->{$parts[1]} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4], 'F'=>{}}; 
     132        $id_2_worker_id->{$parts[0]} = $parts[1]; 
     133      } 
     134      elsif ($parts[1] =~ /T\d+/) 
     135      { 
     136        my $worker_id = $id_2_worker_id->{$parts[7]}; 
     137        my $stop = $parts[4]; 
     138        my $filepath = $parts[8]; 
     139        $filepath =~ s/^\s+|\s+$//g; 
     140        $import_dir = &longestCommonPath($filepath, $import_dir); 
     141        $timing_data->{$worker_id}->{'F'}->{$parts[3]} = {'FN'=>$filepath, 'S'=>$parts[3], 'PS'=>($stop - $parts[5]), 'PE'=>$stop, 'E'=>$stop, 'DL'=>$parts[6]}; 
     142      } 
     143    } 
     144    close(TIN); 
     145  } 
     146  else 
     147  { 
     148    die('Error! Failed to open file for reading: ' . $timing_csv_path); 
     149  } 
     150  my $number_of_workers = scalar(keys(%{$id_2_worker_id}));; 
     151  print "Done\n"; 
     152 
     153  # 3. Produce pretty HTML chart of timing information including jobs 
     154  print " - generating timing information as chart in HTML... "; 
     155  open(HTMLOUT, '>:utf8', $gantt_path) or die('Error! Failed to open file for writing: gantt.html'); 
     156  print HTMLOUT "<html>\n"; 
     157  print HTMLOUT '<head>' . "\n"; 
     158  print HTMLOUT '<style type="text/css">' . "\n"; 
     159  print HTMLOUT 'div.thread {position:relative}' . "\n"; 
     160  print HTMLOUT 'div.master {border:1px solid gray;color:white;font-weight:bold}' . "\n"; 
     161  print HTMLOUT 'div.worker {border:1px solid black;background-color:green;color:white;font-weight:bold}' . "\n"; 
     162  print HTMLOUT 'div.time {font-size:smaller;font-weight:normal}' . "\n"; 
     163  print HTMLOUT 'div.job {background-color:transparent;color:black;border:1px solid black;display:block;font-size:smaller;position:relative;text-align:center;overflow:hidden}' . "\n"; 
     164  print HTMLOUT 'span.process {z-index:-1;background-color:#C7C7C7;position:absolute}' . "\n"; 
     165  print HTMLOUT 'span.label {z-index:1;background-color:transparent;overflow:hidden;white-space:nowrap;}' . "\n"; 
     166  print HTMLOUT "th {text-align:left}\n"; 
     167  print HTMLOUT '</style>' . "\n"; 
     168  print HTMLOUT '</head>' . "\n"; 
     169  print HTMLOUT "<body>\n"; 
     170  print HTMLOUT "<h2>Statistics</h2>\n"; 
     171  print HTMLOUT "<table>\n"; 
     172 
     173  my $total_duration = $timing_data->{'M'}->{'E'} - $timing_data->{'M'}->{'S'}; 
     174  my $file_count = 0; 
     175  my $data_locality = 0; 
     176  my $total_io_time = 0; 
     177  my $total_process_time = 0; 
     178  my $fastest_file = 0; 
     179  my $slowest_file = 0; 
     180  my $problem_files = 0; 
     181  foreach my $worker_id (keys %{$timing_data}) 
     182  { 
     183    if ($worker_id ne 'M') 
     184    { 
     185      foreach my $job_start ( keys %{$timing_data->{$worker_id}->{'F'}} ) 
     186      { 
     187        my $process_start = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PS'}; 
     188        my $process_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PE'}; 
     189        my $job_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'E'}; 
     190        if ($process_start == 0 || $process_end == 0 || $job_end == 0) 
    133191        { 
    134           $fastest_file = $total_duration; 
     192          $problem_files++; 
    135193        } 
    136         if ($slowest_file == 0 || $total_duration > $slowest_file) 
     194        else 
    137195        { 
    138           $slowest_file = $total_duration; 
     196          my $io_duration = ($process_start - $job_start) + ($job_end - $process_end); 
     197          my $process_duration = $process_end - $process_start; 
     198          my $total_duration = $io_duration + $process_duration; 
     199          &debugPrint("filename: " . $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}); 
     200          &debugPrint("start: $job_start ps: $process_start pe: $process_end end: $job_end"); 
     201          &debugPrint("io: $io_duration process: $process_duration duration: $total_duration"); 
     202          # Running stats 
     203          $total_io_time += $io_duration; 
     204          $total_process_time += $process_duration; 
     205          if ($fastest_file == 0 || $total_duration < $fastest_file) 
     206          { 
     207            $fastest_file = $total_duration; 
     208          } 
     209          if ($slowest_file == 0 || $total_duration > $slowest_file) 
     210          { 
     211            $slowest_file = $total_duration; 
     212          } 
    139213        } 
    140       } 
    141       # Shorten filename 
    142       if (defined $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}) 
    143       { 
    144         $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'} = substr($timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}, length($import_dir) + 1); 
    145       } 
    146       $file_count++; 
    147       if ($timing_data->{$worker_id}->{'F'}->{$job_start}->{'DL'} == 1) 
    148       { 
    149         $data_locality++; 
    150       } 
    151     } 
    152   } 
    153 } 
    154 my $avg_processing_time = floor(($total_io_time + $total_process_time) / $file_count); 
    155 my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"); 
    156  
    157 print HTMLOUT "<tr><th>Import Directory:</th><td>" . $import_dir . "</td></tr>\n"; 
    158 my ($sec, $min, $hour, $day, $month, $year) = (localtime($timing_data->{'M'}->{'S'}))[0,1,2,3,4,5]; 
    159 print HTMLOUT "<tr><th>Start Time:</th><td>" . sprintf('%04d%s%02d %02d:%02d:%02d', ($year+1900), $months[$month], $day, $hour, $min, $sec) . "</td></tr>\n"; 
    160 print HTMLOUT "<tr><th>Processing Time:</th><td>" . &renderTime($total_duration) . "</td></tr>\n"; 
    161 print HTMLOUT "<tr><th>Processing Threads:</th><td>" . $number_of_workers . "</td></tr>\n"; 
    162 print HTMLOUT "<tr><th>Files Processed:</th><td>" . $file_count . "</td></tr>\n"; 
    163 if ($data_locality > 0) 
    164 { 
    165   print HTMLOUT "<tr><th>Data Locality:</th><td>" . sprintf('%d%% [%d out of %d]', (($data_locality / $file_count) * 100), $data_locality, $file_count) . "</td></tr>\n"; 
    166 } 
    167 print HTMLOUT "<tr><th>Serial Processing Time:</th><td>" . &renderTime($total_process_time) . "</td></tr>\n"; 
    168 print HTMLOUT "<tr><th>Serial IO Time:</th><td>" . &renderTime($total_io_time) . "</td></tr>\n"; 
    169 print HTMLOUT "<tr><th>Average File Processing Time:</th><td>" . &renderTime($avg_processing_time) . "</td></tr>\n"; 
    170 print HTMLOUT "<tr><th>Fastest File:</th><td>" . &renderTime($fastest_file) . "</td></tr>\n"; 
    171 print HTMLOUT "<tr><th>Slowest File:</th><td>" . &renderTime($slowest_file) . "</td></tr>\n"; 
    172 print HTMLOUT "<tr><th>Problem Files:</th><td>" . $problem_files . "</td></tr>\n"; 
    173  
    174 print HTMLOUT "</table>\n"; 
    175 print HTMLOUT "<hr />\n"; 
    176 print HTMLOUT "<h2>Timing Chart (Gannt)</h2>\n"; 
    177 print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'master', $timing_data->{'M'}->{'N'}, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, {}); 
    178 foreach my $worker_id (nsort keys %{$timing_data}) 
    179 { 
    180   if ($worker_id ne 'M') 
    181   { 
    182     my $data = $timing_data->{$worker_id}; 
    183     print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'worker', $worker_id . ' [' . $data->{'N'} . ']', $data->{'S'}, $data->{'E'}, $data->{'F'}); 
    184   } 
    185 } 
    186 print HTMLOUT '<div>' . "\n"; 
    187 print HTMLOUT "</body>\n"; 
    188 print HTMLOUT "</html>"; 
    189 close(HTMLOUT); 
    190  
    191 print "Done!\n"; 
    192 print "Complete!\n\n"; 
    193 exit; 
     214        # Shorten filename 
     215        if (defined $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}) 
     216        { 
     217          $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'} = substr($timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}, length($import_dir) + 1); 
     218        } 
     219        $file_count++; 
     220        if ($timing_data->{$worker_id}->{'F'}->{$job_start}->{'DL'} == 1) 
     221        { 
     222          $data_locality++; 
     223        } 
     224      } 
     225    } 
     226  } 
     227  my $avg_processing_time = floor(($total_io_time + $total_process_time) / $file_count); 
     228  my $avg_io_time = int(($total_io_time / $file_count) + 0.5); 
     229  my $avg_cpu_time = int(($total_process_time / $file_count) + 0.5); 
     230 
     231  print HTMLOUT "<tr><th>Import Directory:</th><td>" . $import_dir . "</td></tr>\n"; 
     232  my ($sec, $min, $hour, $day, $month, $year) = (localtime($timing_data->{'M'}->{'S'}))[0,1,2,3,4,5]; 
     233  print HTMLOUT "<tr><th>Start Time:</th><td>" . sprintf('%04d%s%02d %02d:%02d:%02d', ($year+1900), $months[$month], $day, $hour, $min, $sec) . "</td></tr>\n"; 
     234  print HTMLOUT "<tr><th>Processing Time:</th><td>" . &renderTime($total_duration) . "</td></tr>\n"; 
     235  print HTMLOUT "<tr><th>Processing Threads:</th><td>" . $number_of_workers . "</td></tr>\n"; 
     236  print HTMLOUT "<tr><th>Files Processed:</th><td>" . $file_count . "</td></tr>\n"; 
     237  if ($data_locality > 0) 
     238  { 
     239    print HTMLOUT "<tr><th>Data Locality:</th><td>" . sprintf('%d%% [%d out of %d]', (($data_locality / $file_count) * 100), $data_locality, $file_count) . "</td></tr>\n"; 
     240  } 
     241  print HTMLOUT "<tr><th>Serial Processing Time:</th><td>" . &renderTime($total_process_time) . "</td></tr>\n"; 
     242  print HTMLOUT "<tr><th>Serial IO Time:</th><td>" . &renderTime($total_io_time) . "</td></tr>\n"; 
     243  print HTMLOUT '<tr><th>IO Percentage:</th><td>' . sprintf('%d%%', (($total_io_time / $total_process_time) * 100)) . "</td></tr>\n"; 
     244  print HTMLOUT "<tr><th>Average File Processing Time:</th><td>" . &renderTime($avg_processing_time) . "</td></tr>\n"; 
     245  print HTMLOUT "<tr><th>Average File IO Time:</th><td>" . &renderTime($avg_io_time) . "</td></tr>\n"; 
     246  print HTMLOUT "<tr><th>Average File CPU Time:</th><td>" . &renderTime($avg_cpu_time) . "</td></tr>\n"; 
     247  print HTMLOUT "<tr><th>Fastest File:</th><td>" . &renderTime($fastest_file) . "</td></tr>\n"; 
     248  print HTMLOUT "<tr><th>Slowest File:</th><td>" . &renderTime($slowest_file) . "</td></tr>\n"; 
     249  print HTMLOUT "<tr><th>Problem Files:</th><td>" . $problem_files . "</td></tr>\n"; 
     250 
     251  print HTMLOUT "</table>\n"; 
     252  print HTMLOUT "<hr />\n"; 
     253  print HTMLOUT "<h2>Timing Chart (Gannt)</h2>\n"; 
     254  print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'master', $timing_data->{'M'}->{'N'}, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, {}); 
     255  foreach my $worker_id (nsort keys %{$timing_data}) 
     256  { 
     257    if ($worker_id ne 'M') 
     258    { 
     259      my $data = $timing_data->{$worker_id}; 
     260      print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'worker', $worker_id . ' [' . $data->{'N'} . ']', $data->{'S'}, $data->{'E'}, $data->{'F'}); 
     261    } 
     262  } 
     263  print HTMLOUT '<div>' . "\n"; 
     264  print HTMLOUT "</body>\n"; 
     265  print HTMLOUT "</html>"; 
     266  close(HTMLOUT); 
     267  print "Done!\n\n"; 
     268  $chart_count++; 
     269} 
     270## generateChart() ## 
    194271 
    195272 
     
    213290  my $path = join('/', @_); 
    214291  $path =~ s/[\/\\]+/\//g; 
    215   # protocols 
    216   $path =~ s/^(HDFS|HDFSShell|HDThriftFS):\//$1:\/\//; 
    217292  return $path; 
    218293} 
    219294## filenameCat() ## 
     295 
     296 
     297## @function printError() 
     298# 
     299sub printError 
     300{ 
     301  my $msg = shift(@_); 
     302  die('Error! ' . $msg . "\n\n"); 
     303} 
     304## printError() ## 
     305 
    220306 
    221307## @function printUsage() 
     
    228314    print 'Error! ' . $msg . "\n"; 
    229315  } 
    230   die("Usage: generate_gantt.pl <results dir> [<width in pixels>]\n\n"); 
     316  die("Usage: generate_gantt.pl [-width <width in pixels>] <dir> [<dir> ...]\n\n"); 
    231317} 
    232318## printUsage() ## 
     
    241327  if (defined $path_current) 
    242328  { 
     329    # Hide protocol before we split by slash 
     330    $path_new =~ s/:\/\//:/; 
     331    $path_current =~ s/:\/\//:/; 
    243332    my @path_new_parts = split(/\//, $path_new); 
    244333    my @path_current_parts = split(/\//, $path_current); 
     
    256345    } 
    257346    $result = &filenameCat(@path_parts); 
     347    # Restore protocol 
     348    $result =~ s/:/:\/\//; 
    258349  } 
    259350  else 
     
    312403    my $rjend = $jobs->{$jstart}->{'E'} - $start; 
    313404    my $jduration = $jobs->{$jstart}->{'E'} - $jstart; 
     405    my $io_duration = $rpstart - $rjstart; 
     406    my $cpu_duration = $rpend - $rpstart; 
    314407    # Scale Job co-ordinates 
    315408    my $jleft_percent = $rjstart / $duration; 
     
    331424      $html .= 'border:1px dashed black;'; 
    332425    } 
    333     $html .= '" title="FN:' . $jobs->{$jstart}->{'FN'} . ', S:' . renderTime($rjstart) . ', E:' . renderTime($rjend) . ', CPU: ' . $cpu_percent . '%"><span class="process" style="left:' . $rpleft . 'px;width:' . $rpwidth . 'px">&nbsp;</span><span class="label"'; 
     426    $html .= '" title="FN:' . $jobs->{$jstart}->{'FN'} . ', S:' . &renderTime($rjstart) . ', E:' . &renderTime($rjend) . ', CPU: ' . $cpu_percent . '% [' . &renderTime($io_duration) . ', ' . &renderTime($cpu_duration) . ']"><span class="process" style="left:' . $rpleft . 'px;width:' . $rpwidth . 'px">&nbsp;</span><span class="label"'; 
    334427    if ($jobs->{$jstart}->{'DL'} != 1) 
    335428    { 
     
    365458    $time_str = sprintf('%dh%02dm%02ds', $hours, $minutes, $seconds); 
    366459  } 
    367   elsif ($minutes > 0) 
     460  else 
    368461  { 
    369462    $time_str = sprintf('%dm%02ds', $minutes, $seconds); 
    370463  } 
    371   else 
    372   { 
    373     $time_str = $seconds . 's'; 
    374   } 
    375464  return $time_str; 
    376465}