".
#------------------------------------------------------------------------------
if ($input_line =~ /$header_name_regex/)
{
$scan_header = $TRUE;
}
elsif ($input_line =~ /$total_marker_regex/)
{
$scan_header = $FALSE;
$scan_function_data = $TRUE;
}
if ($scan_header)
{
#------------------------------------------------------------------------------
# This group is only defined for the first line of the header and $4 contains
# the remaining part of the line after "Name", without the leading spaces.
#------------------------------------------------------------------------------
if (defined ($4))
{
$remaining_part_header = $4;
$msg = "remaining_part_header = $remaining_part_header";
gp_message ("debugXL", $subr_name, $msg);
#------------------------------------------------------------------------------
# Determine the maximum length of the header. This needs to be done before
# the HTML controls are added.
#------------------------------------------------------------------------------
my $header_length = length ($remaining_part_header);
$max_header_length = max ($max_header_length, $header_length);
#------------------------------------------------------------------------------
# TBD Should change this and not yet include html in header_lines
#------------------------------------------------------------------------------
$html_line = "" . $remaining_part_header . "";
push (@header_lines, $html_line);
gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
gp_message ("debugXL", $subr_name, "html_line = $html_line");
}
#------------------------------------------------------------------------------
# Captures the subsequent header lines. Assume they exist.
#------------------------------------------------------------------------------
elsif ($input_line =~ /$catch_all_regex/)
{
$header_line = $1;
gp_message ("debugXL", $subr_name, "header_line = $header_line");
my $header_length = length ($header_line);
$max_header_length = max ($max_header_length, $header_length);
#------------------------------------------------------------------------------
# TBD Should change this and not yet include html in header_lines
#------------------------------------------------------------------------------
$html_line = "" . $header_line . "";
push (@header_lines, $html_line);
gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
gp_message ("debugXL", $subr_name, "html_line = $html_line");
}
}
#------------------------------------------------------------------------------
# This is a line with function data.
#------------------------------------------------------------------------------
if ($scan_function_data and (not ($input_line =~ /$empty_line_regex/)))
{
$msg = "detected a line with function data";
gp_message ("debugXL", $subr_name, $msg);
my ($hex_address_ref, $marker_ref, $reduced_line_ref,
$list_with_metrics_ref) =
split_function_data_line (\$input_line);
$full_hex_address = ${ $hex_address_ref };
$marker = ${ $marker_ref };
$routine = ${ $reduced_line_ref };
$all_metrics = ${ $list_with_metrics_ref };
$msg = "RESULT full_hex_address = " . $full_hex_address;
$msg .= " -- metric values = " . $all_metrics;
$msg .= " -- marker = " . $marker;
$msg .= " -- function name = " . $routine;
gp_message ("debugXL", $subr_name, $msg);
@fields = split (" ", $input_line);
$no_of_fields = $#fields + 1;
$elements_in_name = $no_of_fields - $number_of_metrics - 1;
$msg = "no_of_fields = " . $no_of_fields;
$msg .= " elements_in_name = " . $elements_in_name;
gp_message ("debugXL", $subr_name, $msg);
#------------------------------------------------------------------------------
# In case the last metric is 0. only, we append 3 extra characters that
# represent zero. We cannot change the number to 0.000 though because that
# has a different interpretation than 0.
# In a later phase, the "ZZZ" symbol will be removed again, but for now it
# creates consistency in, for example, the length of the metrics part.
#------------------------------------------------------------------------------
if ($all_metrics =~ /$zero_dot_at_end_regex/)
{
if (defined ($1) )
{
#------------------------------------------------------------------------------
# Somewhat overkill, but remove the leading "\" from the decimal separator
# in the debug print since it is used for internal purposes only.
#------------------------------------------------------------------------------
my $decimal_point = $decimal_separator;
$decimal_point =~ s/$backward_slash_regex//;
my $txt = "all_metrics = $all_metrics ended with 0";
$txt .= "$decimal_point ($decimal_separator)";
gp_message ("debugXL", $subr_name, $txt);
$all_metrics .= "ZZZ";
}
}
$metrics_length = length ($all_metrics);
$max_metrics_length = max ($max_metrics_length, $metrics_length);
gp_message ("debugXL", $subr_name, "$routine all_metrics = $all_metrics metrics_length = $metrics_length");
$msg = "verify full_hex_address = " . $full_hex_address;
gp_message ("debugXL", $subr_name, $msg);
if ($full_hex_address =~ /$get_hex_address_regex/)
{
$hex_address = "0x" . $2;
}
else
{
$msg = "full_hex_address = $full_hex_address has the wrong format";
gp_message ("assertion", $subr_name, $msg);
}
push (@address_field, $full_hex_address);
$msg = "pushed full_hex_address = " . $full_hex_address;
gp_message ("debugXL", $subr_name, $msg);
push (@metric_values, $all_metrics);
#------------------------------------------------------------------------------
# Record the function name "as is". Below we figure out what the final name
# should be in case there are multiple occurrences of the same name.
#
# The reason to decouple this is to avoid the code gets too complex here.
#------------------------------------------------------------------------------
push (@function_names, $routine);
}
} #-- End of loop over the input lines
#------------------------------------------------------------------------------
# Store the maximum lengths for the header and metrics.
#------------------------------------------------------------------------------
gp_message ("debugXL", $subr_name, "final max_header_length = $max_header_length");
gp_message ("debugXL", $subr_name, "final max_metrics_length = $max_metrics_length");
$function_view_structure{"max header length"} = $max_header_length;
$function_view_structure{"max metrics length"} = $max_metrics_length;
#------------------------------------------------------------------------------
# Determine the final name for the functions and set up the HTML block.
#------------------------------------------------------------------------------
my @final_html_function_block = ();
my @function_index_list = ();
#------------------------------------------------------------------------------
# First, an index list is built. If we are to index the functions in order of
# appearance in the function overview from 0 to n-1, the value of the array
# for index "i" is the index into the large "function_info" structure. This
# has the final name, the html function block, etc.
#------------------------------------------------------------------------------
for my $i (keys @address_field)
{
$msg = "address_field[" . $i ."] = " . $address_field[$i];
gp_message ("debugM", $subr_name, $msg);
}
#------------------------------------------------------------------------------
## TBD: Use get_index_function_info??!!
#------------------------------------------------------------------------------
for my $i (keys @function_names)
{
#------------------------------------------------------------------------------
# Get the function name and the address from the function overview. The
# address is used to differentiate in case a function has multiple occurences.
#------------------------------------------------------------------------------
my $routine = $function_names[$i];
my $current_address = $address_field[$i];
my $final_function_name;
my $found_a_match = $FALSE;
my $msg;
my $ref_index;
$msg = "on entry - routine = " . $routine;
$msg .= " current_address = " . $current_address;
gp_message ("debugM", $subr_name, $msg);
#------------------------------------------------------------------------------
# Check if there are duplicate entries for this function. If there are, use
# the address to find the right match in the function_info structure.
#------------------------------------------------------------------------------
gp_message ("debugXL", $subr_name, "$routine: first check for multiple occurrences");
if (exists ($g_multi_count_function{$routine}))
{
$msg = "$g_multi_count_function{$routine} exists";
gp_message ("debugXL", $subr_name, $msg);
$msg = "g_function_occurrences{$routine} = ";
$msg .= $g_function_occurrences{$routine};
gp_message ("debugXL", $subr_name, $msg);
for my $ref (keys @{ $g_map_function_to_index{$routine} })
{
my $ref_index = $g_map_function_to_index{$routine}[$ref];
my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
#------------------------------------------------------------------------------
# The address has the following format: 6:0x0003af50, but we only need the
# part after the colon and remove the first part.
#------------------------------------------------------------------------------
$addr_offset =~ s/$get_addr_offset_regex//;
gp_message ("debugXL", $subr_name, "$routine: ref_index = $ref_index");
gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");
gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
if ($addr_offset eq $current_address)
#------------------------------------------------------------------------------
# There is a match and we can store the index.
#------------------------------------------------------------------------------
{
$found_a_match = $TRUE;
push (@function_index_list, $ref_index);
last;
}
}
}
else
{
#------------------------------------------------------------------------------
# This is the easy case. There is only one index value. We do check if the
# array element that contains it, exists. If this is not the case, something
# has gone horribly wrong earlier and we need to bail out.
#------------------------------------------------------------------------------
if (defined ($g_map_function_to_index{$routine}[0]))
{
$found_a_match = $TRUE;
$ref_index = $g_map_function_to_index{$routine}[0];
push (@function_index_list, $ref_index);
my $final_function_name = $function_info[$ref_index]{"routine"};
gp_message ("debugXL", $subr_name, "pushed single occurrence: ref_index = $ref_index final_function_name = $final_function_name");
}
}
if (not $found_a_match)
#------------------------------------------------------------------------------
# This should not happen. All we can do is print an error message and stop.
#------------------------------------------------------------------------------
{
$msg = "cannot find the index for $routine: found_a_match = ";
$msg .= ($found_a_match == $TRUE) ? "TRUE" : "FALSE";
gp_message ("assertion", $subr_name, $msg);
}
}
#------------------------------------------------------------------------------
# The loop over all function names has completed and @function_index_list
# contains the index values into @function_info for the functions.
#
# All we now need to do is to retrieve the correct field(s) from the array.
#------------------------------------------------------------------------------
for my $i (keys @function_index_list)
{
my $index_for_function = $function_index_list[$i];
push (@final_html_function_block, $function_info[$index_for_function]{"html function block"});
}
for my $i (keys @final_html_function_block)
{
my $txt = "final_html_function_block[$i] = $final_html_function_block[$i]";
gp_message ("debugXL", $subr_name, $txt);
}
#------------------------------------------------------------------------------
# Since the numbers are right aligned, we know that any difference between the
# metric line length and the maximum must be caused by the first column. All
# we need to do is to prepend spaces in case of a difference.
#
# While we have the line with the metric values, we also replace ZZZ by 3
# spaces.
#------------------------------------------------------------------------------
for my $i (keys @metric_values)
{
if (length ($metric_values[$i]) < $max_metrics_length)
{
my $pad = $max_metrics_length - length ($metric_values[$i]);
my $spaces = "";
for my $s (1 .. $pad)
{
$spaces .= " ";
}
$metric_values[$i] = $spaces . $metric_values[$i];
}
$metric_values[$i] =~ s/ZZZ/ /g;
}
#------------------------------------------------------------------------------
# Determine the column widths. The start and end index of the words in the
# input line are stored in elements 0 and 1 of @word_index_values.
#
# The assumption made is that the first digit of a metric value on the first
# line is left # aligned with the header text. These are the Total values
# and other than for some derived metrics, e.g. CPI, should be the largest.
#
# The positions of the start of the value is what we should then use for the
# word "(sort)" to start.
#
# For example:
#
# Excl. Excl. CPU Excl. Excl. Excl. Excl.
# Total Cycles Instructions Last-Level IPC CPI
# CPU sec. sec. Executed Cache Misses
# 174.664 179.250 175838403203 1166209617 0.428 2.339
#------------------------------------------------------------------------------
my $foundit_ref;
my $foundit;
my @index_values = ();
my $index_values_ref;
#------------------------------------------------------------------------------
# Search for "Excl." in the top row. The metric values are aligned with this
# word and we can use it to position "(sort)" in the last header line.
#
# In @index_values, we store the position(s) of "Excl." in the header line.
# If none can be found, an exception is raised because at least one should
# be there.
#
# TBD: Check if this can be done only once.
#------------------------------------------------------------------------------
my $target_keyword = "Excl.";
($foundit_ref, $index_values_ref) = find_keyword_in_string (
\$remaining_part_header,
\$target_keyword);
$foundit = ${ $foundit_ref };
@index_values = @{ $index_values_ref };
if ($foundit)
{
for my $i (keys @index_values)
{
my $txt = "index_values[$i] = $index_values[$i]";
gp_message ("debugXL", $subr_name, $txt);
}
}
else
{
$msg = "keyword $target_keyword not found in $remaining_part_header";
gp_message ("assertion", $subr_name, $msg);
}
#------------------------------------------------------------------------------
# Compute the number of spaces we need to add between the "(sort)" strings.
#
# For example:
#
# 01234567890123456789
#
# Excl. Excl.
# (sort) (sort)
# xxxxxxxx
#
# The number of spaces required is 14 - 6 = 8.
#
# The number of spaces to be added is stored in @padding_values. These are
# the spaces to be added before the occurrence of "(sort)". This is why the
# first padding value is 0.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# TBD: This needs to be done only once.
#------------------------------------------------------------------------------
my @padding_values = ();
my $P_previous = 0;
for my $i (keys @index_values)
{
my $L = $index_values[$i];
my $P = $L + length ("(sort)");
my $pad_spaces = $L - $P_previous;
push (@padding_values, $pad_spaces);
$P_previous = $P;
}
for my $i (keys @padding_values)
{
my $txt = "padding_values[$i] = $padding_values[$i]";
gp_message ("debugXL", $subr_name, $txt);
}
#------------------------------------------------------------------------------
# Build up the sort line. Mark the current metric and make sure the line is
# aligned with the header.
#------------------------------------------------------------------------------
my $sort_string = "(sort)";
my $length_sort_string = length ($sort_string);
my $sort_line = "";
my @active_metrics = split (":", $summary_metrics);
for my $i (0 .. $number_of_metrics-1)
{
my $pad = $padding_values[$i];
my $metric_value = $active_metrics[$i];
my $spaces = "";
for my $s (1 .. $pad)
{
$spaces .= " ";
}
gp_message ("debugXL", $subr_name, "i = $i metric_value = $metric_value pad = $pad");
if ($metric_value eq $exp_type)
#------------------------------------------------------------------------------
# The current metric should have a different background color.
#------------------------------------------------------------------------------
{
$sort_string = "(sort)";
}
elsif (($exp_type eq "functions") and ($metric_value eq $g_first_metric))
#------------------------------------------------------------------------------
# Set the background color for the sort metric in the main function overview.
#------------------------------------------------------------------------------
{
$sort_string = "(sort)";
}
else
#------------------------------------------------------------------------------
# Do not set a specific background for all other metrics.
#------------------------------------------------------------------------------
{
$sort_string = "(sort)";
}
#------------------------------------------------------------------------------
# Prepend the spaces to ensure correct alignment with the rest of the header.
#------------------------------------------------------------------------------
$sort_line .= $spaces . $sort_string;
}
push (@header_lines, $sort_line);
#------------------------------------------------------------------------------
# Print the final results for the header and metrics.
#------------------------------------------------------------------------------
for my $i (keys @header_lines)
{
gp_message ("debugXL", $subr_name, "header_lines[$i] = $header_lines[$i]");
}
for my $i (keys @metric_values)
{
gp_message ("debugXL", $subr_name, "metric_values[$i] = $metric_values[$i]");
}
#------------------------------------------------------------------------------
# Construct the lines for the function overview.
#
# TBD: We could eliminate two structures here because metric_values and
# final_html_function_block are only copied and the result stored.
#------------------------------------------------------------------------------
for my $i (keys @function_names)
{
push (@metrics_part, $metric_values[$i]);
push (@function_view_array, $final_html_function_block[$i]);
}
for my $i (0 .. $#function_view_array)
{
$msg = "function_view_array[$i] = $function_view_array[$i]";
gp_message ("debugXL", $subr_name, $msg);
}
#------------------------------------------------------------------------------
# Element "function table" contains the array with all the function view data.
#------------------------------------------------------------------------------
$function_view_structure{"header"} = [@header_lines];
$function_view_structure{"metrics part"} = [@metrics_part];
$function_view_structure{"function table"} = [@function_view_array];
$msg = "leave subroutine " . $subr_name;
gp_message ("debug", $subr_name, $msg);
return (\%function_view_structure);
} #-- End of subroutine process_function_overview
#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub process_metrics
{
my $subr_name = get_my_name ();
my ($input_string, $sort_fields_ref, $metric_description_ref, $ignored_metrics_ref) = @_;
my @sort_fields = @{ $sort_fields_ref };
my %metric_description = %{ $metric_description_ref };
my %ignored_metrics = %{ $ignored_metrics_ref };
my $outputdir = append_forward_slash ($input_string);
my $LANG = $g_locale_settings{"LANG"};
my $max_len = 0;
my $metric_comment;
my ($imetricn,$outfile);
my ($html_metrics_record,$imetric,$metric);
$html_metrics_record =
"\n\n\n" .
"\n" .
"Function Metrics\n";
$outfile = $outputdir . "metrics.html";
open (METRICSOUT, ">", $outfile)
or die ("$subr_name - unable to open file $outfile for writing - '$!'");
gp_message ("debug", $subr_name, "opened file $outfile for writing");
for $metric (@sort_fields)
{
$max_len = max ($max_len, length ($metric));
gp_message ("debug", $subr_name, "sort_fields: metric = $metric max_len = $max_len");
}
# TBD: Check this
# for $imetric (@IMETRICS)
for $imetric (keys %ignored_metrics)
{
$max_len = max ($max_len, length ($imetric));
gp_message ("debug", $subr_name, "ignored_metrics imetric = $imetric max_len = $max_len");
}
$max_len++;
gp_message ("debug", $subr_name, "max_len = $max_len");
$html_metrics_record .= " Metrics used (".($#sort_fields + 1).")\n
";
for $metric (@sort_fields)
{
my $description = ${ retrieve_metric_description (\$metric, \%metric_description) };
gp_message ("debug", $subr_name, "handling metric metric = $metric->$description");
$html_metrics_record .= " $metric".(' ' x ($max_len - length ($metric)))."$description\n";
}
# $imetricn = scalar (keys %IMETRICS);
$imetricn = scalar (keys %ignored_metrics);
if ($imetricn)
{
$html_metrics_record .= "
Metrics ignored ($imetricn)\n
";
# for $imetric (sort keys %IMETRICS){
for $imetric (sort keys %ignored_metrics)
{
$metric_comment = "(inclusive, exclusive, and percentages)";
$html_metrics_record .= " $imetric".(' ' x ($max_len - length ($imetric))).$metric_comment."\n";
gp_message ("debug", $subr_name, "handling metric imetric = $imetric $metric_comment");
}
}
print METRICSOUT $html_metrics_record;
print METRICSOUT $g_html_credits_line;
close (METRICSOUT);
gp_message ("debug", $subr_name, "closed metrics file $outfile");
return (0);
} #-- End of subroutine process_metrics
#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub process_metrics_data
{
my $subr_name = get_my_name ();
my ($outfile1, $outfile2, $ignored_metrics_ref) = @_;
my %ignored_metrics = %{ $ignored_metrics_ref };
my %metric_value = ();
my %metric_description = ();
my %metric_found = ();
my $user_metrics;
my $system_metrics;
my $wall_metrics;
my $metric_spec;
my $metric_flavor;
my $metric_visibility;
my $metric_name;
my $metric_text;
my $metricdata;
my $metric_line;
my $msg;
my $summary_metrics;
my $detail_metrics;
my $detail_metrics_system;
my $call_metrics;
if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
{
$msg = "g_user_settings{default_metrics}{current_value} = ";
$msg .= $g_user_settings{"default_metrics"}{"current_value"};
gp_message ("debug", $subr_name, $msg);
# get metrics
$summary_metrics = '';
$detail_metrics = '';
$detail_metrics_system = '';
$call_metrics = '';
$user_metrics = 0;
$system_metrics = 0;
$wall_metrics = 0;
my ($last_metric,$metric,$value,$i,$r);
open (METRICTOTALS, "<", $outfile2)
or die ("Unable to open metric value data file $outfile2 for reading - '$!'");
gp_message ("debug", $subr_name, "opened $outfile2 to parse metric value data");
#------------------------------------------------------------------------------
# Below an example of the file that has just been opened. The lines I marked
# with a * has been wrapped by my for readability. This is not the case in the
# file, but makes for a really long line.
#
# Also, the data comes from one PC experiment and two HWC experiments.
#------------------------------------------------------------------------------
#
# Exclusive Total CPU Time: 32.473 (100.0%)
# Inclusive Total CPU Time: 32.473 (100.0%)
# Exclusive CPU Cycles: 23.586 (100.0%)
# " count: 47054706905
# Inclusive CPU Cycles: 23.586 (100.0%)
# " count: 47054706905
# Exclusive Instructions Executed: 54417033412 (100.0%)
# Inclusive Instructions Executed: 54417033412 (100.0%)
# Exclusive Last-Level Cache Misses: 252730685 (100.0%)
# Inclusive Last-Level Cache Misses: 252730685 (100.0%)
# * Exclusive Instructions Per Cycle: Inclusive Instructions Per Cycle:
# * Exclusive Cycles Per Instruction:
# * Inclusive Cycles Per Instruction:
# * Size: 0
# PC Address: 1:0x00000000
# Source File: (unknown)
# Object File: (unknown)
# Load Object:
# Mangled Name:
# Aliases:
#------------------------------------------------------------------------------
while ()
{
$metricdata = $_; chomp ($metricdata);
gp_message ("debug", $subr_name, "file metrictotals: $metricdata");
#------------------------------------------------------------------------------
# Ignoring whitespace, search for any line with a ":" in it, followed by
# a number with or without a dot. So, an integer or floating-point number.
#------------------------------------------------------------------------------
if ($metricdata =~ /\s*(.*):\s+(\d+\.*\d*)/)
{
gp_message ("debug", $subr_name, " candidate => $metricdata");
$metric = $1;
$value = $2;
if ( ($metric eq "PC Address") or ($metric eq "Size"))
{
gp_message ("debug", $subr_name, " skipped => $metric $value");
next;
}
gp_message ("debug", $subr_name, " proceed => $metric $value");
if ($metric eq '" count')
#------------------------------------------------------------------------------
# Hardware counter experiments have this info. Note that this line is not the
# first one to be encountered, so $last_metric has been defined already.
#------------------------------------------------------------------------------
{
$metric = $last_metric." Count"; # we presume .......
gp_message ("debug", $subr_name, "last_metric = $last_metric metric = $metric");
}
$i=index ($metricdata,":");
$r=rindex ($metricdata,":");
gp_message ("debug", $subr_name, "metricdata = $metricdata => i = $i r = $r");
if ($i == $r)
{
if ($value > 0) # Not interested in metrics contributing zero
{
$metric_value{$metric} = $value;
gp_message ("debug", $subr_name, "archived metric_value{$metric} = $metric_value{$metric}");
# e.g. $metric_value{Exclusive Total Thread Time} = 302.562
# e.g. $metric_value{Exclusive Instructions Executed} = 2415126222484
}
}
else
#------------------------------------------------------------------------------
# TBD This code deals with an old bug and may be removed.
#------------------------------------------------------------------------------
{ # er_print bug - e.g.
# Exclusive Instructions Per Cycle: Inclusive Instructions Per Cycle: Exclusive Cycles Per Instruction: Inclusive Cycles Per Instruction: Exclusive OpenMP Work Time: 162.284 (100.0%)
gp_message ("debug", $subr_name, "metrictotals odd line:->$metricdata<-");
$r=rindex ($metricdata,":",$r-1);
if ($r == -1)
{ # ignore
gp_message ("debug", $subr_name, "metrictotals odd line ignored<-");
$last_metric = "foo";
next;
}
my ($good_part)=substr ($metricdata,$r+1);
if ($good_part =~ /\s*(.*):\s+(\d+\.*\d*)/)
{
$metric = $1;
$value = $2;
if ($value>0) # Not interested in metrics contributing zero
{
$metric_value{$metric} = $value;
$msg = "metrictotals odd line rescued '$metric'=$value";
gp_message ("debug", $subr_name, $msg);
}
}
}
#------------------------------------------------------------------------------
# Preserve the current metric.
#------------------------------------------------------------------------------
$last_metric = $metric;
}
}
close (METRICTOTALS);
}
if (scalar (keys %metric_value) == 0)
#------------------------------------------------------------------------------
# If we have no metrics > 0, fudge a "Exclusive Total CPU Time", else we
# blow up later.
#
# TBD: See if this can be handled differently.
#------------------------------------------------------------------------------
{
$metric_value{"Exclusive Total CPU Time"} = 0;
gp_message ("debug", $subr_name, "no metrics found and a stub was added");
}
for my $metric (sort keys %metric_value)
{
gp_message ("debug", $subr_name, "Stored metric_value{$metric} = $metric_value{$metric}");
}
gp_message ("debug", $subr_name, "proceed to process file $outfile1");
#------------------------------------------------------------------------------
# Open and process the metrics file.
#------------------------------------------------------------------------------
open (METRICS, "<", $outfile1)
or die ("Unable to open metrics file $outfile1: '$!'");
gp_message ("debug", $subr_name, "opened file $outfile1 for reading");
#------------------------------------------------------------------------------
# Parse the file. This is a typical example:
#
# Exp Sel Total
# === === =====
# 1 all 2
# 2 all 1
# 3 all 2
# Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Available metrics:
# Exclusive Total CPU Time: e.%totalcpu
# Inclusive Total CPU Time: i.%totalcpu
# Exclusive CPU Cycles: e.+%cycles
# Inclusive CPU Cycles: i.+%cycles
# Exclusive Instructions Executed: e+%insts
# Inclusive Instructions Executed: i+%insts
# Exclusive Last-Level Cache Misses: e+%llm
# Inclusive Last-Level Cache Misses: i+%llm
# Exclusive Instructions Per Cycle: e+IPC
# Inclusive Instructions Per Cycle: i+IPC
# Exclusive Cycles Per Instruction: e+CPI
# Inclusive Cycles Per Instruction: i+CPI
# Size: size
# PC Address: address
# Name: name
#------------------------------------------------------------------------------
while ()
{
$metric_line = $_;
chomp ($metric_line);
gp_message ("debug", $subr_name, "processing line $metric_line");
#------------------------------------------------------------------------------
# The original regex has bugs because the line should not be allowed to start
# with a ":". So this is wrong:
# if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
#
# This is better:
# if (($metric =~ /\s*(.+):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
#
# In general, this regex has some potential issues and has been replaced by
# the one shown below.
#
# We select a line that does not start with "Current" and aside from whitespace
# starts with anything (although it should be a string with words only),
# followed by whitespace and either an "e" or "i". This is called the "flavor"
# and is followed by a visibility marker (.,+,%, or !) and a metric name.
#------------------------------------------------------------------------------
# Ruud if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) && !($metric =~/^Current/)){
($metric_spec, $metric_flavor, $metric_visibility, $metric_name, $metric_text) =
extract_metric_specifics ($metric_line);
# if (($metric_line =~ /\s*(.+):\s+([ei])([\.\+%]+)(\S*)/) and !($metric_line =~/^Current/))
if ($metric_spec eq "skipped")
{
gp_message ("debug", $subr_name, "skipped line: $metric_line");
}
else
{
gp_message ("debug", $subr_name, "line of interest: $metric_line");
$metric_found{$metric_spec} = 1;
if ($g_user_settings{"ignore_metrics"}{"defined"})
{
gp_message ("debug", $subr_name, "check for $metric_spec");
if (exists ($ignored_metrics{$metric_name}))
{
gp_message ("debug", $subr_name, "user asked to ignore metric $metric_name");
next;
}
}
#------------------------------------------------------------------------------
# This metric is not on the ignored list and qualifies, so store it.
#------------------------------------------------------------------------------
$metric_description{$metric_spec} = $metric_text;
# TBD: add for other visibilities too, like +
gp_message ("debug", $subr_name, "stored $metric_description{$metric_spec} = $metric_description{$metric_spec}");
if ($metric_flavor ne "e")
{
gp_message ("debug", $subr_name, "metric $metric_spec is ignored");
}
else
#------------------------------------------------------------------------------
# Only the exclusive metrics are shown.
#------------------------------------------------------------------------------
{
gp_message ("debug", $subr_name, "metric $metric_spec ($metric_text) is considered");
if ($metric_spec =~ /user/)
{
$user_metrics = $TRUE;
gp_message ("debug", $subr_name, "m: user_metrics set to TRUE");
}
elsif ($metric_spec =~ /system/)
{
$system_metrics = $TRUE;
gp_message ("debug", $subr_name, "m: system_metrics set to TRUE");
}
elsif ($metric_spec =~ /wall/)
{
$wall_metrics = $TRUE;
gp_message ("debug", $subr_name, "m: wall_metrics set to TRUE");
}
#------------------------------------------------------------------------------
# TBD I don't see why these need to be skipped. Also, should be totalcpu.
#------------------------------------------------------------------------------
elsif (($metric_spec =~ /^e\.total$/) or ($metric_spec =~/^e\.total_cpu$/))
{
# skip total thread time and total CPU time
gp_message ("debug", $subr_name, "m: skip above");
}
elsif (defined ($metric_value{$metric_text}))
{
gp_message ("debug", $subr_name, "Total attributed to this metric metric_value{$metric_text} = $metric_value{$metric_text}");
if ($summary_metrics ne '')
{
$summary_metrics = $summary_metrics.':'.$metric_spec;
gp_message ("debug", $subr_name, "updated summary_metrics = $summary_metrics - 1");
if ($metric_spec !~ /\.wait$|\.ulock$|\.text$|\.data$|\.owait$|total$|mpiwork$|mpiwait$|ompwork$|ompwait$/)
{
$detail_metrics = $detail_metrics.':'.$metric_spec;
gp_message ("debug", $subr_name, "updated m:detail_metrics=$detail_metrics - 1");
$detail_metrics_system = $detail_metrics_system.':'.$metric_spec;
gp_message ("debug", $subr_name, "updated m:detail_metrics_system=$detail_metrics_system - 1");
}
else
{
gp_message ("debug", $subr_name, "m: no want above metric for detail_metrics or detail_metrics_system");
}
}
else
{
$summary_metrics = $metric_spec;
gp_message ("debug", $subr_name, "initialized summary_metrics = $summary_metrics - 2");
if ($metric_spec !~ /\.wait$|\.ulock$|\.text$|\.data$|\.owait$|total$|mpiwork$|mpiwait$|ompwork$|ompwait$/)
{
$detail_metrics = $metric_spec;
gp_message ("debug", $subr_name, "m:detail_metrics=$detail_metrics - 2");
$detail_metrics_system = $metric_spec;
gp_message ("debug", $subr_name, "m:detail_metrics_system=$detail_metrics_system - 2");
}
else
{
gp_message ("debug", $subr_name, "m: no want above metric for detail_metrics or detail_metrics_system");
}
}
gp_message ("debug", $subr_name, " metric $metric_spec added");
}
else
{
gp_message ("debug", $subr_name, "m: no want above metric was a 0 total");
}
}
}
}
close METRICS;
if ($wall_metrics > 0)
{
gp_message ("debug", $subr_name,"m:wall_metrics set adding to summary_metrics");
$summary_metrics = "e.wall:".$summary_metrics;
gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 3");
}
if ($system_metrics > 0)
{
gp_message ("debug", $subr_name,"m:system_metrics set adding to summary_metrics,call_metrics and detail_metrics_system");
$summary_metrics = "e.system:".$summary_metrics;
$call_metrics = "i.system:".$call_metrics;
$detail_metrics_system ='e.system:'.$detail_metrics_system;
gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 4");
gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics");
gp_message ("debug", $subr_name,"m:detail_metrics_system=$detail_metrics_system - 3");
}
#------------------------------------------------------------------------------
# TBD: e.user and i.user do not always exist!!
#------------------------------------------------------------------------------
if ($user_metrics > 0)
{
gp_message ("debug", $subr_name,"m:user_metrics set adding to summary_metrics,detail_metrics,detail_metrics_system and call_metrics");
# Ruud if (!exists ($IMETRICS{"i.user"})){
if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{"user"}))
{
$summary_metrics = "e.user:".$summary_metrics;
}
else
{
$summary_metrics = "e.user:i.user:".$summary_metrics;
}
$detail_metrics = "e.user:".$detail_metrics;
$detail_metrics_system = "e.user:".$detail_metrics_system;
gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 5");
gp_message ("debug", $subr_name,"m:detail_metrics=$detail_metrics - 3");
gp_message ("debug", $subr_name,"m:detail_metrics_system=$detail_metrics_system - 4");
if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{"user"}))
{
$call_metrics = "a.user:".$call_metrics;
}
else
{
$call_metrics = "a.user:i.user:".$call_metrics;
}
gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics - 2");
}
if ($call_metrics eq "")
{
$call_metrics = $detail_metrics;
gp_message ("debug", $subr_name,"m:call_metrics is not set, setting it to detail_metrics ");
gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics - 3");
}
for my $metric (sort keys %ignored_metrics)
{
if ($ignored_metrics{$metric})
{
gp_message ("debug", $subr_name, "active metric, but ignored: $metric");
}
}
return (\%metric_value, \%metric_description, \%metric_found, $user_metrics, $system_metrics, $wall_metrics,
$summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics);
} #-- End of subroutine process_metrics_data
#------------------------------------------------------------------------------
# Process source lines that are not part of the target function.
#
# Generate straightforward HTML, but define an anchor based on the source line
# number in the list.
#------------------------------------------------------------------------------
sub process_non_target_source
{
my $subr_name = get_my_name ();
my ($start_scan, $end_scan,
$src_times_regex, $function_regex, $number_of_metrics,
$file_contents_ref, $modified_html_ref) = @_;
my @file_contents = @{ $file_contents_ref };
my @modified_html = @{ $modified_html_ref };
my $colour_code_line = $FALSE;
my $input_line;
my $line_id;
my $modified_line;
#------------------------------------------------------------------------------
# Main loop to parse all of the source code and take action as needed.
#------------------------------------------------------------------------------
for (my $line_no=$start_scan; $line_no <= $end_scan; $line_no++)
{
$input_line = $file_contents[$line_no];
#------------------------------------------------------------------------------
# Generate straightforward HTML, but define an anchor based on the source line
# number in the list.
#------------------------------------------------------------------------------
$line_id = extract_source_line_number ($src_times_regex,
$function_regex,
$number_of_metrics,
$input_line);
if ($input_line =~ /$function_regex/)
{
$colour_code_line = $TRUE;
}
#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "<".
#------------------------------------------------------------------------------
$input_line =~ s/$g_less_than_regex/$g_html_less_than_regex/g;
#------------------------------------------------------------------------------
# Add an id.
#------------------------------------------------------------------------------
$modified_line = "";
my $coloured_line;
if ($colour_code_line)
{
my $boldface = $TRUE;
$coloured_line = color_string (
$input_line,
$boldface,
$g_html_color_scheme{"non_target_function_name"});
$colour_code_line = $FALSE;
$modified_line .= "$coloured_line";
}
else
{
$modified_line .= "$input_line";
}
gp_message ("debugXL", $subr_name, " $line_no : modified_line = $modified_line");
push (@modified_html, $modified_line);
}
return (\@modified_html);
} #-- End of subroutine process_non_target_source
#------------------------------------------------------------------------------
# This function scans the configuration file and adapts the internal settings
# accordingly.
#
# Errors are stored during the parsing and processing phase. They are printed
# at the end and sorted by line number.
#
#
# TBD: Does not yet use the warnings/error system. This needs to be fixed.
#------------------------------------------------------------------------------
sub process_rc_file
{
my $subr_name = get_my_name ();
my ($rc_file_name, $rc_file_paths_ref) = @_;
#------------------------------------------------------------------------------
# Local structures.
#------------------------------------------------------------------------------
# Stores the values extracted from the config file:
my %rc_settings_user = ();
my %error_and_warning_msgs = ();
my @rc_file_paths = ();
my @split_line;
my @my_fields;
my $msg;
my $first_part;
my $line;
my $line_number;
my $no_of_arguments;
my $number_of_fields;
my $number_of_paths;
my $parse_errors; #-- Count the number of errors
my $parse_warnings; #-- Count the number of errors
my $rc_config_file;
my $rc_file_found;
my $rc_keyword;
my $rc_value;
@rc_file_paths = @{$rc_file_paths_ref};
$number_of_paths = scalar (@rc_file_paths);
if ($number_of_paths == 0)
#------------------------------------------------------------------------------
# This should not happen, but is a good safety net to add.
#------------------------------------------------------------------------------
{
my $msg = "search path list is empty";
gp_message ("assertion", $subr_name, $msg);
}
#------------------------------------------------------------------------------
# Check for the presence of a configuration file.
#------------------------------------------------------------------------------
$msg = "number_of_paths = $number_of_paths rc_file_paths = @rc_file_paths";
gp_message ("debug", $subr_name, $msg);
$rc_file_found = $FALSE;
for my $path_name (@rc_file_paths)
{
$rc_config_file = $path_name . "/" . $rc_file_name;
$msg = "looking for configuration file " . $rc_config_file;
gp_message ("debug", $subr_name, $msg);
if (-f $rc_config_file)
{
$msg = "found configuration file " . $rc_config_file;
gp_message ("debug", $subr_name, $msg);
$rc_file_found = $TRUE;
last;
}
}
if (not $rc_file_found)
#------------------------------------------------------------------------------
# There is no configuration file and we can skip this subroutine.
#------------------------------------------------------------------------------
{
$msg = "configuration file $rc_file_name not found";
gp_message ("verbose", $subr_name, $msg);
return (0);
}
else
{
$msg = "unable to open file $rc_config_file for reading:";
open (GP_DISPLAY_HTML_RC, "<", "$rc_config_file")
or die ($subr_name . " - " . $msg . " " . $!);
#------------------------------------------------------------------------------
# The configuration file has been opened for reading.
#------------------------------------------------------------------------------
$msg = "file $rc_config_file has been opened for reading";
gp_message ("debug", $subr_name, $msg);
}
$msg = "found configuration file $rc_config_file";
gp_message ("verbose", $subr_name, $msg);
$msg = "processing configuration file " . $rc_config_file;
gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# Here we scan the configuration file for the settings.
#
# A setting consists of a keyword, optionally followed by a value. It is
# optional because not all keywords may require a value.
#
# At the end of this block, all keyword/value pairs are stored in a hash.
#
# We do not yet check for the validity of these pairs. This is done next.
#
# The original code had this all integrated, but it made the code very
# complex with deeply nested if-statements. The flow was also hard to follow.
#------------------------------------------------------------------------------
$parse_errors = 0;
$parse_warnings = 0;
$line_number = 0;
while (my $line = )
{
chomp ($line);
$line_number++;
gp_message ("debug", $subr_name, "read input line = $line");
#------------------------------------------------------------------------------
# Ignore a line with whitespace only
#------------------------------------------------------------------------------
if ($line =~ /^\s*$/)
{
gp_message ("debug", $subr_name, "ignored a line with whitespace");
next;
}
#------------------------------------------------------------------------------
# Ignore a comment line, defined by starting with a "#", possibly prepended by
# whitespace.
#------------------------------------------------------------------------------
if ($line =~ /^\s*\#/)
{
gp_message ("debug", $subr_name, "ignored a full comment line");
next;
}
#------------------------------------------------------------------------------
# Split the input line using the "#" symbol as a separator. We have already
# handled the case of an isolated comment line, so there may only be an
# embedded comment.
#
# Regardless of this, we are only interested in the first part.
#------------------------------------------------------------------------------
@split_line = split ("#", $line);
for my $i (@split_line)
{
gp_message ("debug", $subr_name, "elements after split of line: $i");
}
$first_part = $split_line[0];
gp_message ("debug", $subr_name, "relevant part = $first_part");
if ($first_part =~ /[&\^\*\@\$]+/)
#------------------------------------------------------------------------------
# The &, ^, *, @ and $ symbols should not occur. If they do, we flag an error
# an fetch the next line.
#------------------------------------------------------------------------------
{
$parse_errors++;
$msg = "non-supported character(s) (\&,\^,\*,\@,\$) found: $line";
$error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
next;
}
else
#------------------------------------------------------------------------------
# Split the first part on whitespace and verify the number of fields to be
# valid. Although we currently only have keywords with a value, a keyword
# without value is supported to.
#
# If the number of fields is valid, the keyword and value are stored. In case
# of a single field, the value is assigned a special string.
#
# Although this situation should not occur, we do abort if something unexpected
# is encountered here.
#------------------------------------------------------------------------------
{
@my_fields = split (/\s/, $split_line[0]);
$number_of_fields = scalar (@my_fields);
$msg = "number of fields = " . $number_of_fields;
gp_message ("debug", $subr_name, $msg);
}
if ($number_of_fields ge 3)
#------------------------------------------------------------------------------
# This is not supported.
#------------------------------------------------------------------------------
{
$parse_errors++;
$msg = "more than 2 fields found: $first_part";
$error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
next;
}
elsif ($number_of_fields eq 2)
{
$rc_keyword = $my_fields[0];
$rc_value = $my_fields[1];
}
elsif ($number_of_fields eq 1)
{
$rc_keyword = $my_fields[0];
$rc_value = "the_field_is_empty";
}
else
{
$msg = "[line $line_number] $rc_config_file -";
$msg .= " number of fields = $number_of_fields";
gp_message ("assertion", $subr_name, $msg);
}
#------------------------------------------------------------------------------
# Store the keyword, value and line number.
#------------------------------------------------------------------------------
if (exists ($rc_settings_user{$rc_keyword}))
{
$parse_warnings++;
my $prev_value = $rc_settings_user{$rc_keyword}{"value"};
my $prev_line_number = $rc_settings_user{$rc_keyword}{"line_no"};
if ($rc_value ne $prev_value)
{
$msg = "option $rc_keyword previously set at line";
$msg .= " $prev_line_number: new value '$rc_value'";
$msg .= " ' overrides '$prev_value'";
}
else
{
$msg = "option $rc_keyword previously set to the same value";
$msg .= " at line $prev_line_number";
}
$error_and_warning_msgs{"warning"}{$line_number}{"message"} = $msg;
}
$rc_settings_user{$rc_keyword}{"value"} = $rc_value;
$rc_settings_user{$rc_keyword}{"line_no"} = $line_number;
gp_message ("debug", $subr_name, "stored keyword = $rc_keyword");
gp_message ("debug", $subr_name, "stored value = $rc_value");
gp_message ("debug", $subr_name, "stored line number = $line_number");
}
#------------------------------------------------------------------------------
# Completed the parsing of the configuration file. It can be closed.
#------------------------------------------------------------------------------
close (GP_DISPLAY_HTML_RC);
#------------------------------------------------------------------------------
# Print the raw input as just collected from the configuration file.
#------------------------------------------------------------------------------
gp_message ("debug", $subr_name, "contents of %rc_settings_user:");
for my $keyword (keys %rc_settings_user)
{
my $key_value = $rc_settings_user{$keyword}{"value"};
$msg = "keyword = " . $keyword . " value = " . $key_value;
gp_message ("debug", $subr_name, $msg);
}
for my $rc_keyword (keys %g_user_settings)
{
for my $fields (keys %{ $g_user_settings{$rc_keyword} })
{
$msg = "before config file: $rc_keyword $fields =";
$msg .= " " . $g_user_settings{$rc_keyword}{$fields};
gp_message ("debug", $subr_name, $msg);
}
}
#------------------------------------------------------------------------------
# We are almost done. Check for all keywords found whether they are valid.
# Also verify that the corresponding value is valid.
#
# Update the g_user_settings table if everything is okay.
#------------------------------------------------------------------------------
for my $rc_keyword (keys %rc_settings_user)
{
my $rc_value = $rc_settings_user{$rc_keyword}{"value"};
if (exists ( $g_user_settings{$rc_keyword}))
{
#------------------------------------------------------------------------------
# This is a supported keyword. There are two more things left to do:
# - Check how many values it requires (currently exactly one is supported)
# - Is the value a valid number or string?
#------------------------------------------------------------------------------
$no_of_arguments = $g_user_settings{$rc_keyword}{"no_of_arguments"};
if ($no_of_arguments eq 1)
{
my $input_value = $rc_value;
if ($input_value ne "the_field_is_empty")
#
#------------------------------------------------------------------------------
# So far, so good. We only need to check if the value is valid for the keyword.
#------------------------------------------------------------------------------
{
my $data_type = $g_user_settings{$rc_keyword}{"data_type"};
my $valid_input =
verify_if_input_is_valid ($input_value, $data_type);
#------------------------------------------------------------------------------
# Check if the value is valid.
#------------------------------------------------------------------------------
if ($valid_input)
{
$g_user_settings{$rc_keyword}{"current_value"} =
$rc_value;
$g_user_settings{$rc_keyword}{"defined"} = $TRUE;
}
else
{
$parse_errors++;
$line_number = $rc_settings_user{$rc_keyword}{"line_no"};
$msg = "input value '$input_value' for keyword";
$msg .= " $rc_keyword is not valid";
$error_and_warning_msgs{"error"}{$line_number}{"message"}
= $msg;
next;
}
}
else
#------------------------------------------------------------------------------
# This keyword requires a value, but none has been found.
#------------------------------------------------------------------------------
{
$parse_errors++;
$line_number = $rc_settings_user{$rc_keyword}{"line_no"};
$msg = "missing value for keyword '$rc_keyword'";
$error_and_warning_msgs{"error"}{$line_number}{"message"}
= $msg;
next;
}
}
elsif ($no_of_arguments eq 0)
#------------------------------------------------------------------------------
# Currently a theoretical scenario since all commands require a value, but in
# case this is no longer true, we need to at least flag the fact the user set
# this command.
#------------------------------------------------------------------------------
{
$g_user_settings{$rc_keyword}{"defined"} = $TRUE;
}
else
#------------------------------------------------------------------------------
# The code is not prepared for the situation one command has multiple values,
# but this situation should never occur. Still it won't hurt to add a check.
#------------------------------------------------------------------------------
{
my $msg = "cannot handle $no_of_arguments in the input";
gp_message ("assertion", $subr_name, $msg);
}
}
else
#------------------------------------------------------------------------------
# A non-valid keyword is found. This is flagged as an error.
#------------------------------------------------------------------------------
{
$parse_errors++;
$line_number = $rc_settings_user{$rc_keyword}{"line_no"};
$msg = "keyword $rc_keyword is not supported";
$error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
}
}
for my $rc_keyword (keys %g_user_settings)
{
for my $fields (keys %{ $g_user_settings{$rc_keyword} })
{
$msg = "after config file: $rc_keyword $fields =";
$msg .= " " . $g_user_settings{$rc_keyword}{$fields};
gp_message ("debug", $subr_name, $msg);
}
}
print_table_user_settings ("debug", "upon the return from $subr_name");
if ( ($parse_errors == 0) and ($parse_warnings == 0) )
{
$msg = "successfully parsed and processed the configuration file";
gp_message ("verbose", $subr_name, $msg);
}
else
{
if ($parse_errors > 0)
{
my $plural_or_single = ($parse_errors > 1) ? "errors" : "error";
$msg = $g_error_keyword . "found $parse_errors fatal";
$msg .= " " . $plural_or_single . " in the configuration file:";
gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# Sort the hash keys, the line numbers, alphabetically and print the
# corresponding error messages.
#------------------------------------------------------------------------------
for my $line_no (sort {$a <=> $b}
(keys %{ $error_and_warning_msgs{"error"} }))
{
$msg = $g_error_keyword . "[line $line_no] in file";
$msg .= $rc_config_file . " - ";
$msg .= $error_and_warning_msgs{"error"}{$line_no}{"message"};
gp_message ("debug", $subr_name, $msg);
}
}
if (not $g_quiet)
{
if ($parse_warnings > 0)
{
$msg = $g_warn_keyword . " found $parse_warnings warnings in";
$msg .= " the configuration file:";
gp_message ("debug", $subr_name, $msg);
for my $line_no (sort {$a <=> $b}
(keys %{ $error_and_warning_msgs{"warning"} }))
{
$msg = $g_warn_keyword;
$msg .= " [line $line_no] in file $rc_config_file - ";
$msg .= $error_and_warning_msgs{"warning"}{$line_no}{"message"};
gp_message ("debug", $subr_name, $msg);
}
}
}
}
return ($parse_errors);
} #-- End of subroutine process_rc_file
#------------------------------------------------------------------------------
# Generate the annotated html file for the source listing.
#------------------------------------------------------------------------------
sub process_source
{
my $subr_name = get_my_name ();
my ($number_of_metrics, $function_info_ref,
$outputdir, $input_filename) = @_;
my @function_info = @{ $function_info_ref };
#------------------------------------------------------------------------------
# The regex section
#------------------------------------------------------------------------------
my $end_src1_header_regex = '(^\s+)(\d+)\.\s+(.*)';
my $end_src2_header_regex = '(^\s+)(';
my $function_regex = '^(\s*)';
my $function2_regex = '^(\s*)<Function:\s(.*)>';
my $src_regex = '(\s*)(\d+)\.(.*)';
my $txt_ext_regex = '\.txt$';
my $src_filename_id_regex = '^file\.(\d+)\.src\.txt$';
my $integer_only_regex = '\d+';
#------------------------------------------------------------------------------
# Computed dynamically below.
# TBD: Try to move this up.
#------------------------------------------------------------------------------
my $src_times_regex;
my $hot_lines_regex;
my $metric_regex;
my $metric_extra_regex;
my @components = ();
my @fields_in_line = ();
my @file_contents = ();
my @hot_source_lines = ();
my @max_metric_values = ();
my @modified_html = ();
my @transposed_hot_lines = ();
my $colour_coded_line;
my $colour_coded_line_ref;
my $line_id;
my $ignore_value;
my $func_name_in_src_file;
my $html_new_line = "
";
my $input_line;
my $metric_values;
my $modified_html_ref;
my $modified_line;
my $is_empty;
my $start_all_source;
my $start_target_source;
my $end_target_source;
my $output_line;
my $hot_line;
my $src_line_no;
my $src_code_line;
my $decimal_separator = $g_locale_settings{"decimal_separator"};
my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
my $file_title;
my $found_target;
my $html_dis_record;
my $html_end;
my $html_header;
my $html_home;
my $rounded_percentage;
my $start_tracking;
my $threshold_line;
my $base;
my $boldface;
my $msg;
my $routine;
my $LANG = $g_locale_settings{"LANG"};
my $the_title = set_title ($function_info_ref, $input_filename,
"process source");
my $outfile = $input_filename . ".html";
#------------------------------------------------------------------------------
# Remove the .txt from file..src.txt
#------------------------------------------------------------------------------
my $html_output_file = $input_filename;
$html_output_file =~ s/$txt_ext_regex/.html/;
gp_message ("debug", $subr_name, "input_filename = $input_filename");
gp_message ("debug", $subr_name, "the_title = $the_title");
$file_title = $the_title;
$html_header = ${ create_html_header (\$file_title) };
$html_home = ${ generate_home_link ("right") };
push (@modified_html, $html_header);
push (@modified_html, $html_home);
push (@modified_html, "");
#------------------------------------------------------------------------------
# Open the html file used for the output.
#------------------------------------------------------------------------------
open (NEW_HTML, ">", $html_output_file)
or die ("$subr_name - unable to open file $html_output_file for writing: '$!'");
gp_message ("debug", $subr_name , "opened file $html_output_file for writing");
$base = get_basename ($input_filename);
gp_message ("debug", $subr_name, "base = $base");
if ($base =~ /$src_filename_id_regex/)
{
my $file_id = $1;
if (defined ($function_info[$file_id]{"routine"}))
{
$routine = $function_info[$file_id]{"routine"};
gp_message ("debugXL", $subr_name, "target routine = $routine");
}
else
{
my $msg = "cannot retrieve routine name for file_id = $file_id";
gp_message ("assertion", $subr_name, $msg);
}
}
#------------------------------------------------------------------------------
# Check if the input file is empty. If so, generate a short text in the html
# file and return. Otherwise open the file and read the contents.
#------------------------------------------------------------------------------
$is_empty = is_file_empty ($input_filename);
if ($is_empty)
{
#------------------------------------------------------------------------------
# The input file is empty. Write a diagnostic message in the html file and exit.
#------------------------------------------------------------------------------
gp_message ("debug", $subr_name ,"file $input_filename is empty");
my $comment = "No source listing generated by $tool_name - " .
"file $input_filename is empty";
my $error_file = $outputdir . "gp-listings.err";
my $html_empty_file_ref = html_text_empty_file (\$comment, \$error_file);
my @html_empty_file = @{ $html_empty_file_ref };
print NEW_HTML "$_\n" for @html_empty_file;
close NEW_HTML;
return (0);
}
else
#------------------------------------------------------------------------------
# Open the input file with the source code
#------------------------------------------------------------------------------
{
open (SRC_LISTING, "<", $input_filename)
or die ("$subr_name - unable to open file $input_filename for reading: '$!'");
gp_message ("debug", $subr_name, "opened file $input_filename for reading");
}
#------------------------------------------------------------------------------
# Generate the regex for the metrics. This depends on the number of metrics.
#------------------------------------------------------------------------------
gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator<--");
$metric_regex = '';
$metric_extra_regex = '';
for my $metric_used (1 .. $number_of_metrics)
{
$metric_regex .= '(\d+' . $decimal_separator . '*\d*)\s+';
}
$metric_extra_regex = $metric_regex . '(\d+' . $decimal_separator . ')';
$hot_lines_regex = '^(#{2})\s+';
$hot_lines_regex .= '('.$metric_regex.')';
$hot_lines_regex .= '([0-9?]+)\.\s+(.*)';
$src_times_regex = '^(#{2}|\s{2})\s+';
$src_times_regex .= '('.$metric_extra_regex.')';
$src_times_regex .= '(.*)';
gp_message ("debugXL", $subr_name, "metric_regex = $metric_regex");
gp_message ("debugXL", $subr_name, "hot_lines_regex = $hot_lines_regex");
gp_message ("debugXL", $subr_name, "src_times_regex = $src_times_regex");
gp_message ("debugXL", $subr_name, "src_regex = $src_regex");
gp_message ("debugXL", $subr_name, "end_src1_header_regex = $end_src1_header_regex");
gp_message ("debugXL", $subr_name, "end_src2_header_regex = $end_src2_header_regex");
gp_message ("debugXL", $subr_name, "function_regex = $function_regex");
gp_message ("debugXL", $subr_name, "function2_regex = $function2_regex");
gp_message ("debugXL", $subr_name, "src_regex = $src_regex");
#------------------------------------------------------------------------------
# Read the file into memory.
#------------------------------------------------------------------------------
chomp (@file_contents = );
#------------------------------------------------------------------------------
# Identify the header lines. Make the minimal assumptions.
#
# In both cases, the first line after the header has whitespace. This is
# followed by either one of the following:
#
# - .
# - " . $input_line . "