public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
* [binutils-gdb] gprofng: implement a functional gp-display-html
@ 2022-07-06 21:59 Vladimir Mezentsev
  0 siblings, 0 replies; only message in thread
From: Vladimir Mezentsev @ 2022-07-06 21:59 UTC (permalink / raw)
  To: bfd-cvs, gdb-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=41bbac64c36ffc5a418524be55fde20fad888e11

commit 41bbac64c36ffc5a418524be55fde20fad888e11
Author: Ruud van der Pas <ruud.vanderpas@oracle.com>
Date:   Tue Jun 28 10:37:19 2022 -0700

    gprofng: implement a functional gp-display-html
    
    This patch enables the first support for the "gprofng display html" command.
    This command works for C/C++ applications on x86_64. Using one or more gprofng
    experiment directories as input, a new directory with html files is created.
    Through the index.html file in this directory, the performance results may be
    viewed in a browser.
    
    gprofng/Changelog:
    2022-06-28  Ruud van der Pas  <ruud.vanderpas@oracle.com>
    
            * gp-display-html/gp-display-html.in: implement first support for x86_64 and C/C++

Diff:
---
 gprofng/gp-display-html/gp-display-html.in | 14631 ++++++++++++++++++++++++++-
 1 file changed, 14468 insertions(+), 163 deletions(-)

diff --git a/gprofng/gp-display-html/gp-display-html.in b/gprofng/gp-display-html/gp-display-html.in
index f8fbc244c52..54a87d7a3c7 100644
--- a/gprofng/gp-display-html/gp-display-html.in
+++ b/gprofng/gp-display-html/gp-display-html.in
@@ -1,5 +1,4 @@
-#!/usr/bin/perl
-
+#!/usr/bin/env perl
 #   Copyright (C) 2021 Free Software Foundation, Inc.
 #   Contributed by Oracle.
 #
@@ -20,14 +19,34 @@
 #   Foundation, 51 Franklin Street - Fifth Floor, Boston,
 #   MA 02110-1301, USA.
  
+use strict;
+use warnings;
+use feature qw (state);
+use File::stat;
+
 #------------------------------------------------------------------------------
-# gp-display-html, last updated July 2021
-#
-# NOTE: This is a skeleton version. The real code will follow as an update.
+# Check as early as possible if the version of Perl used is supported.
 #------------------------------------------------------------------------------
+INIT
+{
+  my $perl_minimal_version_supported = version->parse ("5.10.0")->normal;
+  my $perl_current_version           = version->parse ("$]")->normal;
 
-use strict;
-use warnings;
+  if ($perl_current_version lt $perl_minimal_version_supported)
+    {
+      my $msg;
+
+      $msg  = "Error: minimum Perl release required: ";
+      $msg .= $perl_minimal_version_supported;
+      $msg .= " current: ";
+      $msg .= $perl_current_version;
+      $msg .= "\n";
+
+      print $msg;
+
+      exit (1);
+     }
+} #-- End of INIT
 
 #------------------------------------------------------------------------------
 # Poor man's version of a boolean.
@@ -35,222 +54,14508 @@ use warnings;
 my $TRUE    = 1;
 my $FALSE   = 0;
 
+my $g_max_length_first_metric;
+
+#-------------------------------------------------------------------------------
+# Code debugging flag
+#-------------------------------------------------------------------------------
+my $g_test_code = $FALSE;
+
+#-------------------------------------------------------------------------------
+# GPROFNG commands and files used.
+#-------------------------------------------------------------------------------
+my $GP_DISPLAY_TEXT = "gp-display-text";
+
+my $g_gp_output_file   = $GP_DISPLAY_TEXT.".stdout.log";
+my $g_gp_error_logfile = $GP_DISPLAY_TEXT.".stderr.log";
+
+#------------------------------------------------------------------------------
+# Global variables.
+#------------------------------------------------------------------------------
+my $g_addressing_mode = "64 bit";
+
+#------------------------------------------------------------------------------
+# The global regex section.
+#
+# First step towards consolidating all regexes.
+#------------------------------------------------------------------------------
+  my $g_less_than_regex      = '<';
+  my $g_html_less_than_regex = '&lt;';
+  my $g_endbr_inst_regex     = 'endbr[32|64]';
+
+#------------------------------------------------------------------------------
+# These are the regex's used.
+#------------------------------------------------------------------------------
+#------------------------------------------------------------------------------
+# Disassembly analysis
+#------------------------------------------------------------------------------
+  my $g_branch_regex = '\.*([0-9a-fA-F]*):\s+(j).*\s*0x([0-9a-fA-F]+)';
+  my $g_endbr_regex  = '\.*([0-9a-fA-F]*):\s+(endbr[32|64])';
+  my $g_function_call_v2_regex = '(.*)\s+([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)\s*';
+
+#------------------------------------------------------------------------------
+# Convenience.  These map the on/off value to $TRUE/$FALSE to make the code
+# easier to read.  For example: "if ($g_verbose)" as opposed to the following:
+# "if ($verbose_setting eq "on").
+#------------------------------------------------------------------------------
+my $g_verbose;
+my $g_warnings;
+my $g_quiet;
+
+my $g_first_metric; 
+
+my $binutils_version;
+my $driver_cmd;
+my $tool_name;
+my $version_info;
+
+my %g_mapped_cmds = ();
+
+#------------------------------------------------------------------------------
+# TBD All warning messages are collected and are accessible through the main
+# page.
+#------------------------------------------------------------------------------
+my @g_warning_messages = ();
+
+#------------------------------------------------------------------------------
+# Contains the names that have already been tagged.  This is a global
+# structure because otherwise the code would get much more complicated.
+#------------------------------------------------------------------------------
+my %g_tagged_names = ();
+
+#------------------------------------------------------------------------------
+# TBD Remove the use of these structures. No longer used.
+#------------------------------------------------------------------------------
+my %g_function_tag_id = ();
+my $g_context = 5; # Defines the range of scan
+
+my $g_default_setting_lang = "en-US.UTF-8";
+my %g_exp_dir_meta_data;
+
+my @g_user_input_errors = ();
+
+my $g_html_credits_line;
+
+my $g_warn_keyword  = "Input warning: ";
+my $g_error_keyword = "Input error:   ";
+
+my %g_function_occurrences = ();
+my %g_map_function_to_index = ();
+my %g_multi_count_function = ();
+my %g_function_view_all = ();
+my @g_full_function_view_table = ();
+
+my @g_html_experiment_stats = ();
+
+#-------------------------------------------------------------------------------
+# These structures contain the information printed in the function views.
+#-------------------------------------------------------------------------------
+my $g_header_lines;
+
+my @g_html_function_name = ();
+
+#-------------------------------------------------------------------------------
+# TBD: This variable may not be needed and replaced by tp_value
+my $thresh = 0;
+#-------------------------------------------------------------------------------
+
 #-------------------------------------------------------------------------------
 # Define the driver command, tool name and version number.
 #-------------------------------------------------------------------------------
-my $driver_cmd       = "gprofng display html";
-my $tool_name        = "gp-display-html";
-my $binutils_version = "BINUTILS_VERSION";
-my $version_info     = $tool_name . " GNU binutils version " . $binutils_version;
+$driver_cmd       = "gprofng display html";
+$tool_name        = "gp-display-html";
+#$binutils_version = "2.38.50";
+$binutils_version = "BINUTILS_VERSION";
+$version_info     = $tool_name . " GNU binutils version " . $binutils_version;
+
+#-------------------------------------------------------------------------------
+
+#-------------------------------------------------------------------------------
+# Define several key data structures.
+#-------------------------------------------------------------------------------
+my %g_user_settings = 
+  (
+    output           => { option => "-o" , no_of_arguments => 1, data_type => "path"    , current_value => undef, defined => $FALSE},
+    overwrite        => { option => "-O" , no_of_arguments => 1, data_type => "path"    , current_value => undef, defined => $FALSE},
+    calltree         => { option => "-ct", no_of_arguments => 1, data_type => "onoff"   , current_value => "off"      , defined => $FALSE},
+    func_limit       => { option => "-fl", no_of_arguments => 1, data_type => "pinteger", current_value => 500        , defined => $FALSE},
+    highlight_percentage => { option => "-hp", no_of_arguments => 1, data_type => "pfloat"  , current_value => 90.0       , defined => $FALSE},
+    threshold_percentage => { option => "-tp", no_of_arguments => 1, data_type => "pfloat"  , current_value => 100.0      , defined => $FALSE},
+    default_metrics  => { option => "-dm", no_of_arguments => 1, data_type => "onoff"   , current_value => "off"      , defined => $FALSE},
+    ignore_metrics   => { option => "-im", no_of_arguments => 1, data_type => "metric_names", current_value => undef, defined => $FALSE},
+    verbose          => { option => "--verbose" , no_of_arguments => 1, data_type => "onoff"  , current_value => "off" , defined => $FALSE},
+    warnings         => { option => "--warnings" , no_of_arguments => 1, data_type => "onoff"  , current_value => "on" , defined => $FALSE},
+    debug            => { option => "--debug" , no_of_arguments => 1, data_type => "size"  , current_value => "off" , defined => $FALSE},
+    quiet            => { option => "--quiet" , no_of_arguments => 1, data_type => "onoff"   , current_value => "off"      , defined => $FALSE},
+  );
+
+my %g_debug_size = 
+  (
+    "on"  => $FALSE,
+    "s"   => $FALSE,
+    "m"   => $FALSE,
+    "l"   => $FALSE,
+    "xl"  => $FALSE,
+  );
+
+my %local_system_config =
+  (
+    kernel_name       => "undefined",
+    nodename          => "undefined",
+    kernel_release    => "undefined",
+    kernel_version    => "undefined",
+    machine           => "undefined",
+    processor         => "undefined",
+    hardware_platform => "undefined",
+    operating_system  => "undefined",
+    hostname_current  => "undefined",
+  );
+
+# Note that we use single quotes here, because regular expressions wreak havoc otherwise.
+
+my %g_arch_specific_settings =
+  (
+    arch_supported  => $FALSE,
+    arch            => 'undefined',
+    regex           => 'undefined',
+    subexp          => 'undefined',
+    linksubexp      => 'undefined',
+  );
+
+my %g_locale_settings = (
+  LANG              => "en_US.UTF-8",
+  decimal_separator => "\\.",
+  covert_to_dot     => $FALSE
+);
 
 #------------------------------------------------------------------------------
-# This is cosmetic, but helps with the scoping of variables.
+# See this page for a nice overview with the colors:
+# https://www.w3schools.com/colors/colors_groups.asp
 #------------------------------------------------------------------------------
 
-  main ();
+my %g_html_color_scheme = (
+  "control_flow"  => "Brown",
+  "target_function_name" => "Red",
+  "non_target_function_name" => "BlueViolet",
+  "background_color_hot" => "PeachPuff",
+  "background_color_lukewarm" => "LemonChiffon",
+  "link_outside_range" => "Crimson",
+  "error_message" => "LightPink",
+  "background_color_page" => "White",
+#  "background_color_page" => "LightGray",
+  "background_selected_sort" => "LightSlateGray",
+  "index" => "Lavender",
+);
 
