Index: /gs2-extensions/parallel-building/trunk/src/bin/script/generate_gantt.pl
===================================================================
--- /gs2-extensions/parallel-building/trunk/src/bin/script/generate_gantt.pl	(revision 27642)
+++ /gs2-extensions/parallel-building/trunk/src/bin/script/generate_gantt.pl	(revision 27643)
@@ -1,7 +1,10 @@
 #!/usr/bin/perl
 
+# Pragma
 use strict;
 use warnings;
-
+use 5.012; # so readdir assigns to $_ in a lone while test
+
+# Modules
 use Sort::Naturally;
 use POSIX qw(floor strftime);
@@ -9,187 +12,261 @@
 print "\n===== Generate Timing (GANTT) =====\n";
 
-# 0. Configuration
+# 0. Init
+# - configurables
+my $chart_width = 1600;
 my $debug = 0;
-my $import_dir;
-
-# 1. Initialization
-if (!defined $ARGV[0] || !-d $ARGV[0])
-{
-  &printUsage('Directory not provided or doesn\'t exist');
-}
-my $dir = $ARGV[0];
-my $timing_csv_path = &filenameCat($dir, 'timing.csv');
-if (!-e $timing_csv_path)
-{
-  &printUsage('Directory doesn\'t contain timing.csv: ' . $dir);
-}
-print 'Timing File: ' . $timing_csv_path . "\n";
-my $chart_width = 1024;
-if (defined $ARGV[1])
-{
-  if ($ARGV[1] !~ /^\d+$/)
-  {
-    &printUsage('Chart width not a number');
-  }
-  $chart_width = $ARGV[1];
+# - globals
+my $chart_count = 0;
+my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
+
+# 1. Parse options
+while (defined $ARGV[0] && $ARGV[0] =~ /^-/)
+{
+  my $option = shift(@ARGV);
+  if ($option eq '-debug')
+  {
+    $debug = 1;
+  }
+  elsif ($option eq '-width')
+  {
+    if (!defined $ARGV[0])
+    {
+      &printUsage('Error! No width value specified');
+    }
+    my $value = shift(@ARGV);
+    if ($value !~ /^\d+$/)
+    {
+      &printUsage('Error! Chart width not a number');
+    }
+    $chart_width = $value;
+  }
+  else
+  {
+    &printUsage('Error! Unknown option: ' . $option);
+  }
 }
 print "Chart Width: " . $chart_width . "px\n";
-
-my ($epoc) = $dir =~ /(\d+)$/;
-my $gantt_path = $dir . '/' . $epoc . '-gantt.html';
-print "Gantt Chart: " . $gantt_path . "\n";
-
+print "Debug? " . ($debug ? 'Yes' : 'No') . "\n";
 print "===================================\n\n";
 
-# Read in timing.csv and parse information into data structure
-my $timing_data = {};
-my $id_2_worker_id = {};
-if (open(TIN, '<:utf8', $timing_csv_path))
-{
-  my $line;
-  while ($line = <TIN>)
-  {
-    my @parts = split(/,/, $line);
-    if ($parts[1] eq 'M0')
-    {
-      $timing_data->{'M'} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4]};
-    }
-    elsif ($parts[1] =~ /W\d+/)
-    {
-      $timing_data->{$parts[1]} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4], 'F'=>{}};
-      $id_2_worker_id->{$parts[0]} = $parts[1];
-    }
-    elsif ($parts[1] =~ /T\d+/)
-    {
-      my $worker_id = $id_2_worker_id->{$parts[7]};
-      my $stop = $parts[4];
-      my $filepath = $parts[8];
-      $filepath =~ s/^\s+|\s+$//g;
-      $import_dir = &longestCommonPath($filepath, $import_dir);
-      $timing_data->{$worker_id}->{'F'}->{$parts[3]} = {'FN'=>$filepath, 'PS'=>($stop - $parts[5]), 'PE'=>$stop, 'E'=>$stop, 'DL'=>$parts[6]};
-    }
-  }
-  close(TIN);
-}
-else
-{
-  die('Error! Failed to open file for reading: ' . $timing_csv_path);
-}
-my $number_of_workers = scalar(keys(%{$id_2_worker_id}));;
-
-# 3. Produce pretty HTML chart of timing information including jobs
-print " * Generating timing information as HTML... ";
-open(HTMLOUT, '>:utf8', $gantt_path) or die('Error! Failed to open file for writing: gantt.html');
-print HTMLOUT "<html>\n";
-print HTMLOUT '<head>' . "\n";
-print HTMLOUT '<style type="text/css">' . "\n";
-print HTMLOUT 'div.thread {position:relative}' . "\n";
-print HTMLOUT 'div.master {border:1px solid gray;color:white;font-weight:bold}' . "\n";
-print HTMLOUT 'div.worker {border:1px solid black;background-color:green;color:white;font-weight:bold}' . "\n";
-print HTMLOUT 'div.time {font-size:smaller;font-weight:normal}' . "\n";
-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";
-print HTMLOUT 'span.process {z-index:-1;background-color:#C7C7C7;position:absolute}' . "\n";
-print HTMLOUT 'span.label {z-index:1;background-color:transparent;overflow:hidden;white-space:nowrap;}' . "\n";
-print HTMLOUT "th {text-align:left}\n";
-print HTMLOUT '</style>' . "\n";
-print HTMLOUT '</head>' . "\n";
-print HTMLOUT "<body>\n";
-print HTMLOUT "<h2>Statistics</h2>\n";
-print HTMLOUT "<table>\n";
-
-my $total_duration = $timing_data->{'M'}->{'E'} - $timing_data->{'M'}->{'S'};
-my $file_count = 0;
-my $data_locality = 0;
-my $total_io_time = 0;
-my $total_process_time = 0;
-my $fastest_file = 0;
-my $slowest_file = 0;
-my $problem_files = 0;
-foreach my $worker_id (keys %{$timing_data})
-{
-  if ($worker_id ne 'M')
-  {
-    foreach my $job_start ( keys %{$timing_data->{$worker_id}->{'F'}} )
-    {
-      my $process_start = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PS'};
-      my $process_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PE'};
-      my $job_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'E'};
-      if ($process_start == 0 || $process_end == 0 || $job_end == 0)
-      {
-        $problem_files++;
-      }
-      else
-      {
-        my $io_duration = ($process_start - $job_start) + ($job_end - $process_end);
-        my $process_duration = $process_end - $process_start;
-        my $total_duration = $io_duration + $process_duration;
-        &debugPrint("filename: " . $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'});
-        &debugPrint("start: $job_start ps: $process_start pe: $process_end end: $job_end");
-        &debugPrint("io: $io_duration process: $process_duration duration: $total_duration");
-        # Running stats
-        $total_io_time += $io_duration;
-        $total_process_time += $process_duration;
-        if ($fastest_file == 0 || $total_duration < $fastest_file)
+# 2. Search for valid directories (containing timing.csv)
+while (defined $ARGV[0])
+{
+  my $dir = shift(@ARGV);
+  if (!-d $dir)
+  {
+    &printUsage('Error! Not a directory: ' . $dir);
+  }
+  &searchForTimingCSV($dir);
+}
+
+# 3. Done
+print "Complete!\n\n";
+print "===================================\n";
+print 'Generated ' . $chart_count . " charts\n";
+print "===================================\n\n";
+exit;
+## main() ##
+
+
+## @function searchForTimingCSV()
+#
+sub searchForTimingCSV
+{
+  my $dir = shift(@_);
+  # For every directory where we find a timing.csv we generate a gantt chart
+  my $timing_path = &filenameCat($dir, 'timing.csv');
+  if (-e $timing_path)
+  {
+    &generateChart($dir, $timing_path);
+  }
+  # We also recursively search for other directories containing timing.csv's
+  opendir(my $dh, $dir) or &printError('Failed to open directory for reading: ' . $dir);
+  while (readdir($dh))
+  {
+    my $file = $_;
+    if ($file !~ /^\./)
+    {
+      my $path = &filenameCat($dir, $file);
+      if (-d $path)
+      {
+        &searchForTimingCSV($path);
+      }
+    }
+  }
+}
+## searchForTimingCSV() ##
+
+
+## @function generateChart()
+#
+sub generateChart
+{
+  my $dir = shift(@_);
+  my $timing_csv_path = shift(@_);
+  my $import_dir;
+  my ($epoc) = $dir =~ /(\d+)$/;
+  my $gantt_path = $dir . '/' . $epoc . '-gantt.html';
+
+  print ' * Generating chart for: ' . $dir . "\n";
+  print ' - timing file: ' . $timing_csv_path . "\n";
+  print ' - gantt chart: ' . $gantt_path . "\n";
+
+  # Read in timing.csv and parse information into data structure
+  print ' - parsing timing.csv... ';
+  my $timing_data = {};
+  my $id_2_worker_id = {};
+  if (open(TIN, '<:utf8', $timing_csv_path))
+  {
+    my $line;
+    while ($line = <TIN>)
+    {
+      my @parts = split(/,/, $line);
+      if ($parts[1] eq 'M0')
+      {
+        $timing_data->{'M'} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4]};
+      }
+      elsif ($parts[1] =~ /W\d+/)
+      {
+        $timing_data->{$parts[1]} = {'N'=>$parts[2], 'S'=>$parts[3], 'E'=>$parts[4], 'F'=>{}};
+        $id_2_worker_id->{$parts[0]} = $parts[1];
+      }
+      elsif ($parts[1] =~ /T\d+/)
+      {
+        my $worker_id = $id_2_worker_id->{$parts[7]};
+        my $stop = $parts[4];
+        my $filepath = $parts[8];
+        $filepath =~ s/^\s+|\s+$//g;
+        $import_dir = &longestCommonPath($filepath, $import_dir);
+        $timing_data->{$worker_id}->{'F'}->{$parts[3]} = {'FN'=>$filepath, 'S'=>$parts[3], 'PS'=>($stop - $parts[5]), 'PE'=>$stop, 'E'=>$stop, 'DL'=>$parts[6]};
+      }
+    }
+    close(TIN);
+  }
+  else
+  {
+    die('Error! Failed to open file for reading: ' . $timing_csv_path);
+  }
+  my $number_of_workers = scalar(keys(%{$id_2_worker_id}));;
+  print "Done\n";
+
+  # 3. Produce pretty HTML chart of timing information including jobs
+  print " - generating timing information as chart in HTML... ";
+  open(HTMLOUT, '>:utf8', $gantt_path) or die('Error! Failed to open file for writing: gantt.html');
+  print HTMLOUT "<html>\n";
+  print HTMLOUT '<head>' . "\n";
+  print HTMLOUT '<style type="text/css">' . "\n";
+  print HTMLOUT 'div.thread {position:relative}' . "\n";
+  print HTMLOUT 'div.master {border:1px solid gray;color:white;font-weight:bold}' . "\n";
+  print HTMLOUT 'div.worker {border:1px solid black;background-color:green;color:white;font-weight:bold}' . "\n";
+  print HTMLOUT 'div.time {font-size:smaller;font-weight:normal}' . "\n";
+  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";
+  print HTMLOUT 'span.process {z-index:-1;background-color:#C7C7C7;position:absolute}' . "\n";
+  print HTMLOUT 'span.label {z-index:1;background-color:transparent;overflow:hidden;white-space:nowrap;}' . "\n";
+  print HTMLOUT "th {text-align:left}\n";
+  print HTMLOUT '</style>' . "\n";
+  print HTMLOUT '</head>' . "\n";
+  print HTMLOUT "<body>\n";
+  print HTMLOUT "<h2>Statistics</h2>\n";
+  print HTMLOUT "<table>\n";
+
+  my $total_duration = $timing_data->{'M'}->{'E'} - $timing_data->{'M'}->{'S'};
+  my $file_count = 0;
+  my $data_locality = 0;
+  my $total_io_time = 0;
+  my $total_process_time = 0;
+  my $fastest_file = 0;
+  my $slowest_file = 0;
+  my $problem_files = 0;
+  foreach my $worker_id (keys %{$timing_data})
+  {
+    if ($worker_id ne 'M')
+    {
+      foreach my $job_start ( keys %{$timing_data->{$worker_id}->{'F'}} )
+      {
+        my $process_start = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PS'};
+        my $process_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'PE'};
+        my $job_end = $timing_data->{$worker_id}->{'F'}->{$job_start}->{'E'};
+        if ($process_start == 0 || $process_end == 0 || $job_end == 0)
         {
-          $fastest_file = $total_duration;
+          $problem_files++;
         }
-        if ($slowest_file == 0 || $total_duration > $slowest_file)
+        else
         {
-          $slowest_file = $total_duration;
+          my $io_duration = ($process_start - $job_start) + ($job_end - $process_end);
+          my $process_duration = $process_end - $process_start;
+          my $total_duration = $io_duration + $process_duration;
+          &debugPrint("filename: " . $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'});
+          &debugPrint("start: $job_start ps: $process_start pe: $process_end end: $job_end");
+          &debugPrint("io: $io_duration process: $process_duration duration: $total_duration");
+          # Running stats
+          $total_io_time += $io_duration;
+          $total_process_time += $process_duration;
+          if ($fastest_file == 0 || $total_duration < $fastest_file)
+          {
+            $fastest_file = $total_duration;
+          }
+          if ($slowest_file == 0 || $total_duration > $slowest_file)
+          {
+            $slowest_file = $total_duration;
+          }
         }
-      }
-      # Shorten filename
-      if (defined $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'})
-      {
-        $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'} = substr($timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}, length($import_dir) + 1);
-      }
-      $file_count++;
-      if ($timing_data->{$worker_id}->{'F'}->{$job_start}->{'DL'} == 1)
-      {
-        $data_locality++;
-      }
-    }
-  }
-}
-my $avg_processing_time = floor(($total_io_time + $total_process_time) / $file_count);
-my @months = ("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
-
-print HTMLOUT "<tr><th>Import Directory:</th><td>" . $import_dir . "</td></tr>\n";
-my ($sec, $min, $hour, $day, $month, $year) = (localtime($timing_data->{'M'}->{'S'}))[0,1,2,3,4,5];
-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";
-print HTMLOUT "<tr><th>Processing Time:</th><td>" . &renderTime($total_duration) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Processing Threads:</th><td>" . $number_of_workers . "</td></tr>\n";
-print HTMLOUT "<tr><th>Files Processed:</th><td>" . $file_count . "</td></tr>\n";
-if ($data_locality > 0)
-{
-  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";
-}
-print HTMLOUT "<tr><th>Serial Processing Time:</th><td>" . &renderTime($total_process_time) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Serial IO Time:</th><td>" . &renderTime($total_io_time) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Average File Processing Time:</th><td>" . &renderTime($avg_processing_time) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Fastest File:</th><td>" . &renderTime($fastest_file) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Slowest File:</th><td>" . &renderTime($slowest_file) . "</td></tr>\n";
-print HTMLOUT "<tr><th>Problem Files:</th><td>" . $problem_files . "</td></tr>\n";
-
-print HTMLOUT "</table>\n";
-print HTMLOUT "<hr />\n";
-print HTMLOUT "<h2>Timing Chart (Gannt)</h2>\n";
-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'}, {});
-foreach my $worker_id (nsort keys %{$timing_data})
-{
-  if ($worker_id ne 'M')
-  {
-    my $data = $timing_data->{$worker_id};
-    print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'worker', $worker_id . ' [' . $data->{'N'} . ']', $data->{'S'}, $data->{'E'}, $data->{'F'});
-  }
-}
-print HTMLOUT '<div>' . "\n";
-print HTMLOUT "</body>\n";
-print HTMLOUT "</html>";
-close(HTMLOUT);
-
-print "Done!\n";
-print "Complete!\n\n";
-exit;
+        # Shorten filename
+        if (defined $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'})
+        {
+          $timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'} = substr($timing_data->{$worker_id}->{'F'}->{$job_start}->{'FN'}, length($import_dir) + 1);
+        }
+        $file_count++;
+        if ($timing_data->{$worker_id}->{'F'}->{$job_start}->{'DL'} == 1)
+        {
+          $data_locality++;
+        }
+      }
+    }
+  }
+  my $avg_processing_time = floor(($total_io_time + $total_process_time) / $file_count);
+  my $avg_io_time = int(($total_io_time / $file_count) + 0.5);
+  my $avg_cpu_time = int(($total_process_time / $file_count) + 0.5);
+
+  print HTMLOUT "<tr><th>Import Directory:</th><td>" . $import_dir . "</td></tr>\n";
+  my ($sec, $min, $hour, $day, $month, $year) = (localtime($timing_data->{'M'}->{'S'}))[0,1,2,3,4,5];
+  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";
+  print HTMLOUT "<tr><th>Processing Time:</th><td>" . &renderTime($total_duration) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Processing Threads:</th><td>" . $number_of_workers . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Files Processed:</th><td>" . $file_count . "</td></tr>\n";
+  if ($data_locality > 0)
+  {
+    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";
+  }
+  print HTMLOUT "<tr><th>Serial Processing Time:</th><td>" . &renderTime($total_process_time) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Serial IO Time:</th><td>" . &renderTime($total_io_time) . "</td></tr>\n";
+  print HTMLOUT '<tr><th>IO Percentage:</th><td>' . sprintf('%d%%', (($total_io_time / $total_process_time) * 100)) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Average File Processing Time:</th><td>" . &renderTime($avg_processing_time) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Average File IO Time:</th><td>" . &renderTime($avg_io_time) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Average File CPU Time:</th><td>" . &renderTime($avg_cpu_time) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Fastest File:</th><td>" . &renderTime($fastest_file) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Slowest File:</th><td>" . &renderTime($slowest_file) . "</td></tr>\n";
+  print HTMLOUT "<tr><th>Problem Files:</th><td>" . $problem_files . "</td></tr>\n";
+
+  print HTMLOUT "</table>\n";
+  print HTMLOUT "<hr />\n";
+  print HTMLOUT "<h2>Timing Chart (Gannt)</h2>\n";
+  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'}, {});
+  foreach my $worker_id (nsort keys %{$timing_data})
+  {
+    if ($worker_id ne 'M')
+    {
+      my $data = $timing_data->{$worker_id};
+      print HTMLOUT renderLine($chart_width, $timing_data->{'M'}->{'S'}, $timing_data->{'M'}->{'E'}, 'worker', $worker_id . ' [' . $data->{'N'} . ']', $data->{'S'}, $data->{'E'}, $data->{'F'});
+    }
+  }
+  print HTMLOUT '<div>' . "\n";
+  print HTMLOUT "</body>\n";
+  print HTMLOUT "</html>";
+  close(HTMLOUT);
+  print "Done!\n\n";
+  $chart_count++;
+}
+## generateChart() ##
 
 
@@ -213,9 +290,18 @@
   my $path = join('/', @_);
   $path =~ s/[\/\\]+/\//g;
