__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.147: ~ $
#!/usr/bin/perl
#   Copyright (C) 2021-2023 Free Software Foundation, Inc.
#   Contributed by Oracle.
#
#   This file is part of GNU Binutils.
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 3, or (at your option)
#   any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, 51 Franklin Street - Fifth Floor, Boston,
#   MA 02110-1301, USA.
 
use strict;
use warnings;
use feature qw (state);
use File::stat;

#------------------------------------------------------------------------------
# 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;

  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.
#------------------------------------------------------------------------------
my $TRUE    = 1;
my $FALSE   = 0;

#------------------------------------------------------------------------------
# Used to ensure correct alignment of columns.
#------------------------------------------------------------------------------
my $g_max_length_first_metric;

#------------------------------------------------------------------------------
# This variable contains the path used to execute $GP_DISPAY_TEXT.
#------------------------------------------------------------------------------
my $g_path_to_tools;

#-------------------------------------------------------------------------------
# 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.
#-------------------------------------------------------------------------------
$driver_cmd       = "gprofng display html";
$tool_name        = "gp-display-html";
#$binutils_version = "2.38.50";
$binutils_version = "2.40.00";
$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
);

#------------------------------------------------------------------------------
# See this page for a nice overview with the colors:
# https://www.w3schools.com/colors/colors_groups.asp
#------------------------------------------------------------------------------

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",
);

#------------------------------------------------------------------------------
# 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",
);

#------------------------------------------------------------------------------
# This is cosmetic, but helps with the scoping of variables.
#------------------------------------------------------------------------------
  main ();

  exit (0);

#------------------------------------------------------------------------------
# This is the driver part of the program.
#------------------------------------------------------------------------------
sub main
{
  my $subr_name = get_my_name ();

#------------------------------------------------------------------------------
# The name of the configuration file.
#------------------------------------------------------------------------------
  my $rc_file_name = ".gp-display-html.rc";

#------------------------------------------------------------------------------
# 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
    /usr/local/bin
    /usr/local/sbin
    /usr/sbin
    /sbin
  );

#------------------------------------------------------------------------------
# TBD: Eliminate these.
#------------------------------------------------------------------------------
  my $ARCHIVES_MAP_NAME;
  my $ARCHIVES_MAP_VADDR;

#------------------------------------------------------------------------------
# Local structures (hashes and arrays).
#------------------------------------------------------------------------------
  my @exp_dir_list; # List with experiment directories
  my @metrics_data;

  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;

  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;

#------------------------------------------------------------------------------
# Local variables.
#------------------------------------------------------------------------------
  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;

  my $failed_command_mappings; 
  my $option_errors;
  my $total_user_errors;

  my $script_pc_metrics; 
  my $dir_check_errors;
  my $consistency_errors;
  my $outputdir;
  my $return_code;

  my $decimal_separator;
  my $convert_to_dot;
  my $architecture_supported;
  my $elf_arch;
  my $elf_support;
  my $home_dir;
  my $elf_loadobjects_found; 

  my $rc_file_paths_ref;
  my @rc_file_paths = ();
  my $rc_file_errors = 0;

  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;

#------------------------------------------------------------------------------
# If no options are given, print the help info and exit.
#------------------------------------------------------------------------------
  if ($#ARGV == -1)
    {
      $ignore_value = print_help_info (); 
      return (0);
    }

#------------------------------------------------------------------------------
# This part is like a preamble.  Before we continue we need to figure out some 
# things that are needed later on.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Store the absolute path of the command executed.
#------------------------------------------------------------------------------
  my $location_gp_command = $0;

#------------------------------------------------------------------------------
# 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;

  $ignore_value = early_scan_specific_options ();

#------------------------------------------------------------------------------
# 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)
    {
      gp_message ("debug", $subr_name, "verified the OS commands");
    }
  else
    {
      my $msg = "failure in the verification of the OS commands";
      gp_message ("assertion", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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);

  @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);

#------------------------------------------------------------------------------
# Get the ball rolling.  Parse and interpret the configuration file (if any)
# and the command line options.
#
# 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.
#------------------------------------------------------------------------------

  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;

  ($option_errors, $found_exp_dir, $exp_dir_list_ref) = parse_and_check_user_options (
                                                          \$#ARGV, 
                                                          \@ARGV);
  $total_user_errors += $option_errors;

#------------------------------------------------------------------------------
# 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 ($module_errors > 0)
    {
      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);
    }

#------------------------------------------------------------------------------
# The user options have been taken in.  Check for validity and consistency.
#------------------------------------------------------------------------------
  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";
        }
      $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]");
        }
    }

#------------------------------------------------------------------------------
# Bail out in case fatal errors have occurred.
#------------------------------------------------------------------------------
  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);

#------------------------------------------------------------------------------
# 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);
    }

#------------------------------------------------------------------------------
# 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.
# This subroutine only returns a value if the tool can be found."
#------------------------------------------------------------------------------
  $g_path_to_tools = ${ check_availability_tool (\$location_gp_command)};

  $GP_DISPLAY_TEXT = $g_path_to_tools . $GP_DISPLAY_TEXT;

  gp_message ("debug", $subr_name, "updated GP_DISPLAY_TEXT = $GP_DISPLAY_TEXT");

#------------------------------------------------------------------------------
# Check if $GP_DISPLAY_TEXT is executable for user, group, and other.
# If not, print a warning only, since this may not be fatal but could
# potentially lead to issues later on.
#------------------------------------------------------------------------------
  if (not is_file_executable ($GP_DISPLAY_TEXT))
    {
      my $msg = "file $GP_DISPLAY_TEXT is not executable for user, group, and other";
      gp_message ("warning", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Find out what the decimal separator is, as set by the user.
#------------------------------------------------------------------------------
  ($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 cannot 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               = bigint::hex ($instruction_offset);
  $d2               = bigint::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 = bigint::hex ($instruction_address) - $result;
    }
  else
    {
      $result = $d1;
      $dec_branch_target = bigint::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

#------------------------------------------------------------------------------
# Sets the absolute path to all commands in array @cmds.  The commands and 
# their respective paths are stored in hash "g_mapped_cmds".
#
# If no such mapping is found, a warning is issued, but execution continues.
# The warning(s) may help with troubleshooting, should a failure occur later.
#------------------------------------------------------------------------------
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")
        {
          my $msg = "cannot find a path for command $cmd - " .
                    "assume this will still work without a path";
          gp_message ("warning", $subr_name, $msg);
          $no_of_failed_mappings++; 
          $failed_cmds .= $cmd; 
          $g_mapped_cmds{$cmd} = $cmd;
        }
      else
       {
          gp_message ("debug", $subr_name, "path for the $cmd command is $mapped");
       }
    }
  if ($no_of_failed_mappings != 0)
    {
      gp_message ("debug", $subr_name, "failed to find a mapping for $failed_cmds");
      gp_message ("debug", $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.
#
# We first search foe this tool in the current execution directory.  If it
# cannot be found there, use $PATH to try to locate it.
#------------------------------------------------------------------------------
sub check_availability_tool
{
  my $subr_name = get_my_name ();

  my ($location_gp_command_ref) = @_;

  my $error_code;
  my $error_occurred;
  my $msg;
  my $output_which_gp_display_text;
  my $return_value;
  my $target_cmd;

#------------------------------------------------------------------------------
# Get the path to gp-display-text.
#------------------------------------------------------------------------------
  my ($error_occurred_ref, $return_value_ref) = find_path_to_gp_display_text (
                                                       $location_gp_command_ref
                                                        );
  $error_occurred = ${ $error_occurred_ref};   
  $return_value   = ${ $return_value_ref};

  $msg = "error_occurred = $error_occurred return_value = $return_value";
  gp_message ("debugXL", $subr_name, $msg);

  if (not $error_occurred) 
#------------------------------------------------------------------------------
# All is well and gp-display-text has been located.
#------------------------------------------------------------------------------
    {
      $g_path_to_tools = $return_value;

      $msg = "located $GP_DISPLAY_TEXT in execution directory";
      gp_message ("debug", $subr_name, $msg);
      $msg = "g_path_to_tools = $g_path_to_tools";
      gp_message ("debug", $subr_name, $msg);
    }
  else
#------------------------------------------------------------------------------
# Something went wrong, but perhaps we can still continue.  Try to find
# $GP_DISPLAY_TEXT through the search path.
#------------------------------------------------------------------------------
    {
      $msg = "error accessing $GP_DISPLAY_TEXT: $return_value - " .
             "run time behaviour may be undefined";
      gp_message ("warning", $subr_name, $msg);
  
#------------------------------------------------------------------------------
# Check if we can find $GP_DISPLAY_TEXT in the search path.
#------------------------------------------------------------------------------
      $msg = "check for $GP_DISPLAY_TEXT in search path";
      gp_message ("debug", $subr_name, $msg);

      $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)
        {
          my ($gp_file_name, $gp_path, $suffix_not_used) = 
                                     fileparse ($output_which_gp_display_text);
          $g_path_to_tools = $gp_path;

          $msg = "using $GP_DISPLAY_TEXT in $g_path_to_tools instead";
          gp_message ("warning", $subr_name, $msg);

          $msg = "the $GP_DISPLAY_TEXT tool is in the search path";
          gp_message ("debug", $subr_name, $msg);
          $msg = "g_path_to_tools = $g_path_to_tools";
          gp_message ("debug", $subr_name, $msg);
        } 
      else
        {
          $msg = "failure to find $GP_DISPLAY_TEXT in the search path";
          gp_message ("debug", $subr_name, $msg);

          $msg = "fatal error executing command $target_cmd";
          gp_message ("abort", $subr_name, $msg);
        }
     }

  return (\$g_path_to_tools);

} #-- 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 = bigint::hex ($vaddr) - bigint::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}{'archive_files'}{$object}");
            }
        }
    }

  return ($dir_check_errors, $archive_dir_not_empty, $selected_archive, \%elf_rats);

} #-- End of subroutine check_validity_exp_dirs

#------------------------------------------------------------------------------
# Color the string and optionally mark it boldface.
#
# For supported colors, see:
# https://www.w3schools.com/colors/colors_names.asp
#------------------------------------------------------------------------------
sub color_string
{
  my $subr_name = get_my_name ();

  my ($input_string, $boldface, $color) = @_;

  my $colored_string;

  $colored_string = "<font color='" . $color . "'>";

  if ($boldface)
    {
      $colored_string .= "<b>";
    }

  $colored_string .= $input_string;

  if ($boldface)
    {
      $colored_string .= "</b>";
    }
  $colored_string .= "</font>"; 

  return ($colored_string);

} #-- End of subroutine color_string

#------------------------------------------------------------------------------
# Generate the array with the info on the experiment(s).
#------------------------------------------------------------------------------
sub create_exp_info
{
  my $subr_name = get_my_name ();

  my ($experiment_dir_list_ref, $experiment_data_ref) = @_;

  my @experiment_dir_list = @{ $experiment_dir_list_ref };
  my @experiment_data     = @{ $experiment_data_ref };

  my @experiment_stats_html = ();
  my $experiment_stats_line; 
  my $plural;

  $plural = ($#experiment_dir_list > 0) ? "s:" : ":";

  $experiment_stats_line  = "<h3>\n";
  $experiment_stats_line .= "Full pathnames to the input experiment" . $plural . "\n";
  $experiment_stats_line .= "</h3>\n";
  $experiment_stats_line .= "<pre>\n";

  for my $i (0 .. $#experiment_dir_list)
    {
      $experiment_stats_line .= $experiment_dir_list[$i] . " (" . $experiment_data[$i]{"start_date"} . ")\n";
    }
  $experiment_stats_line .= "</pre>\n";

  push (@experiment_stats_html, $experiment_stats_line);

  gp_message ("debugXL", $subr_name, "experiment_stats_line = $experiment_stats_line --");

  return (\@experiment_stats_html);

} #-- End of subroutine create_exp_info

#------------------------------------------------------------------------------
# Trivial function to generate a tag.  This has been made a function to ensure
# consistency creating tags and also make it easier to change them.
#------------------------------------------------------------------------------
sub create_function_tag
{
  my $subr_name = get_my_name ();

  my ($tag_id) = @_;

  my $function_tag = "function_tag_" . $tag_id;

  return ($function_tag);

} #-- End of subroutine create_function_tag

#------------------------------------------------------------------------------
# Generate and return a string with the credits.  Note that this also ends
# the HTML formatting controls.
#------------------------------------------------------------------------------
sub create_html_credits
{
  my $subr_name = get_my_name ();

  my $msg;
  my $the_date;

  my @months = qw (January February March April May June July August September October November December); 

  my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime ();

  $year += 1900;

  $the_date = $months[$mon] . " " . $mday . ", " . $year;

  $msg  = "<i>\n";
  $msg .= "Output generated by the $driver_cmd command ";
  $msg .= "on $the_date ";
  $msg .= "(GNU binutils version " . $binutils_version . ")";
  $msg .= "\n";
  $msg .= "</i>";

  gp_message ("debug", $subr_name, "the date = $the_date");

  return (\$msg);

} #-- End of subroutine create_html_credits

#------------------------------------------------------------------------------
# Generate a string that contains all the necessary HTML header information,
# plus a title.
#
# See also https://www.w3schools.com for the details on the features used.
#------------------------------------------------------------------------------
sub create_html_header
{
  my $subr_name = get_my_name ();

  my ($title_ref) = @_;

   my $title = ${ $title_ref };

  my $LANG = $g_locale_settings{"LANG"};
  my $background_color = $g_html_color_scheme{"background_color_page"}; 

  my $html_header; 

  $html_header  = "<!DOCTYPE html public \"-//w3c//dtd html 3.2//en\">\n";
  $html_header .= "<html lang=\"$LANG\">\n";
  $html_header .= "<head>\n";
  $html_header .= "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n";
  $html_header .= "<title>" . $title . "</title>\n";
  $html_header .= "</head>\n";
  $html_header .= "<body lang=\"$LANG\" bgcolor=". $background_color . ">\n"; 
  $html_header .= "<style>\n";
  $html_header .= "div.left {\n";
  $html_header .= "text-align: left;\n";
  $html_header .= "}\n";
  $html_header .= "div.right {\n";
  $html_header .= "text-align: right;\n";
  $html_header .= "}\n";
  $html_header .= "div.center {\n";
  $html_header .= "text-align: center;\n";
  $html_header .= "}\n";
  $html_header .= "div.justify {\n";
  $html_header .= "text-align: justify;\n";
  $html_header .= "}\n";
  $html_header .= "</style>";

  return (\$html_header);

} #-- End of subroutine create_html_header

#------------------------------------------------------------------------------
# Create an HTML page with the warnings.  If there are no warnings, include
# line to this extent.  The alternative is to supporess the entire page, but
# that breaks the consistency in the output.
#------------------------------------------------------------------------------
sub create_html_warnings_page
{
  my $subr_name = get_my_name ();

  my ($outputdir_ref) = @_;

  my $outputdir = ${ $outputdir_ref };

  my $file_title;
  my $html_acknowledgement;
  my $html_end;
  my $html_header;
  my $html_home_left;
  my $html_home_right;
  my $html_title_header;
  my $msg_no_warnings = "There are no warning messages issued.";
  my $page_title;
  my $position_text;
  my $size_text;
 
  my $outfile = $outputdir . $g_html_base_file_name{"warnings"} . ".html";

  gp_message ("debug", $subr_name, "outfile = $outfile");

  open (WARNINGS_OUT, ">", $outfile)
    or die ("unable to open $outfile for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile for writing");

  gp_message ("debug", $subr_name, "building warning file $outfile");

#------------------------------------------------------------------------------
# Get the number of warnings and in debug mode, print the list. 
#------------------------------------------------------------------------------
  my $number_of_warnings = scalar (@g_warning_messages);
  gp_message ("debug", $subr_name, "number_of_warnings = $number_of_warnings");
  
  if ($number_of_warnings > 0)
    {
      for my $i (0 .. $#g_warning_messages)
        {
          print "$g_warning_messages[$i]\n";
          my $msg = "g_warning_messages[$i] = $g_warning_messages[$i]";
          gp_message ("debug", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# Generate some of the structures used in the HTML output.
#------------------------------------------------------------------------------
  $file_title  = "Warning messages";
  $html_header = ${ create_html_header (\$file_title) };
  $html_home_right   = ${ generate_home_link ("right") };

  $page_title    = "Warning Messages";
  $size_text     = "h2"; 
  $position_text = "center";
  $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
  
#-------------------------------------------------------------------------------
# Get the acknowledgement, return to main link, and final html statements.
#-------------------------------------------------------------------------------
  $html_home_left       = ${ generate_home_link ("left") };
  $html_acknowledgement = ${ create_html_credits () };
  $html_end             = ${ terminate_html_document () };

#-------------------------------------------------------------------------------
# Generate the HTML file.
#-------------------------------------------------------------------------------
  print WARNINGS_OUT $html_header;
  print WARNINGS_OUT $html_home_right;
  print WARNINGS_OUT $html_title_header;

  if ($number_of_warnings > 0)
    {
      print WARNINGS_OUT "<pre>\n";
      print WARNINGS_OUT "$_\n" for @g_warning_messages;
      print WARNINGS_OUT "</pre>\n";
    }
  else
    {
      print WARNINGS_OUT $msg_no_warnings;
    }

  print WARNINGS_OUT $html_home_left;
  print WARNINGS_OUT "<br>\n";
  print WARNINGS_OUT $html_acknowledgement;
  print WARNINGS_OUT $html_end;

  close (WARNINGS_OUT);

  return (\$number_of_warnings);

} #-- End of subroutine create_html_warnings_page

#-------------------------------------------------------------------------------
# Create a complete table.
#-------------------------------------------------------------------------------
sub create_table
{
  my $subr_name = get_my_name ();

  my ($experiment_data_ref, $table_definition_ref) = @_;
 
  my @experiment_data  = @{ $experiment_data_ref };
  my @table_definition = @{ $table_definition_ref };

  my @html_exp_table_data = ();
  my $html_header_line;
  my $html_table_line;
  my $html_end_table;

  $html_header_line = ${ create_table_header_exp (\@experiment_data) };

  push (@html_exp_table_data, $html_header_line);

  for my $i (sort keys @table_definition)
    {
      $html_table_line = ${ create_table_entry_exp (\$table_definition[$i]{"name"}, 
                              \$table_definition[$i]{"key"}, \@experiment_data) };
      push (@html_exp_table_data, $html_table_line);

      my $msg = "i = $i html_table_line = $html_table_line";
      gp_message ("debugXL", $subr_name, $msg);
    }

  $html_end_table  = "</table>\n";
  push (@html_exp_table_data, $html_end_table);

  return (\@html_exp_table_data);

} #-- End of subroutine create_table

#-------------------------------------------------------------------------------
# Create one row for the table with experiment info.
#-------------------------------------------------------------------------------
sub create_table_entry_exp
{
  my $subr_name = get_my_name ();

  my ($entry_name_ref, $key_ref, $experiment_data_ref) = @_;

  my $entry_name       = ${ $entry_name_ref };
  my $key              = ${ $key_ref };
  my @experiment_data  = @{ $experiment_data_ref };

  gp_message ("debugXL", $subr_name, "entry_name = $entry_name key = $key");

  my $html_line;

  $html_line  = "<tr><div class=\"left\"><td><b>&nbsp; ";
  $html_line  = "<tr><div class=\"right\"><td><b>&nbsp; ";
  $html_line .= $entry_name;
  $html_line .= " &nbsp;</b></td>";
  for my $i (sort keys @experiment_data)
    {
      if (exists ($experiment_data[$i]{$key}))
        {
          $html_line .= "<td>&nbsp; " . $experiment_data[$i]{$key} . " &nbsp;</td>";
        }
      else
        {
##          gp_message ("assertion", $subr_name, "experiment_data[$i]{$key} does not exist");
          gp_message ("warning", $subr_name, "experiment_data[$i]{$key} does not exist");
        }
    }
  $html_line .= "</div></tr>\n";

  gp_message ("debugXL", $subr_name, "return html_line = $html_line");

  return (\$html_line);

} #-- End of subroutine create_table_entry_exp

#-------------------------------------------------------------------------------
# Create the table header for the experiment info.
#-------------------------------------------------------------------------------
sub create_table_header_exp
{
  my $subr_name = get_my_name ();

  my ($experiment_data_ref) = @_;

  my @experiment_data = @{ $experiment_data_ref };
  my $html_header_line;

  $html_header_line  = "<style>\n";
  $html_header_line .= "table, th, td {\n";
  $html_header_line .= "border: 1px solid black;\n";
  $html_header_line .= "border-collapse: collapse;\n";
  $html_header_line .= "}\n";
  $html_header_line .= "</style>\n";
  $html_header_line .= "</pre>\n";
  $html_header_line .= "<table>\n";
  $html_header_line .= "<tr><div class=\"center\"><th></th>";

  for my $i (sort keys @experiment_data)
    {
      $html_header_line .= "<th>&nbsp; Experiment ID " . $experiment_data[$i]{"exp_id"} . "&nbsp;</th>";
    }
  $html_header_line .= "</div></tr>\n";

  gp_message ("debugXL", $subr_name, "html_header_line = $html_header_line");

  return (\$html_header_line);

} #-- End of subroutine create_table_header_exp

#-------------------------------------------------------------------------------
# Handle where the output should go. If needed, a directory is created where 
# the results will go.
#-------------------------------------------------------------------------------
sub define_the_output_directory
{
  my $subr_name = get_my_name ();

  my ($define_new_output_dir, $overwrite_output_dir) = @_;

  my $outputdir;

#-------------------------------------------------------------------------------
# If neither -o or -O are set, find the next number to be used in the name for 
# the default output directory.
#-------------------------------------------------------------------------------
  if ((not $define_new_output_dir) and (not $overwrite_output_dir))
    {
      my $dir_id = 1;
      while (-d "er.".$dir_id.".html") 
        { $dir_id++; }
      $outputdir = "er.".$dir_id.".html";
    }

  if (-d $outputdir)
    {
#-------------------------------------------------------------------------------
# The -o option is used, but the directory already exists.
#-------------------------------------------------------------------------------
      if ($define_new_output_dir)
        {
          gp_message ("error", $subr_name, "directory $outputdir already exists");
          gp_message ("abort", $subr_name, "use the -O option to overwrite an existing directory");
        }
#-------------------------------------------------------------------------------
# This is a bit risky, so we proceed with caution. The output directory exists,
# but it is okay to overwrite it. It is removed here and created again below.
#-------------------------------------------------------------------------------
      elsif ($overwrite_output_dir)
        {
          my $target_cmd = $g_mapped_cmds{"rm"};
          my $rm_output  = qx ($target_cmd -rf $outputdir);
          my $error_code = ${^CHILD_ERROR_NATIVE};
          if ($error_code != 0)
            {
              gp_message ("error", $subr_name, $rm_output);
              gp_message ("abort", $subr_name, "fatal error when trying to remove $outputdir");
            }
          else
            {
              gp_message ("debug", $subr_name, "directory $outputdir has been removed");
            }
        }
    }
#-------------------------------------------------------------------------------
# When we get here, the fatal scenarios have been cleared and the name for 
# $outputdir is known. Time to create it.
#-------------------------------------------------------------------------------
  if (mkdir ($outputdir, 0777))
    {
      gp_message ("debug", $subr_name, "created output directory $outputdir");
    }
  else 
    {
      gp_message ("abort", $subr_name, "a fatal problem occurred when creating directory $outputdir");
    }

  return ($outputdir);

} #-- End of subroutine define_the_output_directory

#------------------------------------------------------------------------------
# Return the virtual address for the load object.
#
# Note that at this point, $elf_arch is known to be supported.
#
# TBD: Duplications?
#------------------------------------------------------------------------------
sub determine_base_va_address
{
  my $subr_name = get_my_name ();

  my ($executable_name, $base_va_executable, $loadobj, $routine) = @_;

  my $name_loadobject;
  my $base_va_address;

  gp_message ("debugXL", $subr_name, "base_va_executable = $base_va_executable"); 
  gp_message ("debugXL", $subr_name, "loadobj = $loadobj"); 
  gp_message ("debugXL", $subr_name, "routine = $routine");

#------------------------------------------------------------------------------
# Strip the pathname from the load object name.
#------------------------------------------------------------------------------
  $name_loadobject = get_basename ($loadobj);

#------------------------------------------------------------------------------
# If the load object is the executable, return the base address determined 
# earlier.  Otherwise return 0x0.  Note that I am not sure if this is always
# the right thing to do, but for .so files it seems to work out fine.
#------------------------------------------------------------------------------
  if ($name_loadobject eq $executable_name)
    {
      $base_va_address = $base_va_executable;
    }
  else
    {
      $base_va_address = "0x0";
    }
   
  my $decimal_address = bigint::hex ($base_va_address);
  gp_message ("debugXL", $subr_name, "return base_va_address = $base_va_address (decimal: $decimal_address)");

  return ($base_va_address);

} #-- End of subroutine determine_base_va_address

#-------------------------------------------------------------------------------
# Now that we know the map.xml file(s) are present, we can scan these and get
# the required information.
#-------------------------------------------------------------------------------
sub determine_base_virtual_address
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref) = @_;

  my @exp_dir_list   = @{ $exp_dir_list_ref };

  my $full_path_exec;
  my $executable_name;
  my $va_executable_in_hex;

  my $path_to_map_file; 

  for my $exp_dir (keys %g_exp_dir_meta_data)
    {
      $path_to_map_file  = $g_exp_dir_meta_data{$exp_dir}{"directory_path"}; 
      $path_to_map_file .= $exp_dir;
      $path_to_map_file .= "/map.xml";

      ($full_path_exec, $executable_name, $va_executable_in_hex) = extract_info_from_map_xml ($path_to_map_file);

      $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"} = $full_path_exec;
      $g_exp_dir_meta_data{$exp_dir}{"exec_name"}      = $executable_name;
      $g_exp_dir_meta_data{$exp_dir}{"va_base_in_hex"} = $va_executable_in_hex;

      gp_message ("debug", $subr_name, "exp_dir              = $exp_dir");
      gp_message ("debug", $subr_name, "full_path_exece      = $full_path_exec");
      gp_message ("debug", $subr_name, "executable_name      = $executable_name");
      gp_message ("debug", $subr_name, "va_executable_in_hex = $va_executable_in_hex");
    }

  return (0);

} #-- End of subroutine determine_base_virtual_address

#------------------------------------------------------------------------------
# Determine whether the decimal separator is a point or a comma.
#------------------------------------------------------------------------------
sub determine_decimal_separator
{
  my $subr_name = get_my_name ();

  my $ignore_count;
  my $decimal_separator;
  my $convert_to_dot;
  my $field;
  my $target_found; 
  my $error_code;
  my $cmd_output;
  my $target_cmd;
  my @locale_info;

  my $default_decimal_separator = "\\.";

  $target_cmd  = $g_mapped_cmds{locale} . " -k LC_NUMERIC";
  ($error_code, $cmd_output) = execute_system_cmd ($target_cmd);
   
  if ($error_code != 0)
#-------------------------------------------------------------------------------
# This is unlikely to happen, but you never know.  To reduce the nesting level,
# return right here in case of an error.
#-------------------------------------------------------------------------------
    {
      gp_message ("error", $subr_name, "failure to execute the command $target_cmd");
      
      $convert_to_dot = $TRUE;

      return ($error_code, $default_decimal_separator, $convert_to_dot);
    }

#-------------------------------------------------------------------------------
# Scan the locale info and search for the target line of the form 
# decimal_point="<target>" where <target> is either a dot, or a comma.
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
# Split the output into the different lines and scan for the line we need.
#-------------------------------------------------------------------------------
  @locale_info  = split ("\n", $cmd_output);
  $target_found = $FALSE;
  for my $line (@locale_info) 
    {
      chomp ($line);
      gp_message ("debug", $subr_name, "line from locale_info = $line");
      if ($line =~ /decimal_point=/) 
        {

#-------------------------------------------------------------------------------
# Found the target line. Split this line to get the value field.
#-------------------------------------------------------------------------------
          my @split_line = split ("=", $line); 

#-------------------------------------------------------------------------------
# There should be 2 fields. If not, something went wrong.
#-------------------------------------------------------------------------------
          if (scalar @split_line != 2) 
            {
#     if (scalar @split_line == 2) {
#        $target_found    = $FALSE;
#-------------------------------------------------------------------------------
# Remove the newline before printing the variables.
#-------------------------------------------------------------------------------
              $ignore_count = chomp ($line);
              $ignore_count = chomp (@split_line);
              gp_message ("debug", $subr_name, "warning - line $line matches the search, but the decimal separator has the wrong format");
              gp_message ("debug", $subr_name, "warning - the splitted line is [@split_line] and does not contain 2 fields");
              gp_message ("debug", $subr_name, "warning - the default decimal separator will be used");
            }
          else
            {
#-------------------------------------------------------------------------------
# We know there are 2 fields and the second one has the decimal point.
#-------------------------------------------------------------------------------
              gp_message ("debug", $subr_name, "split_line[1] = $split_line[1]");

              chomp ($split_line[1]);
              $field = $split_line[1];

              if (length ($field) != 3)
#-------------------------------------------------------------------------------
# The field still includes the quotes.  Check if the string has length 3, which
# should be the case, but if not, we flag an error.  The error code is set such
# that the callee will know a problem has occurred.
#-------------------------------------------------------------------------------
                {
                  gp_message ("error", $subr_name, "unexpected output from the $target_cmd command: $field");
                  $error_code = 1;
                  last;
                }

              gp_message ("debug", $subr_name, "field = ->$field<-");

              if (($field eq "\".\"") or ($field eq "\",\""))
#-------------------------------------------------------------------------------
# Found the separator.  Capture the character between the quotes. 
#-------------------------------------------------------------------------------
                {
                  $target_found      = $TRUE;
                  $decimal_separator = substr ($field,1,1);
                  gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator--end skip loop");
                  last;
                }
            }
        }
    }
  if (not $target_found) 
    {
      $decimal_separator = $default_decimal_separator;
      gp_message ("warning", $subr_name, "cannot determine the decimal separator - use the default $decimal_separator");
    } 

  if ($decimal_separator ne ".")
    {
      $convert_to_dot = $TRUE;
    }
  else
    {
      $convert_to_dot = $FALSE;
    }

  $decimal_separator = "\\".$decimal_separator;
  $g_locale_settings{"decimal_separator"} = $decimal_separator;
  $g_locale_settings{"convert_to_dot"}    = $convert_to_dot;

  return ($error_code, $decimal_separator, $convert_to_dot);

} #-- End of subroutine determine_decimal_separator

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub dump_function_info
{
  my $subr_name = get_my_name ();

  my ($function_info_ref, $name) = @_;

  my %function_info = %{$function_info_ref};
  my $kip;

  gp_message ("debug", $subr_name, "function_info for $name");
  $kip = 0;
  for my $farray ($function_info{$name})
    {
      for my $elm (@{$farray})
        {
          gp_message ("debug", $subr_name, "$kip: routine = ${$elm}{'routine'}");
          for my $key (sort keys %{$elm})
            {
              if ($key eq "routine")
                {
                  next;
                }
              gp_message ("debug", $subr_name, "$kip: $key = ${$elm}{$key}");
            }
          $kip++;
        }
    }

  return (0);

} #-- End of subroutine dump_function_info

#------------------------------------------------------------------------------
# This is an early scan to find the settings for some options very early on. 
# For practical reasons the main option parsing and handling is done later, 
# but without this early scan, these options will not be enabled until later
# in the execution.
#
# This early scan fixes that, but it is not very elegant to do it this way
# and in the future, this will be improved.  For now it gets the job done.
#------------------------------------------------------------------------------
sub early_scan_specific_options
{
  my $subr_name = get_my_name ();

  my @options_with_value = qw /verbose warnings debug quiet/;
  my $target_option;

  my $ignore_value;
  my $found_option;
  my $option_requires_value;
  my $option_value;
  my $valid_input;
  my @error_messages = ();

  $option_requires_value = $TRUE;
  for (@options_with_value)
    {
      $target_option = $_;
      ($found_option, $option_value) = find_target_option (
                                         \@ARGV, 
                                         $option_requires_value, 
                                         $target_option);
      if ($found_option)
        {
#------------------------------------------------------------------------------
# This part has been set up such that we can support other options too, should
# this become necessary.
#
# A necessary, but limited check for the validity of a value is performed.
# This avoids that an error message shows up twice later on.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# All option values are converted to lower case.  This makes the checks easier.
#------------------------------------------------------------------------------

          if ($target_option eq "verbose")
            {
              my $verbose_value = lc ($option_value);
              $valid_input = verify_if_input_is_valid ($verbose_value, "onoff");
              if ($valid_input)
                {
                   $g_verbose = ($verbose_value eq "on") ? $TRUE : $FALSE;
                   if ($verbose_value eq "on")
#------------------------------------------------------------------------------
# Set the status and disable output buffering in verbose mode.
#------------------------------------------------------------------------------
                     {
                       $g_user_settings{"verbose"}{"current_value"} = "on";
                       STDOUT->autoflush (1);
                     }
                   elsif ($verbose_value eq "off")
                     {
                       $g_user_settings{"verbose"}{"current_value"} = "off";
                     }
                }
              else
                {
                  my $msg = "$option_value is not supported for the verbose option";
                  push (@error_messages, $msg);
                }
            }
          elsif ($target_option eq "warnings")
            {
              my $warnings_value = lc ($option_value);
              $valid_input = verify_if_input_is_valid ($warnings_value, "onoff");
              if ($valid_input)
                {
                   $g_warnings = ($warnings_value eq "on") ? $TRUE : $FALSE;
                   if ($warnings_value eq "on")
#------------------------------------------------------------------------------
# Set the status and disable output buffering if warnings are enabled.
#------------------------------------------------------------------------------
                     {
                       $g_user_settings{"warnings"}{"current_value"} = "on";
                       STDOUT->autoflush (1);
                     }
                   elsif ($warnings_value eq "off")
                     {
                       $g_user_settings{"warnings"}{"current_value"} = "off";
                     }
                }
              else
                {
                  my $msg = "$option_value is not supported for the warnings option";
                  push (@error_messages, $msg);
                }
            }
          elsif ($target_option eq "quiet") 
            {
              my $quiet_value = lc ($option_value);
              $valid_input = verify_if_input_is_valid ($option_value, "onoff");
              if ($valid_input)
                {
                   $g_quiet = ($quiet_value eq "on") ? $TRUE : $FALSE;
                   if ($quiet_value eq "on")
                     {
                       $g_user_settings{"quiet"}{"current_value"} = "on";
                     }
                   elsif ($quiet_value eq "off")
                     {
                       $g_user_settings{"quiet"}{"current_value"} = "off";
                     }
                }
              else
                {
                  my $msg = "$option_value is not supported for the quiet option";
                  push (@error_messages, $msg);
                }
            }
          elsif ($target_option eq "debug") 
            {
              my $debug_value = lc ($option_value);
              $valid_input = verify_if_input_is_valid ($debug_value, "size");
              if ($valid_input)
                {
                   if ($debug_value ne "off")
#------------------------------------------------------------------------------
# Disable output buffering in debug mode.
#------------------------------------------------------------------------------
                     {
                       $g_user_settings{"debug"}{"current_value"} = "on";
                       STDOUT->autoflush (1);
                     }
#------------------------------------------------------------------------------
# This function also sets $g_user_settings{"debug"}{"current_value"}. 
#------------------------------------------------------------------------------
                   my $ignore_value = set_debug_size (\$debug_value);
                }
              else
                {
                  my $msg = "$option_value is not supported for the debug option";
                  push (@error_messages, $msg);
                }
            }
          else
            {
              my $msg = "target option $target_option not expected";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
    }

#------------------------------------------------------------------------------
# Check for input errors.
#------------------------------------------------------------------------------
  my $input_errors = scalar (@error_messages); 
  if ($input_errors > 0)
    {
      my $plural = ($input_errors == 1) ? 
                        "is one error" : "are $input_errors errors";
      print "There " . $plural . " in the options:\n";
      for my $i (0 .. $#error_messages)
        {
          print "- $error_messages[$i]\n";
        }
      exit (0);
    }
#------------------------------------------------------------------------------
# If quiet mode has been enabled, disable verbose, warnings and debug.
#------------------------------------------------------------------------------
  if ($g_quiet)
    {
      $g_user_settings{"verbose"}{"current_value"} = "off";
      $g_user_settings{"warnings"}{"current_value"} = "off";
      $g_user_settings{"debug"}{"current_value"}   = "off";
      $g_verbose  = $FALSE;
      $g_warnings = $FALSE;
      my $debug_off = "off";
      my $ignore_value = set_debug_size (\$debug_off);
    }

  return (0);

} #-- End of subroutine early_scan_specific_options

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub elf_phdr
{
  my $subr_name = get_my_name ();

  my ($elf_loadobjects_found, $elf_arch, $loadobj, $routine, 
      $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR, $elf_rats_ref) = @_;

  my %elf_rats = %{$elf_rats_ref};

  my $return_value;

#------------------------------------------------------------------------------
# TBD. Quick check. Can be moved up the call tree.
#------------------------------------------------------------------------------
    if ( ($elf_arch ne "Linux") and ($elf_arch ne "SunOS") )
      {
        gp_message ("abort", $subr_name, "$elf_arch is not a supported OS");
      }

#------------------------------------------------------------------------------
# TBD: This should not be in a loop over $loadobj and only use the executable.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# TBD: $routine is not really used in these subroutines. Is this a bug?
#------------------------------------------------------------------------------
  if ($elf_loadobjects_found)
    {
      gp_message ("debugXL", $subr_name, "calling elf_phdr_usual");
      $return_value = elf_phdr_usual ($elf_arch, $loadobj, $routine, \%elf_rats);
    }
  else 
    {
      gp_message ("debugXL", $subr_name, "calling elf_phdr_sometimes");
      $return_value = elf_phdr_sometimes ($elf_arch, $loadobj, $routine, $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR);
    }

  gp_message ("debug", $subr_name, "the return value = $return_value");

  if (not $return_value)
    {
      gp_message ("abort", $subr_name, "need to handle a return value of FALSE");
    } 
  return ($return_value);

} #-- End of subroutine elf_phdr

#------------------------------------------------------------------------------
# Return the virtual address for the load object.
#------------------------------------------------------------------------------
sub elf_phdr_sometimes
{
  my $subr_name = get_my_name ();

  my ($elf_arch, $loadobj, $routine, $ARCHIVES_MAP_NAME, 
      $ARCHIVES_MAP_VADDR) = @_;

  my $arch_uname_s = $local_system_config{"kernel_name"};
  my $arch_uname   = $local_system_config{"processor"};
  my $arch         = $g_arch_specific_settings{"arch"};

  gp_message ("debug", $subr_name, "arch_uname_s = $arch_uname_s");
  gp_message ("debug", $subr_name, "arch_uname   = $arch_uname");
  gp_message ("debug", $subr_name, "arch         = $arch");

  my $target_cmd;
  my $command_string; 
  my $error_code;
  my $cmd_output;

  my $line;
  my $blo;

  my $elf_offset;
  my $i;
  my @foo;
  my $foo;
  my $foo1;
  my $p_vaddr;
  my $rc;
  my $archives_file;
  my $loadobj_SAVE;
  my $Offset;
  my $VirtAddr;
  my $PhysAddr;
  my $FileSiz;
  my $MemSiz;
  my $Flg;
  my $Align;

  if ($ARCHIVES_MAP_NAME eq $blo)
    {
      return ($ARCHIVES_MAP_VADDR);
    } 
  else 
    {
      return ($FALSE);
    }

  if ($arch_uname_s ne $elf_arch)
    {
#------------------------------------------------------------------------------
# We are masquerading between systems, must leave
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name, "masquerading arch_uname_s->$arch_uname_s elf_arch->$elf_arch");
      return ($FALSE);
    }
  if ($loadobj eq "DYNAMIC_FUNCTIONS")
#------------------------------------------------------------------------------
# Linux vDSO, leave for now
#------------------------------------------------------------------------------
    {
      return ($FALSE);
    }

# TBD: STILL NEEDED??!!

  $loadobj_SAVE = $loadobj;

  $blo = get_basename ($loadobj);
  gp_message ("debug", $subr_name, "loadobj = $loadobj");
  gp_message ("debug", $subr_name, "blo     = $blo");
  gp_message ("debug", $subr_name, "ARCHIVES_MAP_NAME  = $ARCHIVES_MAP_NAME");
  gp_message ("debug", $subr_name, "ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR");
  if ($ARCHIVES_MAP_NAME eq $blo)
    {
      return ($ARCHIVES_MAP_VADDR);
    } 
  else 
    {
      return ($FALSE);
    }

} #-- End of subroutine elf_phdr_sometimes

#------------------------------------------------------------------------------
# Return the virtual address for the load object.
#
# Note that at this point, $elf_arch is known to be supported.
#------------------------------------------------------------------------------
sub elf_phdr_usual
{
  my $subr_name = get_my_name ();

  my ($elf_arch, $loadobj, $routine, $elf_rats_ref) = @_;

  my %elf_rats = %{$elf_rats_ref};

  my $return_code;
  my $cmd_output;
  my $target_cmd;
  my $command_string; 
  my $error_code;
  my $error_code1;
  my $error_code2;

  my ($elf_offset, $loadobjARC);
  my ($i, @foo, $foo, $foo1, $p_vaddr, $rc);
  my ($Offset, $VirtAddr, $PhysAddr, $FileSiz, $MemSiz, $Flg, $Align);

  my $arch_uname_s = $local_system_config{"kernel_name"};

  gp_message ("debug", $subr_name, "elf_arch = $elf_arch loadobj = $loadobj routine = $routine");

  my ($base, $ignore_value, $ignore_too) = fileparse ($loadobj);
  gp_message ("debug", $subr_name, "base = $base ".basename ($loadobj));

  if ($elf_arch eq "Linux")
    {
      if ($arch_uname_s ne $elf_arch)
        {
#------------------------------------------------------------------------------
# We are masquerading between systems, must leave.
# Maybe we could use ELF_RATS
#------------------------------------------------------------------------------
          gp_message ("debug", $subr_name, "masquerading arch_uname_s->$arch_uname_s elf_arch->$elf_arch");
          return ($FALSE);
        }
      if ($loadobj eq "DYNAMIC_FUNCTIONS")
        {
#------------------------------------------------------------------------------
# Linux vDSO, leave for now
#------------------------------------------------------------------------------
          gp_message ("debug", $subr_name, "early return: loadobj = $loadobj");
          return ($FALSE);
        }

      $target_cmd     = $g_mapped_cmds{"readelf"};
      $command_string = $target_cmd . " -l " . $loadobj . " 2>/dev/null";

      ($error_code1, $cmd_output) = execute_system_cmd ($command_string);

      gp_message ("debug", $subr_name, "executed command_string = $command_string");
      gp_message ("debug", $subr_name, "cmd_output = $cmd_output");

      if ($error_code1 != 0)
        {
          gp_message ("debug", $subr_name, "call failure for $command_string");
#------------------------------------------------------------------------------
# e.g. $loadobj->/usr/lib64/libc-2.17.so
#------------------------------------------------------------------------------
          $loadobjARC = get_basename ($loadobj);
          gp_message ("debug", $subr_name, "seek elf_rats for $loadobjARC");

          if (exists ($elf_rats{$loadobjARC}))
            {
              my $elfoid  = "$elf_rats{$loadobjARC}[1]/archives/$elf_rats{$loadobjARC}[0]";
              $target_cmd     = $g_mapped_cmds{"readelf"};
              $command_string = $target_cmd . "-l " . $elfoid . " 2>/dev/null";
              ($error_code2, $cmd_output) = execute_system_cmd ($command_string);
 
              if ($error_code2 != 0)
                {
                  gp_message ("abort", $subr_name, "call failure for $command_string");
                }
              else
                {
                  gp_message ("debug", $subr_name, "executed command_string = $command_string");
                  gp_message ("debug", $subr_name, "cmd_output = $cmd_output");
                }
            }
          else 
            {
              my $msg =  "elf_rats{$loadobjARC} does not exist";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
#------------------------------------------------------------------------------
# Example output of "readelf -l" on Linux:
#
# Elf file type is EXEC (Executable file)
# Entry point 0x4023a0
# There are 11 program headers, starting at offset 64
# 
# Program Headers:
#   Type           Offset             VirtAddr           PhysAddr
#                  FileSiz            MemSiz              Flags  Align
#   PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
#                  0x0000000000000268 0x0000000000000268  R      8
#   INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
#                  0x000000000000001c 0x000000000000001c  R      1
#       [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
#   LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
#                  0x0000000000001310 0x0000000000001310  R      1000
#   LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000
#                  0x0000000000006515 0x0000000000006515  R E    1000
#   LOAD           0x0000000000009000 0x0000000000409000 0x0000000000409000
#                  0x000000000006f5a8 0x000000000006f5a8  R      1000
#   LOAD           0x0000000000078dc8 0x0000000000479dc8 0x0000000000479dc8
#                  0x000000000000047c 0x0000000000000f80  RW     1000
#   DYNAMIC        0x0000000000078dd8 0x0000000000479dd8 0x0000000000479dd8
#                  0x0000000000000220 0x0000000000000220  RW     8
#   NOTE           0x00000000000002c4 0x00000000004002c4 0x00000000004002c4
#                  0x0000000000000044 0x0000000000000044  R      4
#   GNU_EH_FRAME   0x00000000000777f4 0x00000000004777f4 0x00000000004777f4
#                  0x000000000000020c 0x000000000000020c  R      4
#   GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
#                  0x0000000000000000 0x0000000000000000  RW     10
#   GNU_RELRO      0x0000000000078dc8 0x0000000000479dc8 0x0000000000479dc8
#                  0x0000000000000238 0x0000000000000238  R      1
# 
#  Section to Segment mapping:
#   Segment Sections...
#    00
#    01     .interp
#    02     .interp .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
#    03     .init .plt .text .fini
#    04     .rodata .eh_frame_hdr .eh_frame
#    05     .init_array .fini_array .dynamic .got .got.plt .data .bss
#    06     .dynamic
#    07     .note.gnu.build-id .note.ABI-tag
#    08     .eh_frame_hdr
#    09
#    10     .init_array .fini_array .dynamic .got
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Analyze the ELF information and try to find the virtual address.
#
# Note that the information printed as part of LOAD needs to have "R E" in it.
# In the example output above, the return value would be "0x0000000000402000".
#
# We also need to distinguish two cases.  It could be that the output is on
# a single line, or spread over two lines:
#
#                 Offset   VirtAddr   PhysAddr   FileSiz  MemSiz   Flg Align
#  LOAD           0x000000 0x08048000 0x08048000 0x61b4ae 0x61b4ae R E 0x1000
# or 2 lines
#  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
#                 0x0000000000001010 0x0000000000001010  R E    200000
#------------------------------------------------------------------------------
      @foo = split ("\n",$cmd_output);
      for $i (0 .. $#foo) 
        {
          $foo = $foo[$i];
          chomp ($foo);
          if ($foo =~ /^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$/)
            {
              $Offset   = $1;
              $VirtAddr = $2;
              $PhysAddr = $3;
              $FileSiz  = $4;
              $MemSiz   = $5;
              $Flg      = $6;
              $Align    = $7;

              $elf_offset = $VirtAddr;
              gp_message ("debug", $subr_name, "single line version elf_offset = $elf_offset");
              return ($elf_offset);
            }
          elsif ($foo =~ /^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)$/)
            { 
#------------------------------------------------------------------------------
# is it a two line version?
#------------------------------------------------------------------------------
              $Offset   = $1;
              $VirtAddr = $2; # maybe
              $PhysAddr = $3;
              if ($i != $#foo)
                {
                  $foo1 = $foo[$i + 1]; 
                  chomp ($foo1);
                  if ($foo1 =~ /^\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$/)
                    {
                      $FileSiz  = $1;
                      $MemSiz   = $2;
                      $Flg      = $3;
                      $Align    = $4;
                      $elf_offset = $VirtAddr;
                      gp_message ("debug", $subr_name, "two line version elf_offset = $elf_offset");
                      return ($elf_offset);
                    }
                }
            }
        }
    }
  elsif ($elf_arch eq "SunOS") 
    {
#------------------------------------------------------------------------------
#Program Header[3]:
#    p_vaddr:      0x10000     p_flags:    [ PF_X PF_R ]
# folowed by
#    p_paddr:      0           p_type:     [ PT_LOAD ]
#------------------------------------------------------------------------------
        if ($arch_uname_s ne $elf_arch)
#------------------------------------------------------------------------------
# we are masquerading between systems, must leave
#------------------------------------------------------------------------------
          { 
            gp_message ("debug", $subr_name,"masquerading arch_uname_s = $arch_uname_s elf_arch = $elf_arch");
            return (0);
           }
        $target_cmd     = $g_mapped_cmds{"elfdump"};
        $command_string = $target_cmd . "-p " . $loadobj . " 2>/dev/null";
        ($error_code, $cmd_output) = execute_system_cmd ($command_string);
        if ($error_code != 0)
          {
            gp_message ("debug", $subr_name,"call failure for $command_string");
            die ("$target_cmd call failure");
          }
        my @foo = split ("\n",$cmd_output);
        for $i (0 .. $#foo) 
          {
            $foo = $foo[$i];
            chomp ($foo);
            if ($foo =~ /^\s+p_vaddr:\s+(\S+)\s+p_flags:\s+\[\sPF_X\sPF_R\s\]$/)
              {
                $p_vaddr = $1; # probably
                if ($i != $#foo)
                  {
                    $foo1 = $foo[$i + 1];
                    chomp ($foo1);
                    if ($foo1 =~ /^\s+p_paddr:\s+(\S+)\s+p_type:\s+\[\sPT_LOAD\s\]$/)
                      {
                        $elf_offset = $p_vaddr;
                        return ($elf_offset);
                      }
                  }
              }
          }
      }

} #-- End of subroutine elf_phdr_usual

#------------------------------------------------------------------------------
# Execute a system command.  In case of an error, a non-zero error code is
# returned.  It is upon the caller to decide what to do next.
#------------------------------------------------------------------------------
sub execute_system_cmd
{
  my $subr_name = get_my_name ();

  my ($target_cmd) = @_;

  chomp ($target_cmd);

  my $cmd_output = qx ($target_cmd);
  my $error_code = ${^CHILD_ERROR_NATIVE};

  if ($error_code != 0)
    {
      gp_message ("error", $subr_name, "failure executing command $target_cmd");
      gp_message ("error", $subr_name, "error code = $error_code");
    }
  else
    {
      chomp ($cmd_output);
      gp_message ("debugM", $subr_name, "executed command $target_cmd");
      gp_message ("debugM", $subr_name, "cmd_output = $cmd_output");
    }

  return ($error_code, $cmd_output);

} #-- End of subroutine execute_system_cmd

#------------------------------------------------------------------------------
# Scan the input file, which should be a gprofng generated map.xml file, and 
# extract the relevant information.
#------------------------------------------------------------------------------
sub extract_info_from_map_xml
{
  my $subr_name = get_my_name ();

  my ($input_map_xml_file) = @_;

  my $extracted_information;
  my $input_line;
  my $vaddr;
  my $foffset;
  my $modes;
  my $name_path;
  my $name;

  my $full_path_exec;
  my $executable_name;
  my $va_executable_in_hex; 

  open (MAP_XML, "<", $input_map_xml_file)
    or die ("$subr_name - unable to open file $input_map_xml_file for reading: $!");
  gp_message ("debug", $subr_name, "opened file $input_map_xml_file for reading");

#------------------------------------------------------------------------------
# Scan the file.  We need to find the name of the executable with the mode set 
# to 0x005.  For this entry we have to capture the name, the mode, the virtual 
# address and the offset.
#------------------------------------------------------------------------------
  $extracted_information = $FALSE;
  while (<MAP_XML>)
    {
      $input_line = $_;
      chomp ($input_line);
      gp_message ("debug", $subr_name, "read input_line = $input_line");
      if ($input_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 = $input_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");

#------------------------------------------------------------------------------
# The base virtual address is calculated as vaddr-foffset.  Although Perl 
# handles arithmetic in hex, we take the safe way here.  Maybe overkill, but
# I prefer to be safe than sorry in cases like this.
#------------------------------------------------------------------------------
          $full_path_exec       = $name_path;
          $executable_name      = $name;
          my $result_VA         = bigint::hex ($vaddr) - bigint::hex ($foffset);
          $va_executable_in_hex = sprintf ("0x%016x", $result_VA);

##          $ARCHIVES_MAP_NAME  = $name;
##          $ARCHIVES_MAP_VADDR = $va_executable_in_hex;

##          gp_message ("debug", $subr_name, "set ARCHIVES_MAP_NAME  = $ARCHIVES_MAP_NAME");
##          gp_message ("debug", $subr_name, "set ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR");
          gp_message ("debug", $subr_name, "result_VA            = $result_VA"); 
          gp_message ("debug", $subr_name, "va_executable_in_hex = $va_executable_in_hex"); 

#------------------------------------------------------------------------------
# Stop reading when we found the correct entry.
#------------------------------------------------------------------------------
          if ($modes eq "005")
            {
              $extracted_information = $TRUE;
              last;
            }
        }
    } #-- End of while-loop

  if (not $extracted_information)
    {
      my $msg = "cannot find the necessary information in file $input_map_xml_file";
      gp_message ("assertion", $subr_name, $msg);
    }

  gp_message ("debug", $subr_name, "full_path_exec       = $full_path_exec"); 
  gp_message ("debug", $subr_name, "executable_name      = $executable_name"); 
  gp_message ("debug", $subr_name, "va_executable_in_hex = $va_executable_in_hex"); 

  return ($full_path_exec, $executable_name, $va_executable_in_hex);

} #-- End of subroutine extract_info_from_map_xml

#------------------------------------------------------------------------------
# This routine analyzes the metric line and extracts the metric specifics 
# from it.
# Example input: Exclusive Total CPU Time: e.%totalcpu
#------------------------------------------------------------------------------
sub extract_metric_specifics
{
  my $subr_name = get_my_name ();

  my ($metric_line) = @_;

  my $metric_description;
  my $metric_flavor;
  my $metric_visibility;
  my $metric_name;
  my $metric_spec;

# Ruud   if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) && !($metric =~/^Current/)){
  if (($metric_line =~ /\s*(.+):\s+([ei])([\.\+%]+)(\S*)/) and !($metric_line =~/^Current/))
    {
      gp_message ("debug", $subr_name, "line of interest: $metric_line");

      $metric_description = $1;
      $metric_flavor      = $2;
      $metric_visibility  = $3;
      $metric_name        = $4;

#------------------------------------------------------------------------------
# Although we have captured the metric visibility, the original code removes
# this from the name.  Since the structure is more complicated, the code is
# more tedious as well.  With our new approach we just leave the visibility
# out.
#------------------------------------------------------------------------------
#      $metric_spec        = $metric_flavor.$metric_visibility.$metric_name;

      $metric_spec        = $metric_flavor . "." . $metric_name;

#------------------------------------------------------------------------------
# From the original code:
#
# On x64 systems there are metrics which contain ~ (for example
# DC_access~umask=0 .  When er_print lists them, they come out
# as DC_access%7e%umask=0 (see 6530691).  Untill 6530691 is
# fixed, we need this.  Later we may need something else, or
# things may just work.
#------------------------------------------------------------------------------
#          $metric_spec=~s/\%7e\%/,/;
#          # remove % metric
#          print "DB: before \$metric_spec = $metric_spec\n";

#------------------------------------------------------------------------------
# TBD: I don't know why the "%" symbol is removed.
#------------------------------------------------------------------------------
#          $metric_spec =~ s/\%//;
#          print "DB: after  \$metric_spec = $metric_spec\n";

      return ($metric_spec, $metric_flavor, $metric_visibility, 
              $metric_name, $metric_description);
    }
  else
    {
      return ("skipped", "void");
    }

} #-- End of subroutine extract_metric_specifics

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub extract_source_line_number
{
  my $subr_name = get_my_name ();

  my ($src_times_regex, $function_regex, $number_of_metrics, $input_line) = @_;

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $find_dot_regex = '\.';

  my @fields_in_line = ();
  my $hot_line;
  my $line_id;

#------------------------------------------------------------------------------
# To extract the source line number, we need to distinguish whether this is 
# a line with, or without metrics.
#------------------------------------------------------------------------------
      @fields_in_line = split (" ", $input_line);
      if ( $input_line =~ /$src_times_regex/ )
        {
          $hot_line = $1;
          if ($hot_line eq "##")
#------------------------------------------------------------------------------
# The line id comes after the "##" symbol and the metrics.
#------------------------------------------------------------------------------
            {
              $line_id = $fields_in_line[$number_of_metrics+1];
            }
          else
#------------------------------------------------------------------------------
# The line id comes after the metrics.
#------------------------------------------------------------------------------
            {
              $line_id = $fields_in_line[$number_of_metrics];
            }
        }
      elsif ($input_line =~ /$function_regex/)
        {
          $line_id = "func";
        }
      else
#------------------------------------------------------------------------------
# The line id is the first non-blank element.
#------------------------------------------------------------------------------
        {
          $line_id = $fields_in_line[0];
        }
#------------------------------------------------------------------------------
# Remove the trailing dot.
#------------------------------------------------------------------------------
      $line_id =~ s/$find_dot_regex//;

   return ($line_id);

} #-- End of subroutine extract_source_line_number

#------------------------------------------------------------------------------
# For a give routine name and address, find the index into the 
# function_info array
#------------------------------------------------------------------------------
sub find_index_in_function_info
{
  my $subr_name = get_my_name ();

  my ($routine_ref, $current_address_ref, $function_info_ref) = @_;

  my $routine = ${ $routine_ref };
  my $current_address = ${ $current_address_ref };
  my @function_info = @{ $function_info_ref };

  my $addr_offset;
  my $ref_index;

  gp_message ("debugXL", $subr_name, "find index for routine = $routine and current_address = $current_address");
  if (exists ($g_multi_count_function{$routine}))
    {

# TBD: Scan all of the function_info list. Or beter: add index to g_multi_count_function!!

      gp_message ("debugXL", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
      for my $ref (keys @{ $g_map_function_to_index{$routine} })
        {
          $ref_index = $g_map_function_to_index{$routine}[$ref];

          gp_message ("debugXL", $subr_name, "$routine: retrieving duplicate entry at ref_index = $ref_index");
          gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");

          $addr_offset = $function_info[$ref_index]{"addressobjtext"};
          gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
  
          $addr_offset =~ s/^@\d+://;
          gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
          if ($addr_offset eq $current_address)
            {
              last;
            }
        }
    }
  else
    {
#------------------------------------------------------------------------------
# There is only a single occurrence and it is straightforward to get the index.
#------------------------------------------------------------------------------
      if (exists ($g_map_function_to_index{$routine}))
        {
          $ref_index = $g_map_function_to_index{$routine}[0];
        }
      else
        {
          my $msg = "index for $routine cannot be determined";
          gp_message ("assertion", $subr_name, $msg);
        }
    }

  gp_message ("debugXL", $subr_name, "routine = $routine current_address = $current_address ref_index = $ref_index");

  return (\$ref_index);

} #-- End of subroutine find_index_in_function_info

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub find_keyword_in_string
{
  my $subr_name = get_my_name ();

  my ($target_string_ref, $target_keyword_ref) = @_;

  my $target_string  = ${ $target_string_ref };
  my $target_keyword = ${ $target_keyword_ref };
  my $foundit = $FALSE;

  my @index_values = ();

    my $ret_val = 0;
    my $offset = 0;
    gp_message ("debugXL", $subr_name, "target_string = $target_string");
    $ret_val = index ($target_string, $target_keyword, $offset);
    gp_message ("debugXL", $subr_name, "ret_val = $ret_val");

    if ($ret_val != -1)
      {
        $foundit = $TRUE;
        while ($ret_val != -1)
          {
             push (@index_values, $ret_val);
             $offset = $ret_val + 1;
             gp_message ("debugXL", $subr_name, "ret_val = $ret_val offset = $offset");
             $ret_val = index ($target_string, $target_keyword, $offset);
          }
        for my $i (keys @index_values)
          {
            gp_message ("debugXL", $subr_name, "index_values[$i] = $index_values[$i]");
          }
      }
    else
      {
        gp_message ("debugXL", $subr_name, "target keyword $target_keyword not found");
      }

  return (\$foundit, \@index_values);

} #-- End of subroutine find_keyword_in_string

#------------------------------------------------------------------------------
# Retrieve the absolute path that was used to execute the command.  This path
# is used to execute gp-display-text later on.
#------------------------------------------------------------------------------
sub find_path_to_gp_display_text
{
  my $subr_name = get_my_name ();

  my ($full_command_ref) = @_;

  my $full_command = ${ $full_command_ref };

  my $error_occurred = $TRUE;
  my $return_value;

#------------------------------------------------------------------------------
# Get the path name.
#------------------------------------------------------------------------------
  my ($gp_file_name, $gp_path, $suffix_not_used) = fileparse ($full_command);

  gp_message ("debug", $subr_name, "full_command = $full_command");
  gp_message ("debug", $subr_name, "gp_path  = $gp_path");

  my $gp_display_text_instance = $gp_path . $GP_DISPLAY_TEXT;

#------------------------------------------------------------------------------
# Check if $GP_DISPLAY_TEXT exists, is not empty, and executable.
#------------------------------------------------------------------------------
  if (not -e $gp_display_text_instance)
    {
      $return_value = "file not found";
    }
  else
    {
      if (is_file_empty ($gp_display_text_instance))
        {
          $return_value = "file is empty";
        }
      else
        {
#------------------------------------------------------------------------------
# All is well.  Capture the path.
#------------------------------------------------------------------------------
          $error_occurred = $FALSE;
          $return_value = $gp_path;
        }
    }

  return (\$error_occurred, \$return_value);

} #-- End of subroutine find_path_to_gp_display_text

#------------------------------------------------------------------------------
# Scan the command line to see if the specified option is present.
#
# Two types of options are supported: options without a value (e.g. --help) or
# those that are set to "on" or "off".
#
# In this phase, we only need to check if a value is valid. If it is, we have
# to enable the corresponding global setting.  If the value is not valid, we
# ignore it, since it will be caught later and a warning message is issued.
#------------------------------------------------------------------------------
sub find_target_option
{
  my $subr_name = get_my_name ();

  my ($command_line_ref, $option_requires_value, $target_option) = @_;

  my @command_line     = @{ $command_line_ref };
  my $option_value     = undef;
  my $found_option     = $FALSE;

  my ($command_line_string) = join (" ", @command_line);

##  if ($command_line_string =~ /\s*($target_option)\s*(on|off)*\s*/)
#------------------------------------------------------------------------------
# This does not make any assumptions on the values we are looking for.
#------------------------------------------------------------------------------
  if ($command_line_string =~ /\s*\-\-($target_option)\s*(\w*)\s*/)
    {
      if (defined ($1))
#------------------------------------------------------------------------------
# We have found the option we are looking for.
#------------------------------------------------------------------------------
        {
          $found_option = $TRUE;
          if ($option_requires_value and defined ($2))
#------------------------------------------------------------------------------
# There is a value and it is passed on to the caller.
#------------------------------------------------------------------------------
            {
              $option_value = $2;
            }
        }
    }

  return ($found_option, $option_value);

} #-- End of subroutine find_target_option

#------------------------------------------------------------------------------
# Find the occurrences of non-space characters in a string and return their
# start and end index values(s).
#------------------------------------------------------------------------------
sub find_words_in_line
{
  my $subr_name = get_my_name ();

  my ($input_line_ref) = @_;

  my $input_line = ${ $input_line_ref };

  my $finished = $TRUE;

  my $space = 0;
  my $space_position = 0;
  my $start_word;
  my $end_word;

  my @word_delimiters = ();

  gp_message ("debugXL", $subr_name, "input_line = $input_line");

    $finished = $FALSE;
    while (not $finished)
      {
        $space = index ($input_line, " ", $space_position);

        my $txt = "string search space_position = $space_position ";
        $txt   .= "space = $space";
        gp_message ("debugXL", $subr_name, $txt);

        if ($space != -1)
          {
            if ($space > $space_position)
              {
                $start_word = $space_position;
                $end_word   = $space - 1;
                $space_position = $space;
                my $keyword = substr ($input_line, $start_word, $end_word - $start_word + 1); 
                gp_message ("debugXL", $subr_name, "string search start_word = $start_word end_word = $end_word space_position = $space_position $keyword");
                push (@word_delimiters, [$start_word, $end_word]);
              }
            elsif ( ($space == $space_position) and ($space < length ($input_line) - 1))
              {
                $space          = $space + 1;
                $space_position = $space; 
              }
            else
              {
                print "DONE\n";
                $finished = $TRUE;
                gp_message ("debugXL", $subr_name, "completed - finished = $finished");
              }
          }
        else
          {
            $finished = $TRUE;
            $start_word = $space_position;
            $end_word = length ($input_line) - 1;
            my $keyword = substr ($input_line, $start_word, $end_word - $start_word + 1); 
            push (@word_delimiters, [$start_word, $end_word]);
            if ($keyword =~ /\s+/)
              {
                my $txt = "end search spaces only";
                gp_message ("debugXL", $subr_name, $txt);
              }
            else
              {
                my $txt  = "end search start_word = $start_word ";
                $txt    .= "end_word = $end_word ";
                $txt    .= "space_position = $space_position -->$keyword<--";
                gp_message ("debugXL", $subr_name, $txt);
              }
          }

       }

  for my $i (keys @word_delimiters)
    {
      gp_message ("debugXL", $subr_name, "i = $i $word_delimiters[$i][0] $word_delimiters[$i][1]");
    }

  return (\@word_delimiters);

} #-- End of subroutine find_words_in_line

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub function_info
{ 
  my $subr_name = get_my_name ();

  my ($outputdir, $FUNC_FILE, $metric, $LINUX_vDSO_ref) = @_;

  my %LINUX_vDSO = %{ $LINUX_vDSO_ref };

  my $index_val;
  my $address_decimal;
  my $full_address_field;

  my $FUNC_FILE_NO_PC;
  my $off_with_the_PC; 

  my $blanks;
  my $lblanks;
  my $lvdso_key;
  my $line_regex;

  my %functions_per_metric_indexes = ();
  my %functions_per_metric_first_index = ();
  my @order;

  my ($line,$line_n,$value);
  my ($df_flag,$n,$u);
  my ($metric_value,$PC_Address,$routine);
  my ($is_calls,$metric_ok,$name_regex,$pc_len);
  my ($segment,$offset,$offy,$spaces,$rest,$not_printed,$vdso_key);

#------------------------------------------------------------------------------
# If the directory name does not end with a "/", add it.
#------------------------------------------------------------------------------
  my $length_of_string = length ($outputdir);

  if (rindex ($outputdir, "/") != $length_of_string-1) 
    {
      $outputdir .= "/";
    }

  gp_message ("debug", $subr_name, "on input FUNC_FILE = $FUNC_FILE metric = $metric");

  $is_calls        = $FALSE;
  $metric_ok       = $TRUE;
  $off_with_the_PC = rindex ($FUNC_FILE, "-PC");
  $FUNC_FILE_NO_PC = substr ($FUNC_FILE, 0, $off_with_the_PC);

  if ($FUNC_FILE_NO_PC eq $outputdir."calls.sort.func")
    {
      $FUNC_FILE_NO_PC = $outputdir."calls";
      $is_calls        = $TRUE;
      $metric_ok       = $FALSE;
    } 
  elsif ($FUNC_FILE_NO_PC eq $outputdir."calltree.sort.func")
    {
      $FUNC_FILE_NO_PC = $outputdir."calltree";
      $metric_ok       = $FALSE;
    } 
  elsif ($FUNC_FILE_NO_PC eq $outputdir."functions.sort.func")
    {
      $FUNC_FILE_NO_PC = $outputdir."functions.func";
      $metric_ok       = $FALSE;
    }
  gp_message ("debugM", $subr_name, "set FUNC_FILE_NO_PC = $FUNC_FILE_NO_PC");

  open (FUNC_FILE, "<", $FUNC_FILE)
    or die ("Not able to open file $FUNC_FILE for reading - '$!'");
  gp_message ("debug", $subr_name, "opened file FUNC_FILE = $FUNC_FILE for reading");

  open (FUNC_FILE_NO_PC, ">", $FUNC_FILE_NO_PC)
    or die ("Not able to open file $FUNC_FILE_NO_PC for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file FUNC_FILE_NO_PC = $FUNC_FILE_NO_PC for writing");

  open (FUNC_FILE_REGEXP, "<", "$FUNC_FILE.name-regex")
    or die ("Not able to open file $FUNC_FILE.name-regex for reading - '$!'");
  gp_message ("debug", $subr_name, "opened file FUNC_FILE_REGEXP = $FUNC_FILE.name-regex for reading");

  $name_regex = <FUNC_FILE_REGEXP>;
  chomp ($name_regex);
  close (FUNC_FILE_REGEXP);

  gp_message ("debugXL", $subr_name, "name_regex = $name_regex");

  $n = 0;
  $u = 0;
  $pc_len = 0;

#------------------------------------------------------------------------------
# Note that the double \\ is needed here.  The regex used will not have these.
#------------------------------------------------------------------------------
  if ($is_calls)
    {
#------------------------------------------------------------------------------
# TBD
# I do not see the "*" in my test output, but no harm to leave the code in.
#
# er_print * before PC for calls ! 101315
#------------------------------------------------------------------------------
      $line_regex = "^(\\s*)(\\**)(\\S+)(:)(\\S+)(\\s+)(.*)";
    } 
  else 
    {
      $line_regex = "^(\\s*)(\\S+)(:)(\\S+)(\\s+)(.*)";
    }
  gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." line_regex->$line_regex<-");
  gp_message ("debugXL", $subr_name, "read FUNC_FILE = $FUNC_FILE");

  $line_n = 0;
  $index_val = 0;
  while (<FUNC_FILE>)
    {
      $line = $_;
      chomp ($line);

#      gp_message ("debug", $subr_name, "FUNC_FILE: input line = $line");

      $line_n++;
      if ($line =~ /$line_regex/) # field 2|3 needs to be \S in case of -ve sign
        {
#------------------------------------------------------------------------------
# A typical target line looks like this:
# 11:0x001492e0  6976.900   <additional_timings> _lwp_start
#------------------------------------------------------------------------------
          gp_message ("debugXL", $subr_name, "select = $line");
          if ($is_calls)
            {
              $segment = $3;
              $offset  = $5;
              $spaces  = $6;
              $rest    = $7;
              $PC_Address = $segment.$4.$offset; # PC Addr.
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$3 = $3");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$5 = $5");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$6 = $6");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$7 = $7");
            } 
          else 
            {
              $segment = $2;
              $offset  = $4;
              $spaces  = $5;
              $rest    = $6;
              $PC_Address = $segment.$3.$offset; # PC Addr.
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$2 = $2");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$4 = $4");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$5 = $5");
              gp_message ("debugXL", $subr_name, "is_calls = ".(($is_calls) ? "TRUE" : "FALSE")." \$6 = $6");
            }
          if ($segment == -1)
            {
#------------------------------------------------------------------------------
# presume vDSO field overflow - er_print used an inadequate format
# or the fsummary (MASTER) had the wrong format for -1?
# rats - get ahead of ourselves - should not be a field abuttal so
#------------------------------------------------------------------------------
              if ($line =~ /$name_regex/)
                {
                  if ($metric_ok)
                    {
                      $metric_value = $1; # whatever
                      $routine = $2;
                    } 
                  else 
                    {
                      $routine = $1;
                    }
                  if ($is_calls)
                    {
                      if (substr ($routine,0,1) eq "*")
                        {
                          $routine = substr ($routine,1);
                        }
                    }
                  for $vdso_key (keys %LINUX_vDSO)
                    {
                      if ($routine eq $LINUX_vDSO{$vdso_key})
                        { 
#------------------------------------------------------------------------------
# presume no duplicates - at least can check offset
#------------------------------------------------------------------------------
                          if ($vdso_key =~ /(\d+):(\S+)/)
#------------------------------------------------------------------------------
# no -ve segments allowed and not expected
#------------------------------------------------------------------------------
                            {
                              if ($2 eq $offset)
                                {
#------------------------------------------------------------------------------
# the real segment
#------------------------------------------------------------------------------
                                  $segment = $1; 
                                  gp_message ("debugXL", $subr_name, "rescued segment for $PC_Address($routine)->$segment:$offset $FUNC_FILE");
                                  $PC_Address = $segment.":".$offset; # PC Addr.
                                  gp_message ("debugXL", $subr_name, "vdso line ->$line");
                                  $line = $PC_Address.(' ' x (length ($spaces)-2)).$rest;
                                  gp_message ("debugXL", $subr_name, "becomes   ->$line");
                                  last;
                                }
                            }
                        }
                    }
                } 
              else 
                {
                  gp_message ("debug", $subr_name, "name_regex failure for file $FUNC_FILE");
                }
            }

#------------------------------------------------------------------------------
# a rotten exception for Linux vDSO
# With a BIG "PC Address" like 32767:0x841fecd0, the functions.sort.func_PC file
# can have lines like
#->32767:0x841fecd0161.553   527182898954  131.936    100003     __vdso_gettimeofday<-
#->32767:0x153ff810 42.460   0                   0   __vdso_gettimeofday<-
#->-1:0xff600000   99.040   0                   0   [vsyscall]<-
#  (Real PC Address: 4294967295:0xff600000)
#-> 4294967295:0xff600000   99.040   0                   0   [vsyscall]<-
#-> 9:0x00000020   49.310   0                   0   <static>@0x7fff153ff600 ([vdso])<-
# Rats!
# $LINUX_vDSO{substr($order[$i]{"addressobjtext"},1)} = $order[$i]{"routine"};
#------------------------------------------------------------------------------

          $not_printed = $TRUE;
          for $vdso_key (keys %LINUX_vDSO)
            {
              if ($line =~ /^(\s*)($vdso_key)(.*)$/)
                {
                  $blanks = 1;
                  $rest   = 3;
                  $lblanks = length ($blanks);
                  $lvdso_key = length ($vdso_key);
                  $PC_Address = $vdso_key; # PC Addr.
                  $offy = ($lblanks+$lvdso_key < $pc_len) ? $pc_len : $lblanks+$lvdso_key;
                  gp_message ("debugXL", $subr_name, "offy = $offy for ->$line<-");
                  if ($pc_len)
                    {
                      print FUNC_FILE_NO_PC substr ($line,$offy)."\n";
                      $not_printed = $FALSE;
                    }
                  else
                    {
                      die ("sod1a");
                    }
                  gp_message ("debugXL", $subr_name, "vdso line ->$line");
                  if (substr ($line,$lblanks+$lvdso_key,1) eq " ")
                    { 
#------------------------------------------------------------------------------
# O.K. no field abuttal
#------------------------------------------------------------------------------
                      gp_message ("debugXL", $subr_name, "vdso no field abuttal line ->$line");
                    } 
                  else 
                    {
                      gp_message ("debugXL", $subr_name, "vdso field abuttal line ->$line");
                      $line = $blanks.$vdso_key." ".$rest;
                    }
                  gp_message ("debugXL", $subr_name, "becomes   ->$line");
                  last;
                }
            }
          if ($not_printed)
            {
              if ($pc_len)
                {
                  print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
                }
              else
                {
                  die ("sod1b");
                }
              $not_printed = $FALSE;
            }
        } 
      else 
        {
          if (!$pc_len)
            {
              if ($line =~ /(^\s*PC Addr.\s+)(\S+)/)
                {
                  $pc_len = length ($1); # say 15
                  print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
                } 
              else 
                {
                  print FUNC_FILE_NO_PC "$line\n";
                }
            } 
          else 
            {
              if ($pc_len)
                {
                  my $strlen = length ($line);
                  if ($strlen > 0 ) 
                    {
                      print FUNC_FILE_NO_PC substr ($line,$pc_len)."\n";
                    }
                  else
                    {
                      print FUNC_FILE_NO_PC "\n";
                    }
                }
              else
                {
                  die ("sod2");
                }
            }
          next;
        }
      $routine = "";
      if ($line =~ /$name_regex/)
        {
          if ($metric_ok)
            {
              $metric_value = $1; # whatever
              $routine = $2;
            } 
          else 
            {
              $routine = $1;
            }
        }

      if ($is_calls)
        {
          if (substr ($routine,0,1) eq "*")
            {
              $routine = substr ($routine,1);
            }
        }
      if (length ($routine))
        {
          $order[$index_val]{"routine"} = $routine;
          if ($metric_ok)
            {
              $order[$index_val]{"metric_value"} = $metric_value;
            }
          $order[$index_val]{"PC Address"} = $PC_Address;
          $df_flag = 0;
          if (not exists ($functions_per_metric_indexes{$routine}))
            {
              $functions_per_metric_indexes{$routine} = [$index_val];
            } 
          else 
            {
              push (@{$functions_per_metric_indexes{$routine}},$index_val); # add $RI to list
            }
          gp_message ("debugXL", $subr_name, "updated functions_per_metric_indexes $routine [$index_val] line = $line");
          if ($PC_Address =~ /\s*(\S+):(\S+)/)
            {
              my ($segment,$offset);
              $segment = $1;
              $offset = $2;
              $address_decimal = bigint::hex ($offset); # decimal
              $full_address_field = '@'.$segment.":".$offset; # e.g. @2:0x0003f280
              $order[$index_val]{"addressobj"} = $address_decimal;
              $order[$index_val]{"addressobjtext"} = $full_address_field;
            }
#------------------------------------------------------------------------------
# Check uniqueness
#------------------------------------------------------------------------------
          if (not exists ($functions_per_metric_first_index{$routine}{$PC_Address}))
            {
              $functions_per_metric_first_index{$routine}{$PC_Address} = $index_val;
              $u++; #$RI
            } 
          else 
            {
              if (!($metric eq "calls" || $metric eq "calltree"))
                {
                  gp_message ("debug", $subr_name, "file $FUNC_FILE: function $routine already has a PC Address");
                }
            } 

          $index_val++;
          gp_message ("debugXL", $subr_name, "updated index_val = $index_val");
          $n++;
          next;
        } 
      else 
        {
          if ($n && length ($line))
            {
              my $msg = "unexpected line format in functions file $FUNC_FILE line->$line<-";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
    }
  close (FUNC_FILE);
  close (FUNC_FILE_NO_PC);

  for my $i (sort keys %functions_per_metric_indexes)
    {
      my $values = "";
      for my $fields (sort keys @{ $functions_per_metric_indexes{$i} })
        {
           $values .= "$functions_per_metric_indexes{$i}[$fields] ";
        }
      gp_message ("debugXL", $subr_name, "on return: functions_per_metric_indexes{$i} = $values");
    }

  return (\@order, \%functions_per_metric_first_index, \%functions_per_metric_indexes);

} #-- End of subroutine function_info

#------------------------------------------------------------------------------
# Generate a html header.
#------------------------------------------------------------------------------
sub generate_a_header
{
  my $subr_name = get_my_name ();

  my ($page_text_ref, $size_text_ref, $position_text_ref) = @_;

  my $page_text     = ${ $page_text_ref };
  my $size_text     = ${ $size_text_ref };
  my $position_text = ${ $position_text_ref };
  my $html_header;

  $html_header  = "<div class=\"" . $position_text . "\">\n";
  $html_header .= "<". $size_text . ">\n";
  $html_header .= $page_text . "\n";
  $html_header .= "</". $size_text . ">\n";
  $html_header .= "</div>";

  gp_message ("debugXL", $subr_name, "on exit page_title = $html_header");

  return (\$html_header);

} #-- End of subroutine generate_a_header

#------------------------------------------------------------------------------
# Generate the caller-callee information.
#------------------------------------------------------------------------------
sub generate_caller_callee
{
  my $subr_name = get_my_name ();

  my ($number_of_metrics_ref, $function_info_ref, $function_view_structure_ref, 
      $function_address_info_ref, $addressobjtextm_ref, 
      $input_string_ref) = @_;

  my $number_of_metrics       = ${ $number_of_metrics_ref };
  my @function_info           = @{ $function_info_ref };
  my %function_view_structure = %{ $function_view_structure_ref };
  my %function_address_info   = %{ $function_address_info_ref };
  my %addressobjtextm         = %{ $addressobjtextm_ref };
  my $input_string            = ${ $input_string_ref };

  my @caller_callee_data = ();
  my $outfile;
  my $input_line;

  my $fullname;
  my $separator = "cuthere";

  my @address_field = ();
  my @fields = ();
  my @function_names = ();
  my @marker = ();
  my @metric_values = ();
  my @word_index_values = ();
  my @header_lines = ();

  my $all_metrics;
  my $elements_in_name;
  my $full_hex_address;
  my $hex_address;

  my $file_title; 
  my $page_title; 
  my $size_text; 
  my $position_text; 
  my @html_metric_sort_header = ();
  my $html_header;
  my $html_title_header;
  my $html_home;
  my $html_acknowledgement;
  my $html_end;
  my $html_line;
 
  my $marker_target_function;
  my $max_metrics_length = 0;
  my $metrics_length; 
  my $modified_line; 
  my $name_regex;
  my $no_of_fields;
  my $routine;
  my $routine_length;
  my $string_length; 
  my $top_header; 
  my $total_header_lines;
  my $word_index_values_ref;
  my $infile;

  my $outputdir               = append_forward_slash ($input_string);
  my $LANG                    = $g_locale_settings{"LANG"};
  my $decimal_separator       = $g_locale_settings{"decimal_separator"};

  gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator");
  gp_message ("debug", $subr_name, "outputdir = $outputdir");

  $infile  = $outputdir . "caller-callee-PC2";
  $outfile = $outputdir . $g_html_base_file_name{"caller_callee"} . ".html";

  gp_message ("debug", $subr_name, "infile = $infile outfile = $outfile");

  open (CALLER_CALLEE_IN, "<", $infile) 
    or die ("unable to open caller file $infile for reading - '$!'");
  gp_message ("debug", $subr_name, "opened file $infile for reading");

  open (CALLER_CALLEE_OUT, ">", $outfile)
    or die ("unable to open $outfile for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile for writing");

  gp_message ("debug", $subr_name, "building caller-callee file $outfile");

#------------------------------------------------------------------------------
# Generate some of the structures used in the HTML output.
#------------------------------------------------------------------------------
  $file_title  = "Caller-callee overview";
  $html_header = ${ create_html_header (\$file_title) };
  $html_home   = ${ generate_home_link ("right") };

  $page_title    = "Caller Callee View";
  $size_text     = "h2"; 
  $position_text = "center";
  $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };
  
#------------------------------------------------------------------------------
# Read all of the file into array with the name caller_callee_data.
#------------------------------------------------------------------------------
  chomp (@caller_callee_data = <CALLER_CALLEE_IN>);

#------------------------------------------------------------------------------
# Typical structure of the input file:
#
# Current metrics: address:name:e.totalcpu:e.cycles:e+insts:e+llm
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Functions sorted by metric: Exclusive Total CPU Time
# Callers and callees sorted by metric: Attributed Total CPU Time
# 
# PC Addr.       Name              Attr.     Attr. CPU  Attr.         Attr.
#                                  Total     Cycles     Instructions  Last-Level
#                                  CPU sec.   sec.      Executed      Cache Misses
# 1:0x00000000  *<Total>           3.502     4.005      15396819700   24024250
# 7:0x00008070   start_thread      3.342     3.865      14500538981   23824045
# 6:0x000233a0   __libc_start_main 0.160     0.140        896280719     200205
# 
# PC Addr.       Name              Attr.     Attr. CPU  Attr.         Attr.
#                                  Total     Cycles     Instructions  Last-Level
#                                  CPU sec.   sec.      Executed      Cache Misses
# 2:0x000021f9   driver_mxv        3.342     3.865      14500538981   23824045
# 2:0x000021ae  *mxv_core          3.342     3.865      14500538981   23824045
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Scan the input file.  The first lines are assumed to be part of the header,
# so we store those. The diagnostic lines that echo some settings are also
# stored, but currently not used. 
#------------------------------------------------------------------------------
  my $scan_header = $FALSE;
  my $scan_caller_callee_data = $FALSE;
  my $data_function_block = "";
  my @function_blocks = ();
  my $first = $TRUE;
  my @html_caller_callee = ();
  my @top_level_header = ();

#------------------------------------------------------------------------------
# The regexes.
#------------------------------------------------------------------------------
  my $empty_line_regex       = '^\s*$';
  my $line_of_interest_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(\**)(.*)';
  my $get_hex_address_regex  = '(\d+):0x(\S+)';
  my $get_metric_field_regex = ')\s+([\s\d' . $decimal_separator . ']*)';
  my $header_name_regex      = '(.*\.)(\s+)(Name)\s+(.*)';
  my $sorted_by_regex        = 'sorted by metric:';
  my $current_regex          = '^Current';
  my $get_addr_offset_regex  = '^@\d+:';

#------------------------------------------------------------------------------
# Get the length of the first metric field across all lines.  This value is 
# used to pad the first metric with spaces and get the alignment right.
#
# Scan the input data and find the line(s) with metric values.  A complication
# is that a function name may consists of more than one field.
#
# Note.  This part could be used to parse the other elements of the input file,
# but that makes the loop very complicated.   Instead, we re-scan the data 
# below and process each block separately.
#
# Since this data is all in memory and relatively small, the performance should
# not suffer much, but it does improve the readability of the code.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "determine the maximum length of the first field");

  $g_max_length_first_metric = 0;
  my @hex_addresses = ();
  my @special_marker = ();
  my @the_function_name = ();
  my @the_metrics = ();
  my @length_first_metric = ();

  for (my $line = 0; $line <= $#caller_callee_data; $line++)
    {
      my $input_line = $caller_callee_data[$line];

      if ($input_line =~ /$line_of_interest_regex/)
        {
          if (defined ($1) and defined ($2) and defined ($3))
#------------------------------------------------------------------------------
# This is a line of interest, since it has the address, the function name and 
# the values for the metrics.  Examples of valid lines are:
#
#  2:0x00005028  *xfree_large                             0.              0
# 12:0x0004c2b0   munmap                                  0.143     6402086
#  7:0x0001b2df   <static>@0x1b2df (<libgomp.so.1.0.0>)   0.              0 
#
# The function name marked with a * is the current target.
#------------------------------------------------------------------------------
            {
              my $full_hex_address = $1;
              my $marker           = $2;
              my $remaining_line   = $3;

              if ($full_hex_address =~ /$get_hex_address_regex/)
                {
                  $hex_address = "0x" . $2;
                  push (@hex_addresses, $hex_address); 
                  gp_message ("debugXL", $subr_name, "pushed $hex_address");
                }
              else
                {
                  my $msg = "full_hex_address = $full_hex_address has an unknown format";
                  gp_message ("assertion", $subr_name, $msg);
                }
              if ($marker eq "*")
                {
                  push (@special_marker, "*"); 
                }
              else
                {
                  push (@special_marker, "X"); 
                }
            }
          else
            {
              my $msg = "input_line = $input_line has an unknown format";
              gp_message ("assertion", $subr_name, $msg);
            }

          my @fields_in_line = split (" ", $input_line);

#------------------------------------------------------------------------------
# We stripped the address and marker (if any), off, so this string starts with
# the function name.
#------------------------------------------------------------------------------
              my $remainder              = $3;
              my $number_of_fields       = scalar (@fields_in_line);
              my $words_in_function_name = $number_of_fields - $number_of_metrics - 1;
              my @remainder_array        = split (" ", $remainder);

#------------------------------------------------------------------------------
# If the first metric is 0. (or 0, depending on the locale), the calculation
# of the length needs to be adjusted, because 0. is really 0.000.
#
# While we could easily add 3 to the length, we assign a symbolic value to the
# first metric (ZZZ) and then compute the length.  This makes things clearer.
# I hope ;-)
#------------------------------------------------------------------------------
              my $first_metric = $remainder_array[$words_in_function_name];
              if ($first_metric =~ /^0$decimal_separator$/)
                {
                  gp_message ("debugXL", $subr_name, "fixed up $first_metric");
                  $first_metric = "0.ZZZ";
                }
              push (@length_first_metric, length ($first_metric));

              my $txt = "words in function name = $words_in_function_name ";
              $txt   .= "first_metric = $first_metric length = ";
              $txt   .= length ($first_metric);
              gp_message ("debugXL", $subr_name, $txt);

#------------------------------------------------------------------------------
# Generate the regex for the metrics. 
#
# TBD: This should be an attribute of the function and be done once only.
#------------------------------------------------------------------------------
              my $m_regex = '(\S+';
              for my $f (2 .. $words_in_function_name)
                 {
                   $m_regex .= '\s+\S+';
                 }
#------------------------------------------------------------------------------
# This last part captures all the metric values.
#------------------------------------------------------------------------------
              $m_regex .= $get_metric_field_regex;
              gp_message ("debugXL", $subr_name, "m_regex = $m_regex");
              gp_message ("debugXL", $subr_name, "remainder = $remainder");

              if ($remainder =~ /$m_regex/)
                {
                  my $func_name   = $1;
                  my $its_metrics = $2;
                  my $msg = "found the info - func_name = " . $func_name .
                            " its metrics = " . $its_metrics;
                  gp_message ("debugXL", $subr_name, $msg);

                  push (@the_function_name, $func_name); 
                  push (@the_metrics, $its_metrics); 
                }
              else
                {
                  my $msg = "remainder string $remainder has an unrecognized format";
                  gp_message ("assertion", $subr_name, $msg);
                }

              $g_max_length_first_metric = max ($g_max_length_first_metric, length ($first_metric));

              my $msg = "first_metric = $first_metric " .
                        "g_max_length_first_metric = $g_max_length_first_metric";
              gp_message ("debugXL", $subr_name, $msg);
        }
    }
  gp_message ("debugXL", $subr_name, "final: g_max_length_first_metric = $g_max_length_first_metric");
  gp_message ("debugXL", $subr_name, "#hex_addresses = $#hex_addresses");

#------------------------------------------------------------------------------
# Main loop over the input data.
#------------------------------------------------------------------------------
  my $index_start = 0;  # 1
  my $index_end   = -1;  # 0
  for (my $line = 0; $line <= $#caller_callee_data; $line++)
    {
      my $input_line = $caller_callee_data[$line];

      if ($input_line =~ /$header_name_regex/)
        {
          $scan_header = $TRUE;
          gp_message ("debugXL", $subr_name, "line = $line encountered start of the header scan_header = $scan_header first = $first");
        }
      elsif (($input_line =~ /$sorted_by_regex/) or ($input_line =~ /$current_regex/))
        {
          my $msg =  "line = " . $line . " captured top level header: " .
                     "input_line = " . $input_line;
          gp_message ("debugXL", $subr_name, $msg);

          push (@top_level_header, $input_line);
        }
      elsif ($input_line =~ /$line_of_interest_regex/)
        {
          $index_end++;
          $scan_header             = $FALSE;
          $scan_caller_callee_data = $TRUE;
          $data_function_block    .= $separator . $input_line;

          my $msg = "line = $line updated index_end   = $index_end";
          gp_message ("debugXL", $subr_name, $msg);
        }
      elsif (($input_line =~ /$empty_line_regex/) and ($scan_caller_callee_data))
        {
#------------------------------------------------------------------------------
# An empty line is interpreted as the end of the current block and we process
# this, including the generation of the html code for this block.
#------------------------------------------------------------------------------
          $first = $FALSE;
          $scan_caller_callee_data = $FALSE;

          gp_message ("debugXL", $subr_name, "new block");
          gp_message ("debugXL", $subr_name, "line = $line index_start = $index_start");
          gp_message ("debugXL", $subr_name, "line = $line index_end   = $index_end");
          gp_message ("debugXL", $subr_name, "line = $line data_function_block = $data_function_block");

          push (@function_blocks, $data_function_block);
          my ($html_block_prologue_ref, $html_code_function_block_ref) = 
                                                generate_html_function_blocks (
                                                  \$index_start,
                                                  \$index_end,
                                                  \@hex_addresses,
                                                  \@the_metrics,
                                                  \@length_first_metric,
                                                  \@special_marker,
                                                  \@the_function_name,
                                                  \$separator,
                                                  $number_of_metrics_ref,
                                                  \$data_function_block,
                                                  $function_info_ref,
                                                  $function_view_structure_ref);

          my @html_block_prologue = @{ $html_block_prologue_ref };
          my @html_code_function_block = @{ $html_code_function_block_ref };

          for my $lines (0 .. $#html_code_function_block)
            {
              my $msg = "final html_code_function_block[" . $lines . "] = " .
                        $html_code_function_block[$lines];
              gp_message ("debugXL", $subr_name, $msg);
            }

          $data_function_block = "";

          push (@html_caller_callee, @html_block_prologue);
          push (@html_caller_callee, @header_lines);
          push (@html_caller_callee, @html_code_function_block);

          $index_start = $index_end + 1;
          $index_end   = $index_start - 1;
          gp_message ("debugXL", $subr_name, "line = $line reset index_start = $index_start");
          gp_message ("debugXL", $subr_name, "line = $line reset index_end   = $index_end");
        }

#------------------------------------------------------------------------------
# Only capture the first header.  They are all identical.
#------------------------------------------------------------------------------
      if ($scan_header and $first)
        {
          if (defined ($4))
            {
#------------------------------------------------------------------------------
# This group is only defined for the first line of the header.
#------------------------------------------------------------------------------
              gp_message ("debugXL", $subr_name, "header1 = $4");
              gp_message ("debugXL", $subr_name, "extra   = $3 spaces=x$2x");
              my $newline = "<b>" . $4 . "</b>";
              push (@header_lines, $newline);
            }
          elsif ($input_line =~ /\s*(.*)/)
            {
#------------------------------------------------------------------------------
# Capture the subsequent header lines.
#------------------------------------------------------------------------------
              gp_message ("debugXL", $subr_name, "headern = $1");
              my $newline = "<b>" . $1 . "</b>";
              push (@header_lines, $newline);
            }
        }

    }

  for my $i (0 .. $#header_lines)
    {
      gp_message ("debugXL", $subr_name, "header_lines[$i] = $header_lines[$i]");
    }
  for my $i (0 .. $#function_blocks)
    {
      gp_message ("debugXL", $subr_name, "function_blocks[$i] = $function_blocks[$i]");
    }

  my $number_of_blocks = $#function_blocks + 1;
  gp_message ("debugXL", $subr_name, "There are " . $number_of_blocks . " function blocks:");

  for my $i (0 .. $#function_blocks)
    {
#------------------------------------------------------------------------------
# The split produces an empty first field and is why we skip the first field.
#------------------------------------------------------------------------------
##      my @entries = split ("cuthere", $function_blocks[$i]);
      my @entries = split ($separator, $function_blocks[$i]);
      for my $k (1 .. $#entries)
        {
          my $msg = "entries[" . $k . "] = ". $entries[$k];
          gp_message ("debugXL", $subr_name, $k . $msg);
        }
    }

#------------------------------------------------------------------------------
# Parse and process the individual function blocks.
#------------------------------------------------------------------------------
  for my $i (0 .. $#function_blocks)
    {
      my $msg = "function_blocks[" . $i . "] = ". $function_blocks[$i];
      gp_message ("debugXL", $subr_name, $msg);
#------------------------------------------------------------------------------
# This split produces an empty first field.  This is why skip this.
#------------------------------------------------------------------------------
      my @entries = split ($separator, $function_blocks[$i]);

#------------------------------------------------------------------------------
# An example of @entries:
# <empty>
# 6:0x0003ad20   drand48           0.100     0.084        768240570          0
# 6:0x0003af50  *erand48_r         0.080     0.084        768240570          0
# 6:0x0003b160   __drand48_iterate 0.020     0.                   0          0
#------------------------------------------------------------------------------
      for my $k (1 .. $#entries)
        {
          my $input_line = $entries[$k];

          my $msg = "input_line = entries[" . $k . "] = ". $entries[$k];
          gp_message ("debugXL", $subr_name, $msg);

          @fields = split (" ", $input_line);

          $no_of_fields = $#fields + 1;
          $elements_in_name = $no_of_fields - $number_of_metrics - 1;
     
#------------------------------------------------------------------------------
# TBD: Too restrictive.
# CHECK CODE IN GENERATE_CALLER_CALLEE
#------------------------------------------------------------------------------
          if ($elements_in_name == 1) 
            {
              $name_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+([\s\*])(\S+)\s+(.*)';
            }
          elsif ($elements_in_name == 2) 
            {
              $name_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+([\s\*])((\S+)\s+(\S+))\s+(.*)';
            }
          else
#------------------------------------------------------------------------------
# TBD: Handle this better in case a function entry has more than 2 words.
#------------------------------------------------------------------------------
            {
              my $msg = "$elements_in_name elements in name exceeds limit";
              gp_message ("assertion", $subr_name, $msg);
            }

          if ($input_line =~ /$name_regex/)
            {
              $full_hex_address = $1;
              $marker_target_function = $2;
              $routine = $3;
              if ($elements_in_name == 1) 
                {
                  $all_metrics = $4;
                }
              elsif ($elements_in_name == 2) 
                {
                  $all_metrics = $6;
                }

              $metrics_length = length ($all_metrics);
              $max_metrics_length = max ($max_metrics_length, $metrics_length);

              if ($full_hex_address =~ /(\d+):0x(\S+)/)
                {
                  $hex_address = "0x" . $2;
                }
              push (@marker, $marker_target_function);
              push (@address_field, $hex_address); 
              $modified_line = $all_metrics . " " . $routine;
              push (@metric_values, $all_metrics);
              gp_message ("debugXL", $subr_name, "xxxxxxx = $modified_line");
              push (@function_names, $routine);
            }
        }

      $total_header_lines = $#header_lines + 1;
      gp_message ("debugXL", $subr_name, "total_header_lines = $total_header_lines");

      gp_message ("debugXL", $subr_name, "Final output");
      for my $i (keys @header_lines)
        {
          gp_message ("debugXL", $subr_name, "$header_lines[$i]");
        }
      for my $i (0 .. $#function_names)
        {
          my $msg = $metric_values[$i] . " " . $marker[$i] .
                    $function_names[$i] . "(" . $address_field[$i] . ")";
          gp_message ("debugXL", $subr_name, $msg);
        }
#------------------------------------------------------------------------------
# Check if this function has multiple occurrences.
# TBD: Replace by the function call for this.
#------------------------------------------------------------------------------
      gp_message ("debugXL", $subr_name, "check for multiple occurrences");
      for my $i (0 .. $#function_names)
        {
          my $current_address = $address_field[$i];
          my $found_a_match;
          my $ref_index;
          my $alt_name;
          $routine = $function_names[$i];
          $alt_name = $routine;
          gp_message ("debugXL", $subr_name, "checking for routine = $routine");
          if (exists ($g_multi_count_function{$routine}))
            {

#------------------------------------------------------------------------------
# TBD: Scan all of the function_info list. Or beter: add index to g_multi_count_function!!
#------------------------------------------------------------------------------

              $found_a_match = $FALSE;
              gp_message ("debugXL", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
              for my $ref (keys @{ $g_map_function_to_index{$routine} })
                {
                  $ref_index = $g_map_function_to_index{$routine}[$ref];

                  gp_message ("debugXL", $subr_name, "$routine: retrieving duplicate entry at ref_index = $ref_index");
                  gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");

                  my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
                  gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
  
                  $addr_offset =~ s/$get_addr_offset_regex//;
                  gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");
                  if ($addr_offset eq $current_address)
                    {
                      $found_a_match = $TRUE;
                      last;
                    }
                }
              gp_message ("debugXL", $subr_name, "$function_info[$ref_index]{'alt_name'} is the actual function for i = $i $found_a_match");
              $alt_name = $function_info[$ref_index]{'alt_name'};
            }
          gp_message ("debugXL", $subr_name, "alt_name = $alt_name");
        }
      gp_message ("debugXL", $subr_name, "completed check for multiple occurrences");

#------------------------------------------------------------------------------
# Figure out the column width.  Since the columns in the header may include
# spaces, we use the first line with metrics for this.
#------------------------------------------------------------------------------
      my $top_header = $metric_values[0];
      my $word_index_values_ref = find_words_in_line (\$top_header);
      my @word_index_values = @{ $word_index_values_ref };

# $i = 0 0 4
# $i = 1 10 14
# $i = 2 21 31
# $i = 3 35 42
      for my $i (keys @word_index_values)
        {
          gp_message ("debugXL", $subr_name, "i = $i $word_index_values[$i][0] $word_index_values[$i][1]");
        }
    }

  push (@html_metric_sort_header, "<i>");
  for my $i (0 .. $#top_level_header)
    {
      $html_line = $top_level_header[$i] . "<br>";
      push (@html_metric_sort_header, $html_line);
    }
  push (@html_metric_sort_header, "</i>");

  print CALLER_CALLEE_OUT $html_header;
  print CALLER_CALLEE_OUT $html_home;
  print CALLER_CALLEE_OUT $html_title_header;
  print CALLER_CALLEE_OUT "$_" for @g_html_experiment_stats;
##  print CALLER_CALLEE_OUT "<br>\n";
##  print CALLER_CALLEE_OUT "$_\n" for @html_metric_sort_header;
  print CALLER_CALLEE_OUT "<pre>\n";
  print CALLER_CALLEE_OUT "$_\n" for @html_caller_callee;
  print CALLER_CALLEE_OUT "</pre>\n";

#-------------------------------------------------------------------------------
# Get the acknowledgement, return to main link, and final html statements.
#-------------------------------------------------------------------------------
  $html_home            = ${ generate_home_link ("left") };
  $html_acknowledgement = ${ create_html_credits () };
  $html_end             = ${ terminate_html_document () };

  print CALLER_CALLEE_OUT $html_home;
  print CALLER_CALLEE_OUT "<br>\n";
  print CALLER_CALLEE_OUT $html_acknowledgement;
  print CALLER_CALLEE_OUT $html_end;

  close (CALLER_CALLEE_OUT);

  return (0);

} #-- End of subroutine generate_caller_callee

#------------------------------------------------------------------------------
# Generate the html version of the disassembly file.
#
# Note to self (TBD)
# https://software.intel.com/content/www/us/en/develop/blogs/intel-release-new-technology-specifications-protect-rop-attacks.html
#------------------------------------------------------------------------------
sub generate_dis_html
{
  my $subr_name = get_my_name ();

  my ($target_function_ref, $number_of_metrics_ref, $function_info_ref, 
      $function_address_and_index_ref, $outputdir_ref, $func_ref, 
      $source_line_ref, $metric_ref, $addressobj_index_ref) = @_;

  my $target_function            = ${ $target_function_ref };
  my $number_of_metrics          = ${ $number_of_metrics_ref };
  my @function_info              = @{ $function_info_ref };
  my %function_address_and_index = %{ $function_address_and_index_ref };
  my $outputdir                  = ${ $outputdir_ref };
  my $func                       = ${ $func_ref };
  my @source_line                = @{ $source_line_ref };
  my @metric                     = @{ $metric_ref };
  my %addressobj_index           = %{ $addressobj_index_ref };

  my $dec_instruction_start;
  my $dec_instruction_end;
  my $hex_instruction_start;
  my $hex_instruction_end;

  my @colour_line = ();
  my $hot_line; 
  my $metric_values; 
  my $src_line;
  my $dec_instr_address; 
  my $instruction;
  my $operands;
 
  my $html_new_line = "<br>";
  my $add_new_line_before;
  my $add_new_line_after; 
  my $address_key; 
  my $boldface;
  my $file;
  my $filename = $func;
  my $func_name;
  my $orig_hex_instr_address; 
  my $hex_instr_address; 
  my $index_string;
  my $input_metric;
  my $linenumber;
  my $name;
  my $last_address; 
  my $last_address_in_hex; 

  my $file_title; 
  my $html_header;
  my $html_home;
  my $html_end;
  
  my $branch_regex     = $g_arch_specific_settings{"regex"};
  my $convert_to_dot    = $g_locale_settings{"convert_to_dot"};
  my $decimal_separator = $g_locale_settings{"decimal_separator"};
  my $hp_value          = $g_user_settings{"highlight_percentage"}{"current_value"};
  my $linksubexp        = $g_arch_specific_settings{"linksubexp"};
  my $subexp            = $g_arch_specific_settings{"subexp"};

  my $is_empty;

  my %branch_target = ();
  my %branch_target_no_ref = ();
  my @disassembly_file = ();
  my %extended_branch_target = ();
  my %inverse_branch_target = ();
  my @metrics = ();
  my @modified_html = ();

  my $branch_target_ref;
  my $extended_branch_target_ref;
  my $branch_target_no_ref_ref;

  my $branch_address; 
  my $dec_branch_address; 
  my $found_it;
  my $found_it_ref;
  my $func_name_in_dis_file;
  my $hex_branch_target;
  my $instruction_address; 
  my $instruction_offset; 
  my $link;
  my $modified_line;
  my $raw_hex_branch_target;
  my $src_line_ref;
  my $threshold_line;
  my $html_dis_out = $func . ".html";

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $call_regex = '.*([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)';
  my $line_of_interest_regex = '^#*\s+([\d' . $decimal_separator . '\s+]+)\[\s*(\d+|\?)\]';
  my $white_space_regex = '\s+';
  my $first_integer_regex = '^\d+$';
  my $integer_regex = '\d+';
  my $qmark_regex = '\?';
  my $src_regex = '(\s*)(\d+)\.(.*)';
  my $function_regex = '^(\s*)<Function:\s(.*)>';
  my $end_src_header_regex = "(^\\s+)(\\d+)\\.\\s+(.*)";
  my $end_dis_header_regex = "(^\\s+)(<Function: )(.*)>";
  my $control_flow_1_regex = 'j[a-z]+';
  my $control_flow_2_regex = 'call';
  my $control_flow_3_regex = 'ret';

##  my $function_call_regex2 = '(.*)\s+([0-9a-fA-F]*):\s+(call)\s*0x([0-9a-fA-F]+)\s*';
##  my $endbr_regex          = '\.*([0-9a-fA-F]*):\s+(endbr[32|64])';
#------------------------------------------------------------------------------
# Dynamic. Computed below.
#
# TBD: Try to move these up.
#------------------------------------------------------------------------------
  my $dis_regex;
  my $metric_regex;

  gp_message ("debug", $subr_name, "g_branch_regex = $g_branch_regex");
  gp_message ("debug", $subr_name, "call_regex = $call_regex");
  gp_message ("debug", $subr_name, "g_function_call_v2_regex = $g_function_call_v2_regex");

  my $the_title = set_title ($function_info_ref, $func, "disassembly");

  gp_message ("debug", $subr_name, "the_title = $the_title");

  $file_title      = $the_title;
  $html_header     = ${ create_html_header (\$file_title) };
  $html_home       = ${ generate_home_link ("right") };

  push (@modified_html, $html_header);
  push (@modified_html, $html_home);
  push (@modified_html, "<pre>");
 
#------------------------------------------------------------------------------
# Open the input and output files.
#------------------------------------------------------------------------------
  open (INPUT_DISASSEMBLY, "<", $filename) 
    or die ("$subr_name - unable to open disassembly file $filename for reading: '$!'");
  gp_message ("debug", $subr_name , "opened file $filename for reading");

  open (HTML_OUTPUT, ">", $html_dis_out)
    or die ("$subr_name - unable to open file $html_dis_out for writing: '$!'");
  gp_message ("debug", $subr_name , "opened file $html_dis_out for writing");

#------------------------------------------------------------------------------
# Check if the file is empty
#------------------------------------------------------------------------------
  $is_empty = is_file_empty ($filename);
  if ($is_empty)
    {

#------------------------------------------------------------------------------
# The input file is empty.  Write a message in the html file and exit.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name ,"file $filename is empty");

      my $comment = "No disassembly generated by $tool_name - file $filename is empty";
      my $gp_error_file = $outputdir . "gp-listings.err";

      my $html_empty_file_ref = html_text_empty_file (\$comment, \$gp_error_file);
      my @html_empty_file = @{ $html_empty_file_ref };

      print HTML_OUTPUT "$_\n" for @html_empty_file;

      close (HTML_OUTPUT);

      return (\@source_line);
    }
  else
    {

#------------------------------------------------------------------------------
# Read the file into memory.
#------------------------------------------------------------------------------
      chomp (@disassembly_file = <INPUT_DISASSEMBLY>);
      gp_message ("debug", $subr_name ,"read file $filename into memory");
    }

  my $max_length_first_metric = 0;
  my $src_line_no;

#------------------------------------------------------------------------------
# First scan through the assembly listing.
#------------------------------------------------------------------------------
  for (my $line_no=0; $line_no <= $#disassembly_file; $line_no++)
    {
      my $input_line = $disassembly_file[$line_no];
      gp_message ("debugXL", $subr_name, "[line $line_no] $input_line");

      if ($input_line =~ /$line_of_interest_regex/)
        {

#------------------------------------------------------------------------------
# Found a matching line.  Examples are:
#      0.370                [37]   4021d1:  addsd  %xmm0,%xmm1
#   ## 1.001                [36]   4021d5:  add    $0x1,%rax
#------------------------------------------------------------------------------
          gp_message ("debugXL", $subr_name, "selected line \$1 = $1 \$2 = $2");

          if (defined ($2) and defined($1))
            {
              @metrics = split (/$white_space_regex/ ,$1);
              $src_line_no = $2;
            }
          else 
            {
              my $msg = "$input_line has an unexpected format";
              gp_message ("assertion", $subr_name, $msg);
            }

#------------------------------------------------------------------------------
# Compute the maximum length of the first metric and pad the field from the 
# left later on.  The fractional part is ignored.
#------------------------------------------------------------------------------
          my $first_metric = $metrics[0];
          my $new_length; 
          if ($first_metric =~ /$first_integer_regex/)
            {
              $new_length = length ($first_metric);
            }
          else
            {
              my @fields = split (/$decimal_separator/, $first_metric);
              $new_length = length ($fields[0]);
            }
          $max_length_first_metric = max ($max_length_first_metric, $new_length);
          my $msg;
          $msg = "first_metric = $first_metric " .
                 "max_length_first_metric = $max_length_first_metric";
          gp_message ("debugXL", $subr_name, $msg);

          if ($src_line_no !~ /$qmark_regex/)
#------------------------------------------------------------------------------
# The source code line number is known and is stored.
#------------------------------------------------------------------------------
            {
              $source_line[$line_no] = $src_line_no;
              my $msg; 
              $msg  = "found an instruction with a source line ref: ";
              $msg .= "source_line[$line_no] = $source_line[$line_no]";
              gp_message ("debugXL", $subr_name, $msg);
            }
            
#------------------------------------------------------------------------------
# Check for function calls.  If found, get the address offset from $4 and 
# compute the target address.
#------------------------------------------------------------------------------
          ($found_it_ref, $branch_target_ref, $extended_branch_target_ref) = 
                                                 check_and_proc_dis_func_call (
                                                   \$input_line,
                                                   \$line_no, 
                                                   \%branch_target,
                                                   \%extended_branch_target);
          $found_it = ${ $found_it_ref };

          if ($found_it)
            {
              %branch_target = %{ $branch_target_ref };
              %extended_branch_target = %{ $extended_branch_target_ref };
            }

#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
          ($found_it_ref, $branch_target_ref, $extended_branch_target_ref,
           $branch_target_no_ref_ref) = check_and_proc_dis_branches (
                                               \$input_line,
                                               \$line_no, 
                                               \%branch_target,
                                               \%extended_branch_target,
                                               \%branch_target_no_ref);
          $found_it = ${ $found_it_ref };

          if ($found_it)
            {
              %branch_target = %{ $branch_target_ref };
              %extended_branch_target = %{ $extended_branch_target_ref };
              %branch_target_no_ref = %{ $branch_target_no_ref_ref };
            }
        }
    } #-- End of loop over line_no

  %inverse_branch_target = reverse (%extended_branch_target);

  gp_message ("debug", $subr_name, "generated inverse of branch target structure");
  gp_message ("debug", $subr_name, "completed parsing file $filename");

  for my $key (sort keys %branch_target)
    {
      gp_message ("debug", $subr_name, "branch_target{$key} = $branch_target{$key}");
    }
  for my $key (sort keys %extended_branch_target)
    {
      gp_message ("debug", $subr_name, "extended_branch_target{$key} = $extended_branch_target{$key}");
    }
  for my $key (sort keys %inverse_branch_target)
    {
      gp_message ("debug", $subr_name, "inverse_branch_target{$key} = $inverse_branch_target{$key}");
    }
  for my $key (sort keys %branch_target_no_ref)
    {
      gp_message ("debug", $subr_name, "branch_target_no_ref{$key} = $branch_target_no_ref{$key}");
      $inverse_branch_target{$key} = $key;
    }
  for my $key (sort keys %inverse_branch_target)
    {
      gp_message ("debug", $subr_name, "inverse_branch_target{$key} = $inverse_branch_target{$key}");
    }

#------------------------------------------------------------------------------
# Process the disassembly.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Dynamically generate the regexes.
#------------------------------------------------------------------------------
  $metric_regex = '';
  for my $metric_used (1 .. $number_of_metrics)
    {
      $metric_regex .= '(\d+' . $decimal_separator . '*\d*)\s+';
    }

  $dis_regex  = '^(#{2}|\s{2})\s+';
  $dis_regex .= '(.*)';
##  $dis_regex .= '\[\s*([0-9?]+)\]\s+([0-9a-fA-F]+):\s+([a-z0-9]+)\s+(.*)';
  $dis_regex .= '\[\s*([0-9?]+)\]\s+([0-9a-fA-F]+):\s+([a-z0-9]+)(.*)';

  gp_message ("debugXL", $subr_name, "metric_regex = $metric_regex");
  gp_message ("debugXL", $subr_name, "dis_regex    = $dis_regex");
  gp_message ("debugXL", $subr_name, "src_regex    = $src_regex");
  gp_message ("debugXL", $subr_name, "contents of lines array");

#------------------------------------------------------------------------------
# Identify the header lines.  Make the minimal assumptions.
#
# In both cases, the first line after the header has whitespace.  This is
# followed by:
#
# - A source line file has "<line_no>." 
# - A dissasembly file has "<Function:"
#
# These are the characteristics we use below.
#------------------------------------------------------------------------------
  for (my $line_no=0; $line_no <= $#disassembly_file; $line_no++)
    {
      my $input_line = $disassembly_file[$line_no];
      gp_message ("debugXL", $subr_name, "[line $line_no] $input_line");

      if ($input_line =~ /$end_src_header_regex/)
        {
          gp_message ("debugXL", $subr_name, "header time is over - hit source line\n");
          gp_message ("debugXL", $subr_name, "$1 $2 $3\n");
          last;
        }
      if ($input_line =~ /$end_dis_header_regex/)
        {
          gp_message ("debugXL", $subr_name, "header time is over - hit disassembly line\n");
          last;
        }
      push (@modified_html, "<i>" . $input_line . "</i>");
      
    }
  my $line_index = scalar (@modified_html);
  gp_message ("debugXL", $subr_name, "final line_index = $line_index");

  for (my $line_no=0; $line_no <= $line_index-1; $line_no++)
    {
      my $msg = " modified_html[$line_no] = $modified_html[$line_no]";
      gp_message ("debugXL", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Source line:
#  20.       for (int64_t r=0; r<repeat_count; r++) {
#
# Disassembly:
#    0.340                [37]   401fec:  addsd   %xmm0,%xmm1
# ## 1.311                [36]   401ff0:  addq    $1,%rax
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Find the hot PCs and store them.
#------------------------------------------------------------------------------
  my @hot_program_counters = ();
  my @transposed_hot_pc = ();
  my @max_metric_values = ();

  gp_message ("debug", $subr_name, "determine the maximum metric values");
  for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
    {
      my $input_line = $disassembly_file[$line_no];

      if ( $input_line =~ /$dis_regex/ )
        {
##          if ( defined ($1) and defined ($2) and defined ($3) and
##               defined ($4) and defined ($5) and defined ($6) )
          if ( defined ($1) and defined ($2) and defined ($3) and
               defined ($4) and defined ($5) )
            {
              $hot_line      = $1;
              $metric_values = $2;
              $src_line      = $3;
              $dec_instr_address = bigint::hex ($4);
              $instruction   = $5;
              if (defined ($6))
                {
                  my $white_space_regex = '\s*';
                  $operands = $6;
                  $operands =~ s/$white_space_regex//;
                }

              if ($hot_line eq "##")
                {
                  my @metrics = split (" ", $metric_values);
                  push (@hot_program_counters, [@metrics]);
                }
            }
        }
    }
  for my $row (keys @hot_program_counters)
    {
      my $msg = "$filename row[" . $row . "] = ";
      for my $col (keys @{$hot_program_counters[$row]})
        {
          $msg .= "$hot_program_counters[$row][$col] "; 
          $transposed_hot_pc[$col][$row] = $hot_program_counters[$row][$col]; 
        }
      gp_message ("debugXL", $subr_name, "hot PC = $msg");
    }
  for my $row (keys @transposed_hot_pc)
    {
      my $msg = "$filename row[" . $row . "] = ";
      for my $col (keys @{$transposed_hot_pc[$row]})
        {
          $msg .= "$transposed_hot_pc[$row][$col] "; 
        }
      gp_message ("debugXL", $subr_name, "$filename transposed = $msg");
    }
#------------------------------------------------------------------------------
# Get the maximum metric values and if integer, convert to floating-point.
# Since it is easier, we transpose the array and access it over the columns.
#------------------------------------------------------------------------------
  for my $row (0 .. $#transposed_hot_pc)
    {
      my $max_val = 0;
      for my $col (0 .. $#{$transposed_hot_pc[$row]})
        {
          $max_val = max ($transposed_hot_pc[$row][$col], $max_val);;
        }
      if ($max_val =~ /$integer_regex/)
        {
          $max_val = sprintf ("%f", $max_val);
        }
      gp_message ("debugXL", $subr_name, "$filename row = $row max_val = $max_val");
      push (@max_metric_values, $max_val);
    }

    for my $metric (0 .. $#max_metric_values)
      {
        my $msg = "$filename maximum[$metric] = $max_metric_values[$metric]";
        gp_message ("debugM", $subr_name, $msg);
      }

#------------------------------------------------------------------------------
# TBD - Integrate this better.
#
# Scan the instructions to find the instruction address range.  This is used
# to determine if a branch is external to this function.
#------------------------------------------------------------------------------
  $dec_instruction_start = undef;
  $dec_instruction_end   = undef;
  for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
    {
      my $input_line = $disassembly_file[$line_no];
      if ( $input_line =~ /$dis_regex/ )
        {
#          if ( defined ($1) and defined ($2) and defined ($3) and
##               defined ($4) and defined ($5) and defined ($6) )
          if ( defined ($1) and defined ($2) and defined ($3) and
               defined ($4) and defined ($5) )
            {
              $hot_line      = $1;
              $metric_values = $2;
              $src_line      = $3;
              $dec_instr_address = bigint::hex ($4);
              $instruction   = $5;
##              $operands      = $6;
              if (defined ($6))
                {
                  my $white_space_regex = '\s*';
                  $operands = $6;
                  $operands =~ s/$white_space_regex//;
                }

              if (defined ($dec_instruction_start))
                {
                  if ($dec_instr_address < $dec_instruction_start) 
                    {
                      $dec_instruction_start = $dec_instr_address;
                    }
                }
              else
                {
                  $dec_instruction_start = $dec_instr_address;
                }
              if (defined ($dec_instruction_end))
                {
                  if ($dec_instr_address > $dec_instruction_end) 
                    {
                      $dec_instruction_end = $dec_instr_address;
                    }
                }
              else
                {
                  $dec_instruction_end = $dec_instr_address;
                }
            }
        }
    }

  if (defined ($dec_instruction_start) and defined ($dec_instruction_end))
    {
      $hex_instruction_start = sprintf ("%x", $dec_instruction_start);
      $hex_instruction_end = sprintf ("%x", $dec_instruction_end);

      my $msg;
      $msg = "$filename $func dec_instruction_start = " .
             "$dec_instruction_start (0x$hex_instruction_start)";
      gp_message ("debugXL", $subr_name, $msg);
      $msg = "$filename $func dec_instruction_end   = " .
             "$dec_instruction_end (0x$hex_instruction_end)";
      gp_message ("debugXL", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# This is where all the results from above come together.
#------------------------------------------------------------------------------
  for (my $line_no=$line_index-1; $line_no <= $#disassembly_file; $line_no++)
    {
      my $input_line = $disassembly_file[$line_no];
      gp_message ("debugXL", $subr_name, "input_line[$line_no] = $input_line");
      if ( $input_line =~ /$dis_regex/ )
        {
          gp_message ("debugXL", $subr_name, "found a disassembly line: $input_line");

          if ( defined ($1) and defined ($2) and defined ($3) and
               defined ($4) and defined ($5) )
            {
#                      $branch_target{$hex_branch_target} = 1;
#                      $extended_branch_target{$instruction_address} = $raw_hex_branch_target;
              $hot_line      = $1;
              $metric_values = $2;
              $src_line      = $3;
              $orig_hex_instr_address = $4;
              $instruction   = $5;
##              $operands      = $6;

              my $msg = "disassembly line: $1 $2 $3 $4 $5";
              if (defined ($6))
                {
                  $msg .= " \$6 = $6";
                  my $white_space_regex = '\s*';
                  $operands = $6;
                  $operands =~ s/$white_space_regex//;
                }
              gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# Pad the line with the metrics to ensure correct alignment.
#------------------------------------------------------------------------------
              my $the_length; 
              my @split_metrics = split (" ", $metric_values);
              my $first_metric = $split_metrics[0];
##              if ($first_metric =~ /^\d+$/)
              if ($first_metric =~ /$first_integer_regex/)
                {
                  $the_length = length ($first_metric);
                }
              else
                {
                  my @fields = split (/$decimal_separator/, $first_metric);
                  $the_length = length ($fields[0]);
                }
              my $spaces = $max_length_first_metric - $the_length;
              my $pad = "";
              for my $p (1 .. $spaces)
                {
                  $pad .= "&nbsp;";
                }
              $metric_values = $pad . $metric_values;
              gp_message ("debugXL", $subr_name, "pad = $pad");
              gp_message ("debugXL", $subr_name, "metric_values = $metric_values");

#------------------------------------------------------------------------------
# Since the instruction address variable may change and because we need the
# original address without html controls, we use a new variable for the 
# (potentially) modified address.
#------------------------------------------------------------------------------
              $hex_instr_address   = $orig_hex_instr_address;
              $add_new_line_before = $FALSE;
              $add_new_line_after  = $FALSE;

              if ($src_line eq "?")

#------------------------------------------------------------------------------
# There is no source line number.  Do not add a link.
#------------------------------------------------------------------------------
                {
                  $modified_line = $hot_line . ' ' . $metric_values . ' [' . $src_line . '] ';
                  gp_message ("debugXL", $subr_name, "initialized modified_line = $modified_line");
                }
              else
                {
#------------------------------------------------------------------------------
# There is a source line number.  Mark it as link.
#------------------------------------------------------------------------------
                  $src_line_ref = "[<a href='#line_".$src_line."'>".$src_line."</a>]";
                  gp_message ("debugXL", $subr_name, "src_line_ref = $src_line_ref");
                  gp_message ("debugXL", $subr_name, "hex_instr_address = $hex_instr_address");

                  $modified_line = $hot_line . ' ' . $metric_values . ' ' . $src_line_ref . ' ';
                  gp_message ("debugXL", $subr_name, "initialized modified_line = $modified_line");
                }

#------------------------------------------------------------------------------
# Mark control flow instructions.  Several cases need to be distinguished.
#
# In all cases we give the instruction a specific color, mark it boldface
# and add a new-line after the instruction 
#------------------------------------------------------------------------------
              if ( ($instruction =~ /$control_flow_1_regex/)   or
                   ($instruction =~ /$control_flow_2_regex/)   or
                   ($instruction =~ /$control_flow_3_regex/) )
                {
                  gp_message ("debugXL", $subr_name, "instruction = $instruction is a control flow instruction");

                  $add_new_line_after = $TRUE;

                  $boldface = $TRUE;
                  $instruction = color_string ($instruction, $boldface, $g_html_color_scheme{"control_flow"});
                }

              if (exists ($extended_branch_target{$hex_instr_address}))
#------------------------------------------------------------------------------
# This is a branch instruction and we need to add the target address.
#
# In case the target address is outside of this load object, the link is
# colored differently.
#
# TBD: Add the name and if possible, a working link to this code.
#------------------------------------------------------------------------------
                {
                  $branch_address = $extended_branch_target{$hex_instr_address};

                  $dec_branch_address = bigint::hex ($branch_address);

                  if ( ($dec_branch_address >= $dec_instruction_start) and
                       ($dec_branch_address <= $dec_instruction_end) )
#------------------------------------------------------------------------------
# The instruction is within the range.
#------------------------------------------------------------------------------
                    {
                      $link = "[ <a href='#".$branch_address."'>".$branch_address."</a> ]";
                    }
                  else
                    {
#------------------------------------------------------------------------------
# The instruction is outside of the range.  Change the color of the link.
#------------------------------------------------------------------------------
                      gp_message ("debugXL", $subr_name, "address is outside of range");

                      $link = "[ <a href='#".$branch_address;
                      $link .= "' style='color:$g_html_color_scheme{'link_outside_range'}'>";
                      $link .= $branch_address."</a> ]";
                    }
                  gp_message ("debugXL", $subr_name, "address exists new link = $link");

                  $operands .= ' ' . $link;
                  gp_message ("debugXL", $subr_name, "update #1 modified_line = $modified_line");
                }
              if (exists ($branch_target_no_ref{$hex_instr_address}))
                {
                  gp_message ("debugXL", $subr_name, "NEWBR branch_target_no_ref{$hex_instr_address} = $branch_target_no_ref{$hex_instr_address}");
                }
##              if (exists ($inverse_branch_target{$hex_instr_address}) or
##                  exists ($branch_target_no_ref{$hex_instr_address}))
              if (exists ($inverse_branch_target{$hex_instr_address})) 
#------------------------------------------------------------------------------
# This is a target address and we need to define the instruction address to be
# a label.
#------------------------------------------------------------------------------
                {
                  $add_new_line_before = $TRUE;

                  my $branch_target = $inverse_branch_target{$hex_instr_address};
                  my $target = "<a name='".$hex_instr_address."'><b>".$hex_instr_address."</b></a>:";
                  gp_message ("debugXL", $subr_name, "inverse exists - hex_instr_address = $hex_instr_address");
                  gp_message ("debugXL", $subr_name, "inverse exists - add a target target = $target");

                  $hex_instr_address = "<a name='".$hex_instr_address."'><b>".$hex_instr_address."</b></a>";
                  gp_message ("debugXL", $subr_name, "update #2 hex_instr_address = $hex_instr_address");
                  gp_message ("debugXL", $subr_name, "update #2 modified_line     = $modified_line");
                }

              $modified_line .= $hex_instr_address . ': ' . $instruction . ' ' . $operands;

              gp_message ("debugXL", $subr_name, "final modified_line = $modified_line");

#------------------------------------------------------------------------------
# This is a control flow instruction, but it is the last one and we do not 
# want to add a newline.
#------------------------------------------------------------------------------
              gp_message ("debugXL", $subr_name, "decide where the <br> should go in the html");
              gp_message ("debugXL", $subr_name, "add_new_line_after  = $add_new_line_after");
              gp_message ("debugXL", $subr_name, "add_new_line_before = $add_new_line_before");
                
              if ( $add_new_line_after and ($orig_hex_instr_address eq $hex_instruction_end) )
                {
                  $add_new_line_after = $FALSE;
                  gp_message ("debugXL", $subr_name, "$instruction is the last instruction - do not add a newline");
                }

              if ($add_new_line_before)
                {

#------------------------------------------------------------------------------
# Get the previous line, if any, so that we can check what it is.
#------------------------------------------------------------------------------
                  my $prev_line = pop (@modified_html);
                  if ( defined ($prev_line) )
                    {
                      gp_message ("debugXL", $subr_name, "prev_line = $prev_line");

#------------------------------------------------------------------------------
# Restore the previously popped line.
#------------------------------------------------------------------------------
                      push (@modified_html, $prev_line);
                      if ($prev_line ne $html_new_line)
                        {
                          gp_message ("debugXL", $subr_name, "add_new_line_before = $add_new_line_before pushed $html_new_line");
#------------------------------------------------------------------------------
# There is no new-line yet, so add it.
#------------------------------------------------------------------------------
                          push (@modified_html, $html_new_line);
                        }
                      else
                        {
#------------------------------------------------------------------------------
# It was a new-line, so do nothing and continue.
#------------------------------------------------------------------------------
                          gp_message ("debugXL", $subr_name, "need to restore $html_new_line");
                        }
                    }
                }
#------------------------------------------------------------------------------
# Add the newly created line.
#------------------------------------------------------------------------------

              if ($hot_line eq "##")
#------------------------------------------------------------------------------
# Highlight the most expensive line.
#------------------------------------------------------------------------------
                {
                  $modified_line = set_background_color_string (
                                 $modified_line, 
                                 $g_html_color_scheme{"background_color_hot"});
                }
#------------------------------------------------------------------------------
# Sub-highlight the lines close enough to the hot line.
#------------------------------------------------------------------------------
              else
                {
                  my @current_metrics = split (" ", $metric_values);
                  for my $metric (0 .. $#current_metrics)
                    {
                      my $current_value; 
                      my $max_value;
                      $current_value = $current_metrics[$metric];
#------------------------------------------------------------------------------
# As part of the padding process, non-breaking spaces may have been inserted
# in an earlier phase.  Temporarily remove these to make sure that the maximum
# metric values can be computed.
#------------------------------------------------------------------------------
                      $current_value =~ s/&nbsp;//g;
                      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");
                              my $relative_distance = 1.00 - abs ( ($max_value - $current_value)/$max_value );
                              gp_message ("debugXL", $subr_name, "relative_distance = $relative_distance");
                              if (($hp_value > 0) and ($relative_distance >= $hp_value/100.0))
                                {
                                  gp_message ("debugXL", $subr_name, "metric $metric is within the relative_distance");
                                  gp_message ("debugXL", $subr_name, "change bg modified_line = $modified_line");
                                  $modified_line = set_background_color_string (
                                                     $modified_line, 
                                                     $g_html_color_scheme{"background_color_lukewarm"});
                                  last;
                                }
                            }
                        }
                    }
                }

##  my @max_metric_values = ();
              push (@modified_html, $modified_line);
              if ($add_new_line_after)
                {
                  gp_message ("debugXL", $subr_name, "add_new_line_after = $add_new_line_after pushed $html_new_line");
                  push (@modified_html, $html_new_line);
                }

            }
          else
            {
              my $msg = "parsing line $input_line";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
      elsif ( $input_line =~ /$src_regex/ )
        {
          if ( defined ($1) and defined ($2) )
            {
####### BUG?
              gp_message ("debugXL", $subr_name, "found a source code line: $input_line");
              gp_message ("debugXL", $subr_name, "\$1 = $1");
              gp_message ("debugXL", $subr_name, "\$2 = $2");
              gp_message ("debugXL", $subr_name, "\$3 = $3");
              my $blanks        = $1;
              my $src_line      = $2;
              my $src_code      = $3;

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
              $src_code =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

              my $target = "<a name='line_".$src_line."'>".$src_line.".</a>";
              gp_message ("debugXL", $subr_name, "src target = $target $src_code");

              my $modified_line = $blanks . $target . $src_code;
              gp_message ("debugXL", $subr_name, "modified_line = $modified_line");
              push (@modified_html, $modified_line);
            }
          else
            {
              my $msg = "parsing line $input_line";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
      elsif ( $input_line =~ /$function_regex/ )
        {
          my $html_name;
          if (defined ($1) and defined ($2))
            {
              $func_name_in_dis_file = $2;
              my $spaces = $1;
              my $boldface = $TRUE;
              gp_message ("debugXL", $subr_name, "function_name = $2");
              my $function_line       = "&lt;Function: " . $func_name_in_dis_file . ">"; 

##### HACK 

              if ($func_name_in_dis_file eq $target_function)
                {
                  my $color_function_name = color_string (
                                 $function_line, 
                                 $boldface, 
                                 $g_html_color_scheme{"target_function_name"});
                  my $label = "<a id=\"" . $g_function_tag_id{$target_function} . "\"></a>";
                  $html_name = $label . $spaces . "<i>" . $color_function_name . "</i>";
                }
              else
                {
                  my $color_function_name = color_string (
                             $function_line, 
                             $boldface, 
                             $g_html_color_scheme{"non_target_function_name"});
                  $html_name = "<i>" . $spaces . $color_function_name . "</i>";
                }
              push (@modified_html, $html_name);
            }
          else
            {
              my $msg = "parsing line $input_line";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
    }

#------------------------------------------------------------------------------
# Add an extra line with diagnostics.
#
# TBD: The same is done in process_source but should be done only once.
#------------------------------------------------------------------------------
  if ($hp_value > 0) 
    {
      my $rounded_percentage = sprintf ("%.1f", $hp_value);
      $threshold_line = "<i>The setting for the highlight percentage (-hp) option: $rounded_percentage (%)</i>";
    }
  else
    {
      $threshold_line = "<i>The highlight percentage (-hp) feature is not enabled</i>";
    }

  $html_home = ${ generate_home_link ("left") };
  $html_end  = ${ terminate_html_document () };

  push (@modified_html, "</pre>");
  push (@modified_html, $html_new_line);
  push (@modified_html, $threshold_line);
  push (@modified_html, $html_home);
  push (@modified_html, $html_new_line);
  push (@modified_html, $g_html_credits_line);
  push (@modified_html, $html_end);

  for my $i (0 .. $#modified_html)
    {
      gp_message ("debugXL", $subr_name, "[$i] -> $modified_html[$i]");
    }

  for my $i (0 .. $#modified_html)
    {
      print HTML_OUTPUT "$modified_html[$i]" . "\n";
    }

  close (HTML_OUTPUT);
  close (INPUT_DISASSEMBLY);

  gp_message ("debug", $subr_name, "output is in file $html_dis_out");
  gp_message ("debug", $subr_name ,"completed processing disassembly");

  undef %branch_target;
  undef %extended_branch_target;
  undef %inverse_branch_target;

  return (\@source_line, \@metric);

} #-- End of subroutine generate_dis_html

#------------------------------------------------------------------------------
# Generate all the function level information.
#------------------------------------------------------------------------------
sub generate_function_level_info
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref, $call_metrics, $summary_metrics, $input_string, 
      $sort_fields_ref) = @_;

  my @exp_dir_list = @{ $exp_dir_list_ref };
  my @sort_fields  = @{ $sort_fields_ref };

  my $expr_name;
  my $first_metric;
  my $gp_display_text_cmd;
  my $gp_functions_cmd;
  my $ignore_value;
  my $script_pc_metrics;

  my $outputdir      = append_forward_slash ($input_string);

  my $script_file_PC = $outputdir."gp-script-PC";
  my $result_file    = $outputdir."gp-out-PC.err";
  my $gp_error_file  = $outputdir."gp-out-PC.err";
  my $func_limit     = $g_user_settings{func_limit}{current_value};

#------------------------------------------------------------------------------
# The number of entries in the Function Overview includes <Total>, but that is
# not a concern to the user and we add "1" to compensate for this.
#------------------------------------------------------------------------------
  $func_limit += 1;

  gp_message ("debug", $subr_name, "increased the local value for func_limit = $func_limit");

  $expr_name = join (" ", @exp_dir_list);

  gp_message ("debug", $subr_name, "expr_name = $expr_name");

  for my $i (0 .. $#sort_fields)
    {
       gp_message ("debug", $subr_name, "sort_fields[$i] = $sort_fields[$i]");
    }

# Ruud $count = 0;

  gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get function information files");

  open (SCRIPT_PC, ">", $script_file_PC) 
    or die ("$subr_name - unable to open script file $script_file_PC for writing: '$!'");
  gp_message ("debug", $subr_name, "opened file $script_file_PC for writing");

#------------------------------------------------------------------------------
# Get the list of functions.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Get the first metric.
#------------------------------------------------------------------------------
  $summary_metrics   =~ /^([^:]+)/;
  $first_metric      = $1;
  $g_first_metric    = $1;
  $script_pc_metrics = "address:$summary_metrics";

  gp_message ("debugXL", $subr_name, "$func_limit");
  gp_message ("debugXL", $subr_name, "$summary_metrics");
  gp_message ("debugXL", $subr_name, "$first_metric");
  gp_message ("debugXL", $subr_name, "$script_pc_metrics");

# Temporarily disabled   print SCRIPT_PC "# limit $func_limit\n";
# Temporarily disabled  print SCRIPT_PC "limit $func_limit\n";
  print SCRIPT_PC "# thread_select all\n";
  print SCRIPT_PC "thread_select all\n";

#------------------------------------------------------------------------------
# Empty header.
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."header\n";
  print SCRIPT_PC "outfile $outputdir"."header\n";

#------------------------------------------------------------------------------
# Else the output from the next line goes to last sort.func
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."gp-metrics-functions-PC\n"; 
  print SCRIPT_PC "outfile $outputdir"."gp-metrics-functions-PC\n"; 
  print SCRIPT_PC "# metrics $script_pc_metrics\n";
  print SCRIPT_PC "metrics $script_pc_metrics\n";
#------------------------------------------------------------------------------
# Not really sorted
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."functions.sort.func-PC\n"; 
  print SCRIPT_PC "outfile $outputdir"."functions.sort.func-PC\n"; 
  print SCRIPT_PC "# functions\n";
  print SCRIPT_PC "functions\n";

  print SCRIPT_PC "# outfile $outputdir"."functions.sort.func-PC2\n"; 
  print SCRIPT_PC "outfile $outputdir"."functions.sort.func-PC2\n"; 
  print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
  print SCRIPT_PC "metrics address:name:$summary_metrics\n";
  print SCRIPT_PC "# sort $first_metric\n";
  print SCRIPT_PC "sort $first_metric\n";
  print SCRIPT_PC "# functions\n";
  print SCRIPT_PC "functions\n";
#------------------------------------------------------------------------------
# Go through all the possible metrics and sort by each of them.
#------------------------------------------------------------------------------
  for my $field (@sort_fields)
    {
      gp_message ("debug", $subr_name, "sort_fields field = $field");
#------------------------------------------------------------------------------
# Else the output from the next line goes to last sort.func
#------------------------------------------------------------------------------
      print SCRIPT_PC "# outfile $outputdir"."gp-metrics-".$field."-PC\n"; 
      print SCRIPT_PC "outfile $outputdir"."gp-metrics-".$field."-PC\n"; 
      print SCRIPT_PC "# metrics $script_pc_metrics\n";
      print SCRIPT_PC "metrics $script_pc_metrics\n";
      print SCRIPT_PC "# outfile $outputdir".$field.".sort.func-PC\n";
      print SCRIPT_PC "outfile $outputdir".$field.".sort.func-PC\n";
      print SCRIPT_PC "# sort $field\n";
      print SCRIPT_PC "sort $field\n";
      print SCRIPT_PC "# functions\n";
      print SCRIPT_PC "functions\n";

      print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
      print SCRIPT_PC "metrics address:name:$summary_metrics\n";
      print SCRIPT_PC "# outfile $outputdir".$field.".sort.func-PC2\n";
      print SCRIPT_PC "outfile $outputdir".$field.".sort.func-PC2\n";
      print SCRIPT_PC "# sort $field\n";
      print SCRIPT_PC "sort $field\n";
      print SCRIPT_PC "# functions\n";
      print SCRIPT_PC "functions\n";
    }

#------------------------------------------------------------------------------
# Get caller-callee list
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile " . $outputdir."caller-callee-PC2\n";
  print SCRIPT_PC "outfile " . $outputdir."caller-callee-PC2\n";
  print SCRIPT_PC "# metrics address:name:$summary_metrics\n";
  print SCRIPT_PC "metrics address:name:$summary_metrics\n";
  print SCRIPT_PC "# callers-callees\n";
  print SCRIPT_PC "callers-callees\n";
#------------------------------------------------------------------------------
# Else the output from the next line goes to last sort.func
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."gp-metrics-calls-PC\n"; 
  print SCRIPT_PC "outfile $outputdir"."gp-metrics-calls-PC\n"; 
  $script_pc_metrics = "address:$call_metrics";
  print SCRIPT_PC "# metrics $script_pc_metrics\n";
  print SCRIPT_PC "metrics $script_pc_metrics\n";

#------------------------------------------------------------------------------
# Not really sorted
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."calls.sort.func-PC\n"; 
  print SCRIPT_PC "outfile $outputdir"."calls.sort.func-PC\n"; 

#------------------------------------------------------------------------------
# Get caller-callee list
#------------------------------------------------------------------------------
  print SCRIPT_PC "# callers-callees\n";
  print SCRIPT_PC "callers-callees\n";

#------------------------------------------------------------------------------
# Else the output from the next line goes to last sort.func
#------------------------------------------------------------------------------
  print SCRIPT_PC "# outfile $outputdir"."gp-metrics-calltree-PC\n";
  print SCRIPT_PC "outfile $outputdir"."gp-metrics-calltree-PC\n";
  print SCRIPT_PC "# metrics $script_pc_metrics\n";
  print SCRIPT_PC "metrics $script_pc_metrics\n";

  if ($g_user_settings{"calltree"}{"current_value"} eq "on")
    {
      gp_message ("verbose", $subr_name, "Generate the file with the calltree information");
#------------------------------------------------------------------------------
# Get calltree list
#------------------------------------------------------------------------------
      print SCRIPT_PC "# outfile $outputdir"."calltree.sort.func-PC\n";
      print SCRIPT_PC "outfile $outputdir"."calltree.sort.func-PC\n";
      print SCRIPT_PC "# calltree\n";
      print SCRIPT_PC "calltree\n";
    }

#------------------------------------------------------------------------------
# Get the default set of metrics
#------------------------------------------------------------------------------
  my $full_metrics_ref;
  my $all_metrics;
  my $full_function_view = $outputdir . "functions.full";

  $full_metrics_ref = get_all_the_metrics (\$expr_name, \$outputdir);

  $all_metrics  = "address:name:";
  $all_metrics .= ${$full_metrics_ref};
  gp_message ("debug", $subr_name, "all_metrics = $all_metrics");
#------------------------------------------------------------------------------
# Get the name, address, and full overview of all metrics for all functions
#------------------------------------------------------------------------------
   print SCRIPT_PC "# limit 0\n";
   print SCRIPT_PC "limit 0\n";
   print SCRIPT_PC "# metrics $all_metrics\n";
   print SCRIPT_PC "metrics $all_metrics\n";
   print SCRIPT_PC "# thread_select all\n";
   print SCRIPT_PC "thread_select all\n";
   print SCRIPT_PC "# sort default\n";
   print SCRIPT_PC "sort default\n";
   print SCRIPT_PC "# outfile $full_function_view\n";
   print SCRIPT_PC "outfile $full_function_view\n";
   print SCRIPT_PC "# functions\n";
   print SCRIPT_PC "functions\n";

  close (SCRIPT_PC);

  $result_file    = $outputdir."gp-out-PC.err";
  $gp_error_file  = $outputdir.$g_gp_error_logfile;

  $gp_functions_cmd  = "$GP_DISPLAY_TEXT -limit $func_limit ";
  $gp_functions_cmd .= "-viewmode machine -compare off ";
  $gp_functions_cmd .= "-script $script_file_PC $expr_name";

  gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get function level information");

  $gp_display_text_cmd = "$gp_functions_cmd 1> $result_file 2>> $gp_error_file";

  gp_message ("debugXL", $subr_name,"cmd = $gp_display_text_cmd");

  my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", "execution terminated");
    }

#-------------------------------------------------------------------------------
# Parse the full function view and store the data.
#-------------------------------------------------------------------------------
  my @input_data = ();
  my $empty_line_regex = '^\s*$';
  
##  my $full_function_view = $outputdir . "functions.full";

  open (ALL_FUNC_DATA, "<", $full_function_view)
    or die ("$subr_name - unable to open output file $full_function_view for reading '$!'");
  gp_message ("debug", $subr_name, "opened file $full_function_view for reading");

  chomp (@input_data = <ALL_FUNC_DATA>);

  my $start_scanning = $FALSE;
  for (my $line = 0; $line <= $#input_data; $line++)
    {
      my $input_line = $input_data[$line];
     
#      if ($input_line =~ /^<Total>\s+.*/)
      if ($input_line =~ /\s*(\d+:0x[a-fA-F0-9]+)\s+(\S+)\s+(.*)/)
        {
          $start_scanning = $TRUE;
        }
      elsif ($input_line =~ /$empty_line_regex/)
        {
          $start_scanning = $FALSE;
        }

      if ($start_scanning)
        {
          gp_message ("debugXL", $subr_name, "$line: $input_data[$line]");

          push (@g_full_function_view_table, $input_data[$line]);
 
          my $hex_address;
          my $full_hex_address = $1;
          my $routine = $2;
          my $all_metrics = $3;
          if ($full_hex_address =~ /(\d+):0x(\S+)/)
            {
              $hex_address = "0x" . $2;
            }
          $g_function_view_all{$routine}{"hex_address"} = $hex_address;
          $g_function_view_all{$routine}{"all_metrics"} = $all_metrics;
        }
    }

  for my $i (keys %g_function_view_all)
    {
      gp_message ("debugXL", $subr_name, "key = $i $g_function_view_all{$i}{'hex_address'} $g_function_view_all{$i}{'all_metrics'}");
    }

  for my $i (keys @g_full_function_view_table)
    {
      gp_message ("debugXL", $subr_name, "g_full_function_view_table[$i] = $i $g_full_function_view_table[$i]");
    }

  return ($script_pc_metrics);

} #-- End of subroutine generate_function_level_info

#------------------------------------------------------------------------------
# Generate all the files needed for the function view.
#------------------------------------------------------------------------------
sub generate_function_view
{
  my $subr_name = get_my_name ();

  my ($directory_name_ref, $summary_metrics_ref, $number_of_metrics_ref, 
      $function_info_ref, $function_view_structure_ref, $function_address_info_ref, 
      $sort_fields_ref, $exp_dir_list_ref, $addressobjtextm_ref) = @_;

  my $directory_name          = ${ $directory_name_ref };
  my @function_info           = @{ $function_info_ref };
  my %function_view_structure = %{ $function_view_structure_ref };
  my $summary_metrics         = ${ $summary_metrics_ref };
  my $number_of_metrics       = ${ $number_of_metrics_ref };
  my %function_address_info   = %{ $function_address_info_ref };
  my @sort_fields             = @{ $sort_fields_ref };
  my @exp_dir_list            = @{ $exp_dir_list_ref };
  my %addressobjtextm         = %{ $addressobjtextm_ref };

  my @abs_path_exp_dirs = ();
  my @experiment_directories; 

  my $target_function; 
  my $html_line;
  my $ftag;
  my $routine_length; 
  my %html_source_functions = ();

  my $href_link; 
  my $infile;
  my $input_experiments;
  my $keep_value; 
  my $loadobj; 
  my $address_field; 
  my $address_offset; 
  my $msg;
  my $exe; 
  my $extra_field; 
  my $new_target_function;
  my $file_title; 
  my $html_output_file; 
  my $html_function_view; 
  my $overview_file; 
  my $exp_name; 
  my $exp_type; 
  my $html_header;
  my $routine; 
  my $length_header; 
  my $length_metrics;
  my $full_index_line; 
  my $acknowledgement; 
  my @full_function_view_line = ();
  my $spaces;
  my $size_text; 
  my $position_text; 
  my $html_first_metric_file; 
  my $html_new_line = "<br>";
  my $html_acknowledgement; 
  my $html_end;
  my $html_home; 
  my $page_title; 
  my $html_title_header; 

  my $outputdir         = append_forward_slash ($directory_name);
  my $LANG              = $g_locale_settings{"LANG"};
  my $decimal_separator = $g_locale_settings{"decimal_separator"};

  $input_experiments = join (", ", @exp_dir_list);

  for my $i (0 .. $#exp_dir_list)
    {
      my $dir = get_basename ($exp_dir_list[$i]);
      push @abs_path_exp_dirs, $dir;
    }
  $input_experiments = join (", ", @abs_path_exp_dirs);

  gp_message ("debug", $subr_name, "input_experiments = $input_experiments");

#------------------------------------------------------------------------------
# TBD: This should be done only once and much earlier.
#------------------------------------------------------------------------------
  @experiment_directories = split (",", $input_experiments);

#------------------------------------------------------------------------------
# For every function in the function overview, set up an html structure with
# the various hyperlinks.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Core loop that generates an HTML line for each function.
#------------------------------------------------------------------------------
  my $top_of_table = $FALSE;
  for my $i (0 .. $#function_info)
    {
      if (defined ($function_info[$i]{"alt_name"}))
        {
          $target_function = $function_info[$i]{"alt_name"};
        }
      else
        {
          my $msg = "function_info[$i]{\"alt_name\"} is not defined";
          gp_message ("assertion", $subr_name, $msg);
        }

      $html_source_functions{$target_function} = $function_info[$i]{"html function block"}; 
    }

  for my $i (sort keys %html_source_functions)
    {
      gp_message ("debugXL", $subr_name, "html_source_functions{$i} = $html_source_functions{$i}");
    }

  $file_title = "Function view for experiments " . $input_experiments;

#------------------------------------------------------------------------------
# Example input file:

# Current metrics: address:name:e.totalcpu:e.cycles:e+insts:e+llm
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Functions sorted by metric: Exclusive Total CPU Time
# 
# PC Addr.        Name              Excl.     Excl. CPU  Excl.         Excl.
#                                   Total     Cycles     Instructions  Last-Level
#                                   CPU sec.   sec.      Executed      Cache Misses
#  1:0x00000000   <Total>           3.502     4.005      15396819700   24024250
#  2:0x000021ae   mxv_core          3.342     3.865      14500538981   23824045
#  6:0x0003af50   erand48_r         0.080     0.084        768240570          0
#  2:0x00001f7b   init_data         0.040     0.028         64020043     200205
#  6:0x0003b160   __drand48_iterate 0.020     0.                   0          0
#  ...
#------------------------------------------------------------------------------

  for my $metric (@sort_fields)
    {
      $overview_file = $outputdir . $metric . ".sort.func-PC2";

      $exp_type = $metric;

      if ($metric eq "functions")
        {
          $html_function_view .= $g_html_base_file_name{"function_view"} . ".html";
        }
      else
        {
          $html_function_view = $g_html_base_file_name{"function_view"} . "." . $metric . ".html";
        }
#------------------------------------------------------------------------------
# The default function view is based upon the first metric in the list.  We use
# this file in the index.html file. 
#------------------------------------------------------------------------------
      if ($metric eq $g_first_metric)
        {
          $html_first_metric_file = $html_function_view;
          my $txt = "g_first_metric = $g_first_metric ";
          $txt   .= "html_first_metric_file = $html_first_metric_file";
          gp_message ("debugXL", $subr_name, $txt);
        }

      $html_output_file = $outputdir . $html_function_view;

      open (FUNCTION_VIEW, ">", $html_output_file) 
        or die ("$subr_name - unable to open file $html_output_file for writing - '$!'");
      gp_message ("debug", $subr_name, "opened file $html_output_file for writing");

      $html_home       = ${ generate_home_link ("right") };
      $html_header     = ${ create_html_header (\$file_title) };

      $page_title    = "Function View";
      $size_text     = "h2"; 
      $position_text = "center";
      $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };

      print FUNCTION_VIEW $html_header;
      print FUNCTION_VIEW $html_home;
      print FUNCTION_VIEW $html_title_header;
      print FUNCTION_VIEW "$_" for @g_html_experiment_stats;
      print FUNCTION_VIEW $html_new_line . "\n";

      my $function_view_structure_ref = process_function_overview (
                                          \$metric, 
                                          \$exp_type, 
                                          \$summary_metrics, 
                                          \$number_of_metrics, 
                                          \@function_info, 
                                          \%function_view_structure, 
                                          \$overview_file);

      my %function_view_structure = %{ $function_view_structure_ref };

#------------------------------------------------------------------------------
# Core part: extract the true function name and find the html code for it.
#------------------------------------------------------------------------------
      gp_message ("debugXL", $subr_name, "the final table");
 
      print FUNCTION_VIEW "<pre>\n";
      print FUNCTION_VIEW "$_\n" for @{ $function_view_structure{"header"} };

      my $max_length_header  = $function_view_structure{"max header length"}; 
      my $max_length_metrics = $function_view_structure{"max metrics length"};

#------------------------------------------------------------------------------
# Add 4 more spaces for the distance to the function names.  Purely cosmetic.
#------------------------------------------------------------------------------
      my $pad    = max ($max_length_metrics, $max_length_header) + 4;
      my $spaces = "";
      for my $i (1 .. $pad)
        {
          $spaces .= "&nbsp;";
        }

#------------------------------------------------------------------------------
# Add extra space for the /blank/*/ marker!
#------------------------------------------------------------------------------
      $spaces .= "&nbsp;";
      my $func_header = $spaces . $function_view_structure{"table name"};
      gp_message ("debugXL", $subr_name, "func_header = " . $func_header);

      
      print FUNCTION_VIEW $spaces . "<b>" . 
                          $function_view_structure{"table name"} . 
                          "</b>" . $html_new_line . "\n";

#------------------------------------------------------------------------------
# If the header is longer than the metrics, add spaces to padd the difference.
# Also add the same 4 spaces between the metric values and the function name.
#------------------------------------------------------------------------------
      $pad = 0;
      if ($max_length_header > $max_length_metrics)
        {
          $pad = $max_length_header - $max_length_metrics;
        }
      $pad += 4;
      $spaces = "";
      for my $i (1 .. $pad)
        {
          $spaces .= "&nbsp;";
        }

#------------------------------------------------------------------------------
# This is where it literally all comes together.  The metrics and function
# parts are combined.
#------------------------------------------------------------------------------
##      for my $i (keys @{ $function_view_structure{"function table"} })
      for my $i (0 .. $#{ $function_view_structure{"function table"} })
        {
          my $p1 = $function_view_structure{"metrics part"}[$i];
          my $p2 = $function_view_structure{"function table"}[$i];

          $full_index_line = $p1 . $spaces . $p2;

          push (@full_function_view_line, $full_index_line);
        }

      print FUNCTION_VIEW "$_\n" for @full_function_view_line;

#-------------------------------------------------------------------------------
# Clear the array before filling it up again.
#-------------------------------------------------------------------------------
      @full_function_view_line = ();

#-------------------------------------------------------------------------------
# Get the acknowledgement, return to main link, and final html statements.
#-------------------------------------------------------------------------------
      $html_home            = ${ generate_home_link ("left") };
      $html_acknowledgement = ${ create_html_credits () };
      $html_end             = ${ terminate_html_document () };

      print FUNCTION_VIEW "</pre>\n";
      print FUNCTION_VIEW $html_home;
      print FUNCTION_VIEW $html_new_line . "\n";
      print FUNCTION_VIEW $html_acknowledgement;
      print FUNCTION_VIEW $html_end;

      close (FUNCTION_VIEW);
    }

  return (\$html_first_metric_file); 

} #-- End of subroutine generate_function_view

#------------------------------------------------------------------------------
# Generate an html line that links back to index.html.  The text can either
# be positioned to the left or to the right.
#------------------------------------------------------------------------------
sub generate_home_link
{
  my $subr_name = get_my_name ();

  my ($which_side) = @_;

  my $html_home_line; 

  if (($which_side ne "left") and ($which_side ne "right"))
    {
      my $msg = "which_side = $which_side not supported";
      gp_message ("assertion", $subr_name, $msg);
    }

  $html_home_line .= "<div class=\"" . $which_side . "\">";
  $html_home_line .= "<br><a href='" . $g_html_base_file_name{"index"}; 
  $html_home_line .= ".html' style='background-color:"; 
  $html_home_line .= $g_html_color_scheme{"index"};
  $html_home_line .= "'><b>Return to main view</b></a>";
  $html_home_line .= "</div>";

  return (\$html_home_line);

} #-- End of subroutine generate_home_link

#------------------------------------------------------------------------------
# Generate a block of html for this function block.
#------------------------------------------------------------------------------
sub generate_html_function_blocks
{
  my $subr_name = get_my_name ();

  my (
  $index_start_ref,
  $index_end_ref,
  $hex_addresses_ref,
  $the_metrics_ref,
  $length_first_metric_ref,
  $special_marker_ref,
  $the_function_name_ref,
  $separator_ref, 
  $number_of_metrics_ref, 
  $data_function_block_ref, 
  $function_info_ref, 
  $function_view_structure_ref) = @_; 

  my $index_start = ${ $index_start_ref };
  my $index_end   = ${ $index_end_ref };
  my @hex_addresses = @{ $hex_addresses_ref };
  my @the_metrics     = @{ $the_metrics_ref };
  my @length_first_metric = @{ $length_first_metric_ref };
  my @special_marker = @{ $special_marker_ref };
  my @the_function_name = @{ $the_function_name_ref};

  my $separator               = ${ $separator_ref };
  my $number_of_metrics       = ${ $number_of_metrics_ref };
  my $data_function_block     = ${ $data_function_block_ref };
  my @function_info           = @{ $function_info_ref };
  my %function_view_structure = %{ $function_view_structure_ref };

  my $decimal_separator = $g_locale_settings{"decimal_separator"}; 

  my @html_block_prologue = ();
  my @html_code_function_block = ();
  my @function_lines           = ();
  my @fields = ();
  my @address_field = ();
  my @metric_values = ();
  my @function_names = ();
  my @final_function_names = ();
  my @marker = ();
  my @split_number = ();
  my @function_tags = ();

  my $all_metrics; 
  my $current_function_name;
  my $no_of_fields;
  my $name_regex;
  my $full_hex_address;
  my $hex_address;
  my $target_function; 
  my $marker_function; 
  my $routine; 
  my $routine_length; 
  my $metrics_length; 
  my $max_metrics_length = 0;
  my $modified_line;
  my $string_length; 
  my $addr_offset; 
  my $current_address; 
  my $found_a_match;
  my $ref_index;
  my $alt_name;
  my $length_first_field; 
  my $gap;
  my $ipad; 
  my $html_line; 
  my $target_tag; 
  my $tag_for_header; 
  my $href_file; 
  my $found_alt_name; 
  my $name_in_header;
  my $create_hyperlinks;

  state $first_call = $TRUE;
  state $reference_length;

#------------------------------------------------------------------------------
# If the length of the first metric is less than the maximum over all first
# metrics, add spaces to the left to ensure correct alignment.
#------------------------------------------------------------------------------
  for my $k ($index_start .. $index_end)
    {
      my $pad = $g_max_length_first_metric - $length_first_metric[$k];
      if ($pad ge 1)
        {
          my $spaces = "";
          for my $s (1 .. $pad)
            {
              $spaces .= "&nbsp;";
            }
          $the_metrics[$k] = $spaces . $the_metrics[$k];

          my $msg = "padding spaces = $spaces the_metrics[$k] = $the_metrics[$k]";
          gp_message ("debugXL", $subr_name, $msg);
        }

##      my $end_game = "end game3=> pad = $pad" . $hex_addresses[$k] . " " . $the_metrics[$k] . " " . $special_marker[$k] . $the_function_name[$k];
##      gp_message ("debugXL", $subr_name, $end_game);
    }

#------------------------------------------------------------------------------
# An example what @function_lines should look like after the split:
# <empty>
# 6:0x0003ad20   drand48           0.100     0.084        768240570          0
# 6:0x0003af50  *erand48_r         0.080     0.084        768240570          0
# 6:0x0003b160   __drand48_iterate 0.020     0.                   0          0
#------------------------------------------------------------------------------
  @function_lines = split ($separator, $data_function_block);

#------------------------------------------------------------------------------
# Parse the individual lines.  Replace multi-occurrence functions by their
# unique alternative name and mark the target function.
#
# The above split operation produces an empty first field because the line
# starts with the separator.  This is why skip the first field.
#------------------------------------------------------------------------------
  for my $i ($index_start .. $index_end)
    {
      my $input_line = $the_metrics[$i];

      gp_message ("debugXL", $subr_name, "the_metrics[$i] = ". $the_metrics[$i]);

#------------------------------------------------------------------------------
# In case the last metric is 0. only, we append 3 extra characters that
# represent zero.  We cannot change the number to 0.000 though because that
# has a different interpretation than 0.
# In a later phase, the "ZZZ" symbol will be removed again, but for now it
# creates consistency in, for example, the length of the metrics part.
#------------------------------------------------------------------------------
      if ($input_line =~ /[\w0-9$decimal_separator]*(0$decimal_separator$)/)
        {
          if (defined ($1) )
            {
              my $decimal_point = $decimal_separator;
              $decimal_point =~ s/\\//;
              my $txt = "input_line = $input_line = ended with 0"; 
              $txt   .= $decimal_point;
              gp_message ("debugXL", $subr_name, $txt);

              $the_metrics[$i] .= "ZZZ";
            }
        }

      $hex_address     = $hex_addresses[$i];
      $marker_function = $special_marker[$i];
      $routine         = $the_function_name[$i];
#------------------------------------------------------------------------------
# Get the length of the metrics line before ZZZ is replaced by spaces.
#------------------------------------------------------------------------------
      $all_metrics     = $the_metrics[$i];
      $metrics_length  = length ($all_metrics);
      $all_metrics     =~ s/ZZZ/&nbsp;&nbsp;&nbsp;/g;

      $max_metrics_length = max ($max_metrics_length, $metrics_length);

      push (@marker, $marker_function);
      push (@address_field, $hex_address); 
      push (@metric_values, $all_metrics);
      push (@function_names, $routine);

      my $index_into_function_info_ref = get_index_function_info (
                                         \$routine, 
                                         \$hex_addresses[$i], 
                                         $function_info_ref);

      my $index_into_function_info = ${ $index_into_function_info_ref }; 
      $target_tag = $function_info[$index_into_function_info]{"tag_id"};
      $alt_name = $function_info[$index_into_function_info]{"alt_name"};

#------------------------------------------------------------------------------
# Keep the name of the target function (the one marked with a *) for later use.
# This is the tag that identifies the block in the caller-callee output.  The
# tag is used in the link to the caller-callee in the function overview.
#------------------------------------------------------------------------------
      if ($marker_function eq "*")
        {
          $tag_for_header = $target_tag;
          $name_in_header = $alt_name;

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
          $name_in_header =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

        }
      push (@final_function_names, $alt_name);
      push (@function_tags, $target_tag);

      gp_message ("debugXL", $subr_name, "index_into_function_info = $index_into_function_info");
      gp_message ("debugXL", $subr_name, "target_tag = $target_tag");
      gp_message ("debugXL", $subr_name, "alt_name   = $alt_name");

    } #-- End of loop for my $i ($index_start .. $index_end)

  my $tag_line = "<a id='" . $tag_for_header . "'></a>";
  $html_line  = "<br>\n";
  $html_line .= $tag_line . "Function name: ";
  $html_line .= "<span style='color:" . $g_html_color_scheme{"target_function_name"} . "'>";
  $html_line .= "<b>" . $name_in_header . "</b></span>\n";
  $html_line .= "<br>";

  push (@html_block_prologue, $html_line);

  gp_message ("debugXL", $subr_name, "the final function block for $name_in_header");

  $href_file = $g_html_base_file_name{"caller_callee"} . ".html";

#------------------------------------------------------------------------------
# Process the function blocks and generate the HTML structure for them.
#------------------------------------------------------------------------------
  for my $i (0 .. $#final_function_names)
    {
      $current_function_name = $final_function_names[$i];
      gp_message ("debugXL", $subr_name, "current_function_name = $current_function_name");

#------------------------------------------------------------------------------
# Do not add hyperlinks for <Total>.
#------------------------------------------------------------------------------
      if ($current_function_name eq "<Total>")
        {
          $create_hyperlinks = $FALSE;
        }
      else
        {
          $create_hyperlinks = $TRUE;
        }

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
      $current_function_name =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

      $html_line = $metric_values[$i] . " ";

      if ($marker[$i] eq "*")
        {
          $current_function_name = "<b>" . $current_function_name . "</b>";
        }
      $html_line .= " <a href='" . $href_file . "#" . $function_tags[$i] . "'>" . $current_function_name . "</a>";

      if ($marker[$i] eq "*")
        {
            $html_line = "<br>" . $html_line; 
        }
      elsif (($marker[$i] ne "*") and ($i == 0))
        {
            $html_line = "<br>" . $html_line; 
        }

      gp_message ("debugXL", $subr_name, "html_line = $html_line");

#------------------------------------------------------------------------------
# Find the index into "function_info" for this particular function.
#------------------------------------------------------------------------------
      $routine         = $function_names[$i];
      $current_address = $address_field[$i];

      my $target_index_ref = find_index_in_function_info (\$routine, \$current_address, \@function_info); 
      my $target_index     = ${ $target_index_ref };

      gp_message ("debugXL", $subr_name, "routine = $routine current_address = $current_address target_index = $target_index");

#------------------------------------------------------------------------------
# TBD Do this once for each function and store the result.  This is a saving
# because functions may and typically will appear more than once.
#------------------------------------------------------------------------------
      my $spaces_left = $function_view_structure{"max function length"} - $function_info[$target_index]{"function length"}; 

#------------------------------------------------------------------------------
# Add the links to the line. Make sure there is at least one space.
#------------------------------------------------------------------------------
      my $spaces = "&nbsp;";
      for my $k (1 .. $spaces_left)
        {
          $spaces .= "&nbsp;";
        }

      if ($create_hyperlinks)
        {
          $html_line .= $spaces;
          $html_line .= $function_info[$target_index]{"href_source"};
          $html_line .= "&nbsp;";
          $html_line .= $function_info[$target_index]{"href_disassembly"};
        }

      push (@html_code_function_block, $html_line); 
    }

    for my $lines (0 .. $#html_code_function_block)
      {
        gp_message ("debugXL", $subr_name, "final html block = " . $html_code_function_block[$lines]);
      }

  return (\@html_block_prologue, \@html_code_function_block); 

} #-- End of subroutine generate_html_function_blocks

#------------------------------------------------------------------------------
# Generate the index.html file.
#------------------------------------------------------------------------------
sub generate_index
{
  my $subr_name = get_my_name ();

  my ($outputdir_ref, $html_first_metric_file_ref, $summary_metrics_ref, 
      $number_of_metrics_ref, $function_info_ref, $function_address_info_ref, 
      $sort_fields_ref, $exp_dir_list_ref, $addressobjtextm_ref,
      $metric_description_reversed_ref, $number_of_warnings_ref,
      $table_execution_stats_ref) = @_;

  my $outputdir               = ${ $outputdir_ref };
  my $html_first_metric_file  = ${ $html_first_metric_file_ref };
  my $summary_metrics         = ${ $summary_metrics_ref };
  my $number_of_metrics       = ${ $number_of_metrics_ref };
  my @function_info           = @{ $function_info_ref };
  my %function_address_info   = %{ $function_address_info_ref };
  my @sort_fields             = @{ $sort_fields_ref };
  my @exp_dir_list            = @{ $exp_dir_list_ref };
  my %addressobjtextm         = %{ $addressobjtextm_ref };
  my %metric_description_reversed = %{ $metric_description_reversed_ref };
  my $number_of_warnings      = ${ $number_of_warnings_ref };
  my @table_execution_stats   = @{ $table_execution_stats_ref };

  my @file_contents = ();

  my $acknowledgement; 
  my @abs_path_exp_dirs = ();
  my $input_experiments;
  my $target_function; 
  my $html_line;
  my $ftag;
  my $max_length = 0;
  my %html_source_functions = ();
  my $html_header; 
  my @experiment_directories = ();
  my $html_acknowledgement; 
  my $html_file_title; 
  my $html_output_file; 
  my $html_function_view;
  my $html_caller_callee_view;
  my $html_experiment_info; 
  my $html_warnings_page;
  my $href_link; 
  my $file_title; 
  my $html_gprofng;
  my $html_end;
  my $max_length_metrics;
  my $page_title;
  my $size_text;
  my $position_text;
 
  my $ln;
  my $base;
  my $base_index_page;
  my $infile;
  my $outfile;
  my $rec;
  my $skip;
  my $callsize;
  my $dest;
  my $final_string;
  my @headers;
  my $header;
  my $sort_index;
  my $pc_address;
  my $anchor;
  my $directory_name;
  my $f2;
  my $f3;
  my $file;
  my $sline;
  my $src;
  my $srcfile_name;
  my $tmp1;
  my $tmp2;
  my $fullsize;
  my $regf2;
  my $trimsize;
  my $EIL;
  my $EEIL;
  my $AOBJ;
  my $RI;
  my $HDR;
  my $CALLER_CALLEE;
  my $NAME;
  my $SRC;
  my $TRIMMED;

#------------------------------------------------------------------------------
# Add a forward slash to make it easier when creating file names.
#------------------------------------------------------------------------------
  $outputdir         = append_forward_slash ($outputdir);
  gp_message ("debug", $subr_name, "outputdir = $outputdir");

  my $LANG              = $g_locale_settings{"LANG"};
  my $decimal_separator = $g_locale_settings{"decimal_separator"};

  $input_experiments = join (", ", @exp_dir_list);

  for my $i (0 .. $#exp_dir_list)
    {
      my $dir = get_basename ($exp_dir_list[$i]);
      push @abs_path_exp_dirs, $dir;
    }
  $input_experiments = join (", ", @abs_path_exp_dirs);

  gp_message ("debug", $subr_name, "input_experiments = $input_experiments");
  
#------------------------------------------------------------------------------
# TBD: Pass in the values for $expr_name and $cmd
#------------------------------------------------------------------------------
  $html_file_title = "Main index page";

  @experiment_directories = split (",", $input_experiments);
  $html_acknowledgement = ${ create_html_credits () };

  $html_end              = ${ terminate_html_document () };

  $html_output_file = $outputdir . $g_html_base_file_name{"index"} . ".html"; 

  open (INDEX, ">", $html_output_file) 
    or die ("$subr_name - unable to open file $html_output_file for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $html_output_file for writing");

  $page_title    = "GPROFNG Performance Analysis";
  $size_text     = "h1";
  $position_text = "center";
  $html_gprofng = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };

  $html_header     = ${ create_html_header (\$html_file_title) };

  print INDEX $html_header;
  print INDEX $html_gprofng;
  print INDEX "$_" for @g_html_experiment_stats;
  print INDEX "$_" for @table_execution_stats;

  $html_experiment_info  = "<a href=\'";
  $html_experiment_info .= $g_html_base_file_name{"experiment_info"} . ".html";
  $html_experiment_info .= "\'><h3>Experiment Details</h3></a>\n";

  $html_warnings_page  = "<a href=\'";
  $html_warnings_page .= $g_html_base_file_name{"warnings"} . ".html";
  $html_warnings_page .= "\'><h3>Warnings (" . $number_of_warnings . ")</h3></a>\n";

  $html_function_view  = "<a href=\'";
  $html_function_view .= $html_first_metric_file;
  $html_function_view .= "\'><h3>Function View</h3></a>\n";

  $html_caller_callee_view  = "<a href=\'";
  $html_caller_callee_view .= $g_html_base_file_name{"caller_callee"} . ".html";
  $html_caller_callee_view .= "\'><h3>Caller Callee View</h3></a>\n";

  print INDEX "<br>\n";
##  print INDEX "<b>\n";
  print INDEX $html_experiment_info;
  print INDEX $html_warnings_page;;
##  print INDEX "<br>\n";
##  print INDEX "<br>\n";
  print INDEX $html_function_view;
##  print INDEX "<br>\n";
##  print INDEX "<br>\n";
  print INDEX $html_caller_callee_view;
##  print INDEX "</b>\n";
##  print INDEX "<br>\n";
##  print INDEX "<br>\n";
 
  print INDEX $html_acknowledgement;
  print INDEX $html_end;

  close (INDEX);

  gp_message ("debug", $subr_name, "closed file $html_output_file");

  return (0);

} #-- End of subroutine generate_index

#------------------------------------------------------------------------------
# Get all the metrics available 
#
# (gp-display-text) metric_list
# Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Available metrics:
#          Exclusive Total CPU Time: e.%totalcpu
#          Inclusive Total CPU Time: i.%totalcpu
#              Exclusive CPU Cycles: e.+%cycles
#              Inclusive CPU Cycles: i.+%cycles
#   Exclusive Instructions Executed: e+%insts
#   Inclusive Instructions Executed: i+%insts
# Exclusive Last-Level Cache Misses: e+%llm
# Inclusive Last-Level Cache Misses: i+%llm
#  Exclusive Instructions Per Cycle: e+IPC
#  Inclusive Instructions Per Cycle: i+IPC
#  Exclusive Cycles Per Instruction: e+CPI
#  Inclusive Cycles Per Instruction: i+CPI
#                              Size: size
#                        PC Address: address
#                              Name: name
#------------------------------------------------------------------------------
sub get_all_the_metrics
{
  my $subr_name = get_my_name ();

  my ($experiments_ref, $outputdir_ref) = @_;

  my $experiments = ${ $experiments_ref };
  my $outputdir   = ${ $outputdir_ref };

  my $ignore_value;
  my $gp_functions_cmd; 
  my $gp_display_text_cmd; 

  my $metrics_output_file = $outputdir . "metrics-all";
  my $result_file   = $outputdir . $g_gp_output_file;
  my $gp_error_file = $outputdir . $g_gp_error_logfile;
  my $script_file_metrics = $outputdir . "script-metrics";

  my @metrics_data = ();

  open (SCRIPT_METRICS, ">", $script_file_metrics) 
    or die ("$subr_name - unable to open script file $script_file_metrics for writing: '$!'");
  gp_message ("debug", $subr_name, "opened script file $script_file_metrics for writing");

  print SCRIPT_METRICS "# outfile $metrics_output_file\n";
  print SCRIPT_METRICS "outfile $metrics_output_file\n";
  print SCRIPT_METRICS "# metric_list\n";
  print SCRIPT_METRICS "metric_list\n";

  close (SCRIPT_METRICS);

  $gp_functions_cmd  = "$GP_DISPLAY_TEXT -script $script_file_metrics $experiments";

  gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to get all the metrics");

  $gp_display_text_cmd = "$gp_functions_cmd 1>> $result_file 2>> $gp_error_file";
  gp_message ("debug", $subr_name, "cmd = $gp_display_text_cmd");

  my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", $subr_name, "execution terminated");
    }

  open (METRICS_INFO, "<", $metrics_output_file) 
    or die ("$subr_name - unable to open file $metrics_output_file for reading '$!'");
  gp_message ("debug", $subr_name, "opened file $metrics_output_file for reading");

#------------------------------------------------------------------------------
# Read the input file into memory.
#------------------------------------------------------------------------------
  chomp (@metrics_data = <METRICS_INFO>);
  gp_message ("debug", $subr_name, "read all contents of file $metrics_output_file into memory");
  gp_message ("debug", $subr_name, "\$#metrics_data = $#metrics_data");

  my $input_line;
  my $ignore_lines_regex = '^(?:Current|Available|\s+Size:|\s+PC Address:|\s+Name:)';
  my $split_line_regex = '(.*): (.*)';
  my $empty_line_regex = '^\s*$';
  my @metric_list_all = ();
  for (my $line_no=0; $line_no <= $#metrics_data; $line_no++)
    {

      $input_line = $metrics_data[$line_no];

##      if ( not (($input_line =~ /$ignore_lines_regex/ or ($input_line =~ /^\s*$/))))
      if ( not ($input_line =~ /$ignore_lines_regex/) and not ($input_line =~ /$empty_line_regex/) )
        {
          if ($input_line =~ /$split_line_regex/)
            {
#------------------------------------------------------------------------------
# Remove the percentages.
#------------------------------------------------------------------------------
              my $metric_definition = $2;
              $metric_definition =~ s/\%//g;
              gp_message ("debug", $subr_name, "line_no = $line_no $metrics_data[$line_no] metric_definition = $metric_definition");
              push (@metric_list_all, $metric_definition);
            }
        }

    }

  gp_message ("debug", $subr_name, "\@metric_list_all = @metric_list_all");

  my $final_list = join (":", @metric_list_all);
  gp_message ("debug", $subr_name, "final_list = $final_list");

  close (METRICS_INFO);

  return (\$final_list);

} #-- End of subroutine get_all_the_metrics

#------------------------------------------------------------------------------
# A simple function to return the basename using fileparse.  To keep things
# simple, a suffixlist is not supported.  In case this is needed, use the
# fileparse function directly.
#------------------------------------------------------------------------------
sub get_basename
{
  my ($full_name) = @_;

  my $ignore_value_1;
  my $ignore_value_2;
  my $basename_value;

  ($basename_value, $ignore_value_1, $ignore_value_2) = fileparse ($full_name);

  return ($basename_value);

} #-- End of subroutine get_basename

#------------------------------------------------------------------------------
# Get the details on the experiments and store these in a file.  Each 
# experiment has its own file.  This makes the processing easier.
#------------------------------------------------------------------------------
sub get_experiment_info
{
  my $subr_name = get_my_name ();

  my ($outputdir_ref, $exp_dir_list_ref) = @_;

  my $outputdir    = ${ $outputdir_ref };
  my @exp_dir_list = @{ $exp_dir_list_ref };

  my $cmd_output;
  my $current_slot;
  my $error_code;
  my $exp_info_file; 
  my @exp_info       = ();
  my @experiment_data = ();
  my $gp_error_file;
  my $gp_display_text_cmd;
  my $gp_functions_cmd; 
  my $gp_log_file; 
  my $ignore_value;
  my $overview_file;
  my $result_file;
  my $script_file;
  my $the_experiments;

  $the_experiments = join (" ", @exp_dir_list);

  $script_file   = $outputdir . "gp-info-exp.script";
  $exp_info_file = $outputdir . "gp-info-exp-list.out";
  $overview_file = $outputdir . "gp-overview.out";
  $gp_log_file   = $outputdir . $g_gp_output_file;
  $gp_error_file = $outputdir . $g_gp_error_logfile;

  open (SCRIPT_EXPERIMENT_INFO, ">", $script_file) 
    or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
  gp_message ("debug", $subr_name, "opened script file $script_file for writing");

#------------------------------------------------------------------------------
# Attributed User CPU Time=a.user : for calltree - see P37 in manual
#------------------------------------------------------------------------------
  print SCRIPT_EXPERIMENT_INFO "# compare on\n";
  print SCRIPT_EXPERIMENT_INFO "compare on\n";
  print SCRIPT_EXPERIMENT_INFO "# outfile $exp_info_file\n";
  print SCRIPT_EXPERIMENT_INFO "outfile $exp_info_file\n";
  print SCRIPT_EXPERIMENT_INFO "# exp_list\n";
  print SCRIPT_EXPERIMENT_INFO "exp_list\n";
  print SCRIPT_EXPERIMENT_INFO "# outfile $overview_file\n";
  print SCRIPT_EXPERIMENT_INFO "outfile $overview_file\n";
  print SCRIPT_EXPERIMENT_INFO "# overview\n";
  print SCRIPT_EXPERIMENT_INFO "overview\n";

  close SCRIPT_EXPERIMENT_INFO;

  $gp_functions_cmd = "$GP_DISPLAY_TEXT -script $script_file $the_experiments";

  gp_message ("debug", $subr_name, "executing $GP_DISPLAY_TEXT to get the experiment information");

  $gp_display_text_cmd = "$gp_functions_cmd 1>> $gp_log_file 2>> $gp_error_file";

  ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", $subr_name, "execution terminated");
    }

#-------------------------------------------------------------------------------
# The first file has the following format:
#
# ID Sel     PID Experiment
# == === ======= ======================================================
#  1 yes 2078714 <absolute_path/mxv.hwc.1.thr.er
#  2 yes 2078719 <absolute_path/mxv.hwc.2.thr.er
#-------------------------------------------------------------------------------
  open (EXP_INFO, "<", $exp_info_file) 
    or die ("$subr_name - unable to open file $exp_info_file for reading '$!'");
  gp_message ("debug", $subr_name, "opened script file $exp_info_file for reading");

  chomp (@exp_info = <EXP_INFO>);

#-------------------------------------------------------------------------------
# TBD - Check for the groups to exist below:
#-------------------------------------------------------------------------------
  $current_slot = 0;
  for my $i (0 .. $#exp_info)
    {
      my $input_line = $exp_info[$i];

      gp_message ("debug", $subr_name, "$i => exp_info[$i] = $exp_info[$i]");

      if ($input_line =~ /^\s*(\d+)\s+(.+)/)
        {
          my $exp_id    = $1;
          my $remainder = $2;
          $experiment_data[$current_slot]{"exp_id"} = $exp_id;
          $experiment_data[$current_slot]{"exp_data_file"} = $outputdir . "gp-info-exp-" . $exp_id . ".out";
          gp_message ("debug", $subr_name, $i . " " . $exp_id . " " . $remainder);
          if ($remainder =~ /^(\w+)\s+(\d+)\s+(.+)/)
            {
              my $exp_name = $3;
              $experiment_data[$current_slot]{"exp_name_full"} = $exp_name;
              $experiment_data[$current_slot]{"exp_name_short"} = get_basename ($exp_name);
              $current_slot++;
              gp_message ("debug", $subr_name, $i . " " . $1 . " " . $2 . " " . $3);
            }
          else
            {
              my $msg = "remainder = $remainder has an unexpected format";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
    }
#-------------------------------------------------------------------------------
# The experiment IDs and names are known.  We can now generate the info for
# each individual experiment.
#-------------------------------------------------------------------------------
  $gp_log_file   = $outputdir . $g_gp_output_file;
  $gp_error_file = $outputdir . $g_gp_error_logfile;

  $script_file = $outputdir . "gp-details-exp.script";

  open (SCRIPT_EXPERIMENT_DETAILS, ">", $script_file) 
    or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
  gp_message ("debug", $subr_name, "opened script file $script_file for writing");

  for my $i (sort keys @experiment_data)
    {
      my $exp_id = $experiment_data[$i]{"exp_id"};

      $result_file = $experiment_data[$i]{"exp_data_file"};

# statistics
# header
      print SCRIPT_EXPERIMENT_DETAILS "# outfile "    . $result_file . "\n";
      print SCRIPT_EXPERIMENT_DETAILS "outfile "      . $result_file . "\n";
      print SCRIPT_EXPERIMENT_DETAILS "# header "     . $exp_id . "\n";
      print SCRIPT_EXPERIMENT_DETAILS "header "       . $exp_id . "\n";
      print SCRIPT_EXPERIMENT_DETAILS "# statistics " . $exp_id . "\n";
      print SCRIPT_EXPERIMENT_DETAILS "statistics "   . $exp_id . "\n";

    }

  close (SCRIPT_EXPERIMENT_DETAILS);

  $gp_functions_cmd = "$GP_DISPLAY_TEXT -script $script_file $the_experiments";

  gp_message ("debug", $subr_name, "executing $GP_DISPLAY_TEXT to get the experiment details");

  $gp_display_text_cmd = "$gp_functions_cmd 1>> $gp_log_file 2>> $gp_error_file";

  ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
#-------------------------------------------------------------------------------
# This is unlikely to happen, but you never know.
#-------------------------------------------------------------------------------
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", $subr_name, "execution terminated");
    }

  return (\@experiment_data);

} #-- End of subroutine get_experiment_info

#------------------------------------------------------------------------------
# This subroutine returns a string of the type "size=<n>", where <n> is the
# size of the file passed in.  If n > 1024, a unit is appended.
#------------------------------------------------------------------------------
sub getfilesize
{
  my $subr_name = get_my_name ();

  my ($filename) = @_;

  my $size;
  my $file_stat;

  if (not -e $filename)
    {
#------------------------------------------------------------------------------
# The return value is used in the caller.  This is why we return the empty
# string in case the file does not exist.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name, "filename = $filename not found");
      return ("");
    }
  else
    {
      $file_stat = stat ($filename);
      $size      = $file_stat->size;

      gp_message ("debug", $subr_name, "filename = $filename");
      gp_message ("debug", $subr_name, "size     = $size");

      if ($size > 1024)
        {
          if ($size > 1024*1024)
            {
              $size = $size/1024/1024;
              $size =~ s/\..*//;
              $size = $size."MB";
            }
          else
            {
              $size = $size/1024;
              $size =~ s/\..*//;
              $size = $size."KB";
            }
        }
      else
        {
          $size=$size." bytes";
        }
      gp_message ("debug", $subr_name, "size = $size title=\"$size\"");

      return ("title=\"$size\"");
    }

} #-- End of subroutine getfilesize

#------------------------------------------------------------------------------
# Parse the fsummary output and for all functions, store all the information 
# found in "function_info".  In addition to this, several derived structures 
# are stored as well, making this structure a "onestop" place to get all the
# info that is needed.
#------------------------------------------------------------------------------
sub get_function_info
{ 
  my $subr_name = get_my_name ();

  my ($FSUMMARY_FILE) = @_;

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $white_space_regex = '\s*';

  my @function_info              = ();
  my %function_address_and_index = ();
  my %LINUX_vDSO                 = ();
  my %function_view_structure    = ();
  my %addressobjtextm            = ();
#------------------------------------------------------------------------------
# TBD: This structure is no longer used and most likely can be removed.
#------------------------------------------------------------------------------
  my %functions_index             = ();

# TBD: check
  my $full_address_field;
  my %source_files   = ();

  my $i;
  my $line;
  my $routine_flag;
  my $value;
  my $whatever;
  my $df_flag;
  my $address_decimal;
  my $routine;

  my $num_source_files           = 0;
  my $number_of_functions        = 0;
  my $number_of_unique_functions = 0;
  my $number_of_non_unique_functions = 0;

#------------------------------------------------------------------------------
# Open the file generated using the -fsummary option.
#------------------------------------------------------------------------------
  open (FSUMMARY_FILE, "<", $FSUMMARY_FILE)
    or die ("$subr_name - unable to open $FSUMMARY_FILE for reading: '$!'");
  gp_message ("debug", $subr_name, "opened file $FSUMMARY_FILE for reading");

#------------------------------------------------------------------------------
# This is the typical structure of the fsummary output:
#
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Functions sorted by metric: Exclusive Total CPU Time
# 
# <Total>
#         Exclusive Total CPU Time: 11.538 (100.0%)
#         Inclusive Total CPU Time: 11.538 (100.0%)
#                             Size:      0
#                       PC Address: 1:0x00000000
#                      Source File: (unknown)
#                      Object File: (unknown)
#                      Load Object: <Total>
#                     Mangled Name:
#                          Aliases:
# 
# a_function_name
#         Exclusive Total CPU Time:  4.003 ( 34.7%)
#         Inclusive Total CPU Time:  4.003 ( 34.7%)
#                             Size:    715
#                       PC Address: 2:0x00006c61
#                      Source File: <absolute path to source file> 
#                      Object File: <object filename> 
#                      Load Object: <executable name>
#                     Mangled Name:
#                          Aliases:
#
# The previous block is repeated for every function.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Skip the header.  The header is defined to end with a blank line.
#------------------------------------------------------------------------------
  while (<FSUMMARY_FILE>)
    {
      $line = $_;
      chomp ($line);
      if ($line =~ /^\s*$/)
        {
          last;
        }
    }

#------------------------------------------------------------------------------
# Process the remaining blocks.  Note that the first line should be <Total>,
# but this is currently not checked.
#------------------------------------------------------------------------------
  $i = 0;
  $routine_flag = $TRUE;
  while (<FSUMMARY_FILE>)
    {
      $line = $_;
      chomp ($line);
      gp_message ("debugXL", $subr_name, "line = $line");

      if ($line =~ /^\s*$/)
#------------------------------------------------------------------------------
# Blank line.
#------------------------------------------------------------------------------
        {
          $routine_flag = $TRUE;
          $df_flag = 0;

#------------------------------------------------------------------------------
# Linux vDSO exception
#
# TBD: Check if still relevant.
#------------------------------------------------------------------------------
          if ($function_info[$i]{"Load Object"} eq "DYNAMIC_FUNCTIONS")
            {
              $LINUX_vDSO{substr ($function_info[$i]{"addressobjtext"},1)} = $function_info[$i]{"routine"};
            }
          $i++;
          next;
        }

      if ($routine_flag)
#------------------------------------------------------------------------------
# Should be the first line after the blank line.
#------------------------------------------------------------------------------
        {
          $routine                      = $line;
          push (@{ $g_map_function_to_index{$routine} }, $i);
          gp_message ("debugXL", $subr_name, "pushed i = $i to g_map_function_to_index{$routine}");

#------------------------------------------------------------------------------
# In a later parsing phase we need to know how many fields there are in a
# function name. For example, "<static>@0x21850 (<libc-2.28.so>)" is name that
# may show up in a function list.
#
# Here we determine the number of fields and store it.
#------------------------------------------------------------------------------
          my @fields_in_name = split (" ", $routine);
          $function_info[$i]{"fields in routine name"} = scalar (@fields_in_name);

#------------------------------------------------------------------------------
# This name may change if the function has multiple occurrences, but in any
# case, at the end of this routine this component has the final name to be
# used.
#------------------------------------------------------------------------------
          $function_info[$i]{"alt_name"} = $routine;
          if (not exists ($g_function_occurrences{$routine}))
            {
              gp_message ("debugXL", $subr_name, "the entry in function_info for $routine does not exist");
              $function_info[$i]{"routine"} = $routine;
              $g_function_occurrences{$routine} = 1;

              gp_message ("debugXL", $subr_name, "g_function_occurrences{$routine} = $g_function_occurrences{$routine}");
            }
          else
            {
              gp_message ("debugXL", $subr_name, "the entry in function_info for $routine exists already");
              $function_info[$i]{"routine"} = $routine;
              $g_function_occurrences{$routine} += 1;
              if (not exists ($g_multi_count_function{$routine}))
                {
                  $g_multi_count_function{$routine} = $TRUE;
                }
              my $msg = "g_function_occurrences{$routine} = " .
                        $g_function_occurrences{$routine};
              gp_message ("debugXL", $subr_name, $msg);
            }
#------------------------------------------------------------------------------
# New: used when generating the index.
#------------------------------------------------------------------------------
          $function_info[$i]{"function length"} = length ($routine);
          $function_info[$i]{"tag_id"} = create_function_tag ($i);
          if (not exists ($g_function_tag_id{$routine}))
            {
              $g_function_tag_id{$routine} = create_function_tag ($i);
            }
          else
            {

#------------------------------------------------------------------------------
## TBD HACK!!! CHECK!!!!!
#------------------------------------------------------------------------------
              $g_function_tag_id{$routine} = $i;
            }

          $routine_flag = $FALSE;
          gp_message ("debugXL", $subr_name, "stored " . $function_info[$i]{"routine"});

#------------------------------------------------------------------------------
# The $functions_index hash contains an array.  After an initial assignment, 
# other values that have been found are pushed onto the arrays.
#------------------------------------------------------------------------------
          if (not exists ($functions_index{$routine}))
            {
              $functions_index{$routine} = [$i];
            } 
          else 
            {
#------------------------------------------------------------------------------
# Add the array index to the list
#------------------------------------------------------------------------------
              push (@{$functions_index{$routine}}, $i);
            }
          next;
        }

#------------------------------------------------------------------------------
# Expected format of an input line:
#   Exclusive Total CPU Time:  4.003 ( 34.7%)
# or:
#   Source File: <absolute_path>/name_of_source_file
#------------------------------------------------------------------------------
      $line =~ s/^\s+//;

      my @input_fields   = split (":", $line);
      my $no_of_elements = scalar (@input_fields);

      gp_message ("debugXL", $subr_name, "#input_fields = $#input_fields");
      gp_message ("debugXL", $subr_name, "no_of_elements = $no_of_elements");
      gp_message ("debugXL", $subr_name, "input_fields[0] = $input_fields[0]");

      if ($no_of_elements == 1)
        {
          $whatever = $input_fields[0];
          $value    = "";
        }
      elsif ($no_of_elements == 2)
        {
#------------------------------------------------------------------------------
# Note that value may consist of multiple fields (e.g. 1.651 ( 95.4%)).
#------------------------------------------------------------------------------
          $whatever = $input_fields[0];
          $value    = $input_fields[1];
        }
      elsif ($no_of_elements == 3)
        {
#------------------------------------------------------------------------------
# Assumption: must be an address field.  Restore the second colon.
#------------------------------------------------------------------------------
          $whatever = $input_fields[0];
          $value    = $input_fields[1] . ":" . $input_fields[2];
        }
      else
        {
          my $msg = "unexpected: number of fields = " . $no_of_elements; 
          gp_message ("assertion", $subr_name, $msg);
        }
#------------------------------------------------------------------------------
# Remove any leading whitespace characters.
#------------------------------------------------------------------------------
      $value =~ s/$white_space_regex//;

      gp_message ("debugXL", $subr_name, "whatever = $whatever value = $value");

      $function_info[$i]{$whatever} = $value;

#------------------------------------------------------------------------------
# TBD: Seems to be not used anymore and can most likely be removed. Check this.
#------------------------------------------------------------------------------
      if ($whatever =~ /Source File/)
        {
          if (!exists ($source_files{$value}))
            {
              $source_files{$value} = $TRUE;
              $num_source_files++;
            }
        }

      if ($whatever =~ /PC Address/)
        {
          my $segment;
          my $offset;
#------------------------------------------------------------------------------
# The format of the address is assumed to be the following 2:0x000070a8
# Note that the regex is pretty wide.  This is from the original code and 
# could be made more specific:
#          if ($value =~ /\s*(\S+):(\S+)/)
#------------------------------------------------------------------------------
#          if ($value =~ /\s*(\S+):(\S+)/)
          if ($value =~ /\s*(\d+):0x([0-9a-zA-Z]+)/)
            {
              $segment = $1;
              $offset  = $2;
#------------------------------------------------------------------------------
# Convert to a base 10 number
#------------------------------------------------------------------------------
              $address_decimal = bigint::hex ($offset); # decimal
#------------------------------------------------------------------------------
# Construct the address field.  Note that we use the hex address here.
#------------------------------------------------------------------------------
              $full_address_field = '@'.$segment.":0x".$offset; # e.g. @2:0x0003f280

              $function_info[$i]{"addressobj"}     = $address_decimal;
              $function_info[$i]{"addressobjtext"} = $full_address_field;
              $addressobjtextm{$full_address_field} = $i; # $RI
            }
          if (not exists ($function_address_and_index{$routine}{$value}))
            {
              $function_address_and_index{$routine}{$value} = $i;

              my $msg = "function_address_and_index{$routine}{$value} = " .
                        $function_address_and_index{$routine}{$value};
              gp_message ("debugXL", $subr_name, $msg);
            } 
          else 
            {
              gp_message ("debugXL", $subr_name, "function_info: $FSUMMARY_FILE: function $routine already has a PC Address");
            } 

          $number_of_functions++;
        }
    }
  close (FSUMMARY_FILE);
 
#------------------------------------------------------------------------------
# For every function in the function overview, set up an html structure with
# the various hyperlinks.
#------------------------------------------------------------------------------
  gp_message ("debugXL", $subr_name, "augment function_info with alt_name");
  my $target_function; 
  my $html_line;
  my $ftag;
  my $routine_length; 
  my %html_source_functions = ();
  for my $i (keys @function_info)
    {
      $target_function = $function_info[$i]{"routine"};

      gp_message ("debugXL", $subr_name, "i = $i target_function = $target_function");

      my $href_link;
##      $href_link  = "<a href=\'file." . $i . ".src.new.html#";
      $href_link  = "<a href=\'file." . $i . ".";
      $href_link .= $g_html_base_file_name{"source"};
      $href_link .= ".html#";
      $href_link .= $function_info[$i]{"tag_id"};
      $href_link .= "\'>source</a>";
      $function_info[$i]{"href_source"} = $href_link;

      $href_link  = "<a href=\'file." . $i . ".";
      $href_link .= $g_html_base_file_name{"disassembly"};
      $href_link .= ".html#";
      $href_link .= $function_info[$i]{"tag_id"};
      $href_link .= "\'>disassembly</a>";
      $function_info[$i]{"href_disassembly"} = $href_link;

      $href_link  = "<a href=\'";
      $href_link .= $g_html_base_file_name{"caller_callee"};
      $href_link .= ".html#";
      $href_link .= $function_info[$i]{"tag_id"};
      $href_link .= "\'>caller-callee</a>";
      $function_info[$i]{"href_caller_callee"} = $href_link;

      gp_message ("debug", $subr_name, "g_function_occurrences{$target_function} = $g_function_occurrences{$target_function}");

      if ($g_function_occurrences{$target_function} > 1)
        {
#------------------------------------------------------------------------------
# In case a function occurs more than one time in the function overview, we
# add the load object and address offset info to make it unique.
#
# This forces us to update some entries in function_info too.
#------------------------------------------------------------------------------
          my $loadobj = $function_info[$i]{"Load Object"};
          my $address_field = $function_info[$i]{"addressobjtext"};
          my $address_offset; 

#------------------------------------------------------------------------------
# The address field has the following format: @<n>:<address_offset>
# We only care about the address offset.
#------------------------------------------------------------------------------
          if ($address_field =~ /(^@\d*:*)(.+)/)
            {
              $address_offset = $2;
            }
          else
            {
              my $msg = "failed to extract the address offset from $address_field - use the full field";
              gp_message ("warning", $subr_name, $msg);
              $address_offset = $address_field;
            }
          my $exe = get_basename ($loadobj);
          my $extra_field = " (<" . $exe . " $address_offset" .">)";
###          $target_function .= $extra_field;
          $function_info[$i]{"alt_name"} = $target_function . $extra_field;
          gp_message ("debugXL", $subr_name, "function_info[$i]{\"alt_name\"} = " . $function_info[$i]{"alt_name"});

#------------------------------------------------------------------------------
# Store the length of the function name and get the tag id.
#------------------------------------------------------------------------------
          $function_info[$i]{"function length"} = length ($target_function . $extra_field);
          $function_info[$i]{"tag_id"} = create_function_tag ($i);

          gp_message ("debugXL", $subr_name, "updated function_info[$i]{'routine'} = $function_info[$i]{'routine'}");
          gp_message ("debugXL", $subr_name, "updated function_info[$i]{'alt_name'} = $function_info[$i]{'alt_name'}");
          gp_message ("debugXL", $subr_name, "updated function_info[$i]{'function length'} = $function_info[$i]{'function length'}");
          gp_message ("debugXL", $subr_name, "updated function_info[$i]{'tag_id'} = $function_info[$i]{'tag_id'}");
        }
    }
  gp_message ("debug", $subr_name, "augment function_info with alt_name completed");

#------------------------------------------------------------------------------
# Compute the maximum function name length. 
#
# The maximum length is stored in %function_view_structure.
#------------------------------------------------------------------------------
  my $max_function_length = 0;
  for my $i (0 .. $#function_info)
    {
      $max_function_length = max ($max_function_length, $function_info[$i]{"function length"});

      gp_message ("debugXL", $subr_name, "function_info[$i]{\"alt_name\"} = " . $function_info[$i]{"alt_name"} . " length = " . $function_info[$i]{"function length"});
    }

#------------------------------------------------------------------------------
# Define the name of the table and take the length into account, since it may
# be longer than the function name(s).
#------------------------------------------------------------------------------
  $function_view_structure{"table name"} = "Function name";

  $max_function_length = max ($max_function_length, length ($function_view_structure{"table name"}));

  $function_view_structure{"max function length"} = $max_function_length;

#------------------------------------------------------------------------------
# Core loop that generates an HTML line for each function.  This line is
# stored in function_info.
#------------------------------------------------------------------------------
  my $top_of_table = $FALSE;
  for my $i (keys @function_info)
    {
      my $new_target_function; 

      if (defined ($function_info[$i]{"alt_name"}))
        {
          $target_function = $function_info[$i]{"alt_name"};
          gp_message ("debugXL", $subr_name, "retrieved function_info[$i]{'alt_name'} = $function_info[$i]{'alt_name'}");
        }
      else
        {
          my $msg = "function_info[$i]{\"alt_name\"} is not defined";
          gp_message ("assertion", $subr_name, $msg);
        }

      my $function_length  = $function_info[$i]{"function length"};
      my $number_of_blanks = $function_view_structure{"max function length"} - $function_length;

      my $spaces = "&nbsp;&nbsp;";
      for my $i (1 .. $number_of_blanks)
        {
          $spaces .= "&nbsp;";
        }
      if ($target_function eq "<Total>")
#------------------------------------------------------------------------------
# <Total> is a pseudo function and there is no source, or disassembly for it.
# We could add a link to the caller-callee part, but this is currently not 
# done.
#------------------------------------------------------------------------------
        {
          $top_of_table = $TRUE;
          $html_line  = "&nbsp;<b>&lt;Total></b>";
        }
      else
        {
#------------------------------------------------------------------------------
# Add the * symbol as a marker in case the same function occurs multiple times.
# Otherwise insert a space.
#------------------------------------------------------------------------------
          my $base_function_name = $function_info[$i]{"routine"};
          if (exists ($g_function_occurrences{$base_function_name}))
            {
              if ($g_function_occurrences{$base_function_name} > 1)
                {
                  $new_target_function = "*" . $target_function;
                }
              else
                {
                  $new_target_function = "&nbsp;" . $target_function;
                }
            }
          else
            {
              my $msg = "g_function_occurrences{$base_function_name} does not exist";
              gp_message ("assertion", $subr_name, $msg);
            }

#------------------------------------------------------------------------------
# Create the block with the function name, in boldface, plus the links to the 
# source, disassembly and caller-callee views.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
          $new_target_function =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

          $html_line  = "<b>$new_target_function</b>" . $spaces;
          $html_line .= $function_info[$i]{"href_source"}      . "&nbsp;";
          $html_line .= $function_info[$i]{"href_disassembly"} . "&nbsp;";
          $html_line .= $function_info[$i]{"href_caller_callee"};
        }

      gp_message ("debugXL", $subr_name, "target_function = $target_function html_line = $html_line");
      $html_source_functions{$target_function} = $html_line;

#------------------------------------------------------------------------------
# TBD: In the future we want to re-use this block elsewhere.
#------------------------------------------------------------------------------
      $function_info[$i]{"html function block"} = $html_line;
    }

  for my $i (keys %html_source_functions)
    {
      gp_message ("debugXL", $subr_name, "html_source_functions{$i} = $html_source_functions{$i}");
    }
  for my $i (keys @function_info)
    {
      gp_message ("debugXL", $subr_name, "function_info[$i]{\"html function block\"} = " . $function_info[$i]{"html function block"}); 
    }

#------------------------------------------------------------------------------
# Print the key data structure %function_info.  This is a nested hash.
#------------------------------------------------------------------------------
  for my $i (0 .. $#function_info)
    {
      for my $role (sort keys %{ $function_info[$i] })
        {
           gp_message ("debug", $subr_name, "on return: function_info[$i]{$role} = $function_info[$i]{$role}");
        }
    }
#------------------------------------------------------------------------------
# Print the data structure %function_address_and_index. This is a nested hash.
#------------------------------------------------------------------------------
  for my $F (keys %function_address_and_index)
    {
      for my $fields (sort keys %{ $function_address_and_index{$F} })
        {
           gp_message ("debug", $subr_name, "on return: function_address_and_index{$F}{$fields} = $function_address_and_index{$F}{$fields}");
        }
    }
#------------------------------------------------------------------------------
# Print the data structure %functions_index. This is a hash with an arrray.
#------------------------------------------------------------------------------
  for my $F (keys %functions_index)
    {
      gp_message ("debug", $subr_name, "on return: functions_index{$F} = @{ $functions_index{$F} }");
# alt code      for my $i (0 .. $#{ $functions_index{$F} } )
# alt code        {
# alt code           gp_message ("debug", $subr_name, "on return: \$functions_index{$F} = $functions_index{$F}[$i]");
# alt code        }
    }

#------------------------------------------------------------------------------
# Print the data structure %function_view_structure. This is a hash. 
#------------------------------------------------------------------------------
  for my $F (keys %function_view_structure)
    {
      gp_message ("debug", $subr_name, "on return: function_view_structure{$F} = $function_view_structure{$F}");
    }

#------------------------------------------------------------------------------
# Print the data structure %g_function_occurrences and use this structure to
# gather statistics about the functions.
#
# TBD: add this info to the experiment data overview.
#------------------------------------------------------------------------------
  $number_of_unique_functions = 0;
  $number_of_non_unique_functions = 0;
  for my $F (keys %g_function_occurrences)
    {
      gp_message ("debug", $subr_name, "on return: g_function_occurrences{$F} = $g_function_occurrences{$F}");
      if ($g_function_occurrences{$F} == 1)
        {
          $number_of_unique_functions++; 
        }
      else
        {
          $number_of_non_unique_functions++; 
        }
    }

  for my $i (keys %g_map_function_to_index)
    {
      my $n = scalar (@{ $g_map_function_to_index{$i} });
      gp_message ("debug", $subr_name, "on return: g_map_function_to_index [$n] : $i => @{ $g_map_function_to_index{$i} }");
    }

#------------------------------------------------------------------------------
# TBD: Include in experiment data. Include names with multiple occurrences.
#------------------------------------------------------------------------------
  my $msg;

  $msg = "Number of source files                                        : " .
         $num_source_files;
  gp_message ("debug", $subr_name, $msg);
  $msg = "Total number of functions: $number_of_functions"; 
  gp_message ("debug", $subr_name, $msg);
  $msg = "Number of functions functions with a unique name              : " .
         $number_of_unique_functions; 
  gp_message ("debug", $subr_name, $msg);
  $msg = "Number of functions functions with more than one occurrence   : " .
         $number_of_non_unique_functions; 
  gp_message ("debug", $subr_name, $msg);
  my $multi_occurrences = $number_of_functions - $number_of_unique_functions; 
  $msg = "Total number of multiple occurences of the same function name : " .
         $multi_occurrences; 
  gp_message ("debug", $subr_name, $msg);

  return (\@function_info, \%function_address_and_index, \%addressobjtextm, 
          \%LINUX_vDSO, \%function_view_structure);

} #-- End of subroutine get_function_info
#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub get_hdr_info
{
  my $subr_name = get_my_name ();

  my ($outputdir, $file) = @_;

  state $first_call = $TRUE;

  my $ASORTFILE;
  my @HDR;
  my $HDR;
  my $metric;
  my $line;
  my $ignore_directory;
  my $ignore_suffix;
  my $number_of_header_lines;

#------------------------------------------------------------------------------
# Add a "/" to simplify the construction of path names in the remainder.
#------------------------------------------------------------------------------
  $outputdir = append_forward_slash ($outputdir);

# Could get more header info from
# <metric>[e.bit_fcount].sort.func file - etc.

  gp_message ("debug", $subr_name, "input file->$file<-");
#-----------------------------------------------
  if ($file eq $outputdir."calls.sort.func")
    {
      $ASORTFILE=$outputdir."calls";
      $metric = "calls"
    } 
  elsif ($file eq $outputdir."calltree.sort.func")
    {
      $ASORTFILE=$outputdir."calltree";
      $metric = "calltree"
    }
  elsif ($file eq $outputdir."functions.sort.func")
    {
      $ASORTFILE=$outputdir."functions.func";
      $metric = "functions";
    }
  else
    {
      $ASORTFILE = $file;
#      $metric = basename ($file,".sort.func");
      ($metric, $ignore_directory,  $ignore_suffix) = fileparse ($file, ".sort.func");
      gp_message ("debug", $subr_name, "ignore_directory = $ignore_directory ignore_suffix = $ignore_suffix");
    }

  gp_message ("debug", $subr_name, "file = $file metric = $metric");

  open (ASORTFILE,"<", $ASORTFILE)
    or die ("$subr_name - unable to open file $ASORTFILE for reading: '$!'");
  gp_message ("debug", $subr_name, "opened file $ASORTFILE for reading");

  $number_of_header_lines = 0;
  while (<ASORTFILE>)
    {
      $line =$_;
      chomp ($line);

      if ($line  =~ /^Current/)
        {
          next;
        }
      if ($line  =~ /^Functions/)
        {
          next;
        }
      if ($line  =~ /^Callers/)
        {
          next;
        }
      if ($line  =~ /^\s*$/)
        {
          next;
        }
      if (!($line  =~ /^\s*\d/))
        {
          $HDR[$number_of_header_lines] = $line;
          $number_of_header_lines++;
          next;
        }
      last;
     }
  close (ASORTFILE);
#-------------------------------------------------------------------------------
# Ruud - Fixed a bug. The output should not be appended, but overwritten.
# open (HI,">>$OUTPUTDIR"."hdrinfo");
#-------------------------------------------------------------------------------
  my $outfile = $outputdir."hdrinfo";

  if ($first_call)
    {
      $first_call = $FALSE;
      open (HI ,">", $outfile)
        or die ("$subr_name - unable to open file $outfile for writing: '$!'");
      gp_message ("debug", $subr_name, "opened file $outfile for writing");
    }
  else
    {
      open (HI ,">>", $outfile)
        or die ("$subr_name - unable to open file $outfile in append mode: '$!'");
      gp_message ("debug", $subr_name, "opened file $outfile in append mode");
    }

  print HI "\#$metric hdrlines=$number_of_header_lines\n";
  my $len = 0;
  for $HDR (@HDR)
    {
      print HI "$HDR\n";
      gp_message ("debugXL", $subr_name, "HDR = $HDR\n");
    }
  close (HI);
  if ($first_call)
    {
      gp_message ("debug", $subr_name, "wrote file $outfile");
    }
  else
    {
      gp_message ("debug", $subr_name, "updated file $outfile");
    }
#-----------------------------------------------

} #-- End of subroutine get_hdr_info

#------------------------------------------------------------------------------
# Get the home directory and the location(s) of the configuration file on the 
# current system.
#------------------------------------------------------------------------------
sub get_home_dir_and_rc_path
{
  my $subr_name = get_my_name ();

  my ($rc_file_name) = @_;

  my @rc_file_paths;
  my $target_cmd;
  my $home_dir;
  my $error_code;

  $target_cmd  = $g_mapped_cmds{"printenv"} . " HOME";

  ($error_code, $home_dir) = execute_system_cmd ($target_cmd);
   
  if ($error_code != 0)
    {
      my $msg = "cannot find a setting for HOME - please set this"; 
      gp_message ("assertion", $subr_name, $msg);
    }
  else

#------------------------------------------------------------------------------
# The home directory is known and we can define the locations for the 
# configuration file.
#------------------------------------------------------------------------------
    {
      @rc_file_paths = (".", "$home_dir");
    } 
  
  gp_message ("debug", $subr_name, "upon return: \@rc_file_paths = @rc_file_paths");

  return ($home_dir, \@rc_file_paths);

} #-- End of subroutine get_home_dir_and_rc_path

#------------------------------------------------------------------------------
# This subroutine generates a list with the hot functions.
#------------------------------------------------------------------------------
sub get_hot_functions
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref, $summary_metrics, $input_string) = @_;

  my @exp_dir_list = @{ $exp_dir_list_ref };

  my $cmd_output;
  my $error_code;
  my $expr_name;
  my $first_metric;
  my $gp_display_text_cmd;
  my $ignore_value;

  my @sort_fields = ();

  $expr_name = join (" ", @exp_dir_list);

  gp_message ("debug", $subr_name, "expr_name = $expr_name");

  my $outputdir = append_forward_slash ($input_string);

  my $script_file   = $outputdir."gp-fsummary.script";
  my $outfile       = $outputdir."gp-fsummary.out";
  my $result_file   = $outputdir."gp-fsummary.stderr";
  my $gp_error_file = $outputdir.$g_gp_error_logfile;

  @sort_fields = split (":", $summary_metrics);

#------------------------------------------------------------------------------
# This is extremely unlikely to happen, but if so, it is a fatal error.
#------------------------------------------------------------------------------
  my $number_of_elements = scalar (@sort_fields);

  gp_message ("debug", $subr_name, "number of fields in summary_metrics = $number_of_elements");

  if ($number_of_elements == 0)
    {
      my $msg = "there are $number_of_elements in the metrics list";
      gp_message ("assertion", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Get the summary of the hot functions
#------------------------------------------------------------------------------
  open (SCRIPT, ">", $script_file) 
    or die ("$subr_name - unable to open script file $script_file for writing: '$!'");
  gp_message ("debug", $subr_name, "opened script file $script_file for writing");

#------------------------------------------------------------------------------
# TBD: Check what this is about:
# Attributed User CPU Time=a.user : for calltree - see P37 in manual
#------------------------------------------------------------------------------
  print SCRIPT "# limit 0\n";
  print SCRIPT "limit 0\n";
  print SCRIPT "# metrics $summary_metrics\n";
  print SCRIPT "metrics $summary_metrics\n";
  print SCRIPT "# thread_select all\n";
  print SCRIPT "thread_select all\n";

#------------------------------------------------------------------------------
# Use first out of summary metrics as first (it doesn't matter which one)
# $first_metric = (split /:/,$summary_metrics)[0];
#------------------------------------------------------------------------------

  $first_metric = $sort_fields[0];

  print SCRIPT "# outfile $outfile\n";
  print SCRIPT "outfile $outfile\n";
  print SCRIPT "# sort $first_metric\n";
  print SCRIPT "sort $first_metric\n";
  print SCRIPT "# fsummary\n";
  print SCRIPT "fsummary\n";

  close SCRIPT;

  my $gp_functions_cmd = "$GP_DISPLAY_TEXT -viewmode machine -compare off -script $script_file $expr_name";

  gp_message ("debug", $subr_name, "executing $GP_DISPLAY_TEXT to get the list of functions");

  $gp_display_text_cmd = "$gp_functions_cmd 1> $result_file 2>> $gp_error_file";

  ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", $subr_name, "execution terminated");
      my $msg = "error code = $error_code - failure executing command $gp_display_text_cmd";
      gp_message ("abort", $subr_name, $msg);
    }

  return ($outfile,\@sort_fields);

} #-- End of subroutine get_hot_functions

#------------------------------------------------------------------------------
# For a given function name, return the index into "function_info".  This
# index gives access to all the meta data for the input function.
#------------------------------------------------------------------------------
sub get_index_function_info
{
  my $subr_name = get_my_name ();

  my ($routine_ref, $hex_address_ref, $function_info_ref) = @_;

  my $routine     = ${ $routine_ref };
  my $hex_address = ${ $hex_address_ref };
  my @function_info = @{ $function_info_ref };

#------------------------------------------------------------------------------
# Check if this function has multiple occurrences.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "check for multiple occurrences");

  my $current_address = $hex_address;
  my $alt_name = $routine;

  my $found_a_match;
  my $index_into_function_info;
  my $target_tag;

  if (not exists ($g_multi_count_function{$routine}))
    {
#------------------------------------------------------------------------------
# There is only a single occurrence and it is straightforward to get the tag.
#--------------------------------------------------------------------------
##          push (@final_function_names, $routine);
      if (exists ($g_map_function_to_index{$routine}))
        {
          $index_into_function_info = $g_map_function_to_index{$routine}[0];
        }
      else
        {
          my $msg = "no entry for $routine in g_map_function_to_index";
          gp_message ("assertion", $subr_name, $msg);
        }
    }
  else
    {
#------------------------------------------------------------------------------
# The function name has more than one occurrence and we need to find the one
# that matches with the address.
#------------------------------------------------------------------------------
      $found_a_match = $FALSE;
      gp_message ("debug", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
      for my $ref (keys @{ $g_map_function_to_index{$routine} })
        {
          my $ref_index   = $g_map_function_to_index{$routine}[$ref];
          my $addr_offset = $function_info[$ref_index]{"addressobjtext"};

          gp_message ("debug", $subr_name, "$routine: retrieving duplicate entry at ref_index = $ref_index");
          gp_message ("debug", $subr_name, "$routine: addr_offset = $addr_offset");
  
#------------------------------------------------------------------------------
# TBD: Do this substitution when storing "addressobjtext" in function_info.
#------------------------------------------------------------------------------
          $addr_offset =~ s/^@\d+://;
          gp_message ("debug", $subr_name, "$routine: addr_offset = $addr_offset");
          if ($addr_offset eq $current_address)
            {
              $found_a_match = $TRUE;
              $index_into_function_info = $ref_index;
              last;
            }
        }

#------------------------------------------------------------------------------
# If there is no match, something has gone really wrong and we bail out.
#------------------------------------------------------------------------------
      if (not $found_a_match)
        {
          my $msg = "cannot find the mapping in function_info for function $routine";
          gp_message ("assertion", $subr_name, $msg);
        }
    }

  return (\$index_into_function_info);

} #-- End of subroutine get_index_function_info

#-------------------------------------------------------------------------------
# Get the setting for LANG, or assign a default if it is not set.
#-------------------------------------------------------------------------------
sub get_LANG_setting
{
  my $subr_name = get_my_name ();

  my $error_code;
  my $lang_setting;
  my $target_cmd;
  my $command_string; 
  my $LANG;

  $target_cmd = $g_mapped_cmds{"printenv"};
#------------------------------------------------------------------------------
# Use the printenv command to get the settings for LANG.
#------------------------------------------------------------------------------
  if ($target_cmd eq "road_to_nowhere")
    {
      $error_code = 1;
    }
  else
    {
      $command_string = $target_cmd . " LANG";
      ($error_code, $lang_setting) = execute_system_cmd ($command_string);
    }

  if ($error_code == 0)
    {
      chomp ($lang_setting);
      $LANG = $lang_setting;
    }
  else
    {
      $LANG = $g_default_setting_lang;
      my $msg = "cannot find a setting for LANG - use a default setting";
      gp_message ("warning", $subr_name, $msg);
    }

  return ($LANG);

} #-- End of subroutine get_LANG_setting

#------------------------------------------------------------------------------
# This subroutine gathers the basic information about the metrics.
#------------------------------------------------------------------------------
sub get_metrics_data
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref, $outputdir, $outfile1, $outfile2, $error_file) = @_;

  my @exp_dir_list = @{ $exp_dir_list_ref };

  my $cmd_options; 
  my $cmd_output; 
  my $error_code;
  my $expr_name;
  my $metrics_cmd;
  my $metrics_output;
  my $target_cmd;

  $expr_name = join (" ", @exp_dir_list);

  gp_message ("debug", $subr_name, "expr_name = $expr_name");

#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
  $cmd_options   = " -viewmode machine -compare off -thread_select all"; 
  $cmd_options  .= " -outfile $outfile2";
  $cmd_options  .= " -fsingle '<Total>' -metric_list $expr_name";

  $metrics_cmd   = "$GP_DISPLAY_TEXT $cmd_options 1> $outfile1 2> $error_file";

  gp_message ("debug", $subr_name, "command used to gather the information:");
  gp_message ("debug", $subr_name, $metrics_cmd);

  ($error_code, $metrics_output) = execute_system_cmd ($metrics_cmd);

#------------------------------------------------------------------------------
# Error handling.  Any error that occurred is fatal and execution 
# should be aborted by the caller.
#------------------------------------------------------------------------------
  if ($error_code == 0)
    {
      gp_message ("debug", $subr_name, "metrics data in files $outfile1 and $outfile2");
    }
  else
    {
      $target_cmd  = $g_mapped_cmds{"cat"} . " $error_file";

      ($error_code, $cmd_output) = execute_system_cmd ($target_cmd);

      chomp ($cmd_output);

      gp_message ("error", $subr_name, "contents of file $error_file:");
      gp_message ("error", $subr_name, $cmd_output);
    }

  return ($error_code);

} #-- End of subroutine get_metrics_data

#------------------------------------------------------------------------------
# Wrapper that returns the last part of the subroutine name.  The assumption is
# that the last part of the input name is of the form "aa::bb" or just "bb".
#------------------------------------------------------------------------------
sub get_my_name
{
  my $called_by = (caller (1))[3];
  my @parts     = split ("::", $called_by);
  return ($parts[$#parts]);

##  my ($the_full_name_ref) = @_;

##  my $the_full_name = ${ $the_full_name_ref };
##  my $last_part;

#------------------------------------------------------------------------------
# If the regex below fails, use the full name."
#------------------------------------------------------------------------------
##  $last_part = $the_full_name;

#------------------------------------------------------------------------------
# Capture the last part if there are multiple parts separated by "::".
#------------------------------------------------------------------------------
##  if ($the_full_name =~ /.*::(.+)$/)
##    {
##      if (defined ($1))
##        {
##          $last_part = $1;
##        }
##    }

##  return (\$last_part);

} #-- End of subroutine get_my_name

#-------------------------------------------------------------------------------
# Determine the characteristics of the current system
#-------------------------------------------------------------------------------
sub get_system_config_info
{
#------------------------------------------------------------------------------
# The output from the "uname" command is used for this. Although not all of
# these are currently used, we store all fields in separate variables.
#------------------------------------------------------------------------------
#
#------------------------------------------------------------------------------
# The options supported on uname from GNU coreutils 8.22:
#------------------------------------------------------------------------------
#   -a, --all                print all information, in the following order,
#                              except omit -p and -i if unknown:
#   -s, --kernel-name        print the kernel name
#   -n, --nodename           print the network node hostname
#   -r, --kernel-release     print the kernel release
#   -v, --kernel-version     print the kernel version
#   -m, --machine            print the machine hardware name
#   -p, --processor          print the processor type or "unknown"
#   -i, --hardware-platform  print the hardware platform or "unknown"
#   -o, --operating-system   print the operating system
#------------------------------------------------------------------------------
# Sample output:
# Linux ruudvan-vm-2-8-20200701 4.14.35-2025.400.8.el7uek.x86_64 #2 SMP Wed Aug 26 12:22:05 PDT 2020 x86_64 x86_64 x86_64 GNU/Linux
#------------------------------------------------------------------------------
  my $subr_name = get_my_name ();

  my $target_cmd;
  my $hostname_current;
  my $error_code;
  my $ignore_output; 
#------------------------------------------------------------------------------
# Test once if the command succeeds.  This avoids we need to check every 
# specific # command below.
#------------------------------------------------------------------------------
  $target_cmd    = $g_mapped_cmds{uname};
  ($error_code, $ignore_output) = execute_system_cmd ($target_cmd);
   
  if ($error_code != 0)
#-------------------------------------------------------------------------------
# This is unlikely to happen, but you never know.
#-------------------------------------------------------------------------------
    {
      gp_message ("abort", $subr_name, "failure to execute the uname command");
    }
  
  my $kernel_name       = qx ($target_cmd -s); chomp ($kernel_name);
  my $nodename          = qx ($target_cmd -n); chomp ($nodename);
  my $kernel_release    = qx ($target_cmd -r); chomp ($kernel_release);
  my $kernel_version    = qx ($target_cmd -v); chomp ($kernel_version);
  my $machine           = qx ($target_cmd -m); chomp ($machine);
  my $processor         = qx ($target_cmd -p); chomp ($processor);
  my $hardware_platform = qx ($target_cmd -i); chomp ($hardware_platform);
  my $operating_system  = qx ($target_cmd -o); chomp ($operating_system);
  
  $local_system_config{"kernel_name"}       = $kernel_name;
  $local_system_config{"nodename"}          = $nodename;
  $local_system_config{"kernel_release"}    = $kernel_release;
  $local_system_config{"kernel_version"}    = $kernel_version;
  $local_system_config{"machine"}           = $machine;
  $local_system_config{"processor"}         = $processor;
  $local_system_config{"hardware_platform"} = $hardware_platform;
  $local_system_config{"operating_system"}  = $operating_system;
  
  gp_message ("debug", $subr_name, "the output from the $target_cmd command is split into the following variables:");
  gp_message ("debug", $subr_name, "kernel_name       = $kernel_name");
  gp_message ("debug", $subr_name, "nodename          = $nodename");
  gp_message ("debug", $subr_name, "kernel_release    = $kernel_release");
  gp_message ("debug", $subr_name, "kernel_version    = $kernel_version");
  gp_message ("debug", $subr_name, "machine           = $machine");
  gp_message ("debug", $subr_name, "processor         = $processor");
  gp_message ("debug", $subr_name, "hardware_platform = $hardware_platform");
  gp_message ("debug", $subr_name, "operating_system  = $operating_system");
  
#------------------------------------------------------------------------------
# Check if the system we are running on is supported.
#------------------------------------------------------------------------------
  my $is_supported = ${ check_support_for_processor (\$machine) };

  if (not $is_supported)
    {
      gp_message ("error", $subr_name, "$machine is not supported");
      exit (0);
    }
#------------------------------------------------------------------------------
# The current hostname is used to compare against the hostname(s) found in the
# experiment directories.
#------------------------------------------------------------------------------
  $target_cmd       = $g_mapped_cmds{hostname};
  $hostname_current = qx ($target_cmd); chomp ($hostname_current);
  $error_code       = ${^CHILD_ERROR_NATIVE};
   
  if ($error_code == 0)
    {
      $local_system_config{"hostname_current"} = $hostname_current;
    }
  else
#-------------------------------------------------------------------------------
# This is unlikely to happen, but you never know.
#-------------------------------------------------------------------------------
    {
      gp_message ("abort", $subr_name, "failure to execute the hostname command");
    }
  for my $key (sort keys %local_system_config)
    {
      gp_message ("debug", $subr_name, "local_system_config{$key} = $local_system_config{$key}");
    }

  return (0);

} #-- End of subroutine get_system_config_info

#-------------------------------------------------------------------------------
# This subroutine prints a message.  Several types of messages are supported.
# In case the type is "abort", or "error", execution is terminated.  
#
# Note that "debug", "warning", and "error" mode, the name of the calling 
# subroutine is truncated to 30 characters.  In case the name is longer, 
# a warning message # is issued so you know this has happened.
#
# Note that we use lcfirst () and ucfirst () to enforce whether the first 
# character is printed in lower or uppercase.  It is nothing else than a
# convenience, but creates more consistency across messages.
#-------------------------------------------------------------------------------
sub gp_message
{
  my $subr_name = get_my_name ();

  my ($action, $caller_name, $comment_line) = @_;

#-------------------------------------------------------------------------------
# The debugXL identifier is special.  It is accepted, but otherwise ignored.
# This allows to (temporarily) disable debug print statements, but keep them
# around.
#-------------------------------------------------------------------------------
  my %supported_identifiers = (
    "verbose" => "[Verbose]",
    "debug"   => "[Debug]",
    "error"   => "[Error]",
    "warning" => "[Warning]",
    "abort"   => "[Abort]",
    "assertion" => "[Assertion error]",
    "diag"    => "",
  );

  my $debug_size; 
  my $identifier;
  my $fixed_size_name; 
  my $string_limit = 30;
  my $strlen = length ($caller_name);
  my $trigger_debug = $FALSE;
  my $truncated_name; 
  my $msg;

  if ($action =~ /debug\s*(.+)/)
    {
      if (defined ($1))
        {
          my $orig_value = $1;
          $debug_size = lc ($1);

          if ($debug_size =~ /^s$|^m$|^l$|^xl$/)
            {
              if ($g_debug_size{$debug_size})
                {
#-------------------------------------------------------------------------------
# All we need to know is whether a debug action is requested and whether the
# size has been enabled.  By setting $action to "debug", the code below is
# simplified.  Note that only using $trigger_debug below is actually sufficient.
#-------------------------------------------------------------------------------
                  $trigger_debug = $TRUE;
                }
            }
          else
            {
              die "$subr_name: debug size $orig_value is not supported";
            }
          $action = "debug";
        }
    }
  elsif ($action eq "debug")
    {
      $trigger_debug = $TRUE;
    }

#-------------------------------------------------------------------------------
# Catch any non-supported identifier.
#-------------------------------------------------------------------------------
  if (defined ($supported_identifiers{$action}))
    {
      $identifier = $supported_identifiers{$action};
    }
  else
    {
      die ("$subr_name - input error: $action is not supported");
    }
  if (($action eq "debug") and ($g_user_settings{"debug"}{"current_value"} eq "off"))
    {
      $trigger_debug = $FALSE;
    }

#-------------------------------------------------------------------------------
# Unconditionally buffer all warning messages.  These are meant to be displayed
# separately. 
#-------------------------------------------------------------------------------
  if ($action eq "warning")
    {
      push (@g_warning_messages, ucfirst ($comment_line));
    }

#-------------------------------------------------------------------------------
# Quick return in several cases.  Note that "debug", "verbose", "warning", and 
# "diag" messages are suppressed in quiet mode, but "error", "abort" and
# "assertion" always pass.
#-------------------------------------------------------------------------------
  if ((
           ($action eq "verbose") and (not $g_verbose))
       or (($action eq "debug")   and (not $trigger_debug))
       or (($action eq "verbose") and ($g_quiet)) 
       or (($action eq "debug")   and ($g_quiet)) 
       or (($action eq "warning") and (not $g_warnings)) 
       or (($action eq "diag")    and ($g_quiet)))
    {
      return (0);
    }

#-------------------------------------------------------------------------------
# In diag mode, just print the input line and nothing else.
#-------------------------------------------------------------------------------
  if ((
          $action eq "debug") 
      or ($action eq "abort")
      or ($action eq "warning") 
      or ($action eq "assertion") 
      or ($action eq "error"))
    {
#-------------------------------------------------------------------------------
# Construct the string to be printed.  Include an identifier and the name of 
# the function. 
#-------------------------------------------------------------------------------
      if ($strlen > $string_limit)
        {
          $truncated_name  = substr ($caller_name, 0, $string_limit);
          $fixed_size_name = sprintf ("%-"."$string_limit"."s", $truncated_name);
          print "Warning in $subr_name - the name of the caller is: $caller_name\n";
          print "Warning in $subr_name - the string length is $strlen and exceeds $string_limit\n";
        }
      else
        {
          $fixed_size_name = sprintf ("%-"."$string_limit"."s", $caller_name);
        }

      if (($action eq "error") or ($action eq "abort")) 
#-------------------------------------------------------------------------------
# Enforce that the message starts with a lowercase symbol.  Since these are
# user errors, the name of the routine is not shown.  The same for "abort".
# If you want to display the routine name too, use an assertion.
#-------------------------------------------------------------------------------
        {
          printf ("%-9s %s\n", $identifier, lcfirst ($comment_line));
        }
      elsif ($action eq "assertion")
#-------------------------------------------------------------------------------
# Enforce that the message starts with a lowercase symbol.
#-------------------------------------------------------------------------------
        {
          printf ("%-9s %-30s - %s\n", $identifier, $fixed_size_name, $comment_line);
        }
      elsif (($action eq "debug") and ($trigger_debug))
#-------------------------------------------------------------------------------
# Debug messages are printed "as is".  Avoids issues when searching for them ;-)
#-------------------------------------------------------------------------------
        {
          printf ("%-9s %-30s - %s\n", $identifier, $fixed_size_name, $comment_line);
        }
      else
#-------------------------------------------------------------------------------
# Enforce that the message starts with a lowercase symbol.
#-------------------------------------------------------------------------------
        {
          printf ("%-9s %-30s - %s\n", $identifier, $fixed_size_name, lcfirst ($comment_line));
        }
    }
  elsif ($action eq "verbose")
#-------------------------------------------------------------------------------
# The first character in the verbose message is capatilized.
#-------------------------------------------------------------------------------
    {
      printf ("%s\n", ucfirst ($comment_line));
    }
  elsif ($action eq "diag")
#-------------------------------------------------------------------------------
# The diag messages are meant to be diagnostics.  Only the comment line is
# printed.
#-------------------------------------------------------------------------------
    {
      printf ("%s\n", $comment_line);
      return (0);
    }

#-------------------------------------------------------------------------------
# Terminate execution in case the identifier is "abort".
#-------------------------------------------------------------------------------
  if (($action eq "abort") or ($action eq "assertion"))
    {
##      print "ABORT temporarily disabled for testing purposes\n";
      exit (-1);
    }
  else
    {
      return (0);
    }
   
} #-- End of subroutine gp_message

#------------------------------------------------------------------------------
# Dynamically load the modules needed.  Returns a list with the modules that
# could not be loaded.
#------------------------------------------------------------------------------
sub handle_module_availability
{
  my $subr_name = get_my_name ();

  gp_message ("verbose", $subr_name, "Handling module requirements");

#------------------------------------------------------------------------------
# This is clunky at best, but there is a chicken egg problem here.  For the
# man page to be generated, the --help and --version options need to work,
# but this part of the code only works if the "stat" function is available.
# The "feature qw (state)" is required for the code to compile.
#
# TBD: Consider using global variables and to decouple parts of the option
# handling.
#;
##  my @modules_used = ("feature", 
##                     "File::stat", 
#------------------------------------------------------------------------------
  my @modules_used = (
                      "List::Util", 
                      "Cwd", 
                      "File::Basename", 
                      "File::stat", 
                      "POSIX", 
                      "bigint", 
                      "bignum");

  my @missing_modules = ();
  my $cmd;
  my $result;
  
#------------------------------------------------------------------------------
# This loop checks for the availability of the modules and if so, imports 
# the module.
#
# The names of missing modules, if any, are stored and printed in the error
# handling section below.
#------------------------------------------------------------------------------
  for my $i (0 .. $#modules_used)
    {
      my $m = $modules_used[$i];
      if (eval "require $m;")
        {
          if ($m eq "feature")
            {
              $cmd = $m . "->import ( qw (state))";
            }
          elsif ($m eq "List::Util")
            {
              $cmd = $m . "->import ( qw (min max))";
            }
          elsif ($m eq "bigint")
            {
              $cmd = $m . "->import ( qw (hex))";
            }
          else
            {
              $cmd = $m . "->import";
            }
          $cmd .= ";";
          $result = eval ("$cmd");
          gp_message ("debugM", $subr_name, "cmd = $cmd");
        }
      else
        {
          push (@missing_modules, $m);
        }
    }

#------------------------------------------------------------------------------
# Count the number of missing modules.  It is upon the caller to decide what 
# to do in case of errors.  Currently, execution is aborted.
#------------------------------------------------------------------------------
  my $errors = scalar (@missing_modules);

  return (\$errors, \@missing_modules);

} #-- End of subroutine handle_module_availability

#------------------------------------------------------------------------------
# Generate the HTML with the experiment summary.
#------------------------------------------------------------------------------
sub html_generate_exp_summary
{
  my $subr_name = get_my_name ();

  my ($outputdir_ref, $experiment_data_ref) = @_;

  my $outputdir       = ${ $outputdir_ref };
  my @experiment_data = @{ $experiment_data_ref };
  my $file_title;
  my $outfile;
  my $page_title;
  my $size_text; 
  my $position_text;
  my $html_header;
  my $html_home;
  my $html_title_header;
  my $html_acknowledgement;
  my $html_end;
  my @html_exp_table_data = ();
  my $html_exp_table_data_ref;
  my @table_execution_stats = ();
  my $table_execution_stats_ref;

  gp_message ("debug", $subr_name, "outputdir = $outputdir");
  $outputdir = append_forward_slash ($outputdir);
  gp_message ("debug", $subr_name, "outputdir = $outputdir");

  $file_title = "Experiment information";
  $page_title = "Experiment Information";
  $size_text = "h2";
  $position_text = "center";
  $html_header = ${ create_html_header (\$file_title) };
  $html_home   = ${ generate_home_link ("right") };

  $html_title_header = ${ generate_a_header (\$page_title, \$size_text, \$position_text) };

  $outfile = $outputdir . $g_html_base_file_name{"experiment_info"} . ".html";
  open (EXP_INFO, ">", $outfile)
    or die ("unable to open $outfile for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile for writing");

  print EXP_INFO $html_header;
  print EXP_INFO $html_home;
  print EXP_INFO $html_title_header;

  ($html_exp_table_data_ref, $table_execution_stats_ref) = html_generate_table_data ($experiment_data_ref);

  @html_exp_table_data   = @{ $html_exp_table_data_ref };
  @table_execution_stats = @{ $table_execution_stats_ref };

  print EXP_INFO "$_" for @html_exp_table_data;
;
##  print EXP_INFO "<pre>\n";
##  print EXP_INFO "$_\n" for @html_caller_callee;
##  print EXP_INFO "</pre>\n";

#-------------------------------------------------------------------------------
# Get the acknowledgement, return to main link, and final html statements.
#-------------------------------------------------------------------------------
  $html_home            = ${ generate_home_link ("left") };
  $html_acknowledgement = ${ create_html_credits () };
  $html_end             = ${ terminate_html_document () };

  print EXP_INFO $html_home;
  print EXP_INFO "<br>\n";
  print EXP_INFO $html_acknowledgement;
  print EXP_INFO $html_end;

  close (EXP_INFO);

  return (\@table_execution_stats);

} #-- End of subroutine html_generate_exp_summary

#-------------------------------------------------------------------------------
# Generate the entries for the tables with the experiment info.
#-------------------------------------------------------------------------------
sub html_generate_table_data
{
  my $subr_name = get_my_name ();

  my ($experiment_data_ref) = @_;

  my @experiment_data     = ();
  my @html_exp_table_data = ();
  my $html_line;
##  my $html_header_line;
  my $entry_name; 
  my $key;
  my $size_text; 
  my $position_text;
  my $title_table_1; 
  my $title_table_2; 
  my $title_table_3; 
  my $title_table_summary; 
  my $html_table_title; 

  my @experiment_table_1_def = ();
  my @experiment_table_2_def = ();
  my @experiment_table_3_def = ();
  my @exp_table_summary_def = ();
  my @experiment_table_1 = ();
  my @experiment_table_2 = ();
  my @experiment_table_3 = ();
  my @exp_table_summary = ();
  my @exp_table_selection = ();

  @experiment_data = @{ $experiment_data_ref };

  for my $i (sort keys @experiment_data)
    {
      for my $fields (sort keys %{ $experiment_data[$i] })
        {
          gp_message ("debugXL", $subr_name, "$i => experiment_data[$i]{$fields} = $experiment_data[$i]{$fields}");
        }
    }

  $title_table_1 = "Target System Configuration";
  $title_table_2 = "Experiment Statistics";
  $title_table_3 = "Run Time Statistics";
  $title_table_summary = "Main Statistics";

  $size_text     = "h3"; 
  $position_text = "left";

  push @experiment_table_1_def, { name => "Experiment name" , key => "exp_name_short"}; 
  push @experiment_table_1_def, { name => "Hostname"        , key => "hostname"}; 
  push @experiment_table_1_def, { name => "Operating system", key => "OS"}; 
  push @experiment_table_1_def, { name => "Architecture",     key => "architecture"}; 
  push @experiment_table_1_def, { name => "Page size",        key => "page_size"}; 
  
  push @experiment_table_2_def, { name => "Target command"          , key => "target_cmd"}; 
  push @experiment_table_2_def, { name => "Date command executed"   , key => "start_date"}; 
  push @experiment_table_2_def, { name => "Data collection duration", key => "data_collection_duration"}; 
  push @experiment_table_2_def, { name => "End time of the experiment", key => "end_experiment"}; 

  push @experiment_table_3_def, { name => "User CPU time (seconds)", key => "user_cpu_time"}; 
##  push @experiment_table_3_def, { name => "User CPU time (percentage)", key => "user_cpu_percentage"}; 
  push @experiment_table_3_def, { name => "System CPU time (seconds)", key => "system_cpu_time"}; 
##  push @experiment_table_3_def, { name => "System CPU time (percentage)", key => "system_cpu_percentage"}; 
  push @experiment_table_3_def, { name => "Sleep time (seconds)", key => "sleep_time"}; 
##  push @experiment_table_3_def, { name => "Sleep time (percentage)", key => "sleep_percentage"}; 

  push @exp_table_summary_def, { name => "Experiment name" , key => "exp_name_short"}; 
  push @exp_table_summary_def, { name => "Hostname"        , key => "hostname"}; 
  push @exp_table_summary_def, { name => "User CPU time (seconds)", key => "user_cpu_time"}; 
  push @exp_table_summary_def, { name => "System CPU time (seconds)", key => "system_cpu_time"}; 
  push @exp_table_summary_def, { name => "Sleep time (seconds)", key => "sleep_time"}; 

  $html_table_title = ${ generate_a_header (\$title_table_1, \$size_text, \$position_text) };

  push (@html_exp_table_data, $html_table_title);

  @experiment_table_1 = @{ create_table (\@experiment_data, \@experiment_table_1_def) };

  push (@html_exp_table_data, @experiment_table_1);

  $html_table_title = ${ generate_a_header (\$title_table_2, \$size_text, \$position_text) };

  push (@html_exp_table_data, $html_table_title);

  @experiment_table_2 = @{ create_table (\@experiment_data, \@experiment_table_2_def) };

  push (@html_exp_table_data, @experiment_table_2);

  $html_table_title = ${ generate_a_header (\$title_table_3, \$size_text, \$position_text) };

  push (@html_exp_table_data, $html_table_title);

  @experiment_table_3 = @{ create_table (\@experiment_data, \@experiment_table_3_def) };

  push (@html_exp_table_data, @experiment_table_3);

  $html_table_title = ${ generate_a_header (\$title_table_summary, \$size_text, \$position_text) };

  push (@exp_table_summary, $html_table_title);

  @exp_table_selection = @{ create_table (\@experiment_data, \@exp_table_summary_def) };

  push (@exp_table_summary, @exp_table_selection);

  return (\@html_exp_table_data, \@exp_table_summary);

} #-- End of subroutine html_generate_table_data

#------------------------------------------------------------------------------
# Generate the HTML text to print in case a file is empty.
#------------------------------------------------------------------------------
sub html_text_empty_file
{
  my $subr_name = get_my_name ();

  my ($comment_ref, $error_file_ref) = @_;

  my $comment; 
  my $error_file; 
  my $error_message; 
  my $file_title; 
  my $html_end;
  my $html_header;
  my $html_home; 

  my @html_empty_file = ();

  $comment     = ${ $comment_ref };
  $error_file  = ${ $error_file_ref };

  $file_title  = "File is empty";
  $html_header = ${ create_html_header (\$file_title) };
  $html_end    = ${ terminate_html_document () };
  $html_home   = ${ generate_home_link ("left") };

  push (@html_empty_file, $html_header);

  $error_message = "<b>" . $comment . "</b>";
  $error_message = set_background_color_string ($error_message, $g_html_color_scheme{"error_message"});
  push (@html_empty_file, $error_message);

  if (not is_file_empty ($error_file))
    {
      $error_message = "<p><em>Check file $error_file for more information</em></p>";
    }
  push (@html_empty_file, $error_message);
  push (@html_empty_file, $html_home);
  push (@html_empty_file, "<br>");
  push (@html_empty_file, $g_html_credits_line);
  push (@html_empty_file, $html_end);

  return (\@html_empty_file);

} #-- End of subroutine html_text_empty_file

#------------------------------------------------------------------------------
# This subroutine checks if a file is empty and returns $TRUE or $FALSE.
#------------------------------------------------------------------------------
sub is_file_empty
{
  my $subr_name = get_my_name ();

  my ($filename) = @_;

  my $size;
  my $file_stat;
  my $is_empty;

  chomp ($filename);

  if (not -e $filename)
    {
#------------------------------------------------------------------------------
# The return value is used in the caller.  This is why we return the empty
# string in case the file does not exist.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name, "filename = $filename not found");
      $is_empty = $TRUE;
    }
  else
    {
      $file_stat = stat ($filename);
      $size      = $file_stat->size;
      $is_empty  = ($size == 0) ? $TRUE : $FALSE;
    }

  gp_message ("debug", $subr_name, "filename = $filename size = $size is_empty = $is_empty");

  return ($is_empty);

} #-- End of subroutine is_file_empty

#------------------------------------------------------------------------------
# Check if a file is executable and return $TRUE or $FALSE.
#------------------------------------------------------------------------------
sub is_file_executable
{
  my $subr_name = get_my_name ();

  my ($filename) = @_;

  my $file_permissions;
  my $index_offset;
  my $is_executable;
  my $mode;
  my $number_of_bytes; 
  my @permission_settings = ();
  my %permission_values = ();

  chomp ($filename);

  gp_message ("debug", $subr_name, "check if filename = $filename is executable");

  if (not -e $filename)
    {
#------------------------------------------------------------------------------
# The return value is used in the caller.  This is why we return the empty
# string in case the file does not exist.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name, "filename = $filename not found");
      $is_executable = $FALSE;
    }
  else
    {
      $mode = stat ($filename)->mode;

      gp_message ("debugXL", $subr_name, "mode = $mode");
#------------------------------------------------------------------------------
# Get username.  We currently do not do anything with this though and the
# code is commented out.
#
#      my $my_name = getlogin () || getpwuid($<) || "Kilroy";;
#      gp_message ("debug", $subr_name, "my_name = $my_name");
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Convert file permissions to octal, split the individual numbers and store
# the values for the respective users.
#------------------------------------------------------------------------------
      $file_permissions = sprintf("%o", $mode & 07777);

      @permission_settings = split (//, $file_permissions);

      $number_of_bytes = scalar (@permission_settings);

      gp_message ("debugXL", $subr_name, "file_permissions = $file_permissions");
      gp_message ("debugXL", $subr_name, "permission_settings = @permission_settings");
      gp_message ("debugXL", $subr_name, "number_of_settings = $number_of_bytes");

      if ($number_of_bytes == 4)
        {
          $index_offset = 1;
        }
      elsif ($number_of_bytes == 3)
        {
          $index_offset = 0;
        }
      else
        {
          my $msg = "unexpected number of $number_of_bytes bytes " .
                    "in permission settings: @permission_settings";
          gp_message ("assertion", $subr_name, $msg);
        }

      $permission_values{user}  = $permission_settings[$index_offset++];
      $permission_values{group} = $permission_settings[$index_offset++];
      $permission_values{other} = $permission_settings[$index_offset];

#------------------------------------------------------------------------------
# The executable bit should be set for user, group and other.  If this fails
# we mark the file as not executable.  Note that this is gprofng specific.
#------------------------------------------------------------------------------
      $is_executable = $TRUE;
      for my $k (keys %permission_values)
        {
          my $msg = "permission_values{" . $k . "} = " .
                    $permission_values{$k};
          gp_message ("debugXL", $subr_name, $msg);
    
          if ($permission_values{$k} % 2 == 0)
            {
              $is_executable = $FALSE;
              last;
            }
        }
    }

  gp_message ("debug", $subr_name, "is_executable = $is_executable");

  return ($is_executable);

} #-- End of subroutine is_file_executable

#-------------------------------------------------------------------------------
# TBD.
#-------------------------------------------------------------------------------
sub name_regex
{
  my $subr_name = get_my_name ();

  my ($metric_description_ref, $metrics, $field, $file) = @_;

  my %metric_description = %{ $metric_description_ref };

  my @splitted_metrics;
  my $splitted_metrics;
  my $m;
  my $mf;
  my $nf;
  my $re;
  my $Xre;
  my $noPCfile;
  my @reported_metrics;
  my $reported_metrics;
  my $hdr_regex;
  my $hdr_href_regex;
  my $hdr_src_regex;
  my $new_metrics;
  my $pre;
  my $post;
  my $rat;
  my @moo = ();

  my $gp_metrics_file;
  my $gp_metrics_dir;
  my $suffix_not_used;

  my $is_calls    = $FALSE;
  my $is_calltree = $FALSE;

  gp_message ("debugXL", $subr_name,"1:metrics->$metrics<- field->$field<- file->$file<-");

#-------------------------------------------------------------------------------
# According to https://perldoc.perl.org/File::Basename, both dirname and 
# basename are not reliable and fileparse () is recommended instead.
#
# Note that $gp_metrics_dir has a trailing "/".
#-------------------------------------------------------------------------------
  ($gp_metrics_file, $gp_metrics_dir, $suffix_not_used) = fileparse ($file, ".sort.func-PC");

  gp_message ("debugXL", $subr_name, "gp_metrics_dir = $gp_metrics_dir gp_metrics_file = $gp_metrics_file");
  gp_message ("debugXL", $subr_name, "suffix_not_used = $suffix_not_used");

  if ($gp_metrics_file eq "calls")
    {
      $is_calls = $TRUE;
    }
  if ($gp_metrics_file eq "calltree")
    {
      $is_calltree = $TRUE;
    }

  $gp_metrics_file = "gp-metrics-" . $gp_metrics_file . "-PC";
  $gp_metrics_file = $gp_metrics_dir . $gp_metrics_file;

  gp_message ("debugXL", $subr_name, "gp_metrics_file is $gp_metrics_file");

  open (GP_METRICS, "<", $gp_metrics_file)
    or die ("$subr_name - unable to open gp_metrics file $gp_metrics_file for reading - '$!'");
  gp_message ("debug", $subr_name, "opened file $gp_metrics_file for reading");

  $new_metrics = $metrics;

  while (<GP_METRICS>)
    {
      $rat = $_;
      chomp ($rat);
      gp_message ("debugXL", $subr_name, "rat = $rat - new_metrics = $new_metrics");
#-------------------------------------------------------------------------------
# Capture the string after "Current metrics:" and if it ends with ":name",
# remove it.
#-------------------------------------------------------------------------------
      if ($rat =~ /^\s*Current metrics:\s*(.*)$/)
        {
          $new_metrics = $1;
          if ($new_metrics =~ /^(.*):name$/)
            {
              $new_metrics = $1;
            }
          last;
        }
    }
  close (GP_METRICS);

  if ($is_calls or $is_calltree)
    {
#-------------------------------------------------------------------------------
# Remove any inclusive metrics from the list.
#-------------------------------------------------------------------------------
      while ($new_metrics =~ /(.*)(i\.[^:]+)(.*)$/)
        {
          $pre  = $1;
          $post = $3;
          gp_message ("debugXL", $subr_name, "1b: new_metrics = $new_metrics pre = $pre post = $post");
          if (substr ($post,0,1) eq ":")
            {
              $post = substr ($post,1);
            }
          $new_metrics = $pre.$post;
        }
    }

  $metrics = $new_metrics;

  gp_message ("debugXL", $subr_name, "2:metrics->$metrics<- field->$field<- file->$file<-");

#-------------------------------------------------------------------------------
# Find the line starting with "address:" and strip this part away.
#-------------------------------------------------------------------------------
  if ($metrics =~ /^address:(.*)/)
    {
      $reported_metrics = $1;
#-------------------------------------------------------------------------------
# Focus on the filename ending with "-PC".  When found, strip this part away.
#-------------------------------------------------------------------------------
      if ($file =~ /^(.*)-PC$/)
        {
          $noPCfile = $1;
          if ($noPCfile =~ /^(.*)functions.sort.func$/)
            {
              $noPCfile = $1."functions.func";
            }
          push (@moo, "$reported_metrics\n");
        }
    }

#-------------------------------------------------------------------------------
# Split the list into an array with the individual metrics.
#
# TBD: This should be done only once!
#-------------------------------------------------------------------------------
  @reported_metrics = split (":", $reported_metrics);
  for my $i (@reported_metrics)
    {
      gp_message ("debugXL", $subr_name, "reported_metrics = $i");
    }

  $hdr_regex      = "^\\s*";
  $hdr_href_regex = "^\\s*";
  $hdr_src_regex  = "^(\\s+|<i>\\s+)";

  for my $m (@reported_metrics)
    {

      my $description = ${ retrieve_metric_description (\$m, \%metric_description) };
      gp_message ("debugXL", $subr_name, "m = $m description = $description");
      if (substr ($m,0,1) eq "e")
        {
          push (@moo,"$m:$description\n");
          $hdr_regex .= "(Excl\\.\.*)";
          $hdr_href_regex .= "(<a.*>)(Excl\\.)(<\/a>)([^<]+)";
          $hdr_src_regex .= "(Excl\\.\.*)";
          next;
        }
      if (substr ($m,0,1) eq "i")
        {
          push (@moo,"$m:$description\n");
          $hdr_regex .= "(Incl\\.\.*)";
          $hdr_href_regex .= "(<a.*>)(Incl\\.)(<\/a>)([^<]+)";
          $hdr_src_regex .= "(Incl\\.\.*)";
          next;
        }
      if (substr ($m,0,1) eq "a")
        {
          my $a;
          my $am;
          $a = $m; 
          $a =~ s/^a/e/; 
          $am = ${ retrieve_metric_description (\$a, \%metric_description) };
          $am =~ s/Exclusive/Attributed/;
          push (@moo,"$m:$am\n");
          $hdr_regex .= "(Attr\\.\.*)";
          $hdr_href_regex .= "(<a.*>)(Attr\\.)(<\/a>)([^<]+)";
          $hdr_src_regex .= "(Attr\\.\.*)";next;
        }
    }

  $hdr_regex      .= "(Name\.*)";
  $hdr_href_regex .= "(Name\.*)";

  @splitted_metrics = split (":","$metrics");
  $nf               = scalar (@splitted_metrics);
  gp_message ("debug", $subr_name,"number of fields in $metrics -> $nf");

  open (ZMETRICS, ">", "$noPCfile.metrics")
    or die ("Not able to open file $noPCfile.metrics for writing - '$!'");
  gp_message ("debug", $subr_name, "$noPCfile - opened file $noPCfile.metrics for writing");

  print ZMETRICS @moo;
  close (ZMETRICS);

  gp_message ("debug", $subr_name, "wrote file $noPCfile.metrics");

  open (XREGEXP, ">", "$noPCfile.c.regex")
    or die ("Not able to open file $noPCfile.c.regex for writing - '$!'");
  gp_message ("debug", $subr_name, "$noPCfile - opened file $noPCfile.c.regex for writing");

  print XREGEXP "\# Number of metric fields\n";
  print XREGEXP "$nf\n";
  print XREGEXP "\# Header regex\n";
  print XREGEXP "$hdr_regex\n";
  print XREGEXP "\# href Header regex\n";
  print XREGEXP "$hdr_href_regex\n";
  print XREGEXP "\# src Header regex\n";
  print XREGEXP "$hdr_src_regex\n";

  $mf = 1;
#---------------------------------------------------------------------------
# Find the index of "field" in the metric list, plus one.
#---------------------------------------------------------------------------
  if ( ($field eq "functions") or ($field eq "calls") or ($field eq "calltree"))
    {
      $mf = $nf + 1;
    } 
  else
    {
      for my $candidate_metric (@splitted_metrics)
        {
          gp_message ("debugXL", $subr_name, "field = $field candidate_metric = $candidate_metric and mf = $mf");
          if ($candidate_metric eq $field)
            {
              last;
            }
          $mf++;
        }
    }
  gp_message ("debugXL", $subr_name, "Final value mf = $mf");

  if ($mf == 1)
    {
      $re = "^\\s*(\\S+)"; # metric value
    } 
  else 
    {
      $re = "^\\s*\\S+";
    }
  $Xre = "^\\s*(\\S+)";

  $m = 2;
  while (--$nf)
    {
      if ($nf)
        {
          if ($m == $mf)
            {
              $re .= "\\s+(\\S+)"; # metric value
            } 
          else 
            {
              $re .= "\\s+\\S+";
            }
          if ($nf != 1)
            {
              $Xre .= "\\s+(\\S+)";
            }
          $m++;
        }
    }

  if ($field eq "calltree")
    {
      $re .= "\\s+.*\\+-(.*)"; # name
      $Xre .= "\\s+.*\\+-(.*)\$"; # name (Right?)
    } 
  else 
    {
      $re .= "\\s+(.*)"; # name
      $Xre .= "\\s+(.*)\$"; # name
    }

  print XREGEXP "\# Metrics and Name regex\n";
  print XREGEXP "$Xre\n";
  close (XREGEXP);

  gp_message ("debug", $subr_name, "wrote file $noPCfile.c.regex");
  gp_message ("debugXL", $subr_name, "on return Xre = $Xre");
  gp_message ("debugXL", $subr_name, "on return re  = $re");

  return ($re);

} #-- End of subroutine name_regex

#-------------------------------------------------------------------------------
# TBD
#-------------------------------------------------------------------------------
sub nosrc
{
  my $subr_name = get_my_name ();

  my ($input_string) = @_;

  my $directory_name = append_forward_slash ($input_string);
  my $LANG           = $g_locale_settings{"LANG"};
  my $result_file    = $directory_name."no_source.html";

  gp_message ("debug", $subr_name, "result_file = $result_file");

  open (NS, ">", $result_file)
    or die ("$subr_name: cannot open file $result_file for writing - '$!'");

  print NS "<!doctype html public \"-//w3c//dtd html 3.2//en\">\n<html lang=\"$LANG\">\n<head>\n".
           "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n" .
           "<title>No source</title></head><body lang=\"$LANG\" bgcolor=".$g_html_color_scheme{"background_color_page"}."><pre>\n";
  print NS "<a name=\"line1\"></a><font color=#C80707>"."No source was found"."</font>\n"; # red font
  print NS "</pre>\n<pre>Output generated by $version_info</pre>\n";
  print NS "</body></html>\n";

  close (NS);

  return (0);

} #-- End of subroutine nosrc

#------------------------------------------------------------------------------
# TBD.
#------------------------------------------------------------------------------
sub numerically 
{
  my $f1;
  my $f2;

  if ($a =~ /^([^\d]*)(\d+)/)
    {
      $f1 = int ($2);
      if ($b=~ /^([^\d]*)(\d+)/)
        {
          $f2 = int ($2);
          $f1 == $f2 ? 0 : ($f1 < $f2 ? -1 : +1);
        }
    } 
  else 
    {
      return ($a <=> $b);
    }
} #-- End of subroutine numerically

#------------------------------------------------------------------------------
# Parse the user options. Also perform a basic check.  More checks and also
# some specific to the option will be performed after this subroutine.
#------------------------------------------------------------------------------
sub parse_and_check_user_options
{
  my $subr_name = get_my_name ();

  my ($no_of_args_ref, $option_list_ref) = @_;

  my $no_of_args  = ${ $no_of_args_ref };
  my @option_list = @{ $option_list_ref };

  my @exp_dir_list;

  my $arg;
  my $calltree_value; 
  my $debug_value; 
  my $default_metrics_value; 
  my $func_limit_value; 
  my $found_exp_dir = $FALSE;
  my $ignore_metrics_value; 
  my $ignore_value;
  my $message;
  my $outputdir_value;
  my $quiet_value; 
  my $hp_value;
  my $valid;
  my $verbose_value; 

  $no_of_args++;

  gp_message ("debug", $subr_name, "no_of_args  = $no_of_args");
  gp_message ("debug", $subr_name, "option_list = @option_list");

  my $option_errors = 0;

  while (defined ($arg = shift @ARGV))
    {
      gp_message ("debug", $subr_name, "parsing options arg = $arg");
      gp_message ("debug", $subr_name, "parsing options \@ARGV = @ARGV");

#------------------------------------------------------------------------------
# The gprofng driver adds this option.  We need to get rid of it.
#------------------------------------------------------------------------------
      next if ($arg eq "--whoami=gprofng display html");

#------------------------------------------------------------------------------
# Parse the input options and check for the values to be valid.
#
# Valid values are stored in the main option table.
#
# TBD: The early check handles some of these already and the duplicates
# can be removed.  Be aware of some global settings though.
#------------------------------------------------------------------------------
      if ($arg eq "--version")
        {
          print_version_info (); 
          exit (0);
        }
      elsif ($arg eq "--help")
        {
          $ignore_value = print_help_info ();
          exit (0);
        }
      elsif (($arg eq "-v") or ($arg eq "--verbose"))
        {
          $verbose_value = shift @ARGV; 
          $valid = check_user_option ("verbose", $verbose_value);
          if (not $valid)
            {
              $option_errors++;
            }
          else
            {
              $g_verbose = $g_user_settings{"verbose"}{"current_value"} eq "on" ? $TRUE : $FALSE;
            }
          next;
        }
      elsif (($arg eq "-w") or ($arg eq "--warnings"))
        {
          my $warnings_value = shift @ARGV; 
          $valid = check_user_option ("warnings", $warnings_value);
          if (not $valid)
            {
              $option_errors++;
            }
          else
            {
              $g_warnings = $g_user_settings{"warnings"}{"current_value"} eq "on" ? $TRUE : $FALSE;
            }
          next;
        }
      elsif (($arg eq "-d") or ($arg eq "--debug"))
        {
          $debug_value = shift @ARGV;
          $valid = check_user_option ("debug", $debug_value);
          if (not $valid)
            {
              $option_errors++;
            }
          else
            {
#------------------------------------------------------------------------------
# This function internally converts the value to uppercase. 
#------------------------------------------------------------------------------
              my $ignore_value = set_debug_size (\$debug_value);
            }
          next;
        }
      elsif (($arg eq "-q") or ($arg eq "--quiet"))
        {
          $quiet_value = shift @ARGV; 
          $valid = check_user_option ("quiet", $quiet_value);

          if (not $valid)
            {
              $option_errors++;
            }
          else
            {
              $g_quiet = $g_user_settings{"quiet"}{"current_value"} eq "on" ? $TRUE : $FALSE;
            }
          next;
        }
      elsif (($arg eq "-o") or ($arg eq "--output"))
        {
          $outputdir_value = shift @ARGV; 
          $valid = check_user_option ("output", $outputdir_value);

          if (not $valid)
            {
              $option_errors++;
            }

          next;
        }
      elsif (($arg eq "-O") or ($arg eq "--overwrite"))
        {
          $outputdir_value = shift @ARGV; 
          $valid = check_user_option ("overwrite", $outputdir_value);

          if (not $valid)
            {
              $option_errors++;
            }

          next; 
        }
      elsif (($arg eq "-hp") or ($arg eq "--highlight-percentage"))
        { 
          $hp_value     = shift @ARGV; 
          $valid = check_user_option ("highlight_percentage", $hp_value);

          if (not $valid)
            {
              $option_errors++;
            }

          next;
        }
# Temporarily disabled       elsif (($arg eq "-fl") or ($arg eq "--func-limit"))
# Temporarily disabled         {
# Temporarily disabled           $func_limit_value = shift @ARGV; 
# Temporarily disabled           $valid = check_user_option ("func_limit", $func_limit_value);
# Temporarily disabled 
# Temporarily disabled           if (not $valid)
# Temporarily disabled             {
# Temporarily disabled               $option_errors++;
# Temporarily disabled             }
# Temporarily disabled 
# Temporarily disabled           next;
# Temporarily disabled         }
# Temporarily disabled       elsif (($arg eq "-ct") or ($arg eq "--calltree"))
# Temporarily disabled         {
# Temporarily disabled           $calltree_value = shift @ARGV;
# Temporarily disabled           $valid = check_user_option ("calltree", $calltree_value);
# Temporarily disabled 
# Temporarily disabled           if (not $valid)
# Temporarily disabled             {
# Temporarily disabled               $option_errors++;
# Temporarily disabled             }
# Temporarily disabled 
# Temporarily disabled           next; 
# Temporarily disabled         }
# Temporarily disabled       elsif (($arg eq "-tp") or ($arg eq "--threshold-percentage"))
# Temporarily disabled         { 
# Temporarily disabled           $tp_value     = shift @ARGV; 
# Temporarily disabled           $valid = check_user_option ("threshold_percentage", $tp_value);
# Temporarily disabled 
# Temporarily disabled           if (not $valid)
# Temporarily disabled             {
# Temporarily disabled               $option_errors++;
# Temporarily disabled             }
# Temporarily disabled 
# Temporarily disabled           next;
# Temporarily disabled         }
# Temporarily disabled       elsif (($arg eq "-dm") or ($arg eq "--default-metrics"))
# Temporarily disabled         { 
# Temporarily disabled           $default_metrics_value = shift @ARGV;
# Temporarily disabled           $valid = check_user_option ("default_metrics", $default_metrics_value);
# Temporarily disabled 
# Temporarily disabled           if (not $valid)
# Temporarily disabled             {
# Temporarily disabled               $option_errors++;
# Temporarily disabled             }
# Temporarily disabled 
# Temporarily disabled           next;
# Temporarily disabled         }
# Temporarily disabled       elsif (($arg eq "-im") or ($arg eq "--ignore-metrics"))
# Temporarily disabled         { 
# Temporarily disabled           $ignore_metrics_value = shift @ARGV; 
# Temporarily disabled           $valid = check_user_option ("ignore_metrics", $ignore_metrics_value);
# Temporarily disabled 
# Temporarily disabled           if (not $valid)
# Temporarily disabled             {
# Temporarily disabled               $option_errors++;
# Temporarily disabled             }
# Temporarily disabled 
# Temporarily disabled           next;
# Temporarily disabled         }
      else
        {
  
#------------------------------------------------------------------------------
# When we get to this part of the code it means that the current command line 
# argument is not a known option.
#
# We check if it is the name of an experiment directory and if so, add it to 
# the list with directories to use.
#
# If not, print an error message and increment the error variable because this
# is clearly something that is not right.
#-------------------------------------------------------------------------------

          if ($arg =~ /^\-.*/)
            {
#-------------------------------------------------------------------------------
# It is an option, but not a supported one.  Print a message and increment
# the error count.
#-------------------------------------------------------------------------------
              $message = "option $arg is not a known option";
              push (@g_user_input_errors, $message);

              $option_errors++;
            }
          else
            {
#-------------------------------------------------------------------------------
# Other than options, the input has to consist of at least one directory name.  
# First remove any trailing slashes (/) and then check if the name is valid.
#-------------------------------------------------------------------------------
              $arg =~ s/\/*\/$//;
              if ($arg =~ /.+\.er$/)
                {
#-------------------------------------------------------------------------------
# It is the name of an experiment directory and is added to the list.
#-------------------------------------------------------------------------------
                  $found_exp_dir = $TRUE;
                  push (@exp_dir_list, $arg);
                }
              else
                {
#-------------------------------------------------------------------------------
# It is not a valid experiment directory name. Print a message and exit.
#-------------------------------------------------------------------------------
                  $message = "not a valid experiment directory name: $arg";
                  push (@g_user_input_errors, $message);

                  $option_errors++;
                }
            }

        } #-- End of last else

    } #-- End of while-loop

#-------------------------------------------------------------------------------
# Check if the name of the experiment directories is valid.  Note that later
# we check for these directories to exist.
#-------------------------------------------------------------------------------
  if (not $found_exp_dir) 
    {
      $message = "experiment directory name(s) are either not valid, or missing";
      push (@g_user_input_errors, $message);

      $option_errors++;
    }

#------------------------------------------------------------------------------
# Check for fatal errors to have occurred. If so, stop execution.  Otherwise,
# confirm the verbose setting.
#------------------------------------------------------------------------------
  if ($option_errors > 0)
    {
      gp_message ("debug", $subr_name, "a total of $option_errors input errors have been found");
    }
  else
    {
      gp_message ("debug", $subr_name, "no errors in the options found");
    }

  return ($option_errors, $found_exp_dir, \@exp_dir_list);

} #-- End of subroutine parse_and_check_user_options

#------------------------------------------------------------------------------
# Parse the generated .dis files
#------------------------------------------------------------------------------
sub parse_dis_files
{
  my $subr_name = get_my_name ();

  my ($number_of_metrics_ref, $function_info_ref, 
      $function_address_and_index_ref, $input_string_ref, 
      $addressobj_index_ref) = @_;

#------------------------------------------------------------------------------
# Note that $function_address_and_index_ref is not used, but we need to pass 
# in the address into generate_dis_html.
#------------------------------------------------------------------------------
  my $number_of_metrics = ${ $number_of_metrics_ref };
  my @function_info     = @{ $function_info_ref };
  my $input_string      = ${ $input_string_ref };
  my %addressobj_index  = %{ $addressobj_index_ref };

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $dis_filename_id_regex = 'file\.([0-9]+)\.dis';

  my $filename;
  my $outputdir = append_forward_slash ($input_string);

  my @source_line = ();
  my $source_line_ref; 

  my @metric = ();
  my $metric_ref;

  my $target_function;
 
  gp_message ("debug", $subr_name, "building disassembly files");
  gp_message ("debug", $subr_name, "outputdir = $outputdir");

  while (glob ($outputdir.'*.dis'))
    {
      gp_message ("debug", $subr_name, "processing disassembly file: $_");

      my $base_name = get_basename ($_);

      if ($base_name =~ /$dis_filename_id_regex/)
        {
          if (defined ($1))
            {
              gp_message ("debug", $subr_name, "processing disassembly file: $base_name $1");
              if (exists ($function_info[$1]{"routine"}))
                {
                  $target_function = $function_info[$1]{"routine"};
                  gp_message ("debug", $subr_name, "processing disassembly file: $base_name target_function = $target_function");
                }
              if (exists ($g_function_tag_id{$target_function}))
                {
                  gp_message ("debug", $subr_name, "target_function = $target_function ftag = $g_function_tag_id{$target_function}");
                }
              else
                {
                  my $msg = "no function tag found for $target_function";
                  gp_message ("assertion", $subr_name, $msg);
                }
            }
          else
            {
              gp_message ("debug", $subr_name, "processing disassembly file: $base_name unknown id");
            }
        }
        
      $filename = $_;
      gp_message ("verbose", $subr_name, "  Processing disassembly file $filename");
      ($source_line_ref, $metric_ref) = generate_dis_html (
                                          \$target_function,
                                          \$number_of_metrics, 
                                          $function_info_ref, 
                                          $function_address_and_index_ref, 
                                          \$outputdir, 
                                          \$filename, 
                                          \@source_line, 
                                          \@metric, 
                                          \%addressobj_index);

      @source_line = @{ $source_line_ref };
      @metric      = @{ $metric_ref };
    }
 
  return (0)

} #-- End of subroutine parse_dis_files

#------------------------------------------------------------------------------
# Parse the .src.txt files
#------------------------------------------------------------------------------
sub parse_source_files
{
  my $subr_name = get_my_name ();

  my ($number_of_metrics_ref, $function_info_ref, $outputdir_ref) = @_;

  my $number_of_metrics = ${ $number_of_metrics_ref };
  my $outputdir         = ${ $outputdir_ref };
  my $ignore_value;

  my $outputdir_with_slash = append_forward_slash ($outputdir);

  gp_message ("verbose", $subr_name, "building source files");

  while (glob ($outputdir_with_slash.'*.src.txt'))
    {
      gp_message ("verbose", $subr_name, "  Processing source file: $_");
      gp_message ("debug", $subr_name, "processing source file: $_");

      my $found_target = process_source (
                           $number_of_metrics, 
                           $function_info_ref, 
                           $outputdir_with_slash, 
                           $_);

      if (not $found_target)
        {
          gp_message ("debug", $subr_name, "target function not found");
        }
    }

} #-- End of subroutine parse_source_files

#------------------------------------------------------------------------------
# Routine to prepend \\ to selected symbols.
#------------------------------------------------------------------------------
sub prepend_backslashes
{
  my $subr_name = get_my_name ();

  my ($target_string) = @_;

  gp_message ("debug", $subr_name, "target_string on entry  = $target_string");

  $target_string =~ s/\(/\\\(/g; 
  $target_string =~ s/\)/\\\)/g; 
  $target_string =~ s/\+/\\\+/g; 
  $target_string =~ s/\[/\\\[/g; 
  $target_string =~ s/\]/\\\]/g; 
  $target_string =~ s/\*/\\\*/g; 
  $target_string =~ s/\./\\\./g; 
  $target_string =~ s/\$/\\\$/g; 
  $target_string =~ s/\^/\\\^/g; 
  $target_string =~ s/\#/\\\#/g; 

  gp_message ("debug", $subr_name, "target_string on return = $target_string");

  return ($target_string);

} #-- End of subroutine prepend_backslashes

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub preprocess_function_files
{
  my $subr_name = get_my_name ();

  my ($metric_description_ref, $script_pc_metrics, $input_string, $sort_fields_ref) = @_;

  my $outputdir   = append_forward_slash ($input_string);
  my @sort_fields = @{ $sort_fields_ref };
  
  my $error_code; 
  my $cmd_output;
  my $re;

# TBD  $outputdir .= "/";

  gp_message ("debug", $subr_name, "enter subroutine");

  my %metric_description = %{ $metric_description_ref };

  for my $m (keys %metric_description)
    {
      gp_message ("debug", $subr_name, "metric_description{$m} = $metric_description{$m}");
    }

  $re = name_regex ($metric_description_ref, $script_pc_metrics, "functions", $outputdir."functions.sort.func-PC");
  ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."functions.sort.func-PC.name-regex");
  if ($error_code != 0 )
    {
      gp_message ("abort", $subr_name, "execution terminated");
    }

  for my $field (@sort_fields)
    {
      $re = name_regex ($metric_description_ref, $script_pc_metrics, $field, $outputdir."$field.sort.func-PC");
      ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."$field.sort.func-PC.name-regex");
      if ($error_code != 0 )
        {
          gp_message ("abort", $subr_name, "execution terminated");
        }
    }

  $re = name_regex ($metric_description_ref, $script_pc_metrics, "calls", $outputdir."calls.sort.func-PC");
  ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."calls.sort.func-PC.name-regex");
  if ($error_code != 0 )
    {
      gp_message ("abort", $subr_name, "execution terminated");
    }

  if ($g_user_settings{"calltree"}{"current_value"} eq "on")
    {
      $re = name_regex ($metric_description_ref, $script_pc_metrics, "calltree", $outputdir."calltree.sort.func-PC");
      ($error_code, $cmd_output) = execute_system_cmd ("echo '$re' > $outputdir"."calltree.sort.func-PC.name-regex");
      if ($error_code != 0 )
        {
          gp_message ("abort", $subr_name, "execution terminated");
        }
    }

  return (0);

} #-- End of subroutine preprocess_function_files

#-------------------------------------------------------------------------------
# Print the help overview
#-------------------------------------------------------------------------------
sub print_help_info 
{
  print
    #-------Marker line - do not go beyond this line ------------------------------
    "Usage: $driver_cmd [OPTION(S)] EXPERIMENT(S)\n".
    "\n".
    "Process one or more experiments to generate a directory containing the\n" .
    "index.html file that may 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/disable verbose mode that shows diagnostic\n" .
    "                       messages about the processing of the data; default\n" .
    "                       is off.\n".
    #-------Marker line - do not go beyond this line ------------------------------
    " -d, --debug {on|s|m|l|xl|off}  control the printing of run time information\n" .
    "                        to assist with troubleshooting, or further\n" .
    "                        development of this tool; on gives a modest amount\n" .
    "                        of information; s, m, l, or xl gives an increasing\n" .
    "                        amount of information and off disables the printing\n" .
    "                        of debug information; note that currently on, s, m,\n" .
    "                        and l are equivalent; this is expected to change in\n" .
    "                        future updates; default is off.\n" .
    #-------Marker line - do not go beyond this line ------------------------------
    " -hp, ----highlight-percentage <value>  a percentage value in the interval\n" .
    "                                 [0,100] to select and color code source\n" .
    "                                 lines as well as instructions that are\n" .
    "                                 within this percentage of the maximum\n" .
    "                                 metric value(s); a value of zero (-hp 0)\n" .
    "                                 disables this feature; the default is 90.\n".
    #-------Marker line - do not go beyond this line ------------------------------
    " -o, --output <dir-name>  use <dir-name> to store the results in; the\n" .
    "                            default name is ./display.<n>.html with <n> the\n" .
    "                            first positive integer number not in use; an\n" .
    "                            existing directory is not overwritten.\n".
    #-------Marker line - do not go beyond this line ------------------------------
    " -O, --overwrite <dir-name>  use <dir-name> to store the results in and\n" .
    "                               overwrite any existing directory with the\n" .
    "                               same name; make sure that umask is set to the\n" .
    "                               correct access permissions.\n" .
    #-------Marker line - do not go beyond this line ------------------------------
    " -q, --quiet {on|off}  disable/allow the display of all warning, debug and\n" .
    "                         verbose messages; if set to on, the settings for\n" .
    "                         verbose, warnings and debug are ignored; default\n" .
    "                         is off.\n".
    #-------Marker line - do not go beyond this line ------------------------------
    " -w, --warnings {on|off}  enable/disable run time warning messages;\n" .
    "                            default is on.\n".
    "\n".
# Temmporarily disabled    " -fl, --func-limit <limit>  impose a limit on the number of functions processed;\n".
# Temmporarily disabled    "                             this is an integer number; set to 0 to process all\n".
# Temmporarily disabled    "                             functions; the default value is 100.\n".
# Temmporarily disabled    "\n".
# Temmporarily disabled    "  -ct, --calltree {on|off}  enable or disable an html page with a call tree linked\n".
# Temmporarily disabled    "                             from the bottom of the first page; default is off.\n".
# Temmporarily disabled    "\n".
# Temmporarily disabled    "  -tp, --threshold-percentage <percentage>  provide a percentage of metric accountability; the\n".
# Temmporarily disabled    "                                             inclusion of functions for each metric will take\n".
# Temmporarily disabled    "                                             place in sort order until the percentage has been\n".
# Temmporarily disabled    "                                             reached.\n".
# Temmporarily disabled    "\n".
# Temmporarily disabled    "  -dm, --default-metrics {on|off}  enable or disable automatic selection of metrics\n".
# Temmporarily disabled    "                                   and use a default set of metrics; the default is off.\n".
# Temmporarily disabled    "\n".
# Temmporarily disabled    "  -im, --ignore-metrics <metric-list>  ignore the metrics from <metric-list>.\n".
# Temmporarily disabled    "\n".
# Temmporarily disabled     "Environment:\n".
# Temmporarily disabled     "\n".
# Temmporarily disabled     "The options can be set in a configuration file called .gp-display-html.rc.  This\n".
# Temmporarily disabled     "file needs to be either in the current directory, or in the home directory of the user.\n".
# Temmporarily disabled     "The long name of the option without the leading dashes is supported.  For example calltree\n".
# Temmporarily disabled     "to enable or disable the call tree.  Note that some options take a value.  In case the same option\n".
# Temmporarily disabled     "occurs multiple times in this file, only the last setting encountered is preserved.\n".
# Temmporarily disabled     "\n".
    "Documentation:\n".
    "\n".
    "A getting started guide for gprofng is maintained as a Texinfo manual.\n" .
    "If the info and gprofng programs are properly installed at your site,\n" .
    "the command \"info gprofng\" 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";

    return (0);

} #-- End of subroutine print_help_info

#-------------------------------------------------------------------------------
# Print the meta data for each experiment directory.
#-------------------------------------------------------------------------------
sub print_meta_data_experiments
{
  my $subr_name = get_my_name ();

  my ($mode) = @_;

  for my $exp (sort keys %g_exp_dir_meta_data)
    {
      for my $meta (sort keys %{$g_exp_dir_meta_data{$exp}})
        {
          gp_message ($mode, $subr_name, "$exp => $meta = $g_exp_dir_meta_data{$exp}{$meta}");
        }
    }

  return (0);

} #-- End of subroutine print_meta_data_experiments

#------------------------------------------------------------------------------
# Brute force subroutine that prints the contents of a structure with function
# level information.  This version is for a top level array structure, 
# followed by a hash.
#------------------------------------------------------------------------------
sub print_metric_function_array
{
  my $subr_name = get_my_name ();

  my ($metric, $struct_type_name, $target_structure_ref) = @_;

  my @target_structure = @{$target_structure_ref}; 

  gp_message ("debugXL", $subr_name, "contents of structure ".$struct_type_name."{".$metric."}:");

  for my $fields (sort keys @target_structure)
    {
          for my $elems (sort keys % {$target_structure[$fields]})
            {
              my $msg = $struct_type_name."{$metric}[$fields]{$elems} = ";
              $msg   .= $target_structure[$fields]{$elems};
              gp_message ("debugXL", $subr_name, $msg);
            }
    }

  return (0);

} #-- End of subroutine print_metric_function_array

#------------------------------------------------------------------------------
# Brute force subroutine that prints the contents of a structure with function
# level information.  This version is for a top level hash structure.  The
# next level may be another hash, or an array.
#------------------------------------------------------------------------------
sub print_metric_function_hash
{
  my $subr_name = get_my_name ();

  my ($sub_struct_type, $metric, $struct_type_name, $target_structure_ref) = @_;

  my %target_structure = %{$target_structure_ref}; 

  gp_message ("debugXL", $subr_name, "contents of structure ".$struct_type_name."{".$metric."}:");

  for my $fields (sort keys %target_structure)
    {
      gp_message ("debugXL", $subr_name, "metric = $metric fields = $fields");
      if ($sub_struct_type eq "hash_hash")
        {
          for my $elems (sort keys %{$target_structure{$fields}})
            {
              my $txt = $struct_type_name."{$metric}{$fields}{$elems} = ";
              $txt   .= $target_structure{$fields}{$elems};
              gp_message ("debugXL", $subr_name, $txt);
            }
        }
      elsif ($sub_struct_type eq "hash_array")
        {
          my $values = "";
          for my $elems (sort keys @{$target_structure{$fields}})
            {
              $values .= "$target_structure{$fields}[$elems] ";
            }
          gp_message ("debugXL", $subr_name, $struct_type_name."{$metric}{$fields} = $values");
        }
      else
        {
          my $msg = "sub-structure type '$sub_struct_type' is not supported";
          gp_message ("assertion", $subr_name, $msg);
        }
    }
        
  return (0);

} #-- End of subroutine print_metric_function_hash

#------------------------------------------------------------------------------
# Print the opening message.
#------------------------------------------------------------------------------
sub print_opening_message
{
  my $subr_name = get_my_name ();
#------------------------------------------------------------------------------
# Since the second argument is an array, we pass it in by reference.  The
# alternative is to make it the last argument.
#------------------------------------------------------------------------------
  my ($outputdir, $exp_dir_list_ref, $time_percentage_multiplier) = @_;

  my @exp_dir_list = @{$exp_dir_list_ref};

  my $msg;
  my $no_of_dirs = scalar (@exp_dir_list);
#------------------------------------------------------------------------------
# Build a comma separated list with all directory names.  If there is only one
# entry, the leading comma will not be inserted.
#------------------------------------------------------------------------------
  my $dir_list   = join (", ", @exp_dir_list);

#------------------------------------------------------------------------------
# If there are at least two entries, find the last comma and replace it by
# " and".  Note that we know there is at least one comma, so the value 
# returned by rindex () cannot be -1.
#------------------------------------------------------------------------------
  if ($no_of_dirs > 1)
    {
      my $last_comma   = rindex ($dir_list, ",");
      my $ignore_value = substr ($dir_list, $last_comma, 1, " and");
    }
  $msg = "start $tool_name, generating directory $outputdir from $dir_list";

  gp_message ("verbose", $subr_name, $msg);

  if ($time_percentage_multiplier < 1.0)
    {
      $msg = "Handle at least ";
    }
  else
    {
      $msg = "Handle ";
    }

  $msg .= ($time_percentage_multiplier*100.0)."% of the time";
      
  gp_message ("verbose", $subr_name, $msg);

} #-- End of subroutine print_opening_message

#------------------------------------------------------------------------------
# TBD.
#------------------------------------------------------------------------------
sub print_program_header
{
  my $subr_name = get_my_name ();

  my ($mode, $tool_name, $binutils_version) = @_;

  my $header_limit = 60;
  my $dashes = "-";

#------------------------------------------------------------------------------
# Generate the dashed line
#------------------------------------------------------------------------------
  for (2 .. $header_limit)
    {
      $dashes .= "-";
    }

    gp_message ($mode, $subr_name, $dashes);
    gp_message ($mode, $subr_name, "Tool name: $tool_name");
    gp_message ($mode, $subr_name, "Version  : $binutils_version");
    gp_message ($mode, $subr_name, "Date     : " . localtime ());
    gp_message ($mode, $subr_name, $dashes);

} #-- End of subroutine print_program_header

#------------------------------------------------------------------------------
# Print a comment string, followed by the values of the options. The list
# with the keywords is sorted alphabetically.
#
# The value stored in $mode is passed on to gp_message ().  The intended use
# for this is to call this function in verbose and/or debug mode.
#
# The comment string is converted to uppercase.
#
# In case the length of the comment exceeds the length of the dashed line,
# the comment line is allowed to stick out to the right.
#
# If the length of the comment is less than the dashed line, it is centered 
# relative to the # length of the dashed line. 

# If the length of the comment and this line do not divide, an extra space is 
# added to the left of the comment.
#
# For example, if the comment is 55 long, there are 5 spaces to be distributed.
# There will be 3 spaces, followed by the comment. 
#------------------------------------------------------------------------------
sub print_table_user_settings
{
  my $subr_name = get_my_name ();

  my ($mode, $comment) = @_;

  my $leftover;
  my $padding;

  my $keyword;
  my $user_option;
  my $defined;
  my $value;
  my $data_type; 

  my $HEADER_LIMIT = 60;
  my $header = sprintf ("%-20s   %-9s   %8s   %s", "keyword", "option", "user set", "value");

#------------------------------------------------------------------------------
# Generate the dashed line
#------------------------------------------------------------------------------
  my $dashes = "-";
  for (2 .. $HEADER_LIMIT)
    {
      $dashes .= "-";
    }

#------------------------------------------------------------------------------
# Determine the padding needed to the left of the comment.
#------------------------------------------------------------------------------
  my $length_comment = length ($comment);

  $leftover = $length_comment%2;

  if ($length_comment <= ($HEADER_LIMIT-2))
    {
      $padding = ($HEADER_LIMIT - $length_comment + $leftover)/2;
    }
  else
    {
      $padding = 0;
    }
    
#------------------------------------------------------------------------------
# Generate the first blank part of the line.
#------------------------------------------------------------------------------
  my $blank_line = "";
  for (1 .. $padding)
    {
      $blank_line .= " ";
    }

#------------------------------------------------------------------------------
# Add the comment line with the first letter in uppercase.
#------------------------------------------------------------------------------
  my $final_comment = $blank_line.ucfirst ($comment);

  gp_message ($mode, $subr_name, $dashes);
  gp_message ($mode, $subr_name, $final_comment);
  gp_message ($mode, $subr_name, $dashes);
  gp_message ($mode, $subr_name, $header);
  gp_message ($mode, $subr_name, $dashes);

#------------------------------------------------------------------------------
# Print a line for each option. The list is sorted alphabetically.
#------------------------------------------------------------------------------
  for my $rc_keyword  (sort keys %g_user_settings)
    {
      $keyword     = $rc_keyword;
      $user_option = $g_user_settings{$rc_keyword}{"option"};
      $defined     = ($g_user_settings{$rc_keyword}{"defined"} ? "set" : "not set");
      $data_type   = $g_user_settings{$rc_keyword}{"data_type"};

      if (defined ($g_user_settings{$rc_keyword}{"current_value"}))
        {
          $value = $g_user_settings{$rc_keyword}{"current_value"};
          if ($data_type eq "boolean")
            {
              $value = $value ? "on" : "off";
            }
        }
      else
        {
          $value       = "undefined";
        }

      my $print_line = sprintf ("%-20s   %-9s   %8s   %s", $keyword, $user_option, $defined, $value);

      gp_message ($mode, $subr_name, $print_line);
    }
} #-- End of subroutine print_table_user_settings

#------------------------------------------------------------------------------
# Dump the contents of nested hash "g_user_settings".  Some simple formatting 
# is applied to make it easier to distinguish the various values.
#------------------------------------------------------------------------------
sub print_user_settings
{
  my $subr_name = get_my_name ();

  my ($mode, $comment) = @_;

  my $keyword_value_pair; 

  gp_message ($mode, $subr_name, $comment);

  for my $rc_keyword (keys %g_user_settings)
    {
      my $print_line = sprintf ("%-20s =>", $rc_keyword);
      for my $fields (sort keys %{ $g_user_settings{$rc_keyword} })
        {
          if (defined ($g_user_settings{$rc_keyword}{$fields}))
            {
              $keyword_value_pair = $fields." = ".$g_user_settings{$rc_keyword}{$fields};
            }
          else
            {
              $keyword_value_pair = $fields." = ". "undefined";
            }
           $print_line = join ("  ", $print_line, $keyword_value_pair);
        }
        gp_message ($mode, $subr_name, $print_line);
    }
} #-- End of subroutine print_user_settings

#------------------------------------------------------------------------------
# Print the version number and license information.
#------------------------------------------------------------------------------
sub print_version_info 
{
  print "$version_info\n";
  print "Copyright (C) 2023 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";

  return (0);

} #-- End of subroutine print_version_info

#------------------------------------------------------------------------------
# Process the call tree input data and generate HTML output.
#------------------------------------------------------------------------------
sub process_calltree
{
  my $subr_name = get_my_name ();

  my ($function_info_ref, $function_address_info_ref, $addressobjtextm_ref, 
       $input_string) = @_;

  my @function_info         = @{ $function_info_ref };
  my %function_address_info = %{ $function_address_info_ref };
  my %addressobjtextm       = %{ $addressobjtextm_ref };

  my $outputdir = append_forward_slash ($input_string);

  my @call_tree_data = ();

  my $LANG              = $g_locale_settings{"LANG"};
  my $decimal_separator = $g_locale_settings{"decimal_separator"};

  my $infile  = $outputdir . "calltree";
  my $outfile = $outputdir . "calltree.html";

  open (CALL_TREE_IN, "<", $infile) 
    or die ("Not able to open calltree file $infile for reading - '$!'");
  gp_message ("debug", $subr_name, "opened file $infile for reading");

  open (CALL_TREE_OUT, ">", $outfile) 
    or die ("Not able to open $outfile for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile for writing");

  gp_message ("debug", $subr_name, "building calltree file $outfile");
  
#------------------------------------------------------------------------------
# The directory name is potentially used below, but since it is a constant,
# we get it here and only once.
#------------------------------------------------------------------------------
#  my ($ignore_file_name, $directory_name, $ignore_suffix) = fileparse ($infile,"");
#  gp_message ("debug", $subr_name, "directory_name = $directory_name");

#------------------------------------------------------------------------------
# Generate some of the structures used in the HTML output.
#------------------------------------------------------------------------------
  my $file_title      = "Call Tree overview";
  my $html_header     = ${ create_html_header (\$file_title) };
  my $html_home_right = ${ generate_home_link ("right") };

  my $page_title    = "Call Tree View";
  my $size_text     = "h2";
  my $position_text = "center";
  my $html_title_header = ${ generate_a_header (
                            \$page_title, 
                            \$size_text, 
                            \$position_text) };

#-------------------------------------------------------------------------------
# Get the acknowledgement, return to main link, and final html statements.
#-------------------------------------------------------------------------------
  my $html_home_left       = ${ generate_home_link ("left") };
  my $html_acknowledgement = ${ create_html_credits () };
  my $html_end             = ${ terminate_html_document () };

#------------------------------------------------------------------------------
# Read all of the file into array with the name call_tree_data.
#------------------------------------------------------------------------------
  chomp (@call_tree_data = <CALL_TREE_IN>);
  close (CALL_TREE_IN);

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Process the data here and generate the HTML lines.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Print the top part of the HTML file.
#------------------------------------------------------------------------------
  print CALL_TREE_OUT $html_header;
  print CALL_TREE_OUT $html_home_right;
  print CALL_TREE_OUT $html_title_header;

#-------------------------------------------------------------------------------
# Print the generated HTML structures here.
#-------------------------------------------------------------------------------
##  print CALL_TREE_OUT "$_" for @whatever;
##  print CALL_TREE_OUT "<pre>\n";
##  print CALL_TREE_OUT "$_\n" for @whatever2;
##  print CALL_TREE_OUT "</pre>\n";

#-------------------------------------------------------------------------------
# Print the last part of the HTML file.
#-------------------------------------------------------------------------------
  print CALL_TREE_OUT $html_home_left;
  print CALL_TREE_OUT "<br>\n";
  print CALL_TREE_OUT $html_acknowledgement;
  print CALL_TREE_OUT $html_end;

  close (CALL_TREE_OUT);

  return (0);

} #-- End of subroutine process_calltree

#-------------------------------------------------------------------------------
# Process the generated experiment info file(s).
#-------------------------------------------------------------------------------
sub process_experiment_info
{
  my $subr_name = get_my_name ();

  my ($experiment_data_ref) = @_;

  my @exp_info;
  my @experiment_data = @{ $experiment_data_ref };

  my $exp_id;
  my $exp_name;
  my $exp_data_file;
  my $input_line;
  my $target_cmd;
  my $hostname ;
  my $OS;
  my $page_size;
  my $architecture;
  my $start_date;
  my $end_experiment;
  my $data_collection_duration;
  my $total_thread_time;
  my $user_cpu_time;
  my $user_cpu_percentage;
  my $system_cpu_time;
  my $system_cpu_percentage;
  my $sleep_time;
  my $sleep_percentage;

#-------------------------------------------------------------------------------
# Define the regular expressions used to capture the info.
#-------------------------------------------------------------------------------
# Target command (64-bit): './../bindir/mxv-pthreads.exe -m 3000 -n 2000 -t 2'

  my $target_cmd_regex = '\s*Target command\s+(\(.+\)):\s+\'(.+)\'';

# Host `ruudvan-vm-haswell-2-20210609', OS `Linux 5.4.17-2102.202.5.el8uek.x86_64', page size 4096, architecture `x86_64'

  my $host_system_regex = '\s*Host\s+\`(.+)\',\s+OS\s+\`(.+)\',\s+page size\s+(\d+),\s+architecture\s+\`(.+)\'';

# Experiment started Mon Aug 30 13:03:20 2021

  my $start_date_regex = '\s*Experiment started\s+(.+)';

# Experiment Ended: 1.812441219

  my $end_experiment_regex = '\s*Experiment Ended:\s+(.+)';

# Data Collection Duration: 1.812441219

  my $data_collection_duration_regex = '\s*Data Collection Duration:\s+(.+)';

#                           Total Thread Time (sec.): 1.812

  my $total_thread_time_regex = '\s*Total Thread Time (sec.):\s+(.+)';

#                                          User CPU: 1.685 ( 95.0%)

  my $user_cpu_regex = '\s*User CPU:\s+(.+)\s+\(\s*(.+)\)';

#                                        System CPU: 0.088 (  5.0%)

  my $system_cpu_regex = '\s*System CPU:\s+(.+)\s+\(\s*(.+)\)';

#                                             Sleep: 0.    (  0. %)

  my $sleep_regex = '\s*Sleep:\s+(.+)\s+\(\s*(.+)\)';

#-------------------------------------------------------------------------------
# Scan the experiment data and select the info of interest.
#-------------------------------------------------------------------------------
  for my $i (sort keys @experiment_data)
    {
      $exp_id        = $experiment_data[$i]{"exp_id"};
      $exp_name      = $experiment_data[$i]{"exp_name_full"};
      $exp_data_file = $experiment_data[$i]{"exp_data_file"};

      my $msg = "exp_id = $exp_id name = $exp_name file = $exp_data_file";
      gp_message ("debug", $subr_name, $msg);

      open (EXPERIMENT_INFO, "<", $exp_data_file) 
        or die ("$subr_name - unable to open file $exp_data_file for reading '$!'");
      gp_message ("debug", $subr_name, "opened file $exp_data_file for reading");

      chomp (@exp_info = <EXPERIMENT_INFO>);

#-------------------------------------------------------------------------------
# Process the info for the current experiment.
#-------------------------------------------------------------------------------
      for my $line (0 .. $#exp_info)
        {
          $input_line = $exp_info[$line];

          my $msg = "exp_id = $exp_id: input_line = $input_line";
          gp_message ("debugM", $subr_name, $msg);

          if ($input_line =~ /$target_cmd_regex/)
            {
              $target_cmd = $2;
              gp_message ("debugM", $subr_name, "$exp_id => $target_cmd");
              $experiment_data[$i]{"target_cmd"} = $target_cmd;
            }
          elsif ($input_line =~ /$host_system_regex/)
            {
              $hostname  = $1;
              $OS        = $2;
              $page_size = $3;
              $architecture = $4;
              gp_message ("debugM", $subr_name, "$exp_id => $hostname $OS $page_size $architecture");
              $experiment_data[$i]{"hostname"} = $hostname;
              $experiment_data[$i]{"OS"} = $OS;
              $experiment_data[$i]{"page_size"} = $page_size;
              $experiment_data[$i]{"architecture"} = $architecture;
            }
          elsif ($input_line =~ /$start_date_regex/)
            {
              $start_date = $1;
              gp_message ("debugM", $subr_name, "$exp_id => $start_date");
              $experiment_data[$i]{"start_date"} = $start_date;
            }
          elsif ($input_line =~ /$end_experiment_regex/) 
            {
              $end_experiment = $1;
              gp_message ("debugM", $subr_name, "$exp_id => $end_experiment");
              $experiment_data[$i]{"end_experiment"} = $end_experiment;
            }
          elsif ($input_line =~ /$data_collection_duration_regex/) 
            {
              $data_collection_duration = $1;
              gp_message ("debugM", $subr_name, "$exp_id => $data_collection_duration");
              $experiment_data[$i]{"data_collection_duration"} = $data_collection_duration;
            }
#------------------------------------------------------------------------------
#                                       Start Label: Total
#                                          End Label: Total
#                                  Start Time (sec.): 0.000
#                                    End Time (sec.): 1.812
#                                    Duration (sec.): 1.812
#                           Total Thread Time (sec.): 1.812
#                          Average number of Threads: 1.000
# 
#                               Process Times (sec.):
#                                           User CPU: 1.666 ( 91.9%)
#                                         System CPU: 0.090 (  5.0%)
#                                           Trap CPU: 0.    (  0. %)
#                                          User Lock: 0.    (  0. %)
#                                    Data Page Fault: 0.    (  0. %)
#                                    Text Page Fault: 0.    (  0. %)
#                                  Kernel Page Fault: 0.    (  0. %)
#                                            Stopped: 0.    (  0. %)
#                                           Wait CPU: 0.    (  0. %)
#                                              Sleep: 0.056 (  3.1%)
#------------------------------------------------------------------------------
          elsif ($input_line =~ /$total_thread_time_regex/) 
            {
              $total_thread_time = $1;
              gp_message ("debugM", $subr_name, "$exp_id => $total_thread_time");
              $experiment_data[$i]{"total_thread_time"} = $total_thread_time;
            }
          elsif ($input_line =~ /$user_cpu_regex/) 
            {
              $user_cpu_time       = $1;
              $user_cpu_percentage = $2;
              gp_message ("debugM", $subr_name, "$exp_id => $user_cpu_time $user_cpu_percentage");
              $experiment_data[$i]{"user_cpu_time"} = $user_cpu_time . "&nbsp; (" . $user_cpu_percentage . ")";
              $experiment_data[$i]{"user_cpu_percentage"} = $user_cpu_percentage;
            }
          elsif ($input_line =~ /$system_cpu_regex/) 
            {
              $system_cpu_time       = $1;
              $system_cpu_percentage = $2;
              gp_message ("debugM", $subr_name, "$exp_id => $system_cpu_time $system_cpu_percentage");
              $experiment_data[$i]{"system_cpu_time"} = $system_cpu_time . "&nbsp; (" . $system_cpu_percentage . ")";
              $experiment_data[$i]{"system_cpu_percentage"} = $system_cpu_percentage;
            }
          elsif ($input_line =~ /$sleep_regex/) 
            {
              $sleep_time       = $1;
              $sleep_percentage = $2;
              $experiment_data[$i]{"sleep_time"} = $sleep_time . "&nbsp; (" . $sleep_percentage . ")";
              $experiment_data[$i]{"sleep_percentage"} = $sleep_percentage;

              my $msg = "exp_id = $exp_id => sleep_time = $sleep_time " .
                        "sleep_percentage = $sleep_percentage";
              gp_message ("debugM", $subr_name, $msg);
            }
        }
    }

  for my $keys (0 .. $#experiment_data)
    {
      for my $fields (sort keys %{ $experiment_data[$keys] })
        {
          my $msg = "experiment_data[$keys]{$fields} = " .
             $experiment_data[$keys]{$fields};
          gp_message ("debugM", $subr_name, $msg);
        }
    }

  return (\@experiment_data);

} #-- End of subroutine process_experiment_info

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub process_function_files
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref, $executable_name, $time_percentage_multiplier, 
      $summary_metrics, $process_all_functions, $elf_loadobjects_found, 
      $outputdir, $sort_fields_ref, $function_info_ref, 
      $function_address_and_index_ref, $LINUX_vDSO_ref, 
      $metric_description_ref, $elf_arch, $base_va_executable, 
      $ARCHIVES_MAP_NAME, $ARCHIVES_MAP_VADDR, $elf_rats_ref) = @_;

  my $old_fsummary; 
  my $total_attributed_time;
  my $current_attributed_time;
  my $value;

  my @exp_dir_list               = @{ $exp_dir_list_ref };
  my @function_info              = @{ $function_info_ref };
  my %function_address_and_index = %{ $function_address_and_index_ref };
  my @sort_fields                = @{ $sort_fields_ref };
  my %metric_description         = %{ $metric_description_ref };
  my %elf_rats                   = %{ $elf_rats_ref };

#------------------------------------------------------------------------------
# The regex section.
#
# TBD: Remove the part regarding clones. Legacy.
#------------------------------------------------------------------------------
  my $replace_quote_regex = '"/\"';
  my $find_clone_regex    = '^(.*)(\s+--\s+cloned\s+version\s+\[)([^]]+)(\])';

  my %addressobj_index = ();
  my %function_address_info = ();
  my $function_address_info_ref;

  $outputdir = append_forward_slash ($outputdir);

  my %functions_per_metric_indexes = ();
  my $functions_per_metric_indexes_ref;

  my %functions_per_metric_first_index = ();
  my $functions_per_metric_first_index_ref;

  my %routine_list = ();
  my %handled_routines = ();

#------------------------------------------------------------------------------
# TBD: Name cleanup needed.
#------------------------------------------------------------------------------

  my $number_of_metrics;
  my $expr_name;
  my $routine;
  my $tmp;
  my $loadobj;
  my $PCA;
  my $address_field;
  my $limit_txt;
  my $n_metrics_text;
  my $disfile;
  my $srcfile;
  my $RIN;
  my $gp_listings_cmd;
  my $gp_display_text_cmd; 
  my $ignore_value;

  my $result_file   = $outputdir . "gp-listings.out";
  my $gp_error_file = $outputdir . "gp-listings.err";

  my $convert_to_dot    = $g_locale_settings{"convert_to_dot"};
  my $decimal_separator = $g_locale_settings{"decimal_separator"};
  my $length_of_string  = length ($outputdir);

  $expr_name = join (" ", @exp_dir_list);

  gp_message ("debug", $subr_name, "expr_name = $expr_name");

#------------------------------------------------------------------------------
# Loop over the files in $outputdir.
#------------------------------------------------------------------------------
  while (glob ($outputdir.'*.sort.func-PC'))
    {
      my $metric;
      my $infile;
      my $ignore_value;
      my $suffix_not_used;

      $infile = $_;

      ($metric, $ignore_value, $suffix_not_used) = fileparse ($infile, ".sort.func-PC");

      gp_message ("debugXL", $subr_name, "suffix_not_used = $suffix_not_used");
      gp_message ("debugXL", $subr_name, "func-PC->$infile<- metric->$metric<-");
  
   # Function_info creates the functions files from the PC ones
   # as well as culling PC and metric information

      ($function_address_info_ref, 
       $functions_per_metric_first_index_ref, 
       $functions_per_metric_indexes_ref) = function_info (
                                              $outputdir, 
                                              $infile, 
                                              $metric, 
                                              $LINUX_vDSO_ref);

      @{$function_address_info{$metric}}            = @{$function_address_info_ref};
      %{$functions_per_metric_indexes{$metric}}     = %{$functions_per_metric_indexes_ref};
      %{$functions_per_metric_first_index{$metric}} = %{$functions_per_metric_first_index_ref};

      $ignore_value = print_metric_function_array ($metric, 
                                                   "function_address_info", 
                                                   \@{$function_address_info{$metric}});
      $ignore_value = print_metric_function_hash ("hash_hash",  $metric, 
                                                  "functions_per_metric_first_index", 
                                                  \%{$functions_per_metric_first_index{$metric}});
      $ignore_value = print_metric_function_hash ("hash_array", $metric, 
                                                  "functions_per_metric_indexes", 
                                                  \%{$functions_per_metric_indexes{$metric}});
    }

#------------------------------------------------------------------------------
# Get header info for use in post processing er_html output
#------------------------------------------------------------------------------
  gp_message ("debugXL", $subr_name, "get_hdr_info section");

  get_hdr_info ($outputdir, $outputdir."functions.sort.func");

  for my $field (@sort_fields)
    {
      get_hdr_info ($outputdir, $outputdir."$field.sort.func");
    }

#------------------------------------------------------------------------------
# Caller-callee
#------------------------------------------------------------------------------
  get_hdr_info ($outputdir, $outputdir."calls.sort.func");

#------------------------------------------------------------------------------
# Calltree
#------------------------------------------------------------------------------
  if ($g_user_settings{"calltree"}{"current_value"} eq "on")
    {
      get_hdr_info ($outputdir, $outputdir."calltree.sort.func");
    }
  
  gp_message ("debug", $subr_name, "process functions");

  my $scriptfile     = $outputdir.'gp-script';
  my $script_metrics = "$summary_metrics";
  my $func_limit     = $g_user_settings{"func_limit"}{"current_value"};

  open (SCRIPT, ">", $scriptfile)
    or die ("Unable to create script file $scriptfile - '$!'");
  gp_message ("debug", $subr_name, "opened script file $scriptfile for writing");

  print SCRIPT "# limit $func_limit\n";
  print SCRIPT "limit $func_limit\n";
  print SCRIPT "# thread_select all\n";
  print SCRIPT "thread_select all\n";
  print SCRIPT "# metrics $script_metrics\n";
  print SCRIPT "metrics $script_metrics\n";

  for my $metric (@sort_fields)
    {
      gp_message ("debug", $subr_name, "handling $metric->$metric_description{$metric}");
  
      $total_attributed_time   = 0;
      $current_attributed_time = 0;
  
      $value = $function_address_info{$metric}[0]{"metric_value"}; # <Total>
      if ($convert_to_dot)
        {
          $value =~ s/$decimal_separator/\./;
        }
      $total_attributed_time = $value;
  
#------------------------------------------------------------------------------
# start at 1 - skipping <Total>
#------------------------------------------------------------------------------
      for my $INDEX (1 .. $#{$function_address_info{$metric}}) 
        {
#------------------------------------------------------------------------------
#Looking to handle at least 99% of the time - or what the user asked for
#------------------------------------------------------------------------------
          $value   = $function_address_info{$metric}[$INDEX]{"metric_value"};
          $routine = $function_address_info{$metric}[$INDEX]{"routine"};

          gp_message ("debugXL", $subr_name, " total $total_attributed_time current $current_attributed_time");
          gp_message ("debugXL", $subr_name, "  (found routine $routine : value $value)");

          if ($convert_to_dot) 
            {
              $value =~ s/$decimal_separator/\./;
            }

          if ( ($value > $total_attributed_time*(1-$time_percentage_multiplier)) or
               ( ($total_attributed_time == 0) and ($value>0) ) or 
               $process_all_functions) 
            {
              $PCA = $function_address_info{$metric}[$INDEX]{"PC Address"};

              if (not exists ($functions_per_metric_first_index{$metric}{$routine}{$PCA}))
                {
                  gp_message ("debugXL", $subr_name, "not exists: functions_per_metric_first_index{$metric}{$routine}{$PCA}");
                }
              if (not exists ($function_address_and_index{$routine}{$PCA}))
                {
                  gp_message ("debugXL", $subr_name, "not exists: function_address_and_index{$routine}{$PCA}");
                }

              if (exists ($functions_per_metric_first_index{$metric}{$routine}{$PCA}) and 
                  exists ($function_address_and_index{$routine}{$PCA}))
                {
#------------------------------------------------------------------------------
# handled_routines now contains $RI from "first_metric" (?)
#------------------------------------------------------------------------------
                  $handled_routines{$function_address_and_index{$routine}{$PCA}} = 1; 
                  my $description = ${ retrieve_metric_description (\$metric, \%metric_description) };
                  if ($metric_description{$metric} =~ /Exclusive Total CPU Time/)
                    {
                      $routine_list{$routine} = 1 
                    }

                  gp_message ("debugXL", $subr_name, " $routine is candidate");
                } 
              else 
                {
                  die ("internal error for metric $metric and routine $routine");
                }

              $current_attributed_time += $value;
            }
        }
    }
#------------------------------------------------------------------------------
# Sort numerically in ascending order.
#------------------------------------------------------------------------------
  for my $routine_index (sort {$a <=> $b} keys %handled_routines)
    {
      $routine = $function_info[$routine_index]{"routine"};
      gp_message ("debugXL", $subr_name, "routine_index = $routine_index routine = $routine");
      next unless $routine_list{$routine};

# not used      $source = $function_info[$routine_index]{"Source File"};

      $function_info[$routine_index]{"srcline"} = "";
      $address_field = $function_info[$routine_index]{"addressobjtext"};

##      $disfile = "file\.$routine_index\.dis"; 
      $disfile = "file." . $routine_index . "." . $g_html_base_file_name{"disassembly"};
      $srcfile = "";
      $srcfile = "file\.$routine_index\.src.txt";

#------------------------------------------------------------------------------
# If the file is unknown, we can disassemble anyway and add disassembly 
# to the script.
#------------------------------------------------------------------------------
      print SCRIPT "# outfile $outputdir"."$disfile\n";
      print SCRIPT "outfile $outputdir"."$disfile\n";
#------------------------------------------------------------------------------
# TBD: Legacy. Not sure why this is needed, but it won't harm things. I hope.
#------------------------------------------------------------------------------
      $tmp = $routine;
      $tmp =~ s/$replace_quote_regex//g;
      print SCRIPT "# disasm \"$tmp\" $address_field\n";
      print SCRIPT "disasm \"$tmp\" $address_field\n";
      if ($srcfile=~/file/)
        {
          print SCRIPT "# outfile $outputdir"."$srcfile\n";
          print SCRIPT "outfile $outputdir"."$srcfile\n";
          print SCRIPT "# source \"$tmp\" $address_field\n";
          print SCRIPT "source \"$tmp\" $address_field\n";
        }

      if ($routine =~ /$find_clone_regex/)
        {
          my ($clone_routine) = $1.$2.$3.$4;
          my ($clone) = $3;
        }
     }
  close SCRIPT;

#------------------------------------------------------------------------------
# Remember the number of handled routines depends on the limit setting passed
# to er_print together with the sorting order on the metrics, which usually results
# in different routines at the top. Thus $RIN below can be greater than the limit.
#------------------------------------------------------------------------------

  $RIN = scalar (keys %handled_routines);

  if (!$func_limit)
    {
      $limit_txt = "unlimited";
    }
  else
    {
      $limit_txt = $func_limit - 1;
  }

  $number_of_metrics = scalar (@sort_fields); 

  $n_metrics_text = ($number_of_metrics == 1) ? "metric" : "metrics";

  gp_message ("debugXL", $subr_name, "built function list with $RIN functions");
  gp_message ("debugXL", $subr_name, "$number_of_metrics $n_metrics_text and a function limit of $limit_txt");

# add ELF program header offset 

  for my $routine_index (sort {$a <=> $b} keys %handled_routines)
    {
      $routine = $function_info[$routine_index]{"routine"};
      $loadobj = $function_info[$routine_index]{"Load Object"};

      gp_message ("debugXL", $subr_name, "routine = $routine loadobj = $loadobj elf_arch = $elf_arch");

      if ($loadobj ne '')
        {
    # <Truncated-stack> is associated with <Total>. Its load object is <Total>
          if ($loadobj eq "<Total>")
            {
              next;
            }
    # Have seen a routine called <Unknown>. Its load object is <Unknown>
          if ($loadobj eq "<Unknown>")
            {
              next;
            }
###############################################################################
## RUUD: The new approach gives a different result. Investigate this.
#
# Turns out the new code improves the result.  The addresses are now correct
# and as a result, more ftag's are created later on.
###############################################################################
          gp_message ("debugXL", $subr_name, "before function_info[$routine_index]{addressobj} = $function_info[$routine_index]{'addressobj'}");

          $function_info[$routine_index]{"addressobj"} += bigint::hex (
                                                determine_base_va_address (
                                                  $executable_name, 
                                                  $base_va_executable, 
                                                  $loadobj, 
                                                  $routine));
          $addressobj_index{$function_info[$routine_index]{"addressobj"}} = $routine_index;

          gp_message ("debugXL", $subr_name, "after  function_info[$routine_index]{addressobj} = $function_info[$routine_index]{'addressobj'}");
          gp_message ("debugXL", $subr_name, "after  addressobj_index{function_info[$routine_index]{addressobj}} = $addressobj_index{$function_info[$routine_index]{'addressobj'}}");
        }
    }

#------------------------------------------------------------------------------
# Get the disassembly and source code output.
#------------------------------------------------------------------------------
  $gp_listings_cmd = "$GP_DISPLAY_TEXT -limit $func_limit -viewmode machine " .
                     "-compare off -script $scriptfile $expr_name";

  $gp_display_text_cmd = "$gp_listings_cmd 1> $result_file 2>> $gp_error_file";

  gp_message ("debugXL", $subr_name,"gp_display_text_cmd = $gp_display_text_cmd");

  gp_message ("debug", $subr_name, "calling $GP_DISPLAY_TEXT to produce disassembly and source code output");

  my ($error_code, $cmd_output) = execute_system_cmd ($gp_display_text_cmd);

  if ($error_code != 0)
    {
      $ignore_value = msg_display_text_failure ($gp_display_text_cmd, 
                                                $error_code, 
                                                $gp_error_file);
      gp_message ("abort", "execution terminated");
    }

  return (\@function_info, \%function_address_info, \%addressobj_index);

} #-- End of subroutine process_function_files

#------------------------------------------------------------------------------
# Process the information found in the function overview file passed in.
#
# Example input:
#
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Functions sorted by metric: Exclusive Total CPU Time
# 
# PC Addr.       Name              Excl.     Excl. CPU  Excl.         Excl.         Excl.   Excl.
#                                  Total     Cycles     Instructions  Last-Level    IPC     CPI
#                                  CPU sec.   sec.      Executed      Cache Misses
# 1:0x00000000   <Total>           3.713     4.256      15396819712   27727992       1.577  0.634
# 2:0x000021ae   mxv_core          3.532     4.116      14500538992   27527781       1.536  0.651
# 2:0x00001f7b   init_data         0.070     0.084         64020034     200211       0.333  3.000
#------------------------------------------------------------------------------
sub process_function_overview
{
  my $subr_name = get_my_name ();

  my ($metric_ref, $exp_type_ref, $summary_metrics_ref, $number_of_metrics_ref,
      $function_info_ref, $function_view_structure_ref, $overview_file_ref) = @_;

  my $metric                  = ${ $metric_ref };
  my $exp_type                = ${ $exp_type_ref };
  my $summary_metrics         = ${ $summary_metrics_ref };
  my $number_of_metrics       = ${ $number_of_metrics_ref };
  my @function_info           = @{ $function_info_ref };
  my %function_view_structure = %{ $function_view_structure_ref };
  my $overview_file           = ${ $overview_file_ref };

  my $all_metrics; 
  my $decimal_separator = $g_locale_settings{"decimal_separator"};
  my $length_of_block;
  my $elements_in_name; 
  my $full_hex_address;
  my $header_line;
  my $hex_address;
  my $html_line;
  my $input_line;
  my $name_regex; 
  my $no_of_fields; 
  my $metrics_length;
  my $missing_digits; 
  my $remaining_part_header;
  my $routine; 
  my $routine_length; 
  my $scan_header        = $FALSE;
  my $scan_function_data = $FALSE;
  my $string_length;
  my $total_header_lines; 

  my @address_field           = ();
  my @fields                  = (); 
  my @function_data           = ();
  my @function_names          = ();
  my @function_view_array     = ();
  my @function_view_modified  = ();
  my @header_lines            = ();
  my @metrics_part            = ();
  my @metric_values           = ();

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $header_name_regex     = '(.*\.)(\s+)(Name)\s+(.*)';
  my $total_marker_regex    = '\s*(\d+:0x[a-fA-F0-9]+)\s+(<Total>)\s+(.*)';
  my $empty_line_regex      = '^\s*$';
  my $catch_all_regex       = '\s*(.*)';
  my $get_hex_address_regex = '(\d+):0x(\S+)';
  my $get_addr_offset_regex = '^@\d+:';
  my $zero_dot_at_end_regex = '[\w0-9' . $decimal_separator . ']*(0' . $decimal_separator . '$)';
  my $backward_slash_regex  = '\/';

#------------------------------------------------------------------------------
  if (is_file_empty ($overview_file))
    {
      gp_message ("error", $subr_name, "assertion error: file $overview_file is empty");
    }

  open (FUNC_OVERVIEW, "<", $overview_file) 
    or die ("$subr_name - unable to open file $overview_file for reading '$!'");
  gp_message ("debug", $subr_name, "opened file $overview_file for reading");

  gp_message ("debug", $subr_name, "processing file for exp_type = $exp_type");

  gp_message ("debugM", $subr_name, "header_name_regex  = $header_name_regex");
  gp_message ("debugM", $subr_name, "total_marker_regex = $total_marker_regex");
  gp_message ("debugM", $subr_name, "empty_line_regex   = $empty_line_regex");
  gp_message ("debugM", $subr_name, "catch_all_regex    = $catch_all_regex");
  gp_message ("debugM", $subr_name, "get_hex_address_regex = $get_hex_address_regex");
  gp_message ("debugM", $subr_name, "get_addr_offset_regex = $get_addr_offset_regex");
  gp_message ("debugM", $subr_name, "zero_dot_at_end_regex = $zero_dot_at_end_regex");
  gp_message ("debugM", $subr_name, "backward_slash_regex  = $backward_slash_regex");

#------------------------------------------------------------------------------
# Read the input file into memory.
#------------------------------------------------------------------------------
  chomp (@function_data = <FUNC_OVERVIEW>);
  gp_message ("debug", $subr_name, "read all of file $overview_file into memory");

#-------------------------------------------------------------------------------
# Parse the function view info and store the data.
#-------------------------------------------------------------------------------
  my $max_header_length  = 0;
  my $max_metrics_length = 0;

#------------------------------------------------------------------------------
# Loop over all the lines.  Extract the header, metric values, function names,
# and the addresses.
#
# This is also where the maximum lengths for the header and metric lines are
# computed.  This is used to get the correct alignment in the HTML output.
#------------------------------------------------------------------------------
  for (my $line = 0; $line <= $#function_data; $line++)
    {
      $input_line = $function_data[$line];
      gp_message ("debugXL", $subr_name, "input_line = $input_line");

#------------------------------------------------------------------------------
# The table header is assumed to start at the line that has "Name" in it.
# The header ends when we see the function name "<Total>".
#------------------------------------------------------------------------------
      if ($input_line =~ /$header_name_regex/)
        {
          $scan_header = $TRUE;
        }
      elsif ($input_line =~ /$total_marker_regex/) 
        {
          $scan_header        = $FALSE;
          $scan_function_data = $TRUE;
        }

      if ($scan_header)
        {
#------------------------------------------------------------------------------
# This group is only defined for the first line of the header and $4 contains 
# the remaining part of the line after "Name", without the leading spaces.
#------------------------------------------------------------------------------
          if (defined ($4))
            {
              $remaining_part_header = $4;
              my $msg =  "remaining_part_header = $remaining_part_header";
              gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# Determine the maximum length of the header.  This needs to be done before 
# the HTML controls are added.
#------------------------------------------------------------------------------
              my $header_length = length ($remaining_part_header);
              $max_header_length = max ($max_header_length, $header_length);

#------------------------------------------------------------------------------
# TBD Should change this and not yet include html in header_lines
#------------------------------------------------------------------------------
              $html_line = "<b>" . $remaining_part_header . "</b>";

              push (@header_lines, $html_line);

              gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
              gp_message ("debugXL", $subr_name, "html_line = $html_line");
            }
#------------------------------------------------------------------------------
# Captures the subsequent header lines.  Assume they exist.
#------------------------------------------------------------------------------
          elsif ($input_line =~ /$catch_all_regex/)
            { 
              $header_line = $1;
              gp_message ("debugXL", $subr_name, "header_line = $header_line");

              my $header_length = length ($header_line);
              $max_header_length = max ($max_header_length, $header_length);

#------------------------------------------------------------------------------
# TBD Should change this and not yet include html in header_lines
#------------------------------------------------------------------------------
              $html_line = "<b>" . $header_line . "</b>";

              push (@header_lines, $html_line);

              gp_message ("debugXL", $subr_name, "max_header_length = $max_header_length");
              gp_message ("debugXL", $subr_name, "html_line = $html_line");
            } 
        }
#------------------------------------------------------------------------------
# This is a line with function data.
#------------------------------------------------------------------------------
      if ($scan_function_data and (not ($input_line =~ /$empty_line_regex/)))
        {
          @fields = split (" ", $input_line);

          $no_of_fields = $#fields + 1;
          $elements_in_name = $no_of_fields - $number_of_metrics - 1;

          gp_message ("debugXL", $subr_name, "no_of_fields = $no_of_fields elements_in_name = $elements_in_name");
     
#------------------------------------------------------------------------------
# TBD: Handle this better in case a function entry has more than 2 words.
# Build the regex dynamically and use eval to capture the correct group.
# CHECK CODE IN GENERATE_CALLER_CALLEE
#------------------------------------------------------------------------------
          if ($elements_in_name == 1) 
            {
              $name_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(\S+)\s+(.*)';
            }
          elsif ($elements_in_name == 2) 
            {
              $name_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+((\S+)\s+(\S+))\s+(.*)';
            }
          else
            {
              gp_message ("error", $subr_name, "assertion error: $elements_in_name elements in name exceeds limit");
            }

          if ($input_line =~ /$name_regex/)
            {
              $full_hex_address   = $1;
              $routine            = $2;

              if ($elements_in_name == 1) 
                {
                  $all_metrics = $3;
                }
              elsif ($elements_in_name == 2) 
                {
                  $all_metrics = $5;
                }

#------------------------------------------------------------------------------
# In case the last metric is 0. only, we append 3 extra characters that 
# represent zero.  We cannot change the number to 0.000 though because that
# has a different interpretation than 0.
# In a later phase, the "ZZZ" symbol will be removed again, but for now it 
# creates consistency in, for example, the length of the metrics part.
#------------------------------------------------------------------------------
              if ($all_metrics =~ /$zero_dot_at_end_regex/)
                {
                  if (defined ($1) )
                    {
#------------------------------------------------------------------------------
# Somewhat overkill, but remove the leading "\" from the decimal separator
# in the debug print since it is used for internal purposes only.
#------------------------------------------------------------------------------
                      my $decimal_point = $decimal_separator;
                      $decimal_point =~ s/$backward_slash_regex//;
                      my $txt = "all_metrics = $all_metrics ended with 0"; 
                      $txt   .= "$decimal_point ($decimal_separator)";
                      gp_message ("debugXL", $subr_name, $txt);

                      $all_metrics .= "ZZZ";
                    }
                }
              $metrics_length = length ($all_metrics);
              $max_metrics_length = max ($max_metrics_length, $metrics_length);
              gp_message ("debugXL", $subr_name, "$routine all_metrics = $all_metrics metrics_length = $metrics_length"); 

              if ($full_hex_address =~ /$get_hex_address_regex/)
                {
                  $hex_address = "0x" . $2;
                }

              push (@address_field, $hex_address); 
              push (@metric_values, $all_metrics);

#------------------------------------------------------------------------------
# Record the function name "as is".  Below we figure out what the final name
# should be in case there are multiple occurrences of the same name.
#
# The reason to decouple this is to avoid the code gets too complex here.
#------------------------------------------------------------------------------
              push (@function_names, $routine);
            }
        }
    } #-- End of loop over the input lines

#------------------------------------------------------------------------------
# Store the maximum lengths for the header and metrics.
#------------------------------------------------------------------------------
    gp_message ("debugXL", $subr_name, "final max_header_length  = $max_header_length");
    gp_message ("debugXL", $subr_name, "final max_metrics_length = $max_metrics_length");

    $function_view_structure{"max header length"}  = $max_header_length;
    $function_view_structure{"max metrics length"} = $max_metrics_length;

#------------------------------------------------------------------------------
# Determine the final name for the functions and set up the HTML block.
#------------------------------------------------------------------------------
  my @final_html_function_block = ();
  my @function_index_list       = ();

#------------------------------------------------------------------------------
# First, an index list is built.  If we are to index the functions in order of 
# appearance in the function overview from 0 to n-1, the value of the array
# for index "i" is the index into the large "function_info" structure.  This
# has the final name, the html function block, etc.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
## TBD: Use get_index_function_info??!!
#------------------------------------------------------------------------------
  for my $i (keys @function_names)
    {
#------------------------------------------------------------------------------
# Get the function name and the address from the function overview.  The
# address is used to differentiate in case a function has multiple occurences.
#------------------------------------------------------------------------------
      my $routine = $function_names[$i];
      my $current_address = $address_field[$i];

      my $found_a_match = $FALSE;
      my $final_function_name; 
      my $ref_index; 

#------------------------------------------------------------------------------
# Check if there are duplicate entries for this function.  If there are, use
# the address to find the right match in the function_info structure.
#------------------------------------------------------------------------------
      gp_message ("debugXL", $subr_name, "$routine: first check for multiple occurrences");
      if (exists ($g_multi_count_function{$routine}))
        {
          gp_message ("debugXL", $subr_name, "$routine: occurrences = $g_function_occurrences{$routine}");
          for my $ref (keys @{ $g_map_function_to_index{$routine} })
            {
              my $ref_index = $g_map_function_to_index{$routine}[$ref];
              my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
#------------------------------------------------------------------------------
# The address has the following format: 6:0x0003af50, but we only need the
# part after the colon and remove the first part.
#------------------------------------------------------------------------------
              $addr_offset =~ s/$get_addr_offset_regex//;
             
              gp_message ("debugXL", $subr_name, "$routine: ref_index = $ref_index");
              gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");
              gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");

              if ($addr_offset eq $current_address)
#------------------------------------------------------------------------------
# There is a match and we can store the index.
#------------------------------------------------------------------------------
                {
                  $found_a_match = $TRUE;
                  push (@function_index_list, $ref_index);
                  last;
                }
            }
        }
      else
        {
#------------------------------------------------------------------------------
# This is the easy case.  There is only one index value.  We do check if the
# array element that contains it, exists.  If this is not the case, something
# has gone horribly wrong earlier and we need to bail out.
#------------------------------------------------------------------------------
          if (defined ($g_map_function_to_index{$routine}[0]))
            {
              $found_a_match = $TRUE;
              $ref_index = $g_map_function_to_index{$routine}[0]; 
              push (@function_index_list, $ref_index);
              my $final_function_name = $function_info[$ref_index]{"routine"};
              gp_message ("debugXL", $subr_name, "pushed single occurrence: ref_index = $ref_index final_function_name = $final_function_name");
            }
          }
      if (not $found_a_match)
#------------------------------------------------------------------------------
# This should not happen. All we can do is print an error message and stop.
#------------------------------------------------------------------------------
        {
          my $msg = "cannot find the index for $routine: found_a_match = ";
          $msg .= ($found_a_match == $TRUE) ? "TRUE" : "FALSE";
          gp_message ("assertion", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# The loop over all function names has completed and @function_index_list 
# contains the index values into @function_info for the functions.
#
# All we now need to do is to retrieve the correct field(s) from the array.
#------------------------------------------------------------------------------
  for my $i (keys @function_index_list)
    {
      my $index_for_function = $function_index_list[$i];
      push (@final_html_function_block, $function_info[$index_for_function]{"html function block"});
    }
  for my $i (keys @final_html_function_block)
    {
      my $txt = "final_html_function_block[$i] = $final_html_function_block[$i]";
      gp_message ("debugXL", $subr_name, $txt);
    }

#------------------------------------------------------------------------------
# Since the numbers are right aligned, we know that any difference between the
# metric line length and the maximum must be caused by the first column.  All
# we need to do is to prepend spaces in case of a difference.
#
# While we have the line with the metric values, we also replace ZZZ by 3
# spaces.
#------------------------------------------------------------------------------
    for my $i (keys @metric_values)
      {
        if (length ($metric_values[$i]) < $max_metrics_length)
          {
            my $pad = $max_metrics_length - length ($metric_values[$i]);
            my $spaces = "";
            for my $s (1 .. $pad)
              {
                $spaces .= "&nbsp;";
              }
            $metric_values[$i] = $spaces . $metric_values[$i];
          }
          $metric_values[$i] =~ s/ZZZ/&nbsp;&nbsp;&nbsp;/g;
      }

#------------------------------------------------------------------------------
# Determine the column widths.  The start and end index of the words in the
# input line are stored in elements 0 and 1 of @word_index_values.
#
# The assumption made is that the first digit of a metric value on the first
# line is left # aligned with the header text.  These are the Total values
# and other than for some derived metrics, e.g. CPI, should be the largest.
#
# The positions of the start of the value is what we should then use for the
# word "(sort)" to start.
#
# For example:
#
# Excl.     Excl. CPU  Excl.         Excl.         Excl.  Excl.
# Total     Cycles     Instructions  Last-Level    IPC    CPI
# CPU sec.     sec.    Executed      Cache Misses
# 174.664   179.250    175838403203  1166209617    0.428   2.339
#------------------------------------------------------------------------------

    my $foundit_ref;
    my $foundit;
    my @index_values = ();
    my $index_values_ref;

#------------------------------------------------------------------------------
# Search for "Excl." in the top row.  The metric values are aligned with this
# word and we can use it to position "(sort)" in the last header line.
#
# In @index_values, we store the position(s) of "Excl." in the header line.
# If none can be found, an exception is raised because at least one should
# be there.
#
# TBD: Check if this can be done only once.
# ------------------------------------------------------------------------------
    my $target_keyword = "Excl.";

    ($foundit_ref, $index_values_ref) = find_keyword_in_string (
                                          \$remaining_part_header, 
                                          \$target_keyword);

    $foundit      = ${ $foundit_ref };
    @index_values = @{ $index_values_ref };

    if ($foundit) 
      {
        for my $i (keys @index_values)
          {
            my $txt = "index_values[$i] = $index_values[$i]";
            gp_message ("debugXL", $subr_name, $txt);
          }
      }
    else
      {
        my $msg = "keyword $target_keyword not found in $remaining_part_header";
        gp_message ("assertion", $subr_name, $msg);
      }

# ------------------------------------------------------------------------------
# Compute the number of spaces we need to add between the "(sort)" strings.
#
# For example:
#
# 01234567890123456789
#
# Excl.         Excl.
# (sort)        (sort)
#       xxxxxxxx
#
# The number of spaces required is 14 - 6 = 8.
#
# The number of spaces to be added is stored in @padding_values.  These are
# the spaces to be added before the occurrence of "(sort)".  This is why the
# first padding value is 0.
# ------------------------------------------------------------------------------

# ------------------------------------------------------------------------------
# TBD: This needs to be done only once.
# ------------------------------------------------------------------------------
    my @padding_values = ();
    my $P_previous     = 0;
    for my $i (keys @index_values)
      {
        my $L = $index_values[$i];
        my $P = $L + length ("(sort)");
        my $pad_spaces = $L - $P_previous;

        push (@padding_values, $pad_spaces);

        $P_previous = $P;
      }

    for my $i (keys @padding_values)
      {
        my $txt = "padding_values[$i] = $padding_values[$i]";
        gp_message ("debugXL", $subr_name, $txt);
      }
    
#------------------------------------------------------------------------------
# Build up the sort line.  Mark the current metric and make sure the line is
# aligned with the header.
#------------------------------------------------------------------------------
    my $sort_string = "(sort)";
    my $length_sort_string = length ($sort_string);
    my $sort_line = "";
    my @active_metrics = split (":", $summary_metrics);
    for my $i (0 .. $number_of_metrics-1)
      {
        my $pad          = $padding_values[$i];
        my $metric_value = $active_metrics[$i];

        my $spaces = "";
        for my $s (1 .. $pad)
          {
            $spaces .= "&nbsp;";
          }

        gp_message ("debugXL", $subr_name, "i = $i metric_value = $metric_value pad = $pad");

        if ($metric_value eq $exp_type)
#------------------------------------------------------------------------------
# The current metric should have a different background color.
#------------------------------------------------------------------------------
          {
            $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} . 
                           "." . $metric_value . ".html' style='background-color:" . 
                           $g_html_color_scheme{"background_selected_sort"} . 
                           "\'><b>(sort)</b></a>";
          }
        elsif (($exp_type eq "functions") and ($metric_value eq $g_first_metric))
#------------------------------------------------------------------------------
# Set the background color for the sort metric in the main function overview. 
#------------------------------------------------------------------------------
          {
            $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} . 
                           "." . $metric_value . ".html' style='background-color:" . 
                           $g_html_color_scheme{"background_selected_sort"} . 
                           "'><b>(sort)</b></a>";
          }
        else
#------------------------------------------------------------------------------
# Do not set a specific background for all other metrics.
#------------------------------------------------------------------------------
          {
            $sort_string = "<a href=\'" . $g_html_base_file_name{"function_view"} . 
                           "." . $metric_value . ".html'>(sort)</a>";
          }

#------------------------------------------------------------------------------
# Prepend the spaces to ensure correct alignment with the rest of the header.
#------------------------------------------------------------------------------
          $sort_line .= $spaces . $sort_string;
      }

    push (@header_lines, $sort_line);

#------------------------------------------------------------------------------
# Print the final results for the header and metrics.
#------------------------------------------------------------------------------
  for my $i (keys @header_lines)
    {
      gp_message ("debugXL", $subr_name, "header_lines[$i] = $header_lines[$i]");
    }
  for my $i (keys @metric_values)
    {
      gp_message ("debugXL", $subr_name, "metric_values[$i] = $metric_values[$i]");
    }

#------------------------------------------------------------------------------
# Construct the lines for the function overview.
#
# TBD: We could eliminate two structures here because metric_values and
# final_html_function_block are only copied and the result stored.
#------------------------------------------------------------------------------
   for my $i (keys @function_names)
      {
        push (@metrics_part, $metric_values[$i]);
        push (@function_view_array, $final_html_function_block[$i]);
      }

  for my $i (0 .. $#function_view_array)
    {
      my $msg = "function_view_array[$i] = $function_view_array[$i]";
      gp_message ("debugXL", $subr_name, $msg);
    }
#------------------------------------------------------------------------------
# Element "function table" contains the array with all the function view data. 
#------------------------------------------------------------------------------
  $function_view_structure{"header"}         = [@header_lines];
  $function_view_structure{"metrics part"}   = [@metrics_part];
  $function_view_structure{"function table"} = [@function_view_array];

  return (\%function_view_structure);

} #-- End of subroutine process_function_overview

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub process_metrics
{
  my $subr_name = get_my_name ();

  my ($input_string, $sort_fields_ref, $metric_description_ref, $ignored_metrics_ref) = @_;

  my @sort_fields        = @{ $sort_fields_ref };
  my %metric_description = %{ $metric_description_ref };
  my %ignored_metrics    = %{ $ignored_metrics_ref };

  my $outputdir = append_forward_slash ($input_string);
  my $LANG      = $g_locale_settings{"LANG"};
  my $max_len   = 0;
  my $metric_comment;

  my ($imetricn,$outfile);
  my ($html_metrics_record,$imetric,$metric);

  $html_metrics_record =
    "<!doctype html public \"-//w3c//dtd html 3.2//EN\">\n<html lang=\"$LANG\">\n<head>\n" . 
    "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n" .
    "<title>Function Metrics</title></head><body lang=\"$LANG\" bgcolor=".$g_html_color_scheme{"background_color_page"}."<pre>\n";

  $outfile = $outputdir . "metrics.html";

  open (METRICSOUT, ">", $outfile) 
    or die ("$subr_name - unable to open file $outfile for writing - '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile for writing");

  for $metric (@sort_fields)
    {
      $max_len = max ($max_len, length ($metric));
      gp_message ("debug", $subr_name, "sort_fields: metric = $metric max_len = $max_len");
    }

# TBD: Check this
#  for $imetric (@IMETRICS)
  for $imetric (keys %ignored_metrics)
    {
      $max_len = max ($max_len, length ($imetric));
      gp_message ("debug", $subr_name, "ignored_metrics imetric = $imetric max_len = $max_len");
    }

  $max_len++;

  gp_message ("debug", $subr_name, "max_len = $max_len");

  $html_metrics_record .= "<p style=\"font-size:14px;color:red\"> Metrics used (".($#sort_fields + 1).")\n</p><p style=\"font-size:14px\">";
  for $metric (@sort_fields)
    {
      my $description = ${ retrieve_metric_description (\$metric, \%metric_description) };
      gp_message ("debug", $subr_name, "handling metric metric = $metric->$description");
      $html_metrics_record .= "       $metric".(' ' x ($max_len - length ($metric)))."$description\n";
    }

#  $imetricn = scalar (keys %IMETRICS);
  $imetricn = scalar (keys %ignored_metrics);
  if ($imetricn) 
    {
      $html_metrics_record .= "</p><p style=\"font-size:14px;color:red\"> Metrics ignored ($imetricn)\n</p><p style=\"font-size:14px\">";
#      for $imetric (sort keys %IMETRICS){
      for $imetric (sort keys %ignored_metrics)
        {
              $metric_comment = "(inclusive, exclusive, and percentages)";
          $html_metrics_record .= "       $imetric".(' ' x ($max_len - length ($imetric))).$metric_comment."\n";
          gp_message ("debug", $subr_name, "handling metric imetric = $imetric $metric_comment");
        }
    }

  print METRICSOUT $html_metrics_record;
  print METRICSOUT $g_html_credits_line;
  close (METRICSOUT);

  gp_message ("debug", $subr_name, "closed metrics file $outfile");

  return (0);

} #-- End of subroutine process_metrics

#-------------------------------------------------------------------------------
# TBD
#-------------------------------------------------------------------------------
sub process_metrics_data
{
  my $subr_name = get_my_name ();

  my ($outfile1, $outfile2, $ignored_metrics_ref) = @_;

  my %ignored_metrics    = %{ $ignored_metrics_ref };

  my %metric_value       = ();
  my %metric_description = ();
  my %metric_found       = ();

  my $user_metrics;
  my $system_metrics;
  my $wall_metrics;
  my $metric_spec;
  my $metric_flavor;
  my $metric_visibility;
  my $metric_name;
  my $metric_text;
  my $metricdata;
  my $metric_line; 

  my $summary_metrics;
  my $detail_metrics;
  my $detail_metrics_system;
  my $call_metrics;

  if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
    {
      gp_message ("debug", $subr_name, "g_user_settings{default_metrics}{current_value} = " . $g_user_settings{"default_metrics"}{"current_value"});
  # get metrics

      $summary_metrics='';
      $detail_metrics='';
      $detail_metrics_system='';
      $call_metrics = '';
      $user_metrics=0;
      $system_metrics=0;
      $wall_metrics=0;

      my ($last_metric,$metric,$value,$i,$r);

      open (METRICTOTALS, "<", $outfile2) 
        or die ("Unable to open metric value data file $outfile2 for reading - '$!'");
      gp_message ("debug", $subr_name, "opened $outfile2 to parse metric value data");

#------------------------------------------------------------------------------
# Below an example of the file that has just been opened.  The lines I marked 
# with a * has been wrapped by my for readability.  This is not the case in the
# file, but makes for a really long line.
#
# Also, the data comes from one PC experiment and two HWC experiments.
#------------------------------------------------------------------------------
# <Total>
#              Exclusive Total CPU Time:      32.473 (100.0%)
#              Inclusive Total CPU Time:      32.473 (100.0%)
#                  Exclusive CPU Cycles:      23.586 (100.0%)
#                               " count: 47054706905
#                  Inclusive CPU Cycles:      23.586 (100.0%)
#                               " count: 47054706905
#       Exclusive Instructions Executed: 54417033412 (100.0%)
#       Inclusive Instructions Executed: 54417033412 (100.0%)
#     Exclusive Last-Level Cache Misses:   252730685 (100.0%)
#     Inclusive Last-Level Cache Misses:   252730685 (100.0%)
#  *   Exclusive Instructions Per Cycle:      Inclusive Instructions Per Cycle:      
#  *         Exclusive Cycles Per Instruction:      
#  *         Inclusive Cycles Per Instruction:                                  
#  *         Size:           0
#                            PC Address: 1:0x00000000
#                           Source File: (unknown)
#                           Object File: (unknown)
#                           Load Object: <Total>
#                          Mangled Name:
#                               Aliases:
#------------------------------------------------------------------------------

      while (<METRICTOTALS>)
        {
          $metricdata = $_; chomp ($metricdata);
          gp_message ("debug", $subr_name, "file metrictotals: $metricdata");

#------------------------------------------------------------------------------
# Ignoring whitespace, search for any line with a ":" in it, followed by 
# a number with or without a dot.  So, an integer or floating-point number.
#------------------------------------------------------------------------------
          if ($metricdata =~ /\s*(.*):\s+(\d+\.*\d*)/)
            {
              gp_message ("debug", $subr_name, "  candidate => $metricdata");
              $metric = $1;
              $value  = $2;
              if ( ($metric eq "PC Address") or ($metric eq "Size"))
                {
                  gp_message ("debug", $subr_name, "  skipped => $metric $value");
                  next;
                }
              gp_message ("debug", $subr_name, "  proceed => $metric $value");
              if ($metric eq '" count')
#------------------------------------------------------------------------------
# Hardware counter experiments have this info.  Note that this line is not the
# first one to be encountered, so $last_metric has been defined already.
#------------------------------------------------------------------------------
                {
                  $metric = $last_metric." Count"; # we presume .......
                  gp_message ("debug", $subr_name, "last_metric = $last_metric metric = $metric");
                }
              $i=index ($metricdata,":");
              $r=rindex ($metricdata,":");
              gp_message ("debug", $subr_name, "metricdata = $metricdata => i = $i r = $r");
              if ($i == $r)
                {
                  if ($value > 0) # Not interested in metrics contributing zero
                    {
                      $metric_value{$metric} = $value;
                      gp_message ("debug", $subr_name, "archived metric_value{$metric} = $metric_value{$metric}");
                      # e.g. $metric_value{Exclusive Total Thread Time} = 302.562
                      # e.g. $metric_value{Exclusive Instructions Executed} = 2415126222484
                    }
                }
              else
#------------------------------------------------------------------------------
# TBD This code deals with an old bug and may be removed.
#------------------------------------------------------------------------------
                { # er_print bug - e.g.
#  Exclusive Instructions Per Cycle:       Inclusive Instructions Per Cycle:       Exclusive Cycles Per Instruction:   Inclusive Cycles Per Instruction:             Exclusive OpenMP Work Time: 162.284 (100.0%)
                  gp_message ("debug", $subr_name, "metrictotals odd line:->$metricdata<-");
                  $r=rindex ($metricdata,":",$r-1);
                  if ($r == -1)
                    { # ignore
                      gp_message ("debug", $subr_name, "metrictotals odd line ignored<-");
                      $last_metric = "foo";
                      next;
                    }
                  my ($good_part)=substr ($metricdata,$r+1);
                  if ($good_part =~ /\s*(.*):\s+(\d+\.*\d*)/)
                    {
                      $metric = $1;
                      $value  = $2;
                      if ($value>0) # Not interested in metrics contributing zero
                        {
                          $metric_value{$metric} = $value;
                          my $msg = "metrictotals odd line rescued '$metric'=$value";
                          gp_message ("debug", $subr_name, $msg);
                        }
                    }
                }
#------------------------------------------------------------------------------
# Preserve the current metric.
#------------------------------------------------------------------------------
              $last_metric = $metric;
            }
        }
      close (METRICTOTALS);
    }

    if (scalar (keys %metric_value) == 0)
#------------------------------------------------------------------------------
# If we have no metrics > 0, fudge a "Exclusive Total CPU Time", else we 
# blow up later.
#
# TBD: See if this can be handled differently.
#------------------------------------------------------------------------------
      {
        $metric_value{"Exclusive Total CPU Time"} = 0;
        gp_message ("debug", $subr_name, "no metrics found and a stub was added");
      }

  for my $metric (sort keys %metric_value)
    {
      gp_message ("debug", $subr_name, "Stored metric_value{$metric} = $metric_value{$metric}");
    }

  gp_message ("debug", $subr_name, "proceed to process file $outfile1");

#------------------------------------------------------------------------------
# Open and process the metrics file.
#------------------------------------------------------------------------------
  open (METRICS, "<", $outfile1)
    or die ("Unable to open metrics file $outfile1: '$!'");
  gp_message ("debug", $subr_name, "opened file $outfile1 for reading");

#------------------------------------------------------------------------------
# Parse the file.  This is a typical example:
#
# Exp Sel Total
# === === =====
#   1 all     2
#   2 all     1
#   3 all     2
# Current metrics: e.totalcpu:i.totalcpu:e.cycles:e+insts:e+llm:name
# Current Sort Metric: Exclusive Total CPU Time ( e.totalcpu )
# Available metrics:
#          Exclusive Total CPU Time: e.%totalcpu
#          Inclusive Total CPU Time: i.%totalcpu
#              Exclusive CPU Cycles: e.+%cycles
#              Inclusive CPU Cycles: i.+%cycles
#   Exclusive Instructions Executed: e+%insts
#   Inclusive Instructions Executed: i+%insts
# Exclusive Last-Level Cache Misses: e+%llm
# Inclusive Last-Level Cache Misses: i+%llm
#  Exclusive Instructions Per Cycle: e+IPC
#  Inclusive Instructions Per Cycle: i+IPC
#  Exclusive Cycles Per Instruction: e+CPI
#  Inclusive Cycles Per Instruction: i+CPI
#                              Size: size
#                        PC Address: address
#                              Name: name
#------------------------------------------------------------------------------
  while (<METRICS>)
    {
      $metric_line = $_;
      chomp ($metric_line);

      gp_message ("debug", $subr_name, "processing line $metric_line");
#------------------------------------------------------------------------------
# The original regex has bugs because the line should not be allowed to start
# with a ":".  So this is wrong:
#  if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
# 
# This is better:
#      if (($metric =~ /\s*(.+):\s+(\S)((\.\S+)|(\+\S+))/) and !($metric =~/^Current/))
#
# In general, this regex has some potential issues and has been replaced by
# the one shown below.
#
# We select a line that does not start with "Current" and aside from whitespace
# starts with anything (although it should be a string with words only),
# followed by whitespace and either an "e" or "i". This is called the "flavor"
# and is followed by a visibility marker (.,+,%, or !) and a metric name.
#------------------------------------------------------------------------------
# Ruud   if (($metric =~ /\s*(.*):\s+(\S)((\.\S+)|(\+\S+))/) && !($metric =~/^Current/)){

      ($metric_spec, $metric_flavor, $metric_visibility, $metric_name, $metric_text) = 
              extract_metric_specifics ($metric_line);

#      if (($metric_line =~ /\s*(.+):\s+([ei])([\.\+%]+)(\S*)/) and !($metric_line =~/^Current/))
      if ($metric_spec eq "skipped")
        {
          gp_message ("debug", $subr_name, "skipped line: $metric_line");
        }
      else
        {
          gp_message ("debug", $subr_name, "line of interest: $metric_line");

          $metric_found{$metric_spec} = 1;

          if ($g_user_settings{"ignore_metrics"}{"defined"})
            {
              gp_message ("debug", $subr_name, "check for $metric_spec");
              if (exists ($ignored_metrics{$metric_name}))
                {
                  gp_message ("debug", $subr_name, "user asked to ignore metric $metric_name");
                  next;
                }
              }

#------------------------------------------------------------------------------
# This metric is not on the ignored list and qualifies, so store it.
#------------------------------------------------------------------------------
          $metric_description{$metric_spec} = $metric_text;

# TBD: add for other visibilities too, like +
          gp_message ("debug", $subr_name, "stored $metric_description{$metric_spec}          = $metric_description{$metric_spec}");

          if ($metric_flavor ne "e")
            {
              gp_message ("debug", $subr_name, "metric $metric_spec is ignored");
            }
          else
#------------------------------------------------------------------------------
# Only the exclusive metrics are shown.
#------------------------------------------------------------------------------
            {
              gp_message ("debug", $subr_name, "metric $metric_spec ($metric_text) is considered");

              if ($metric_spec =~ /user/)
                {
                  $user_metrics = $TRUE;
                  gp_message ("debug", $subr_name, "m: user_metrics set to TRUE");
                } 
              elsif ($metric_spec =~ /system/)
                { 
                  $system_metrics = $TRUE;
                  gp_message ("debug", $subr_name, "m: system_metrics set to TRUE");
                } 
              elsif ($metric_spec =~ /wall/)
                {
                  $wall_metrics = $TRUE;
                  gp_message ("debug", $subr_name, "m: wall_metrics set to TRUE");
                } 
#------------------------------------------------------------------------------
# TBD I don't see why these need to be skipped.  Also, should be totalcpu.
#------------------------------------------------------------------------------
              elsif (($metric_spec =~ /^e\.total$/) or ($metric_spec =~/^e\.total_cpu$/))
                {
                # skip total thread time and total CPU time
                  gp_message ("debug", $subr_name, "m: skip above");
                }
              elsif (defined ($metric_value{$metric_text}))
                {
                  gp_message ("debug", $subr_name, "Total attributed to this metric metric_value{$metric_text} = $metric_value{$metric_text}");
                  if ($summary_metrics ne '')
                    {
                      $summary_metrics = $summary_metrics.':'.$metric_spec;
                      gp_message ("debug", $subr_name, "updated summary_metrics = $summary_metrics - 1");
                      if ($metric_spec !~ /\.wait$|\.ulock$|\.text$|\.data$|\.owait$|total$|mpiwork$|mpiwait$|ompwork$|ompwait$/)
                        {
                          $detail_metrics = $detail_metrics.':'.$metric_spec;
                          gp_message ("debug", $subr_name, "updated m:detail_metrics=$detail_metrics - 1");
                          $detail_metrics_system = $detail_metrics_system.':'.$metric_spec;
                          gp_message ("debug", $subr_name, "updated m:detail_metrics_system=$detail_metrics_system - 1");
                        } 
                      else
                        {
                          gp_message ("debug", $subr_name, "m: no want above metric for detail_metrics or detail_metrics_system");
                        }
                    } 
                  else 
                    {
                      $summary_metrics = $metric_spec;
                      gp_message ("debug", $subr_name, "initialized summary_metrics = $summary_metrics - 2");
                      if ($metric_spec !~ /\.wait$|\.ulock$|\.text$|\.data$|\.owait$|total$|mpiwork$|mpiwait$|ompwork$|ompwait$/)
                        {
                          $detail_metrics = $metric_spec;
                          gp_message ("debug", $subr_name, "m:detail_metrics=$detail_metrics - 2");
                          $detail_metrics_system = $metric_spec;
                          gp_message ("debug", $subr_name, "m:detail_metrics_system=$detail_metrics_system - 2");
                        } 
                      else 
                        {
                          gp_message ("debug", $subr_name, "m: no want above metric for detail_metrics or detail_metrics_system");
                        }
                    }
                  gp_message ("debug", $subr_name, " metric $metric_spec added");
                } 
              else 
                {
                  gp_message ("debug", $subr_name, "m: no want above metric was a 0 total");
                }
            } 
        }
    }

  close METRICS;

  if ($wall_metrics > 0)
    {
      gp_message ("debug", $subr_name,"m:wall_metrics set adding to summary_metrics");
      $summary_metrics = "e.wall:".$summary_metrics;
      gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 3");
    }

  if ($system_metrics > 0)
    {
      gp_message ("debug", $subr_name,"m:system_metrics set adding to summary_metrics,call_metrics and detail_metrics_system");
      $summary_metrics       = "e.system:".$summary_metrics;
      $call_metrics          = "i.system:".$call_metrics;
      $detail_metrics_system ='e.system:'.$detail_metrics_system;

      gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 4");
      gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics");
      gp_message ("debug", $subr_name,"m:detail_metrics_system=$detail_metrics_system - 3");
    }


#------------------------------------------------------------------------------
# TBD: e.user and i.user do not always exist!!
#------------------------------------------------------------------------------

  if ($user_metrics > 0)
    {
      gp_message ("debug", $subr_name,"m:user_metrics set adding to summary_metrics,detail_metrics,detail_metrics_system and call_metrics");
# Ruud      if (!exists ($IMETRICS{"i.user"})){
      if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{"user"}))
        {
          $summary_metrics = "e.user:".$summary_metrics;
        }
      else 
        {
          $summary_metrics = "e.user:i.user:".$summary_metrics;
        }
      $detail_metrics        = "e.user:".$detail_metrics;
      $detail_metrics_system = "e.user:".$detail_metrics_system;

      gp_message ("debug", $subr_name,"m:summary_metrics=$summary_metrics - 5");
      gp_message ("debug", $subr_name,"m:detail_metrics=$detail_metrics - 3");
      gp_message ("debug", $subr_name,"m:detail_metrics_system=$detail_metrics_system - 4");

      if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{"user"}))
        {
          $call_metrics = "a.user:".$call_metrics;
        } 
      else 
        {
          $call_metrics = "a.user:i.user:".$call_metrics;
        }
      gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics - 2");
    }

  if ($call_metrics eq "")
    {
      $call_metrics = $detail_metrics;

      gp_message ("debug", $subr_name,"m:call_metrics is not set, setting it to detail_metrics ");
      gp_message ("debug", $subr_name,"m:call_metrics=$call_metrics - 3");
    }

  for my $metric (sort keys %ignored_metrics)
    {
      if ($ignored_metrics{$metric})
        {
          gp_message ("debug", $subr_name, "active metric, but ignored: $metric");
        }

    }

  return (\%metric_value, \%metric_description, \%metric_found, $user_metrics, $system_metrics, $wall_metrics,
          $summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics);

} #-- End of subroutine process_metrics_data

#------------------------------------------------------------------------------
# Process source lines that are not part of the target function.
#
# Generate straightforward HTML, but define an anchor based on the source line
# number in the list.
#------------------------------------------------------------------------------
sub process_non_target_source
{
  my $subr_name = get_my_name ();

  my ($start_scan, $end_scan, 
      $src_times_regex, $function_regex, $number_of_metrics,
      $file_contents_ref, $modified_html_ref) = @_;

  my @file_contents = @{ $file_contents_ref };
  my @modified_html = @{ $modified_html_ref };
  my $colour_code_line = $FALSE;
  my $input_line;
  my $line_id;
  my $modified_line;

#------------------------------------------------------------------------------
# Main loop to parse all of the source code and take action as needed.
#------------------------------------------------------------------------------
  for (my $line_no=$start_scan; $line_no <= $end_scan; $line_no++)
    {
      $input_line = $file_contents[$line_no];

#------------------------------------------------------------------------------
# Generate straightforward HTML, but define an anchor based on the source line
# number in the list.
#------------------------------------------------------------------------------
      $line_id = extract_source_line_number ($src_times_regex, 
                                             $function_regex, 
                                             $number_of_metrics, 
                                             $input_line);

      if ($input_line =~ /$function_regex/)
        {
          $colour_code_line = $TRUE;
        }

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
      $input_line =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

#------------------------------------------------------------------------------
# Add an id.
#------------------------------------------------------------------------------
      $modified_line = "<a id=\"line_" . $line_id . "\"></a>";

      my $coloured_line; 
      if ($colour_code_line)
        {
          my $boldface = $TRUE;
          $coloured_line = color_string (
                             $input_line, 
                             $boldface, 
                             $g_html_color_scheme{"non_target_function_name"});
          $colour_code_line = $FALSE;
          $modified_line .= "$coloured_line";
        }
      else
        {
          $modified_line .= "$input_line";
        }
      gp_message ("debugXL", $subr_name, " $line_no : modified_line = $modified_line");
      push (@modified_html, $modified_line);
    }

  return (\@modified_html);

} #-- End of subroutine process_non_target_source

#------------------------------------------------------------------------------
# This function scans the configuration file and adapts the internal settings
# accordingly.
#
# Errors are stored during the parsing and processing phase.  They are printed
# at the end and sorted by line number.
#------------------------------------------------------------------------------
sub process_rc_file
{
  my $subr_name = get_my_name ();

  my ($rc_file_name, $rc_file_paths_ref) = @_;

#------------------------------------------------------------------------------
# Local structures.
#------------------------------------------------------------------------------
  my %rc_settings_user = ();  #-- Store the values extracted from the config file
  my %error_and_warning_msgs = ();
  my @rc_file_paths = (); 

  my @split_line;
  my @my_fields;

  my $message;
  my $first_part; 
  my $line;
  my $line_number;
  my $number_of_fields; 
  my $number_of_paths; 
  my $parse_errors;   #-- Count the number of errors
  my $parse_warnings; #-- Count the number of errors

  my $rc_config_file;
  my $rc_file_found;
  my $rc_keyword;
  my $rc_value;

  @rc_file_paths   = @{$rc_file_paths_ref};
  $number_of_paths = scalar (@rc_file_paths);

  if ($number_of_paths == 0)
#------------------------------------------------------------------------------
# This should not happen, but is a good safety net to add.
#------------------------------------------------------------------------------
    {
      my $msg = "search path list is empty";
      gp_message ("assertion", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Check for the presence of a configuration file.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "number_of_paths = $number_of_paths rc_file_paths = @rc_file_paths");

  $rc_file_found = $FALSE;
  for my $path_name (@rc_file_paths)
    {
      $rc_config_file = $path_name . "/" . $rc_file_name;
      gp_message ("debug", $subr_name, "looking for configuration file $rc_config_file");
      if (-f $rc_config_file) 
        {
          gp_message ("debug", $subr_name, "found configuration file $rc_config_file");
          $rc_file_found  = $TRUE;
          last;
        }
    }

  if (not $rc_file_found)
#------------------------------------------------------------------------------
# There is no configuration file and we can skip this subroutine.
#------------------------------------------------------------------------------
    {
      gp_message ("verbose", $subr_name, "Configuration file $rc_file_name not found");
      return (0);
    }
  else
    {
      open (GP_DISPLAY_HTML_RC, "<", "$rc_config_file")
        or die ("$subr_name - unable to open file $rc_config_file for reading: $!");
#------------------------------------------------------------------------------
# The configuration file has been opened for reading.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name, "file $rc_config_file has been opened for reading");
    }

  gp_message ("verbose", $subr_name, "Found configuration file $rc_config_file");
  gp_message ("debug",   $subr_name, "processing configuration file $rc_config_file");

#------------------------------------------------------------------------------
# Here we scan the configuration file for the settings.
#
# A setting consists of a keyword, optionally followed by a value.  It is
# optional because not all keywords may require a value.
#
# At the end of this block, all keyword/value pairs are stored in a hash.
#
# We do not yet check for the validity of these pairs. This is done next.
#
# The original code had this all integrated, but it made the code very
# complex with deeply nested if-statements. The flow was also hard to follow.
#------------------------------------------------------------------------------
  $parse_errors   = 0;
  $parse_warnings = 0;
  $line_number    = 0;
  while (my $line = <GP_DISPLAY_HTML_RC>)
    {
      chomp ($line);
      $line_number++;

      gp_message ("debug", $subr_name, "read input line = $line");

#------------------------------------------------------------------------------
# Ignore a line with whitespace only
#------------------------------------------------------------------------------
      if ($line =~ /^\s*$/)
        {
          gp_message ("debug", $subr_name, "ignored a line with whitespace");
          next;
        }

#------------------------------------------------------------------------------
# Ignore a comment line, defined by starting with a "#", possibly prepended by
# whitespace.
#------------------------------------------------------------------------------
      if ($line =~ /^\s*\#/)
        {
          gp_message ("debug", $subr_name, "ignored a full comment line");
          next;
        }

#------------------------------------------------------------------------------
# Split the input line using the "#" symbol as a separator.  We have already 
# handled the case of an isolated comment line, so there may only be an 
# embedded comment.
#
# Regardless of this, we are only interested in the first part.
#------------------------------------------------------------------------------
      @split_line = split ("#", $line);

      for my $i (@split_line)
        {
          gp_message ("debug", $subr_name, "elements after split of line: $i");
        }

      $first_part = $split_line[0];
      gp_message ("debug", $subr_name, "relevant part = $first_part");

      if ($first_part =~ /[&\^\*\@\$]+/)
#------------------------------------------------------------------------------
# The &, ^, *, @ and $ symbols should not occur.  If they do, we flag an error
# an fetch the next line.
#------------------------------------------------------------------------------
        {
          $parse_errors++;
          $message = "non-supported character(s) (\&,\^,\*,\@,\$) found: $line"; 
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $message;
          next;
        }
      else
#------------------------------------------------------------------------------
# Split the first part on whitespace and verify the number of fields to be
# valid.  Although we currently only have keywords with a value, a keyword
# without value is supported to.
#
# If the number of fields is valid, the keyword and value are stored.  In case
# of a single field, the value is assigned a special string.
#
# Although this situation should not occur, we do abort if something unexpected
# is encountered here.
#------------------------------------------------------------------------------
        {
          @my_fields = split (/\s/, $split_line[0]);

          $number_of_fields = scalar (@my_fields);
          gp_message ("debug", $subr_name, "number of fields = $number_of_fields");
        }

      if ($number_of_fields ge 3) 
#------------------------------------------------------------------------------
# This is not supported.
#------------------------------------------------------------------------------
        {
          $parse_errors++;
          $message = "more than 2 fields found: $first_part"; 
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $message;
          next;
        }
      elsif ($number_of_fields eq 2)
        {
          $rc_keyword = $my_fields[0];
          $rc_value   = $my_fields[1];
        }
      elsif ($number_of_fields eq 1)
        {
          $rc_keyword = $my_fields[0];
          $rc_value   = "the_field_is_empty";
        }
      else
        {
          my $msg = "[line $line_number] $rc_config_file - number of fields = $number_of_fields";
          gp_message ("assertion", $subr_name, $msg);
        }

#------------------------------------------------------------------------------
# Store the keyword, value and line number. 
#------------------------------------------------------------------------------
      if (exists ($rc_settings_user{$rc_keyword}))
        {
          $parse_warnings++;
          my $prev_value = $rc_settings_user{$rc_keyword}{"value"};
          my $prev_line_number = $rc_settings_user{$rc_keyword}{"line_no"};
          if ($rc_value ne $prev_value)
            {
              $message = "option $rc_keyword previously set at line $prev_line_number: new value '$rc_value' overrides '$prev_value'";
            }
          else
            {
              $message = "option $rc_keyword previously set to the same value at line $prev_line_number";
            }
          $error_and_warning_msgs{"warning"}{$line_number}{"message"} = $message;
        }
      $rc_settings_user{$rc_keyword}{"value"}   = $rc_value;
      $rc_settings_user{$rc_keyword}{"line_no"} = $line_number;

      gp_message ("debug", $subr_name, "stored keyword     = $rc_keyword"); 
      gp_message ("debug", $subr_name, "stored value       = $rc_value"); 
      gp_message ("debug", $subr_name, "stored line number = $line_number"); 
    }

#------------------------------------------------------------------------------
# Completed the parsing of the configuration file. It can be closed.
#------------------------------------------------------------------------------
  close (GP_DISPLAY_HTML_RC);

#------------------------------------------------------------------------------
# Print the raw input as just collected from the configuration file.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "contents of %rc_settings_user:");
  for my $keyword (keys %rc_settings_user)
    {
      my $key_value = $rc_settings_user{$keyword}{"value"};
      gp_message ("debug", $subr_name, "keyword = $keyword value = $key_value");
    }

  for my $rc_keyword  (keys %g_user_settings)
    {
       for my $fields (keys %{ $g_user_settings{$rc_keyword} })
         {
           gp_message ("debug", $subr_name, "before config file: $rc_keyword $fields = $g_user_settings{$rc_keyword}{$fields}");
         }
    }

#------------------------------------------------------------------------------
# We are almost done.  Check for all keywords found whether they are valid.  
# Also verify that the corresponding value is valid.
#
# Update the g_user_settings table if everything is okay.
#------------------------------------------------------------------------------

  for my $rc_keyword (keys %rc_settings_user)
    {
      my $rc_value = $rc_settings_user{$rc_keyword}{"value"};

      if (exists ( $g_user_settings{$rc_keyword}))
        {

#------------------------------------------------------------------------------
# This is a supported keyword.  There are two more things left to do:
# - Check how many values it requires (currently exactly one is supported)
# - Is the value a valid number or string?
#------------------------------------------------------------------------------
          my $no_of_arguments = $g_user_settings{$rc_keyword}{"no_of_arguments"};

          if ($no_of_arguments eq 1)
            {
              my $input_value = $rc_value;
              if ($input_value ne "the_field_is_empty")
#
#------------------------------------------------------------------------------
# So far, so good.  We only need to check if the value is valid for the keyword.
#------------------------------------------------------------------------------
                {
                  my $data_type   = $g_user_settings{$rc_keyword}{"data_type"};
                  my $valid_input = verify_if_input_is_valid ($input_value, $data_type);
#------------------------------------------------------------------------------
# Check if the value is valid.
#------------------------------------------------------------------------------
                  if ($valid_input)
                    {
                      $g_user_settings{$rc_keyword}{"current_value"} = $rc_value;
                      $g_user_settings{$rc_keyword}{"defined"}  = $TRUE;
                    }
                  else
                    {
                      $parse_errors++;
                      $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
                      $message = "input value '$input_value' for keyword $rc_keyword is not valid";
                      $error_and_warning_msgs{"error"}{$line_number}{"message"} = $message;
                      next;
                    }
                }
              else
#------------------------------------------------------------------------------
# This keyword requires a value, but none has been found.
#------------------------------------------------------------------------------
                {
                  $parse_errors++;
                  $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
                  $message = "missing value for keyword '$rc_keyword'";
                  $error_and_warning_msgs{"error"}{$line_number}{"message"} = $message;
                  next;
                }
            }
          elsif ($no_of_arguments eq 0)
#------------------------------------------------------------------------------
# Currently a theoretical scenario since all commands require a value, but in
# case this is no longer true, we need to at least flag the fact the user set
# this command.
#------------------------------------------------------------------------------
            {
              $g_user_settings{$rc_keyword}{"defined"}  = $TRUE;
            }
          else
#------------------------------------------------------------------------------
# The code is not prepared for the situation one command has multiple values,
# but this situation should never occur. Still it won't hurt to add a check.
#------------------------------------------------------------------------------
            {
               my $msg = "cannot handle $no_of_arguments in the input";
               gp_message ("assertion", $subr_name, $msg);
            }
        }
      else
#------------------------------------------------------------------------------
# A non-valid keyword is found. This is flagged as an error.
#------------------------------------------------------------------------------
        {
          $parse_errors++;
          $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
          $message = "keyword $rc_keyword is not supported";
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $message;
        }
    }
  for my $rc_keyword  (keys %g_user_settings)
    {
       for my $fields (keys %{ $g_user_settings{$rc_keyword} })
         {
           gp_message ("debug", $subr_name, "after config file: $rc_keyword $fields = $g_user_settings{$rc_keyword}{$fields}");
         }
    }
  print_table_user_settings ("debug", "upon the return from $subr_name");

  if ( ($parse_errors == 0) and ($parse_warnings == 0) )
    {
      gp_message ("verbose", $subr_name, "Successfully parsed and processed the configuration file");
    }
  else
    {
      if ($parse_errors > 0)
        {
          my $plural_or_single = ($parse_errors > 1) ? "errors" : "error";
          $message = $g_error_keyword . "found $parse_errors fatal $plural_or_single in the configuration file:";
          gp_message ("debug", $subr_name, $message);
#------------------------------------------------------------------------------
# Sort the hash keys, the line numbers, alphabetically and print the 
# corresponding error messages.
#------------------------------------------------------------------------------
          for my $line_no (sort {$a <=> $b} (keys %{ $error_and_warning_msgs{"error"} }))
            {
              $message  = $g_error_keyword. "[line $line_no] in file $rc_config_file - ";
              $message .= $error_and_warning_msgs{"error"}{$line_no}{"message"};
              gp_message ("debug", $subr_name, $message);
            }
        }

      if (not $g_quiet)
        {
          if ($parse_warnings > 0)
            {
              $message = $g_warn_keyword . "found $parse_warnings warnings in the configuration file:";
              gp_message ("debug", $subr_name, $message);
              for my $line_no (sort {$a <=> $b} (keys %{ $error_and_warning_msgs{"warning"} }))
                {
                  $message = $g_warn_keyword . "[line $line_no] in file $rc_config_file - ";
                  $message .= $error_and_warning_msgs{"warning"}{$line_no}{"message"};
                  gp_message ("debug", $subr_name, $message);
                }
            }
        }
    }

  return ($parse_errors);

} #-- End of subroutine process_rc_file

#------------------------------------------------------------------------------
# Generate the annotated html file for the source listing.
#------------------------------------------------------------------------------
sub process_source
{
  my $subr_name = get_my_name ();

  my ($number_of_metrics, $function_info_ref, 
      $outputdir, $input_filename) = @_;

  my @function_info = @{ $function_info_ref };

#------------------------------------------------------------------------------
# The regex section
#------------------------------------------------------------------------------
  my $end_src1_header_regex = '(^\s+)(\d+)\.\s+(.*)';
  my $end_src2_header_regex = '(^\s+)(<Function: )(.*)>';
  my $function_regex        = '^(\s*)<Function:\s(.*)>';
  my $function2_regex       = '^(\s*)&lt;Function:\s(.*)>';
  my $src_regex             = '(\s*)(\d+)\.(.*)';
  my $txt_ext_regex         = '\.txt$';
  my $src_filename_id_regex = '^file\.(\d+)\.src\.txt$';
  my $integer_only_regex    = '\d+';
#------------------------------------------------------------------------------
# Computed dynamically below.
# TBD: Try to move this up.
#------------------------------------------------------------------------------
  my $src_times_regex; 
  my $hot_lines_regex; 
  my $metric_regex; 
  my $metric_extra_regex;

  my @components = (); 
  my @fields_in_line = ();
  my @file_contents = ();
  my @hot_source_lines  = ();
  my @max_metric_values = ();
  my @modified_html = ();
  my @transposed_hot_lines = ();

  my $colour_coded_line; 
  my $colour_coded_line_ref; 
  my $line_id;
  my $ignore_value;
  my $func_name_in_src_file; 
  my $html_new_line = "<br>";
  my $input_line; 
  my $metric_values;
  my $modified_html_ref; 
  my $modified_line; 
  my $is_empty;
  my $start_all_source;
  my $start_target_source;
  my $end_target_source;
  my $output_line; 
  my $hot_line;
  my $src_line_no;
  my $src_code_line; 

  my $decimal_separator = $g_locale_settings{"decimal_separator"};
  my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};
 
  my $file_title;
  my $found_target; 
  my $html_dis_record; 
  my $html_end; 
  my $html_header;
  my $html_home; 
  my $rounded_percentage; 
  my $start_tracking; 
  my $threshold_line; 

  my $base;
  my $boldface;
  my $msg;
  my $routine;

  my $LANG      = $g_locale_settings{"LANG"};
  my $the_title = set_title ($function_info_ref, $input_filename, 
                             "process source");
  my $outfile   = $input_filename . ".html";

#------------------------------------------------------------------------------
# Remove the .txt from file.<n>.src.txt
#------------------------------------------------------------------------------
  my $html_output_file  = $input_filename;
  $html_output_file     =~ s/$txt_ext_regex/.html/; 

  gp_message ("debug", $subr_name, "input_filename = $input_filename");
  gp_message ("debug", $subr_name, "the_title = $the_title");

  $file_title  = $the_title;
  $html_header = ${ create_html_header (\$file_title) };
  $html_home   = ${ generate_home_link ("right") };

  push (@modified_html, $html_header);
  push (@modified_html, $html_home);
  push (@modified_html, "<pre>");

#------------------------------------------------------------------------------
# Open the html file used for the output.
#------------------------------------------------------------------------------
  open (NEW_HTML, ">", $html_output_file)
    or die ("$subr_name - unable to open file $html_output_file for writing: '$!'");
  gp_message ("debug", $subr_name , "opened file $html_output_file for writing");

  $base = get_basename ($input_filename);

  gp_message ("debug", $subr_name, "base = $base");

  if ($base =~ /$src_filename_id_regex/)
    {
      my $file_id = $1;
      if (defined ($function_info[$file_id]{"routine"}))
        {
          $routine = $function_info[$file_id]{"routine"};

          gp_message ("debugXL", $subr_name, "target routine = $routine");
        }
      else
        {
          my $msg = "cannot retrieve routine name for file_id = $file_id";
          gp_message ("assertion", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# Check if the input file is empty.  If so, generate a short text in the html
# file and return.  Otherwise open the file and read the contents.
#------------------------------------------------------------------------------
  $is_empty = is_file_empty ($input_filename);

  if ($is_empty)
    {
#------------------------------------------------------------------------------
# The input file is empty. Write a diagnostic message in the html file and exit.
#------------------------------------------------------------------------------
      gp_message ("debug", $subr_name ,"file $input_filename is empty");

      my $comment = "No source listing generated by $tool_name - " .
                    "file $input_filename is empty";
      my $error_file = $outputdir . "gp-listings.err";

      my $html_empty_file_ref = html_text_empty_file (\$comment, \$error_file);
      my @html_empty_file     = @{ $html_empty_file_ref };

      print NEW_HTML "$_\n" for @html_empty_file;

      close NEW_HTML;

      return (0);
    }
  else
#------------------------------------------------------------------------------
# Open the input file with the source code
#------------------------------------------------------------------------------
    {
      open (SRC_LISTING, "<", $input_filename) 
        or die ("$subr_name - unable to open file $input_filename for reading: '$!'");
      gp_message ("debug", $subr_name, "opened file $input_filename for reading");
    }

#------------------------------------------------------------------------------
# Generate the regex for the metrics.  This depends on the number of metrics.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "decimal_separator = $decimal_separator<--");

  $metric_regex = '';
  $metric_extra_regex = '';
  for my $metric_used (1 .. $number_of_metrics)
    {
      $metric_regex .= '(\d+' . $decimal_separator . '*\d*)\s+';
    }
  $metric_extra_regex = $metric_regex . '(\d+' . $decimal_separator . ')';

  $hot_lines_regex = '^(#{2})\s+';
  $hot_lines_regex .= '('.$metric_regex.')';
  $hot_lines_regex .= '([0-9?]+)\.\s+(.*)';

  $src_times_regex = '^(#{2}|\s{2})\s+';
  $src_times_regex .= '('.$metric_extra_regex.')';
  $src_times_regex .= '(.*)';

  gp_message ("debugXL", $subr_name, "metric_regex   = $metric_regex");
  gp_message ("debugXL", $subr_name, "hot_lines_regex = $hot_lines_regex");
  gp_message ("debugXL", $subr_name, "src_times_regex = $src_times_regex");
  gp_message ("debugXL", $subr_name, "src_regex      = $src_regex");

  gp_message ("debugXL", $subr_name, "end_src1_header_regex = $end_src1_header_regex");
  gp_message ("debugXL", $subr_name, "end_src2_header_regex = $end_src2_header_regex");
  gp_message ("debugXL", $subr_name, "function_regex = $function_regex");
  gp_message ("debugXL", $subr_name, "function2_regex = $function2_regex");
  gp_message ("debugXL", $subr_name, "src_regex = $src_regex");

#------------------------------------------------------------------------------
# Read the file into memory.
#------------------------------------------------------------------------------
  chomp (@file_contents = <SRC_LISTING>);

#------------------------------------------------------------------------------
# Identify the header lines.  Make the minimal assumptions.
#
# In both cases, the first line after the header has whitespace.  This is
# followed by either one of the following:
#
# - <line_no>. 
# - <Function:
#
# These are the characteristics we use below.
#------------------------------------------------------------------------------
  for (my $line_number=0; $line_number <= $#file_contents; $line_number++)
    {
      $input_line = $file_contents[$line_number];

#------------------------------------------------------------------------------
# We found the first source code line.  Bail out.
#------------------------------------------------------------------------------
      if (($input_line =~ /$end_src1_header_regex/) or
          ($input_line =~ /$end_src2_header_regex/))
        {
          gp_message ("debugXL", $subr_name, "header time is over - hit source line");
          gp_message ("debugXL", $subr_name, "line_number = $line_number");
          gp_message ("debugXL", $subr_name, "input_line = $input_line");
          last;
        }
      else
#------------------------------------------------------------------------------
# Store the header lines in the html structure.
#------------------------------------------------------------------------------
        {
          $modified_line = "<i>" . $input_line . "</i>";
          push (@modified_html, $modified_line); 
        }
    }
#------------------------------------------------------------------------------
# We know the source code starts at this index value:
#------------------------------------------------------------------------------
  $start_all_source = scalar (@modified_html);
  gp_message ("debugXL", $subr_name, "source starts at start_all_source = $start_all_source");

#------------------------------------------------------------------------------
# Scan the file to identify where the target source starts and ends.
#------------------------------------------------------------------------------
  gp_message ("debugXL", $subr_name, "search for target function $routine");
  $start_tracking = $FALSE;
  $found_target   = $FALSE;
  for (my $line_number=0; $line_number <= $#file_contents; $line_number++)
    {
      $input_line = $file_contents[$line_number];

      gp_message ("debugXL", $subr_name, "[$line_number] $input_line");

      if ($input_line =~ /$function_regex/)
        {
          if (defined ($1) and defined ($2))
            {
              $func_name_in_src_file = $2;
              my $msg = "found a function - name = $func_name_in_src_file";
              gp_message ("debugXL", $subr_name, $msg);

              if ($start_tracking)
                {
                  $start_tracking = $FALSE;
                  $end_target_source = $line_number - 1;
                  my $msg =  "end_target_source = $end_target_source";
                  gp_message ("debugXL", $subr_name, $msg);
                  last;
                }

              if ($func_name_in_src_file eq $routine)
                {
                  $found_target        = $TRUE;
                  $start_tracking      = $TRUE;
                  $start_target_source = $line_number;

                  gp_message ("debugXL", $subr_name, "found target function $routine");
                  gp_message ("debugXL", $subr_name, "function_name = $2 routine = $routine");
                  gp_message ("debugXL", $subr_name, "routine = $routine start_tracking = $start_tracking"); 
                  gp_message ("debugXL", $subr_name, "start_target_source = $start_target_source");
                }
            }
          else
            {
              my $msg = "parsing line $input_line";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
    }

#------------------------------------------------------------------------------
# This is not supposed to happen, but it is not a fatal error either.  The
# hyperlinks related to this function will not work, so a warning is issued.
# A message is issued both in debug mode, and as a warning.
#------------------------------------------------------------------------------
  if (not $found_target)
    {
      my $msg; 
      gp_message ("debug", $subr_name, "target function $routine not found");

      $msg = "function $routine not found in $base - " .
             "links to source code involving this function will not work";
      gp_message ("warning", $subr_name, $msg);

      return ($found_target);
    }

#------------------------------------------------------------------------------
# Catch the line number of the last function.
#------------------------------------------------------------------------------
  if ($start_tracking)
    {
      $end_target_source = $#file_contents;
    }
  gp_message ("debugXL", $subr_name, "routine = $routine start_tracking = $start_tracking"); 
  gp_message ("debugXL", $subr_name, "start_target_source = $start_target_source"); 
  gp_message ("debugXL", $subr_name, "end_target_source   = $end_target_source");

#------------------------------------------------------------------------------
# We now have the index range for the function of interest and will parse it.
# Since we already handled the first line with the function marker, we start
# with the line following.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Find the hot source lines and store them.
#------------------------------------------------------------------------------
  gp_message ("debugXL", $subr_name, "determine the maximum metric values");
  for (my $line_number=$start_target_source+1; $line_number <= $end_target_source; $line_number++)
    {
      $input_line = $file_contents[$line_number];
      gp_message ("debugXL", $subr_name, " $line_number : check input_line = $input_line");

      if ( $input_line =~ /$hot_lines_regex/ )
        {
          gp_message ("debugXL", $subr_name, " $line_number : found a hot line");
#------------------------------------------------------------------------------
# We found a hot line and the metric fields are stored in $2.  We turn this   
# string into an array and add it as a row to hot_source_lines.
#------------------------------------------------------------------------------
              $hot_line      = $1;
              $metric_values = $2;

              gp_message ("debugXL", $subr_name, "hot_line = $hot_line");
              gp_message ("debugXL", $subr_name, "metric_values = $metric_values");

              my @metrics = split (" ", $metric_values);
              push (@hot_source_lines, [@metrics]);
        }
      gp_message ("debugXL", $subr_name, " $line_number : completed check for hot line");
    }

#------------------------------------------------------------------------------
# Transpose the array with the hot lines.  This means each row has all the
# values for a metrict and it makes it easier to determine the maximum values.
#------------------------------------------------------------------------------
  for my $row (keys @hot_source_lines)
    {
      my $msg = "row[" . $row . "] = ";
      for my $col (keys @{$hot_source_lines[$row]})
        {
          $msg .= "$hot_source_lines[$row][$col] ";
          $transposed_hot_lines[$col][$row] = $hot_source_lines[$row][$col];
        }
    }

#------------------------------------------------------------------------------
# Print the maximum metric values found.  Each row contains the data for a
# different metric.
#------------------------------------------------------------------------------
  for my $row (keys @transposed_hot_lines)
    {
      my $msg = "row[" . $row . "] = ";
      for my $col (keys @{$transposed_hot_lines[$row]})
        {
          $msg .= "$transposed_hot_lines[$row][$col] ";
        }
      gp_message ("debugXL", $subr_name, "hot lines = $msg");
    }

#------------------------------------------------------------------------------
# Determine the maximum value for each metric.
#------------------------------------------------------------------------------
  for my $row (keys @transposed_hot_lines)
    {
      my $max_val = 0;
      for my $col (keys @{$transposed_hot_lines[$row]})
        {
          $max_val = max ($transposed_hot_lines[$row][$col], $max_val);
        }
#------------------------------------------------------------------------------
# Convert to a floating point number.
#------------------------------------------------------------------------------
      if ($max_val =~ /$integer_only_regex/)
        {
          $max_val = sprintf ("%f", $max_val);
        }
      push (@max_metric_values, $max_val);
    }

    for my $metric (keys @max_metric_values)
      {
        my $msg = "$input_filename max_metric_values[$metric] = " .
                  $max_metric_values[$metric];
        gp_message ("debugXL", $subr_name, $msg);
      }

#------------------------------------------------------------------------------
# Process those functions that are not the current target.
#------------------------------------------------------------------------------
  $modified_html_ref = process_non_target_source ($start_all_source, 
                                                  $start_target_source-1,
                                                  $src_times_regex,
                                                  $function_regex, 
                                                  $number_of_metrics, 
                                                  \@file_contents,
                                                  \@modified_html);
  @modified_html = @{ $modified_html_ref };

#------------------------------------------------------------------------------
# This is the core part to process the information for the target function.
#------------------------------------------------------------------------------
  gp_message ("debugXL", $subr_name, "parse and process the target source");
  $modified_html_ref = process_target_source ($start_target_source,
                                              $end_target_source,
                                              $routine,
                                              \@max_metric_values,
                                              $src_times_regex,
                                              $function2_regex, 
                                              $number_of_metrics, 
                                              \@file_contents,
                                              \@modified_html);
  @modified_html = @{ $modified_html_ref };

  if ($end_target_source < $#file_contents)
    {
      $modified_html_ref = process_non_target_source ($end_target_source+1,
                                                      $#file_contents,
                                                      $src_times_regex,
                                                      $function_regex, 
                                                      $number_of_metrics, 
                                                      \@file_contents,
                                                      \@modified_html);
      @modified_html = @{ $modified_html_ref };
    }

  gp_message ("debug", $subr_name, "completed reading source");

#------------------------------------------------------------------------------
# Add an extra line with diagnostics.
#
# TBD: The same is done in generate_dis_html but should be done only once.
#------------------------------------------------------------------------------
  if ($hp_value > 0) 
    {
      my $rounded_percentage = sprintf ("%.1f", $hp_value);
      $threshold_line = "<i>The setting for the highlight percentage (-hp) option: $rounded_percentage (%)</i>";
    }
  else
    {
      $threshold_line = "<i>The highlight percentage (-hp) feature is not enabled</i>";
    }

  $html_home = ${ generate_home_link ("left") };
  $html_end  = ${ terminate_html_document () };

  push (@modified_html, "</pre>");
  push (@modified_html, "<br>");
  push (@modified_html, $threshold_line);
  push (@modified_html, $html_home);
  push (@modified_html, "<br>");
  push (@modified_html, $g_html_credits_line);
  push (@modified_html, $html_end);

  for my $i (0 .. $#modified_html)
    {
      gp_message ("debugXL", $subr_name, "[$i] -> $modified_html[$i]");
    }

#------------------------------------------------------------------------------
# Write the generated HTML text to file.
#------------------------------------------------------------------------------
  for my $i (0 .. $#modified_html)
    {
      print NEW_HTML "$modified_html[$i]" . "\n";
    }
  close (NEW_HTML);
  close (SRC_LISTING);
  
  return ($found_target);

} #-- End of subroutine process_source

#------------------------------------------------------------------------------
# Process the source lines for the target function.
#------------------------------------------------------------------------------
sub process_target_source
{
  my $subr_name = get_my_name ();

  my ($start_scan, $end_scan, $target_function, $max_metric_values_ref,
      $src_times_regex, $function2_regex, $number_of_metrics,
      $file_contents_ref, $modified_html_ref) = @_;

  my @file_contents = @{ $file_contents_ref };
  my @modified_html = @{ $modified_html_ref };
  my @max_metric_values = @{ $max_metric_values_ref };

  my @components = ();

  my $colour_coded_line;
  my $colour_coded_line_ref;
  my $hot_line;
  my $input_line;
  my $line_id;
  my $modified_line;
  my $metric_values;
  my $src_code_line;
  my $src_line_no;

  gp_message ("debug", $subr_name, "parse and process the core loop");

  for (my $line_number=$start_scan; $line_number <= $end_scan; $line_number++)
    {
      $input_line = $file_contents[$line_number];

#------------------------------------------------------------------------------
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
      $input_line =~ s/$g_less_than_regex/$g_html_less_than_regex/g;

      $line_id = extract_source_line_number ($src_times_regex, 
                                             $function2_regex, 
                                             $number_of_metrics, 
                                             $input_line);

      gp_message ("debug", $subr_name, "line_number = $line_number : input_line = $input_line line_id = $line_id");

      if ($input_line =~ /$function2_regex/)
#------------------------------------------------------------------------------
# Found the function marker.
#------------------------------------------------------------------------------
        {
          if (defined ($1) and defined ($2))
            {
              my $func_name_in_file = $2;
              my $spaces = $1;
              my $boldface = $TRUE;
              gp_message ("debug", $subr_name, "function_name = $2");
              my $function_line       = "&lt;Function: " . $func_name_in_file . ">";
              my $color_function_name = color_string (
                                          $function_line, 
                                          $boldface, 
                                          $g_html_color_scheme{"target_function_name"});
              my $ftag;
              if (exists ($g_function_tag_id{$target_function}))
                {
                  $ftag = $g_function_tag_id{$target_function};
                  gp_message ("debug", $subr_name, "target_function = $target_function ftag = $ftag");
                }
              else
                {
                  my $msg = "no ftag found for $target_function";
                  gp_message ("assertion", $subr_name, $msg);
                }
              $modified_line = "<a id=\"" . $ftag . "\"></a>";
              $modified_line .= $spaces . "<i>" . $color_function_name . "</i>";
            }
        }
      elsif ($input_line =~ /$src_times_regex/)
#------------------------------------------------------------------------------
# This is a line with metric values.
#------------------------------------------------------------------------------
        {
          gp_message ("debug", $subr_name, "input line has metrics");

          $hot_line      = $1;
          $metric_values = $2;
          $src_line_no   = $3;
          $src_code_line = $4;

          gp_message ("debug", $subr_name, "hot_line = $hot_line");
          gp_message ("debug", $subr_name, "metric_values = $metric_values");
          gp_message ("debug", $subr_name, "src_line_no = $src_line_no");
          gp_message ("debug", $subr_name, "src_code_line = $src_code_line");

          if ($hot_line eq "##")
#------------------------------------------------------------------------------
# Highlight the most expensive line.
#------------------------------------------------------------------------------
            {
              @components = split (" ", $input_line, 1+$number_of_metrics+2);
              $modified_line = set_background_color_string (
                                 $input_line, 
                                 $g_html_color_scheme{"background_color_hot"});
            }
          else
            {
#------------------------------------------------------------------------------
# Highlight those lines close enough to the most expensive line.
#------------------------------------------------------------------------------
              @components = split (" ", $input_line, $number_of_metrics + 2);
              for my $i (0 .. $number_of_metrics-1)
                {
                  gp_message ("debugXL", $subr_name, "$line_number : time check components[$i] = $components[$i]");
                }

              $colour_coded_line_ref = check_metric_values ($metric_values, \@max_metric_values);

              $colour_coded_line = $ {$colour_coded_line_ref};
              if ($colour_coded_line)
                {
                  gp_message ("debugXL", $subr_name, "$line_number : change background colour modified_line = $modified_line");
                  $modified_line = set_background_color_string ($input_line, $g_html_color_scheme{"background_color_lukewarm"});
                }
              else
                {
                  $modified_line = "<a id=\"line_" . $line_id . "\"></a>";
                  $modified_line .= "$input_line";
                }
            }
        }
      else
#------------------------------------------------------------------------------
# This is a regular line that is not modified.
#------------------------------------------------------------------------------
        {
#------------------------------------------------------------------------------
# Add an id.
#------------------------------------------------------------------------------
          gp_message ("debug", $subr_name, "$line_number : input line is a regular line");
          $modified_line = "<a id=\"line_" . $line_id . "\"></a>";
          $modified_line .= "$input_line";
        }
      gp_message ("debug", $subr_name, "$line_number : mod = $modified_line");
      push (@modified_html, $modified_line);
    }

  return (\@modified_html);

} #-- End of subroutine process_target_source

#------------------------------------------------------------------------------
# Process the options.  Set associated variables and check the options for
# correctness.  For example, detect if conflicting options have been set.
#------------------------------------------------------------------------------
sub process_user_options
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref) = @_;
  
  my @exp_dir_list = @{ $exp_dir_list_ref };

  my %ignored_metrics = ();

  my $error_code; 
  my $message;

  my $outputdir;

  my $target_cmd;
  my $rm_output_msg; 
  my $mkdir_output_msg;
  my $time_percentage_multiplier; 
  my $process_all_functions;

  my $option_errors = 0;

#------------------------------------------------------------------------------
# The -o and -O options are mutually exclusive.
#------------------------------------------------------------------------------
  my $define_new_output_dir = $g_user_settings{"output"}{"defined"};
  my $overwrite_output_dir  = $g_user_settings{"overwrite"}{"defined"};
  my $dir_o_option          = $g_user_settings{"output"}{"current_value"};
  my $dir_O_option          = $g_user_settings{"overwrite"}{"current_value"};

  if ($define_new_output_dir and $overwrite_output_dir)
    {
      my $msg;

      $msg  = "the -o/--output and -O/--overwrite options are both set, " .
              "but are mutually exclusive";
      push (@g_user_input_errors, $msg);

      $msg  = "(setting for -o = $dir_o_option, " .
              "setting for -O = $dir_O_option)";
      push (@g_user_input_errors, $msg);

      $option_errors++;
    }

#------------------------------------------------------------------------------
# Define the quiet mode.  While this is an on/off keyword in the input, we 
# use a boolean in the remainder, because it reads easier.
#------------------------------------------------------------------------------
  my $quiet_value = $g_user_settings{"quiet"}{"current_value"};
  $g_quiet        = ($quiet_value eq "on") ? $TRUE : $FALSE;

#------------------------------------------------------------------------------
# In quiet mode, all verbose, warnings and debug messages are suppressed.
#------------------------------------------------------------------------------
  if ($g_quiet)
    {
      $g_user_settings{"verbose"}{"current_value"} = "off";
      $g_user_settings{"warnings"}{"current_value"} = "off";
      $g_user_settings{"debug"}{"current_value"}   = "off";
      $g_verbose  = $FALSE;
      $g_warnings = $FALSE;
      my $debug_off = "off";
      my $ignore_value = set_debug_size (\$debug_off);
    }
  else
    {
#------------------------------------------------------------------------------
# Get the verbose mode.
#------------------------------------------------------------------------------
      my $verbose_value = $g_user_settings{"verbose"}{"current_value"};
      $g_verbose        = ($verbose_value eq "on") ? $TRUE : $FALSE;
#------------------------------------------------------------------------------
# Get the warning mode.
#------------------------------------------------------------------------------
      my $warning_value = $g_user_settings{"warnings"}{"current_value"};
      $g_warnings       = ($warning_value eq "on") ? $TRUE : $FALSE;
    }

#------------------------------------------------------------------------------
# The value for HP should be in the interval (0,100]. We already enforced
# the number to be positive, but the limits have not been checked yet.
#------------------------------------------------------------------------------
  my $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};

  if (($hp_value < 0) or ($hp_value > 100))
    {
      my $msg = "the value for the highlight percentage is set to $hp_value, ";
      $msg   .= "but must be in the range [0, 100]"; 
      push (@g_user_input_errors, $msg);

      $option_errors++;
    }

#------------------------------------------------------------------------------
# The value for TP should be in the interval (0,100]. We already enforced
# the number to be positive, but the limits have not been checked yet.
#------------------------------------------------------------------------------
  my $tp_value = $g_user_settings{"threshold_percentage"}{"current_value"};

  if (($tp_value < 0) or ($tp_value > 100))
    {
      my $msg = "the value for the total percentage is set to $tp_value, " .
                "but must be in the range (0, 100]"; 
      push (@g_user_input_errors, $message);

      $option_errors++;
    }
  else
    {
      $time_percentage_multiplier = $tp_value/100.0;

# Ruud  if (($TIME_PERCENTAGE_MULTIPLIER*100.) >= 100.)

      if ($tp_value == 100)
        {
          $process_all_functions = $TRUE; # ensure that all routines are handled
        }
      else
        {
          $process_all_functions = $FALSE;
        }

      my $txt;
      $txt = "value of time_percentage_multiplier = " .
             $time_percentage_multiplier; 
      gp_message ("debugM", $subr_name, $txt);
      $txt = "value of process_all_functions      = " .
             ($process_all_functions ? "TRUE" : "FALSE");
      gp_message ("debugM", $subr_name, $txt);
    }

#------------------------------------------------------------------------------
# If imetrics has been set, split the list into the individual metrics that
# need to be excluded.  The associated hash called $ignore_metrics has the
# to be excluded metrics as an index.  The value of $TRUE assigned does not
# really matter.
#------------------------------------------------------------------------------
  my @candidate_ignored_metrics;

  if ($g_user_settings{"ignore_metrics"}{"defined"})
    {
      @candidate_ignored_metrics = 
              split (":", $g_user_settings{"ignore_metrics"}{"current_value"});
    }
  for my $metric (@candidate_ignored_metrics)
    {
# TBD: bug?      $ignored_metrics{$metric} = $FALSE;
      $ignored_metrics{$metric} = $TRUE;
    }
  for my $metric (keys %ignored_metrics)
    {
      my $txt = "ignored_metrics{$metric} = $ignored_metrics{$metric}";
      gp_message ("debugM", $subr_name, $txt);
    }

#------------------------------------------------------------------------------
# Check if the experiment directories exist.
#------------------------------------------------------------------------------
  for my $i (0 .. $#exp_dir_list)
    {
      if (-d $exp_dir_list[$i])
        {
          my $abs_path_dir = Cwd::abs_path ($exp_dir_list[$i]);
          $exp_dir_list[$i] = $abs_path_dir;
          my $txt = "directory $exp_dir_list[$i] exists";
          gp_message ("debugM", $subr_name, $txt);
        }
      else
        {
          my $msg = "directory $exp_dir_list[$i] does not exist";

          push (@g_user_input_errors, $msg);
          $option_errors++;
        }
    }

  return ($option_errors, \%ignored_metrics, $outputdir, 
          $time_percentage_multiplier, $process_all_functions,
          \@exp_dir_list);

} #-- End of subroutine process_user_options

#------------------------------------------------------------------------------
# This is a hopefully temporary routine to disable/ignore selected user
# settings.  As the functionality expands, this list will get shorter.
#------------------------------------------------------------------------------
sub reset_selected_settings
{
  my $subr_name = get_my_name ();

  $g_locale_settings{"decimal_separator"} = "\\.";
  $g_locale_settings{"convert_to_dot"}    = $FALSE;
  $g_user_settings{func_limit}{current_value} = 1000000;

  gp_message ("debug", $subr_name, "reset selected settings");

  return (0);

} #-- End of subroutine reset_selected_settings

#------------------------------------------------------------------------------
# There may be various different visibility characters in a metric definition.
# For example: e+%CPI.
#
# Internally we use a normalized definition that only uses the dot (e.g.
# e.CPI) as an index into the description structure.
#
# Here we reduce the incoming metric definition to the normalized form, look
# up the text, and return a pointer to it.
#------------------------------------------------------------------------------
sub retrieve_metric_description
{
  my $subr_name = get_my_name ();

  my ($metric_name_ref, $metric_description_ref) = @_;

  my $metric_name        = ${ $metric_name_ref };
  my %metric_description = %{ $metric_description_ref };

  my $description;
  my $normalized_metric;

  $metric_name =~ /([ei])([\.\+%]+)(.*)/;

  if (defined ($1) and defined ($3))
    {
      $normalized_metric = $1 . "." . $3;
    }
  else
    {
      my $msg = "metric $metric_name has an unknown format";
      gp_message ("assertion", $subr_name, $msg);
    }

  if (defined ($metric_description{$normalized_metric}))
    {
      $description = $metric_description{$normalized_metric};
    }
  else
    {
      my $msg = "description for normalized metric $normalized_metric not found";
      gp_message ("assertion", $subr_name, $msg);
    }

  return (\$description);

} #-- End of subroutine retrieve_metric_description

#------------------------------------------------------------------------------
# TBD.
#------------------------------------------------------------------------------
sub rnumerically 
{
  my ($f1,$f2);
  if ($a =~ /^([^\d]*)(\d+)/)
    {
      $f1 = int ($2);
      if ($b=~ /^([^\d]*)(\d+)/)
        {
          $f2 = int ($2);
          $f1 == $f2 ? 0 : ($f1 > $f2 ? -1 : +1);
        }
    } 
  else 
    {
      return ($b <=> $a);
    }
} #-- End of subroutine rnumerically

#------------------------------------------------------------------------------
# TBD: Remove - not used any longer.
# Set the architecture and associated regular expressions.
#------------------------------------------------------------------------------
sub set_arch_and_regexes
{
  my $subr_name = get_my_name ();

  my ($arch_uname) = @_;

  my $architecture_supported;

  gp_message ("debug", $subr_name, "arch_uname = $arch_uname");

  if ($arch_uname eq "x86_64") 
    {
      #x86/x64 hardware uses jump
      $architecture_supported = $TRUE;
#      $arch='x64';
#      $regex=':\s+(j).*0x[0-9a-f]+';
#      $subexp='(\[\s*)(0x[0-9a-f]+)';
#      $linksubexp='(\[\s*)(0x[0-9a-f]+)';
      gp_message ("debug", $subr_name, "detected $arch_uname hardware");

      $architecture_supported = $TRUE;
      $g_arch_specific_settings{"arch_supported"}  = $TRUE;
      $g_arch_specific_settings{"arch"}       = 'x64';
      $g_arch_specific_settings{"regex"}     = ':\s+(j).*0x[0-9a-f]+';
      $g_arch_specific_settings{"subexp"}     = '(\[\s*)(0x[0-9a-f]+)';
      $g_arch_specific_settings{"linksubexp"} = '(\[\s*)(0x[0-9a-f]+)';
    }
#-------------------------------------------------------------------------------
# TBD: Remove the elsif block
#-------------------------------------------------------------------------------
  elsif ($arch_uname=~m/sparc/s) 
    {
      #sparc hardware uses branch
      $architecture_supported = $FALSE;
#      $arch='sparc';
#      $regex=':\s+(c|b|fb).*0x[0-9a-f]+\s*$';
#      $subexp='(\s*)(0x[0-9a-f]+)\s*$';
#      $linksubexp='(\s*)(0x[0-9a-f]+\s*$)';
#      gp_message ("debug", $subr_name, "detected $arch_uname hardware arch = $arch - this is no longer supported");
      $architecture_supported = $FALSE;
      $g_arch_specific_settings{arch_supported}  = $FALSE;
      $g_arch_specific_settings{arch}       = 'sparc';
      $g_arch_specific_settings{regex}     = ':\s+(c|b|fb).*0x[0-9a-f]+\s*$';
      $g_arch_specific_settings{subexp}     = '(\s*)(0x[0-9a-f]+)\s*$';
      $g_arch_specific_settings{linksubexp} = '(\s*)(0x[0-9a-f]+\s*$)';
    }
  else 
    {
      $architecture_supported = $FALSE;
      gp_message ("debug", $subr_name, "detected $arch_uname hardware - this not supported; limited functionality");
    }

    return ($architecture_supported);

} #-- End of subroutine set_arch_and_regexes

#------------------------------------------------------------------------------
# Set the background color of the input string.
#
# For supported colors, see:
# https://www.w3schools.com/colors/colors_names.asp
#------------------------------------------------------------------------------
sub set_background_color_string
{
  my $subr_name = get_my_name ();

  my ($input_string, $color) = @_;

  my $background_color_string;
  my $msg;

  $msg = "color = $color input_string = $input_string";
  gp_message ("debugXL", $subr_name, $msg);

  $background_color_string = "<span style='background-color: " . $color . 
                             "'>" . $input_string . "</span>";

  $msg = "color = $color background_color_string = " .
         $background_color_string;
  gp_message ("debugXL", $subr_name, $msg);

  return ($background_color_string);

} #-- End of subroutine set_background_color_string

#------------------------------------------------------------------------------
# Set the g_debug_size structure for a given value for "size".  Also set the
# value in $g_user_settings{"debug"}{"current_value"}
#------------------------------------------------------------------------------
sub set_debug_size
{
  my $subr_name = get_my_name ();

  my ($debug_value_ref) = @_;

  my $debug_value = lc (${ $debug_value_ref });

#------------------------------------------------------------------------------
# Regardless of the value, the debug settings are defined here.
#------------------------------------------------------------------------------
  $g_user_settings{"debug"}{"defined"} = $TRUE;

#------------------------------------------------------------------------------
# By default, set the value to "on", but correct below if needed.
#------------------------------------------------------------------------------
  $g_user_settings{"debug"}{"current_value"} = "on";

  if (($debug_value eq "on") or ($debug_value eq "s"))
    {
      $g_debug_size{"on"} = $TRUE;
      $g_debug_size{"s"}  = $TRUE;
    }
  elsif ($debug_value eq "m")
    {
      $g_debug_size{"on"} = $TRUE;
      $g_debug_size{"s"}  = $TRUE;
      $g_debug_size{"m"}  = $TRUE;
    }
  elsif ($debug_value eq "l")
    {
      $g_debug_size{"on"} = $TRUE;
      $g_debug_size{"s"}  = $TRUE;
      $g_debug_size{"m"}  = $TRUE;
      $g_debug_size{"l"}  = $TRUE;
    }
  elsif ($debug_value eq "xl")
    {
      $g_debug_size{"on"} = $TRUE;
      $g_debug_size{"s"}  = $TRUE;
      $g_debug_size{"m"}  = $TRUE;
      $g_debug_size{"l"}  = $TRUE;
      $g_debug_size{"xl"} = $TRUE;
    }
  else
#------------------------------------------------------------------------------
# Any other value is considered to disable debugging.
#------------------------------------------------------------------------------
    {
      $g_user_settings{"debug"}{"current_value"} = "off";
      $g_debug_size{"on"} = $FALSE;
      $g_debug_size{"s"}  = $FALSE;
      $g_debug_size{"m"}  = $FALSE;
      $g_debug_size{"l"}  = $FALSE;
      $g_debug_size{"xl"} = $FALSE;
    }

#------------------------------------------------------------------------------
# Activate in case of an emergency :-)
#------------------------------------------------------------------------------
##  if ($g_debug_size{$debug_value})
##    {
##      for my $i (keys %g_debug_size)
##        {
##          print "$subr_name g_debug_size{$i} = $g_debug_size{$i}\n";
##        }
##    }

  return (0);

} #-- End of subroutine set_debug_size

#------------------------------------------------------------------------------
# This subroutine defines the default metrics.
#------------------------------------------------------------------------------
sub set_default_metrics
{
  my $subr_name = get_my_name ();

  my ($outfile1, $ignored_metrics_ref) = @_;

  my %ignored_metrics = %{ $ignored_metrics_ref };

  my %metric_description = ();
  my %metric_found       = ();

  my $detail_metrics;
  my $detail_metrics_system;

  my $call_metrics    = "";
  my $summary_metrics = "";

  open (METRICS, "<", $outfile1)
    or die ("Unable to open metrics file $outfile1 for reading - '$!'");
  gp_message ("debug", $subr_name, "opened $outfile1 for reading");

  while (<METRICS>)
    {
      my $metric_line = $_;
      chomp ($metric_line);

      gp_message ("debug", $subr_name,"the value of metric_line = $metric_line");

#------------------------------------------------------------------------------
# Decode the metric part of the input line. If a valid line, return the 
# metric components. Otherwise return "skipped" in the metric_spec field.
#------------------------------------------------------------------------------
      my ($metric_spec, $metric_flavor, $metric_visibility, $metric_name, $metric_description) = extract_metric_specifics ($metric_line);

      gp_message ("debug", $subr_name, "metric_spec   = $metric_spec");
      gp_message ("debug", $subr_name, "metric_flavor = $metric_flavor");

      if ($metric_spec eq "skipped")
#------------------------------------------------------------------------------
# Not a valid input line.
#------------------------------------------------------------------------------
        {
          gp_message ("debug", $subr_name, "skipped line: $metric_line");
        }
      else
        {
#------------------------------------------------------------------------------
# A valid metric field has been found.
#------------------------------------------------------------------------------
          gp_message ("debug", $subr_name, "metric_name        = $metric_name");
          gp_message ("debug", $subr_name, "metric_description = $metric_description");

#        if (exists ($IMETRICS{$m})){
          if ($g_user_settings{"ignore_metrics"}{"defined"} and exists ($ignored_metrics{$metric_name}))
            {
              gp_message ("debug", $subr_name, "user requested to ignore metric $metric_name");
              next;
            }

#------------------------------------------------------------------------------
# Only the exclusive metric is selected.
#------------------------------------------------------------------------------
          if ($metric_flavor eq "e")
            {
              $metric_found{$metric_spec}       = $TRUE;
              $metric_description{$metric_spec} = $metric_description;

# TBD: remove the -AO:
              gp_message ("debug", $subr_name,"-AO metric_description{$metric_spec} = $metric_description{$metric_spec}");

              $summary_metrics .= $metric_spec.":";
              $call_metrics .= "a.".$metric_name.":";
            }
        }
    }
  close (METRICS);

  chop ($call_metrics);
  chop ($summary_metrics);

  $detail_metrics        = $summary_metrics;
  $detail_metrics_system = $summary_metrics;

  return (\%metric_description, \%metric_found, 
         $summary_metrics, $detail_metrics, $detail_metrics_system, $call_metrics);

} #-- End of subroutine set_default_metrics

#------------------------------------------------------------------------------
# Set various system specific variables.  These depend upon both the processor
# architecture and OS. The values are stored in global structure 
# g_arch_specific_settings.
#------------------------------------------------------------------------------
sub set_system_specific_variables
{
  my $subr_name = get_my_name ();

  my ($arch_uname, $arch_uname_s) = @_;

  my $elf_arch;
  my $read_elf_cmd;
  my $elf_support; 
  my $architecture_supported;
  my $arch;
  my $regex;
  my $subexp;
  my $linksubexp;

  if ($arch_uname eq "x86_64") 
    {
#------------------------------------------------------------------------------
# x86/x64 hardware uses jump
#------------------------------------------------------------------------------
      $architecture_supported = $TRUE;
      $arch       = 'x64';
      $regex     =':\s+(j).*0x[0-9a-f]+';
      $subexp     ='(\[\s*)(0x[0-9a-f]+)';
      $linksubexp ='(\[\s*)(0x[0-9a-f]+)';

#      gp_message ("debug", $subr_name, "detected $arch_uname hardware arch = $arch");

      $g_arch_specific_settings{"arch_supported"} = $TRUE;
      $g_arch_specific_settings{"arch"}           = 'x64';
#------------------------------------------------------------------------------
# Define the regular expressions to parse branch instructions.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# TBD: Need much more than these
#------------------------------------------------------------------------------
      $g_arch_specific_settings{"regex"} = '\.*([0-9a-fA-F]*):\s+(j).*\s*0x([0-9a-fA-F]+)';
      $g_arch_specific_settings{"subexp"} = '(0x[0-9a-f]+)';
      $g_arch_specific_settings{"linksubexp"} = '(\s*)(0x[0-9a-f]+)';
    }
  else 
    {
      $architecture_supported = $FALSE;
      $g_arch_specific_settings{"arch_supported"}  = $FALSE;
    }

#------------------------------------------------------------------------------
# TBD Ruud: need to handle this better
#------------------------------------------------------------------------------
  if ($arch_uname_s eq "Linux") 
    {
      $elf_arch     = $arch_uname_s;
      $read_elf_cmd = $g_mapped_cmds{"readelf"};

      if ($read_elf_cmd eq "road_to_nowhere")
        {
          $elf_support = $FALSE;
        }
      else
        {
          $elf_support = $TRUE;
        }
      gp_message ("debugXL", $subr_name, "elf_support = $elf_support read_elf_cmd = $read_elf_cmd elf_arch = $elf_arch");
    } 
  else 
    {
      gp_message ("abort", $subr_name, "the $arch_uname_s operating system is not supported");
    }

  return ($architecture_supported, $elf_arch, $elf_support);

} #-- End of subroutine set_system_specific_variables

#------------------------------------------------------------------------------
# TBD
#------------------------------------------------------------------------------
sub set_title
{
  my $subr_name = get_my_name ();

  my ($function_info_ref, $func, $from_where) = @_ ;

  my $msg;
  my @function_info = @{$function_info_ref};
  my $filename = $func ;

  my $base;
  my $first_line;
  my $src_file;
  my $RI;
  my $the_title;
  my $routine = "?";
  my $DIS;
  my $SRC;

  chomp ($filename);

  $base = get_basename ($filename);

  gp_message ("debug", $subr_name, "from_where = $from_where");
  gp_message ("debug", $subr_name, "base = $base filename = $filename");

  if ($from_where eq "process source")
    {
      if ($base =~ /^file\.(\d+)\.src\.txt$/)
        {
          if (defined ($1))
            {
              $RI = $1;
            }
          else
            {
              $msg = "unexpected error encountered parsing $filename";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
      $the_title = "Source";
    } 
  elsif ($from_where eq "disassembly")
    {
      if ($base =~ /^file\.(\d+)\.dis$/)
        {
          if (defined ($1))
            {
              $RI = $1;
            }
          else
            {
              $msg = "unexpected error encountered parsing $filename";
              gp_message ("assertion", $subr_name, $msg);
            }
        }
      $the_title = "Disassembly";
    } 
  else 
    {
      $msg = "called from unknown routine - $from_where";
      gp_message ("assertion", $subr_name, $msg);
    }

  if (defined ($function_info[$RI]{"routine"}))
    {
      $routine = $function_info[$RI]{"routine"};
    }
  
  if ($from_where eq "process source")
    {
      my $is_empty = is_file_empty ($filename);

      if ($is_empty)
        {
          $src_file = "";
        }
      else
        {
          open ($SRC, "<", $filename) 
            or die ("$subr_name - unable to open source file $filename for reading:'$!'");
          gp_message ("debug", $subr_name, "opened file $filename for reading");

          $first_line = <$SRC>;
          chomp ($first_line);

          close ($SRC);

          gp_message ("debug", $subr_name, "first_line = $first_line");

          if ($first_line =~ /^Source\s+file:\s+([^\s]+)/)
            {
              $src_file = $1
            }
          else
            {
              $src_file = "";
            }
        }
    }
  elsif ($from_where eq "disassembly")
    {
      open ($DIS, "<", $filename)
        or die ("$subr_name - unable to open disassembly file $filename for reading: '$!'");
      gp_message ("debug", $subr_name, "opened file $filename for reading");

      $first_line = <$DIS>;
      close ($DIS);
  
      if ($first_line =~ /^Source\s+file:\s+([^\s]+)/)
        {
          $src_file = "$1"
        }
      else
        {
          $src_file = "";
        }
    }

  if (length ($routine))
    {
      $the_title .= " $routine";
    }

  if (length ($src_file))
    {
      if ($src_file ne "(unknown)")
        {
          $the_title .= " ($src_file)";
        } 
      else 
        {
          $the_title .= " $src_file";
        }
    }

  return ($the_title);

} #-- End of subroutine set_title

#------------------------------------------------------------------------------
# Handles where the output should go.  If needed, a directory is # created 
# where the results will go.
#------------------------------------------------------------------------------
sub set_up_output_directory
{
  my $subr_name = get_my_name ();

  my $error_code;
  my $message;
  my $mkdir_output_msg;
  my $option_errors;
  my $outputdir = "does_not_exist_yet";
  my $rm_output_msg;
  my $target_cmd;

  my $define_new_output_dir = $g_user_settings{"output"}{"defined"};
  my $overwrite_output_dir  = $g_user_settings{"overwrite"}{"defined"};

  $option_errors = 0;

  if ((not $define_new_output_dir) and (not $overwrite_output_dir))
#------------------------------------------------------------------------------
# If neither -o or -O are set, find the next number to be used in the name for 
# the default output directory.
#------------------------------------------------------------------------------
    {
      my $dir_id = 1;
      while (-d "display.".$dir_id.".html") 
        { $dir_id++; }
      $outputdir = "display.".$dir_id.".html";
    }
  elsif ($define_new_output_dir)
#------------------------------------------------------------------------------
# The output directory is defined with the -o option.
#------------------------------------------------------------------------------
    {
      $outputdir = $g_user_settings{"output"}{"current_value"};
    }
  elsif ($overwrite_output_dir)
#------------------------------------------------------------------------------
# The output directory is defined with the -O option.
#------------------------------------------------------------------------------
    {
      $outputdir = $g_user_settings{"overwrite"}{"current_value"};
    }

#------------------------------------------------------------------------------
# The name of the output directory is known and we can proceed.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "the target output directory is $outputdir");

  if (-d $outputdir)
    {
#------------------------------------------------------------------------------
# The -o option is used, but the directory already exists.
#------------------------------------------------------------------------------
      if ($define_new_output_dir)
        {
          $message  = "directory $outputdir already exists";
          $message .= " (use the -O option to overwrite an existing directory)";
          push (@g_user_input_errors, $message);

          $option_errors++;

          return ($option_errors, $outputdir);
        }
      elsif ($overwrite_output_dir)
#------------------------------------------------------------------------------
# It is a bit risky to remove this directory and so we proceed with caution.
# What if the user decides to call it "*" e.g. "-O \*" for example? While this
# should have been caught when processing the options, we still like to 
# be very cautious here before executing /bin/rm -rf.
#------------------------------------------------------------------------------
        {
          if ($outputdir eq "*") 
            {
              $message = "it is not allowed to use * as a value for the -O option";
              push (@g_user_input_errors, $message);

              $option_errors++;

              return ($option_errors, $outputdir);
            }
          else
            {
#------------------------------------------------------------------------------
# The output directory exists, but it is okay to overwrite it. It is 
# removed here and created again below.
#------------------------------------------------------------------------------
              $target_cmd = $g_mapped_cmds{"rm"} . " -rf " . $outputdir;
              ($error_code, $rm_output_msg) = execute_system_cmd ($target_cmd);

                if ($error_code != 0)
                  {
                    gp_message ("error", $subr_name, $rm_output_msg);
                    gp_message ("abort", $subr_name, "fatal error when trying to remove $outputdir");
                  }
                else
                  {
                    gp_message ("debug", $subr_name, "directory $outputdir has been removed");
                  }
            }
        }
    } #-- End of if-check for $outputdir

#-------------------------------------------------------------------------------
# When we get here, the fatal scenarios have been cleared and the name for 
# $outputdir is known.  Time to create it.  Note that recursive creation is
# supported and umask controls the access permissions.
#-------------------------------------------------------------------------------
  $target_cmd = $g_mapped_cmds{"mkdir"} . " -p " . $outputdir;
  ($error_code, $mkdir_output_msg) = execute_system_cmd ($target_cmd);

  if ($error_code != 0)
    {
      my $msg = "a fatal problem occurred when creating directory $outputdir";
      gp_message  ("abort", $subr_name, $msg);
    }
  else
    {
      gp_message  ("debug", $subr_name, "created output directory $outputdir");
    }

  return ($option_errors, $outputdir);

} #-- End of subroutine set_up_output_directory

#------------------------------------------------------------------------------
# Routine to generate webfriendly names
#------------------------------------------------------------------------------
sub tag_name
{
  my $subr_name = get_my_name ();

  my ($target_name) = @_;

#------------------------------------------------------------------------------
# Keeps track how many names have been tagged already.
#------------------------------------------------------------------------------
  state $S_total_tagged_names = 0; 

  my $unique_name;

  gp_message ("debug", $subr_name, "target_name on entry  = $target_name");

#------------------------------------------------------------------------------
# Undo conversion of < in to &lt;
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# TBD: Legacy - What is going on here and is this really needed?!
# We need to replace the "<" symbol in the code by "&lt;".
#------------------------------------------------------------------------------
  $target_name =~ s/$g_html_less_than_regex/$g_less_than_regex/g;

#------------------------------------------------------------------------------
# Remove inlining info
#------------------------------------------------------------------------------
  $target_name =~ s/, instructions from source file.*//; 

  if (defined $g_tagged_names{$target_name})
    {
      gp_message ("debug", $subr_name, "target_name = $target_name is already defined: $g_tagged_names{$target_name}");
      gp_message ("debug", $subr_name, "target_name on return = $target_name");
      return ($g_tagged_names{$target_name});
    }
  else
    {
      $unique_name = "ftag".$S_total_tagged_names;
      $S_total_tagged_names++; 
      $g_tagged_names{$target_name} = $unique_name;
      gp_message ("debug", $subr_name, "target_name = $target_name is new and added: g_tagged_names{$target_name} = $g_tagged_names{$target_name}");
      gp_message ("debug", $subr_name, "target_name on return = $target_name");
      return ($unique_name);
    }

} #-- End of subroutine tag_name

#------------------------------------------------------------------------------
# Generate a string to terminate the HTML document.
#------------------------------------------------------------------------------
sub terminate_html_document
{
  my $subr_name = get_my_name ();

  my $html_line;

  $html_line  = "</body>\n";
  $html_line .= "</html>";

  return (\$html_line);

} #-- End of subroutine terminate_html_document

#-------------------------------------------------------------------------------
# Perform some basic checks to ensure the input data is consistent.  This part
# could be refined and expanded over time.  For example by using a checksum
# mechanism to verify the consistency of the executables.
#-------------------------------------------------------------------------------
sub verify_consistency_experiments
{
  my $subr_name = get_my_name ();

  my ($exp_dir_list_ref) = @_;

  my @exp_dir_list    = @{ $exp_dir_list_ref };

  my $executable_name;
  my $full_path_executable_name;
  my $ref_executable_name;

  my $first_exp_dir     = $TRUE;
  my $count_differences = 0;

#-------------------------------------------------------------------------------
# Enforce that the full path names to the executable are the same.  This could
# be overkill and a checksum approach would be more flexible.
#-------------------------------------------------------------------------------
  for my $full_exp_dir (@exp_dir_list)
    {
      my $exp_dir = get_basename ($full_exp_dir);
      gp_message ("debug", $subr_name, "exp_dir = $exp_dir");
      if ($first_exp_dir)
        {
          $first_exp_dir = $FALSE;
          $ref_executable_name = $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"}; 
          gp_message ("debug", $subr_name, "ref_executable_name = $ref_executable_name");
          next;
        }
        $full_path_executable_name = $g_exp_dir_meta_data{$exp_dir}{"full_path_exec"}; 
        gp_message ("debug", $subr_name, "full_path_executable_name = $full_path_executable_name");

        if ($full_path_executable_name ne $ref_executable_name)
          {
            $count_differences++;
            gp_message ("debug", $subr_name, "$full_path_executable_name does not match $ref_executable_name");
          }
    }

  $executable_name = get_basename ($ref_executable_name);

  return ($count_differences, $executable_name);

} #-- End of subroutine verify_consistency_experiments

#------------------------------------------------------------------------------
# Check if the input item is valid for the data type specified. Validity is
# verified in the context of gprofng.  The definition for the metrics is a 
# good example of that.
#------------------------------------------------------------------------------
sub verify_if_input_is_valid
{
  my $subr_name = get_my_name ();

  my ($input_item, $data_type) = @_;

  my $return_value = $FALSE;

#------------------------------------------------------------------------------
# These value are allowed to be case insensitive, so we convert to lower
# case first.
#------------------------------------------------------------------------------
  if (($data_type eq "onoff") or ($data_type eq "size"))
    {
      $input_item = lc ($input_item);
    }

  if ($data_type eq "metrics")
#------------------------------------------------------------------------------
# A gprofng metric definition.  Either consists of "default" only, or starts
# with e or i, followed by one or more from the set {.,%,!,+} and a keyword.
# This pattern may be repeated with a ":" as the separator.
#------------------------------------------------------------------------------
    {
      my @metric_list = split (":", $input_item);

#------------------------------------------------------------------------------
# Check if the pattern is valid.  If not, bail out and return $FALSE.
#------------------------------------------------------------------------------
      for my $metric (@metric_list)
        {
          if ($metric =~ /^default$|^[ei]*[\.%\!\+]+[a-z]*$/)
            {
              $return_value = $TRUE;
            }
          else
            {
              $return_value = $FALSE;
              last;
            }
        }
    }
  elsif ($data_type eq "metric_names")
#------------------------------------------------------------------------------
# A gprofng metric definition but without the flavour and visibility .  Either 
# the name consists of "default" only, or a keyword with lowercase letters
# only.  This pattern may be repeated with a ":" as the separator.
#------------------------------------------------------------------------------
    {
      my @metric_list = split (":", $input_item);

#------------------------------------------------------------------------------
# Check if the pattern is valid.  If not, bail out and return $FALSE.
#------------------------------------------------------------------------------
      for my $metric (@metric_list)
        {
          if ($metric =~ /^default$|^[a-z]*$/)
            {
              $return_value = $TRUE;
            }
          else
            {
              $return_value = $FALSE;
              last;
            }
        }
    }
  elsif ($data_type eq "path")
#------------------------------------------------------------------------------
# This can be almost anything, including "/" and "."
#------------------------------------------------------------------------------
    {
      if ($input_item =~ /^[\w\/\.]*$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "boolean")
    {
#------------------------------------------------------------------------------
# This is TRUE (=1) or FALSE (0).
#------------------------------------------------------------------------------
      if ($input_item =~ /^[01]$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "onoff")
#------------------------------------------------------------------------------
# This is either "on" OR "off".
#------------------------------------------------------------------------------
    {
      if ($input_item =~ /^on$|^off$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "size")
#------------------------------------------------------------------------------
# Supported values are "on", "off", "s", "m", "l", OR "xl".
#------------------------------------------------------------------------------
    {
      if ($input_item =~ /^on$|^off$|^s$|^m$|^l$|^xl$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "pinteger")
#------------------------------------------------------------------------------
# This is a positive integer.
#------------------------------------------------------------------------------
    {
      if ($input_item =~ /^\d*$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "integer")
#------------------------------------------------------------------------------
# This is a positive or negative integer.
#------------------------------------------------------------------------------
    {
      if ($input_item =~ /^\-?\d*$/)
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "pfloat")
#------------------------------------------------------------------------------
# This is a positive floating point number, but we accept a positive integer
# number as well.
#
# TBD: Note that we use the "." here. Maybe should support a "," too.
#------------------------------------------------------------------------------
    {
      if (($input_item =~ /^\d*\.\d*$/) or ($input_item =~ /^\d*$/))
        {
          $return_value = $TRUE;
        }
    }
  elsif ($data_type eq "float")
#------------------------------------------------------------------------------
# This is a positive or negative floating point number, but we accept an
# integer number as well.
#
# TBD: Note that we use the "." here. Maybe should support a "," too.
#------------------------------------------------------------------------------
    {
      if (($input_item =~ /^\-?\d*\.\d*$/) or ($input_item =~ /^\-?\d*$/))
        {
          $return_value = $TRUE;
        }
    }
  else
    {
      my $msg = "the $data_type data type for input $input_item is not supported";
      gp_message ("assertion", $subr_name, $msg);
    } 

  return ($return_value);

} #-- End of subroutine verify_if_input_is_valid

Filemanager

Name Type Size Permission Actions
X11 Folder 0755
411toppm File 65.8 KB 0755
7z File 39 B 0755
7za File 40 B 0755
7zr File 40 B 0755
GET File 15.82 KB 0755
HEAD File 15.82 KB 0755
JxrDecApp File 22.16 KB 0755
JxrEncApp File 23.52 KB 0755
POST File 15.82 KB 0755
RTIMULibCal File 66.47 KB 0755
RTIMULibDrive11 File 66.25 KB 0755
X File 274 B 0755
Xorg File 274 B 0755
Xvnc File 1.54 MB 4755
Xvnc-core File 11.77 MB 0755
Xwayland File 2.28 MB 0755
[ File 66.74 KB 0755
aa-enabled File 66.09 KB 0755
aa-exec File 66.28 KB 0755
aa-features-abi File 66.3 KB 0755
aarch64-linux-gnu-addr2line File 67 KB 0755
aarch64-linux-gnu-ar File 67.01 KB 0755
aarch64-linux-gnu-as File 454.76 KB 0755
aarch64-linux-gnu-c++filt File 66.39 KB 0755
aarch64-linux-gnu-cpp File 1.26 MB 0755
aarch64-linux-gnu-cpp-12 File 1.26 MB 0755
aarch64-linux-gnu-dwp File 1.82 MB 0755
aarch64-linux-gnu-elfedit File 66.9 KB 0755
aarch64-linux-gnu-g++ File 1.26 MB 0755
aarch64-linux-gnu-g++-12 File 1.26 MB 0755
aarch64-linux-gnu-gcc File 1.26 MB 0755
aarch64-linux-gnu-gcc-12 File 1.26 MB 0755
aarch64-linux-gnu-gcc-ar File 66.48 KB 0755
aarch64-linux-gnu-gcc-ar-12 File 66.48 KB 0755
aarch64-linux-gnu-gcc-nm File 66.48 KB 0755
aarch64-linux-gnu-gcc-nm-12 File 66.48 KB 0755
aarch64-linux-gnu-gcc-ranlib File 66.48 KB 0755
aarch64-linux-gnu-gcc-ranlib-12 File 66.48 KB 0755
aarch64-linux-gnu-gcov File 708.14 KB 0755
aarch64-linux-gnu-gcov-12 File 708.14 KB 0755
aarch64-linux-gnu-gcov-dump File 579.99 KB 0755
aarch64-linux-gnu-gcov-dump-12 File 579.99 KB 0755
aarch64-linux-gnu-gcov-tool File 580.07 KB 0755
aarch64-linux-gnu-gcov-tool-12 File 580.07 KB 0755
aarch64-linux-gnu-gold File 5.26 MB 0755
aarch64-linux-gnu-gp-archive File 195.11 KB 0755
aarch64-linux-gnu-gp-collect-app File 195.16 KB 0755
aarch64-linux-gnu-gp-display-html File 578.29 KB 0755
aarch64-linux-gnu-gp-display-src File 194.71 KB 0755
aarch64-linux-gnu-gp-display-text File 261.28 KB 0755
aarch64-linux-gnu-gprof File 132.18 KB 0755
aarch64-linux-gnu-gprofng File 194.49 KB 0755
aarch64-linux-gnu-ld File 1.63 MB 0755
aarch64-linux-gnu-ld.bfd File 1.63 MB 0755
aarch64-linux-gnu-ld.gold File 5.26 MB 0755
aarch64-linux-gnu-lto-dump File 25.77 MB 0755
aarch64-linux-gnu-lto-dump-12 File 25.77 MB 0755
aarch64-linux-gnu-nm File 67.84 KB 0755
aarch64-linux-gnu-objcopy File 199.55 KB 0755
aarch64-linux-gnu-objdump File 394.5 KB 0755
aarch64-linux-gnu-pkg-config File 67.88 KB 0755
aarch64-linux-gnu-pkgconf File 67.88 KB 0755
aarch64-linux-gnu-python3-config File 3.01 KB 0755
aarch64-linux-gnu-python3.11-config File 3.01 KB 0755
aarch64-linux-gnu-ranlib File 67.01 KB 0755
aarch64-linux-gnu-readelf File 779.36 KB 0755
aarch64-linux-gnu-size File 66.71 KB 0755
aarch64-linux-gnu-strings File 66.84 KB 0755
aarch64-linux-gnu-strip File 199.56 KB 0755
ab File 66.23 KB 0755
aconnect File 66.16 KB 0755
addpart File 66.16 KB 0755
addr2line File 67 KB 0755
agnostics File 66.77 KB 0755
airscan-discover File 195.01 KB 0755
alacarte File 1.01 KB 0755
alsabat File 66.2 KB 0755
alsaloop File 131.09 KB 0755
alsamixer File 132.02 KB 0755
alsatplg File 130.16 KB 0755
alsaucm File 66.6 KB 0755
amidi File 66.16 KB 0755
amixer File 66.22 KB 0755
animate File 66.09 KB 0755
animate-im6 File 66.09 KB 0755
animate-im6.q16 File 66.09 KB 0755
anytopnm File 12.26 KB 0755
apg File 274 B 0755
apgbfm File 22.3 KB 0755
aplay File 130.21 KB 0755
aplaymidi File 66.16 KB 0755
apropos File 67.03 KB 0755
apt File 66.16 KB 0755
apt-cache File 130.3 KB 0755
apt-cdrom File 66.31 KB 0755
apt-config File 66.16 KB 0755
apt-extracttemplates File 66.25 KB 0755
apt-file File 31.04 KB 0755
apt-ftparchive File 258.41 KB 0755
apt-get File 66.24 KB 0755
apt-key File 27.32 KB 0755
apt-listchanges File 11.96 KB 0755
apt-mark File 66.24 KB 0755
apt-sortpkgs File 66.17 KB 0755
ar File 67.01 KB 0755
arch File 66.81 KB 0755
arecord File 130.21 KB 0755
arecordmidi File 66.17 KB 0755
aria_chk File 5.46 MB 0755
aria_dump_log File 5.27 MB 0755
aria_ftdump File 5.27 MB 0755
aria_pack File 5.27 MB 0755
aria_read_log File 5.39 MB 0755
arpaname File 66.08 KB 0755
as File 454.76 KB 0755
asciitopgm File 65.8 KB 0755
aseqdump File 66.16 KB 0755
aseqnet File 66.2 KB 0755
atktopbm File 65.8 KB 0755
attr File 66.08 KB 0755
autotouch File 1.28 KB 0755
avstopam File 65.8 KB 0755
awk File 712.3 KB 0755
axfer File 130.26 KB 0755
b2sum File 66.95 KB 0755
base32 File 66.87 KB 0755
base64 File 66.87 KB 0755
basename File 66.8 KB 0755
basenc File 66.87 KB 0755
bash File 1.28 MB 0755
bashbug File 6.71 KB 0755
bc File 90.59 KB 0755
bdftopcf File 38.27 KB 0755
bdftruncate File 10.09 KB 0755
bioradtopgm File 65.8 KB 0755
bluemoon File 66.16 KB 0755
bluetooth-sendto File 66.16 KB 0755
bluetoothctl File 388.88 KB 0755
bmptopnm File 65.8 KB 0755
bmptoppm File 65.8 KB 0755
breeze-settings5 File 66.08 KB 0755
brushtopbm File 65.8 KB 0755
btattach File 66.16 KB 0755
bthelper File 1.52 KB 0755
btmgmt File 197.55 KB 0755
btmon File 1 MB 0755
btuart File 1010 B 0755
bunzip2 File 66.14 KB 0755
busctl File 130.35 KB 0755
busybox File 834.63 KB 0755
bwrap File 66.23 KB 0755
bzcat File 66.14 KB 0755
bzcmp File 2.17 KB 0755
bzdiff File 2.17 KB 0755
bzegrep File 3.69 KB 0755
bzexe File 4.78 KB 0755
bzfgrep File 3.69 KB 0755
bzgrep File 3.69 KB 0755
bzip2 File 66.14 KB 0755
bzip2recover File 66.08 KB 0755
bzless File 1.27 KB 0755
bzmore File 1.27 KB 0755
c++ File 1.26 MB 0755
c++filt File 66.39 KB 0755
c89 File 428 B 0755
c89-gcc File 428 B 0755
c99 File 454 B 0755
c99-gcc File 454 B 0755
c_rehash File 6.73 KB 0755
cacaclock File 66.53 KB 0755
cacademo File 322.51 KB 0755
cacafire File 66.36 KB 0755
cacaplay File 66.33 KB 0755
cacaserver File 66.43 KB 0755
cacaview File 66.6 KB 0755
cam File 322.25 KB 0755
camera-bug-report File 4.95 KB 0755
cameratopam File 129.88 KB 0755
canberra-gtk-play File 14 KB 0755
cancel File 66.09 KB 0755
cancel-rename File 1.62 KB 0755
captoinfo File 130.2 KB 0755
cat File 66.93 KB 0755
catdoc File 67.67 KB 0755
catman File 66.52 KB 0755
catppt File 67.14 KB 0755
cc File 1.26 MB 0755
cct File 66.27 KB 0755
cd-create-profile File 66.08 KB 0755
cd-fix-profile File 66.08 KB 0755
cd-iccdump File 66.08 KB 0755
cd-it8 File 66.08 KB 0755
cec-compliance File 324.2 KB 0755
cec-ctl File 393.31 KB 0755
cec-follower File 194.76 KB 0755
certbot File 958 B 0755
cgi-fcgi File 65.93 KB 0755
chacl File 66.08 KB 0755
chafa File 194.06 KB 0755
chage File 134.2 KB 2755
chardet File 221 B 0755
chardetect File 221 B 0755
chattr File 66.09 KB 0755
chcon File 67.09 KB 0755
check-language-support File 2.71 KB 0755
checkgid File 66.09 KB 0755
chfn File 68.96 KB 4755
chgrp File 67.01 KB 0755
chmod File 66.95 KB 0755
choom File 66.16 KB 0755
chown File 67.02 KB 0755
chromium File 6.02 KB 0755
chromium-browser File 6.02 KB 0755
chrt File 130.16 KB 0755
chsh File 67.45 KB 4755
chvt File 66.55 KB 0755
cifscreds File 66.41 KB 0755
ciptool File 66.25 KB 0755
cistopbm File 65.8 KB 0755
cjpeg File 66.18 KB 0755
ckbcomp File 147.14 KB 0755
cksum File 131.01 KB 0755
clear File 66.09 KB 0755
clear_console File 66.01 KB 0755
cmp File 66.83 KB 0755
cmuwmtopbm File 65.8 KB 0755
codepage File 66.29 KB 0755
col File 66.16 KB 0755
colcrt File 66.16 KB 0755
colormgr File 66.11 KB 0755
colrm File 66.16 KB 0755
column File 66.16 KB 0755
comm File 66.87 KB 0755
compare File 66.12 KB 0755
compare-im6 File 66.12 KB 0755
compare-im6.q16 File 66.12 KB 0755
compose File 18.06 KB 0755
composite File 66.09 KB 0755
composite-im6 File 66.09 KB 0755
composite-im6.q16 File 66.09 KB 0755
con2fbmap File 65.93 KB 0755
conjure File 66.09 KB 0755
conjure-im6 File 66.09 KB 0755
conjure-im6.q16 File 66.09 KB 0755
convert File 66.09 KB 0755
convert-dtsv0 File 66.18 KB 0755
convert-im6 File 66.09 KB 0755
convert-im6.q16 File 66.09 KB 0755
corelist File 15.01 KB 0755
cp File 195.57 KB 0755
cpan File 8.16 KB 0755
cpan5.36-aarch64-linux-gnu File 8.19 KB 0755
cpio File 198.34 KB 0755
cpp File 1.26 MB 0755
cpp-12 File 1.26 MB 0755
create_xauth File 104 B 0755
crontab File 66.44 KB 2755
cs2cs File 66.2 KB 0755
csplit File 131.1 KB 0755
ctstat File 66.43 KB 0755
cupstestppd File 66.16 KB 0755
curl File 322.16 KB 0755
cut File 66.91 KB 0755
cvlc File 45 B 0755
cvtsudoers File 332.84 KB 0755
cx18-ctl File 66.39 KB 0755
dash File 130.51 KB 0755
date File 131 KB 0755
dbilogstrip File 1.35 KB 0755
dbiprof File 6.06 KB 0755
dbiproxy File 5.27 KB 0755
dbus-cleanup-sockets File 66.08 KB 0755
dbus-daemon File 258.4 KB 0755
dbus-launch File 65.92 KB 0755
dbus-monitor File 66.08 KB 0755
dbus-run-session File 66.08 KB 0755
dbus-send File 66.08 KB 0755
dbus-update-activation-environment File 66.08 KB 0755
dbus-uuidgen File 66.08 KB 0755
dbwrap_tool File 66.3 KB 0755
dc File 50.02 KB 0755
dconf File 66.01 KB 0755
dd File 131.1 KB 0755
ddbugtopbm File 65.8 KB 0755
ddcmon File 12.97 KB 0755
deallocvt File 66.51 KB 0755
deb-systemd-helper File 23.79 KB 0755
deb-systemd-invoke File 6.09 KB 0755
debconf File 2.79 KB 0755
debconf-apt-progress File 11.27 KB 0755
debconf-communicate File 608 B 0755
debconf-copydb File 1.68 KB 0755
debconf-escape File 647 B 0755
debconf-get-selections File 1.72 KB 0755
debconf-getlang File 6.94 KB 0755
debconf-loadtemplate File 933 B 0755
debconf-mergetemplate File 5.09 KB 0755
debconf-set-selections File 2.92 KB 0755
debconf-show File 1.78 KB 0755
debian-reference File 243 B 0755
decode-dimms File 107 KB 0755
decode-edid File 5.47 KB 0755
decode-vaio File 6.38 KB 0755
decode_tm6000 File 66.44 KB 0755
delpart File 66.16 KB 0755
delv File 69.04 KB 0755
derb File 66.65 KB 0755
desktop-file-edit File 92.2 KB 0755
desktop-file-install File 92.2 KB 0755
desktop-file-validate File 76.43 KB 0755
devdump File 199.88 KB 0755
df File 131.73 KB 0755
dh_bash-completion File 4.31 KB 0755
dh_installxmlcatalogs File 9.22 KB 0755
dh_numpy3 File 1.94 KB 0755
dh_perl_dbi File 1.17 KB 0755
dh_perl_openssl File 1.53 KB 0755
diff File 195.42 KB 0755
diff3 File 66.97 KB 0755
dig File 194.58 KB 0755
dillo File 903.02 KB 0755
dillo-install-hyphenation File 5.59 KB 0755
dir File 195.74 KB 0755
dircolors File 66.87 KB 0755
dirmngr File 582.19 KB 0755
dirmngr-client File 130.68 KB 0755
dirname File 66.8 KB 0755
dirsplit File 16.74 KB 0755
display File 66.09 KB 0755
display-im6 File 66.09 KB 0755
display-im6.q16 File 66.09 KB 0755
djpeg File 66.18 KB 0755
dm-tool File 66.09 KB 0755
dmesg File 138.4 KB 0755
dmypy File 234 B 0755
dnsdomainname File 66.01 KB 0755
dnssec-cds File 67.05 KB 0755
dnssec-dsfromkey File 66.16 KB 0755
dnssec-importkey File 66.16 KB 0755
dnssec-keyfromlabel File 66.16 KB 0755
dnssec-keygen File 66.16 KB 0755
dnssec-revoke File 66.16 KB 0755
dnssec-settime File 66.16 KB 0755
dnssec-signzone File 130.2 KB 0755
dnssec-verify File 66.17 KB 0755
dnstap-read File 66.1 KB 0755
docutils File 960 B 0755
domainname File 66.01 KB 0755
dos2unix File 54.09 KB 0755
dpkg File 322.48 KB 0755
dpkg-architecture File 14.85 KB 0755
dpkg-buildapi File 1.79 KB 0755
dpkg-buildflags File 8.14 KB 0755
dpkg-buildpackage File 34.13 KB 0755
dpkg-buildtree File 1.88 KB 0755
dpkg-checkbuilddeps File 7.45 KB 0755
dpkg-deb File 194.47 KB 0755
dpkg-distaddfile File 2.72 KB 0755
dpkg-divert File 194.4 KB 0755
dpkg-genbuildinfo File 18.54 KB 0755
dpkg-genchanges File 17.92 KB 0755
dpkg-gencontrol File 14.24 KB 0755
dpkg-gensymbols File 10.66 KB 0755
dpkg-maintscript-helper File 20.71 KB 0755
dpkg-mergechangelogs File 8.87 KB 0755
dpkg-name File 6.61 KB 0755
dpkg-parsechangelog File 4.83 KB 0755
dpkg-query File 194.4 KB 0755
dpkg-realpath File 4.09 KB 0755
dpkg-scanpackages File 8.45 KB 0755
dpkg-scansources File 9.1 KB 0755
dpkg-shlibdeps File 31.46 KB 0755
dpkg-source File 23.05 KB 0755
dpkg-split File 130.32 KB 0755
dpkg-statoverride File 130.2 KB 0755
dpkg-trigger File 130.33 KB 0755
dpkg-vendor File 3.18 KB 0755
driverless File 66.1 KB 0755
driverless-fax File 537 B 0755
dtc File 199.67 KB 0755
dtdiff File 680 B 0755
dtmerge File 66.41 KB 0755
dtoverlay File 66.9 KB 0755
dtparam File 66.9 KB 0755
du File 195.3 KB 0755
dump_xsettings File 22.27 KB 0755
dumpkeys File 194.99 KB 0755
dumpmscat File 66.08 KB 0755
dvdauthor File 195.59 KB 0755
dvddirdel File 876 B 0755
dvdunauthor File 130.16 KB 0755
dvgrab File 298.1 KB 0755
dvipdf File 1007 B 0755
dwp File 1.82 MB 0755
echo File 66.8 KB 0755
ed File 66.29 KB 0755
edit File 18.06 KB 0755
editor File 324.64 KB 0755
eepdump File 66.41 KB 0755
eepflash.sh File 4.31 KB 0755
eepmake File 66.38 KB 0755
egrep File 41 B 0755
eject File 130.01 KB 0755
elfedit File 66.9 KB 0755
enc2xs File 40.96 KB 0755
encguess File 3 KB 0755
env File 67.38 KB 0755
envsubst File 66.16 KB 0755
eom File 514.16 KB 0755
eps2eps File 639 B 0755
epylint File 219 B 0755
eqn File 265.2 KB 0755
escp2topbm File 65.8 KB 0755
evince File 515.58 KB 0755
evince-previewer File 66.54 KB 0755
evince-thumbnailer File 66.16 KB 0755
ex File 1.65 MB 0755
exifautotran File 1.11 KB 0755
expand File 66.86 KB 0755
expiry File 66.29 KB 2755
expr File 131 KB 0755
eyuvtoppm File 65.8 KB 0755
f2py File 949 B 0755
f2py3 File 957 B 0755
f2py3.11 File 960 B 0755
faad File 67.48 KB 0755
factor File 131.15 KB 0755
faillog File 66.37 KB 0755
faked-sysv File 66.65 KB 0755
faked-tcp File 66.66 KB 0755
fakeroot File 3.9 KB 0755
fakeroot-sysv File 3.9 KB 0755
fakeroot-tcp File 3.9 KB 0755
fallocate File 66.16 KB 0755
false File 66.78 KB 0755
fbcli File 66.16 KB 0755
fbd-theme-validate File 130.16 KB 0755
fbset File 66.09 KB 0755
fbtest File 66.17 KB 0755
fc-cache File 66.47 KB 0755
fc-cat File 66.41 KB 0755
fc-conflist File 66.22 KB 0755
fc-list File 66.28 KB 0755
fc-match File 66.35 KB 0755
fc-pattern File 66.27 KB 0755
fc-query File 66.24 KB 0755
fc-scan File 66.3 KB 0755
fc-validate File 66.29 KB 0755
fcgistarter File 66.09 KB 0755
fdtdump File 66.17 KB 0755
fdtget File 66.17 KB 0755
fdtoverlay File 66.17 KB 0755
fdtput File 66.17 KB 0755
ffmpeg File 322.4 KB 0755
ffplay File 194.39 KB 0755
ffprobe File 198.91 KB 0755
fgconsole File 66.56 KB 0755
fgrep File 41 B 0755
fiascotopnm File 130.65 KB 0755
figlet File 22.91 KB 0755
figlet-toilet File 22.91 KB 0755
filan File 131.85 KB 0755
file File 66.33 KB 0755
file2brl File 66.01 KB 0755
filezilla File 4.19 MB 0755
fincore File 66.2 KB 0755
find File 259.41 KB 0755
findmnt File 131.43 KB 0755
fio File 1.73 MB 0755
fio-btrace2fio File 66.15 KB 0755
fio-dedupe File 66.24 KB 0755
fio-genzipf File 66.2 KB 0755
fio-verify-state File 66.08 KB 0755
fio2gnuplot File 21.73 KB 0755
fio_generate_plots File 4.09 KB 0755
fio_jsonplus_clat2csv File 12.92 KB 0755
firefox File 848 B 0755
fitstopnm File 65.8 KB 0755
flask File 948 B 0755
flock File 66.23 KB 0755
fmt File 66.87 KB 0755
fold File 66.87 KB 0755
fonttosfnt File 34.27 KB 0755
foomatic-rip File 143.14 KB 0755
free File 66.16 KB 0755
fstopgm File 65.8 KB 0755
funzip File 66.34 KB 0755
fuser File 67.6 KB 0755
fusermount File 66.16 KB 4755
fusermount3 File 66.16 KB 4755
fzputtygen File 322.01 KB 0755
fzsftp File 642.46 KB 0755
g++ File 1.26 MB 0755
g++-12 File 1.26 MB 0755
g3topbm File 66.63 KB 0755
galculator File 156.41 KB 0755
galera_new_cluster File 917 B 0755
galera_recovery File 3.29 KB 0755
gamma4scanimage File 66.09 KB 0755
gapplication File 66.16 KB 0755
gatttool File 131.35 KB 0755
gawk File 712.3 KB 0755
gawkbug File 6.55 KB 0755
gcc File 1.26 MB 0755
gcc-12 File 1.26 MB 0755
gcc-ar File 66.48 KB 0755
gcc-ar-12 File 66.48 KB 0755
gcc-nm File 66.48 KB 0755
gcc-nm-12 File 66.48 KB 0755
gcc-ranlib File 66.48 KB 0755
gcc-ranlib-12 File 66.48 KB 0755
gchempaint File 22.34 KB 0755
gchempaint-0.14 File 22.34 KB 0755
gcore File 3.43 KB 0755
gcov File 708.14 KB 0755
gcov-12 File 708.14 KB 0755
gcov-dump File 579.99 KB 0755
gcov-dump-12 File 579.99 KB 0755
gcov-tool File 580.07 KB 0755
gcov-tool-12 File 580.07 KB 0755
gcr-viewer File 66.15 KB 0755
gdb File 10.01 MB 0755
gdb-add-index File 4.52 KB 0755
gdbtui File 126 B 0755
gdbus File 66.16 KB 0755
gdialog File 9.01 KB 0755
gdk-pixbuf-csource File 66.11 KB 0755
gdk-pixbuf-pixdata File 66.09 KB 0755
gdk-pixbuf-thumbnailer File 66.17 KB 0755
gdm-control File 66.08 KB 0755
geany File 66.04 KB 0755
gemtopbm File 65.8 KB 0755
gemtopnm File 65.8 KB 0755
genbrk File 66.55 KB 0755
gencat File 66.48 KB 0755
gencfu File 66.5 KB 0755
gencnval File 66.41 KB 0755
gendict File 66.55 KB 0755
genfio File 8.63 KB 0755
genisoimage File 665.41 KB 0755
genrb File 195.79 KB 0755
geod File 66.1 KB 0755
geoiplookup File 66.09 KB 0755
geoiplookup6 File 66.09 KB 0755
geqn File 265.2 KB 0755
get-edid File 14.16 KB 0755
get_objgraph File 1.61 KB 0755
getcifsacl File 66.09 KB 0755
getconf File 66.25 KB 0755
geteltorito File 6.06 KB 0755
getent File 67.19 KB 0755
getfacl File 66.63 KB 0755
getfattr File 66.53 KB 0755
getkeycodes File 66.51 KB 0755
getopt File 66.16 KB 0755
gettext File 66.16 KB 0755
gettext.sh File 5.07 KB 0755
ghb File 7.22 MB 0755
ghostscript File 66.12 KB 0755
gie File 66.2 KB 0755
giftopnm File 65.8 KB 0755
gio File 130.21 KB 0755
gio-querymodules File 66.09 KB 0755
git File 3.58 MB 0755
git-receive-pack File 3.58 MB 0755
git-shell File 2.09 MB 0755
git-upload-archive File 3.58 MB 0755
git-upload-pack File 3.58 MB 0755
gkbd-keyboard-display File 66.07 KB 0755
glib-compile-schemas File 66.31 KB 0755
gmake File 238.46 KB 0755
gnome-control-center File 3.07 MB 0755
gnome-help File 66.01 KB 0755
gnome-keyring File 66.29 KB 0755
gnome-keyring-3 File 66.29 KB 0755
gnome-keyring-daemon File 1.13 MB 0755
gnome-www-browser File 6.02 KB 0755
gnumeric File 66.16 KB 0755
gold File 5.26 MB 0755
gouldtoppm File 65.8 KB 0755
gp-archive File 195.11 KB 0755
gp-collect-app File 195.16 KB 0755
gp-display-html File 578.29 KB 0755
gp-display-src File 194.71 KB 0755
gp-display-text File 261.28 KB 0755
gpasswd File 134.13 KB 4755
gpg File 1.08 MB 0755
gpg-agent File 453.05 KB 0755
gpg-connect-agent File 194.78 KB 0755
gpg-wks-server File 258.84 KB 0755
gpg-zip File 3.43 KB 0755
gpgcompose File 901.96 KB 0755
gpgconf File 198.53 KB 0755
gpgparsemail File 66.16 KB 0755
gpgsm File 583.56 KB 0755
gpgsplit File 130.4 KB 0755
gpgtar File 195.36 KB 0755
gpgv File 514.83 KB 0755
gpic File 260.11 KB 0755
gpiodetect File 66.16 KB 0755
gpiofind File 66.16 KB 0755
gpioget File 66.16 KB 0755
gpioinfo File 66.16 KB 0755
gpiomon File 66.16 KB 0755
gpioset File 66.16 KB 0755
gprof File 132.18 KB 0755
gprofng File 194.49 KB 0755
grdctl File 66.17 KB 0755
grep File 194.22 KB 0755
gresource File 66.09 KB 0755
grim File 30.16 KB 0755
groff File 137.31 KB 0755
grog File 2.71 KB 0755
grops File 201.7 KB 0755
grotty File 137.33 KB 0755
groups File 66.86 KB 0755
gs File 66.12 KB 0755
gsbj File 350 B 0755
gsdj File 352 B 0755
gsdj500 File 352 B 0755
gsettings File 66.09 KB 0755
gslj File 353 B 0755
gslp File 350 B 0755
gsnd File 277 B 0755
gtbl File 194.29 KB 0755
gtf File 66.3 KB 0755
gtk-nop File 66.05 KB 0755
gtk-update-icon-cache File 66.32 KB 0755
gtk4-broadwayd File 195.3 KB 0755
gtk4-builder-tool File 66.91 KB 0755
gtk4-encode-symbolic-svg File 8.27 MB 0755
gtk4-launch File 66.23 KB 0755
gtk4-query-settings File 66.09 KB 0755
gtk4-update-icon-cache File 66.48 KB 0755
gui-pkinst File 66.45 KB 0755
gui-updater File 66.52 KB 0755
gunzip File 2.29 KB 0755
gzexe File 6.3 KB 0755
gzip File 91.55 KB 0755
h2ph File 28.54 KB 0755
h2xs File 59.51 KB 0755
handbrake File 7.22 MB 0755
handbrake-gtk File 7.22 MB 0755
hardlink File 66.23 KB 0755
hciattach File 68.23 KB 0755
hciconfig File 200.13 KB 0755
hcitool File 199.66 KB 0755
hd File 66.17 KB 0755
hdifftopam File 65.8 KB 0755
hdmi-audio-select File 682 B 0755
head File 66.89 KB 0755
helpztags File 2.46 KB 0755
hex2hcd File 66.16 KB 0755
hexdump File 66.17 KB 0755
hipstopgm File 65.8 KB 0755
host File 130.56 KB 0755
hostid File 66.8 KB 0755
hostname File 66.01 KB 0755
hostnamectl File 66.23 KB 0755
hp-align File 9.14 KB 0755
hp-check File 39.2 KB 0755
hp-clean File 7.05 KB 0755
hp-colorcal File 9.08 KB 0755
hp-config_usb_printer File 6.98 KB 0755
hp-doctor File 12.69 KB 0755
hp-firmware File 6.47 KB 0755
hp-info File 6.26 KB 0755
hp-levels File 6.85 KB 0755
hp-logcapture File 12.15 KB 0755
hp-makeuri File 5.6 KB 0755
hp-pkservice File 3.13 KB 0755
hp-plugin File 13.62 KB 0755
hp-probe File 7.98 KB 0755
hp-query File 4.94 KB 0755
hp-scan File 86.9 KB 0755
hp-setup File 37.25 KB 0755
hp-testpage File 5.98 KB 0755
hp-timedate File 3.31 KB 0755
hpcdtoppm File 799 B 0755
htcacheclean File 66.09 KB 0755
htdbm File 66.09 KB 0755
htdigest File 66.09 KB 0755
htop File 325.66 KB 0755
htpasswd File 66.09 KB 0755
httpx File 204 B 0755
iceauth File 66.64 KB 0755
icontopbm File 65.8 KB 0755
iconv File 66.87 KB 0755
icuexportdata File 69.05 KB 0755
icuinfo File 66.4 KB 0755
id File 66.95 KB 0755
identify File 66.1 KB 0755
identify-im6 File 66.1 KB 0755
identify-im6.q16 File 66.1 KB 0755
iecset File 66.16 KB 0755
ilbmtoppm File 65.88 KB 0755
imagetops File 1.21 KB 0755
img2sixel File 22.09 KB 0755
img2txt File 66.77 KB 0755
imgtoppm File 65.8 KB 0755
import File 66.09 KB 0755
import-im6 File 66.09 KB 0755
import-im6.q16 File 66.09 KB 0755
infocmp File 66.16 KB 0755
infotocap File 130.2 KB 0755
infotopam File 65.84 KB 0755
innochecksum File 4.57 MB 0755
innotop File 445.44 KB 0755
install File 195.73 KB 0755
instmodsh File 4.27 KB 0755
invgeod File 66.1 KB 0755
invproj File 66.19 KB 0755
ionice File 66.16 KB 0755
ip File 729.26 KB 0755
ipa_verify File 66.17 KB 0755
ipcmk File 66.23 KB 0755
ipcrm File 66.16 KB 0755
ipcs File 130.16 KB 0755
ippfind File 66.18 KB 0755
ipptool File 130.09 KB 0755
iptables-xml File 132.77 KB 0755
ir-ctl File 66.17 KB 0755
ischroot File 66.18 KB 0755
isodump File 199.88 KB 0755
isoinfo File 342.2 KB 0755
isovfy File 199.84 KB 0755
ispell-wrapper File 7.05 KB 0755
ivtv-ctl File 66.46 KB 0755
jbigtopnm File 76.63 KB 0755
join File 66.95 KB 0755
journalctl File 130.49 KB 0755
jp2a File 70.53 KB 0755
jpeg2ktopam File 264.27 KB 0755
jpegexiforient File 66.09 KB 0755
jpegtopnm File 65.88 KB 0755
jpegtran File 66.11 KB 0755
json_pp File 4.88 KB 0755
jsonpointer File 1.79 KB 0755
jsonschema File 213 B 0755
kactivities-cli File 66.01 KB 0755
kanshi File 66.1 KB 0755
kazam File 6.28 KB 0755
kbd_mode File 66.69 KB 0755
kbdinfo File 66.54 KB 0755
kbookmarkmerger File 66.09 KB 0755
kbuildsycoca5 File 65.93 KB 0755
kbxutil File 194.61 KB 0755
kcookiejar5 File 66.15 KB 0755
kde-geo-uri-handler File 65.93 KB 0755
kdeconnect-app File 66.09 KB 0755
kdeconnect-cli File 66.09 KB 0755
kdeconnect-handler File 66.09 KB 0755
kdeconnect-indicator File 130.16 KB 0755
kdeconnect-settings File 66.09 KB 0755
kdeconnect-sms File 258.28 KB 0755
kded5 File 130.01 KB 0755
kdeinit5 File 66.12 KB 0755
kdeinit5_shutdown File 66.08 KB 0755
kdeinit5_wrapper File 66.08 KB 0755
kdenlive File 9 MB 0755
kdenlive_render File 130.16 KB 0755
kdtc File 5.51 KB 0755
keditbookmarks File 386.16 KB 0755
kernel-install File 12.75 KB 0755
keyctl File 66.22 KB 0755
kglobalaccel5 File 66.09 KB 0755
kiconfinder5 File 66.09 KB 0755
kill File 66.16 KB 0755
killall File 67.81 KB 0755
kmod File 194.28 KB 0755
kmsblank File 66.25 KB 0755
kmsprint File 66.28 KB 0755
kmstest File 259.27 KB 0755
kpackagelauncherqml File 65.93 KB 0755
kpackagetool5 File 130.01 KB 0755
kquitapp5 File 65.93 KB 0755
kreadconfig5 File 66.09 KB 0755
kshell5 File 66.08 KB 0755
ktelnetservice5 File 66.08 KB 0755
ktrash5 File 66.08 KB 0755
kwallet-query File 66.16 KB 0755
kwalletd5 File 578.27 KB 0755
kwrapper5 File 66.08 KB 0755
kwriteconfig5 File 66.09 KB 0755
l2ping File 66.09 KB 0755
l2test File 66.41 KB 0755
labwc File 514.95 KB 0755
labwc-pi File 391 B 0755
labwc-prompt File 66.18 KB 0755
lame File 130.18 KB 0755
laptop-detect File 3.73 KB 0755
last File 66.16 KB 0755
lastb File 66.16 KB 0755
lastlog File 67.55 KB 0755
lc-compliance File 514.27 KB 0755
lcf File 7.6 KB 0755
ld File 1.63 MB 0755
ld.bfd File 1.63 MB 0755
ld.gold File 5.26 MB 0755
ld.so File 198.16 KB 0755
ldd File 5.19 KB 0755
leaftoppm File 65.8 KB 0755
less File 209.96 KB 0755
lessecho File 66.1 KB 0755
lessfile File 8.83 KB 0755
lesskey File 67.48 KB 0755
lesspipe File 8.83 KB 0755
letsencrypt File 958 B 0755
lexgrog File 131.28 KB 0755
libcamerify File 844 B 0755
libinput File 67.37 KB 0755
libnetcfg File 15.41 KB 0755
libsixel-config File 1.81 KB 0755
link File 66.8 KB 0755
linux-check-removal File 4.56 KB 0755
linux-update-symlinks File 6.17 KB 0755
linux-version File 2.63 KB 0755
linux32 File 66.41 KB 0755
linux64 File 66.41 KB 0755
lispmtopgm File 65.8 KB 0755
ln File 67.05 KB 0755
lnstat File 66.43 KB 0755
loadkeys File 259.43 KB 0755
loadunimap File 66.88 KB 0755
locale File 73.91 KB 0755
localectl File 66.23 KB 0755
localedef File 331.92 KB 0755
logger File 66.73 KB 0755
logilab-pytest File 134 B 0755
login File 67.59 KB 0755
loginctl File 66.38 KB 0755
logname File 66.8 KB 0755
logresolve File 66.09 KB 0755
look File 66.16 KB 0755
lowntfs-3g File 130.77 KB 0755
lp File 66.09 KB 0755
lp-connection-editor File 841.9 KB 0755
lp_solve File 46.86 KB 0755
lpoptions File 66.16 KB 0755
lpstat File 66.37 KB 0755
ls File 195.74 KB 0755
lsattr File 66.09 KB 0755
lsb_release File 2.59 KB 0755
lsblk File 258.16 KB 0755
lscpu File 130.16 KB 0755
lsfd File 132.14 KB 0755
lsinitramfs File 735 B 0755
lsipc File 130.16 KB 0755
lsirq File 66.33 KB 0755
lslocks File 130.54 KB 0755
lslogins File 130.19 KB 0755
lsmem File 130.16 KB 0755
lsmod File 194.28 KB 0755
lsns File 130.16 KB 0755
lsof File 203.44 KB 0755
lspci File 131.85 KB 0755
lspgpot File 1.06 KB 0755
lsusb File 295 KB 0755
lto-dump File 25.77 MB 0755
lto-dump-12 File 25.77 MB 0755
lua File 198.95 KB 0755
lua5.1 File 198.95 KB 0755
luac File 126.45 KB 0755
luac5.1 File 126.45 KB 0755
luajit File 578.96 KB 0755
lwp-download File 10.05 KB 0755
lwp-dump File 2.65 KB 0755
lwp-mirror File 2.36 KB 0755
lwp-request File 15.82 KB 0755
lwrespawn File 135 B 0755
lxappearance File 51.67 KB 0755
lxclipboard File 66.16 KB 0755
lxde-logout File 90 B 0755
lxhotkey File 17.93 KB 0755
lxpanel File 195.77 KB 0755
lxpanelctl File 65.93 KB 0755
lxpolkit File 66.01 KB 0755
lxrandr File 25.93 KB 0755
lxsession File 322.23 KB 0755
lxsession-db File 66.16 KB 0755
lxsession-default File 9 KB 0755
lxsession-default-terminal File 1019 B 0755
lxsession-edit File 65.93 KB 0755
lxsession-logout File 66.12 KB 0755
lxsession-xdg-autostart File 66.16 KB 0755
lxtask File 65.93 KB 0755
lxterminal File 133.39 KB 0755
lynx File 1.92 MB 0755
lzcat File 130.51 KB 0755
lzcmp File 7.25 KB 0755
lzdiff File 7.25 KB 0755
lzegrep File 10.09 KB 0755
lzfgrep File 10.09 KB 0755
lzgrep File 10.09 KB 0755
lzless File 1.77 KB 0755
lzma File 130.51 KB 0755
lzmainfo File 66.16 KB 0755
lzmore File 2.14 KB 0755
mac2unix File 54.09 KB 0755
macptopbm File 65.8 KB 0755
make File 238.46 KB 0755
make-first-existing-target File 4.79 KB 0755
makeconv File 66.85 KB 0755
malcontent-client File 22.54 KB 0755
malcontent-control File 66.12 KB 0755
man File 133.16 KB 0755
man-recode File 67.14 KB 0755
mandb File 195.43 KB 0755
manpath File 66.55 KB 0755
mapscrn File 66.88 KB 0755
mariadb File 5.05 MB 0755
mariadb-access File 109.31 KB 0755
mariadb-admin File 4.83 MB 0755
mariadb-analyze File 4.83 MB 0755
mariadb-binlog File 5.09 MB 0755
mariadb-check File 4.83 MB 0755
mariadb-conv File 4.57 MB 0755
mariadb-convert-table-format File 4.12 KB 0755
mariadb-dump File 4.96 MB 0755
mariadb-dumpslow File 8.05 KB 0755
mariadb-find-rows File 3.21 KB 0755
mariadb-fix-extensions File 1.22 KB 0755
mariadb-hotcopy File 34.53 KB 0755
mariadb-import File 4.83 MB 0755
mariadb-install-db File 22.13 KB 0755
mariadb-optimize File 4.83 MB 0755
mariadb-plugin File 4.57 MB 0755
mariadb-repair File 4.83 MB 0755
mariadb-report File 49.02 KB 0755
mariadb-secure-installation File 13.49 KB 0755
mariadb-service-convert File 2.45 KB 0755
mariadb-setpermission File 17.56 KB 0755
mariadb-show File 4.83 MB 0755
mariadb-slap File 4.83 MB 0755
mariadb-tzinfo-to-sql File 4.57 MB 0755
mariadb-upgrade File 4.96 MB 0755
mariadb-waitpid File 4.51 MB 0755
mariadbcheck File 4.83 MB 0755
mariadbd-multi File 26.69 KB 0755
mariadbd-safe File 30.42 KB 0755
mariadbd-safe-helper File 4.51 MB 0755
markdown-it File 220 B 0755
markdown_py File 969 B 0755
mate-polkit File 61 B 0755
mawk File 150.49 KB 0755
mbim-network File 11.08 KB 0755
mbimcli File 199.46 KB 0755
mcookie File 66.23 KB 0755
md5sum File 66.91 KB 0755
md5sum.textutils File 66.91 KB 0755
mdatopbm File 65.8 KB 0755
mdig File 66.18 KB 0755
media-ctl File 66.78 KB 0755
melt File 65.93 KB 0755
melt-7 File 65.93 KB 0755
memusage File 7.3 KB 0755
memusagestat File 66.42 KB 0755
mencoder File 2.57 MB 0755
mesa-overlay-control.py File 5.59 KB 0755
mesg File 66.16 KB 0755
meson File 903 B 0755
mgrtopbm File 65.8 KB 0755
migrate-pubring-from-classic-gpg File 2.99 KB 0755
mk_modmap File 15.78 KB 0755
mkdir File 131.23 KB 0755
mkfifo File 67.13 KB 0755
mkfontdir File 65 B 0755
mkfontscale File 34.9 KB 0755
mkisofs File 665.41 KB 0755
mknod File 67.16 KB 0755
mktemp File 66.87 KB 0755
mkvextract File 3.57 MB 0755
mkvinfo File 2.7 MB 0755
mkvmerge File 6.14 MB 0755
mkvpropedit File 3.01 MB 0755
mkzftree File 66.66 KB 0755
mmcli File 265.49 KB 0755
modeline2fb File 4.31 KB 0755
mogrify File 66.09 KB 0755
mogrify-im6 File 66.09 KB 0755
mogrify-im6.q16 File 66.09 KB 0755
monitor-sensor File 14.09 KB 0755
montage File 66.09 KB 0755
montage-im6 File 66.09 KB 0755
montage-im6.q16 File 66.09 KB 0755
more File 66.16 KB 0755
mount File 66.16 KB 4755
mountpoint File 66.16 KB 0755
mousepad File 66.09 KB 0755
mpeg2desc File 66.17 KB 0755
mplayer File 3.01 MB 0755
mpris-proxy File 130.35 KB 0755
mrftopbm File 65.8 KB 0755
msql2mysql File 1.41 KB 0755
mt File 131.12 KB 0755
mt-gnu File 131.12 KB 0755
mtrace File 6.35 KB 0755
mtvtoppm File 65.8 KB 0755
mv File 131.57 KB 0755
mvxattr File 66.08 KB 0755
my_print_defaults File 4.51 MB 0755
myisam_ftdump File 4.83 MB 0755
myisamchk File 4.96 MB 0755
myisamlog File 4.83 MB 0755
myisampack File 4.89 MB 0755
mypy File 230 B 0755
mypyc File 213 B 0755
mysql File 5.05 MB 0755
mysql_convert_table_format File 4.12 KB 0755
mysql_find_rows File 3.21 KB 0755
mysql_fix_extensions File 1.22 KB 0755
mysql_install_db File 22.13 KB 0755
mysql_plugin File 4.57 MB 0755
mysql_secure_installation File 13.49 KB 0755
mysql_setpermission File 17.56 KB 0755
mysql_tzinfo_to_sql File 4.57 MB 0755
mysql_upgrade File 4.96 MB 0755
mysql_waitpid File 4.51 MB 0755
mysqlaccess File 109.31 KB 0755
mysqladmin File 4.83 MB 0755
mysqlanalyze File 4.83 MB 0755
mysqlbinlog File 5.09 MB 0755
mysqlcheck File 4.83 MB 0755
mysqld_multi File 26.69 KB 0755
mysqld_safe File 30.42 KB 0755
mysqld_safe_helper File 4.51 MB 0755
mysqldump File 4.96 MB 0755
mysqldumpslow File 8.05 KB 0755
mysqlhotcopy File 34.53 KB 0755
mysqlimport File 4.83 MB 0755
mysqloptimize File 4.83 MB 0755
mysqlrepair File 4.83 MB 0755
mysqlreport File 49.02 KB 0755
mysqlshow File 4.83 MB 0755
mysqlslap File 4.83 MB 0755
mytop File 71.95 KB 0755
named-checkconf File 66.16 KB 0755
named-checkzone File 66.16 KB 0755
named-compilezone File 66.16 KB 0755
named-journalprint File 66.08 KB 0755
named-nzd2nzf File 66.08 KB 0755
named-rrchecker File 66.08 KB 0755
namei File 66.16 KB 0755
nano File 324.64 KB 0755
nawk File 712.3 KB 0755
ncdu File 130.07 KB 0755
neofetch File 333.58 KB 0755
neotoppm File 65.8 KB 0755
neqn File 913 B 0755
net File 1.02 MB 0755
netstat File 199.34 KB 0755
networkctl File 130.49 KB 0755
newgrp File 67.55 KB 4755
ngettext File 66.16 KB 0755
nice File 66.84 KB 0755
ninja File 323.49 KB 0755
nisdomainname File 66.01 KB 0755
nl File 131.08 KB 0755
nm File 67.84 KB 0755
nm-applet File 259.98 KB 0755
nm-connection-editor File 770.78 KB 0755
nm-online File 66.08 KB 0755
nmblookup File 194.26 KB 0755
nmcli File 1007.52 KB 0755
nmtui File 805.5 KB 0755
nmtui-connect File 805.5 KB 0755
nmtui-edit File 805.5 KB 0755
nmtui-hostname File 805.5 KB 0755
nohup File 66.86 KB 0755
noip-duc File 2.51 MB 0755
normalizer File 244 B 0755
nproc File 66.85 KB 0755
nroff File 3.22 KB 0755
nsec3hash File 66.09 KB 0755
nsenter File 66.38 KB 0755
nslookup File 130.55 KB 0755
nstat File 144.27 KB 0755
nsupdate File 66.29 KB 0755
ntfs-3g File 194.8 KB 4755
ntfs-3g.probe File 66.16 KB 0755
ntfscat File 66.19 KB 0755
ntfscluster File 66.19 KB 0755
ntfscmp File 66.19 KB 0755
ntfsdecrypt File 66.2 KB 0755
ntfsfallocate File 66.2 KB 0755
ntfsfix File 66.29 KB 0755
ntfsinfo File 66.2 KB 0755
ntfsls File 67.24 KB 0755
ntfsmove File 66.19 KB 0755
ntfsrecover File 130.28 KB 0755
ntfssecaudit File 130.66 KB 0755
ntfstruncate File 66.12 KB 0755
ntfsusermap File 66.11 KB 0755
ntfswipe File 66.7 KB 0755
ntpmon File 20.7 KB 0755
ntpq File 69.28 KB 0755
ntptrace File 5.39 KB 0755
numfmt File 130.98 KB 0755
nvlc File 47 B 0755
oLschema2ldif File 66.08 KB 0755
obamenu File 6.76 KB 0755
obconf File 73.93 KB 0755
obexctl File 130.17 KB 0755
obfs4proxy File 4.87 MB 0755
objcopy File 199.55 KB 0755
objdump File 394.5 KB 0755
obxprop File 66.08 KB 0755
od File 130.96 KB 0755
ogg123 File 131.48 KB 0755
oggdec File 66.45 KB 0755
oggenc File 131.88 KB 0755
ogginfo File 66.17 KB 0755
open File 25.46 KB 0755
openbox File 387.55 KB 0755
openbox-lxde File 77 B 0755
openbox-lxde-pi File 80 B 0755
openbox-session File 595 B 0755
openocd File 4.16 MB 0755
openssl File 992.91 KB 0755
openvt File 66.98 KB 0755
otpset File 5.79 KB 0755
overlaycheck File 25.35 KB 0755
ovmerge File 46.81 KB 0755
p11-kit File 66.17 KB 0755
p7zip File 4.64 KB 0755
pa-info File 2.29 KB 0755
pacat File 66.2 KB 0755
pacmd File 66.16 KB 0755
pactl File 130.18 KB 0755
padsp File 2.18 KB 0755
pager File 209.96 KB 0755
palmtopnm File 65.8 KB 0755
pamaddnoise File 65.8 KB 0755
pamaltsat File 65.84 KB 0755
pamarith File 65.8 KB 0755
pambackground File 65.8 KB 0755
pambayer File 65.88 KB 0755
pambrighten File 65.8 KB 0755
pamcat File 65.8 KB 0755
pamchannel File 65.8 KB 0755
pamcomp File 65.8 KB 0755
pamcrater File 65.8 KB 0755
pamcut File 65.8 KB 0755
pamdeinterlace File 65.8 KB 0755
pamdepth File 65.8 KB 0755
pamdice File 65.8 KB 0755
pamditherbw File 65.81 KB 0755
pamedge File 65.8 KB 0755
pamendian File 65.8 KB 0755
pamenlarge File 65.88 KB 0755
pamexec File 65.8 KB 0755
pamfile File 65.8 KB 0755
pamfind File 65.8 KB 0755
pamfix File 65.8 KB 0755
pamfixtrunc File 2.01 KB 0755
pamflip File 65.8 KB 0755
pamfunc File 65.8 KB 0755
pamgauss File 65.8 KB 0755
pamgetcolor File 65.88 KB 0755
pamgradient File 65.8 KB 0755
pamhomography File 65.8 KB 0755
pamhue File 65.8 KB 0755
pamlevels File 65.8 KB 0755
pamlookup File 65.8 KB 0755
pammasksharpen File 65.8 KB 0755
pammixinterlace File 65.88 KB 0755
pammixmulti File 65.8 KB 0755
pammosaicknit File 65.8 KB 0755
pamoil File 65.8 KB 0755
pamon File 66.2 KB 0755
pampaintspill File 65.8 KB 0755
pamperspective File 65.88 KB 0755
pampick File 65.8 KB 0755
pampop9 File 65.8 KB 0755
pamrecolor File 65.8 KB 0755
pamrestack File 65.8 KB 0755
pamrgbatopng File 65.8 KB 0755
pamrubber File 65.8 KB 0755
pamscale File 65.88 KB 0755
pamseq File 65.8 KB 0755
pamshadedrelief File 65.8 KB 0755
pamsharpmap File 65.8 KB 0755
pamsharpness File 65.8 KB 0755
pamshuffle File 65.8 KB 0755
pamsistoaglyph File 65.8 KB 0755
pamslice File 65.8 KB 0755
pamsplit File 65.8 KB 0755
pamstack File 65.8 KB 0755
pamstereogram File 65.8 KB 0755
pamstretch File 65.8 KB 0755
pamstretch-gen File 3.83 KB 0755
pamsumm File 65.8 KB 0755
pamsummcol File 65.8 KB 0755
pamtable File 65.8 KB 0755
pamthreshold File 65.8 KB 0755
pamtilt File 65.8 KB 0755
pamtoavs File 65.8 KB 0755
pamtodjvurle File 65.8 KB 0755
pamtofits File 65.8 KB 0755
pamtogif File 65.8 KB 0755
pamtohdiff File 65.8 KB 0755
pamtohtmltbl File 65.8 KB 0755
pamtojpeg2k File 264.27 KB 0755
pamtompfont File 65.8 KB 0755
pamtooctaveimg File 65.8 KB 0755
pamtopam File 65.8 KB 0755
pamtopdbimg File 65.88 KB 0755
pamtopfm File 65.8 KB 0755
pamtopng File 65.8 KB 0755
pamtopnm File 65.8 KB 0755
pamtoqoi File 65.8 KB 0755
pamtosrf File 65.8 KB 0755
pamtosvg File 65.8 KB 0755
pamtotga File 65.8 KB 0755
pamtotiff File 65.88 KB 0755
pamtouil File 65.8 KB 0755
pamtowinicon File 65.8 KB 0755
pamtoxvmini File 65.8 KB 0755
pamtris File 65.8 KB 0755
pamundice File 65.8 KB 0755
pamunlookup File 65.8 KB 0755
pamvalidate File 65.8 KB 0755
pamwipeout File 65.8 KB 0755
pamx File 65.88 KB 0755
paperconf File 66.23 KB 0755
paplay File 66.2 KB 0755
parec File 66.2 KB 0755
parecord File 66.2 KB 0755
parse-edid File 21.73 KB 0755
partx File 130.16 KB 0755
passwd File 70.36 KB 4755
paste File 66.84 KB 0755
pastebinit File 16.31 KB 0755
pasuspender File 66.17 KB 0755
patch File 183.36 KB 0755
pathchk File 66.82 KB 0755
pax11publish File 66.09 KB 0755
pbget File 2.51 KB 0755
pbmclean File 65.8 KB 0755
pbmlife File 65.8 KB 0755
pbmmake File 65.8 KB 0755
pbmmask File 65.8 KB 0755
pbmminkowski File 65.8 KB 0755
pbmnoise File 65.8 KB 0755
pbmpage File 65.8 KB 0755
pbmpscale File 65.8 KB 0755
pbmreduce File 65.8 KB 0755
pbmtext File 65.97 KB 0755
pbmtextps File 65.8 KB 0755
pbmto10x File 65.8 KB 0755
pbmto4425 File 65.8 KB 0755
pbmtoascii File 65.8 KB 0755
pbmtoatk File 65.8 KB 0755
pbmtobbnbg File 65.8 KB 0755
pbmtocis File 65.8 KB 0755
pbmtocmuwm File 65.8 KB 0755
pbmtodjvurle File 65.8 KB 0755
pbmtoepsi File 65.8 KB 0755
pbmtoepson File 65.8 KB 0755
pbmtoescp2 File 65.8 KB 0755
pbmtog3 File 66.63 KB 0755
pbmtogem File 65.8 KB 0755
pbmtogo File 65.8 KB 0755
pbmtoibm23xx File 65.8 KB 0755
pbmtoicon File 65.8 KB 0755
pbmtolj File 65.8 KB 0755
pbmtoln03 File 65.8 KB 0755
pbmtolps File 65.8 KB 0755
pbmtomacp File 65.8 KB 0755
pbmtomatrixorbital File 65.8 KB 0755
pbmtomda File 65.8 KB 0755
pbmtomgr File 65.8 KB 0755
pbmtomrf File 65.8 KB 0755
pbmtonokia File 65.8 KB 0755
pbmtopgm File 65.8 KB 0755
pbmtopi3 File 65.8 KB 0755
pbmtopk File 66.09 KB 0755
pbmtoplot File 65.8 KB 0755
pbmtoppa File 65.88 KB 0755
pbmtopsg3 File 67.43 KB 0755
pbmtoptx File 65.8 KB 0755
pbmtosunicon File 65.8 KB 0755
pbmtowbmp File 65.8 KB 0755
pbmtox10bm File 2.83 KB 0755
pbmtoxbm File 65.8 KB 0755
pbmtoybm File 65.8 KB 0755
pbmtozinc File 65.8 KB 0755
pbmupc File 65.8 KB 0755
pbput File 2.51 KB 0755
pbputs File 2.51 KB 0755
pc1toppm File 65.8 KB 0755
pcdindex File 6.56 KB 0755
pcdovtoppm File 6.56 KB 0755
pcmanfm File 327.73 KB 0755
pcre2-config File 1.93 KB 0755
pcxtoppm File 65.8 KB 0755
pdb3 File 62.4 KB 0755
pdb3.11 File 62.4 KB 0755
pdbedit File 66.08 KB 0755
pdbimgtopam File 65.88 KB 0755
pdf2dsc File 698 B 0755
pdf2ps File 909 B 0755
pdfattach File 66.16 KB 0755
pdfdetach File 66.24 KB 0755
pdffonts File 66.25 KB 0755
pdfimages File 66.25 KB 0755
pdfinfo File 66.26 KB 0755
pdfseparate File 66.16 KB 0755
pdfsig File 66.52 KB 0755
pdftocairo File 194.24 KB 0755
pdftohtml File 130.23 KB 0755
pdftoppm File 66.2 KB 0755
pdftops File 66.27 KB 0755
pdftotext File 66.27 KB 0755
pdfunite File 66.16 KB 0755
pear File 793 B 0755
peardev File 814 B 0755
pecl File 727 B 0755
peekfd File 66.63 KB 0755
perl File 3.51 MB 0755
perl5.36-aarch64-linux-gnu File 66.29 KB 0755
perl5.36.0 File 3.51 MB 0755
perlbug File 44.12 KB 0755
perldoc File 125 B 0755
perlivp File 10.61 KB 0755
perlthanks File 44.12 KB 0755
perror File 4.73 MB 0755
pf2afm File 498 B 0755
pfbtopfa File 516 B 0755
pfmtopam File 65.8 KB 0755
pgmabel File 65.8 KB 0755
pgmbentley File 65.8 KB 0755
pgmcrater File 3.49 KB 0755
pgmdeshadow File 65.8 KB 0755
pgmedge File 65.8 KB 0755
pgmenhance File 65.8 KB 0755
pgmhist File 65.8 KB 0755
pgmkernel File 65.8 KB 0755
pgmmake File 65.8 KB 0755
pgmmedian File 65.8 KB 0755
pgmminkowski File 65.8 KB 0755
pgmmorphconv File 65.8 KB 0755
pgmnoise File 65.8 KB 0755
pgmnorm File 65.8 KB 0755
pgmoil File 65.8 KB 0755
pgmramp File 65.8 KB 0755
pgmslice File 65.8 KB 0755
pgmtexture File 65.8 KB 0755
pgmtofs File 65.8 KB 0755
pgmtolispm File 65.8 KB 0755
pgmtopbm File 65.8 KB 0755
pgmtopgm File 65.8 KB 0755
pgmtoppm File 65.8 KB 0755
pgmtosbig File 65.8 KB 0755
pgmtost4 File 65.8 KB 0755
pgrep File 66.24 KB 0755
pgzrun File 371 B 0755
phar File 14.88 KB 0755
phar.phar File 14.88 KB 0755
phar.phar8.2 File 14.88 KB 0755
phar8.2 File 14.88 KB 0755
phar8.2.phar File 14.88 KB 0755
php File 5.38 MB 0755
php8.2 File 5.38 MB 0755
phpdbg File 5.38 MB 0755
phpdbg8.2 File 5.38 MB 0755
pi-gpk-dbus-service File 196.68 KB 0755
pi-gpk-install-local-file File 67.23 KB 0755
pi-gpk-log File 131.61 KB 0755
pi-gpk-prefs File 131.62 KB 0755
pi-gpk-update-viewer File 197.04 KB 0755
pi-packages File 197.5 KB 0755
pi1toppm File 65.8 KB 0755
pi3topbm File 65.8 KB 0755
pic File 260.11 KB 0755
piclone File 66.53 KB 0755
pico File 324.64 KB 0755
piconv File 8.16 KB 0755
pidof File 66.21 KB 0755
pidwait File 66.24 KB 0755
pig2vcd File 9.97 KB 0755
pigpiod File 14.22 KB 0755
pigs File 43.48 KB 0755
pinctrl File 66.48 KB 0755
pinentry File 130.45 KB 0755
pinentry-curses File 66.45 KB 0755
pinentry-gnome3 File 130.45 KB 0755
pinentry-x11 File 130.45 KB 0755
ping File 144.36 KB 4755
ping4 File 144.36 KB 4755
ping6 File 144.36 KB 4755
pinky File 66.98 KB 0755
pinout File 959 B 0755
pintest File 961 B 0755
pip File 221 B 0755
pip3 File 221 B 0755
pip3.11 File 221 B 0755
pipanel File 131.23 KB 0755
pipewire File 66.16 KB 0755
pipewire-aes67 File 66.16 KB 0755
pipewire-avb File 66.16 KB 0755
pipewire-pulse File 66.16 KB 0755
pishutdown File 66.21 KB 0755
piwiz File 131.74 KB 0755
pjtoppm File 65.81 KB 0755
pkaction File 66.08 KB 0755
pkcheck File 66.08 KB 0755
pkexec File 66.05 KB 4755
pkg-config File 67.88 KB 0755
pkgconf File 67.88 KB 0755
pkgdata File 67.3 KB 0755
pkill File 66.24 KB 0755
pktopbm File 65.8 KB 0755
pkttyagent File 66.08 KB 0755
pl2pm File 4.43 KB 0755
plasmapkg2 File 66.09 KB 0755
play File 66.23 KB 0755
pldd File 66.35 KB 0755
plog File 146 B 0755
plymouth File 66.7 KB 0755
pmap File 66.19 KB 0755
pngtopam File 65.88 KB 0755
pngtopnm File 65.88 KB 0755
pnmalias File 65.8 KB 0755
pnmarith File 65.8 KB 0755
pnmcat File 65.8 KB 0755
pnmcolormap File 65.8 KB 0755
pnmcomp File 65.8 KB 0755
pnmconvol File 65.8 KB 0755
pnmcrop File 65.88 KB 0755
pnmcut File 65.8 KB 0755
pnmdepth File 65.8 KB 0755
pnmenlarge File 65.88 KB 0755
pnmfile File 65.8 KB 0755
pnmflip File 3.63 KB 0755
pnmgamma File 65.8 KB 0755
pnmhisteq File 65.8 KB 0755
pnmhistmap File 65.8 KB 0755
pnmindex File 65.8 KB 0755
pnminterp File 65.8 KB 0755
pnminterp-gen File 3.83 KB 0755
pnminvert File 65.8 KB 0755
pnmmargin File 2.75 KB 0755
pnmmercator File 65.8 KB 0755
pnmmontage File 65.8 KB 0755
pnmnlfilt File 65.85 KB 0755
pnmnoraw File 31 B 0755
pnmnorm File 65.8 KB 0755
pnmpad File 65.8 KB 0755
pnmpaste File 65.8 KB 0755
pnmpsnr File 65.8 KB 0755
pnmquant File 10.63 KB 0755
pnmquantall File 6.84 KB 0755
pnmremap File 65.8 KB 0755
pnmrotate File 65.8 KB 0755
pnmscale File 65.88 KB 0755
pnmscalefixed File 65.8 KB 0755
pnmshear File 65.8 KB 0755
pnmsmooth File 65.8 KB 0755
pnmsplit File 65.8 KB 0755
pnmstitch File 66.52 KB 0755
pnmtile File 65.8 KB 0755
pnmtoddif File 65.8 KB 0755
pnmtofiasco File 195.65 KB 0755
pnmtofits File 65.8 KB 0755
pnmtojbig File 76.63 KB 0755
pnmtojpeg File 65.8 KB 0755
pnmtopalm File 65.8 KB 0755
pnmtopclxl File 66.3 KB 0755
pnmtoplainpnm File 31 B 0755
pnmtopng File 65.8 KB 0755
pnmtopnm File 65.8 KB 0755
pnmtops File 65.8 KB 0755
pnmtorast File 65.8 KB 0755
pnmtorle File 66.04 KB 0755
pnmtosgi File 65.8 KB 0755
pnmtosir File 65.8 KB 0755
pnmtotiff File 65.88 KB 0755
pnmtotiffcmyk File 65.88 KB 0755
pnmtoxwd File 65.8 KB 0755
pod2html File 4.04 KB 0755
pod2man File 14.68 KB 0755
pod2text File 10.55 KB 0755
pod2usage File 4.01 KB 0755
podchecker File 3.57 KB 0755
poff File 2.77 KB 0755
pon File 1.33 KB 0755
powerprofilesctl File 8.62 KB 0755
ppdc File 130.3 KB 0755
ppdhtml File 130.3 KB 0755
ppdi File 130.3 KB 0755
ppdmerge File 66.16 KB 0755
ppdpo File 130.3 KB 0755
pphs File 404 B 0755
ppm3d File 65.8 KB 0755
ppmbrighten File 2 KB 0755
ppmchange File 65.8 KB 0755
ppmcie File 65.96 KB 0755
ppmcolormask File 65.8 KB 0755
ppmcolors File 65.8 KB 0755
ppmdcfont File 65.8 KB 0755
ppmddumpfont File 65.8 KB 0755
ppmdim File 65.8 KB 0755
ppmdist File 65.8 KB 0755
ppmdither File 65.8 KB 0755
ppmdmkfont File 69.25 KB 0755
ppmdraw File 65.8 KB 0755
ppmfade File 11.78 KB 0755
ppmflash File 65.8 KB 0755
ppmforge File 65.8 KB 0755
ppmglobe File 65.8 KB 0755
ppmhist File 65.8 KB 0755
ppmlabel File 65.8 KB 0755
ppmmake File 65.8 KB 0755
ppmmix File 65.8 KB 0755
ppmnorm File 65.8 KB 0755
ppmntsc File 65.8 KB 0755
ppmpat File 65.8 KB 0755
ppmquant File 2.3 KB 0755
ppmquantall File 6.84 KB 0755
ppmrainbow File 3.48 KB 0755
ppmrelief File 65.8 KB 0755
ppmrough File 65.8 KB 0755
ppmshadow File 10.85 KB 0755
ppmshift File 65.8 KB 0755
ppmspread File 65.8 KB 0755
ppmtoacad File 66.63 KB 0755
ppmtoapplevol File 65.8 KB 0755
ppmtoarbtxt File 65.8 KB 0755
ppmtoascii File 65.88 KB 0755
ppmtobmp File 65.8 KB 0755
ppmtoeyuv File 65.8 KB 0755
ppmtogif File 65.8 KB 0755
ppmtoicr File 65.8 KB 0755
ppmtoilbm File 65.89 KB 0755
ppmtojpeg File 65.8 KB 0755
ppmtoleaf File 65.8 KB 0755
ppmtolj File 65.8 KB 0755
ppmtomap File 82 B 0755
ppmtomitsu File 65.8 KB 0755
ppmtompeg File 489.51 KB 0755
ppmtoneo File 65.8 KB 0755
ppmtopcx File 65.8 KB 0755
ppmtopgm File 65.8 KB 0755
ppmtopi1 File 65.8 KB 0755
ppmtopict File 65.8 KB 0755
ppmtopj File 65.88 KB 0755
ppmtopjxl File 65.88 KB 0755
ppmtoppm File 65.8 KB 0755
ppmtopuzz File 65.8 KB 0755
ppmtorgb3 File 65.8 KB 0755
ppmtosixel File 65.8 KB 0755
ppmtospu File 65.8 KB 0755
ppmtoterm File 65.8 KB 0755
ppmtotga File 65.8 KB 0755
ppmtouil File 65.8 KB 0755
ppmtowinicon File 65.8 KB 0755
ppmtoxpm File 65.8 KB 0755
ppmtoyuv File 65.8 KB 0755
ppmtoyuvsplit File 65.8 KB 0755
ppmtv File 65.8 KB 0755
ppmwheel File 65.8 KB 0755
pprompt.sh File 336 B 0755
pr File 131.08 KB 0755
preconv File 66.29 KB 0755
preparetips5 File 1019 B 0755
print File 18.06 KB 0755
printafm File 395 B 0755
printenv File 66.8 KB 0755
printf File 66.88 KB 0755
prlimit File 66.66 KB 0755
procan File 131.73 KB 0755
profiles File 66.08 KB 0755
proj File 66.19 KB 0755
projinfo File 130.18 KB 0755
projsync File 130.26 KB 0755
protocoltojson File 66.08 KB 0755
prove File 13.34 KB 0755
proxy File 65.93 KB 0755
prtstat File 66.55 KB 0755
ps File 194.78 KB 0755
ps2ascii File 631 B 0755
ps2epsi File 1.23 KB 0755
ps2pdf File 272 B 0755
ps2pdf12 File 215 B 0755
ps2pdf13 File 215 B 0755
ps2pdf14 File 215 B 0755
ps2pdfwr File 1.05 KB 0755
ps2ps File 647 B 0755
ps2ps2 File 669 B 0755
ps2txt File 631 B 0755
psfaddtable File 66.5 KB 0755
psfgettable File 66.5 KB 0755
psfstriptable File 66.5 KB 0755
psfxtable File 66.5 KB 0755
psidtopgm File 65.8 KB 0755
pslog File 66.34 KB 0755
pstopnm File 65.8 KB 0755
pstree File 67.59 KB 0755
pstree.x11 File 67.59 KB 0755
ptar File 3.48 KB 0755
ptardiff File 2.58 KB 0755
ptargrep File 4.29 KB 0755
ptx File 131.17 KB 0755
pv File 71.81 KB 0755
pw-cat File 194.16 KB 0755
pw-cli File 130.26 KB 0755
pw-config File 66.16 KB 0755
pw-container File 66.16 KB 0755
pw-dot File 66.16 KB 0755
pw-dsdplay File 194.16 KB 0755
pw-dump File 130.23 KB 0755
pw-encplay File 194.16 KB 0755
pw-link File 66.16 KB 0755
pw-loopback File 66.16 KB 0755
pw-metadata File 66.16 KB 0755
pw-mididump File 66.16 KB 0755
pw-midiplay File 194.16 KB 0755
pw-midirecord File 194.16 KB 0755
pw-mon File 130.2 KB 0755
pw-play File 194.16 KB 0755
pw-profiler File 66.16 KB 0755
pw-record File 194.16 KB 0755
pw-reserve File 66.16 KB 0755
pw-top File 130.16 KB 0755
pwd File 66.89 KB 0755
pwdx File 66.16 KB 0755
pwrkey File 170 B 0755
py3clean File 7.63 KB 0755
py3compile File 13 KB 0755
py3versions File 12.52 KB 0755
pyav File 210 B 0755
pybabel File 956 B 0755
pybabel-python3 File 956 B 0755
pydoc File 79 B 0755
pydoc3 File 79 B 0755
pydoc3.11 File 79 B 0755
pygettext3 File 23.67 KB 0755
pygettext3.11 File 23.67 KB 0755
pygmentize File 970 B 0755
pylint File 217 B 0755
pylint-config File 233 B 0755
pyreverse File 223 B 0755
pyserial-miniterm File 975 B 0755
pyserial-ports File 969 B 0755
python File 6.31 MB 0755
python-dotenv File 977 B 0755
python3 File 6.31 MB 0755
python3-config File 3.01 KB 0755
python3.11 File 6.31 MB 0755
python3.11-config File 3.01 KB 0755
pzstd File 642.34 KB 0755
qcam File 322.99 KB 0755
qmi-firmware-update File 195.8 KB 0755
qmi-network File 16.04 KB 0755
qmicli File 589.67 KB 0755
qoitopam File 65.8 KB 0755
qrttoppm File 65.8 KB 0755
qt-faststart File 66.08 KB 0755
qt5ct File 642.25 KB 0755
qvlc File 42 B 0755
raindrop File 67.55 KB 0755
ranlib File 67.01 KB 0755
raspi-config File 127.15 KB 0755
raspi-gpio File 71.55 KB 0755
raspinfo File 5.8 KB 0755
rasputin File 66.77 KB 0755
rasttopnm File 65.8 KB 0755
rawtopgm File 65.8 KB 0755
rawtoppm File 65.8 KB 0755
rbash File 1.28 MB 0755
rc_gui File 66.93 KB 0755
rctest File 66.12 KB 0755
rdjpgcom File 66.09 KB 0755
rdma File 208.41 KB 0755
rds-ctl File 66.88 KB 0755
readelf File 779.36 KB 0755
readlink File 66.85 KB 0755
realpath File 66.86 KB 0755
rec File 66.23 KB 0755
recordmydesktop File 87.72 KB 0755
red File 89 B 0755
regexp-assemble File 4.92 KB 0755
rename-user File 2.56 KB 0755
rename.ul File 66.16 KB 0755
renice File 66.16 KB 0755
replace File 4.51 MB 0755
reset File 66.09 KB 0755
resizepart File 130.16 KB 0755
resolve_stack_dump File 4.51 MB 0755
resolveip File 4.51 MB 0755
rev File 66.16 KB 0755
rfcomm File 66.5 KB 0755
rgb3toppm File 65.8 KB 0755
rgrep File 30 B 0755
rlatopam File 65.8 KB 0755
rletopnm File 66.05 KB 0755
rm File 66.98 KB 0755
rmdir File 66.88 KB 0755
rnano File 324.64 KB 0755
rotatelogs File 66.16 KB 0755
routel File 1.62 KB 0755
rp-bookshelf File 67.39 KB 0755
rp-prefapps File 67.16 KB 0755
rpcgen File 130.84 KB 0755
rpcinfo File 66.45 KB 0755
rpi-bootloader-key-convert File 1.37 KB 0755
rpi-connect File 7.44 MB 0755
rpi-connect-env File 216 B 0755
rpi-connectd File 12.63 MB 0755
rpi-eeprom-config File 23.46 KB 0755
rpi-eeprom-digest File 5.12 KB 0755
rpi-eeprom-update File 36.12 KB 0755
rpi-imager File 37 MB 0755
rpi-otp-private-key File 6.08 KB 0755
rpi-sign-bootcode File 10.86 KB 0755
rpi-update File 18.33 KB 0755
rpicam-hello File 66.62 KB 0755
rpicam-jpeg File 131.03 KB 0755
rpicam-raw File 195.13 KB 0755
rpicam-still File 195.36 KB 0755
rpicam-vid File 195.16 KB 0755
rpinters File 327.82 KB 0755
rrsync File 12.34 KB 0755
rst-buildhtml File 11.17 KB 0755
rst2html File 592 B 0755
rst2html4 File 714 B 0755
rst2html5 File 1.03 KB 0755
rst2latex File 791 B 0755
rst2man File 614 B 0755
rst2odt File 780 B 0755
rst2odt_prepstyles File 2.01 KB 0755
rst2pseudoxml File 599 B 0755
rst2s5 File 635 B 0755
rst2xetex File 871 B 0755
rst2xml File 600 B 0755
rstpep2html File 668 B 0755
rsync File 529.99 KB 0755
rsync-ssl File 5.02 KB 0755
rtstat File 66.43 KB 0755
run-mailcap File 18.06 KB 0755
run-parts File 66.79 KB 0755
runcon File 66.93 KB 0755
rview File 1.65 MB 0755
rvlc File 42 B 0755
rygel File 66.16 KB 0755
samba-regedit File 131.1 KB 0755
samba-tool File 1.52 KB 0755
sane-find-scanner File 195.11 KB 0755
savelog File 10.24 KB 0755
sbigtopgm File 65.8 KB 0755
sbout File 113 B 0755
sbtest File 186 B 0755
scalar File 2.16 MB 0755
scanimage File 67.04 KB 0755
scp File 322.48 KB 0755
scp-dbus-service File 90 B 0755
screendump File 66.36 KB 0755
script File 130.16 KB 0755
scriptlive File 66.16 KB 0755
scriptreplay File 66.16 KB 0755
scrot File 67.1 KB 0755
sdiff File 66.95 KB 0755
sdptool File 137.63 KB 0755
sed File 131.38 KB 0755
see File 18.06 KB 0755
select-default-iwrap File 474 B 0755
select-editor File 2.39 KB 0755
send2trash File 218 B 0755
sensible-browser File 1.26 KB 0755
sensible-editor File 1.24 KB 0755
sensible-pager File 565 B 0755
seq File 66.89 KB 0755
sessreg File 66.4 KB 0755
setarch File 66.41 KB 0755
setcifsacl File 66.09 KB 0755
setfacl File 66.68 KB 0755
setfattr File 66.37 KB 0755
setfont File 66.76 KB 0755
setkeycodes File 66.53 KB 0755
setleds File 66.52 KB 0755
setlogcons File 66.41 KB 0755
setmetamode File 66.64 KB 0755
setpci File 66.24 KB 0755
setpriv File 130.16 KB 0755
setsid File 66.16 KB 0755
setterm File 66.16 KB 0755
setup_env File 565 B 0755
setupcon File 39.17 KB 0755
setvtrgb File 66.63 KB 0755
setxkbmap File 22.88 KB 0755
sftp File 322.46 KB 0755
sg File 67.55 KB 4755
sgitopnm File 65.8 KB 0755
sh File 130.51 KB 0755
sha1sum File 66.91 KB 0755
sha224sum File 66.91 KB 0755
sha256sum File 66.91 KB 0755
sha384sum File 66.91 KB 0755
sha512sum File 66.91 KB 0755
sharesec File 66.16 KB 0755
shasum File 9.75 KB 0755
showconsolefont File 66.64 KB 0755
showkey File 66.63 KB 0755
showrgb File 66.28 KB 0755
shred File 67.07 KB 0755
shuf File 66.97 KB 0755
sirtopnm File 65.8 KB 0755
sixel2png File 10.09 KB 0755
size File 66.71 KB 0755
skill File 66.2 KB 0755
slabtop File 66.21 KB 0755
sldtoppm File 65.86 KB 0755
sleep File 66.83 KB 0755
slogin File 1.13 MB 0755
smb2-quota File 6.3 KB 0755
smbcontrol File 66.35 KB 0755
smbinfo File 28.64 KB 0755
smbpasswd File 66.16 KB 0755
smbstatus File 130.16 KB 0755
snice File 66.2 KB 0755
socat File 453.92 KB 0755
soelim File 66.29 KB 0755
sort File 131.62 KB 0755
sotruss File 4.19 KB 0755
sox File 66.23 KB 0755
soxi File 66.23 KB 0755
spa-acp-tool File 323.88 KB 0755
spa-inspect File 130.25 KB 0755
spa-json-dump File 66.16 KB 0755
spa-monitor File 66.25 KB 0755
spa-resample File 66.38 KB 0755
spctoppm File 65.8 KB 0755
speaker-test File 66.22 KB 0755
splain File 18.99 KB 0755
split File 67.51 KB 0755
splitfont File 66.27 KB 0755
spottopgm File 65.82 KB 0755
sprof File 66.55 KB 0755
spumux File 196.07 KB 0755
sputoppm File 65.8 KB 0755
spuunmux File 130.16 KB 0755
squeekboard File 31.45 MB 0755
squeekboard-restyled File 428 B 0755
srftopam File 65.8 KB 0755
ss File 208.91 KB 0755
ssconvert File 66.16 KB 0755
ssdiff File 66.16 KB 0755
ssgrep File 66.16 KB 0755
ssh File 1.13 MB 0755
ssh-add File 578.3 KB 0755
ssh-agent File 514.23 KB 2755
ssh-argv0 File 1.42 KB 0755
ssh-copy-id File 12.38 KB 0755
ssh-import-id File 409 B 0755
ssh-import-id-gh File 785 B 0755
ssh-import-id-lp File 785 B 0755
ssh-keygen File 706.41 KB 0755
ssh-keyscan File 706.45 KB 0755
sshfs File 132.55 KB 0755
ssindex File 66.16 KB 0755
st4topgm File 65.8 KB 0755
startlxde File 1.46 KB 0755
startlxde-pi File 659 B 0755
startx File 5.39 KB 0755
stat File 131.17 KB 0755
stdbuf File 66.88 KB 0755
strace File 1.56 MB 0755
strace-log-merge File 1.78 KB 0755
stream File 66.09 KB 0755
stream-im6 File 66.09 KB 0755
stream-im6.q16 File 66.09 KB 0755
streamzip File 7.75 KB 0755
strings File 66.84 KB 0755
strip File 199.56 KB 0755
stty File 130.95 KB 0755
stubgen File 211 B 0755
stubtest File 212 B 0755
su File 130.19 KB 4755
sudo File 263 KB 4755
sudoedit File 263 KB 4755
sudopwd File 66.36 KB 0755
sudoreplay File 131.53 KB 0755
sum File 66.93 KB 0755
sunicontopnm File 65.8 KB 0755
svgtopam File 65.8 KB 0755
svlc File 46 B 0755
swayidle File 66.11 KB 0755
swaylock File 132.25 KB 0755
symilar File 219 B 0755
sync File 66.84 KB 0755
systemctl File 1.32 MB 0755
systemd File 130.34 KB 0755
systemd-analyze File 194.76 KB 0755
systemd-ask-password File 66.34 KB 0755
systemd-cat File 66.23 KB 0755
systemd-cgls File 66.34 KB 0755
systemd-cgtop File 66.27 KB 0755
systemd-creds File 66.48 KB 0755
systemd-cryptenroll File 66.46 KB 0755
systemd-delta File 66.23 KB 0755
systemd-detect-virt File 66.23 KB 0755
systemd-escape File 66.23 KB 0755
systemd-firstboot File 66.45 KB 0755
systemd-hwdb File 130.85 KB 0755
systemd-id128 File 66.23 KB 0755
systemd-inhibit File 66.25 KB 0755
systemd-machine-id-setup File 66.34 KB 0755
systemd-mount File 66.45 KB 0755
systemd-notify File 66.23 KB 0755
systemd-path File 66.23 KB 0755
systemd-repart File 194.59 KB 0755
systemd-run File 66.51 KB 0755
systemd-socket-activate File 66.23 KB 0755
systemd-stdio-bridge File 66.23 KB 0755
systemd-sysext File 66.35 KB 0755
systemd-sysusers File 66.59 KB 0755
systemd-tmpfiles File 130.5 KB 0755
systemd-tty-ask-password-agent File 66.23 KB 0755
systemd-umount File 66.45 KB 0755
systemsettings File 194.16 KB 0755
systemsettings5 File 194.16 KB 0755
tabs File 66.09 KB 0755
tac File 131.01 KB 0755
tail File 131.07 KB 0755
tar File 527.24 KB 0755
tasksel File 26.82 KB 0755
taskset File 130.16 KB 0755
tbl File 194.29 KB 0755
tdbbackup File 66.09 KB 0755
tdbbackup.tdbtools File 66.09 KB 0755
tdbdump File 66.09 KB 0755
tdbrestore File 66.09 KB 0755
tdbtool File 66.57 KB 0755
tee File 66.88 KB 0755
tempfile File 66.31 KB 0755
test File 66.84 KB 0755
testparm File 66.13 KB 0755
tgatoppm File 65.8 KB 0755
thinkjettopbm File 65.8 KB 0755
thonny File 945 B 0755
tic File 130.2 KB 0755
tifftopnm File 65.88 KB 0755
timedatectl File 66.23 KB 0755
timeout File 67.42 KB 0755
tjbench File 66.4 KB 0755
tload File 66.17 KB 0755
toe File 66.09 KB 0755
toilet File 22.91 KB 0755
top File 135.34 KB 0755
tor File 3.36 MB 0755
tor-gencert File 481.04 KB 0755
tor-print-ed-signing-cert File 417.02 KB 0755
tor-resolve File 194.07 KB 0755
torify File 1.34 KB 0755
torsocks File 11.42 KB 0755
touch File 131.02 KB 0755
tput File 66.12 KB 0755
tqdm File 207 B 0755
tr File 66.87 KB 0755
tree File 131.66 KB 0755
troff File 815.7 KB 0755
true File 66.78 KB 0755
truncate File 66.85 KB 0755
trust File 258.41 KB 0755
tset File 66.09 KB 0755
tsort File 66.85 KB 0755
tty File 66.81 KB 0755
twolame File 27.05 KB 0755
tzselect File 15 KB 0755
ucf File 40.69 KB 0755
ucfq File 18.91 KB 0755
ucfr File 10.85 KB 0755
uclampset File 130.16 KB 0755
uconv File 66.6 KB 0755
ucs2any File 18.18 KB 0755
udevadm File 1.26 MB 0755
udisksctl File 66.16 KB 0755
ul File 66.16 KB 0755
umax_pp File 195.24 KB 0755
umount File 66.16 KB 4755
uname File 66.81 KB 0755
uncompress File 2.29 KB 0755
undill File 587 B 0755
unexpand File 66.88 KB 0755
unicode_start File 2.71 KB 0755
unicode_stop File 528 B 0755
uniq File 66.91 KB 0755
unix2dos File 54.09 KB 0755
unix2mac File 54.09 KB 0755
unlink File 66.8 KB 0755
unlzma File 130.51 KB 0755
unmkinitramfs File 3.59 KB 0755
unrar File 323.08 KB 0755
unrar-nonfree File 323.08 KB 0755
unshare File 130.38 KB 0755
unxz File 130.51 KB 0755
unzip File 194.95 KB 0755
unzipsfx File 130.76 KB 0755
unzstd File 1.13 MB 0755
update-alternatives File 66.17 KB 0755
update-desktop-database File 22.16 KB 0755
update-mime-database File 59.04 KB 0755
upower File 66.08 KB 0755
uptime File 66.16 KB 0755
usb-devices File 4.35 KB 0755
usbhid-dump File 66.64 KB 0755
usbreset File 66.36 KB 0755
users File 66.85 KB 0755
utmpdump File 66.16 KB 0755
uuid File 66.1 KB 0755
v4l2-compliance File 707.45 KB 0755
v4l2-ctl File 459.84 KB 0755
v4l2-sysfs-path File 66.23 KB 0755
vcgencmd File 66.3 KB 0755
vclog File 66.47 KB 0755
vcmailbox File 66.27 KB 0755
vcut File 66.09 KB 0755
vdir File 195.74 KB 0755
vi File 1.65 MB 0755
view File 1.65 MB 0755
vim.tiny File 1.65 MB 0755
vkcube File 259.17 KB 0755
vkcube-wayland File 259.4 KB 0755
vkcubepp File 323.52 KB 0755
vlc File 66.08 KB 0755
vlc-wrapper File 66.08 KB 0755
vmstat File 66.53 KB 0755
vncagent File 1.02 MB 0755
vncfopshelper File 824.33 KB 0755
vncinitconfig File 52.44 KB 0755
vnclicense File 1016.38 KB 0755
vnclicensewiz File 3.63 MB 0755
vncpamhelper File 720.35 KB 0755
vncpasswd File 648.23 KB 0755
vncserver File 581 B 0755
vncserver-virtual File 1.18 MB 0755
vncserver-virtuald File 3.07 MB 0755
vncserver-x11 File 1.54 MB 4755
vncserver-x11-core File 6.17 MB 0755
vncserver-x11-serviced File 624.28 KB 0755
vncserverui File 4.29 MB 0755
vncviewer File 10.79 MB 0755
vorbiscomment File 66.16 KB 0755
vorbistagedit File 4.32 KB 0755
vsftpdwho File 54 B 0755
vulkaninfo File 643.31 KB 0755
w File 66.19 KB 0755
w3m File 1.6 MB 0755
w3mman File 1.37 KB 0755
wall File 66.16 KB 0755
watch File 66.55 KB 0755
watchgnupg File 66.09 KB 0755
wayfire File 1.19 MB 0755
wayfire-pi File 510 B 0755
wayvnc File 201.99 KB 0755
wayvncctl File 69.46 KB 0755
wbmptopbm File 65.8 KB 0755
wc File 66.99 KB 0755
wcmgr File 65.98 KB 0755
wdctl File 130.19 KB 0755
webalizer File 212.18 KB 0755
webazolver File 212.18 KB 0755
wf-panel-pi File 711.14 KB 0755
wfpanelctl File 697 B 0755
wfrespawn File 135 B 0755
wget File 467.09 KB 0755
whatis File 67.03 KB 0755
whereis File 66.59 KB 0755
which File 946 B 0755
which.debianutils File 946 B 0755
whiptail File 66.65 KB 0755
who File 66.98 KB 0755
whoami File 66.81 KB 0755
winicontopam File 65.8 KB 0755
winicontoppm File 65.8 KB 0755
wireplumber File 66.23 KB 0755
wlopm File 66.05 KB 0755
wlr-randr File 66.1 KB 0755
wordview File 8.97 KB 0755
wpa_passphrase File 130.1 KB 0755
wpctl File 66.44 KB 0755
wpexec File 66.3 KB 0755
write File 66.16 KB 0755
wrjpgcom File 66.09 KB 0755
wsrep_sst_backup File 2.39 KB 0755
wsrep_sst_common File 66.86 KB 0755
wsrep_sst_mariabackup File 49.23 KB 0755
wsrep_sst_mysqldump File 8.11 KB 0755
wsrep_sst_rsync File 29.72 KB 0755
wsrep_sst_rsync_wan File 29.72 KB 0755
www-browser File 1.92 MB 0755
x-session-manager File 659 B 0755
x-terminal-emulator File 133.39 KB 0755
x-window-manager File 387.55 KB 0755
x-www-browser File 848 B 0755
xarchiver File 323.19 KB 0755
xargs File 130.27 KB 0755
xauth File 67.37 KB 0755
xbmtopbm File 65.8 KB 0755
xcmsdb File 67.11 KB 0755
xcompmgr File 38.08 KB 0755
xdg-dbus-proxy File 65.93 KB 0755
xdg-desktop-icon File 20.16 KB 0755
xdg-desktop-menu File 42.27 KB 0755
xdg-email File 26.3 KB 0755
xdg-icon-resource File 29.33 KB 0755
xdg-mime File 41.14 KB 0755
xdg-open File 25.46 KB 0755
xdg-screensaver File 37.11 KB 0755
xdg-settings File 37.5 KB 0755
xdg-user-dir File 234 B 0755
xdg-user-dirs-update File 66.01 KB 0755
xfallback.sh File 451 B 0755
xgamma File 66.32 KB 0755
xhost File 66.47 KB 0755
ximtoppm File 65.8 KB 0755
xinit File 18.47 KB 0755
xinput File 55.32 KB 0755
xkbbell File 14.36 KB 0755
xkbcomp File 204.64 KB 0755
xkbevd File 38.66 KB 0755
xkbprint File 82.63 KB 0755
xkbvleds File 23.23 KB 0755
xkbwatch File 19.25 KB 0755
xkeystone File 16.58 KB 0755
xls2csv File 67.27 KB 0755
xml2-config File 1.4 KB 0755
xmlstarlet File 130.03 KB 0755
xmodmap File 66.91 KB 0755
xpmtoppm File 65.84 KB 0755
xrandr File 66.95 KB 0755
xrdb File 66.8 KB 0755
xrefresh File 66.4 KB 0755
xscreensaver File 66.11 KB 0755
xscreensaver-command File 66.09 KB 0755
xscreensaver-demo File 452.78 KB 0755
xscreensaver-settings File 452.78 KB 0755
xsel File 65.93 KB 0755
xset File 66.73 KB 0755
xsetmode File 66.3 KB 0755
xsetpointer File 66.32 KB 0755
xsetroot File 66.48 KB 0755
xsettingsd File 46.38 KB 0755
xstdcmap File 66.91 KB 0755
xsubpp File 5.05 KB 0755
xvidtune File 68.07 KB 0755
xvminitoppm File 65.8 KB 0755
xwayland-xauth File 448 B 0755
xwdtopnm File 65.8 KB 0755
xz File 130.51 KB 0755
xzcat File 130.51 KB 0755
xzcmp File 7.25 KB 0755
xzdiff File 7.25 KB 0755
xzegrep File 10.09 KB 0755
xzfgrep File 10.09 KB 0755
xzgrep File 10.09 KB 0755
xzless File 1.77 KB 0755
xzmore File 2.14 KB 0755
ybmtopbm File 65.8 KB 0755
yelp File 66.01 KB 0755
yes File 66.8 KB 0755
ypdomainname File 66.01 KB 0755
yuvsplittoppm File 65.8 KB 0755
yuvtoppm File 65.8 KB 0755
yuy2topam File 65.8 KB 0755
zcat File 1.94 KB 0755
zcmp File 1.64 KB 0755
zdiff File 6.31 KB 0755
zdump File 66.27 KB 0755
zegrep File 29 B 0755
zeisstopnm File 65.8 KB 0755
zenity File 136.3 KB 0755
zenoty File 66.3 KB 0755
zfgrep File 29 B 0755
zforce File 2.03 KB 0755
zgrep File 7.91 KB 0755
zip File 264.05 KB 0755
zipcloak File 132.28 KB 0755
zipdetails File 68.55 KB 0755
zipgrep File 2.89 KB 0755
zipinfo File 194.95 KB 0755
zipnote File 131.96 KB 0755
zipsplit File 131.98 KB 0755
zless File 2.15 KB 0755
zmore File 1.8 KB 0755
znew File 4.47 KB 0755
zstd File 1.13 MB 0755
zstdcat File 1.13 MB 0755
zstdgrep File 3.78 KB 0755
zstdless File 197 B 0755
zstdmt File 1.13 MB 0755
Filemanager