-  exit (0);
+#------------------------------------------------------------------------------
+# These are the base names for the HTML files that are generated.
+#------------------------------------------------------------------------------
+my %g_html_base_file_name = (
+  "caller_callee"  => "caller-callee",
+  "disassembly" => "dis",
+  "experiment_info"  => "experiment-info",
+  "function_view"  => "function-view-sorted",
+  "index" => "index",
+  "source" => "src",
+  "warnings" => "warnings",
+);
 
 #------------------------------------------------------------------------------
-#                             THE SUBROUTINES
+# This is cosmetic, but helps with the scoping of variables.
 #------------------------------------------------------------------------------
+  main ();
+
+  exit (0);
 
 #------------------------------------------------------------------------------
 # This is the driver part of the program.
 #------------------------------------------------------------------------------
-sub
-main
+sub main
 {
-  my $subr_name = "main";
-  my $ignore_value; 
+  my $subr_name = get_my_name ();
 
 #------------------------------------------------------------------------------
-# If no options are given, print the help info and exit.
+# The name of the configuration file.
 #------------------------------------------------------------------------------
-  $ignore_value = early_scan_specific_options();
+  my $rc_file_name = ".gp-display-html.rc";
 
-  $ignore_value = be_patient (); 
+#------------------------------------------------------------------------------
+# OS commands executed and search paths.
+#------------------------------------------------------------------------------
+  my @selected_os_cmds = qw (rm mv cat hostname locale which printenv ls 
+                             uname readelf mkdir);
+  my @search_paths_os_cmds = qw (/usr/bin /bin);
 
-  return (0);
+#------------------------------------------------------------------------------
+# TBD: Eliminate these.
+#------------------------------------------------------------------------------
+  my $ARCHIVES_MAP_NAME;
+  my $ARCHIVES_MAP_VADDR;
 
-} #-- End of subroutine main
+#------------------------------------------------------------------------------
+# Local structures (hashes and arrays).
+#------------------------------------------------------------------------------
+  my @exp_dir_list; # List with experiment directories
+  my @metrics_data;
 
-sub
-be_patient
-{
-  print "Functionality not implemented yet - please stay tuned for updates\n";
+  my %function_address_info = ();
+  my $function_address_info_ref; 
+
+  my @function_info = ();
+  my $function_info_ref;
+
+  my %function_address_and_index = ();
+  my $function_address_and_index_ref;
+
+  my %addressobjtextm = ();
+  my $addressobjtextm_ref;
 
-} #-- End of subroutine be_patient
+  my %addressobj_index = ();
+  my $addressobj_index_ref;
+
+  my %LINUX_vDSO = ();
+  my $LINUX_vDSO_ref;
+
+  my %function_view_structure = ();
+  my $function_view_structure_ref;
+
+  my %elf_rats = ();
+  my $elf_rats_ref;
 
 #------------------------------------------------------------------------------
-# Prints the version number and license information.
+# Local variables.
 #------------------------------------------------------------------------------
-sub 
-print_version_info 
-{
-  print "$version_info\n";
-  print "Copyright (C) 2021 Free Software Foundation, Inc.\n";
-  print "License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.\n";
-  print "This is free software: you are free to change and redistribute it.\n";
-  print "There is NO WARRANTY, to the extent permitted by law.\n";
+  my $abs_path_outputdir; 
+  my $archive_dir_not_empty;
+  my $base_va_executable; 
+  my $executable_name;
+  my $exp_dir_list_ref;
+  my $found_exp_dir;
+  my $ignore_value;
+  my $message;
+  my $number_of_metrics;
+  my $va_executable_in_hex;
 
-  return (0);
+  my $failed_command_mappings; 
+  my $option_errors;
+  my $total_user_errors;
 
-} #-- End of subroutine print_version_info
+  my $script_pc_metrics; 
+  my $dir_check_errors;
+  my $consistency_errors;
+  my $outputdir;
+  my $return_code;
 
-#-------------------------------------------------------------------------------
-# Print the help overview
-#-------------------------------------------------------------------------------
-sub 
-print_help_info 
-{
-  print
-    "Usage: $driver_cmd [OPTION(S)] EXPERIMENT(S)\n".
-    "\n".
-    "Process one or more experiments to generate a directory containing an index.html\n".
-    "file that can be used to browse the experiment data\n".
-    "\n".
-    "Options:\n".
-    "\n".
-    " --help              print usage information and exit.\n".
-    " --version           print the version number and exit.\n".
-    " --verbose {on|off}  enable (on) or disable (off) verbose mode; the default is \"off\".\n".
-    "\n".
-    "\n".
-    " -o, --output <dir-name>  use <dir-name> to store the results in; the default\n".
-    "                           name is ./display.<n>.html with <n> the first number\n".
-    "                           not in use; an existing directory is not overwritten.\n".
-    "\n".
-    " -O, --overwrite <dir-name>  use <dir-name> to store the results in and overwrite\n".
-    "                              any existing directory with the same name; make sure\n".
-    "                              that umask is set to the correct access permissions.\n".
-    "\n".
-    " -fl, --func_limit <limit>  impose a limit on the number of functions processed;\n".
-    "                             this is an integer number; set to 0 to process all\n".
-    "                             functions; the default value is 100.\n".
-    "\n".
-    "  -ct, --calltree {on|off}  enable or disable an html page with a call tree linked\n".
-    "                             from the bottom of the first page; default is off.\n".
-    "\n".
-    "  -tp, --threshold_percentage <percentage>  provide a percentage of metric accountability; the\n".
-    "                                             inclusion of functions for each metric will take\n".
-    "                                             place in sort order until the percentage has been\n".
-    "                                             reached.\n".
-    "\n".
-    "  -dm, --default_metrics {on|off}  enable or disable automatic selection of metrics\n".
-    "                                   and use a default set of metrics; the default is off.\n".
-    "\n".
-    "  -im, --ignore_metrics <metric-list>  ignore the metrics from <metric-list>.\n".
-    "\n".
-    "  -db, --debug {on|off}  enable/disable debug mode; print detailed information to assist with troubleshooting\n".
-    "                          or further development of this tool; default is off.\n".
-    "\n".
-    "  -q, --quiet {on|off}  disable/enable the display of warnings; default is off.\n".
-    "\n".
-    "Environment:\n".
-    "\n".
-    "The options can be set in a configuration file called .gp-display-html.rc. This\n".
-    "file needs to be either in the current directory, or in the home directory of the user.\n".
-    "The long name of the option without the leading dashes is supported. For example calltree\n".
-    "to enable or disable the call tree. Note that some options take a value. In case the same option\n".
-    "occurs multiple times in this file, only the last setting encountered is preserved.\n".
-    "\n".
-    "Documentation:\n".
-    "\n".
-    "A getting started guide for gprofng is maintained as a Texinfo manual. If the info and\n".
-    "gprofng programs are properly installed at your site, the command \"info gprofng\"\n".
-    "should give you access to this document.\n".
-    "\n".
-    "See also:\n".
-    "\n".
-    "gprofng(1), gp-archive(1), gp-collect-app(1), gp-display-src(1), gp-display-text(1)\n";
+  my $decimal_separator;
+  my $convert_to_dot;
+  my $architecture_supported;
+  my $elf_arch;
+  my $elf_support;
+  my $home_dir;
+  my $elf_loadobjects_found; 
 
-    return (0);
+  my $rc_file_paths_ref;
+  my @rc_file_paths = ();
+  my $rc_file_errors = 0;
 
-} #-- End of subroutine print_help_info
+  my @sort_fields = ();
+  my $summary_metrics;
+  my $call_metrics;
+  my $user_metrics;
+  my $system_metrics;
+  my $wall_metrics;
+  my $detail_metrics;
+  my $detail_metrics_system; 
+
+  my $pretty_dir_list; 
+
+  my %metric_value       = ();
+  my %metric_description = ();
+  my %metric_description_reversed = ();
+  my %metric_found = ();
+  my %ignored_metrics = ();
+
+  my $metric_value_ref;
+  my $metric_description_ref;
+  my $metric_found_ref;
+  my $ignored_metrics_ref;
+
+  my @table_execution_stats = ();
+  my $table_execution_stats_ref;
+
+  my $html_first_metric_file_ref;
+  my $html_first_metric_file;
+
+  my $arch;
+  my $subexp;
+  my $linksubexp;
+
+  my $setting_for_LANG;
+  my $time_percentage_multiplier;
+  my $process_all_functions;
+
+  my $selected_archive;
 
 #------------------------------------------------------------------------------
-# Scan the command line for specific options.
+# If no options are given, print the help info and exit.
 #------------------------------------------------------------------------------