-  # protocols
-  $path =~ s/^(HDFS|HDFSShell|HDThriftFS):\//$1:\/\//;
   return $path;
 }
 ## filenameCat() ##
+
+
+## @function printError()
+#
+sub printError
+{
+  my $msg = shift(@_);
+  die('Error! ' . $msg . "\n\n");
+}
+## printError() ##
+
 
 ## @function printUsage()
@@ -228,5 +314,5 @@
     print 'Error! ' . $msg . "\n";
   }
-  die("Usage: generate_gantt.pl <results dir> [<width in pixels>]\n\n");
+  die("Usage: generate_gantt.pl [-width <width in pixels>] <dir> [<dir> ...]\n\n");
 }
 ## printUsage() ##
@@ -241,4 +327,7 @@
   if (defined $path_current)
   {
+    # Hide protocol before we split by slash
+    $path_new =~ s/:\/\//:/;
+    $path_current =~ s/:\/\//:/;
     my @path_new_parts = split(/\//, $path_new);
     my @path_current_parts = split(/\//, $path_current);
@@ -256,4 +345,6 @@
     }
     $result = &filenameCat(@path_parts);
+    # Restore protocol
+    $result =~ s/:/:\/\//;
   }
   else
@@ -312,4 +403,6 @@
     my $rjend = $jobs->{$jstart}->{'E'} - $start;
     my $jduration = $jobs->{$jstart}->{'E'} - $jstart;
+    my $io_duration = $rpstart - $rjstart;
+    my $cpu_duration = $rpend - $rpstart;
     # Scale Job co-ordinates
     my $jleft_percent = $rjstart / $duration;
@@ -331,5 +424,5 @@
       $html .= 'border:1px dashed black;';
     }
-    $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"';
+    $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"';
     if ($jobs->{$jstart}->{'DL'} != 1)
     {
@@ -365,12 +458,8 @@
     $time_str = sprintf('%dh%02dm%02ds', $hours, $minutes, $seconds);
   }
-  elsif ($minutes > 0)
+  else
   {
     $time_str = sprintf('%dm%02ds', $minutes, $seconds);
   }
-  else
-  {
-    $time_str = $seconds . 's';
-  }
   return $time_str;
 }