-sub
-early_scan_specific_options
-{
-  my $subr_name = "early_scan_specific_options";
+  if ($#ARGV == -1)
+    {
+      $ignore_value = print_help_info (); 
+      return (0);
+    }
 
-  my $ignore_value;
-  my $found_option;
-  my $option_has_value;
-  my $option_value;
+#------------------------------------------------------------------------------
+# This part is like a preamble.  Before we continue we need to figure out some 
+# things that are needed later on.
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# The very first thing to do is to quickly determine if the user has enabled 
+# one of the following options and take action accordingly:
+# --version, --verbose, --debug, --quiet
+#
+# This avoids that there is a gap between the start of the execution and the
+# moment the options are parsed, checked, and interpreted.
+#
+# When parsing the full command line, these options will be more extensively
+# checked and also updated in %g_user_settings
+
+# Note that a confirmation message, if any, is printed here and not when the 
+# options are parsed and processed.
+#------------------------------------------------------------------------------
+
+  $g_verbose  = $g_user_settings{"verbose"}{"current_value"} eq "on" ? $TRUE : $FALSE;
+  $g_warnings = $g_user_settings{"warnings"}{"current_value"} eq "on" ? $TRUE : $FALSE;
+  $g_quiet    = $g_user_settings{"quiet"}{"current_value"} eq "on" ? $TRUE : $FALSE;
 
-  my $verbose_setting = $FALSE;
-  my $debug_setting   = $FALSE;
-  my $quiet_setting   = $FALSE;
+  $ignore_value = early_scan_specific_options ();
 
-  $option_has_value = $FALSE;
-  ($found_option, $option_value) = find_target_option (\@ARGV, $option_has_value, "--version");
-  if ($found_option)
+#------------------------------------------------------------------------------
+# The next subroutine is executed early to ensure the OS commands we need are 
+# available.
+#
+# This subroutine stores the commands and the full path names as an associative
+# array called "g_mapped_cmds".  The command is the key and the value is the full 
+# path.  For example: ("uname", /usr/bin/uname).
+#------------------------------------------------------------------------------
+  $failed_command_mappings = check_and_define_cmds (\@selected_os_cmds, \@search_paths_os_cmds);
+
+  if ($failed_command_mappings == 0)
     {
-      $ignore_value = print_version_info ();
-      exit(0);
+      gp_message ("debug", $subr_name, "verified the OS commands");
     }
-  $option_has_value = $FALSE;
-  ($found_option, $option_value) = find_target_option (\@ARGV, $option_has_value, "--help");
-  if ($found_option)
+  else
     {
-      $ignore_value = print_help_info ();
-      exit(0);
+      my $msg = "failure in the verification of the OS commands";
+      gp_message ("assertion", $subr_name, $msg);
     }
 
-  return (0);
+#------------------------------------------------------------------------------
+# Get the home directory and the locations for the configuration file on the 
+# current system.
+#------------------------------------------------------------------------------
+  ($home_dir, $rc_file_paths_ref) = get_home_dir_and_rc_path ($rc_file_name);
 
-} #-- End of subroutine early_scan_specific_options
+  @rc_file_paths = @{ $rc_file_paths_ref };
+  gp_message ("debug", $subr_name, "the home directory is $home_dir");
+  gp_message ("debugXL", $subr_name, "the search path for the rc file is @rc_file_paths");
+
+  $pretty_dir_list = build_pretty_dir_list (\@rc_file_paths);
 
 #------------------------------------------------------------------------------
-# Scan the command line to see if the specified option is present.
+# Get the ball rolling.  Parse and interpret the configuration file (if any)
+# and the command line options.
 #
-# Two types of options are supported: options without value (e.g. --help) or
-# those that are set to "on" or "off".
+# If either $rc_file_errors or $total_user_errors, or both, are non-zero it
+# means a fatal error has occured. In this case, all error messages are 
+# printed and execution is terminated.
+#
+# Note that the verbose, debug, and quiet options can be set in this file.
+# It is a deliberate choice to ignore these for now.  The assumption is that
+# the user will not be happy if we ignore the command line settings for a
+# while.
 #------------------------------------------------------------------------------
-sub
-find_target_option
-{
-  my ($command_line_ref, $has_value, $target_option) = @_;
 
-  my @command_line = @{ $command_line_ref };
+  gp_message ("debugXL", $subr_name, "processing of the rc file disabled for now");
+
+# Temporarily disabled  print_table_user_settings ("debugXL", "before function process_rc_file");
+# Temporarily disabled
+# Temporarily disabled  $rc_file_errors = process_rc_file ($rc_file_name, $rc_file_paths_ref);
+# Temporarily disabled  
+# Temporarily disabled  if ($rc_file_errors != 0)
+# Temporarily disabled    {
+# Temporarily disabled      $message = "fatal errors in file $rc_file_name encountered";
+# Temporarily disabled      gp_message ("debugXL", $subr_name, $message);
+# Temporarily disabled    }
+# Temporarily disabled
+# Temporarily disabled  print_table_user_settings ("debugXL", "after function process_rc_file");
+
+#------------------------------------------------------------------------------
+# Get the ball rolling. Parse and interpret the options.  Some first checks
+# are performed.
+#
+# Instead of bailing out on the first user error, we capture all errors, print
+# messages and then bail out. This is more user friendly.
+#------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Parse the user options");
+
+  $total_user_errors = 0;
 
-  my ($command_line_string) = join(" ", @command_line);
+  ($option_errors, $found_exp_dir, $exp_dir_list_ref) = parse_and_check_user_options (
+                                                          \$#ARGV, 
+                                                          \@ARGV);
+  $total_user_errors += $option_errors;
 
-  my $option_value = "not set";
-  my $found_option = $FALSE;
+#------------------------------------------------------------------------------
+# Dynamically load the modules needed.  If a module is not available, print 
+# an error message and bail out.
+#
+# This call replaces the following:
+#
+# use feature qw (state);
+# use List::Util qw (min max);
+# use Cwd;
+# use File::Basename;
+# use File::stat;
+# use POSIX;
+# use bignum;
+#
+# Note that this check cannot be done earlier, because in case of a missing 
+# module, the man page would not be generated if the code ends prematurely
+# in case the --help and --version options are used..
+#------------------------------------------------------------------------------
+  my ($module_errors_ref, $missing_modules_ref) = handle_module_availability ();
+ 
+  my $module_errors = ${ $module_errors_ref };
 
-  if ($command_line_string =~ /\s*($target_option)\s*(on|off)*\s*/)
+  if ($module_errors > 0)
     {
-      if ($has_value)
+      my $msg;
+
+      my $plural_or_single = ($module_errors > 1) ? "modules are" : "module is";
+      my @missing_modules = @{ $missing_modules_ref };
+
+      for my $i (0 .. $#missing_modules)
         {
+          $msg = "module $missing_modules[$i] is missing";
+          gp_message ("error", $subr_name, $msg);
+        }
+      
+      $msg = $module_errors . " " . $plural_or_single  .
+             "missing - execution is terminated";
+      gp_message ("abort", $subr_name, $msg);
+    }
+
 #------------------------------------------------------------------------------
-# We are looking for this kind if substring: "--verbose on"
+# The user options have been taken in.  Check for validity and consistency.
 #------------------------------------------------------------------------------
-          if (defined($1) and defined($2))
-            {
-              if ( ($2 eq "on") or ($2 eq "off") )
-                {
-                  $found_option = $TRUE;
-                  $option_value = $2;
-                }
-            }
+  gp_message ("verbose", $subr_name, "Process user options");
+
+  ($option_errors, $ignored_metrics_ref, $outputdir, 
+   $time_percentage_multiplier, $process_all_functions,
+   $exp_dir_list_ref) = process_user_options ($exp_dir_list_ref);
+
+  @exp_dir_list = @{ $exp_dir_list_ref };
+  %ignored_metrics = %{$ignored_metrics_ref};
+
+  $total_user_errors += $option_errors;
+
+#------------------------------------------------------------------------------
+# If no option is given for the output directory, pick a default.  Otherwise,
+# if the output directory exists, wipe it clean in case the -O option is used.
+# If not, flag an error because the -o option does not overwrite an existing
+# directory.
+#------------------------------------------------------------------------------
+  if ($total_user_errors == 0)
+    {
+      ($option_errors, $outputdir) = set_up_output_directory ();
+      $abs_path_outputdir = cwd () . "/" . $outputdir;
+      $total_user_errors += $option_errors;
+    }
+
+  if ($total_user_errors == 0)
+    {
+      gp_message ("debug", $subr_name, "the output directory is $outputdir");
+    }
+  else
+    {
+#------------------------------------------------------------------------------
+# All command line errors and warnings are printed here.
+#------------------------------------------------------------------------------
+      my $plural_or_single = ($total_user_errors > 1) ? "errors have" : "error has";
+      $message  =  $g_error_keyword;
+      $message .=  $total_user_errors;
+      if ($rc_file_errors > 0)
+        {
+          $message .=  " additional";
         }
-      else
+      $message .=  " fatal input $plural_or_single been detected:";
+      gp_message ("error", $subr_name, $message);
+      for my $key (keys @g_user_input_errors)
         {
+          gp_message ("error", $subr_name, "$g_error_keyword  $g_user_input_errors[$key]");
+        }
+    }
+
 #------------------------------------------------------------------------------
-# We are looking for this kind if substring: "--help"
+# Bail out in case fatal errors have occurred.
 #------------------------------------------------------------------------------
-          if (defined($1))
-            {
-              $found_option = $TRUE;
-            }
-        }
+  if ( ($rc_file_errors + $total_user_errors) > 0)
+    {
+      my $msg = "the current values for the user controllable settings";
+      print_user_settings ("debug", $msg);
+
+      gp_message ("abort", $subr_name, "execution terminated");
     }
+  else
+    {
+      my $msg = "after parsing the user options, the final values are";
+      print_user_settings ("debug", $msg);
 
-  return($found_option, $option_value);
+#------------------------------------------------------------------------------
+# TBD: Enable once all planned features have been implemented and tested.
+#------------------------------------------------------------------------------
+# Temporarily disabled      $msg = "the final values for the user controllable settings";
+# Temporarily disabled      print_table_user_settings ("verbose", $msg);
+    }
 
-} #-- End of subroutine find_target_option
+#------------------------------------------------------------------------------
+# Print a list with the experiment directory names
+#------------------------------------------------------------------------------
+  $pretty_dir_list = build_pretty_dir_list (\@exp_dir_list);
+
+  my $plural = ($#exp_dir_list > 0) ? "directories are" : "directory is";
+
+  gp_message ("verbose", $subr_name, "The experiment " . $plural . ":");
+  gp_message ("verbose", $subr_name, $pretty_dir_list);
+
+#------------------------------------------------------------------------------
+# Set up the first entry with the meta data for the experiments.  This field
+# contains the absolute paths to the experiment directories.
+#------------------------------------------------------------------------------
+  for my $exp_dir (@exp_dir_list)
+    {
+     my ($filename, $directory_path, $ignore_suffix) = fileparse ($exp_dir);
+     gp_message ("debug", $subr_name, "exp_dir = $exp_dir"); 
+     gp_message ("debug", $subr_name, "filename = $filename"); 
+     gp_message ("debug", $subr_name, "directory_path = $directory_path"); 
+     $g_exp_dir_meta_data{$filename}{"directory_path"} = $directory_path; 
+    }
+
+#------------------------------------------------------------------------------
+# Check whether the experiment directories are valid.  If not, it is a fatal
+# error.
+# Upon successful return, one directory has been selected to be used in the
+# remainder.  This is not always the correct thing to do, but is the same as
+# the original code.  In due time this should be addressed though.
+#------------------------------------------------------------------------------
+  ($dir_check_errors, $archive_dir_not_empty, $selected_archive, 
+   $elf_rats_ref) = check_validity_exp_dirs ($exp_dir_list_ref);
+
+  if ($dir_check_errors)
+    {
+      gp_message ("abort", $subr_name, "execution terminated");
+    }
+  else
+    {
+      gp_message ("verbose", $subr_name, "The experiment directories have been verified and are valid");
+    }
+
+  %elf_rats = %{$elf_rats_ref};
+
+#-------------------------------------------------------------------------------
+# Now that we know the map.xml file(s) are present, we can scan these and get
+# the required information.  This includes setting the base virtual address.
+#-------------------------------------------------------------------------------
+  $ignore_value = determine_base_virtual_address ($exp_dir_list_ref);
+
+#------------------------------------------------------------------------------
+# Check whether the experiment directories are consistent.
+#------------------------------------------------------------------------------
+  ($consistency_errors, $executable_name) = verify_consistency_experiments ($exp_dir_list_ref);
+
+  if ($consistency_errors == 0)
+    {
+      gp_message ("verbose", $subr_name, "The experiment directories are consistent");
+    }
+  else
+    {
+      gp_message ("abort", $subr_name, "number of consistency errors detected: $consistency_errors"); 
+    }
+
+#------------------------------------------------------------------------------
+# The directories are consistent.  We can now set the base virtual address of
+# the executable.
+#------------------------------------------------------------------------------
+  $base_va_executable = $g_exp_dir_meta_data{$selected_archive}{"va_base_in_hex"}; 
+
+  gp_message ("debug", $subr_name, "executable_name    = $executable_name");
+  gp_message ("debug", $subr_name, "selected_archive = $selected_archive");
+  gp_message ("debug", $subr_name, "base_va_executable = $base_va_executable");
+
+#------------------------------------------------------------------------------
+# The gp-display-text tool is critical and has to be available in order to proceed.
+#------------------------------------------------------------------------------
+  $ignore_value = check_availability_tool ();
+
+  ($return_code, $decimal_separator, $convert_to_dot) = 
+                                                determine_decimal_separator ();
+
+  if ($return_code == 0)
+    {
+      my $txt  = "decimal separator is $decimal_separator " . 
+                 "(conversion to dot is " .
+                 ($convert_to_dot == $TRUE ? "enabled" : "disabled").")";
+      gp_message ("debugXL", $subr_name, $txt);
+    }
+  else
+    {
+      my $msg = "the decimal separator can not be determined - set to $decimal_separator";
+      gp_message ("warning", $subr_name, $msg);
+    }
+
+#------------------------------------------------------------------------------
+# Collect and store the system information.
+#------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Collect system information and adapt settings");
+
+  $return_code = get_system_config_info (); 
+
+#------------------------------------------------------------------------------
+# The 3 variables below are used in the remainder.
+#
+# The output from "uname -p" is recommended to be used for the ISA.
+#------------------------------------------------------------------------------
+  my $hostname_current = $local_system_config{hostname_current};
+  my $arch_uname_s     = $local_system_config{kernel_name};
+  my $arch_uname       = $local_system_config{processor};
+
+  gp_message ("debug", $subr_name, "set hostname_current = $hostname_current");
+  gp_message ("debug", $subr_name, "set arch_uname_s     = $arch_uname_s");
+  gp_message ("debug", $subr_name, "set arch_uname       = $arch_uname");
+
+#-------------------------------------------------------------------------------
+# This function also sets the values in "g_arch_specific_settings".  This 
+# includes several definitions of regular expressions.
+#-------------------------------------------------------------------------------
+  ($architecture_supported, $elf_arch, $elf_support) = 
+                     set_system_specific_variables ($arch_uname, $arch_uname_s);
+
+  gp_message ("debug", $subr_name, "architecture_supported = $architecture_supported");
+  gp_message ("debug", $subr_name, "elf_arch               = $elf_arch");
+  gp_message ("debug", $subr_name, "elf_support            = ".($elf_arch ? "TRUE" : "FALSE"));
+
+  for my $feature (sort keys %g_arch_specific_settings)
+    {
+      gp_message ("debug", $subr_name, "g_arch_specific_settings{$feature} = $g_arch_specific_settings{$feature}");
+    }
+
+  $arch       = $g_arch_specific_settings{"arch"};
+  $subexp     = $g_arch_specific_settings{"subexp"};
+  $linksubexp = $g_arch_specific_settings{"linksubexp"};
+
+  $g_locale_settings{"LANG"} =  get_LANG_setting ();
+
+  gp_message ("debugXL", $subr_name, "after get_LANG_setting: LANG = $g_locale_settings{'LANG'}");
+
+#------------------------------------------------------------------------------
+# Temporarily reset selected settings since these are not yet implemented.
+#------------------------------------------------------------------------------
+  $ignore_value = reset_selected_settings ();
+
+#------------------------------------------------------------------------------
+# TBD: Revisit. Is this really necessary?
+#------------------------------------------------------------------------------
+
+  ($executable_name, $va_executable_in_hex) = check_loadobjects_are_elf ($selected_archive);
+  $elf_loadobjects_found = $TRUE;
+
+# TBD: Hack and those ARCHIVES_ names can be eliminated
+  $ARCHIVES_MAP_NAME  = $executable_name;
+  $ARCHIVES_MAP_VADDR = $va_executable_in_hex;
+  gp_message ("debugXL", $subr_name, "hack ARCHIVES_MAP_NAME  = $ARCHIVES_MAP_NAME");
+  gp_message ("debugXL", $subr_name, "hack ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR");
+
+  gp_message ("debugXL", $subr_name, "after call to check_loadobjects_are_elf forced elf_loadobjects_found = $elf_loadobjects_found");
+  
+  $g_html_credits_line = ${ create_html_credits () };
+  gp_message ("debugXL", $subr_name, "g_html_credits_line = $g_html_credits_line");
+#------------------------------------------------------------------------------
+# Add a "/" to simplify the construction of path names in the remainder.
+#
+# TBD: Push this into a subroutine(s).
+#------------------------------------------------------------------------------
+  $outputdir = append_forward_slash ($outputdir);
+
+  gp_message ("debug", $subr_name, "prepared outputdir = $outputdir");
+
+#------------------------------------------------------------------------------
+#------------------------------------------------------------------------------
+# ******* TBD: e.system not available on Linux!!
+#------------------------------------------------------------------------------
+#------------------------------------------------------------------------------
+
+##  my $summary_metrics       = 'e.totalcpu';
+  $detail_metrics        = 'e.totalcpu';
+  $detail_metrics_system = 'e.totalcpu:e.system';
+  $call_metrics          = 'a.totalcpu';
+
+  my $cmd_options; 
+  my $metrics_cmd;
+
+  my $outfile1      = $outputdir   ."metrics";
+  my $outfile2      = $outputdir . "metrictotals";
+  my $gp_error_file = $outputdir . $g_gp_error_logfile;
+
+#------------------------------------------------------------------------------
+# Execute the $GP_DISPLAY_TEXT tool with the appropriate options.  The goal is
+# to get all the output in files $outfile1 and $outfile2.  These are then
+# parsed.
+#------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Gather the metrics data from the experiments");
+
+  $return_code = get_metrics_data (\@exp_dir_list, $outputdir, $outfile1, $outfile2, $gp_error_file);
+
+  if ($return_code != 0)
+    {
+      gp_message ("abort", $subr_name, "execution terminated");
+    }
+
+#------------------------------------------------------------------------------
+# TBD: Test this code
+#------------------------------------------------------------------------------
+  open (METRICS, "<", $outfile1) 
+    or die ("$subr_name - unable to open metric value data file $outfile1 for reading: '$!'");
+  gp_message ("debug", $subr_name, "opened file $outfile1 for reading");
+
+  chomp (@metrics_data = <METRICS>);
+  close (METRICS);
+
+  for my $i (keys @metrics_data)
+    {
+      gp_message ("debugXL", $subr_name, "metrics_data[$i] = $metrics_data[$i]");
+    }
+
+#------------------------------------------------------------------------------
+# Process the generated metrics data.
+#------------------------------------------------------------------------------
+  if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
+
+#------------------------------------------------------------------------------
+# The metrics will be derived from the experiments.
+#------------------------------------------------------------------------------
+    {
+      gp_message ("verbose", $subr_name, "Process the metrics data");
+
+      ($metric_value_ref, $metric_description_ref, $metric_found_ref, 
+       $user_metrics, $system_metrics, $wall_metrics,
+       $summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics
+       ) = process_metrics_data ($outfile1, $outfile2, \%ignored_metrics);
+
+      %metric_value                = %{ $metric_value_ref };
+      %metric_description          = %{ $metric_description_ref };
+      %metric_found                = %{ $metric_found_ref };
+      %metric_description_reversed = reverse %metric_description;
+
+      gp_message ("debugXL", $subr_name, "after the call to process_metrics_data");
+      for my $metric (sort keys %metric_value)
+        {
+          gp_message ("debugXL", $subr_name, "metric_value{$metric} = $metric_value{$metric}");
+        }
+      for my $metric (sort keys %metric_description)
+        {
+          gp_message ("debugXL", $subr_name, "metric_description{$metric} = $metric_description{$metric}");
+        }
+      gp_message ("debugXL", $subr_name, "user_metrics   = $user_metrics");
+      gp_message ("debugXL", $subr_name, "system_metrics = $system_metrics");
+      gp_message ("debugXL", $subr_name, "wall_metrics   = $wall_metrics");
+    }
+  else
+    {
+#------------------------------------------------------------------------------
+# A default set of metrics will be used.
+#
+# TBD: These should be OS dependent.
+#------------------------------------------------------------------------------
+      gp_message ("verbose", $subr_name, "Select the set of default metrics"); 
+
+      ($metric_description_ref, $metric_found_ref, $summary_metrics, 
+       $detail_metrics, $detail_metrics_system, $call_metrics
+       ) = set_default_metrics ($outfile1, \%ignored_metrics);
+
+
+      %metric_description          = %{ $metric_description_ref };
+      %metric_found                = %{ $metric_found_ref };
+      %metric_description_reversed = reverse %metric_description;
+
+      gp_message ("debug", $subr_name, "after the call to set_default_metrics");
+
+    }
+
+  $number_of_metrics = split (":", $summary_metrics);
+
+  gp_message ("debugXL", $subr_name, "summary_metrics       = $summary_metrics");
+  gp_message ("debugXL", $subr_name, "detail_metrics        = $detail_metrics");
+  gp_message ("debugXL", $subr_name, "detail_metrics_system = $detail_metrics_system");
+  gp_message ("debugXL", $subr_name, "call_metrics          = $call_metrics");
+  gp_message ("debugXL", $subr_name, "number_of_metrics = $number_of_metrics");
+
+#------------------------------------------------------------------------------
+# TBD Find a way to better handle this situation:
+#------------------------------------------------------------------------------
+  for my $im (keys %metric_found)
+    {
+      gp_message ("debugXL", $subr_name, "metric_found{$im} = $metric_found{$im}");
+    }
+  for my $im (keys %ignored_metrics)
+    {
+      if (not exists ($metric_found{$im}))
+        {
+          gp_message ("debugXL", $subr_name, "user requested ignored metric (-im) $im does not exist in collected metrics");
+        }
+    }
+
+#------------------------------------------------------------------------------
+# Get the information on the experiments.
+#------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Generate the experiment information");
+  
+  my $exp_info_file_ref;
+  my $exp_info_file;
+  my $exp_info_ref;
+  my @exp_info;
+
+  my $experiment_data_ref;
+
+  $experiment_data_ref = get_experiment_info (\$outputdir, \@exp_dir_list);
+  my @experiment_data = @{ $experiment_data_ref };
+
+  for my $i (sort keys @experiment_data)
+    {
+      my $msg = "i = $i " . $experiment_data[$i]{"exp_id"} . " => " . 
+                $experiment_data[$i]{"exp_name_full"};
+      gp_message ("debugM", $subr_name, $msg);
+    }
+
+  $experiment_data_ref = process_experiment_info ($experiment_data_ref);
+  @experiment_data = @{ $experiment_data_ref };
+
+  for my $i (sort keys @experiment_data)
+    {
+      for my $fields (sort keys %{ $experiment_data[$i] })
+        {
+          my $msg = "i = $i experiment_data[$i]{$fields} = " .
+                    $experiment_data[$i]{$fields};
+          gp_message ("debugXL", $subr_name, $msg);
+        }
+    }
+
+  @g_html_experiment_stats = @{ create_exp_info (
+                                  \@exp_dir_list,
+                                  \@experiment_data) };
+
+  $table_execution_stats_ref = html_generate_exp_summary (
+                                 \$outputdir, 
+                                 \@experiment_data);
+  @table_execution_stats = @{ $table_execution_stats_ref };
+
+#------------------------------------------------------------------------------
+# Get the function overview.
+#------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Generate the list with functions executed");
+
+  my ($outfile, $sort_fields_ref) = get_hot_functions (\@exp_dir_list, $summary_metrics, $outputdir);
+
+  @sort_fields = @{$sort_fields_ref};
+
+#------------------------------------------------------------------------------
+# Parse the output from the fsummary command and store the relevant data for
+# all the functions listed there.
+#------------------------------------------------------------------------------
+
+  gp_message ("verbose", $subr_name, "Analyze and store the relevant function information");
+
+  ($function_info_ref, $function_address_and_index_ref, $addressobjtextm_ref, 
+   $LINUX_vDSO_ref, $function_view_structure_ref) = get_function_info ($outfile);
+
+  @function_info              = @{ $function_info_ref };
+  %function_address_and_index = %{ $function_address_and_index_ref };
+  %addressobjtextm            = %{ $addressobjtextm_ref };
+  %LINUX_vDSO                 = %{ $LINUX_vDSO_ref };
+  %function_view_structure    = %{ $function_view_structure_ref };
+
+  for my $keys (0 .. $#function_info)
+    {
+      for my $fields (keys %{$function_info[$keys]})
+        {
+          gp_message ("debugXL", $subr_name,"$keys $fields $function_info[$keys]{$fields}");
+        }
+    }
+
+  for my $i (keys %addressobjtextm)
+    {
+      gp_message ("debugXL", $subr_name,"addressobjtextm{$i} = $addressobjtextm{$i}");
+    }
+
+  gp_message ("verbose", $subr_name, "Generate the files with function overviews and the callers-callees information"); 
+
+  $script_pc_metrics = generate_function_level_info (\@exp_dir_list, 
+                                                     $call_metrics, 
+                                                     $summary_metrics, 
+                                                     $outputdir, 
+                                                     $sort_fields_ref);
+
+  gp_message ("verbose", $subr_name, "Preprocess the files with the function level information");
+
+  $ignore_value = preprocess_function_files (
+                    $metric_description_ref, 
+                    $script_pc_metrics, 
+                    $outputdir, 
+                    \@sort_fields);
+
+  gp_message ("verbose", $subr_name, "For each function, generate a set of files");
+
+  ($function_info_ref, $function_address_info_ref, $addressobj_index_ref) = process_function_files (
+                                                                            \@exp_dir_list,
+                                                                            $executable_name,
+                                                                            $time_percentage_multiplier,
+                                                                            $summary_metrics,
+                                                                            $process_all_functions,
+                                                                            $elf_loadobjects_found, 
+                                                                            $outputdir, 
+                                                                            \@sort_fields, 
+                                                                            \@function_info, 
+                                                                            \%function_address_and_index,
+                                                                            \%LINUX_vDSO,
+                                                                            \%metric_description,
+                                                                            $elf_arch,
+                                                                            $base_va_executable,
+                                                                            $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR, \%elf_rats);
+
+  @function_info         = @{ $function_info_ref };
+  %function_address_info = %{ $function_address_info_ref };
+  %addressobj_index      = %{ $addressobj_index_ref };
+
+#-------------------------------------------------------------------------------------
+# Parse the disassembly information and generate the html files.
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Parse the disassembly files and generate the html files");
+
+  $ignore_value = parse_dis_files (\$number_of_metrics, \@function_info, 
+                   \%function_address_and_index,
+                   \$outputdir, \%addressobj_index);
+
+#-------------------------------------------------------------------------------------
+# Parse the source information and generate the html files.
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Parse the source files and generate the html files");
+
+  parse_source_files (\$number_of_metrics, \@function_info, \$outputdir);
+
+#-------------------------------------------------------------------------------------
+# Parse the caller-callee information and generate the html files.
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Process the caller-callee information and generate the html file");
+
+#-------------------------------------------------------------------------------------
+# Generate the caller-callee information.
+#-------------------------------------------------------------------------------------
+  $ignore_value = generate_caller_callee (
+                    \$number_of_metrics, 
+                    \@function_info, 
+                    \%function_view_structure,
+                    \%function_address_info, 
+                    \%addressobjtextm, 
+                    \$outputdir);
+
+#-------------------------------------------------------------------------------------
+# Parse the calltree information and generate the html files.
+#-------------------------------------------------------------------------------------
+  if ($g_user_settings{"calltree"}{"current_value"} eq "on")
+    {
+      my $msg = "Process the call tree information and generate the html file";
+      gp_message ("verbose", $subr_name, $msg);
+
+      $ignore_value = process_calltree (
+                        \@function_info, 
+                        \%function_address_info, 
+                        \%addressobjtextm, 
+                        $outputdir);
+    }
+
+#-------------------------------------------------------------------------------------
+# TBD
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Generate the html file with the metrics information");
+
+  $ignore_value = process_metrics (
+                    $outputdir, 
+                    \@sort_fields, 
+                    \%metric_description, 
+                    \%ignored_metrics);
+
+#-------------------------------------------------------------------------------------
+# Generate the function view html files.
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Generate the function view html files");
+
+  $html_first_metric_file_ref = generate_function_view (
+                                  \$outputdir, 
+                                  \$summary_metrics, 
+                                  \$number_of_metrics, 
+                                  \@function_info, 
+                                  \%function_view_structure,
+                                  \%function_address_info, 
+                                  \@sort_fields, 
+                                  \@exp_dir_list, 
+                                  \%addressobjtextm);
+
+  $html_first_metric_file = ${ $html_first_metric_file_ref };
+
+  gp_message ("debugXL", $subr_name, "html_first_metric_file = $html_first_metric_file");
+
+  my $html_test = ${ generate_home_link ("left") };
+  gp_message ("debugXL", $subr_name, "html_test = $html_test");
+
+  my $number_of_warnings_ref = create_html_warnings_page (\$outputdir);
+
+#-------------------------------------------------------------------------------------
+# Generate the index.html file.
+#-------------------------------------------------------------------------------------
+  gp_message ("verbose", $subr_name, "Generate the index.html file");
+
+  $ignore_value = generate_index (\$outputdir, 
+                                  \$html_first_metric_file,
+                                  \$summary_metrics, 
+                                  \$number_of_metrics, 
+                                  \@function_info, 
+                                  \%function_address_info, 
+                                  \@sort_fields, 
+                                  \@exp_dir_list, 
+                                  \%addressobjtextm, 
+                                  \%metric_description_reversed,
+                                  $number_of_warnings_ref,
+                                  \@table_execution_stats);
+
+#-------------------------------------------------------------------------------------
+# We're done.  In debug mode, print the meta data for the experiment directories.
+#-------------------------------------------------------------------------------------
+  $ignore_value = print_meta_data_experiments ("debug");
+
+  my $results_file = $abs_path_outputdir . "/index.html";
+  my $prologue_text = "Processing completed - view file $results_file in a browser";
+  gp_message ("diag", $subr_name, $prologue_text);
+
+  return (0);
+
+} #-- End of subroutine main
+
+#------------------------------------------------------------------------------
+# Print a message after a failure in $GP_DISPLAY_TEXT.
+#------------------------------------------------------------------------------
+sub msg_display_text_failure
+{
+  my $subr_name = get_my_name ();
+
+  my ($gp_display_text_cmd, $error_code, $error_file) = @_;
+
+  my $msg;
+
+  $msg = "error code = $error_code - failure executing the following command:";
+  gp_message ("error", $subr_name, $msg);
+
+  gp_message ("error", $subr_name, $gp_display_text_cmd);
+
+  $msg = "check file $error_file for more details";
+  gp_message ("error", $subr_name, $msg);
+
+  return (0);
+
+} #-- End of subroutine msg_display_text_failure
+
+#------------------------------------------------------------------------------
+# If it is not present, add a "/" to the name of the argument.  This is
+# intended to be used for the name of the output directory and makes it 
+# easier to construct pathnames.
+#------------------------------------------------------------------------------
+sub append_forward_slash
+{
+  my $subr_name = get_my_name ();
+
+  my ($input_string) = @_;
+
+  my $length_of_string = length ($input_string);
+  my $return_string    = $input_string;
+
+  if (rindex ($input_string, "/") != $length_of_string-1) 
+    {
+      $return_string .= "/";
+    }
+
+  return ($return_string);
+
+} #-- End of subroutine append_forward_slash
+
+#------------------------------------------------------------------------------
+# Return a string with a comma separated list of directory names.
+#------------------------------------------------------------------------------
+sub build_pretty_dir_list
+{
+  my $subr_name = get_my_name ();
+
+  my ($dir_list_ref) = @_;
+
+  my @dir_list = @{ $dir_list_ref};
+
+  my $pretty_dir_list = join ("\n", @dir_list);
+
+  return ($pretty_dir_list);
+
+} #-- End of subroutine build_pretty_dir_list
+
+#------------------------------------------------------------------------------
+# Calculate the target address in hex by adding the instruction to the 
+# instruction address.
+#------------------------------------------------------------------------------
+sub calculate_target_hex_address
+{
+  my $subr_name = get_my_name ();
+
+  my ($instruction_address, $instruction_offset) = @_;
+
+  my $dec_branch_target; 
+  my $d1;
+  my $d2;
+  my $first_char;
+  my $length_of_string;
+  my $mask;
+  my $number_of_fields;
+  my $raw_hex_branch_target; 
+  my $result;
+
+  if ($g_addressing_mode eq "64 bit")
+    {
+      $mask = "0xffffffffffffffff";
+      $number_of_fields = 16;
+    }
+  else
+    {
+      gp_message ("abort", $subr_name, "g_addressing_mode = $g_addressing_mode not supported\n");
+    }
+  
+  $length_of_string = length ($instruction_offset); 
+  $first_char       = lcfirst (substr ($instruction_offset,0,1));
+  $d1               = hex ($instruction_offset);
+  $d2               = hex ($mask);
+#          if ($first_char eq "f")
+  if (($first_char =~ /[89a-f]/) and ($length_of_string == $number_of_fields))
+    {
+#------------------------------------------------------------------------------
+# The offset is negative.  Convert to decimal and perform the subtrraction.
+#------------------------------------------------------------------------------
+#------------------------------------------------------------------------------
+# XOR the decimal representation and add 1 to the result.
+#------------------------------------------------------------------------------
+      $result = ($d1 ^ $d2) + 1;
+      $dec_branch_target = hex ($instruction_address) - $result;
+    }
+  else
+    {
+      $result = $d1;
+      $dec_branch_target = hex ($instruction_address) + $result;
+    }
+#------------------------------------------------------------------------------
+# Convert to hexadecimal.
+#------------------------------------------------------------------------------
+  $raw_hex_branch_target = sprintf ("%x", $dec_branch_target);
+
+  return ($raw_hex_branch_target);
+
+} #-- End of subroutine calculate_target_hex_address
+
+#------------------------------------------------------------------------------
+# This subroutine sets the absolute path to all commands in array @cmds.  The
+# commands and their respective paths are stored in hash "g_mapped_cmds".
+#
+# It is a fatal error if such a path can't be found.
+#------------------------------------------------------------------------------
+sub check_and_define_cmds
+{
+  my $subr_name = get_my_name ();
+
+  my ($cmds_ref, $search_path_ref) = @_;
+
+#------------------------------------------------------------------------------
+# Dereference the array addressess first and then store the contents.
+#------------------------------------------------------------------------------
+  my @cmds        = @{$cmds_ref};
+  my @search_path = @{$search_path_ref};
+
+  my $found_match;
+  my $target_cmd; 
+  my $failed_cmd; 
+  my $no_of_failed_mappings; 
+  my $failed_cmds;
+
+  gp_message ("debug", $subr_name, "\@cmds = @cmds");
+  gp_message ("debug", $subr_name, "\@search_path = @search_path");
+
+#------------------------------------------------------------------------------
+# Search for the command to be in the search path given.  In case no such path
+# can be found, the entry in $g_mapped_cmds is assigned a special value that
+# will be checked for in the next block.
+#------------------------------------------------------------------------------
+  for my $cmd (@cmds)
+    {
+      $found_match = $FALSE;
+      for my $path (@search_path)
+        {
+          $target_cmd = $path."/".$cmd; 
+          if (-x $target_cmd)
+            {
+              $found_match = $TRUE;
+              $g_mapped_cmds{$cmd} = $target_cmd;
+              last;
+            }
+        }
+
+      if (not $found_match)
+        {
+          $g_mapped_cmds{$cmd} = "road_to_nowhere";
+        }
+    }
+
+#------------------------------------------------------------------------------
+# Scan the results stored in $g_mapped_cmds and flag errors.
+#------------------------------------------------------------------------------
+  $no_of_failed_mappings = 0;
+  $failed_cmds           = "";
+  while ( my ($cmd, $mapped) = each %g_mapped_cmds)
+    {
+      if ($mapped eq "road_to_nowhere")
+        {
+          gp_message ("error", $subr_name, "cannot find a path for command $cmd");
+          $no_of_failed_mappings++; 
+          $failed_cmds .= $cmd; 
+        }
+      else
+       {
+          gp_message ("debug", $subr_name, "path for the $cmd command is $mapped");
+       }
+    }
+  if ($no_of_failed_mappings != 0)
+    {
+      gp_message ("error", $subr_name, "failed to find a mapping for $failed_cmds");
+      gp_message ("error", $subr_name, "a total of $no_of_failed_mappings mapping failures");
+    }
+
+  return ($no_of_failed_mappings);
+
+} #-- End of subroutine check_and_define_cmds
+
+#------------------------------------------------------------------------------
+# Look for a branch instruction, or the special endbr32/endbr64 instruction
+# that is also considered to be a branch target.  Note that the latter is x86
+# specific.
+#------------------------------------------------------------------------------
+sub check_and_proc_dis_branches
+{
+  my $subr_name = get_my_name ();
+
+  my ($input_line_ref, $line_no_ref,  $branch_target_ref,
+      $extended_branch_target_ref, $branch_target_no_ref_ref) = @_;
+
+  my $input_line = ${ $input_line_ref };
+  my $line_no    = ${ $line_no_ref };
+  my %branch_target = %{ $branch_target_ref };
+  my %extended_branch_target = %{ $extended_branch_target_ref };
+  my %branch_target_no_ref = %{ $branch_target_no_ref_ref };
+
+  my $found_it = $TRUE;
+  my $hex_branch_target;
+  my $instruction_address;
+  my $instruction_offset;
+  my $msg;
+  my $raw_hex_branch_target;
+
+  if (   ($input_line =~ /$g_branch_regex/) 
+      or ($input_line =~ /$g_endbr_regex/))
+    {
+      if (defined ($3))
+        {
+          $msg = "found a branch or endbr instruction: " .
+                 "\$1 = $1 \$2 = $2 \$3 = $3";
+        }
+      else
+        {
+          $msg = "found a branch or endbr instruction: " .
+                 "\$1 = $1 \$2 = $2";
+        }
+      gp_message ("debugXL", $subr_name, $msg);
+
+      if (defined ($1))
+        {
+#------------------------------------------------------------------------------
+# Found a qualifying instruction
+#------------------------------------------------------------------------------
+          $instruction_address = $1;
+          if (defined ($3))
+            {
+#------------------------------------------------------------------------------
+# This must be the branch target and needs to be converted and processed.
+#------------------------------------------------------------------------------
+              $instruction_offset  = $3;
+              $raw_hex_branch_target = calculate_target_hex_address (
+                                        $instruction_address, 
+                                        $instruction_offset); 
+
+              $hex_branch_target = "0x" . $raw_hex_branch_target;
+              $branch_target{$hex_branch_target} = 1;
+              $extended_branch_target{$instruction_address} = $raw_hex_branch_target;
+            }
+          if (defined ($2) and (not defined ($3)))
+            {
+#------------------------------------------------------------------------------
+# Unlike a branch, the endbr32/endbr64 instructions do not have a second field.
+#------------------------------------------------------------------------------
+              my $instruction_name = $2;
+              if ($instruction_name =~ /$g_endbr_inst_regex/)
+                {
+                  my $msg = "found endbr: $instruction_name " .
+                            $instruction_address;
+                  gp_message ("debugXL", $subr_name, $msg);
+                  $raw_hex_branch_target = $instruction_address;
+
+                  $hex_branch_target = "0x" . $raw_hex_branch_target;
+                  $branch_target_no_ref{$instruction_address} = 1;
+                }
+            }
+        }
+      else
+        {
+#------------------------------------------------------------------------------
+# TBD: Perhaps this should be an assertion or alike.
+#------------------------------------------------------------------------------
+          $branch_target{"0x0000"} = $FALSE;
+          gp_message ("debug", $subr_name, "cannot determine branch target");
+        }
+    }
+  else
+    {
+      $found_it = $FALSE;
+    }
+
+  return (\$found_it, \%branch_target, \%extended_branch_target,
+         \%branch_target_no_ref);
+
+} #-- End of subroutine check_and_proc_dis_branches
+
+#------------------------------------------------------------------------------
+# Check an input line from the disassembly file to include a function call.
+# If it does, process the line and return the branch target results.
+#------------------------------------------------------------------------------
+sub check_and_proc_dis_func_call
+{
+  my $subr_name = get_my_name ();
+
+  my ($input_line_ref, $line_no_ref,  $branch_target_ref,
+      $extended_branch_target_ref) = @_;
+
+  my $input_line = ${ $input_line_ref };
+  my $line_no    = ${ $line_no_ref };
+  my %branch_target = %{ $branch_target_ref };
+  my %extended_branch_target = %{ $extended_branch_target_ref };
+
+  my $found_it = $TRUE;
+  my $hex_branch_target; 
+  my $instruction_address;
+  my $instruction_offset;
+  my $msg;
+  my $raw_hex_branch_target; 
+
+  if ( $input_line =~ /$g_function_call_v2_regex/ )
+    {
+      $msg = "found a function call - line[$line_no] = $input_line";
+      gp_message ("debugXL", $subr_name, $msg);
+      if (not defined ($2))
+        {
+          $msg = "line[$line_no] " .
+                 "an instruction address is expected, but not found";
+          gp_message ("assertion", $subr_name, $msg);
+        }
+      else
+        {
+          $instruction_address = $2;
+
+          $msg = "instruction_address = $instruction_address";
+          gp_message ("debugXL", $subr_name, $msg);
+
+          if (not defined ($4))
+            {
+              $msg = "line[$line_no] " .
+                     "an address offset is expected, but not found";
+              gp_message ("assertion", $subr_name, $msg);
+            }
+          else
+            {
+              $instruction_offset = $4;
+              if ($instruction_offset =~ /[0-9a-fA-F]+/)
+                {
+                  $msg = "calculate branch target: " .
+                         "instruction_address = $instruction_address";
+                  gp_message ("debugXL", $subr_name, $msg);
+                  $msg = "calculate branch target: " .
+                         "instruction_offset  = $instruction_offset";
+                  gp_message ("debugXL", $subr_name, $msg);
+
+#------------------------------------------------------------------------------
+# The instruction offset needs to be converted and added to the instruction
+# address.
+#------------------------------------------------------------------------------
+                  $raw_hex_branch_target = calculate_target_hex_address (
+                                            $instruction_address, 
+                                            $instruction_offset); 
+                  $hex_branch_target     = "0x" . $raw_hex_branch_target;
+
+                  $msg = "calculated hex_branch_target = " .
+                         $hex_branch_target;
+                  gp_message ("debugXL", $subr_name, $msg);
+
+                  $branch_target{$hex_branch_target} = 1;
+                  $extended_branch_target{$instruction_address} = $raw_hex_branch_target;
+
+                  $msg = "set branch_target{$hex_branch_target} to 1";
+                  gp_message ("debugXL", $subr_name, $msg);
+                  $msg  = "added extended_branch_target{$instruction_address}" .
+                          " = $extended_branch_target{$instruction_address}";
+                  gp_message ("debugXL", $subr_name, $msg);
+                }
+              else
+                {
+                  $msg = "line[$line_no] unknown address format";
+                  gp_message ("assertion", $subr_name, $msg);
+                }
+            }
+        }
+    }
+  else
+    {
+      $found_it = $FALSE;
+    }
+
+  return (\$found_it, \%branch_target, \%extended_branch_target);
+
+} #-- End of subroutine check_and_proc_dis_func_call
+
+#------------------------------------------------------------------------------
+# Check for the $GP_DISPLAY_TEXT tool to be available.  This is a critical tool 
+# needed to provide the information.  If it can not be found, execution is 
+# terminated.
+#------------------------------------------------------------------------------
+sub check_availability_tool
+{
+  my $subr_name = get_my_name ();
+
+  my $target_cmd;
+  my $output_which_gp_display_text;
+  my $error_code;
+
+  $target_cmd = $g_mapped_cmds{"which"} . " $GP_DISPLAY_TEXT 2>&1";
+
+  ($error_code, $output_which_gp_display_text) = execute_system_cmd ($target_cmd);
+   
+  if ($error_code == 0)
+    {
+      gp_message ("debug", $subr_name, "tool $GP_DISPLAY_TEXT is in the search path");
+    } 
+  else
+    {
+      gp_message ("abort", $subr_name, "fatal error executing command $target_cmd");
+    }
+
+  return (0);
+
+} #-- End of subroutine check_availability_tool
+
+#------------------------------------------------------------------------------
+# This function determines whether load objects are in ELF format.
+#
+# Compared to the original code, any input value other than 2 or 3 is rejected
+# upfront.  This not only reduces the nesting level, but also eliminates a 
+# possible bug.
+#
+# Also, by isolating the tests for the input files, another nesting level could
+# be eliminated, further simplifying this still too complex code.
+#------------------------------------------------------------------------------
+sub check_loadobjects_are_elf
+{
+  my $subr_name = get_my_name ();
+
+  my ($selected_archive) = @_;
+
+  my $hostname_current = $local_system_config{"hostname_current"};
+  my $arch             = $local_system_config{"processor"};
+  my $arch_uname_s     = $local_system_config{"kernel_name"};
+
+  my $extracted_information; 
+
+  my $elf_magic_number;
+
+  my $executable_name;
+  my $va_executable_in_hex;
+ 
+  my $arch_exp;
+  my $hostname_exp;
+  my $os_exp;
+  my $os_exp_full;
+
+  my $archives_file;
+  my $rc_b;
+  my $file;
+  my $line;
+  my $name;
+  my $name_path;
+  my $foffset;
+  my $vaddr;
+  my $modes;
+
+  my $path_to_map_file; 
+  my $path_to_log_file;
+
+#------------------------------------------------------------------------------
+# TBD: Parameterize and should be the first experiment directory from the list.
+#------------------------------------------------------------------------------
+  $path_to_log_file  = $g_exp_dir_meta_data{$selected_archive}{"directory_path"}; 
+  $path_to_log_file .= $selected_archive;
+  $path_to_log_file .= "/log.xml";
+
+  gp_message ("debug", $subr_name, "hostname_current = $hostname_current");
+  gp_message ("debug", $subr_name, "arch             = $arch");
+  gp_message ("debug", $subr_name, "arch_uname_s     = $arch_uname_s");
+
+#------------------------------------------------------------------------------
+# TBD
+#
+# This check can probably be removed since the presence of the log.xml file is
+# checked for in an earlier phase.
+#------------------------------------------------------------------------------
+  open (LOG_XML, "<", $path_to_log_file)
+    or die ("$subr_name - unable to open file $path_to_log_file for reading: '$!'");
+  gp_message ("debug", $subr_name, "opened file $path_to_log_file for reading");
+    
+  while (<LOG_XML>)
+    {
+      $line = $_;
+      chomp ($line);
+      gp_message ("debug", $subr_name, "read line: $line");
+#------------------------------------------------------------------------------
+# Search for the first line starting with "<system".  Bail out if found and
+# parsed. These are two examples:
+# <system hostname="ruud-vm" arch="x86_64" os="Linux 4.14.35-2025.400.8.el7uek.x86_64" pagesz="4096" npages="30871514">
+# <system hostname="sca-m88-092-pd0" arch="sun4v" os="SunOS 5.11" pagesz="8192" npages="602963968">
+#------------------------------------------------------------------------------
+      if ($line =~ /^\s*<system\s+/)
+        {
+          gp_message ("debug", $subr_name, "selected the following line from the log.xml file:");
+          gp_message ("debug", $subr_name, "$line");
+          if ($line =~ /.*\s+hostname="([^"]+)/)
+            {
+              $hostname_exp = $1;
+              gp_message ("debug", $subr_name, "extracted hostname_exp = $hostname_exp");
+            }
+          if ($line =~ /.*\s+arch="([^"]+)/)
+            {
+              $arch_exp = $1;
+              gp_message ("debug", $subr_name, "extracted arch_exp = $arch_exp");
+            }
+          if ($line =~ /.*\s+os="([^"]+)/)
+            {
+              $os_exp_full = $1;
+#------------------------------------------------------------------------------
+# Capture the first word only.
+#------------------------------------------------------------------------------
+              if ($os_exp_full =~ /([^\s]+)/)
+                {
+                  $os_exp = $1;
+                }
+              gp_message ("debug", $subr_name, "extracted os_exp = $os_exp");
+            }
+          last;
+        }
+    } #-- End of while loop
+
+  close (LOG_XML);
+
+#------------------------------------------------------------------------------
+# If the current system is identical to the system used in the experiment,
+# we can return early.  Otherwise we need to dig deeper.
+#
+# TBD: How about the other experiment directories?! This needs to be fixed.
+#------------------------------------------------------------------------------
+
+  gp_message ("debug", $subr_name, "completed while loop");
+  gp_message ("debug", $subr_name, "hostname_exp     = $hostname_exp");
+  gp_message ("debug", $subr_name, "arch_exp         = $arch_exp");
+  gp_message ("debug", $subr_name, "os_exp           = $os_exp");
+
+#TBD: THIS DOES NOT CHECK IF ELF IS FOUND!
+
+  if (($hostname_current eq $hostname_exp) and
+      ($arch             eq $arch_exp)     and 
+      ($arch_uname_s     eq $os_exp))
+        {
+          gp_message ("debug", $subr_name, "early return: the hostname, architecture and OS match the current system");
+  gp_message ("debug", $subr_name, "FAKE THIS IS NOT THE CASE AND CONTINUE");
+# FAKE          return ($TRUE);
+        }
+
+  if (not $g_exp_dir_meta_data{$selected_archive}{"archive_is_empty"})
+    {
+      gp_message ("debug", $subr_name, "selected_archive = $selected_archive");
+      for my $i (sort keys %{$g_exp_dir_meta_data{$selected_archive}{"archive_files"}})
+        {
+          gp_message ("debug", $subr_name, "stored loadobject $i $g_exp_dir_meta_data{$selected_archive}{'archive_files'}{$i}");
+        }
+    }
+
+#------------------------------------------------------------------------------
+# Check if the selected experiment directory has archived files in ELF format.
+# If not, use the information in map.xml to get the name of the executable 
+# and the virtual address.
+#------------------------------------------------------------------------------
+
+  if ($g_exp_dir_meta_data{$selected_archive}{"archive_in_elf_format"})
+    {
+      gp_message ("debug", $subr_name, "the files in directory $selected_archive/archives are in ELF format");
+      gp_message ("debug", $subr_name, "IGNORE THIS AND USE MAP.XML");
+##      return ($TRUE);
+    }
+
+      gp_message ("debug", $subr_name, "the files in directory $selected_archive/archives are not in ELF format");
+
+      $path_to_map_file  = $g_exp_dir_meta_data{$selected_archive}{"directory_path"}; 
+      $path_to_map_file .= $selected_archive;
+      $path_to_map_file .= "/map.xml";
+
+      open (MAP_XML, "<", $path_to_map_file)
+        or die ($subr_name, "unable to open file $path_to_map_file for reading: $!");
+      gp_message ("debug", $subr_name, "opened file $path_to_map_file for reading");
+
+#------------------------------------------------------------------------------
+# Scan the map.xml file.  We need to find the name of the executable with the
+# mode set to 0x005.  For this entry we have to capture the virtual address.
+#------------------------------------------------------------------------------
+    $extracted_information = $FALSE;
+    while (<MAP_XML>)
+    {
+      $line = $_;
+      chomp ($line);
+      gp_message ("debug", $subr_name, "MAP_XML read line = $line");
+##      if ($line =~ /^<event kind="map"\s.*vaddr="0x([0-9a-fA-F]+)"\s+                                 .*modes="0x([0-9]+)"\s.*name="(.*)".*>$/)
+      if ($line =~   /^<event kind="map"\s.*vaddr="0x([0-9a-fA-F]+)"\s+.*foffset="\+*0x([0-9a-fA-F]+)"\s.*modes="0x([0-9]+)"\s.*name="(.*)".*>$/)
+        {
+          gp_message ("debug", $subr_name, "target line = $line");
+          $vaddr     = $1;
+          $foffset   = $2;
+          $modes     = $3;
+          $name_path = $4;
+          $name      = get_basename ($name_path);
+          gp_message ("debug", $subr_name, "extracted vaddr     = $vaddr foffset = $foffset modes = $modes");
+          gp_message ("debug", $subr_name, "extracted name_path = $name_path name = $name");
+#              $error_extracting_information = $TRUE;
+          $executable_name  = $name;
+          my $result_VA = hex ($vaddr) - hex ($foffset);
+          my $hex_VA = sprintf ("0x%016x", $result_VA);
+          $va_executable_in_hex = $hex_VA;
+          gp_message ("debug", $subr_name, "set executable_name  = $executable_name");
+          gp_message ("debug", $subr_name, "set va_executable_in_hex = $va_executable_in_hex");
+          gp_message ("debug", $subr_name, "result_VA = $result_VA"); 
+          gp_message ("debug", $subr_name, "hex_VA    = $hex_VA"); 
+          if ($modes eq "005")
+            {
+              $extracted_information = $TRUE;
+              last;
+            }
+        }
+    }
+  if (not $extracted_information)
+    {
+      my $msg = "cannot find the necessary information in the $path_to_map_file file";
+      gp_message ("assertion", $subr_name, $msg);
+    }
+
+##  $executable_name = $ARCHIVES_MAP_NAME;
+##  $va_executable_in_hex = $ARCHIVES_MAP_VADDR;
+
+  return ($executable_name, $va_executable_in_hex);
+
+} #-- End of subroutine check_loadobjects_are_elf
+
+#------------------------------------------------------------------------------
+# Compare the current metric values against the maximum values.  Mark the line
+# if a value is within the percentage defined by $hp_value.
+#------------------------------------------------------------------------------
+sub check_metric_values
+{
+  my $subr_name = get_my_name ();
+
+  my ($metric_values, $max_metric_values_ref) = @_;
+
+  my @max_metric_values = @{ $max_metric_values_ref };
+
+  my @current_metrics = ();
+  my $colour_coded_line;
+  my $current_value;
+  my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
+  my $max_value;
+  my $relative_distance;
+
+  @current_metrics = split (" ", $metric_values);
+  $colour_coded_line = $FALSE;
+  for my $metric (0 .. $#current_metrics)
+    {
+      $current_value = $current_metrics[$metric];
+      if (exists ($max_metric_values[$metric]))
+        {
+          $max_value     = $max_metric_values[$metric];
+          gp_message ("debugXL", $subr_name, "metric = $metric current_value = $current_value max_value = $max_value");
+          if ( ($max_value > 0) and ($current_value > 0) and ($current_value != $max_value) )
+            {
+# TBD: abs needed?
+              gp_message ("debugXL", $subr_name, "metric = $metric current_value = $current_value max_value = $max_value");
+              $relative_distance = 1.00 - abs ( ($max_value - $current_value)/$max_value );
+              gp_message ("debugXL", $subr_name, "relative_distance = $relative_distance");
+              if ($relative_distance >= $hp_value/100.0)
+                {
+                  gp_message ("debugXL", $subr_name, "metric $metric is within the relative_distance");
+                  $colour_coded_line = $TRUE;
+                  last;
+                }
+            }
+        }
+    } #-- End of loop over metrics
+
+  return (\$colour_coded_line);
+
+} #-- End of subroutine check_metric_values
+
+#------------------------------------------------------------------------------
+# Check if the system is supported.
+#------------------------------------------------------------------------------
+sub check_support_for_processor
+{
+  my $subr_name = get_my_name ();
+
+  my ($machine_ref) = @_;
+
+  my $machine = ${ $machine_ref };
+  my $is_supported;
+
+  if ($machine eq "x86_64")
+    {
+      $is_supported = $TRUE;
+    }
+  else
+    {
+      $is_supported = $FALSE;
+    }
+
+  return (\$is_supported);
+
+} #-- End of subroutine check_support_for_processor
+
+#------------------------------------------------------------------------------
+# Check if the value for the user option given is valid.
+#
+# In case the value is valid, the g_user_settings table is updated.
+# Otherwise an error message is printed.
+#
+# The return value is TRUE/FALSE.
+#------------------------------------------------------------------------------
+sub check_user_option
+{
+  my $subr_name = get_my_name ();
+
+  my ($internal_option_name, $value) = @_;
+
+  my $message;
+  my $return_value;
+
+  my $option          = $g_user_settings{$internal_option_name}{"option"};
+  my $data_type       = $g_user_settings{$internal_option_name}{"data_type"};
+  my $no_of_arguments = $g_user_settings{$internal_option_name}{"no_of_arguments"};
+
+  if (($no_of_arguments >= 1) and 
+      ((not defined ($value)) or (length ($value) == 0)))
+    {
+#------------------------------------------------------------------------------
+# If there was no value given, but it is required, flag an error.
+# There could also be a value, but it might be the empty string.
+#
+# Note that that there are currently no options with multiple values.  Should
+# these be introduced, the current check may need to be refined.
+#------------------------------------------------------------------------------
+
+      $message = "the $option option requires a value";
+      push (@g_user_input_errors, $message);
+      $return_value = $FALSE;
+    }
+  elsif ($no_of_arguments >= 1)
+    {
+#------------------------------------------------------------------------------
+# There is an input value.  Check if it is valid and if so, store it.
+#
+# Note that we allow the options to be case insensitive.
+#------------------------------------------------------------------------------
+      my $valid = verify_if_input_is_valid ($value, $data_type);
+
+      if ($valid)
+        {
+          if (($data_type eq "onoff") or ($data_type eq "size"))
+            {
+              $g_user_settings{$internal_option_name}{"current_value"} = lc ($value);
+            }
+          else
+            {
+              $g_user_settings{$internal_option_name}{"current_value"} = $value;
+            }
+          $g_user_settings{$internal_option_name}{"defined"}       = $TRUE;
+          $return_value = $TRUE;
+        }
+      else
+        {
+          $message = "incorrect value for $option option: $value";
+          push (@g_user_input_errors, $message);
+
+          $return_value = $FALSE;
+        }
+    }
+
+  return ($return_value);
+
+} #-- End of subroutine check_user_option
+
+#-------------------------------------------------------------------------------
+# This subroutine performs multiple checks on the experiment directories. One 
+# or more failures are fatal.
+#-------------------------------------------------------------------------------
+sub check_validity_exp_dirs
+{
+  my $subr_name = get_my_name ();
+
+  my ($exp_dir_list_ref) = @_;
+
+  my @exp_dir_list = @{ $exp_dir_list_ref };
+ 
+  my %elf_rats = ();
+
+  my $dir_not_found    = $FALSE;
+  my $invalid_dir      = $FALSE;
+  my $dir_check_errors = $FALSE;
+  my $missing_dirs     = 0;
+  my $invalid_dirs     = 0;
+   
+  my $archive_dir_not_empty;
+  my $elf_magic_number; 
+  my $archives_file;
+  my $archives_dir; 
+  my $first_line;
+  my $count_exp_dir_not_elf;
+ 
+  my $first_time;
+  my $filename;
+
+  my $comment;
+
+  my $selected_archive_has_elf_format; 
+
+  my $selected_archive;
+  my $archive_dir_selected;
+  my $no_of_files_in_selected_archive;
+
+#-------------------------------------------------------------------------------
+# Check if the experiment directories exist and are valid.
+#-------------------------------------------------------------------------------
+  for my $exp_dir (@exp_dir_list)
+    {
+      if (not -d $exp_dir)
+        {
+          $dir_not_found = $TRUE;
+          $missing_dirs++;
+          gp_message ("error", $subr_name, "directory $exp_dir not found");
+          $dir_check_errors = $TRUE;
+        }
+      else
+        {
+#-------------------------------------------------------------------------------
+# Files log.xml and map.xml have to be there.
+#-------------------------------------------------------------------------------
+          gp_message ("debug", $subr_name, "directory $exp_dir found");
+          if ((-e $exp_dir."/log.xml") and (-e $exp_dir."/map.xml"))
+            {
+              gp_message ("debug", $subr_name, "directory $exp_dir appears to be a valid experiment directory");
+            }
+          else
+            {
+              $invalid_dir = $TRUE;
+              $invalid_dirs++; 
+              gp_message ("debug", $subr_name, "file ".$exp_dir."/log.xml and/or ".$exp_dir."/map.xml missing");
+              gp_message ("error"  , $subr_name, "directory $exp_dir does not appear to be a valid experiment directory");
+              $dir_check_errors = $TRUE;
+            }
+        }
+    }
+  if ($dir_not_found)
+    {
+      gp_message ("error", $subr_name, "a total of $missing_dirs directories not found");
+    }
+  if ($invalid_dir)
+    {
+      gp_message ("abort", $subr_name, "a total of $invalid_dirs directories are not valid");
+    }
+
+#-------------------------------------------------------------------------------
+# Initialize ELF status to FALSE.
+#-------------------------------------------------------------------------------
+##  for my $exp_dir (@exp_dir_list)
+  for my $exp_dir (keys %g_exp_dir_meta_data)
+    {
+      $g_exp_dir_meta_data{$exp_dir}{"elf_format"} = $FALSE; 
+      $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} = $FALSE; 
+    }
+#-------------------------------------------------------------------------------
+# Check if the load objects are in ELF format.
+#-------------------------------------------------------------------------------
+  for my $exp_dir (keys %g_exp_dir_meta_data)
+    {
+      $archives_dir = $g_exp_dir_meta_data{$exp_dir}{"directory_path"} .  $exp_dir . "/archives";
+      $archive_dir_not_empty = $FALSE;
+      $first_time            = $TRUE;
+      $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"} = $TRUE;
+      $g_exp_dir_meta_data{$exp_dir}{"no_of_files_in_archive"} = 0;
+
+      gp_message ("debug", $subr_name, "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = $g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'}");
+      gp_message ("debug", $subr_name, "checking $archives_dir");
+
+      while (glob ("$archives_dir/*"))
+        {
+          $filename = get_basename ($_);
+          gp_message ("debug", $subr_name, "processing file: $filename");
+
+          $g_exp_dir_meta_data{$exp_dir}{"archive_files"}{$filename} = $TRUE;
+          $g_exp_dir_meta_data{$exp_dir}{"no_of_files_in_archive"}++;
+
+          $archive_dir_not_empty = $TRUE;
+#-------------------------------------------------------------------------------
+# Replaces the ELF_RATS part in elf_phdr.
+#
+# Challenge:  splittable_mrg.c_I0txnOW_Wn5
+#
+# TBD: Store this for each relevant experiment directory.
+#-------------------------------------------------------------------------------
+          my $last_dot              = rindex ($filename,".");
+          my $underscore_before_dot = $TRUE;
+          my $first_underscore      = -1;
+          gp_message ("debugXL", $subr_name, "last_dot = $last_dot");
+          while ($underscore_before_dot)
+            {
+              $first_underscore = index ($filename, "_", $first_underscore+1);
+              if ($last_dot < $first_underscore)
+                {
+                  $underscore_before_dot = $FALSE;
+                }
+            }
+          my $original_name  = substr ($filename, 0, $first_underscore);
+          gp_message ("debug", $subr_name, "stripped archive name: $original_name");
+          if (not exists ($elf_rats{$original_name}))
+            {
+              $elf_rats{$original_name} = [$filename, $exp_dir];
+            }
+#-------------------------------------------------------------------------------
+# We only need to detect the presence of an object once.
+#-------------------------------------------------------------------------------
+          if ($first_time)
+            {
+              $first_time = $FALSE;
+              $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"} = $FALSE;
+              gp_message ("debugXL", $subr_name, "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = $g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'}");
+            }
+        }
+    } #-- End of loop over experiment directories
+
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      my $empty = $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"}; 
+      gp_message ("debug", $subr_name, "archive directory $exp_dir/archives is ".($empty ? "empty" : "not empty"));
+    }
+
+#------------------------------------------------------------------------------
+# Verify that all relevant files in the archive directories are in ELF format.
+#------------------------------------------------------------------------------
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} = $FALSE; 
+      if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
+        {
+          $archives_dir = $g_exp_dir_meta_data{$exp_dir}{"directory_path"} .  $exp_dir . "/archives";
+          gp_message ("debug", $subr_name, "exp_dir = $exp_dir archives_dir = $archives_dir");
+#------------------------------------------------------------------------------
+# Check if any of the loadobjects is of type ELF.  Bail out on the first one
+# found.  The assumption is that all other loadobjects must be of type ELF too
+# then.
+#------------------------------------------------------------------------------
+          for my $aname (sort keys %{$g_exp_dir_meta_data{$exp_dir}{"archive_files"}})
+            {
+              $filename = $g_exp_dir_meta_data{$exp_dir}{"directory_path"} . $exp_dir . "/archives/" . $aname;
+              open (ARCF,"<", $filename)
+                or die ("unable to open file $filename for reading - '$!'");
+
+              $first_line = <ARCF>;
+              close (ARCF);
+
+#------------------------------------------------------------------------------
+# The first 4 hex fields in the header of an ELF file are: 7F 45 4c 46 (7FELF).
+#
+# See also https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+#------------------------------------------------------------------------------
+#              if ($first_line =~ /^\177ELF.*/)
+
+              $elf_magic_number = unpack ('H8', $first_line);
+#              gp_message ("debug", $subr_name, "elf_magic_number = $elf_magic_number");
+              if ($elf_magic_number eq "7f454c46")
+                {
+                  $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"} = $TRUE; 
+                  $g_exp_dir_meta_data{$exp_dir}{"elf_format"} = $TRUE;
+                  last;
+                }
+            }
+        }
+    }
+
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      $comment = "the loadobjects in the archive in $exp_dir are ";
+      $comment .= ($g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"}) ? "in " : "not in ";
+      $comment .= "ELF format";
+      gp_message ("debug", $subr_name, $comment);
+    }
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      if ($g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
+        {
+          gp_message ("debug", $subr_name, "there are no archived files in $exp_dir");
+        }
+    }
+
+#------------------------------------------------------------------------------
+# If there are archived files and they are not in ELF format, a debug is
+# issued.
+#
+# TBD: Bail out?
+#------------------------------------------------------------------------------
+  $count_exp_dir_not_elf = 0;
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      if (not $g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"})
+        {
+          $count_exp_dir_not_elf++; 
+        }
+    }
+  if ($count_exp_dir_not_elf != 0)
+    {
+      gp_message ("debug", $subr_name, "there are $count_exp_dir_not_elf experiments with non-ELF load objects");
+    }
+
+#------------------------------------------------------------------------------
+# Select the experiment directory that is used for the files in the archive.
+# By default, a directory with archived files is used, but in case this does
+# not exist, a directory without archived files is selected.  Obviously this
+# needs to be dealt with later on.
+#------------------------------------------------------------------------------
+
+#------------------------------------------------------------------------------
+# Try the experiments with archived files first.
+#------------------------------------------------------------------------------
+  $archive_dir_not_empty = $FALSE;
+  $archive_dir_selected  = $FALSE;
+##  for my $exp_dir (sort @exp_dir_list)
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      gp_message ("debugXL", $subr_name, "exp_dir = $exp_dir");
+      gp_message ("debugXL", $subr_name, "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'}");
+
+      if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
+        {
+          $selected_archive      = $exp_dir;
+          $archive_dir_not_empty = $TRUE;
+          $archive_dir_selected  = $TRUE;
+          $selected_archive_has_elf_format = ($g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"}) ? $TRUE : $FALSE;
+          last;
+        }
+    }
+  if (not $archive_dir_selected) 
+#------------------------------------------------------------------------------
+# None are found and pick the first one without archived files.
+#------------------------------------------------------------------------------
+    {
+      for my $exp_dir (sort keys %g_exp_dir_meta_data)
+        {
+          if ($g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
+            {
+              $selected_archive      = $exp_dir;
+              $archive_dir_not_empty = $FALSE;
+              $archive_dir_selected  = $TRUE;
+              $selected_archive_has_elf_format = $FALSE;
+              last;
+            }
+        }
+    }
+  gp_message ("debug", $subr_name, "experiment $selected_archive has been selected for archive analysis");
+  gp_message ("debug", $subr_name, "this archive is ". (($archive_dir_not_empty) ? "not empty" : "empty"));
+  gp_message ("debug", $subr_name, "this archive is ". (($selected_archive_has_elf_format) ? "in" : "not in")." ELF format");
+#------------------------------------------------------------------------------
+# Get the size of the hash that contains the archived files.
+#------------------------------------------------------------------------------
+##  $NO_OF_FILES_IN_ARCHIVE = scalar (keys %ARCHIVES_FILES);
+
+  $no_of_files_in_selected_archive = $g_exp_dir_meta_data{$selected_archive}{"no_of_files_in_archive"};
+  gp_message ("debug", $subr_name, "number of files in archive $selected_archive is $no_of_files_in_selected_archive");
+
+
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      my $is_empty = $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
+      gp_message ("debug", $subr_name, "archive directory $exp_dir/archives is ".($is_empty ? "empty" : "not empty"));
+    }
+  for my $exp_dir (sort keys %g_exp_dir_meta_data)
+    {
+      if (not $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
+        {
+          for my $object (sort keys %{$g_exp_dir_meta_data{$exp_dir}{"archive_files"}})
+            {
+              gp_message ("debug", $subr_name, "$exp_dir $object $g_exp_dir_meta_data{$exp_dir}[...]

[diff truncated at 100000 bytes]


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-07-06 21:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-06 21:59 [binutils-gdb] gprofng: implement a functional gp-display-html Vladimir Mezentsev

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).