__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ 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:

[email protected]: ~ $
#!/usr/bin/perl
#   Copyright (C) 2021-2025 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;

# Disable before release
# use Perl::Critic;

use bigint;
use List::Util qw (max);
use Cwd qw (abs_path cwd);
use File::Basename;
use File::stat;
use feature qw (state);
use POSIX;
use Getopt::Long qw (Configure);

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

#------------------------------------------------------------------------------
# The total number of functions to be processed.
#------------------------------------------------------------------------------
my $g_total_function_count = 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 = "gprofng-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]';
  my $g_rm_surrounding_spaces_regex = '^\s+|\s+$';

#------------------------------------------------------------------------------
# For consistency, use a global variable.
#------------------------------------------------------------------------------
  my $g_html_new_line = "<br>";

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

my $g_first_metric;

my $binutils_version;
my $driver_cmd;
my $tool_name;
my $version_info;

my %g_mapped_cmds = ();

#------------------------------------------------------------------------------
# Variables dealing with warnings and errors.  Since a message may span
# multiple lines (for readability reasons), the number of entries in the
# array may not reflect the total number of messages.  This is why we use
# separate variables for the counts.
#------------------------------------------------------------------------------
my @g_error_msgs   = ();
my @g_warning_msgs = ();
my $g_total_error_count = 0;
#------------------------------------------------------------------------------
# This count is used in the html_create_warnings_page HTML page to show how
# many warning messages there are.  Warnings are printed through gp_message(),
# but since one warning may span multiple lines, we update a separate counter
# that contains the total number of warning messages issued so far.
#------------------------------------------------------------------------------
my $g_total_warning_count = 0;
my $g_options_printed     = $FALSE;
my $g_abort_msg = "cannot recover from the error(s)";

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

my $g_warn_keyword  = "[Warning]";
my $g_error_keyword = "[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        = "gprofng-display-html";
#$binutils_version = "2.38.50";
$binutils_version = "2.43.0";
$version_info     = $tool_name . " GNU binutils version " . $binutils_version;

#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Define several key data structures.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# This table has the settings of the variables the user may set.
#------------------------------------------------------------------------------
my %g_user_settings =
  (
    verbose              => { option => "--verbose",
			      no_of_arguments => 1,
			      data_type => "onoff",
			      current_value => "off",  defined => $FALSE},

    debug                => { option => "--debug",
			      no_of_arguments => 1,
			      data_type => "size",
			      current_value => "off",  defined => $FALSE},

    warnings             => { option => "--warnings",
			      no_of_arguments => 1,
			      data_type => "onoff" ,
			      current_value => "off",  defined => $FALSE},

    nowarnings           => { option => "--nowarnings",
			      no_of_arguments => 1,
			      data_type => "onoff",
			      current_value => "off",  defined => $FALSE},

    quiet                => { option => "--quiet",
			      no_of_arguments => 1,
			      data_type => "onoff",
			      current_value => "off",  defined => $FALSE},

    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 => "--highlight-percentage",
			      no_of_arguments => 1,
			      data_type => "pfloat",
			      current_value   => 90.0, defined => $FALSE},

    hp                   => { 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},
  );

#------------------------------------------------------------------------------
# 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  = $FALSE;
my $g_debug    = $FALSE;
my $g_warnings = $TRUE;
my $g_quiet    = $FALSE;

#------------------------------------------------------------------------------
# Since ARGV is modified when parsing the options, a clean copy is used to
# print the original ARGV values in case of a warning, or error.
#------------------------------------------------------------------------------
my @CopyOfARGV = ();

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

#------------------------------------------------------------------------------
# Introducing main() 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 ();

  @CopyOfARGV = @ARGV;

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

#------------------------------------------------------------------------------
# OS commands executed and search paths.
#
# TBD: check if elfdump should be here too (most likely not though)
#------------------------------------------------------------------------------
  my @selected_os_cmds = qw (rm cat hostname locale which printenv 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 = ();
  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 $found_exp_dir;
  my $ignore_value;
  my $msg;
  my $number_of_metrics;
  my $va_executable_in_hex;

  my $failed_command_mappings;

  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 $html_test;
  my @experiment_data;
  my $exp_info_file;
  my $exp_info_ref;
  my @exp_info;

  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;

#------------------------------------------------------------------------------
# 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 warnings and
# errors.  The warnings, if any, will be printed once the command line has
# been parsed and verified.  Execution continues.
#
# Any error(s) accumulated in this phase will be printed after the command
# line has been parsed and verified.  Execution is then terminated.
#
# In the remainder, any error encountered will immediately terminate the
# execution because we can't guarantee the remaining code will work up to
# some point.
#------------------------------------------------------------------------------
  my ($found_exp_dir_ref, $exp_dir_list_ref) = parse_and_check_user_options ();

  $found_exp_dir = ${ $found_exp_dir_ref };

  if ($found_exp_dir)
    {
      @exp_dir_list = @{ $exp_dir_list_ref };
    }
  else
    {
      $msg = "the list with experiments is either missing, or incorrect";
      gp_message ("debug", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# The final settings for verbose, debug, warnings and quiet are known and the
# gp_message() subroutine is aware of these.
#------------------------------------------------------------------------------
  $msg = "parsing of the user options completed";
  gp_message ("verbose", $subr_name, $msg);

#------------------------------------------------------------------------------
# The user options have been taken in.  Check for validity and consistency.
#------------------------------------------------------------------------------
  $msg = "process user options";
  gp_message ("verbose", $subr_name, $msg);

  ($ignored_metrics_ref, $outputdir,
   $time_percentage_multiplier, $process_all_functions, $exp_dir_list_ref) =
					process_user_options (\@exp_dir_list);

  @exp_dir_list    = @{ $exp_dir_list_ref };
  %ignored_metrics = %{$ignored_metrics_ref};

#------------------------------------------------------------------------------
# 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).
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "verify the OS commands");
  $failed_command_mappings = check_and_define_cmds (\@selected_os_cmds,
						    \@search_paths_os_cmds);

  if ($failed_command_mappings == 0)
    {
      $msg = "successfully verified the OS commands";
      gp_message ("debug", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Time to check if any warnings and/or errors have been generated.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# We have completed all the upfront checks.  Print any warnings and errors.
# If there are already any errors, execution is terminated.  As execution
# continues, errors may occur and they are typically fatal.
#------------------------------------------------------------------------------
  if ($g_debug)
    {
      $msg = "internal settings after option processing";
      $ignore_value = print_table_user_settings ("diag", $msg);
    }

#------------------------------------------------------------------------------
# Terminate execution in case fatal errors have occurred.
#------------------------------------------------------------------------------
  if ( $g_total_error_count > 0)
    {
      my $msg = "the current values for the user controllable settings";
      print_user_settings ("debug", $msg);

      gp_message ("abort", $subr_name, $g_abort_msg);
    }
  else
    {
      my $msg = "after parsing the user options, the final values are";
      print_user_settings ("debug", $msg);
    }

#------------------------------------------------------------------------------
# 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, raise an error because the -o option does not overwrite an existing
# directory.
# Also in case of other errors, the execution is terminated.
#------------------------------------------------------------------------------
  $outputdir = set_up_output_directory ();
  $abs_path_outputdir = Cwd::cwd () . "/" . $outputdir;

  $msg = "the output directory is $outputdir";
  gp_message ("debug", $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 };

  $msg = "the home directory is $home_dir";
  gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
# TBD: de-activated until this feature has been fully implemented.
#------------------------------------------------------------------------------
##  $msg =  "the search path for the rc file is @rc_file_paths";
##  gp_message ("debug", $subr_name, $msg);
##  $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.
#
# 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.
#------------------------------------------------------------------------------
  $msg = "processing of the rc file has been disabled for now";
  gp_message ("debugXL", $subr_name, $msg);

# Temporarily disabled
# print_table_user_settings ("debugXL", "before function process_rc_file");
# $rc_file_errors = process_rc_file ($rc_file_name, $rc_file_paths_ref);
# if ($rc_file_errors != 0)
# {
#   $message = "fatal errors in file $rc_file_name encountered";
#   gp_message ("debugXL", $subr_name, $message);
# }
# print_table_user_settings ("debugXL", "after function process_rc_file");

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

  $msg = "the experiment " . $plural . ":";
  gp_message ("verbose", $subr_name, $msg);
  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;
    }

#------------------------------------------------------------------------------
# TBD:
# This subroutine may be overkill.  See what is really needed here and remove
# everything else.
#
# Upon 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.
#------------------------------------------------------------------------------
  ($archive_dir_not_empty, $selected_archive, $elf_rats_ref) =
				check_validity_exp_dirs (\@exp_dir_list);

  %elf_rats = %{$elf_rats_ref};

  $msg = "the experiment directories have been verified and are valid";
  gp_message ("verbose", $subr_name, $msg);

#------------------------------------------------------------------------------
# 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)
    {
      $msg = "the experiment directories are consistent";
      gp_message ("verbose", $subr_name, $msg);
    }
  else
    {
      $msg  = "the number of consistency errors detected: $consistency_errors";
      gp_message ("abort", $subr_name, $msg);
    }

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

  $msg = "executable_name    = " . $executable_name;
  gp_message ("debug", $subr_name, $msg);
  $msg = "selected_archive   = " . $selected_archive;
  gp_message ("debug", $subr_name, $msg);
  $msg = "base_va_executable = " . $base_va_executable;
  gp_message ("debug", $subr_name, $msg);

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

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

#------------------------------------------------------------------------------
# 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))
    {
      $msg  = "file $GP_DISPLAY_TEXT is not executable for user, group, and";
      $msg .= " 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)
    {
      $msg  = "decimal separator is $decimal_separator";
      $msg .= " (conversion to dot is ";
      $msg .= ($convert_to_dot == $TRUE ? "enabled" : "disabled") . ")";
      gp_message ("debugXL", $subr_name, $msg);
    }
  else
    {
      $msg  = "the decimal separator cannot be determined -";
      $msg .= " set to $decimal_separator";
      gp_message ("warning", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Collect and store the system information.
#------------------------------------------------------------------------------
  $msg = "collect system information and adapt settings";
  gp_message ("verbose", $subr_name, $msg);

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

  $msg = "architecture_supported = $architecture_supported";
  gp_message ("debug", $subr_name, $msg);
  $msg = "elf_arch               = $elf_arch";
  gp_message ("debug", $subr_name, $msg);
  $msg = "elf_support            = ".($elf_arch ? "TRUE" : "FALSE");
  gp_message ("debug", $subr_name, $msg);

  for my $feature (sort keys %g_arch_specific_settings)
    {
      $msg  = "g_arch_specific_settings{$feature} = ";
      $msg .= $g_arch_specific_settings{$feature};
      gp_message ("debug", $subr_name, $msg);
    }

  $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 ();

  $msg = "after get_LANG_setting: LANG = $g_locale_settings{'LANG'}";
  gp_message ("debugXL", $subr_name, $msg);

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

  $msg = "hack ARCHIVES_MAP_NAME  = $ARCHIVES_MAP_NAME";
  gp_message ("debugXL", $subr_name, $msg);
  $msg = "hack ARCHIVES_MAP_VADDR = $ARCHIVES_MAP_VADDR";
  gp_message ("debugXL", $subr_name, $msg);

  $msg  = "after call to check_loadobjects_are_elf forced";
  $msg .= " elf_loadobjects_found = $elf_loadobjects_found";
  gp_message ("debugXL", $subr_name, $msg);

  $g_html_credits_line = ${ create_html_credits () };

  $msg = "g_html_credits_line = $g_html_credits_line";
  gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# Add a "/" to simplify the construction of path names in the remainder.
#
# TBD: Push this into a subroutine(s).
#------------------------------------------------------------------------------
  $outputdir = append_forward_slash ($outputdir);

  $msg = "prepared outputdir = ". $outputdir;
  gp_message ("debug", $subr_name, $msg);

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

  $msg = "set detail_metrics_system = " . $detail_metrics_system;
  gp_message ("debug", $subr_name, $msg);
  $msg = "set detail_metrics        = " . $detail_metrics;
  gp_message ("debug", $subr_name, $msg);
  $msg = "set call_metrics          = " . $call_metrics;
  gp_message ("debug", $subr_name, $msg);

  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.
#------------------------------------------------------------------------------
  $msg = "gather the metrics data from the experiments";
  gp_message ("verbose", $subr_name, $msg);

  $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
#------------------------------------------------------------------------------
  $msg = "unable to open metric value data file $outfile1 for reading:";
  open (METRICS, "<", $outfile1)
    or die ($subr_name . " - " . $msg . " " . $!);

  $msg = "opened 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)
    {
      $msg = "metrics_data[$i] = " . $metrics_data[$i];
      gp_message ("debugXL", $subr_name, $msg);
    }

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

      $msg = "after the call to process_metrics_data";
      gp_message ("debugXL", $subr_name, $msg);

      for my $metric (sort keys %metric_value)
        {
          $msg = "metric_value{$metric} = " . $metric_value{$metric};
          gp_message ("debugXL", $subr_name, $msg);
        }
      for my $metric (sort keys %metric_description)
        {
          $msg  = "metric_description{$metric} =";
          $msg .= " " . $metric_description{$metric};
          gp_message ("debugXL", $subr_name, $msg);
        }
      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.
#------------------------------------------------------------------------------
      $msg = "select the set of default metrics";
      gp_message ("verbose", $subr_name, $msg);

      ($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;

      $msg = "after the call to set_default_metrics";
      gp_message ("debug", $subr_name, $msg);

    }

  $number_of_metrics = split (":", $summary_metrics);

  $msg = "summary_metrics       = " . $summary_metrics;
  gp_message ("debugM", $subr_name, $msg);
  $msg = "detail_metrics        = " . $detail_metrics;
  gp_message ("debugM", $subr_name, $msg);
  $msg = "detail_metrics_system = " . $detail_metrics_system;
  gp_message ("debugM", $subr_name, $msg);
  $msg = "call_metrics          = " . $call_metrics;
  gp_message ("debugM", $subr_name, $msg);
  $msg = "number_of_metrics     = " . $number_of_metrics;
  gp_message ("debugM", $subr_name, $msg);

#------------------------------------------------------------------------------
# TBD Find a way to better handle this situation:
#------------------------------------------------------------------------------
  for my $im (keys %metric_found)
    {
      $msg = "metric_found{$im} = " . $metric_found{$im};
      gp_message ("debugXL", $subr_name, $msg);
    }
  for my $im (keys %ignored_metrics)
    {
      if (not exists ($metric_found{$im}))
        {
          $msg  = "user requested ignored metric (-im) $im does not exist in";
          $msg .= " collected metrics";
          gp_message ("debugXL", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# Get the information on the experiments.
#------------------------------------------------------------------------------
  $msg = "generate the experiment information";
  gp_message ("verbose", $subr_name, $msg);

  my $experiment_data_ref = get_experiment_info (\$outputdir, \@exp_dir_list);
  @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.
#------------------------------------------------------------------------------
  $msg = "generate the list with functions executed";
  gp_message ("verbose", $subr_name, $msg);

  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.
#------------------------------------------------------------------------------
  $msg = "analyze and store the relevant function information";
  gp_message ("verbose", $subr_name, $msg);

  ($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 };

  $msg = "found " . $g_total_function_count . " functions to process";
  gp_message ("verbose", $subr_name, $msg);

  for my $keys (0 .. $#function_info)
    {
      for my $fields (keys %{$function_info[$keys]})
        {
          $msg = "$keys $fields $function_info[$keys]{$fields}";
          gp_message ("debugXL", $subr_name, $msg);
        }
    }

  for my $i (keys %addressobjtextm)
    {
      $msg = "addressobjtextm{$i} = " . $addressobjtextm{$i};
      gp_message ("debugXL", $subr_name, $msg);
    }

  $msg  = "generate the files with function overviews and the";
  $msg .= " callers-callees information";
  gp_message ("verbose", $subr_name, $msg);

  $script_pc_metrics = generate_function_level_info (\@exp_dir_list,
                                                     $call_metrics,
                                                     $summary_metrics,
                                                     $outputdir,
                                                     $sort_fields_ref);

  $msg = "preprocess the files with the function level information";
  gp_message ("verbose", $subr_name, $msg);

  $ignore_value = preprocess_function_files (
                    $metric_description_ref,
                    $script_pc_metrics,
                    $outputdir,
                    \@sort_fields);

  $msg = "for each function, generate a set of files";
  gp_message ("verbose", $subr_name, $msg);

  ($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.
#------------------------------------------------------------------------------
  $msg = "parse the disassembly files and generate the html files";
  gp_message ("verbose", $subr_name, $msg);

  $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.
#------------------------------------------------------------------------------
  $msg = "parse the source files and generate the html files";
  gp_message ("verbose", $subr_name, $msg);

  parse_source_files (\$number_of_metrics, \@function_info, \$outputdir);

#------------------------------------------------------------------------------
# Parse the caller-callee information and generate the html files.
#------------------------------------------------------------------------------
  $msg = "process the caller-callee information and generate the html file";
  gp_message ("verbose", $subr_name, $msg);

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

#------------------------------------------------------------------------------
# Process the metric values.
#------------------------------------------------------------------------------
  $msg = "generate the html file with the metrics information";
  gp_message ("verbose", $subr_name, $msg);

  $ignore_value = process_metrics ($outputdir,
				   \@sort_fields,
				   \%metric_description,
				   \%ignored_metrics);

#------------------------------------------------------------------------------
# Generate the function view html files.
#------------------------------------------------------------------------------
  $msg = "generate the function view html files";
  gp_message ("verbose", $subr_name, $msg);

  $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 };

  $msg = "html_first_metric_file = " . $html_first_metric_file;
  gp_message ("debugXL", $subr_name, $msg);

  $html_test = ${ generate_home_link ("left") };
  $msg = "html_test = " . $html_test;
  gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# Unconditionnaly generate the page with the warnings.
#------------------------------------------------------------------------------
  $ignore_value = html_create_warnings_page (\$outputdir);

#------------------------------------------------------------------------------
# Generate the index.html file.
#------------------------------------------------------------------------------
  $msg = "generate the index.html file";
  gp_message ("verbose", $subr_name, $msg);

  $ignore_value = html_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,
				       \@table_execution_stats);

#------------------------------------------------------------------------------
# We're done.  In debug mode, print the meta data for the experiment
# directories.
#------------------------------------------------------------------------------
  $ignore_value = print_meta_data_experiments ("debug");

#------------------------------------------------------------------------------
# Before the execution completes, print the warning(s) on the screen.
#
# Note that this assumes that no additional warnings have been created since
# the call to html_create_warnings_page.  Otherwise there will be a discrepancy
# between what is printed on the screen and shown in the warnings.html page.
#------------------------------------------------------------------------------
  if (($g_total_warning_count > 0) and ($g_warnings))
    {
      $ignore_value = print_warnings_buffer ();
      @g_warning_msgs = ();
    }

#------------------------------------------------------------------------------
# This is not supposed to happen, but in case there are any fatal errors that
# have not caused the execution to terminate, print them here.
#------------------------------------------------------------------------------
  if (@g_error_msgs)
    {
      $ignore_value = print_errors_buffer (\$g_error_keyword);
    }

#------------------------------------------------------------------------------
# One line message to show where the results can be found.
#------------------------------------------------------------------------------
  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

#------------------------------------------------------------------------------
# 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 $msg;
  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
    {
      $msg = "g_addressing_mode = $g_addressing_mode not supported";
      gp_message ("abort", $subr_name, $msg);
    }

  $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.
#
# First, it is checked if the command is in the search path, built-in, or an
# alias.  If this is not the case, search for it in a couple of locations.
#
# If this all fails, warning messages are printed, but this is not a hard
# error. Yet. Most likely, things will go bad later on.
#
# The commands and their respective paths are stored in hash "g_mapped_cmds".
#------------------------------------------------------------------------------
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 @the_fields = ();

  my $cmd;
  my $cmd_found;
  my $error_code;
  my $failed_cmd;
  my $failed_cmds;
  my $found_match;
  my $mapped;
  my $msg;
  my $no_of_failed_mappings;
  my $no_of_fields;
  my $output_cmd;
  my $target_cmd;
  my $failed_mapping = $FALSE;
  my $full_path_cmd;

  gp_message ("debugXL", $subr_name, "\@cmds = @cmds");
  gp_message ("debugXL", $subr_name, "\@search_path = @search_path");

#------------------------------------------------------------------------------
# Search for the command and record the absolute path.  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 $cmd (@cmds)
    {
      $target_cmd = "(command -v $cmd; echo \$\?)";

      $msg = "check target_cmd = " . $target_cmd;
      gp_message ("debug", $subr_name, $msg);

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

      if ($error_code != 0)
#------------------------------------------------------------------------------
# This is unlikely to happen, since it means the command executed failed.
#------------------------------------------------------------------------------
        {
          $msg = "error executing this command: " . $target_cmd;
          gp_message ("warning", $subr_name, $msg);
          $msg = "execution continues, but may fail later on";
          gp_message ("warning", $subr_name, $msg);

          $g_total_warning_count++;
        }
      else
#------------------------------------------------------------------------------
# So far, all is well, but is the target command available?
#------------------------------------------------------------------------------
        {
#------------------------------------------------------------------------------
# The output from the $target_cmd command should contain 2 lines in case the
# command has been found.  The first line shows the command with the full
# path, while the second line has the exit code.
#
# If the exit code is not zero, the command has not been found.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# Split the output at the \n character and check the number of lines as
# well as the return code.
#------------------------------------------------------------------------------
          @the_fields   = split ("\n", $output_cmd);
          $no_of_fields = scalar (@the_fields);
          $cmd_found    = ($the_fields[$no_of_fields-1] == 0 ? $TRUE : $FALSE);

#------------------------------------------------------------------------------
# This is unexpected.  Throw an assertion error and bail out.
#------------------------------------------------------------------------------
          if ($no_of_fields > 2)
            {
              gp_message ("error", $subr_name, "output from $target_cmd:");
              gp_message ("error", $subr_name, $output_cmd);

              $msg = "the output from $target_cmd has more than 2 lines";
              gp_message ("assertion", $subr_name, $msg);
            }

          if ($cmd_found)
            {
              $full_path_cmd = $the_fields[0];
#------------------------------------------------------------------------------
# The command is in the search path.  Store the full path to the command.
#------------------------------------------------------------------------------
              $msg = "the $cmd command is in the search path";
              gp_message ("debug", $subr_name, $msg);

              $g_mapped_cmds{$cmd} = $full_path_cmd;
            }
          else
#------------------------------------------------------------------------------
# A best effort to locate the command elsewhere.  If found, store the command
# with the absolute path included.  Otherwise print a warning, but continue.
#------------------------------------------------------------------------------
            {
              $msg = "the $cmd command is not in the search path";
              $msg .= " - start a best effort search to find it";
              gp_message ("debug", $subr_name, $msg);

              $found_match = $FALSE;
              for my $path (@search_path)
                {
                  $target_cmd = $path . "/" . $cmd;
                  if (-x $target_cmd)
                    {
                      $msg = "found the command in $path";
                      gp_message ("debug", $subr_name, $msg);

                      $found_match = $TRUE;
                      $g_mapped_cmds{$cmd} = $target_cmd;
                      last;
                    }
                  else
                    {
                      $msg = "failure to find the $cmd command in $path";
                      gp_message ("debug", $subr_name, $msg);
                    }
                }

              if (not $found_match)
                {
                  $g_mapped_cmds{$cmd} = "road to nowhere";
                  $failed_mapping = $TRUE;
                }
            }
        }
    }

#------------------------------------------------------------------------------
# Scan the results stored in $g_mapped_cmds and flag errors.
#------------------------------------------------------------------------------
  $no_of_failed_mappings = 0;
  $failed_cmds           = "";

#------------------------------------------------------------------------------
# Print a warning message before showing the results, that at least one search
# has failed.
#------------------------------------------------------------------------------
  if ($failed_mapping)
    {
      $msg  = "<br>" . "failure in the verification of the OS commands:";
      gp_message ("warning", $subr_name, $msg);
    }

  while ( ($cmd, $mapped) = each %g_mapped_cmds)
    {
      if ($mapped eq "road to nowhere")
        {
          $msg  = "cannot find a path for command $cmd";
          gp_message ("warning", $subr_name, $msg);
          gp_message ("debug", $subr_name, $msg);

          $no_of_failed_mappings++;
          $failed_cmds .= $cmd;
          $g_mapped_cmds{$cmd} = $cmd;
        }
      else
       {
          $msg = "path for the $cmd command is $mapped";
          gp_message ("debug", $subr_name, $msg);
       }
    }
  if ($no_of_failed_mappings != 0)
    {
      my $plural_1 = ($no_of_failed_mappings > 1) ? "failures"   : "failure";
      my $plural_2 = ($no_of_failed_mappings > 1) ? "commands" : "command";

      $msg  = "encountered $no_of_failed_mappings $plural_1 to locate";
      $msg .= " selected " . $plural_2;
      gp_message ("warning", $subr_name, $msg);
      gp_message ("debug", $subr_name, $msg);

      $msg  = "execution continues, but may fail later on";
      gp_message ("warning", $subr_name, $msg);
      gp_message ("debug", $subr_name, $msg);

      $g_total_warning_count++;
    }

  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;
          $msg = "cannot determine branch target";
          gp_message ("debug", $subr_name, $msg);
        }
    }
  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}";
                  $msg .= " = $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 if the value for the user option given is valid.
#
# In case the value is valid, the g_user_settings table is updated with the
# (new) value.
#
# Otherwise an error message is pushed into the g_error_msgs buffer.
#
# The return value is TRUE/FALSE.
#------------------------------------------------------------------------------
sub check_and_set_user_option
{
  my $subr_name = get_my_name ();

  my ($internal_opt_name, $value) = @_;

  my $msg;
  my $valid;
  my $option_value_missing;

  my $option     = $g_user_settings{$internal_opt_name}{"option"};
  my $data_type  = $g_user_settings{$internal_opt_name}{"data_type"};
  my $no_of_args = $g_user_settings{$internal_opt_name}{"no_of_arguments"};
 
  if (($no_of_args >= 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.
#------------------------------------------------------------------------------
    {
      $valid                = $FALSE;
      $option_value_missing = $TRUE;
    }
  elsif ($no_of_args >= 1)
    {
      $option_value_missing = $FALSE;
#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
      $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_opt_name}{"current_value"} =
								lc ($value);
            }
          else
            {
              $g_user_settings{$internal_opt_name}{"current_value"} = $value;
            }
          $g_user_settings{$internal_opt_name}{"defined"} = $TRUE;
        }
    }

  return (\$valid, \$option_value_missing);

} #-- End of subroutine check_and_set_user_option

#------------------------------------------------------------------------------
# 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 for 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 $gp_path;
  my $msg;
  my $output_which_gp_display_text;
  my $return_value;
  my $target_cmd;

#------------------------------------------------------------------------------
# Get the path to gprofng-display-text.
#------------------------------------------------------------------------------
  my ($error_occurred_ref, $gp_path_ref, $return_value_ref) =
		       find_path_to_gp_display_text ($location_gp_command_ref);

  $error_occurred = ${ $error_occurred_ref};
  $gp_path        = ${ $gp_path_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 gprofng-display-text has been located.
#------------------------------------------------------------------------------
    {
      $g_path_to_tools = $return_value;

      $msg = "located $GP_DISPLAY_TEXT in the 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  = $g_html_new_line;
      $msg .= "could not find $GP_DISPLAY_TEXT in directory $gp_path :";
      $msg .= " $return_value";
      gp_message ("warning", $subr_name, $msg);

#------------------------------------------------------------------------------
# Check if we can find $GP_DISPLAY_TEXT in the search path.
#------------------------------------------------------------------------------
      $msg = "check for $GP_DISPLAY_TEXT to be in the search path";
      gp_message ("debug", $subr_name, $msg);

      gp_message ("warning", $subr_name, $msg);
      $g_total_warning_count++;

      $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  = "located $GP_DISPLAY_TEXT in $g_path_to_tools";
          gp_message ("warning", $subr_name, $msg);
          $msg = "this is the version that will be used";
          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 ("error", $subr_name, $msg);

          $g_total_error_count++;

          gp_message ("abort", $subr_name, $g_abort_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 $event_kind_map_regex;
  $event_kind_map_regex  = '^<event kind="map"\s.*vaddr=';
  $event_kind_map_regex .= '"0x([0-9a-fA-F]+)"\s+.*foffset=';
  $event_kind_map_regex .= '"\+*0x([0-9a-fA-F]+)"\s.*modes=';
  $event_kind_map_regex .= '"0x([0-9]+)"\s.*name="(.*)".*>$';

  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 $msg;
  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.
#------------------------------------------------------------------------------
  $msg  = " - unable to open file $path_to_log_file for reading:";
  open (LOG_XML, "<", $path_to_log_file)
    or die ($subr_name . $msg . " " . $!);

  $msg = "opened file $path_to_log_file for reading";
  gp_message ("debug", $subr_name, $msg);

  while (<LOG_XML>)
    {
      $line = $_;
      chomp ($line);
      gp_message ("debugM", $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">
#------------------------------------------------------------------------------
      if ($line =~ /^\s*<system\s+/)
        {
          $msg = "selected the following line from the log.xml file:";
          gp_message ("debugM", $subr_name, $msg);
          gp_message ("debugM", $subr_name, "$line");
          if ($line =~ /.*\s+hostname="([^"]+)/)
            {
              $hostname_exp = $1;
              $msg = "extracted hostname_exp = " . $hostname_exp;
              gp_message ("debugM", $subr_name, $msg);
            }
          if ($line =~ /.*\s+arch="([^"]+)/)
            {
              $arch_exp = $1;
              $msg = "extracted arch_exp = " . $arch_exp;
              gp_message ("debugM", $subr_name, $msg);
            }
          if ($line =~ /.*\s+os="([^"]+)/)
            {
              $os_exp_full = $1;
#------------------------------------------------------------------------------
# Capture the first word only.
#------------------------------------------------------------------------------
              if ($os_exp_full =~ /([^\s]+)/)
                {
                  $os_exp = $1;
                }
              $msg = "extracted os_exp = " . $os_exp;
              gp_message ("debugM", $subr_name, $msg);
            }
          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))
        {
          $msg  = "early return: the hostname, architecture and OS match";
          $msg .= " the current system";
          gp_message ("debug", $subr_name, $msg);
          $msg = "FAKE THIS IS NOT THE CASE AND CONTINUE";
          gp_message ("debug", $subr_name, $msg);
# FAKE          return ($TRUE);
        }

  if (not $g_exp_dir_meta_data{$selected_archive}{"archive_is_empty"})
    {
      $msg = "selected_archive = " . $selected_archive;
      gp_message ("debug", $subr_name, $msg);
      for my $i (sort keys
		   %{$g_exp_dir_meta_data{$selected_archive}{"archive_files"}})
        {
          $msg  = "stored loadobject " . $i . " ";
          $msg .= $g_exp_dir_meta_data{$selected_archive}{"archive_files"}{$i};
          gp_message ("debug", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# 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"})
    {
      $msg  = "the files in directory $selected_archive/archives are in";
      $msg .= " ELF format";
      gp_message ("debugM", $subr_name, $msg);
      $msg = "IGNORE THIS AND USE MAP.XML";
      gp_message ("debugM", $subr_name, $msg);
##      return ($TRUE);
    }

  $msg  = "the files in directory $selected_archive/archives are not in";
  $msg .= " ELF format";
  gp_message ("debug", $subr_name, $msg);

  $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";

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

#------------------------------------------------------------------------------
# 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 ("debugM", $subr_name, "MAP_XML read line = $line");
#------------------------------------------------------------------------------
# Replaces this way too long line:
#     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="(.*)".*>$/)
#------------------------------------------------------------------------------
      if ($line =~ /$event_kind_map_regex/)
        {
          gp_message ("debugM", $subr_name, "target line = $line");
          $vaddr     = $1;
          $foffset   = $2;
          $modes     = $3;
          $name_path = $4;
          $name      = get_basename ($name_path);
          $msg  = "extracted vaddr     = $vaddr foffset = $foffset";
          $msg .= " modes = $modes";
          gp_message ("debugM", $subr_name, $msg);
          $msg = "extracted name_path = $name_path name = $name";
          gp_message ("debugM", $subr_name, $msg);
#              $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;

          $msg = "set executable_name      = " . $executable_name;
          gp_message ("debugM", $subr_name, $msg);
          $msg = "set va_executable_in_hex = " . $va_executable_in_hex;
          gp_message ("debugM", $subr_name, $msg);
          $msg = "result_VA                = " . $result_VA;
          gp_message ("debugM", $subr_name, $msg);
          $msg = "hex_VA                   = " . $hex_VA;
          gp_message ("debugM", $subr_name, $msg);

          if ($modes eq "005")
            {
              $extracted_information = $TRUE;
              last;
            }
        }
    }

  close (MAP_XML);

  if (not $extracted_information)
    {
      $msg  = "cannot find the necessary information in";
      $msg .= " 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 $msg;
  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];

          $msg  = "metric = $metric current_value = $current_value";
          $msg .= " max_value = $max_value";
          gp_message ("debugXL", $subr_name, $msg);

          if ( ($max_value > 0) and ($current_value > 0) and
	       ($current_value != $max_value) )
            {
# TBD: abs needed?
              $msg  = "metric = $metric current_value = $current_value";
              $msg .= " max_value = $max_value";
              gp_message ("debugXL", $subr_name, $msg);

              $relative_distance = 1.00 - abs (
				($max_value - $current_value)/$max_value );

              $msg = "relative_distance = $relative_distance";
              gp_message ("debugXL", $subr_name, $msg);

              if ($relative_distance >= $hp_value/100.0)
                {
                  $msg = "metric $metric is within the relative_distance";
                  gp_message ("debugXL", $subr_name, $msg);

                  $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 the command line options for the occurrence of experiments and make
# sure that this list is contigious.  No other names are allowed in this list.
#
# Terminate execution in case of an error.  Otherwise remove the experiment
# names for ARGV (to make the subsequent parsing easier), and return an array
# with the experiment names.
#
# The following patterns are supposed to be detected:
#
# <expdir_1> some other word(s) <expdir_2>
# <expdir> some other word(s)
#------------------------------------------------------------------------------
sub check_the_experiment_list
{
  my $subr_name = get_my_name ();

#------------------------------------------------------------------------------
# The name of an experiment directory can contain any non-whitespace
# character(s), but has to end with .er, or optionally .er/.  Multiple
# forward slashes are allowed.
#------------------------------------------------------------------------------
  my $exp_dir_regex = '^(\S+)(\.er)\/*$';
  my $forward_slash_regex = '\/*$';

  my $current_value;
  my @exp_dir_list = ();
  my $found_experiment = $FALSE;
  my $found_non_exp = $FALSE;
  my $msg;
  my $name_non_exp_dir = "";
  my $no_of_experiments = 0;
  my $no_of_invalid_dirs = 0;
  my $opt_remainder;
  my $valid = $TRUE;

  for my $i (keys @ARGV)
    {
      $current_value = $ARGV[$i];
      if ($current_value =~ /$exp_dir_regex/)
#------------------------------------------------------------------------------
# The current value is an experiment.  Remove any trailing forward slashes,
# Increment the count, push the value into the array and set the
# found_experiment flag to TRUE.
#------------------------------------------------------------------------------
        {
          $no_of_experiments += 1;

          $current_value =~ s/$forward_slash_regex//;
          push (@exp_dir_list, $current_value);

          if (not $found_experiment)
#------------------------------------------------------------------------------
# Start checking for the next field(s).
#------------------------------------------------------------------------------
            {
              $found_experiment = $TRUE;
            }
#------------------------------------------------------------------------------
# We had found non-experiment names and now see another experiment.  Time to
# bail out of the loop.
#------------------------------------------------------------------------------
          if ($found_non_exp)
            {
              last;
            }
        }
      else
        {
          if ($found_experiment)
#------------------------------------------------------------------------------
# The current value is not an experiment, but the value of found_experiment
# indicates at least one experiment has been seen already.  This means that
# the list of experiment names is not contiguous and that is a fatal error.
#------------------------------------------------------------------------------
            {
              $name_non_exp_dir .= $current_value . " ";
              $found_non_exp = $TRUE;
            }
        }

    }

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Error handling.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

  if ($found_non_exp)
#------------------------------------------------------------------------------
# The experiment list is not contiguous.
#------------------------------------------------------------------------------
    {
      $valid = $FALSE;
      $msg = "the list with the experiments is not contiguous:";
      gp_message ("error", $subr_name, $msg);

      $msg = "\"" . $name_non_exp_dir. "\"". " is not an experiment, but" .
             " appears in a list where experiments are expected";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }

  if ($no_of_experiments == 0)
#------------------------------------------------------------------------------
# The experiment list is empty.
#------------------------------------------------------------------------------
    {
      $valid = $FALSE;
      $msg = "the experiment list is missing from the options";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }

  if (not $valid)
#------------------------------------------------------------------------------
# If an error has occurred, print the error(s) and terminate execution.
#------------------------------------------------------------------------------
    {
      gp_message ("abort", $subr_name, $g_abort_msg);
    }

#------------------------------------------------------------------------------
# We now have a list with experiments, but we still need to verify whether they
# exist, and if so, are these valid experiments?
#------------------------------------------------------------------------------
  for my $exp_dir (@exp_dir_list)
    {
      $msg = "checking experiment directory $exp_dir";
      gp_message ("debug", $subr_name, $msg);

      if (-d $exp_dir)
        {
          $msg = "directory $exp_dir found";
          gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# Files log.xml and map.xml have to be there.
#------------------------------------------------------------------------------
          if ((-e $exp_dir."/log.xml") and (-e $exp_dir."/map.xml"))
            {
              $msg  = "directory $exp_dir appears to be a valid experiment";
              $msg .= " directory";
              gp_message ("debug", $subr_name, $msg);
            }
          else
            {
              $no_of_invalid_dirs++;
              $msg  = "file " . $exp_dir . "/log.xml and/or " . $exp_dir;
              $msg .= "/map.xml missing";
              gp_message ("debug", $subr_name, $msg);

              $msg  = "directory " . get_basename($exp_dir) . " does not";
              $msg .= " appear to be a valid experiment directory";
              gp_message ("error", $subr_name, $msg);

              $g_total_error_count++;
            }
        }
      else
        {
          $no_of_invalid_dirs++;
          $msg  = "directory " . get_basename($exp_dir) . " does not exist";
          gp_message ("error", $subr_name, $msg);

          $g_total_error_count++;
        }
    }

  if ($no_of_invalid_dirs > 0)
#------------------------------------------------------------------------------
# This is a fatal error, but for now, we can continue to check for more errors.
# Even if none more are found, execution is terminated before the data is
# generated and processed.  In this way we can catch as many errors as
# possible.
#------------------------------------------------------------------------------
    {
      my $plural_or_single = ($no_of_invalid_dirs == 1) ?
		"one experiment is" : $no_of_invalid_dirs . " experiments are";

      $msg = $plural_or_single . " not valid";
##      gp_message ("abort", $subr_name, $msg);

##      $g_total_error_count++;
    }

#------------------------------------------------------------------------------
# Remove the experiments from ARGV and return the array with the experiment
# names.  Note that these may, or may not be valid, but if invalid, execution
# terminates before they are used.
#------------------------------------------------------------------------------
  for my $i (1 .. $no_of_experiments)
    {
      my $poppy = pop (@ARGV);

      $msg = "popped $poppy from ARGV";
      gp_message ("debug", $subr_name, $msg);

      $msg = "ARGV after update = " . join (" ", @ARGV);
      gp_message ("debug", $subr_name, $msg);
    }

  return (\@exp_dir_list);

} #-- End of subroutine check_the_experiment_list

#------------------------------------------------------------------------------
# Perform multiple checks on the experiment directories.
#
# TBD: It needs to be investigated whether all of this is really neccesary.
#------------------------------------------------------------------------------
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 $missing_dirs     = 0;
  my $invalid_dirs     = 0;

  my $archive_dir_not_empty;
  my $archives_dir;
  my $archives_file;
  my $count_exp_dir_not_elf;
  my $elf_magic_number;
  my $first_line;
  my $msg;

  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;

#------------------------------------------------------------------------------
# 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"};
      $archives_dir .= $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;

      $msg = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = ";
      $msg .= $g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'};
      gp_message ("debug", $subr_name, $msg);

      $msg = "checking $archives_dir";
      gp_message ("debug", $subr_name, $msg);

      while (glob ("$archives_dir/*"))
        {
          $filename = get_basename ($_);

          $msg = "processing file: $filename";
          gp_message ("debug", $subr_name, $msg);

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

          $msg = "last_dot = $last_dot";
          gp_message ("debugXL", $subr_name, $msg);

          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);
          $msg = "stripped archive name: " . $original_name;
          gp_message ("debug", $subr_name, $msg);
          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;
              $msg  = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'} = ";
              $msg .= $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};

              gp_message ("debugXL", $subr_name, $msg);
            }
        }
    } #-- 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"};
      $msg  = "archive directory " . $exp_dir . "/archives is";
      $msg .= " " . ($empty ? "empty" : "not empty");
      gp_message ("debug", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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"};
          $archives_dir .= $exp_dir . "/archives";
          $msg = "exp_dir = " . $exp_dir . " archives_dir = " . $archives_dir;
          gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# 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"};
              $filename .=  $exp_dir . "/archives/" . $aname;
              $msg  = " - unable to open file $filename for reading:";
              open (ARCF,"<", $filename)
                or die ($subr_name . $msg . " " . $!);

              $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);
              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)
    {
      $msg = "the loadobjects in the archive in $exp_dir are";
      $msg .= ($g_exp_dir_meta_data{$exp_dir}{"archive_in_elf_format"}) ?
							" in" : " not in";
      $msg .= " ELF format";
      gp_message ("debug", $subr_name, $msg);
    }
  for my $exp_dir (sort keys %g_exp_dir_meta_data)
    {
      if ($g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"})
        {
          $msg = "there are no archived files in " . $exp_dir;
          gp_message ("debug", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# If there are archived files and they are not in ELF format, a debug message
# 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)
    {
      $msg  = "there are $count_exp_dir_not_elf experiments with non-ELF";
      $msg .= " load objects";
      gp_message ("debug", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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)
    {
      $msg = "exp_dir = " . $exp_dir;
      gp_message ("debugXL", $subr_name, $msg);
      $msg  = "g_exp_dir_meta_data{$exp_dir}{'archive_is_empty'}";
      $msg .= " = " . $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
      gp_message ("debugXL", $subr_name, $msg);

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

  $msg  = "experiment $selected_archive has been selected for";
  $msg .= " archive analysis";
  gp_message ("debug", $subr_name, $msg);
  $msg  = "this archive is";
  $msg .= $archive_dir_not_empty ? " not empty" : " empty";
  gp_message ("debug", $subr_name, $msg);
  $msg  = "this archive is";
  $msg .= $selected_archive_has_elf_format ? " in" : " not in";
  $msg .= " ELF format";
  gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# 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"};

  $msg  = "number of files in archive $selected_archive is";
  $msg .= " " . $no_of_files_in_selected_archive;
  gp_message ("debug", $subr_name, $msg);

  for my $exp_dir (sort keys %g_exp_dir_meta_data)
    {
      my $is_empty = $g_exp_dir_meta_data{$exp_dir}{"archive_is_empty"};
      $msg  = "archive directory $exp_dir/archives is";
      $msg .= $is_empty ? " empty" : " not empty";
      gp_message ("debug", $subr_name, $msg);
    }
  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"}})
            {
              $msg  = $exp_dir . " " . $object . " ";
              $msg .=
		$g_exp_dir_meta_data{$exp_dir}{'archive_files'}{$object};
              gp_message ("debug", $subr_name, $msg);
            }
        }
    }

  return ($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 $msg;
  my $plural;

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

  $experiment_stats_line  = "<h3>\n";
  $experiment_stats_line .= "Full pathnames to the input experiment";
  $experiment_stats_line .= $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_stats_line .= $experiment_data[$i]{"start_date"} . ")\n";
    }
  $experiment_stats_line .= "</pre>\n";

  push (@experiment_stats_html, $experiment_stats_line);

  $msg = "experiment_stats_line = " . $experiment_stats_line;
  gp_message ("debugXL", $subr_name, $msg);

  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\"";
  $html_header .= " 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 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 };

  my $html_line;
  my $msg;

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

##  $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};
          $html_line .= " &nbsp;</td>";
        }
      else
        {
          $msg = "experiment_data[$i]{$key} does not exist";
##          gp_message ("assertion", $subr_name, $msg);
# TBD: warning or error?
          gp_message ("warning", $subr_name, $msg);
        }
    }
  $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;
  my $msg;

  $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 ";
      $html_header_line .= $experiment_data[$i]{"exp_id"} . "&nbsp;</th>";
    }
  $html_header_line .= "</div></tr>\n";

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

  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 $msg;
  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)
        {
          $msg = "directory $outputdir already exists";
          gp_message ("error", $subr_name, $msg);
          $g_total_error_count++;

          $msg  =  "use the -O/--overwrite option to overwrite an existing";
          $msg .= " directory";
          gp_message ("abort", $subr_name, $msg);
        }
#------------------------------------------------------------------------------
# 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);
              $msg = "fatal error when trying to remove " . $outputdir;
              gp_message ("abort", $subr_name, $msg);
            }
          else
            {
              $msg = "directory $outputdir has been removed";
              gp_message ("debug", $subr_name, $msg);
            }
        }
    }
#------------------------------------------------------------------------------
# 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))
    {
      $msg = "created output directory " . $outputdir;
      gp_message ("debug", $subr_name, $msg);
    }
  else
    {
      $msg = "a fatal problem occurred when creating directory " . $outputdir;
      gp_message ("abort", $subr_name, $msg);
    }

  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 $msg;
  my $name_loadobject;
  my $base_va_address;

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

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

  $msg  = "return base_va_address = $base_va_address";
  $msg .= " (decimal: $decimal_address)";
  gp_message ("debugXL", $subr_name, $msg);

  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 $executable_name;
  my $full_path_exec;
  my $msg;
  my $path_to_map_file;
  my $va_executable_in_hex;

  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;

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

  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 $cmd_output;
  my $convert_to_dot;
  my $decimal_separator;
  my $error_code;
  my $field;
  my $ignore_count;
  my @locale_info = ();
  my $msg;
  my $target_cmd;
  my $target_found;

  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.
#------------------------------------------------------------------------------
    {
      $msg = "failure to execute the command " . $target_cmd;
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;

      $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);
      $msg = "line from locale_info = " . $line;
      gp_message ("debug", $subr_name, $msg);

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

              $msg  = "line $line matches the search, but the decimal";
              $msg .= " separator has the wrong format";
              gp_message ("warning", $subr_name, $msg);
              $msg  = "the splitted line is [@split_line] and does not";
              $msg .= " contain 2 fields";
              gp_message ("warning", $subr_name, $msg);
              $msg  = "the default decimal separator will be used";
              gp_message ("warning", $subr_name, $msg);

              $g_total_warning_count++;
            }
          else
            {
#------------------------------------------------------------------------------
# We know there are 2 fields and the second one has the decimal point.
#------------------------------------------------------------------------------
              $msg = "split_line[1] = " . $split_line[1];
              gp_message ("debug", $subr_name, $msg);

              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.
#------------------------------------------------------------------------------
                {
                  $msg  = "unexpected output from the $target_cmd command:";
                  $msg .= " " . $field;
                  gp_message ("error", $subr_name, $msg);

                  $g_total_error_count++;

                  $error_code = 1;
                  last;
                }

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

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

      $g_total_warning_count++;
    }

  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;
  my $msg;

  $msg = "function_info for " . $name;
  gp_message ("debug", $subr_name, $msg);

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

  return (0);

} #-- End of subroutine dump_function_info

#------------------------------------------------------------------------------
# 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 $msg;
  my $return_value;

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

#------------------------------------------------------------------------------
# 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)
    {
      $msg = "need to handle a return value of FALSE";
      gp_message ("error", $subr_name, $msg);
      $g_total_error_count++;
      gp_message ("abort", $subr_name, $g_abort_msg);
    }

  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 $cmd_output;
  my $command_string;
  my $error_code;
  my $msg;
  my $target_cmd;

  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
#------------------------------------------------------------------------------
      $msg = "masquerading arch_uname_s->$arch_uname_s elf_arch->$elf_arch";
      gp_message ("debug", $subr_name, $msg);
      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 $load_long_regex;
  $load_long_regex     = '^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)';
  $load_long_regex    .= '\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$';
  my $load_short_regex = '^\s+LOAD\s+(\S+)\s+(\S+)\s+(\S+)$';
  my $re_regex         = '^\s+(\S+)\s+(\S+)\s+(R\sE)\s+(\S+)$';

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

  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"};

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

  my ($base, $ignore_value, $ignore_too) = fileparse ($loadobj);

  $msg = "base = $base " . basename ($loadobj);
  gp_message ("debug", $subr_name, $msg);

  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
#------------------------------------------------------------------------------
          $msg  = "masquerading arch_uname_s->" . $arch_uname_s;
          $msg .= " elf_arch->" . $elf_arch;
          gp_message ("debug", $subr_name, $msg);

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

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

      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;
              $elfoid  = $elf_rats{$loadobjARC}[1] . "/archives/";
              $elfoid .= $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)
                {
                  $msg = "call failure for " . $command_string;
                  gp_message ("error", $subr_name, $msg);
                  $g_total_error_count++;
                  gp_message ("abort", $subr_name, $g_abort_msg);
                }
              else
                {
                  $msg = "executed command_string = " . $command_string;
                  gp_message ("debug", $subr_name, $msg);
                  $msg = "cmd_output = " . $cmd_output;
                  gp_message ("debug", $subr_name, $msg);
                }
            }
          else
            {
              $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 =~ /$load_long_regex/)
            {
              $Offset   = $1;
              $VirtAddr = $2;
              $PhysAddr = $3;
              $FileSiz  = $4;
              $MemSiz   = $5;
              $Flg      = $6;
              $Align    = $7;

              $elf_offset = $VirtAddr;
              $msg = "single line version elf_offset = " . $elf_offset;
              gp_message ("debug", $subr_name, $msg);
              return ($elf_offset);
            }
          elsif ($foo =~ /$load_short_regex/)
            {
#------------------------------------------------------------------------------
# is it a two line version?
#------------------------------------------------------------------------------
              $Offset   = $1;
              $VirtAddr = $2; # maybe
              $PhysAddr = $3;
              if ($i != $#foo)
                {
                  $foo1 = $foo[$i + 1];
                  chomp ($foo1);
                  if ($foo1 =~ /$re_regex/)
                    {
                      $FileSiz  = $1;
                      $MemSiz   = $2;
                      $Flg      = $3;
                      $Align    = $4;
                      $elf_offset = $VirtAddr;
                      $msg = "two line version elf_offset = " . $elf_offset;
                      gp_message ("debug", $subr_name, $msg);
                      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) = @_;

  my $cmd_output;
  my $error_code;
  my $msg;

  chomp ($target_cmd);

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

  if ($error_code != 0)
    {
      chomp ($cmd_output);
      $msg = "failure executing command " . $target_cmd;
      gp_message ("error", $subr_name, $msg);
      $msg = "error code = " . $error_code;
      gp_message ("error", $subr_name, $msg);
      $msg = "cmd_output = " . $cmd_output;

      gp_message ("error", $subr_name, $msg);
      $g_total_error_count++;
    }
  else
    {
      $msg = "executed command " . $target_cmd;
      gp_message ("debugXL", $subr_name, $msg);
    }

  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 $map_xml_regex;
  $map_xml_regex  = '<event kind="map"\s.*';
  $map_xml_regex .= 'vaddr="0x([0-9a-fA-F]+)"\s+.*';
  $map_xml_regex .= 'foffset="\+*0x([0-9a-fA-F]+)"\s.*';
  $map_xml_regex .= 'modes="0x([0-9]+)"\s.*';
  $map_xml_regex .= 'name="(.*)".*>$';

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

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

  $msg = " - unable to open file $input_map_xml_file for reading:";
  open (MAP_XML, "<", $input_map_xml_file)
    or die ($subr_name . $msg . " " . $!);

  $msg = "opened file $input_map_xml_file for reading";
  gp_message ("debug", $subr_name, $msg);

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

      $msg = "read input_line = $input_line";
      gp_message ("debug", $subr_name, $msg);

      if ($input_line =~  /^$map_xml_regex/)
        {
          $msg = "target line = $input_line";
          gp_message ("debug", $subr_name, $msg);

          $vaddr     = $1;
          $foffset   = $2;
          $modes     = $3;
          $name_path = $4;
          $name      = get_basename ($name_path);

          $msg  = "extracted vaddr = $vaddr foffset = $foffset";
          $msg .= " modes = $modes";
          gp_message ("debug", $subr_name, $msg);

          $msg = "extracted name_path = $name_path name = $name";
          gp_message ("debug", $subr_name, $msg);

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

          $msg = "result_VA            = $result_VA";
          gp_message ("debug", $subr_name, $msg);

          $msg = "va_executable_in_hex = $va_executable_in_hex";
          gp_message ("debug", $subr_name, $msg);

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

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

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

  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 details.
# 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;
  my $msg;

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

      $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";

      $msg = "on return: metric_spec        = " . $metric_spec;
      gp_message ("debugM", $subr_name, $msg);
      $msg = "on return: metric_flavor      = " . $metric_flavor;
      gp_message ("debugM", $subr_name, $msg);
      $msg = "on return: metric_visibility  = " . $metric_visibility;
      gp_message ("debugM", $subr_name, $msg);
      $msg = "on return: metric_name        = " .  $metric_name;
      gp_message ("debugM", $subr_name, $msg);
      $msg = "on return: metric_description = " . $metric_description;
      gp_message ("debugM", $subr_name, $msg);

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

} #-- End of subroutine extract_metric_specifics

#------------------------------------------------------------------------------
# Extract the option value(s) from the input array.  In case the number of
# values execeeds the specified limit, warning messages are printed.
#
# In case the option value is valid, g_user_settings is updated with this
# value and a value of TRUE is returned.  Otherwise the return value is FALSE.
#
# Note that not in all invocations of this subroutine, gp_message() is
# operational.  Only after the debug settings have been finalized, the
# messages are printed.
#
# This subroutine also generates warnings about multiple occurrences
# and the validity of the values.
#------------------------------------------------------------------------------
sub extract_option_value
{
  my $subr_name = get_my_name ();

  my ($option_dir_ref, $max_occurrences_ref, $internal_option_name_ref,
      $option_name_ref) = @_;

  my @option_dir           = @{ $option_dir_ref };
  my $max_occurrences      = ${ $max_occurrences_ref };
  my $internal_option_name = ${ $internal_option_name_ref };
  my $option_name          = ${ $option_name_ref };

  my $deprecated_option_used;
  my $excess_occurrences;
  my $msg;
  my $no_of_occurrences;
  my $no_of_warnings = 0;
  my $option_value   = "not set yet";
  my $option_value_missing;
  my $option_value_missing_ref;
  my $reset_blank_value;
  my $special_treatment = $FALSE;
  my $valid = $FALSE;
  my $valid_ref;

  if (@option_dir)
    {
      $no_of_occurrences = scalar (@option_dir);

      $msg = "option_name          = $option_name";
      gp_message ("debug", $subr_name, $msg);
      $msg = "internal_option_name = $internal_option_name";
      gp_message ("debug", $subr_name, $msg);
      $msg = "no_of_occurrences    = $no_of_occurrences";
      gp_message ("debug", $subr_name, $msg);

      $excess_occurrences = ($no_of_occurrences > $max_occurrences) ?
							$TRUE : $FALSE;

#------------------------------------------------------------------------------
# This is not supposed to happen, but just to be sure, there is a check.
#------------------------------------------------------------------------------
      if ($no_of_occurrences < 1)
        {
          $msg  = "the number of fields is $no_of_occurrences";
          $msg .= " - should at least be 1";
          gp_message ("assertion", $subr_name, $msg);
        }

#------------------------------------------------------------------------------
# For backward compatibility, we support the legacy "on" and "off" values for
# certain options.
#
# We also support the debug option without value.  In case no value is given,
# it is set to "on".
#
# Note that regardless of the value(s) in ARGV, internally we use the on/off
# setting.
#------------------------------------------------------------------------------
      if (($g_user_settings{$internal_option_name}{"data_type"} eq "onoff") or
          ($internal_option_name eq "debug"))
        {
          $msg = "enable special treatment of the option";
          gp_message ("debug", $subr_name, $msg);

          $special_treatment = $TRUE;
        }

#------------------------------------------------------------------------------
# Issue a warning if the same option occcurs more often than what is supported.
#------------------------------------------------------------------------------
      if ($excess_occurrences)
        {
          $msg = "multiple occurrences of the " . $option_name .
                 " option found:";

          gp_message ("debugM", $subr_name, $msg);

          gp_message ("warning", $subr_name, $g_html_new_line . $msg);
        }

#------------------------------------------------------------------------------
# Main loop over all the occurrences of the options.  This is a rather simple
# approach since only the last value seen will be accepted.
#
# To assist the user with troubleshooting, the values that are ignored will be
# checked for validity and a marker to this extent will be printed.
#
# NOTE:
# If an option may have multiple meaningful occurrences, this part needs to be
# revisited.
#------------------------------------------------------------------------------
      $deprecated_option_used = $FALSE;
      for my $key (keys @option_dir)
        {
          $option_value      = $option_dir[$key];
          $reset_blank_value = $FALSE;

#------------------------------------------------------------------------------
# For the "onoff" options, convert a blank value to "on".
#------------------------------------------------------------------------------
          if (($option_value eq "on") or ($option_value eq "off"))
            {
              if (($option_name eq "--verbose") or ($option_name eq "--quiet"))
                {
  		  $deprecated_option_used = $TRUE;
                }
            }

#------------------------------------------------------------------------------
# For the "onoff" options, convert a blank value to "on".
#------------------------------------------------------------------------------
          if ($special_treatment and ($option_value eq ""))
            {
              $option_value = "on";
              $reset_blank_value = $TRUE;

              $msg  = "reset option value for $option_name from blank";
              $msg .= " to \"on\"";
              gp_message ("debug", $subr_name, $msg);
            }

#------------------------------------------------------------------------------
# Check for the option value to be valid.  It may also happen that an option
# does not have a value, while it should have one.
#------------------------------------------------------------------------------
          ($valid_ref, $option_value_missing_ref) = check_and_set_user_option (
							$internal_option_name,
							$option_value);

          $valid                = ${ $valid_ref };
          $option_value_missing = ${ $option_value_missing_ref };

          $msg  = "option_value = $option_value";
          gp_message ("debug", $subr_name, $msg);
          $msg  = "after check_and_set_user_option: valid = $valid";
          $msg .= " option_value_missing = $option_value_missing";
          gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
# Generate warning messages, but if an option value is missing, it will also
# be considered to be a fatal error.
#------------------------------------------------------------------------------
          if ($excess_occurrences)
            {
              if ($option_value_missing)
                {
                  $msg  = "$option_name option - missing a value";
                }
              else
                {
#------------------------------------------------------------------------------
# A little trick to avoid user confusion.  Although we have set the internal
# value to "on", the user did not set this and so we print "" instead.
#------------------------------------------------------------------------------
                  if ($reset_blank_value)
                    {
                      $msg  = "$option_name option - value = \"\"";
                    }
                  else
                    {
                      $msg  = "$option_name option - value = $option_value";
                    }
                  $msg .= ($valid) ? " (valid value)" : " (invalid value)";
                }

              gp_message ("debug", $subr_name, $msg);
              gp_message ("warning", $subr_name, $msg);
            }

#------------------------------------------------------------------------------
# Check for the last occurrence of the option to be valid.  If it is not, it
# is a fatal error.
#------------------------------------------------------------------------------
          if ((not $valid) && ($key == $no_of_occurrences-1))
            {
              if ($option_value_missing)
                {
                  $msg = "the $option_name option requires a value";
                }
              else
                {
                  $msg  = "the value of $option_value for the $option_name";
                  $msg .= " option is invalid";
                }
              gp_message ("debug", $subr_name, $g_error_keyword . $msg);

              gp_message ("error", $subr_name, $msg);

              $g_total_error_count++;
            }
        }

#------------------------------------------------------------------------------
# Issue a warning if the same option occcurs more often than what is supported
# and warn the user that all but the last value will be ignored.
#------------------------------------------------------------------------------
      if ($excess_occurrences)
        {
          $msg = "all values but the last one shown above are ignored";

          gp_message ("debugM", $subr_name, $msg);
          gp_message ("warning", $subr_name, $msg);

          $g_total_warning_count++;
        }
    }

#------------------------------------------------------------------------------
# Issue a warning if the old on/off syntax is used still.
#------------------------------------------------------------------------------
  if ($deprecated_option_used)
    {
      $msg  = "<br>";
      $msg .= "the on/off syntax for option $option_name has been";
      $msg .= " deprecated";
      gp_message ("warning", $subr_name, $msg);

      $msg  = "this option acts like a switch now";
      gp_message ("warning", $subr_name, $msg);

      $msg  = "support for the old syntax may be terminated";
      $msg .= " in a future update";
      gp_message ("warning", $subr_name, $msg);

      $msg  = "please check the gprofng-display-html man page";
      $msg .= " for more details";
      gp_message ("warning", $subr_name, $msg);
      $g_total_warning_count++;
    }

  return (\$valid);

} #-- End of subroutine extract_option_value

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

#------------------------------------------------------------------------------
# Finalize the settings for the special options verbose, debug, warnings and
# quiet.
#------------------------------------------------------------------------------
sub finalize_special_options
{
  my $subr_name = get_my_name ();

  my $msg;

#------------------------------------------------------------------------------
# If quiet mode has been enabled, disable verbose, warnings and debug.
#------------------------------------------------------------------------------
  if ($g_quiet)
    {
      $g_user_settings{"verbose"}{"current_value"}    = "off";
      $g_user_settings{"nowarnings"}{"current_value"} = "on";
      $g_user_settings{"warnings"}{"current_value"}   = "off";
      $g_user_settings{"debug"}{"current_value"}      = "off";
      $g_debug    = $FALSE;
      $g_verbose  = $FALSE;
      $g_warnings = $FALSE;
      my $debug_off = "off";
      my $ignore_value = set_debug_size (\$debug_off);
    }
  else
    {
#------------------------------------------------------------------------------
# Disable output buffering if verbose, debug, and/or warnings are enabled.
#------------------------------------------------------------------------------
      if ($g_verbose or $g_debug or $g_warnings)
        {
          STDOUT->autoflush (1);

          $msg = "enabled autoflush for STDOUT";
          gp_message ("debug", $subr_name, $msg);
        }
#------------------------------------------------------------------------------
# If verbose and/or debug have been enabled, print a message.
#------------------------------------------------------------------------------
##      gp_message ("verbose", $subr_name, "verbose mode has been enabled");
##      gp_message ("debug",   $subr_name, "debug " . $g_debug_size_value . " mode has been enabled");
    }

  return (0);

} #-- End of subroutine finalize_special_options

#------------------------------------------------------------------------------
# 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 gprofng-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, \$gp_path, \$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);
      $line =~ s/ --  no functions found//;

      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
              $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 $caller_callee_data_ref;
  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 $msg;

  my $remainder2;

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

  $msg = "building caller-callee file " . $outfile;
  gp_message ("debug", $subr_name, $msg);
  gp_message ("verbose", $subr_name, $msg);

#------------------------------------------------------------------------------
# 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 an array with the name caller_callee_data.
#------------------------------------------------------------------------------
  chomp (@caller_callee_data = <CALLER_CALLEE_IN>);

#------------------------------------------------------------------------------
# Remove a legacy redundant string, if any.
#------------------------------------------------------------------------------
  @caller_callee_data = @{ remove_redundant_string (\@caller_callee_data)};

#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
  $g_max_length_first_metric = 0;

  my @hex_addresses = ();
  my @metrics_array = ();
  my @length_first_metric = ();
  my @special_marker = ();
  my @the_function_name = ();
  my @the_metrics = ();

  my $find_hex_address_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(.*)';
  my $find_metric_values_regex  = '\)\s+\[.*\]\s+(\d+';
     $find_metric_values_regex .= '[\.\d\ ]*)|\)\s+(\d+[\.\d\ ]*)';
  my $find_marker_regex = '(^\*).*';

  my @html_block_prologue;
  my @html_code_function_block;
  my $marker;
  my $list_with_metrics;
  my $reduced_line;

  $msg  = "loop over the caller-callee data - number of lines = ";
  $msg .= ($#caller_callee_data + 1);
  gp_message ("debugXL", $subr_name, $msg);

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

      $msg = "line = " . $line . " input_line = " . $input_line;
      gp_message ("debugXL", $subr_name, $msg);

      if ($input_line =~ /$find_hex_address_regex/)
#------------------------------------------------------------------------------
# This is an input line of interest.
#------------------------------------------------------------------------------
        {
          my ($hex_address_ref, $marker_ref, $reduced_line_ref, 
              $list_with_metrics_ref) =
                                       split_function_data_line (\$input_line);

          $hex_address       = ${ $hex_address_ref };
          $marker            = ${ $marker_ref };
          $reduced_line      = ${ $reduced_line_ref };
          $list_with_metrics = ${ $list_with_metrics_ref };

          $msg = "RESULT full_hex_address = " . $hex_address;
          $msg .= " -- metric values = " . $list_with_metrics;
          $msg .= " -- marker = " . $marker;
          $msg .= " -- function name = " . $reduced_line;
          gp_message ("debugXL", $subr_name, $msg);
 
#------------------------------------------------------------------------------
# Store the address and marker.
#------------------------------------------------------------------------------
          push (@the_function_name, $reduced_line);
          push (@hex_addresses, $hex_address);
          if ($marker eq "*")
            {
              push (@special_marker, "*");
            }
          else
            {
              push (@special_marker, "X");
            }
#------------------------------------------------------------------------------
# Processing of the metrics.
#------------------------------------------------------------------------------
          @metrics_array = split (" ", $list_with_metrics);

#------------------------------------------------------------------------------
# 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 = $metrics_array[0];
          $msg = "first metric found = " . $first_metric;
          gp_message ("debugXL", $subr_name, $msg);
          if ($first_metric =~ /^0$decimal_separator$/)
            {
              $first_metric = "0.ZZZ";
              $msg = "fixed up $first_metric";
              gp_message ("debugXL", $subr_name, $msg);
            }
              $g_max_length_first_metric = max ($g_max_length_first_metric, 
						length ($first_metric));

              $msg = "first_metric = $first_metric " .
                     "g_max_length_first_metric = $g_max_length_first_metric";
              gp_message ("debugXL", $subr_name, $msg);
              push (@length_first_metric, length ($first_metric));
              push (@the_metrics, $list_with_metrics);
        }
    }

  $msg = "the following function names have been found";
  gp_message ("debugM", $subr_name, $msg);
  for my $i (0 .. $#the_function_name)
    {
      $msg = "the_function_name{" . $i . "] = " . $the_function_name[$i];
      gp_message ("debugM", $subr_name, $msg);
    }

  $msg = "final: g_max_length_first_metric = " . $g_max_length_first_metric;
  gp_message ("debugM", $subr_name, $msg);
  $msg = "\$#hex_addresses = " . $#hex_addresses;
  gp_message ("debugM", $subr_name, $msg);

#------------------------------------------------------------------------------
# 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++)
    {
      $input_line = $caller_callee_data[$line];

      if ($input_line =~ /$header_name_regex/)
        {
          $scan_header = $TRUE;
          $msg  = "line = " . $line . " encountered start of the header";
          $msg .= " scan_header = " . $scan_header . " first = " . $first;
          gp_message ("debugXL", $subr_name, $msg);
        }
      elsif (($input_line =~ /$sorted_by_regex/) or
             ($input_line =~ /$current_regex/))
        {
          $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;

          $msg = "line = $line updated index_end   = $index_end";
          gp_message ("debugXL", $subr_name, $msg);
          $msg = "line = $line input_line          = " . $input_line;
          gp_message ("debugXL", $subr_name, $msg);
          $msg = "line = $line data_function_block = " . $data_function_block;
          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;

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

          $msg  = "line = " . $line . " data_function_block = ";
          $msg .= $data_function_block;
          gp_message ("debugXL", $subr_name, $msg);

          push (@function_blocks, $data_function_block);

##          $msg  = "    generating the html blocks (";
##          $msg .= $index_start . " - " . $index_end .")";
##          gp_message ("verbose", $subr_name, $msg);

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

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

          for my $lines (0 .. $#html_code_function_block)
            {
              $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;
          $msg = "line = " . $line . " reset index_start = " . $index_start;
          gp_message ("debugXL", $subr_name, $msg);
          $msg = "line = " . $line . " reset index_end   = " . $index_end;
          gp_message ("debugXL", $subr_name, $msg);
        }

#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
  $msg  = "Parse and process function blocks - total blocks = ";
  $msg .= $#function_blocks + 1;
  gp_message ("verbose", $subr_name, $msg);

  for my $i (0 .. $#function_blocks)
    {
      $msg = "process function block " . $i;
      gp_message ("debugXL", $subr_name, $msg);

      $msg = "function_blocks[" . $i . "] = ". $function_blocks[$i];
      gp_message ("debugXL", $subr_name, $msg);
#------------------------------------------------------------------------------
# This split produces an empty first field.  This is why we skip this in the
# loop below.
#------------------------------------------------------------------------------
      my @entries = split ($separator, $function_blocks[$i]);

#------------------------------------------------------------------------------
# An example of the content of array @entries:
# <empty line>
# 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];

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

          my ($hex_address_ref, $marker_ref, $reduced_line_ref,
              $list_with_metrics_ref) =
                                       split_function_data_line (\$input_line);

          $full_hex_address       = ${ $hex_address_ref };
          $marker_target_function = ${ $marker_ref };
          $routine                = ${ $reduced_line_ref };
          $all_metrics            = ${ $list_with_metrics_ref };

          $msg = "RESULT full_hex_address = " . $full_hex_address;
          $msg .= " -- metric values = " . $all_metrics;
          $msg .= " -- marker = " . $marker_target_function;
          $msg .= " -- function name = " . $routine;
          gp_message ("debugXL", $subr_name, $msg);

          $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);
          push (@address_field, $full_hex_address);
          $msg  = "pushed " . $full_hex_address;
          $msg .= " to array address_field";
          gp_message ("debugXL", $subr_name, $msg);

          $modified_line = $all_metrics . " " . $routine;
          gp_message ("debugXL", $subr_name, "xxxxxxx = $modified_line");

          push (@metric_values, $all_metrics);
          $msg = "pushed " . $all_metrics . " to array metric_values";
          gp_message ("debugXL", $subr_name, $msg);

          push (@function_names, $routine);
          $msg = "pushed " . $routine . " to array function_names";
          gp_message ("debugXL", $subr_name, $msg);
        }

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

      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)
        {
          $msg  = $metric_values[$i] . " " . $marker[$i]; 
          $msg .= $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.
#------------------------------------------------------------------------------
      $msg  = "check for multiple occurrences - function_names = ";
      $msg .= ($#function_names + 1);
      gp_message ("debugXL", $subr_name, $msg);

      for my $i (0 .. $#function_names)
        {
          my $current_address = $address_field[$i];
          my $found_a_match;
          my $ref_index;
          my $alt_name;
          my $addr_offset;
 
          $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;

              $msg  = $routine . ": occurrences = ";
              $msg .= $g_function_occurrences{$routine};
              gp_message ("debugXL", $subr_name, $msg);

              for my $ref (keys @{ $g_map_function_to_index{$routine} })
                {
                  $ref_index = $g_map_function_to_index{$routine}[$ref];

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

                  $addr_offset = $function_info[$ref_index]{"addressobjtext"};
                  $msg = $routine . ": addr_offset = " . $addr_offset;
                  gp_message ("debugXL", $subr_name, $msg);

                  $addr_offset =~ s/$get_addr_offset_regex//;
                  $msg = $routine . ": addr_offset = " . $addr_offset;
                  gp_message ("debugXL", $subr_name, $msg);

                  if ($addr_offset eq $current_address)
                    {
                      $found_a_match = $TRUE;
                      last;
                    }
                }
              $msg  = $function_info[$ref_index]{'alt_name'};
              $msg .= " is the actual function for i = " . $i . " ";
              $msg .= $found_a_match;
              gp_message ("debugXL", $subr_name, $msg);

              $alt_name = $function_info[$ref_index]{'alt_name'};
            }
          gp_message ("debugXL", $subr_name, "alt_name = $alt_name");
        }
      $msg = "completed the check for multiple occurrences";
      gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# 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)
        {
          $msg  = "i = " . $i . " " . $word_index_values[$i][0] . " ";
          $msg .= $word_index_values[$i][1];
          gp_message ("debugXL", $subr_name, $msg);
        }

#------------------------------------------------------------------------------
# Empty the buffers before processing the next block with data.
#------------------------------------------------------------------------------
      @function_names = ();
      @metric_values = ();
      @address_field = ();
      @marker = ();
 
      $msg  = "erased contents of arrays function_names, metric_values, ";
      $msg .= "address_field, and marker";
      gp_message ("debugXL", $subr_name, $msg);

    }

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

  $msg = "the caller-callee information has been generated";
  gp_message ("verbose", $subr_name, $msg);

  return (0);

} #-- End of subroutine generate_caller_callee

#------------------------------------------------------------------------------
# Generate the html version of the disassembly file.
#
# Note to self (TBD)
# https://community.intel.com/t5/Intel-oneAPI-AI-Analytics/bd-p/ai-analytics-toolkit
#------------------------------------------------------------------------------
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 $file_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
#------------------------------------------------------------------------------
  $file_is_empty = is_file_empty ($filename);
  if ($file_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";
      $threshold_line .= " (--highlight-percentage) option:";
      $threshold_line .= " " . $rounded_percentage . " (%)</i>";
    }
  else
    {
      $threshold_line  = "<i>The highlight percentage feature has not been";
      $threshold_line .= " 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 $msg;
  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.
# TBD: Is still needed? Also, add the header command.
#------------------------------------------------------------------------------
  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";
#------------------------------------------------------------------------------
# TBD: fix the situation that call_metrics is empty.
#------------------------------------------------------------------------------
  if ($call_metrics ne "")
    {
      $script_pc_metrics = "address:$call_metrics";
    }
  else
    {
      $script_pc_metrics = "address";
      $msg = "warning: call_metrics is empty - only address field printed";
      gp_message ("debug", $subr_name, $msg);
    }
  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", $subr_name, "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];

      $input_line =~ s/ --  no functions found//;
      $input_data[$line] =~ s/ --  no functions found//;

      $msg = "line = " . $line . " input_line = " . $input_line;
      gp_message ("debugXL", $subr_name, $msg);

#      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

#------------------------------------------------------------------------------
# Get all the metrics available
#
# (gprofng-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 $msg;
  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
            {
              $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";

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

  $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             = ();

  my $msg;

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

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

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

  my $function_info_regex   = '\s*(\S+[a-zA-Z\s]*):(.*)';
  my $get_hex_address_regex = '(\d+):(0x\S+)';
#------------------------------------------------------------------------------
# Open the file generated using the -fsummary option.
#------------------------------------------------------------------------------
  $msg = " - unable to open file $FSUMMARY_FILE for reading:";
  open (FSUMMARY_FILE, "<", $FSUMMARY_FILE)
    or die ($subr_name . $msg . " " . $!);
  $msg = "opened file $FSUMMARY_FILE for reading";
  gp_message ("debug", $subr_name, $msg);

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

#------------------------------------------------------------------------------
# Legacy issue to deal with. Up until somewhere between binutils 2.40 and 2.41,
# gprofng display text might print the " --  no functions found" comment.
# No, the two spaces after -- are not my typo ;-)
#
# Since then, this comment is no longer printed, but the safe approach is to
# remove any occurrence upfront.
#------------------------------------------------------------------------------
      $line =~ s/ --  no functions found//;

      $msg = "line = " . $line;
      gp_message ("debugXL", $subr_name, $msg);

      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.
#
# REVISIT This may not be needed anymore
#------------------------------------------------------------------------------
          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;
                }
              $msg  = "g_function_occurrences{$routine} = ";
              $msg .= $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;
        }

#------------------------------------------------------------------------------
# Example format of an input block, where $line is one of the following:
#         Exclusive Total CPU Time: 0.001 (  0.0%)
#         Inclusive Total CPU Time: 0.001 (  0.0%)
#                             Size:    92
#                       PC Address: 5:0x00125de0
#                      Source File: (unknown)
#                      Object File: (unknown)
#                      Load Object: /usr/lib64/libc-2.28.so
#                     Mangled Name:
#                          Aliases: __brk
#------------------------------------------------------------------------------
      $line =~ s/^\s+//;
      if ($line =~ /$function_info_regex/)
        {
          if (defined ($1) and defined($2))
            {
              $field = $1;
              $value = $2;
              $value =~ s/$g_rm_surrounding_spaces_regex//g;

              $msg = "initial - field = " . $field . " value = " . $value;
              gp_message ("debugM", $subr_name, $msg);
            }
          else
            {
              $msg = "the input line pattern was not recognized";
              gp_message ("warning", $subr_name, $msg);
              gp_message ("debug", $subr_name, $msg);
              $msg = "execution continues, but there may be a problem later";
              gp_message ("warning", $subr_name, $msg);
              gp_message ("debug", $subr_name, $msg);

              $field = "not recognized";
              $value = "not recognized";
            }
#------------------------------------------------------------------------------
# The field has no value.
#------------------------------------------------------------------------------
          if (length ($value) eq 0)
##          if ($value =~ /^\s+$/)
##              if (length ($2) gt 0)
##              if ($2 == " ")
            {
              if ($field eq "Mangled Name")
                {
                  $value = $routine; 

                  $msg =  "no mangled name found - use the routine name ";
                  $msg .= $routine . " as the mangled name";
                  gp_message ("debugM", $subr_name, $msg);
                }
              else
                {
                  $value = "no_value_given";

                  $msg  =  "no value was found for this field - set to ";
                  $msg .=  $value;
                  gp_message ("debugM", $subr_name, $msg);
                }
            }
#------------------------------------------------------------------------------
# Remove any leading whitespace characters.
#------------------------------------------------------------------------------
          $value =~ s/$white_space_regex//;
#------------------------------------------------------------------------------
# These are the final values that will be used.
#------------------------------------------------------------------------------
          $msg = "final - field = " . $field . " value = " . $value;
          gp_message ("debugM", $subr_name, $msg);

          $function_info[$i]{$field} = $value;
        }
##      $value =~ s/$white_space_regex//;

## \s*(\S+[a-zA-Z\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)
#------------------------------------------------------------------------------
# No value
#------------------------------------------------------------------------------
###         {
###           $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)
###         {
###           $whatever = $input_fields[0];
### 	  if ($whatever eq "PC Address")
### #------------------------------------------------------------------------------
### # Must be an address field.  Restore the second colon.
### #------------------------------------------------------------------------------
### 	    {
###               $value = $input_fields[1] . ":" . $input_fields[2];
### 	    }
### 	  elsif ($whatever eq "Mangled Name")
### #------------------------------------------------------------------------------
### # The mangled name includes a colon (:).  Just copy the entire string.
### #------------------------------------------------------------------------------
### 	    {
###               $value = $input_fields[2];
### 	    }
###         }
###       else
###         {
### 	  if ($whatever eq "Aliases")
### #------------------------------------------------------------------------------
### # The mangled name includes a colon (:).  Just copy the entire string.
### #------------------------------------------------------------------------------
### 	    {
###               $value = $input_fields[2];
### 	    }
### 	  else
### 	    {
###               $msg = "input line = " . $line;
###               gp_message ("debug", $subr_name, $msg);
###               for my $i (keys @input_fields)
###                 {
###                   $msg = "input_fields[$i] = " . $input_fields[$i];
###                   gp_message ("debug", $subr_name, $msg);
###                 }
###               $msg = "unexpected input: number of fields = " . $no_of_elements;
###               gp_message ("debug", $subr_name, $msg);
### ##              gp_message ("assertion", $subr_name, $msg);
### 	    }
###        }
##      $function_info[$i]{$field} = $value;

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

      if ($field =~ /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.
# For example @2:0x0003f280
#------------------------------------------------------------------------------
              $full_address_field = $segment.":0x".$offset;

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

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

          $g_total_function_count++;
        }
    }
  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 =~ /$get_hex_address_regex/)
            {
              $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 = List::Util::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"};
        }

      $msg = "target_function = $target_function html_line = $html_line";
      gp_message ("debugM", $subr_name, $msg);
      $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)
    {
      $msg = "html_source_functions{$i} = $html_source_functions{$i}";
      gp_message ("debugM", $subr_name, $msg);
    }
  for my $i (keys @function_info)
    {
      $msg  = "function_info[$i]{\"html function block\"} = ";
      $msg .= $function_info[$i]{"html function block"};
      gp_message ("debugM", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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] })
        {
           $msg  = "on return: function_info[$i]{$role} = ";
           $msg .= $function_info[$i]{$role};
           gp_message ("debugM", $subr_name, $msg);
        }
    }
#------------------------------------------------------------------------------
# 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} })
        {
           $msg  = "on return: function_address_and_index{$F}{$fields} = ";
           $msg .= $function_address_and_index{$F}{$fields};
           gp_message ("debugM", $subr_name, $msg);
        }
    }
#------------------------------------------------------------------------------
# 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 this info on the page with experiment data.  Include names
# with multiple occurrences.
#------------------------------------------------------------------------------
  $msg = "Number of source files                            : " .
         $num_source_files;
  gp_message ("debug", $subr_name, $msg);
  $msg = "Total number of functions                         : " .
         $g_total_function_count;
  gp_message ("debug", $subr_name, $msg);
  $msg = "Number of functions with a unique name            : " .
         $number_of_unique_functions;
  gp_message ("debug", $subr_name, $msg);
  $msg = "Number of functions with more than one occurrence : " .
         $number_of_non_unique_functions;
  gp_message ("debug", $subr_name, $msg);
  my $multi_occurrences = $g_total_function_count -
                          $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 $msg;
  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);

#-- RUUD

  $msg = "summary_metrics = " . $summary_metrics;
  gp_message ("debug", $subr_name, $msg);
  for my $field (@sort_fields)
    {
     $msg = "metric field = " . $field; 
     gp_message ("debug", $subr_name, $msg);
    }
#------------------------------------------------------------------------------
# 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 };

  my $alt_name = $routine;
  my $current_address = $hex_address;
  my $found_a_match;
  my $index_into_function_info;
  my $msg;
  my $target_tag;

#------------------------------------------------------------------------------
# Check if this function has multiple occurrences.
#------------------------------------------------------------------------------
  $msg = "check for multiple occurrences";
  gp_message ("debugM", $subr_name, $msg);
  $msg = "target routine name = " . $routine;
  gp_message ("debugM", $subr_name, $msg);

  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);
## KANWEG      for my $key (sort keys %g_map_function_to_index)
## KANWEG        {
## KANWEG          $msg = "g_map_function_to_index{". $key . "} = " . $g_map_function_to_index{$key};
## KANWEG          gp_message ("debugXL", $subr_name, $msg);
## KANWEG        }
      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 $error_code;
  my $hostname_current;
  my $ignore_output;
  my $msg;
  my $target_cmd;
#------------------------------------------------------------------------------
# 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)
    {
      $msg = "the $machine instruction set architecture is not supported";
      gp_message ("error", $subr_name, $msg);
      gp_message ("diag", $subr_name, "Error: " . $msg);

      $msg = "temporarily ignored for development purposes";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
      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 $ignore_value;
  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 (not $g_debug))
    {
      $trigger_debug = $FALSE;
    }

#------------------------------------------------------------------------------
# Unconditionally buffer all warning messages.  These are available through the
# index.html page and cannot be disabled.
#
# If the quiet mode has been enabled, warnings are not printed though.
#------------------------------------------------------------------------------
  if ($action eq "warning")
    {
#------------------------------------------------------------------------------
# Remove any leading <br>, capitalize the first letter, and put the <br> back
# before storing the message in the buffer.
#------------------------------------------------------------------------------
      if ($comment_line =~ /^$g_html_new_line/)
        {
          $msg = $comment_line;
          $msg =~ s/$g_html_new_line//;
          $comment_line = $g_html_new_line . ucfirst ($msg);

          push (@g_warning_msgs, $comment_line);
        }
      else
        {
          push (@g_warning_msgs, ucfirst ($comment_line));
        }
    }

#------------------------------------------------------------------------------
# Unconditionally buffer all errror messages.  These will be printed prior to
# terminate execution.
#------------------------------------------------------------------------------
  if ($action eq "error")
#------------------------------------------------------------------------------
# Remove any leading <br>, capitalize the first letter, and put the <br> back.
#------------------------------------------------------------------------------
    {
      if ($comment_line =~ /^$g_html_new_line/)
        {
          $msg = $comment_line;
          $msg =~ s/$g_html_new_line//;
          $comment_line = $g_html_new_line . ucfirst ($msg);

          push (@g_error_msgs, $comment_line);
        }
      else
        {
          push (@g_error_msgs, 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 ($g_quiet))
       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 "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"))
      if ($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.
#------------------------------------------------------------------------------
        {
          my $error_identifier = $supported_identifiers{"error"};
          if (@g_error_msgs)
            {
              $ignore_value = print_errors_buffer (\$error_identifier);
            }
          printf ("%-9s %s", $identifier, ucfirst ($comment_line));
          printf (" - %s\n", "execution is terminated");
        }
      elsif ($action eq "assertion")
#------------------------------------------------------------------------------
# Enforce that the message starts with a lowercase symbol.
#------------------------------------------------------------------------------
        {
#------------------------------------------------------------------------------
# The lines are too long, but breaking the argument list gives this warning:
# printf (...) interpreted as function
#------------------------------------------------------------------------------
          printf ("%-17s %-30s", $identifier, $fixed_size_name);
          printf (" - %s\n", $comment_line);
        }
      elsif (($action eq "debug") and ($trigger_debug))
#------------------------------------------------------------------------------
# Debug messages are printed "as is".  Avoids issues when searching for them ;-)
#------------------------------------------------------------------------------
        {
          printf ("%-9s %-30s", $identifier, $fixed_size_name);
          printf (" - %s\n", $comment_line);
        }
      else
#------------------------------------------------------------------------------
# Enforce that the message starts with a lowercase symbol.
#------------------------------------------------------------------------------
        {
          printf ("%-9s %-30s", $identifier, $fixed_size_name);
          printf (" - %s\n", $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

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

#------------------------------------------------------------------------------
# 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 ($g_total_warning_count > 0)
    {
      print WARNINGS_OUT "<pre>\n";
      print WARNINGS_OUT "$_\n" for @g_warning_msgs;
      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 (0);

} #-- End of subroutine html_create_warnings_page

#------------------------------------------------------------------------------
# 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 index.html file.
#------------------------------------------------------------------------------
sub html_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, $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 @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 (" . $g_total_warning_count;
  $html_warnings_page .= ")</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 html_generate_index

#------------------------------------------------------------------------------
# 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 $is_empty;
  my $file_stat;
  my $msg;
  my $size;

  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.
#------------------------------------------------------------------------------
      $msg = "filename = $filename not found";
      gp_message ("debug", $subr_name, $msg);
      $is_empty = $TRUE;
    }
  else
    {
      $file_stat = stat ($filename);
      $size      = $file_stat->size;
      $is_empty  = ($size == 0) ? $TRUE : $FALSE;
    }

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

  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

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

#------------------------------------------------------------------------------
# TBD. Still needed? I think this entire function and usage can be removed.
#------------------------------------------------------------------------------
sub name_regex
{
  my $subr_name = get_my_name ();

  my ($metric_description_ref, $metrics, $field, $file) = @_;

  my %metric_description = %{ $metric_description_ref };

  my $msg;

  my @splitted_metrics;
  my $splitted_metrics;
  my $m;
  my $mf;
  my $nf;
  my $re = "This value should never show up anywhere";
  my $Xre;
#------------------------------------------------------------------------------
# Make sure to check for these to have a value.
#------------------------------------------------------------------------------
  my $noPCfile = undef;
  my $reported_metrics = undef;
  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!
#------------------------------------------------------------------------------
  if (not defined($reported_metrics))
    {
      $msg = "reported_metrics is not defined";
      gp_message ("debug", $subr_name, $msg);
    }
  else
    {
      $msg = "reported_metrics = " . $reported_metrics;
      gp_message ("debug", $subr_name, $msg);

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

      if (not defined($noPCfile))
        {
          $msg = "noPCfile is not defined";
          gp_message ("debug", $subr_name, $msg);
        }
      else
        {
          $msg = "noPCfile = " . $noPCfile;
          gp_message ("debug", $subr_name, $msg);

          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 more specific to the option, plus cross option checks,  will be
# performed soon after this subroutine has executed.
#
# Warnings, but also errors, are buffered.  In this way we can collect as many
# warnings and errors as possible, before bailing out in case of an error.
#------------------------------------------------------------------------------
sub parse_and_check_user_options
{
  my $subr_name = get_my_name ();

  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 $msg;
  my $outputdir_value;
  my $quiet_value;
  my $hp_value;
  my $valid;
  my $verbose_value;

  my $number_of_fields;

  my $internal_option_name;
  my $option_name;

  my $verbose = undef;
  my $warning = undef;

  my @opt_debug                = ();
  my @opt_highlight_percentage = ();
  my @opt_nowarnings           = ();
  my @opt_obsoleted_hp         = ();
  my @opt_output               = ();
  my @opt_overwrite            = ();
  my @opt_quiet                = ();
  my @opt_verbose              = ();
  my @opt_warnings             = ();

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
  my $no_of_warnings;
  my $total_warning_msgs = 0;
  my $option_value;
  my $option_warnings;
  my $no_of_warnings_ref;
  my $no_of_errors_ref;

  my $index_exp;
  my $first = $TRUE;
  my $trigger = $FALSE;
  my $found_non_exp = $FALSE;
  my $name_non_exp_dir;
  my $no_of_experiments = 0;

  my @opt_help = ();
  my @opt_version = ();
  my $stop_execution = $FALSE;

  my $option_value_ref;
  my $max_occurrences;
#------------------------------------------------------------------------------
# Configure Getopt to:
# - Silence warnings, since these are handled by the code.
# - Enforce case sensitivity in order to support -o and -O for example.
#------------------------------------------------------------------------------
  Getopt::Long::Configure("pass_through", "no_ignore_case");

#------------------------------------------------------------------------------
# Check for the --help and --version options.  Print a message and exit.
# Note that we support using both options simultaneously on the command line.
#------------------------------------------------------------------------------
  GetOptions (
    "help"    => \@opt_help,
    "version" => \@opt_version
  );

  if (@opt_help)
    {
      $stop_execution = $TRUE;
      $ignore_value   = print_help_info ();
    }
  if (@opt_version)
    {
      $stop_execution = $TRUE;
      $ignore_value   = print_version_info ();
    }

  if ($stop_execution)
    {
      exit (0);
    }

#------------------------------------------------------------------------------
# First, scan ARGV for the experiment names.  If there are no names, or the
# list with the names is not contiguous (meaning there is an non-experiment
# name in this list), an error message is printed and execution is terminated.
#
# Upon return from this function, the list with the experiment names is
# known and has been removed from ARGV.
#
# As a result, exp_dir_list is available from there on.
#
# This makes the subsequent processing of ARGV with GetOptions()  easier.
#------------------------------------------------------------------------------
  @exp_dir_list = @{ check_the_experiment_list () };

#------------------------------------------------------------------------------
# Configure Getopt to:
# - Silence warnings, since these are handled by the code.
# - Enforce case sensitivity in order to support -o and -O for example.
# - Allow unique abbreviations (also the default).
#------------------------------------------------------------------------------
  Getopt::Long::Configure("pass_through", "no_ignore_case", "auto_abbrev");
#------------------------------------------------------------------------------
# Get the remaining command line options.
#
# Recall:
# = => option requires a value
# : => option value is optional
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# All options are considered to be a string.
#
# We request every option supported to have an optional value.  Otherwise,
# GetOptions skips an option that does not have a value.
#
# The logic that parses the options deals with this and checks if an option
# that should have a value, actually has one.
#------------------------------------------------------------------------------
  GetOptions (
    "verbose|v:s"            => \@opt_verbose,
    "debug|d:s"              => \@opt_debug,
    "warnings|w:s"           => \@opt_warnings,
    "nowarnings:s"           => \@opt_nowarnings,
    "quiet|q:s"              => \@opt_quiet,
    "output|o=s"             => \@opt_output,
    "overwrite|O=s"          => \@opt_overwrite,
    "highlight-percentage=s" => \@opt_highlight_percentage,
    "hp=s"                   => \@opt_obsoleted_hp
  );

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Handle the user input and where needed, generate warnings.  In a later stage
# we check for (cross option) errors and warnings.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# The very first thing to do is to determine if the user has enabled one of the
# following options and take action accordingly:
# --quiet, --verbose, --debug, --warnings
#
# We first need to check for quiet mode to be set.  If so, all messages need to
# be silenced, regardless of the settings for verbose, debug, and warnings.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# The quiet option.
#------------------------------------------------------------------------------
  if (@opt_quiet)
    {
      $max_occurrences      = 1;
      $internal_option_name = "quiet";
      $option_name          = "--quiet";

      my ($valid_ref) = extract_option_value (\@opt_quiet,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);

      $valid = ${ $valid_ref };

      if ($valid)
        {
          $g_quiet = $g_user_settings{"quiet"}{"current_value"} eq "on" ?
								$TRUE : $FALSE;
        }
    }

#------------------------------------------------------------------------------
# The debug option.
#------------------------------------------------------------------------------
  if (@opt_debug)
    {
      $max_occurrences      = 1;
      $internal_option_name = "debug";
      $option_name          = "-d/--debug";

      my ($valid_ref) = extract_option_value (\@opt_debug,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);

      $valid = ${ $valid_ref };

      if ($valid)
#------------------------------------------------------------------------------
# Set the appropriate debug size (e.g. "XL") in a table that is used in the
# gp_message() subroutine.
#------------------------------------------------------------------------------
        {
          $g_debug = $TRUE;
          $ignore_value = set_debug_size ();
        }
    }

#------------------------------------------------------------------------------
# The verbose option.
#------------------------------------------------------------------------------
  if (@opt_verbose)
    {
      $max_occurrences      = 1;
      $internal_option_name = "verbose";
      $option_name          = "--verbose";

      my ($valid_ref) = extract_option_value (\@opt_verbose,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
      $valid = ${ $valid_ref };

      if ($valid)
        {
          $g_verbose = $g_user_settings{"verbose"}{"current_value"} eq "on" ?
								$TRUE : $FALSE;
        }
    }

#------------------------------------------------------------------------------
# The nowarnings option.
#------------------------------------------------------------------------------
  if (@opt_nowarnings)
    {
      $max_occurrences      = 1;
      $internal_option_name = "nowarnings";
      $option_name          = "--nowarnings";

      my ($valid_ref) = extract_option_value (\@opt_nowarnings,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);

      $valid = ${ $valid_ref };

      if ($valid)
        {
          $g_warnings =
		$g_user_settings{"nowarnings"}{"current_value"} eq "on" ?
								$FALSE : $TRUE;
        }
    }

#------------------------------------------------------------------------------
# The warnings option (deprecated).
#------------------------------------------------------------------------------
  if (@opt_warnings)
    {
      $max_occurrences      = 1;
      $internal_option_name = "warnings";
      $option_name          = "--warnings";

      my ($valid_ref) = extract_option_value (\@opt_warnings,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
    }

#------------------------------------------------------------------------------
# At this point, the debug, verbose, warnings and quiet settings are known.
# This subroutine makes the final decision on these settings.  For example, if
# quiet mode has been specified, the settings for debug, verbose and warnings
# are ignored.
#------------------------------------------------------------------------------
  $ignore_value = finalize_special_options ();

#------------------------------------------------------------------------------
# A this point we know we can start printing messages in case verbose and/or
# debug mode have been set.
#------------------------------------------------------------------------------
  $msg = "the original command line options: " . join (", ", @CopyOfARGV);
  gp_message ("debug", $subr_name, $msg);

  $msg = "the command line options after the special options: " .
         join (", ", @ARGV);
  gp_message ("debug", $subr_name, $msg);

  gp_message ("verbose", $subr_name, "Parsing the user options");

#------------------------------------------------------------------------------
# The output option.
#------------------------------------------------------------------------------
  if (@opt_output)
    {
      $max_occurrences      = 1;
      $internal_option_name = "output";
      $option_name          = "-o/--output";

      my ($valid_ref) = extract_option_value (\@opt_output,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
    }

#------------------------------------------------------------------------------
# The overwrite option.
#------------------------------------------------------------------------------
  if (@opt_overwrite)
    {
      $max_occurrences      = 1;
      $internal_option_name = "overwrite";
      $option_name          = "-O/--overwrite";

      my ($valid_ref) = extract_option_value (\@opt_overwrite,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
    }

#------------------------------------------------------------------------------
# The highlight-percentage option.
#------------------------------------------------------------------------------
  if (@opt_highlight_percentage)
    {
      $max_occurrences      = 1;
      $internal_option_name = "highlight_percentage";
      $option_name          = "--highlight-percentage";

      my ($valid_ref) = extract_option_value (\@opt_highlight_percentage,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
    }

#------------------------------------------------------------------------------
# The hp option (deprecated)
#------------------------------------------------------------------------------
  if (@opt_obsoleted_hp)
    {
      $max_occurrences      = 1;
      $internal_option_name = "hp";
      $option_name          = "-hp";

      my ($valid_ref) = extract_option_value (\@opt_obsoleted_hp,
					      \$max_occurrences,
					      \$internal_option_name,
					      \$option_name);
    }

#------------------------------------------------------------------------------
# By now, all options given on the command line have been processed and the
# list with experiment directories is known.
#
# Process the remainder of ARGV, but other than the option generated by the
# driver, ARGV should be empty.
#------------------------------------------------------------------------------
  $ignore_value = wrap_up_user_options ();

# Temporarily disabled       elsif (($arg eq "-fl") or ($arg eq "--func-limit"))
# Temporarily disabled       elsif (($arg eq "-ct") or ($arg eq "--calltree"))
# Temporarily disabled       elsif (($arg eq "-tp") or ($arg eq "--threshold-percentage"))
# Temporarily disabled       elsif (($arg eq "-dm") or ($arg eq "--default-metrics"))
# Temporarily disabled       elsif (($arg eq "-im") or ($arg eq "--ignore-metrics"))

  if (@exp_dir_list)
#------------------------------------------------------------------------------
# Print the list of the experiment directories found.
#
# Note that later we also check for these directories to actually exist
# and be valid experiments..
#------------------------------------------------------------------------------
    {
      $found_exp_dir = $TRUE;
      $msg = "the following experiment directories will be used:";
      gp_message ("debug", $subr_name, $msg);
      for my $i (keys @exp_dir_list)
        {
          my $msg = "exp_dir_list[$i] = $exp_dir_list[$i]";
          gp_message ("debug", $subr_name, $msg);
        }
    }
  else
#------------------------------------------------------------------------------
# Print a message if the experiment list is not valid, or empty.  There will
# also be error messages in the buffer. These will be printed later.
#------------------------------------------------------------------------------
    {
      $msg = "experiment directory name(s) are either not valid, or missing";
      gp_message ("debug", $subr_name, $msg);
    }

  return (\$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 $msg;
  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 };

#------------------------------------------------------------------------------
# TBD.  This part needs work.  The return variables from generate_dis_html ()
# are not used, so the code below is meaningless, but awaiting a true fix,
# the problem which appears on aarch64 is bypassed.
#------------------------------------------------------------------------------
      if (defined ($metric_ref))
        {
          @metric = @{ $metric_ref };
        }
      else
        {
          $msg = "metric_ref after generate_dis_html is undefined";
          gp_message ("debug", $subr_name, $msg);
        }
    }

  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 Still needed?
#------------------------------------------------------------------------------
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 .= "/";

  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 original list with the command line options.
#------------------------------------------------------------------------------
sub print_command_line_options
{
  my ($identifier_ref) = @_;

  my $identifier = ${ $identifier_ref };
  my $msg;

  $msg = "The command line options (shown for ease of reference): ";
  printf ("%-9s %s\n", $identifier, ucfirst ($msg));

  $msg = join (", ", @CopyOfARGV);
  printf ("%-9s %s\n", $identifier, $msg);

#  printf ("%-9s\n", $identifier);

  return (0);

} #-- End of subroutine print_command_line_options

#------------------------------------------------------------------------------
# Print all the errors messages in the buffer.
#------------------------------------------------------------------------------
sub print_errors_buffer
{
  my $subr_name = get_my_name ();

  my ($identifier_ref) = @_;

  my $ignore_value;
  my $msg;
  my $plural_or_single;
  my $identifier = ${ $identifier_ref };

  $plural_or_single = ($g_total_error_count > 1) ? "errors have" : "error has";

  if (@g_warning_msgs and $g_warnings)
#------------------------------------------------------------------------------
# Make sure that all warnings are printed in case of an error.  This is to
# avoid that warnings get lost in case the program terminates early.
#------------------------------------------------------------------------------
    {
      $ignore_value = print_warnings_buffer ();
    }

  if (not $g_options_printed)
#------------------------------------------------------------------------------
# The options are printed as part of the warnings, so only if the warnings are
# not printed, we need to print them in case of errors.
#------------------------------------------------------------------------------
    {
      $g_options_printed = $TRUE;
      $ignore_value =  print_command_line_options (\$identifier);
    }

  $msg  =  "a total of " . $g_total_error_count;
  $msg .=  " fatal " . $plural_or_single . " been detected:";
  printf ("%-9s %s\n", $identifier, ucfirst ($msg));

  for my $key (keys @g_error_msgs)
    {
      $msg = $g_error_msgs[$key];
      printf ("%-11s %s\n", $identifier, ucfirst ($msg));
    }

  return (0);

} #-- End of subroutine print_errors_buffer

#------------------------------------------------------------------------------
# Print the help overview
#------------------------------------------------------------------------------
sub print_help_info
{
  my $space = " ";

  printf("%s\n",
  "Usage: $driver_cmd [OPTION(S)] EXPERIMENT(S)");
  printf("\n");
  printf("%s\n",
  "Process one or more experiments to generate a directory containing the");
  printf("%s\n",
  "index.html file that may be used to browse the experiment data.");
  printf("\n");
  printf("%s\n",
  "Options:");
  printf("\n");
  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("--help",
  "Print usage information and exit.");

  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("--version",
  "Print the version number and exit.");

  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("--verbose",
  "Enable verbose mode to show diagnostic messages about the");
  print_help_line ("",
  "processing of the data.  By default verbose mode is disabled.");

  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("-d [<db-vol-size>], --debug[=<db-vol-size>]",
  "Control the printing of run time debug information to assist with");
  print_help_line ("",
  "the troubleshooting, or further development of this tool.");
  print_help_line ("",
  "The <db-vol-size> parameter controls the output volume and is");
  print_help_line ("",
  "one from the list {s | S | m | M | l | L | xl | XL}.");
  print_help_line ("",
  "If db-vol-size is not specified, a modest amount of information");
  print_help_line ("",
  "is printed.  This is equivalent to select size s, or S. The");
  print_help_line ("",
  "volume of data goes up as the size increases.  Note that");
  print_help_line ("",
  "currently l/L is  equivalent to xl/XL, but this is expected to");
  print_help_line ("",
  "change in future updates.  By default debug mode is disabled.");

  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("--highlight-percentage=<value>",
  "A percentage value in the interval [0,100] to select and color");
  print_help_line ("",
  "code source lines, as well as instructions, that are within this");
  print_help_line ("",
  "percentage of the maximum metric value(s).  A value of zero");
  print_help_line ("",
  "disables this feature.  The default value is 90 (%).");

  #-------Marker line - do not go beyond this line ----------------------------
  print_help_line ("-o <dirname>, --output=<dirname>",
  "Use <dirname> as the directory name to store the results in.");
  print_help_line ("",
  "In absence of this option, the default name is display.<n>.html.");
  print_help_line ("",
  "This directory is created in the current directory.  The number");
  print_help_line ("",
  "<n> is the first positive integer number not in use in this");
  print_help_line ("",
  "naming scheme.  An existing directory with the same name is not");
  print_help_line ("",
  "overwritten.  Make sure that umask is set to the correct access");
  print_help_line ("",
  "permissions.");

  #-------Marker line - do not go beyond this line --------------------------
  print_help_line ("-O <dirname>, --overwrite=<dirname>",
  "Use <dirname> as the directory name to store the results in.");
  print_help_line ("",
  "In absence of this option, the default name is display.<n>.html.");
  print_help_line ("",
  "This directory is created in the current directory.  The number");
  print_help_line ("",
  "<n> is the first positive integer number not in use in this");
  print_help_line ("",
  "naming scheme.  An existing directory with the same name is");
  print_help_line ("",
  "silently overwritten.  Make sure that umask is set to the");
  print_help_line ("",
  "correct access permissions.");

  #-------Marker line - do not go beyond this line --------------------------
  print_help_line ("-q, --quiet",
  "Disable the display of all warning, debug, verbose and any");
  print_help_line ("",
  "other messages.  If enabled, the settings for verbose and debug");
  print_help_line ("",
  "are accepted, but ignored.  With this option, there is no screen");
  print_help_line ("",
  "output, other than errors.  By default quiet mode is disabled");

  #-------Marker line - do not go beyond this line --------------------------
  print_help_line ("--nowarnings",
  "Disable the printing of warning messages on stdout.  By default");
  print_help_line ("",
  "warning messages are printed.");

  #-------Marker line - do not go beyond this line --------------------------
  printf("\n");
  printf ("%s\n","Report bugs to <https://sourceware.org/bugzilla/>");

  return (0);

} #-- End of subroutine print_help_info

#------------------------------------------------------------------------------
# Print a single line as part of the help output.
#
# If the first item is not the empty string, it is considered to be the
# option.  If the length of the option exceeds the limit set by $max_space,
# it is printed by itself and the text is printed on the next line.  Otherwise
# the text follows the option.
#
# To assist with the development of the help text, we check if the total length
# of the line exceeds the max numbers of columns (79 according to the GNU
# coding standards).
#------------------------------------------------------------------------------
sub print_help_line
{
  my $subr_name = get_my_name ();

  my ($item, $help_text) = @_;

  my $length_item = length ($item);
  my $max_col = 79;
  my $max_space = 14;
  my $no_of_spaces;
  my $pad;
  my $space = " ";
  my $the_message;

  if ($length_item > $max_col)
    {
      printf ("Error: $item is $length_item long - exceeds $max_col\n");
      exit (0);
    }
  elsif ( $length_item == 0 )
    {
      $no_of_spaces = $max_space;

      $pad = "";
      for my $i (1..$no_of_spaces)
        {
          $pad .= $space;
        }
      $the_message = $pad . $help_text;
    }
  else
    {
    if ($length_item < $max_space)
      {
        $no_of_spaces = $max_space - length ($item);
        $pad = "";
        for my $i (1..$no_of_spaces)
          {
            $pad .= $space;
          }
        $the_message = $item . $pad . $help_text;
      }
    else
      {
        $pad = "";
        for my $i (1..$max_space)
          {
            $pad .= $space;
          }
        printf("%s\n", $item);
        $the_message = $pad . $help_text;
      }
    }

  if (length ($the_message) <= $max_col)
    {
      printf ("%s\n", $the_message);
    }
  else
    {
      my $delta = length ($the_message) - $max_col;
      printf ("%s\n", "$the_message - exceeds $max_col by $delta");
      exit (0);
    }


  return (0);

} #-- End of subroutine print_help_line

#------------------------------------------------------------------------------
# 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 $data_type;
  my $debug_size_value = $g_user_settings{"debug"}{"current_value"};
  my $db_size;
  my $defined;
  my $keyword;
  my $leftover;
  my $padding;
  my $user_option;
  my $value;

  my $HEADER_LIMIT = 79;
  my $header = sprintf ("%-20s   %-22s   %8s   %s",
                        "keyword", "option", "user set", "internal 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 $key  (sort keys %g_user_settings)
    {
      $keyword     = $key;
      $user_option = $g_user_settings{$key}{"option"};
      $defined     = ($g_user_settings{$key}{"defined"} ? "set" : "not set");
      $data_type   = $g_user_settings{$key}{"data_type"};

      if (defined ($g_user_settings{$key}{"current_value"}))
        {
          $value = $g_user_settings{$key}{"current_value"};
          if ($data_type eq "boolean")
            {
              $value = $value ? "on" : "off";
            }
#------------------------------------------------------------------------------
# In case of the debug option, we add the "(size)" string to remind the user
# that this is the size.
#------------------------------------------------------------------------------
          if ($key eq "debug")
            {
              $db_size = ($debug_size_value eq "on") ? "s" : $debug_size_value;
              $value = $db_size . " (size)";
            }
        }
      else
        {
          $value = "undefined";
        }

      my $print_line = sprintf ("%-20s   %-22s   %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 $key (keys %g_user_settings)
    {
      my $print_line = sprintf ("%-20s =>", $key);
      for my $fields (sort keys %{ $g_user_settings{$key} })
        {
          if (defined ($g_user_settings{$key}{$fields}))
            {
              $keyword_value_pair = $fields." = ".$g_user_settings{$key}{$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) 2025 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

#------------------------------------------------------------------------------
# Dump all the warning messages in the buffer.
#------------------------------------------------------------------------------
sub print_warnings_buffer
{
  my $subr_name = get_my_name ();

  my $ignore_value;
  my $msg;

  if (not $g_options_printed)
#------------------------------------------------------------------------------
# Only if the options have not yet been printed, print them.
#------------------------------------------------------------------------------
    {
      $g_options_printed = $TRUE;
      $ignore_value = print_command_line_options (\$g_warn_keyword);
    }

  for my $i (keys @g_warning_msgs)
    {
      $msg = $g_warning_msgs[$i];
      if ($msg =~ /^$g_html_new_line/)
        {
          $msg =~ s/$g_html_new_line//;
          printf ("%-9s\n", $g_warn_keyword);
        }
      printf ("%-9s %s\n", $g_warn_keyword, ucfirst ($msg));
    }

  return (0);

} #-- End of subroutine print_warnings_buffer

#------------------------------------------------------------------------------
# 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 $find_clone_regex    = '^(.*)(\s+--\s+cloned\s+version\s+\[)([^]]+)(\])';
  my $remove_number_regex = '^\d+:';
  my $replace_quote_regex = '"/\"';

  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 $msg;

  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"};

#------------------------------------------------------------------------------
# Strip the internal number from the address field.
#------------------------------------------------------------------------------
      $msg = "address_field before regex = " . $address_field;
      gp_message ("debugXL", $subr_name, $msg);
      $address_field =~ s/$remove_number_regex//;
      $msg = "address_field after  regex = " . $address_field;
      gp_message ("debugXL", $subr_name, $msg);

##      $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";
#------------------------------------------------------------------------------
## TBD: adding the address is not supported.  Need to find a way to figure
## out the ID of the function.
##      print SCRIPT "disasm \"$tmp\" $address_field\n";
##      print SCRIPT "source \"$tmp\" $address_field\n";
#------------------------------------------------------------------------------
      print SCRIPT "disasm \"$tmp\"\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\"\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", $subr_name, "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 $marker;
  my $name_regex;
  my $no_of_fields;
  my $metrics_length;
  my $missing_digits;
  my $msg;
  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  = '\/';

  $msg = "enter subroutine " . $subr_name;
  gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
  if (is_file_empty ($overview_file))
    {
      gp_message ("assertion", $subr_name, "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");

#------------------------------------------------------------------------------
# Remove a legacy redundant string, if any.
#------------------------------------------------------------------------------
  @function_data = @{ remove_redundant_string (\@function_data)};

#------------------------------------------------------------------------------
# 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];
##      $input_line =~ s/ --  no functions found//;

      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;
              $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/)))
        {
          $msg = "detected a line with function data";
          gp_message ("debugXL", $subr_name, $msg);

          my ($hex_address_ref, $marker_ref, $reduced_line_ref, 
              $list_with_metrics_ref) =
                                       split_function_data_line (\$input_line);

          $full_hex_address  = ${ $hex_address_ref };
          $marker            = ${ $marker_ref };
          $routine           = ${ $reduced_line_ref };
          $all_metrics       = ${ $list_with_metrics_ref };

          $msg = "RESULT full_hex_address = " . $full_hex_address;
          $msg .= " -- metric values = " . $all_metrics;
          $msg .= " -- marker = " . $marker;
          $msg .= " -- function name = " . $routine;
          gp_message ("debugXL", $subr_name, $msg);

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

          $no_of_fields = $#fields + 1;
          $elements_in_name = $no_of_fields - $number_of_metrics - 1;

          $msg  = "no_of_fields = " . $no_of_fields;
          $msg .= " elements_in_name = " . $elements_in_name;
          gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# In case the last metric is 0. only, we append 3 extra characters that
# represent zero.  We cannot change the number to 0.000 though because that
# has a different interpretation than 0.
# In a later phase, the "ZZZ" symbol will be removed again, but for now it
# creates consistency in, for example, the length of the metrics part.
#------------------------------------------------------------------------------
              if ($all_metrics =~ /$zero_dot_at_end_regex/)
                {
                  if (defined ($1) )
                    {
#------------------------------------------------------------------------------
# Somewhat overkill, but remove the leading "\" from the decimal separator
# in the debug print since it is used for internal purposes only.
#------------------------------------------------------------------------------
                      my $decimal_point = $decimal_separator;
                      $decimal_point =~ s/$backward_slash_regex//;
                      my $txt = "all_metrics = $all_metrics ended with 0";
                      $txt   .= "$decimal_point ($decimal_separator)";
                      gp_message ("debugXL", $subr_name, $txt);

                      $all_metrics .= "ZZZ";
                    }
                }
              $metrics_length = length ($all_metrics);
              $max_metrics_length = max ($max_metrics_length, $metrics_length);
              gp_message ("debugXL", $subr_name, "$routine all_metrics = $all_metrics metrics_length = $metrics_length");

              $msg = "verify full_hex_address = " . $full_hex_address;
              gp_message ("debugXL", $subr_name, $msg);

              if ($full_hex_address =~ /$get_hex_address_regex/)
                {
                  $hex_address = "0x" . $2;
                }
              else
                {
                  $msg = "full_hex_address = $full_hex_address has the wrong format";
                  gp_message ("assertion", $subr_name, $msg);
                }

              push (@address_field, $full_hex_address);

              $msg = "pushed full_hex_address = " . $full_hex_address; 
              gp_message ("debugXL", $subr_name, $msg);

              push (@metric_values, $all_metrics);

#------------------------------------------------------------------------------
# Record the function name "as is".  Below we figure out what the final name
# should be in case there are multiple occurrences of the same name.
#
# The reason to decouple this is to avoid the code gets too complex here.
#------------------------------------------------------------------------------
              push (@function_names, $routine);
        }
    } #-- End of loop over the input lines

#------------------------------------------------------------------------------
# Store the maximum lengths for the header and metrics.
#------------------------------------------------------------------------------
    gp_message ("debugXL", $subr_name, "final max_header_length  = $max_header_length");
    gp_message ("debugXL", $subr_name, "final max_metrics_length = $max_metrics_length");

    $function_view_structure{"max header length"}  = $max_header_length;
    $function_view_structure{"max metrics length"} = $max_metrics_length;

#------------------------------------------------------------------------------
# Determine the final name for the functions and set up the HTML block.
#------------------------------------------------------------------------------
  my @final_html_function_block = ();
  my @function_index_list       = ();

#------------------------------------------------------------------------------
# First, an index list is built.  If we are to index the functions in order of
# appearance in the function overview from 0 to n-1, the value of the array
# for index "i" is the index into the large "function_info" structure.  This
# has the final name, the html function block, etc.
#------------------------------------------------------------------------------

  for my $i (keys @address_field)
    {
      $msg = "address_field[" . $i ."] = " . $address_field[$i];
      gp_message ("debugM", $subr_name, $msg);
    }
#------------------------------------------------------------------------------
## TBD: Use get_index_function_info??!!
#------------------------------------------------------------------------------
  for my $i (keys @function_names)
    {
#------------------------------------------------------------------------------
# Get the function name and the address from the function overview.  The
# address is used to differentiate in case a function has multiple occurences.
#------------------------------------------------------------------------------
      my $routine = $function_names[$i];
      my $current_address = $address_field[$i];

      my $final_function_name;
      my $found_a_match = $FALSE;
      my $msg;
      my $ref_index;

      $msg  = "on entry - routine = " . $routine; 
      $msg .= " current_address = " . $current_address;
      gp_message ("debugM", $subr_name, $msg);

#------------------------------------------------------------------------------
# Check if there are duplicate entries for this function.  If there are, use
# the address to find the right match in the function_info structure.
#------------------------------------------------------------------------------
      gp_message ("debugXL", $subr_name, "$routine: first check for multiple occurrences");
      if (exists ($g_multi_count_function{$routine}))
        {
          $msg = "$g_multi_count_function{$routine} exists";
          gp_message ("debugXL", $subr_name, $msg);
          $msg  = "g_function_occurrences{$routine} = ";
          $msg .= $g_function_occurrences{$routine};
          gp_message ("debugXL", $subr_name, $msg);

          for my $ref (keys @{ $g_map_function_to_index{$routine} })
            {
              my $ref_index = $g_map_function_to_index{$routine}[$ref];
              my $addr_offset = $function_info[$ref_index]{"addressobjtext"};
#------------------------------------------------------------------------------
# The address has the following format: 6:0x0003af50, but we only need the
# part after the colon and remove the first part.
#------------------------------------------------------------------------------
              $addr_offset =~ s/$get_addr_offset_regex//;
 
              gp_message ("debugXL", $subr_name, "$routine: ref_index = $ref_index");
              gp_message ("debugXL", $subr_name, "$routine: function_info[$ref_index]{'alt_name'} = $function_info[$ref_index]{'alt_name'}");
              gp_message ("debugXL", $subr_name, "$routine: addr_offset = $addr_offset");

              if ($addr_offset eq $current_address)
#------------------------------------------------------------------------------
# There is a match and we can store the index.
#------------------------------------------------------------------------------
                {
                  $found_a_match = $TRUE;
                  push (@function_index_list, $ref_index);
                  last;
                }
            }
        }
      else
        {
#------------------------------------------------------------------------------
# This is the easy case.  There is only one index value.  We do check if the
# array element that contains it, exists.  If this is not the case, something
# has gone horribly wrong earlier and we need to bail out.
#------------------------------------------------------------------------------
          if (defined ($g_map_function_to_index{$routine}[0]))
            {
              $found_a_match = $TRUE;
              $ref_index = $g_map_function_to_index{$routine}[0];
              push (@function_index_list, $ref_index);
              my $final_function_name = $function_info[$ref_index]{"routine"};
              gp_message ("debugXL", $subr_name, "pushed single occurrence: ref_index = $ref_index final_function_name = $final_function_name");
            }
          }
      if (not $found_a_match)
#------------------------------------------------------------------------------
# This should not happen. All we can do is print an error message and stop.
#------------------------------------------------------------------------------
        {
          $msg  = "cannot find the index for $routine: found_a_match = ";
          $msg .= ($found_a_match == $TRUE) ? "TRUE" : "FALSE";
          gp_message ("assertion", $subr_name, $msg);
        }
    }

#------------------------------------------------------------------------------
# The loop over all function names has completed and @function_index_list
# contains the index values into @function_info for the functions.
#
# All we now need to do is to retrieve the correct field(s) from the array.
#------------------------------------------------------------------------------
  for my $i (keys @function_index_list)
    {
      my $index_for_function = $function_index_list[$i];
      push (@final_html_function_block, $function_info[$index_for_function]{"html function block"});
    }
  for my $i (keys @final_html_function_block)
    {
      my $txt = "final_html_function_block[$i] = $final_html_function_block[$i]";
      gp_message ("debugXL", $subr_name, $txt);
    }

#------------------------------------------------------------------------------
# Since the numbers are right aligned, we know that any difference between the
# metric line length and the maximum must be caused by the first column.  All
# we need to do is to prepend spaces in case of a difference.
#
# While we have the line with the metric values, we also replace ZZZ by 3
# spaces.
#------------------------------------------------------------------------------
    for my $i (keys @metric_values)
      {
        if (length ($metric_values[$i]) < $max_metrics_length)
          {
            my $pad = $max_metrics_length - length ($metric_values[$i]);
            my $spaces = "";
            for my $s (1 .. $pad)
              {
                $spaces .= "&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
      {
        $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)
    {
      $msg = "function_view_array[$i] = $function_view_array[$i]";
      gp_message ("debugXL", $subr_name, $msg);
    }
#------------------------------------------------------------------------------
# Element "function table" contains the array with all the function view data.
#------------------------------------------------------------------------------
  $function_view_structure{"header"}         = [@header_lines];
  $function_view_structure{"metrics part"}   = [@metrics_part];
  $function_view_structure{"function table"} = [@function_view_array];

  $msg = "leave subroutine " . $subr_name;
  gp_message ("debug", $subr_name, $msg);

  return (\%function_view_structure);

} #-- End of subroutine process_function_overview

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

  my ($input_string, $sort_fields_ref, $metric_description_ref, $ignored_metrics_ref) = @_;

  my @sort_fields        = @{ $sort_fields_ref };
  my %metric_description = %{ $metric_description_ref };
  my %ignored_metrics    = %{ $ignored_metrics_ref };

  my $outputdir = append_forward_slash ($input_string);
  my $LANG      = $g_locale_settings{"LANG"};
  my $max_len   = 0;
  my $metric_comment;

  my ($imetricn,$outfile);
  my ($html_metrics_record,$imetric,$metric);

  $html_metrics_record =
    "<!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 $msg;

  my $summary_metrics;
  my $detail_metrics;
  my $detail_metrics_system;
  my $call_metrics;

#------------------------------------------------------------------------------
# The regex section.
#------------------------------------------------------------------------------
  my $metrics_line_regex         = '\s*(.*):\s+(\d+\.?\d*)';
  my $metric_of_interest_1_regex = '^Exclusive\ *';
  my $metric_of_interest_2_regex = '^Inclusive\ *';

  if ($g_user_settings{"default_metrics"}{"current_value"} eq "off")
    {
      $msg  = "g_user_settings{default_metrics}{current_value} = ";
      $msg .= $g_user_settings{"default_metrics"}{"current_value"};
      gp_message ("debug", $subr_name, $msg);
  # get metrics

      $summary_metrics       = '';
      $detail_metrics        = '';
      $detail_metrics_system = '';
      $call_metrics          = '';
      $user_metrics          = 0;
      $system_metrics        = 0;
      $wall_metrics          = 0;

      my ($last_metric,$metric,$value,$i,$r);

      open (METRICTOTALS, "<", $outfile2)
        or die ("Unable to open metric value data file $outfile2 for reading - '$!'");
      gp_message ("debug", $subr_name, "opened $outfile2 to parse metric value data");

#------------------------------------------------------------------------------
# Below an example of the file that has just been opened.
#------------------------------------------------------------------------------
# <Total>
# 	            Exclusive Total CPU Time:      3.232 (100.0%)
# 	            Inclusive Total CPU Time:      3.232 (100.0%)
# 	              Exclusive insts Events: 7628146366 (100.0%)
# 	              Inclusive insts Events: 7628146366 (100.0%)
# 	             Exclusive cycles Events: 5167454376 (100.0%)
# 	             Inclusive cycles Events: 5167454376 (100.0%)
# 	   Exclusive dTLB-load-misses Events:          0 (  0. %)
# 	   Inclusive dTLB-load-misses Events:          0 (  0. %)
# 	    Exclusive Instructions Per Cycle:      1.476
# 	    Inclusive Instructions Per Cycle:      1.476
# 	    Exclusive Cycles Per Instruction:      0.677
# 	    Inclusive Cycles Per Instruction:      0.677
# 	Exclusive branch-instructions Events: 1268741580 (100.0%)
# 	Inclusive branch-instructions Events: 1268741580 (100.0%)
# 	                                Size:          0
# 	                          PC Address: 1:0x00000000
# 	                         Source File: (unknown)
# 	                         Object File: (unknown)
# 	                         Load Object: <Total>
# 	                        Mangled Name:
# 	                             Aliases:
#------------------------------------------------------------------------------

      while (<METRICTOTALS>)
        {
          $metricdata = $_; chomp ($metricdata);

          $msg = "file metrictotals: input line = " . $metricdata;
          gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
# 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 =~ /$metrics_line_regex/) 
            {
              $msg = "selected input line for processing"; 
              gp_message ("debug", $subr_name, $msg);

              if (defined($1) and defined($2))
                {
                  $metric = $1;
                  $value  = $2;
                  $msg = "metric = " . $metric; 
                  gp_message ("debug", $subr_name, $msg);
                  $msg = "value  = " . $value;
                  gp_message ("debug", $subr_name, $msg);
                }
              else
                {
                  $msg = "unexpected input in " . $metricdata;
                  gp_message ("assertion", $subr_name, $msg);
                }

#------------------------------------------------------------------------------
# Select the metrics of interest.
#------------------------------------------------------------------------------
              if (($metric =~ /$metric_of_interest_1_regex/) or
                  ($metric =~ /$metric_of_interest_2_regex/) )
                {
                  $msg  = "metric of interest = " . $metric;
                  $msg .= " - proceed with processing";
                  gp_message ("debug", $subr_name, $msg);
                }
              else
                {
                  $msg  = "metric = " . $metric;
                  $msg .= " - ignored and further processing is skipped";
                  gp_message ("debug", $subr_name, $msg);
                  next;
                }

#------------------------------------------------------------------------------
# When we get here, it means that this is a metric we want to process.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# TBD - Still needed? Don't see it in the input anymore (?)
#------------------------------------------------------------------------------
              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";
                  $msg = "last_metric = $last_metric metric = $metric";
                  gp_message ("debug", $subr_name, $msg);
                }

              $metric_value{$metric} = $value;
              $msg = "archived: metric_value{$metric} = " .
                     $metric_value{$metric};
              gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# Preserve the current metric.
#------------------------------------------------------------------------------
              $last_metric = $metric;
            }
        }
      close (METRICTOTALS);
    }

    if (scalar (keys %metric_value) == 0)
#------------------------------------------------------------------------------
# This means that there are no metrics in the input file.  That is a fatal
# error and execution is terminated.
#------------------------------------------------------------------------------
      {
        $msg = "no metrics have been found in the input file";
        gp_message ("assertion", $subr_name, $msg);
      }
    else
#------------------------------------------------------------------------------
# All is well.  Print the metrics that have been found.
#------------------------------------------------------------------------------
      {
        $msg = "stored the following metrics and values:";
        gp_message ("debug", $subr_name, $msg);
        for my $metric (sort keys %metric_value)
          {
            $msg = "metric_value{$metric} = " . $metric_value{$metric};
            gp_message ("debug", $subr_name, $msg);
          }
      }

  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")
        {
          $msg = "skipped processing line: " . $metric_line;
          gp_message ("debug", $subr_name, $msg);
          next
        }
      $msg = "line of interest: " . $metric_line;
      gp_message ("debug", $subr_name, $msg);

      $metric_found{$metric_spec} = $TRUE;

#------------------------------------------------------------------------------
# TBD
# Currently always FALSE since this feature has not been fully implemented yet.
#------------------------------------------------------------------------------
      if ($g_user_settings{"ignore_metrics"}{"defined"})
        {
          gp_message ("debug", $subr_name, "check for $metric_spec");
          if (exists ($ignored_metrics{$metric_name}))
            {
              $msg = "user asked to ignore metric " . $metric_name;
              gp_message ("debug", $subr_name, $msg);
              $msg = "further processing of line of interest is skipped";
              gp_message ("debug", $subr_name, $msg);
              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 +
      $msg  = "stored metric_description{$metric_spec} = ";
      $msg .= $metric_description{$metric_spec};
      gp_message ("debug", $subr_name, $msg);

      if ($metric_flavor ne "e")
        {
          $msg = "metric $metric_spec is ignored";
          gp_message ("debug", $subr_name, $msg);
          $msg = "further processing of this line is skipped";
          gp_message ("debug", $subr_name, $msg);
        }
      else
#------------------------------------------------------------------------------
# Only the exclusive metrics are shown.
#------------------------------------------------------------------------------
        {
          $msg = "metric $metric_spec ($metric_text) is considered";
          gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
# Legacy metrics, but may re-appear one day and so the code is left in here.
#------------------------------------------------------------------------------
          if ($metric_spec =~ /user/)
            {
              $user_metrics = $TRUE;
              $msg = "user_metrics set to TRUE";
              gp_message ("debug", $subr_name, $msg);
            }
          elsif ($metric_spec =~ /system/)
            {
              $system_metrics = $TRUE;
              $msg = "system_metrics set to TRUE";
              gp_message ("debug", $subr_name, $msg);
            }
          elsif ($metric_spec =~ /wall/)
            {
              $wall_metrics = $TRUE;
              $msg = "wall_metrics set to TRUE";
              gp_message ("debug", $subr_name, $msg);
            }
          elsif (defined ($metric_value{$metric_text}))
            {
              $msg  = "total attributed to this metric ";
              $msg .= "metric_value{" . $metric_text . "} = ";
              $msg .= $metric_value{$metric_text};
              gp_message ("debug", $subr_name, $msg);

              if ($summary_metrics ne '')
                {
                  $summary_metrics .= ':' . $metric_spec;
                  $msg = "updated summary_metrics = " . $summary_metrics;
                  gp_message ("debug", $subr_name, $msg);
                }
              else
                {
                  $summary_metrics = $metric_spec;
                  $msg = "initialized summary_metrics = " . $summary_metrics;
                  gp_message ("debug", $subr_name, $msg);
                }
              gp_message ("debug", $subr_name, "metric $metric_spec added");
            }
          else
            {
#------------------------------------------------------------------------------
# TBD: This doesn't seem to make much sense.
#------------------------------------------------------------------------------
              $msg = "no action taken for " . $metric_spec;
              gp_message ("debug", $subr_name, $msg);
            }
        }
    }

  close METRICS;

  if ($wall_metrics > 0)
    {
      $msg = "adding e.wall to summary_metrics";
      gp_message ("debug", $subr_name, $msg);
      $summary_metrics = "e.wall:".$summary_metrics;
      $msg = "after update summary_metrics = " . $summary_metrics;
      gp_message ("debug", $subr_name, $msg);
    }

  if ($system_metrics > 0)
    {
      $msg = "adding e.system to summary_metrics and detail_metrics_system";
      gp_message ("debug", $subr_name, $msg);

      $summary_metrics        = "e.system:" . $summary_metrics;
      $detail_metrics_system  = "e.system:" . $detail_metrics_system;

      $msg = "adding i.system to call_metrics";
      gp_message ("debug", $subr_name, $msg);

      $call_metrics = "i.system:" . $call_metrics;

      $msg = "after update summary_metrics       = " . $summary_metrics;
      gp_message ("debug", $subr_name, $msg);
      $msg = "after update call_metrics          = " . $call_metrics;
      gp_message ("debug", $subr_name, $msg);
      $msg = "after update detail_metrics_system = " . $detail_metrics_system;
      gp_message ("debug", $subr_name, $msg);
    }


#------------------------------------------------------------------------------
# TBD: e.user and i.user do not always exist!!
#------------------------------------------------------------------------------

  if ($user_metrics > 0)
    {
# 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;

      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;
        }
      $msg = "updated summary_metrics = " . $summary_metrics;
      gp_message ("debug", $subr_name, $msg);
      $msg = "updated detail_metrics        = " . $detail_metrics;
      gp_message ("debug", $subr_name, $msg);
      $msg = "updated detail_metrics_system = " . $detail_metrics_system;
      gp_message ("debug", $subr_name, $msg);
      $msg = "updated call_metrics          = " . $call_metrics;
      gp_message ("debug", $subr_name, $msg);

    }

#------------------------------------------------------------------------------
# TBD
# It doesn't look right in case call_metrics ends up being set to ""
#------------------------------------------------------------------------------
  if ($call_metrics eq "")
    {
      $call_metrics = $detail_metrics;
      $msg = "call_metrics is not set, setting it to " . $call_metrics;
      gp_message ("debug", $subr_name, $msg);
      if ($detail_metrics eq '')
        {
          $msg  = "detail_metrics and call_metrics are blank and could";
          $msg .= " cause trouble later on";
          gp_message ("debug", $subr_name, $msg);
        }
    }

  for my $metric (sort keys %ignored_metrics)
    {
      if ($ignored_metrics{$metric})
        {
          $msg = "active metric, but ignored: " . $metric;
          gp_message ("debug", $subr_name, $msg);
        }

    }

  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.
#
#
# TBD: Does not yet use the warnings/error system.  This needs to be fixed.
#------------------------------------------------------------------------------
sub process_rc_file
{
  my $subr_name = get_my_name ();

  my ($rc_file_name, $rc_file_paths_ref) = @_;

#------------------------------------------------------------------------------
# Local structures.
#------------------------------------------------------------------------------
# Stores the values extracted from the config file:
  my %rc_settings_user = ();
  my %error_and_warning_msgs = ();
  my @rc_file_paths = ();

  my @split_line;
  my @my_fields;

  my $msg;
  my $first_part;
  my $line;
  my $line_number;
  my $no_of_arguments;
  my $number_of_fields;
  my $number_of_paths;
  my $parse_errors;   #-- Count the number of errors
  my $parse_warnings; #-- Count the number of errors

  my $rc_config_file;
  my $rc_file_found;
  my $rc_keyword;
  my $rc_value;

  @rc_file_paths   = @{$rc_file_paths_ref};
  $number_of_paths = scalar (@rc_file_paths);

  if ($number_of_paths == 0)
#------------------------------------------------------------------------------
# This should not happen, but is a good safety net to add.
#------------------------------------------------------------------------------
    {
      my $msg = "search path list is empty";
      gp_message ("assertion", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Check for the presence of a configuration file.
#------------------------------------------------------------------------------
  $msg = "number_of_paths = $number_of_paths rc_file_paths = @rc_file_paths";
  gp_message ("debug", $subr_name, $msg);

  $rc_file_found = $FALSE;
  for my $path_name (@rc_file_paths)
    {
      $rc_config_file = $path_name . "/" . $rc_file_name;
      $msg = "looking for configuration file " . $rc_config_file;
      gp_message ("debug", $subr_name, $msg);
      if (-f $rc_config_file)
        {
          $msg = "found configuration file " . $rc_config_file;
          gp_message ("debug", $subr_name, $msg);
          $rc_file_found  = $TRUE;
          last;
        }
    }

  if (not $rc_file_found)
#------------------------------------------------------------------------------
# There is no configuration file and we can skip this subroutine.
#------------------------------------------------------------------------------
    {
      $msg = "configuration file $rc_file_name not found";
      gp_message ("verbose", $subr_name, $msg);
      return (0);
    }
  else
    {
      $msg = "unable to open file $rc_config_file for reading:";
      open (GP_DISPLAY_HTML_RC, "<", "$rc_config_file")
        or die ($subr_name . " - " . $msg . " " . $!);
#------------------------------------------------------------------------------
# The configuration file has been opened for reading.
#------------------------------------------------------------------------------
      $msg = "file $rc_config_file has been opened for reading";
      gp_message ("debug", $subr_name, $msg);
    }

  $msg = "found configuration file $rc_config_file";
  gp_message ("verbose", $subr_name, $msg);
  $msg = "processing configuration file " . $rc_config_file;
  gp_message ("debug", $subr_name, $msg);

#------------------------------------------------------------------------------
# Here we scan the configuration file for the settings.
#
# A setting consists of a keyword, optionally followed by a value.  It is
# optional because not all keywords may require a value.
#
# At the end of this block, all keyword/value pairs are stored in a hash.
#
# We do not yet check for the validity of these pairs. This is done next.
#
# The original code had this all integrated, but it made the code very
# complex with deeply nested if-statements. The flow was also hard to follow.
#------------------------------------------------------------------------------
  $parse_errors   = 0;
  $parse_warnings = 0;
  $line_number    = 0;
  while (my $line = <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++;
          $msg = "non-supported character(s) (\&,\^,\*,\@,\$) found: $line";
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
          next;
        }
      else
#------------------------------------------------------------------------------
# Split the first part on whitespace and verify the number of fields to be
# valid.  Although we currently only have keywords with a value, a keyword
# without value is supported to.
#
# If the number of fields is valid, the keyword and value are stored.  In case
# of a single field, the value is assigned a special string.
#
# Although this situation should not occur, we do abort if something unexpected
# is encountered here.
#------------------------------------------------------------------------------
        {
          @my_fields = split (/\s/, $split_line[0]);

          $number_of_fields = scalar (@my_fields);
          $msg = "number of fields = " . $number_of_fields;
          gp_message ("debug", $subr_name, $msg);
        }

      if ($number_of_fields ge 3)
#------------------------------------------------------------------------------
# This is not supported.
#------------------------------------------------------------------------------
        {
          $parse_errors++;
          $msg = "more than 2 fields found: $first_part";
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
          next;
        }
      elsif ($number_of_fields eq 2)
        {
          $rc_keyword = $my_fields[0];
          $rc_value   = $my_fields[1];
        }
      elsif ($number_of_fields eq 1)
        {
          $rc_keyword = $my_fields[0];
          $rc_value   = "the_field_is_empty";
        }
      else
        {
          $msg  = "[line $line_number] $rc_config_file -";
          $msg .= " number of fields = $number_of_fields";
          gp_message ("assertion", $subr_name, $msg);
        }

#------------------------------------------------------------------------------
# Store the keyword, value and line number.
#------------------------------------------------------------------------------
      if (exists ($rc_settings_user{$rc_keyword}))
        {
          $parse_warnings++;
          my $prev_value = $rc_settings_user{$rc_keyword}{"value"};
          my $prev_line_number = $rc_settings_user{$rc_keyword}{"line_no"};
          if ($rc_value ne $prev_value)
            {
              $msg  = "option $rc_keyword previously set at line";
              $msg .= " $prev_line_number: new value '$rc_value'";
              $msg .= " ' overrides '$prev_value'";
            }
          else
            {
              $msg  = "option $rc_keyword previously set to the same value";
              $msg .= " at line $prev_line_number";
            }
          $error_and_warning_msgs{"warning"}{$line_number}{"message"} = $msg;
        }
      $rc_settings_user{$rc_keyword}{"value"}   = $rc_value;
      $rc_settings_user{$rc_keyword}{"line_no"} = $line_number;

      gp_message ("debug", $subr_name, "stored keyword     = $rc_keyword");
      gp_message ("debug", $subr_name, "stored value       = $rc_value");
      gp_message ("debug", $subr_name, "stored line number = $line_number");
    }

#------------------------------------------------------------------------------
# Completed the parsing of the configuration file. It can be closed.
#------------------------------------------------------------------------------
  close (GP_DISPLAY_HTML_RC);

#------------------------------------------------------------------------------
# Print the raw input as just collected from the configuration file.
#------------------------------------------------------------------------------
  gp_message ("debug", $subr_name, "contents of %rc_settings_user:");
  for my $keyword (keys %rc_settings_user)
    {
      my $key_value = $rc_settings_user{$keyword}{"value"};
      $msg = "keyword = " . $keyword . " value = " . $key_value;
      gp_message ("debug", $subr_name, $msg);
    }

  for my $rc_keyword  (keys %g_user_settings)
    {
       for my $fields (keys %{ $g_user_settings{$rc_keyword} })
         {
           $msg  = "before config file: $rc_keyword $fields =";
           $msg .= " " . $g_user_settings{$rc_keyword}{$fields};
           gp_message ("debug", $subr_name, $msg);
         }
    }

#------------------------------------------------------------------------------
# We are almost done.  Check for all keywords found whether they are valid.
# Also verify that the corresponding value is valid.
#
# Update the g_user_settings table if everything is okay.
#------------------------------------------------------------------------------

  for my $rc_keyword (keys %rc_settings_user)
    {
      my $rc_value = $rc_settings_user{$rc_keyword}{"value"};

      if (exists ( $g_user_settings{$rc_keyword}))
        {

#------------------------------------------------------------------------------
# This is a supported keyword.  There are two more things left to do:
# - Check how many values it requires (currently exactly one is supported)
# - Is the value a valid number or string?
#------------------------------------------------------------------------------
          $no_of_arguments = $g_user_settings{$rc_keyword}{"no_of_arguments"};

          if ($no_of_arguments eq 1)
            {
              my $input_value = $rc_value;
              if ($input_value ne "the_field_is_empty")
#
#------------------------------------------------------------------------------
# So far, so good.  We only need to check if the value is valid for the keyword.
#------------------------------------------------------------------------------
                {
                  my $data_type   = $g_user_settings{$rc_keyword}{"data_type"};
                  my $valid_input =
			verify_if_input_is_valid ($input_value, $data_type);
#------------------------------------------------------------------------------
# Check if the value is valid.
#------------------------------------------------------------------------------
                  if ($valid_input)
                    {
                      $g_user_settings{$rc_keyword}{"current_value"} =
								$rc_value;
                      $g_user_settings{$rc_keyword}{"defined"}  = $TRUE;
                    }
                  else
                    {
                      $parse_errors++;
                      $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
                      $msg  = "input value '$input_value' for keyword";
                      $msg .= " $rc_keyword is not valid";
                      $error_and_warning_msgs{"error"}{$line_number}{"message"}
								= $msg;
                      next;
                    }
                }
              else
#------------------------------------------------------------------------------
# This keyword requires a value, but none has been found.
#------------------------------------------------------------------------------
                {
                  $parse_errors++;
                  $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
                  $msg = "missing value for keyword '$rc_keyword'";
                  $error_and_warning_msgs{"error"}{$line_number}{"message"}
								= $msg;
                  next;
                }
            }
          elsif ($no_of_arguments eq 0)
#------------------------------------------------------------------------------
# Currently a theoretical scenario since all commands require a value, but in
# case this is no longer true, we need to at least flag the fact the user set
# this command.
#------------------------------------------------------------------------------
            {
              $g_user_settings{$rc_keyword}{"defined"}  = $TRUE;
            }
          else
#------------------------------------------------------------------------------
# The code is not prepared for the situation one command has multiple values,
# but this situation should never occur. Still it won't hurt to add a check.
#------------------------------------------------------------------------------
            {
               my $msg = "cannot handle $no_of_arguments in the input";
               gp_message ("assertion", $subr_name, $msg);
            }
        }
      else
#------------------------------------------------------------------------------
# A non-valid keyword is found. This is flagged as an error.
#------------------------------------------------------------------------------
        {
          $parse_errors++;
          $line_number = $rc_settings_user{$rc_keyword}{"line_no"};
          $msg = "keyword $rc_keyword is not supported";
          $error_and_warning_msgs{"error"}{$line_number}{"message"} = $msg;
        }
    }
  for my $rc_keyword  (keys %g_user_settings)
    {
       for my $fields (keys %{ $g_user_settings{$rc_keyword} })
         {
           $msg  = "after config file: $rc_keyword $fields =";
           $msg .= " " . $g_user_settings{$rc_keyword}{$fields};
           gp_message ("debug", $subr_name, $msg);
         }
    }
  print_table_user_settings ("debug", "upon the return from $subr_name");

  if ( ($parse_errors == 0) and ($parse_warnings == 0) )
    {
      $msg = "successfully parsed and processed the configuration file";
      gp_message ("verbose", $subr_name, $msg);
    }
  else
    {
      if ($parse_errors > 0)
        {
          my $plural_or_single = ($parse_errors > 1) ? "errors" : "error";
          $msg  = $g_error_keyword . "found $parse_errors fatal";
          $msg .= " " .  $plural_or_single . " in the configuration file:";
          gp_message ("debug", $subr_name, $msg);
#------------------------------------------------------------------------------
# Sort the hash keys, the line numbers, alphabetically and print the
# corresponding error messages.
#------------------------------------------------------------------------------
          for my $line_no (sort {$a <=> $b}
				(keys %{ $error_and_warning_msgs{"error"} }))
            {
              $msg  = $g_error_keyword . "[line $line_no] in file";
              $msg .=  $rc_config_file . " - ";
              $msg .= $error_and_warning_msgs{"error"}{$line_no}{"message"};
              gp_message ("debug", $subr_name, $msg);
            }
        }

      if (not $g_quiet)
        {
          if ($parse_warnings > 0)
            {
              $msg  = $g_warn_keyword . " found $parse_warnings warnings in";
              $msg .= "  the configuration file:";
              gp_message ("debug", $subr_name, $msg);
              for my $line_no (sort {$a <=> $b}
				(keys %{ $error_and_warning_msgs{"warning"} }))
                {
                  $msg  = $g_warn_keyword;
                  $msg .= " [line $line_no] in file $rc_config_file - ";
                  $msg .= $error_and_warning_msgs{"warning"}{$line_no}{"message"};
                  gp_message ("debug", $subr_name, $msg);
                }
            }
        }
    }

  return ($parse_errors);

} #-- End of subroutine process_rc_file

#------------------------------------------------------------------------------
# Generate the annotated html file for the source listing.
#------------------------------------------------------------------------------
sub process_source
{
  my $subr_name = get_my_name ();

  my ($number_of_metrics, $function_info_ref,
      $outputdir, $input_filename) = @_;

  my @function_info = @{ $function_info_ref };

#------------------------------------------------------------------------------
# The regex section
#------------------------------------------------------------------------------
  my $end_src1_header_regex = '(^\s+)(\d+)\.\s+(.*)';
  my $end_src2_header_regex = '(^\s+)(<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;

      $msg = "target function $routine not found in $base - " .
             "links to source code involving this function will not work";
      gp_message ("debug", $subr_name, $msg);
      gp_message ("warning", $subr_name, $msg);
      $g_total_warning_count++;

      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";
      $threshold_line .= " (--highlight-percentage) option:";
      $threshold_line .= " " . $rounded_percentage . " (%)</i>";
    }
  else
    {
      $threshold_line  = "<i>The highlight percentage feature has not been";
      $threshold_line .= " 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 $abs_path_dir;
  my @candidate_ignored_metrics = ();
  my $error_code;
  my $hp_value;
  my $msg;

  my $outputdir;

  my $target_cmd;
  my $rm_output_msg;
  my $mkdir_output_msg;
  my $time_percentage_multiplier;
  my $process_all_functions;

#------------------------------------------------------------------------------
# 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)
    {
      $msg  = "the -o/--output and -O/--overwrite options are both set, " .
              "but are mutually exclusive";
      gp_message ("error", $subr_name, $msg);

      $msg  = "(setting for -o = $dir_o_option, " .
              "setting for -O = $dir_O_option)";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }

#------------------------------------------------------------------------------
# The warnings option is deprecated.  Print a warning to this extent and point
# to the --nowarnings option.
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
# Handle the situation that both or one of the highlight-percentage and hp
# options are set.
#------------------------------------------------------------------------------
  if ($g_user_settings{"warnings"}{"defined"})
    {
      $msg  = "<br>" . "the --warnings option has been deprecated and";
      $msg .= " will be ignored";
      gp_message ("warning", $subr_name, $msg);

      if ($g_user_settings{"nowarnings"}{"defined"})
        {
          $msg  = "since the --nowarnings option is also used, warnings";
          $msg .= " are disabled";
          gp_message ("warning", $subr_name, $msg);
        }
      else
        {
          $msg = "by default, warnings are enabled and can be disabled with";
          gp_message ("warning", $subr_name, $msg);
          $msg = " the --nowarnings option";
          gp_message ("warning", $subr_name, $msg);
        }
      $g_total_warning_count++;
    }

#------------------------------------------------------------------------------
# In case both the --highlight-percentage and -hp option are set, issue a
# warning and continue with the --highlight-percentage value.
#------------------------------------------------------------------------------
  if ($g_user_settings{"hp"}{"defined"})
    {
      $msg  = "<br>" . "the -hp option has been deprecated and";
      $msg .= " will be ignored";
      gp_message ("warning", $subr_name, $msg);

      if ($g_user_settings{"highlight_percentage"}{"defined"})
        {
          $msg  = "since the --highlight-percentage option is also used,";
          $msg .= " the value of ";
          $msg .= $g_user_settings{"highlight_percentage"}{"current_value"};
          $msg .= " will be applied";
          gp_message ("warning", $subr_name, $msg);
        }
      else
        {
#------------------------------------------------------------------------------
# If only the -hp option is set, we use it, because we do not want to break
# compatibility (yet) and force the user to change the option.
#------------------------------------------------------------------------------

## FUTURE          $msg  = "instead, the default setting of "
## FUTURE          $msg .= $g_user_settings{"highlight_percentage"}{"current_value"};
## FUTURE          $msg .= " for the --highlight-percentage will be used";
## FUTURE          gp_message ("warning", $subr_name, $msg);

## FUTURE          $msg = "please use this option to set the highlighting value";
## FUTURE          gp_message ("warning", $subr_name, $msg);

          $g_user_settings{"highlight_percentage"}{"current_value"} =
          $g_user_settings{"hp"}{"current_value"};

          $g_user_settings{"highlight_percentage"}{"defined"} = $TRUE;

          $msg = "for now, the value of " .
                 $g_user_settings{"hp"}{"current_value"} .
                 " for the -hp option is used, but please change the" .
                 " option to --highlight-percentage";
          gp_message ("warning", $subr_name, $msg);
        }

      $g_total_warning_count++;
    }

#------------------------------------------------------------------------------
# Regardless of the use of the -hp option, we continue with the value for
# highlight-percentage.  Some more checks are carried out now.
#------------------------------------------------------------------------------

#------------------------------------------------------------------------------
# This value should be in the interval [0,100].
# the number to be positive, but the limits have not been checked yet.
#------------------------------------------------------------------------------
  $hp_value = $g_user_settings{"highlight_percentage"}{"current_value"};

  if (($hp_value < 0) or ($hp_value > 100))
    {
      $msg  = "the value for the highlight percentage is set to $hp_value,";
      $msg .= " but must be in the range [0, 100]";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }
  elsif ($hp_value == 0.0)
#------------------------------------------------------------------------------
# A value of zero is interpreted to mean that highlighting should be disabled.
# To make the checks for this later on easier, set it to an integer value of 0.
#------------------------------------------------------------------------------
    {
      $g_user_settings{"highlight_percentage"}{"current_value"} = 0;

      $msg  = "reset the highlight percentage value from 0.0 to";
      $msg .= " " . $g_user_settings{"highlight_percentage"}{"current_value"};
      gp_message ("debug", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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))
    {
      $msg  = "the value for the total percentage is set to $tp_value,";
      $msg .=   " but must be in the range (0, 100]";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }
  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;
        }

      $msg = "value of time_percentage_multiplier = " .
             $time_percentage_multiplier;
      gp_message ("debugM", $subr_name, $msg);
      $msg = "value of process_all_functions      = " .
             ($process_all_functions ? "TRUE" : "FALSE");
      gp_message ("debugM", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# 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.
#------------------------------------------------------------------------------
  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 $msg = "ignored_metrics{$metric} = $ignored_metrics{$metric}";
      gp_message ("debugM", $subr_name, $msg);
    }

#------------------------------------------------------------------------------
# Check if the experiment directories exist and if they do, add the absolute
# path.  This is easier in the remainder.
#------------------------------------------------------------------------------
  for my $i (0 .. $#exp_dir_list)
    {
      if (-d $exp_dir_list[$i])
        {
          $abs_path_dir = Cwd::abs_path ($exp_dir_list[$i]);
          $exp_dir_list[$i] = $abs_path_dir;

          $msg = "directory $exp_dir_list[$i] exists";
          gp_message ("debugM", $subr_name, $msg);
        }
    }

  return (\%ignored_metrics, $outputdir, $time_percentage_multiplier,
	  $process_all_functions, \@exp_dir_list);

} #-- End of subroutine process_user_options

#------------------------------------------------------------------------------
# This function addresses a legacy issue.
#
# In binutils 2.40, the "gprofng display text" tool may add a string in the
# function overviews.  This did not add any value and was disruptive to the
# output.  It has been removed in 2.41, but in order to support the older
# versions of gprofng, the string is removed before the data is processed.
#
# Note: the double space in "--  no" is not a typo in this code!
#------------------------------------------------------------------------------
sub remove_redundant_string
{
  my $subr_name = get_my_name ();

  my ($target_array_ref) = @_;

  my @target_array = @{ $target_array_ref };

  my $msg;
  my $redundant_string = " --  no functions found";

  for (my $line = 0; $line <= $#target_array; $line++)
    {
      $target_array[$line] =~ s/$redundant_string//;
    }

  $msg = "removed any occurrence of " . $redundant_string;
  gp_message ("debugM", $subr_name, $msg);

  return (\@target_array);

} #-- End of subroutine remove_redundant_string

#------------------------------------------------------------------------------
# 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 = lc ($g_user_settings{"debug"}{"current_value"});

#------------------------------------------------------------------------------
# Set the corresponding sizes in the table.  A value of "on" is equivalent to
# size "s".
#------------------------------------------------------------------------------
  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            = $FALSE;
      $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 :-)
#------------------------------------------------------------------------------
  my $show_sizes = $FALSE;

  if ($show_sizes)
    {
      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 $file_is_empty;
  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")
    {
      $file_is_empty = is_file_empty ($filename);

      if ($file_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")
    {
      $msg = "unable to open disassembly file $filename for reading:";
      open ($DIS, "<", $filename)
        or die ($subr_name . " - " . $msg . " " . $!);
      gp_message ("debug", $subr_name, "opened file $filename for reading");

      $file_is_empty = is_file_empty ($filename);

      if ($file_is_empty)
#------------------------------------------------------------------------------
# Currently, the disassembly file for <static> functions appears to be empty
# on aarch64.  This might be a bug, but it is in any case better to handle
# this situation.
#------------------------------------------------------------------------------
        {
          $first_line = "";
          $msg = "file $filename is empty";
          gp_message ("debugM", $subr_name, $msg);
        }
      else
        {
          $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 to store the
# results in is created.
#------------------------------------------------------------------------------
sub set_up_output_directory
{
  my $subr_name = get_my_name ();

  my $error_code;
  my $msg;
  my $mkdir_output_msg;
  my $outputdir = "does_not_exist_yet";
  my $rm_output_msg;
  my $success;
  my $target_cmd;

  my $define_new_output_dir = $g_user_settings{"output"}{"defined"};
  my $overwrite_output_dir  = $g_user_settings{"overwrite"}{"defined"};

  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.
#------------------------------------------------------------------------------
  $msg = "the target output directory is $outputdir";
  gp_message ("debug", $subr_name, $msg);

  if (-d $outputdir)
    {
#------------------------------------------------------------------------------
# The -o option is used, but the directory already exists.
#------------------------------------------------------------------------------
      if ($define_new_output_dir)
        {
          $msg  = "directory $outputdir already exists";
          gp_message ("error", $subr_name, $msg);
          $msg  = "use the -O/--overwite  option to overwrite an";
          $msg .= " existing directory";
          gp_message ("error", $subr_name, $msg);

          $g_total_error_count++;

          gp_message ("abort", $subr_name, $g_abort_msg);

        }
      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 "*")
            {
              $msg = "it is not allowed to use * as a value for the -O option";
              gp_message ("error", $subr_name, $msg);

              $g_total_error_count++;

              gp_message ("abort", $subr_name, $g_abort_msg);
            }
          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)
                  {
                    $msg = "fatal error when trying to remove $outputdir";
                    gp_message ("error", $subr_name, $rm_output_msg);
                    gp_message ("error", $subr_name, $msg);

                    $g_total_error_count++;

                    gp_message ("abort", $subr_name, $g_abort_msg);
                  }
                else
                  {
                    $msg = "directory $outputdir has been removed";
                    gp_message ("debug", $subr_name, $msg);
                  }
            }
        }
    } #-- End of if-check for $outputdir

#------------------------------------------------------------------------------
# When we get here, the fatal scenarios have not occurred and the name for
# $outputdir is known.  Time to create it.  Note that recursive creation is
# supported and the user umask settings control 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)
    {
      $msg = "a fatal problem occurred when creating directory $outputdir";
      gp_message ("error", $subr_name, $mkdir_output_msg);
      gp_message  ("error", $subr_name, $msg);

      $g_total_error_count++;

      gp_message ("abort", $subr_name, $g_abort_msg);
    }
  else
    {
      $msg = "created output directory $outputdir";
      gp_message  ("debug", $subr_name, $msg);
    }

  return ($outputdir);

} #-- End of subroutine set_up_output_directory

#------------------------------------------------------------------------------
# Split a line with function data into 3 components.
#------------------------------------------------------------------------------
sub split_function_data_line
{
  my $subr_name = get_my_name ();

  my ($input_line_ref) = @_;

  my $input_line = ${ $input_line_ref };

  my $decimal_separator = $g_locale_settings{"decimal_separator"};
  my $full_hex_address;
  my $function_name;
  my $hex_address;
  my $length_metric_list;
  my $length_remainder;
  my $length_target_string;
  my $list_with_metrics;
  my $marker;
  my $msg;
  my $reduced_line;
  my $remainder;
 
  my @hex_addresses = ();
  my @special_marker = ();
  my @the_function_name = ();

  my $find_hex_address_regex = '\s*(\d+:0x[a-fA-F0-9]+)\s+(.*)';
  my $find_marker_regex = '(^\*).*';
  my $find_metrics_1_regex  = '\)*\ +([0-9,' . $decimal_separator;
     $find_metrics_1_regex .= '\ ]*$)';
  my $find_metrics_2_regex  = '\)*\ +\[.+\]\s+([0-9,' . $decimal_separator;
     $find_metrics_2_regex  = '\ ]*$)';
  my $get_hex_address_regex = '(\d+):0x(\S+)';

  $reduced_line = $input_line;

  if ($input_line =~ /$find_hex_address_regex/)
    {
      if (defined ($1) )
        {
          $full_hex_address = $1;
          $reduced_line =~ s/$full_hex_address//;

          $msg = "full_hex_address = " . $full_hex_address;
          gp_message ("debugXL", $subr_name, $msg);
          $msg = "reduced_line = " . $reduced_line;
          gp_message ("debugXL", $subr_name, $msg);
        }
      if (defined ($2) )
        {
          $remainder = $2;
          $msg = "remainder = " . $remainder;
          gp_message ("debugXL", $subr_name, $msg);

          if (($remainder =~ /$find_metrics_1_regex/) or
              ($remainder =~ /$find_metrics_2_regex/))
            {
              if (defined ($1))
                {
                  $list_with_metrics = $1;
                  $msg = "before list_with_metrics = " . $list_with_metrics;
                  gp_message ("debugXL", $subr_name, $msg);

                  $list_with_metrics =~ s/$g_rm_surrounding_spaces_regex//g;
                  $msg = "after list_with_metrics = " . $list_with_metrics;
                  gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# Remove the function name from the string.
#------------------------------------------------------------------------------
                  $length_remainder   = length ($remainder);
                  $length_metric_list = length ($list_with_metrics);

                  $msg = "length remainder = " . $length_remainder;
                  gp_message ("debugXL", $subr_name, $msg);

                  $msg = "length list_with_metrics = " . $length_metric_list;
                  gp_message ("debugXL", $subr_name, $msg);

                  $length_target_string = $length_remainder -
                                          $length_metric_list - 1;
                  $function_name = substr ($remainder, 0,
                                           $length_target_string, '');

                  $msg = "new function_name  = " . $function_name;
                  gp_message ("debugXL", $subr_name, $msg);

                  $reduced_line = $function_name;
                  $reduced_line =~ s/$g_rm_surrounding_spaces_regex//g;

                  $msg = "reduced_line = " . $reduced_line;
                  gp_message ("debugXL", $subr_name, $msg);

#------------------------------------------------------------------------------
# In some lines, the function name has a "*" prepended.  Isolate this marker
# and later on remove it from the function name.
# TBD: Can probably be done more efficiently.
#------------------------------------------------------------------------------
                  if ($reduced_line =~ /$find_marker_regex/)
                    {
                      if (defined ($1))
                        {
                          $marker = $1;
                          $msg = "found the marker = " . $marker;
                          gp_message ("debugXL", $subr_name, $msg);
                        }
                      else
                        {
                          $msg  = "first character in " . $reduced_line ;
                          $msg .= " is not expected";
                          gp_message ("assertion", $subr_name, $msg);
                        }
                    }
                  else
                    {
                          $marker = "X";
                    }
                }
              else
                {
                  $msg  = "failure to find metric values following the ";
                  $msg .= "function name";
                  gp_message ("assertion", $subr_name, $msg);
                }
            }
          else
            {
              $msg = "cannot find metric values in remainder";
              gp_message ("debugXL", $subr_name, $msg);
              gp_message ("assertion", $subr_name, $msg);
            }
        }
#------------------------------------------------------------------------------
# We now have the 3 main objects from the input line.  Next, they are processed
# and stored.
#------------------------------------------------------------------------------
      if ($full_hex_address =~ /$get_hex_address_regex/)
        {
          if (defined ($1) and defined ($2))
            {
              $hex_address = "0x" . $2;
              push (@hex_addresses, $full_hex_address);

              $msg = "pushed full_hex_address = " . $full_hex_address;
              gp_message ("debugXL", $subr_name, $msg);
            }
        }
      else
        {
          $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");
        }

      $reduced_line =~ s/^\*//;

      $msg = "RESULT full_hex_address = " . $full_hex_address;
      $msg .= " -- metric values = " . $list_with_metrics;
      $msg .= " -- marker = " . $marker;
      $msg .= " -- function name = " . $reduced_line;
      gp_message ("debugXL", $subr_name, $msg);
    }

  return (\$full_hex_address, \$marker, \$reduced_line, \$list_with_metrics);

} #-- End of subroutine split_function_data_line

#------------------------------------------------------------------------------
# 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 $msg;
  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})
    {
      $msg  = "target_name = $target_name is already defined: ";
      $msg .= $g_tagged_names{$target_name};
      gp_message ("debug", $subr_name, $msg);

      $msg = "target_name on return = $target_name";
      gp_message ("debug", $subr_name, $msg);

      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;

      $msg  = "target_name = $target_name is new and added: ";
      $msg .= "g_tagged_names{$target_name} = $g_tagged_names{$target_name}";
      gp_message ("debug", $subr_name, $msg);

      $msg = "target_name on return = $target_name";
      gp_message ("debug", $subr_name, $msg);

      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 $msg;
  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"};
          $msg = "ref_executable_name = " . $ref_executable_name;
          gp_message ("debug", $subr_name, $msg);
          next;
        }
        $full_path_executable_name =
			$g_exp_dir_meta_data{$exp_dir}{"full_path_exec"};
        $msg = "full_path_executable_name = " . $full_path_executable_name;
        gp_message ("debug", $subr_name, $msg);

        if ($full_path_executable_name ne $ref_executable_name)
          {
            $count_differences++;
            $msg  = $full_path_executable_name . " does not match";
            $msg .= " " . $ref_executable_name;
            gp_message ("debug", $subr_name, $msg);
          }
    }

  $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 $msg;
  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
    {
      $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

#------------------------------------------------------------------------------
# Scan the leftovers in ARGV.  Other than the option generated by the driver,
# this list should be empty.  Anything left here is considered to be a fatal
# error and pushed into the g_error_msgs buffer.
#
# We use two different arrays for the errors found.  This allows us to group
# the same type of errors.
#------------------------------------------------------------------------------
sub wrap_up_user_options
{
  my $subr_name = get_my_name ();

  my @opt_unsupported = ();
  my @opt_ignored     = ();

  my $current_option;
  my $driver_inserted = "--whoami=gprofng display html";
  my $ignore_option;
  my $msg;
  my $option_delimiter = "--";

  if (@ARGV)
    {
      $msg = "items in ARGV: " . join (" ", @ARGV);
      gp_message ("debugXL", $subr_name, $msg);

      $ignore_option = $FALSE;
      for my $i (keys @ARGV)
        {
          $current_option = $ARGV[$i];

          $msg = "ARGV[$i] = $current_option";

          if ($current_option eq $option_delimiter)
#------------------------------------------------------------------------------
# The user may use a feature of GetOptions to delimit the options.  After
# this, only experiment names are allowed and these have been handled already,
# so anything found after this delimite is an error.
#
# This is why we set a flag if the delimiter has been found.
#------------------------------------------------------------------------------
            {
              $ignore_option = $TRUE;
              gp_message ("debugXL", $subr_name, $msg . " (option delimiter)");
            }
          elsif ($ignore_option)
#------------------------------------------------------------------------------
# We have seen the delimiter, but there are still options, or other strings.
# In any case, it is not allowed.
#------------------------------------------------------------------------------
            {
              push (@opt_ignored, $current_option);
              gp_message ("debugXL", $subr_name, $msg . " (ignored)");
            }
          elsif ($current_option ne $driver_inserted)
#------------------------------------------------------------------------------
# The gprofng driver inserts this and it should be ignored.  This is why we
# only recorded those options different than the one inserted by the driver.
#------------------------------------------------------------------------------
            {
              push (@opt_unsupported, $current_option);
              gp_message ("debugXL", $subr_name, $msg . " (unsupported)");
            }
          else
#------------------------------------------------------------------------------
# The gprofng driver inserts this option and it should be ignored.
#------------------------------------------------------------------------------
            {
              gp_message ("debugXL", $subr_name, $msg .
                          " (driver inserted and ignored)");
            }
        }
    }

#------------------------------------------------------------------------------
# Store any illegal input in the g_error_msgs buffer.
#------------------------------------------------------------------------------
  if (@opt_ignored)
    {
      $msg = "the following input is out of place:";
      for my $i (keys @opt_ignored)
        {
          $msg .= " " . $opt_ignored[$i];
        }
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }
  if (@opt_unsupported)
    {
      $msg = "the following items in the input are not supported:";
      for my $i (keys @opt_unsupported)
        {
          $msg .= " " . $opt_unsupported[$i];
        }
      gp_message ("error", $subr_name, $msg);

      $msg  = "perhaps an error in the option name, or an option value";
      $msg .= " is missing?";
      gp_message ("error", $subr_name, $msg);

      $g_total_error_count++;
    }

  return (0);

} #-- End of subroutine wrap_up_user_options

Filemanager

Name Type Size Permission Actions
X11 Folder 0755
GET File 15.87 KB 0755
HEAD File 15.87 KB 0755
POST File 15.87 KB 0755
X File 274 B 0755
Xephyr File 2.67 MB 0755
Xorg File 274 B 0755
Xwayland File 2.76 MB 0755
[ File 46.51 KB 0755
aa-enabled File 18.38 KB 0755
aa-exec File 18.38 KB 0755
aa-features-abi File 18.38 KB 0755
ab File 58.51 KB 0755
aconnect File 22.45 KB 0755
acpidbg File 1.58 KB 0755
add-apt-repository File 24.42 KB 0755
addr2line File 30.78 KB 0755
airscan-discover File 154.93 KB 0755
alsabat File 50.52 KB 0755
alsaloop File 91.41 KB 0755
alsamixer File 100.37 KB 0755
alsatplg File 86.45 KB 0755
alsaucm File 34.91 KB 0755
amidi File 30.46 KB 0755
amixer File 66.53 KB 0755
apg File 274 B 0755
apgbfm File 26.38 KB 0755
aplay File 86.5 KB 0755
aplaymidi File 26.46 KB 0755
aplaymidi2 File 22.47 KB 0755
apport-bug File 2.27 KB 0755
apport-cli File 13.56 KB 0755
apport-collect File 2.27 KB 0755
apport-unpack File 3.7 KB 0755
appres File 14.38 KB 0755
appstreamcli File 146.3 KB 0755
apropos File 47.36 KB 0755
apt File 18.46 KB 0755
apt-add-repository File 24.42 KB 0755
apt-cache File 90.54 KB 0755
apt-cdrom File 30.54 KB 0755
apt-config File 30.47 KB 0755
apt-extracttemplates File 26.53 KB 0755
apt-ftparchive File 246.55 KB 0755
apt-get File 58.54 KB 0755
apt-mark File 70.54 KB 0755
apt-sortpkgs File 42.47 KB 0755
aptdcon File 1.01 KB 0755
ar File 54.56 KB 0755
arch File 34.59 KB 0755
arecord File 86.5 KB 0755
arecordmidi File 34.47 KB 0755
arecordmidi2 File 26.48 KB 0755
arm2hpdl File 14.31 KB 0755
as File 795.52 KB 0755
aseqdump File 34.45 KB 0755
aseqnet File 22.51 KB 0755
aseqsend File 22.46 KB 0755
aspell File 162.55 KB 0755
aspell-import File 2 KB 0755
atobm File 14.3 KB 0755
awk File 190.84 KB 0755
axfer File 90.45 KB 0755
b2sum File 54.59 KB 0755
baobab File 302.52 KB 0755
base32 File 42.59 KB 0755
base64 File 42.59 KB 0755
basename File 34.59 KB 0755
basenc File 50.59 KB 0755
bash File 1.66 MB 0755
bashbug File 6.86 KB 0755
bc File 90.82 KB 0755
bdftopcf File 42.56 KB 0755
bdftruncate File 14.38 KB 0755
bitmap File 106.31 KB 0755
bluemoon File 38.45 KB 0755
bluetooth-sendto File 30.48 KB 0755
bluetoothctl File 574.27 KB 0755
bmtoa File 14.32 KB 0755
boltctl File 122.84 KB 0755
bpftrace File 4.57 MB 0755
bpftrace-aotrt File 3.2 MB 0755
brltty File 1.13 MB 0755
brltty-atb File 218.63 KB 0755
brltty-clip File 214.57 KB 0755
brltty-ctb File 330.8 KB 0755
brltty-hid File 274.84 KB 0755
brltty-ktb File 603.28 KB 0755
brltty-lscmds File 250.58 KB 0755
brltty-morse File 286.64 KB 0755
brltty-trtxt File 274.7 KB 0755
brltty-ttb File 318.88 KB 0755
brltty-tune File 302.7 KB 0755
broadwayd File 126.46 KB 0755
browse File 31.53 KB 0755
btattach File 30.45 KB 0755
btmgmt File 186.56 KB 0755
btmon File 1.16 MB 0755
btrfs File 1.49 MB 0755
btrfs-convert File 884.63 KB 0755
btrfs-find-root File 796.63 KB 0755
btrfs-image File 840.63 KB 0755
btrfs-map-logical File 796.63 KB 0755
btrfs-select-super File 792.63 KB 0755
btrfsck File 1.49 MB 0755
btrfstune File 832.63 KB 0755
bunzip2 File 38.45 KB 0755
busctl File 102.6 KB 0755
busybox File 2.34 MB 0755
bwrap File 82.54 KB 0755
bzcat File 38.45 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 38.45 KB 0755
bzip2recover File 18.38 KB 0755
bzless File 1.27 KB 0755
bzmore File 1.27 KB 0755
c++filt File 26.34 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.67 KB 0755
calibrate_ppa File 26.38 KB 0755
canberra-gtk-play File 18.3 KB 0755
cancel File 18.38 KB 0755
captoinfo File 94.49 KB 0755
cat File 42.54 KB 0755
catman File 30.84 KB 0755
cc File 1.13 MB 0755
cd-create-profile File 26.38 KB 0755
cd-fix-profile File 30.38 KB 0755
cd-iccdump File 14.38 KB 0755
cd-it8 File 26.38 KB 0755
certutil File 186.93 KB 0755
cgi-fcgi File 18.23 KB 0755
chacl File 18.3 KB 0755
chage File 83.23 KB 2755
chardet File 221 B 0755
chardetect File 221 B 0755
chattr File 14.38 KB 0755
chcon File 66.59 KB 0755
check-language-support File 2.71 KB 0755
checkgid File 14.38 KB 0755
chfn File 71.16 KB 4755
chgrp File 66.59 KB 0755
chktest File 14.38 KB 0755
chmod File 62.59 KB 0755
choom File 22.45 KB 0755
chown File 66.59 KB 0755
chrt File 30.45 KB 0755
chsh File 47.79 KB 4755
chvt File 14.45 KB 0755
cifsiostat File 26.55 KB 0755
ciptool File 38.56 KB 0755
ckbcomp File 147.14 KB 0755
cksum File 106.6 KB 0755
clear File 14.38 KB 0755
clear_console File 14.3 KB 0755
cloud-id File 972 B 0755
cloud-init File 976 B 0755
cloud-init-per File 2.06 KB 0755
cmp File 50.47 KB 0755
cmsutil File 46.47 KB 0755
codepage File 14.37 KB 0755
col File 22.46 KB 0755
colcrt File 14.46 KB 0755
colormgr File 58.45 KB 0755
colrm File 14.46 KB 0755
column File 38.46 KB 0755
comm File 38.59 KB 0755
corelist File 15.01 KB 0755
cp File 142.59 KB 0755
cpan File 8.16 KB 0755
cpan5.40-x86_64-linux-gnu File 8.18 KB 0755
cpio File 142.02 KB 0755
cpp File 1.13 MB 0755
cpp-14 File 1.13 MB 0755
cpupower File 1.58 KB 0755
crash File 13.91 MB 0755
crlutil File 98.48 KB 0755
crontab File 42.81 KB 2755
csplit File 54.59 KB 0755
ctstat File 22.73 KB 0755
cupstestppd File 74.45 KB 0755
curl File 310.48 KB 0755
cut File 42.59 KB 0755
cvt File 14.23 KB 0755
cvtsudoers File 377.3 KB 0755
dash File 146.84 KB 0755
date File 106.59 KB 0755
dbus-cleanup-sockets File 14.37 KB 0755
dbus-daemon File 242.72 KB 0755
dbus-monitor File 38.37 KB 0755
dbus-run-session File 14.37 KB 0755
dbus-send File 42.37 KB 0755
dbus-update-activation-environment File 18.37 KB 0755
dbus-uuidgen File 14.37 KB 0755
dbxtool File 22.45 KB 0755
dc File 50.24 KB 0755
dconf File 62.3 KB 0755
dd File 66.62 KB 0755
ddstdecode File 18.31 KB 0755
deallocvt File 14.45 KB 0755
deb-systemd-helper File 23.79 KB 0755
deb-systemd-invoke File 6.97 KB 0755
debconf File 2.8 KB 0755
debconf-apt-progress File 11.57 KB 0755
debconf-communicate File 623 B 0755
debconf-copydb File 1.68 KB 0755
debconf-escape File 668 B 0755
debconf-set-selections File 3.14 KB 0755
debconf-show File 1.78 KB 0755
debian-distro-info File 31.03 KB 0755
deja-dup File 402.59 KB 0755
delv File 61.4 KB 0755
derdump File 30.46 KB 0755
desktop-file-edit File 96.57 KB 0755
desktop-file-install File 96.57 KB 0755
desktop-file-validate File 80.85 KB 0755
df File 79.06 KB 0755
dh_bash-completion File 4.42 KB 0755
dh_installxmlcatalogs File 9.22 KB 0755
dh_perl_openssl File 1.53 KB 0755
diff File 154.63 KB 0755
diff3 File 66.59 KB 0755
dig File 154.95 KB 0755
dir File 155.02 KB 0755
dircolors File 46.59 KB 0755
dirmngr File 545.84 KB 0755
dirmngr-client File 55 KB 0755
dirname File 34.46 KB 0755
distro-info File 26.97 KB 0755
dmesg File 80.78 KB 0755
dnsdomainname File 22.3 KB 0755
do-release-upgrade File 9.05 KB 0755
domainname File 22.3 KB 0755
dpkg File 371.2 KB 0755
dpkg-deb File 162.66 KB 0755
dpkg-divert File 134.82 KB 0755
dpkg-maintscript-helper File 20.63 KB 0755
dpkg-query File 158.84 KB 0755
dpkg-realpath File 38.45 KB 0755
dpkg-split File 110.61 KB 0755
dpkg-statoverride File 54.63 KB 0755
dpkg-trigger File 46.61 KB 0755
driverless File 30.47 KB 0755
driverless-fax File 591 B 0755
du File 106.6 KB 0755
dumpkeys File 162.93 KB 0755
duplicity File 968 B 0755
dvipdf File 1007 B 0755
eatmydata File 2.74 KB 0755
ec2metadata File 8.38 KB 0755
echo File 34.46 KB 0755
ed File 70.63 KB 0755
editor File 328.65 KB 0755
editres File 72.77 KB 0755
efibootdump File 22.38 KB 0755
efibootmgr File 47.77 KB 0755
egrep File 41 B 0755
eject File 42.3 KB 0755
elfedit File 34.79 KB 0755
enc2xs File 40.97 KB 0755
encguess File 2.99 KB 0755
enchant-2 File 22.38 KB 0755
enchant-lsmod-2 File 14.38 KB 0755
env File 50.99 KB 0755
envsubst File 38.46 KB 0755
eog File 14.45 KB 0755
eps2eps File 639 B 0755
eqn File 204.52 KB 0755
esc-m File 14.16 KB 0755
eutp File 26.23 KB 0755
ex File 2.16 MB 0755
expand File 38.61 KB 0755
expiry File 22.63 KB 2755
expr File 46.49 KB 0755
factor File 70.59 KB 0755
fallocate File 26.45 KB 0755
false File 34.46 KB 0755
fc-cache File 22.45 KB 0755
fc-cat File 18.45 KB 0755
fc-conflist File 14.45 KB 0755
fc-list File 14.45 KB 0755
fc-match File 14.45 KB 0755
fc-pattern File 14.45 KB 0755
fc-query File 14.45 KB 0755
fc-scan File 14.45 KB 0755
fc-validate File 14.45 KB 0755
fcgistarter File 14.38 KB 0755
fgconsole File 14.45 KB 0755
fgrep File 41 B 0755
file File 30.6 KB 0755
file-roller File 562.61 KB 0755
file2brl File 26.3 KB 0755
find File 207.55 KB 0755
findmnt File 75.92 KB 0755
firefox File 2.32 KB 0755
flock File 26.56 KB 0755
fmt File 42.59 KB 0755
fold File 38.59 KB 0755
fonttosfnt File 46.48 KB 0755
foo2ddst File 34.96 KB 0755
foo2ddst-wrapper File 16.86 KB 0755
foo2hbpl2 File 30.98 KB 0755
foo2hbpl2-wrapper File 17.91 KB 0755
foo2hiperc File 42.99 KB 0755
foo2hiperc-wrapper File 18.11 KB 0755
foo2hp File 42.96 KB 0755
foo2hp2600-wrapper File 18.75 KB 0755
foo2lava File 42.99 KB 0755
foo2lava-wrapper File 19.61 KB 0755
foo2oak File 34.9 KB 0755
foo2oak-wrapper File 17.45 KB 0755
foo2qpdl File 43.02 KB 0755
foo2qpdl-wrapper File 19.06 KB 0755
foo2slx File 30.99 KB 0755
foo2slx-wrapper File 17.19 KB 0755
foo2xqx File 34.99 KB 0755
foo2xqx-wrapper File 17.16 KB 0755
foo2zjs File 43 KB 0755
foo2zjs-icc2ps File 14.39 KB 0755
foo2zjs-pstops File 2.93 KB 0755
foo2zjs-wrapper File 25.34 KB 0755
foomatic-rip File 115.48 KB 0755
fprintd-delete File 94.45 KB 0755
fprintd-enroll File 94.94 KB 0755
fprintd-list File 90.45 KB 0755
fprintd-verify File 90.45 KB 0755
free File 26.45 KB 0755
ftp File 182.9 KB 0755
funzip File 26.45 KB 0755
fuser File 43.42 KB 0755
fusermount File 38.45 KB 4755
fusermount3 File 38.45 KB 4755
fwupdmgr File 122.38 KB 0755
fwupdtool File 130.38 KB 0755
gamemoded File 166.73 KB 0755
gamma4scanimage File 14.38 KB 0755
gapplication File 22.46 KB 0755
gatttool File 126.56 KB 0755
gcalccmd File 286.46 KB 0755
gcc File 1.13 MB 0755
gcc-14 File 1.13 MB 0755
gcc-ar File 30.66 KB 0755
gcc-ar-14 File 30.66 KB 0755
gcc-nm File 30.66 KB 0755
gcc-nm-14 File 30.66 KB 0755
gcc-ranlib File 30.66 KB 0755
gcc-ranlib-14 File 30.66 KB 0755
gcore File 3.62 KB 0755
gcov File 468.19 KB 0755
gcov-14 File 468.19 KB 0755
gcov-dump File 380.14 KB 0755
gcov-dump-14 File 380.14 KB 0755
gcov-tool File 408.23 KB 0755
gcov-tool-14 File 408.23 KB 0755
gcr-viewer File 14.37 KB 0755
gcr-viewer-gtk4 File 34.45 KB 0755
gdb File 11.23 MB 0755
gdb-add-index File 4.55 KB 0755
gdbtui File 126 B 0755
gdbus File 54.46 KB 0755
gdctl File 51.15 KB 0755
gdk-pixbuf-csource File 14.4 KB 0755
gdk-pixbuf-pixdata File 14.38 KB 0755
gdk-pixbuf-thumbnailer File 18.47 KB 0755
gdm-config File 50.75 KB 0755
gdmflexiserver File 22.94 KB 0755
gencat File 34.52 KB 0755
geqn File 204.52 KB 0755
getconf File 26.44 KB 0755
getent File 38.8 KB 0755
getfacl File 30.38 KB 0755
getkeycodes File 14.45 KB 0755
getopt File 22.45 KB 0755
gettext File 38.46 KB 0755
gettext.sh File 5.05 KB 0755
ghostscript File 14.23 KB 0755
ginstall-info File 47.31 KB 0755
gio File 110.48 KB 0755
gio-querymodules File 18.38 KB 0755
gipddecode File 18.31 KB 0755
gjs File 22.71 KB 0755
gjs-console File 22.71 KB 0755
glib-compile-schemas File 54.46 KB 0755
gmake File 344.14 KB 0755
gnome-calculator File 822.91 KB 0755
gnome-calendar File 884.62 KB 0755
gnome-characters File 253 B 0755
gnome-clocks File 458.65 KB 0755
gnome-control-center File 4.28 MB 0755
gnome-disk-image-mounter File 22.46 KB 0755
gnome-disks File 687.57 KB 0755
gnome-extensions File 78.53 KB 0755
gnome-font-viewer File 82.73 KB 0755
gnome-help File 58.3 KB 0755
gnome-keyring File 22.62 KB 0755
gnome-keyring-3 File 22.62 KB 0755
gnome-keyring-daemon File 1.07 MB 0755
gnome-language-selector File 1.41 KB 0755
gnome-logs File 170.88 KB 0755
gnome-power-statistics File 66.43 KB 0755
gnome-session File 958 B 0755
gnome-session-inhibit File 22.38 KB 0755
gnome-session-properties File 66.41 KB 0755
gnome-session-quit File 14.68 KB 0755
gnome-shell File 30.8 KB 0755
gnome-shell-extension-tool File 1.67 KB 0755
gnome-shell-test-tool File 11.12 KB 0755
gnome-system-monitor File 427.49 KB 0755
gnome-terminal File 91.78 KB 0755
gnome-terminal.wrapper File 6.06 KB 0755
gnome-text-editor File 654.7 KB 0755
gnome-thumbnail-font File 26.47 KB 0755
gnome-www-browser File 2.32 KB 0755
gp-archive File 34.59 KB 0755
gp-collect-app File 54.42 KB 0755
gp-display-html File 630.35 KB 0755
gp-display-src File 30.41 KB 0755
gp-display-text File 166.42 KB 0755
gpasswd File 78.54 KB 4755
gpg File 1.3 MB 0755
gpg-agent File 397.59 KB 0755
gpg-connect-agent File 87.38 KB 0755
gpg-wks-client File 147.44 KB 0755
gpgconf File 119.44 KB 0755
gpgparsemail File 34.38 KB 0755
gpgsm File 577.44 KB 0755
gpgsplit File 26.62 KB 0755
gpgtar File 75.91 KB 0755
gpgv File 355.22 KB 0755
gpic File 228.12 KB 0755
gprof File 99.86 KB 0755
gprofng File 22.41 KB 0755
gprofng-archive File 34.59 KB 0755
gprofng-collect-app File 54.42 KB 0755
gprofng-display-html File 630.35 KB 0755
gprofng-display-src File 30.41 KB 0755
gprofng-display-text File 166.42 KB 0755
gpu-manager File 66.9 KB 0755
grdctl File 74.46 KB 0755
grep File 182.45 KB 0755
gresource File 26.38 KB 0755
groff File 102.58 KB 0755
grog File 18.75 KB 0755
grops File 202.62 KB 0755
grotty File 130.58 KB 0755
groups File 38.59 KB 0755
growpart File 29.19 KB 0755
grub-editenv File 401.24 KB 0755
grub-file File 749.96 KB 0755
grub-fstest File 871.4 KB 0755
grub-glue-efi File 102.96 KB 0755
grub-kbdcomp File 1.64 KB 0755
grub-menulst2cfg File 87.27 KB 0755
grub-mkfont File 131.52 KB 0755
grub-mkimage File 381.34 KB 0755
grub-mklayout File 107.3 KB 0755
grub-mknetdir File 437.85 KB 0755
grub-mkpasswd-pbkdf2 File 115.4 KB 0755
grub-mkrelpath File 259.87 KB 0755
grub-mkrescue File 1.01 MB 0755
grub-mkstandalone File 522.24 KB 0755
grub-mount File 694.29 KB 0755
grub-render-label File 766.3 KB 0755
grub-script-check File 126.84 KB 0755
grub-syslinux2cfg File 706.79 KB 0755
gs File 14.23 KB 0755
gsbj File 350 B 0755
gsdj File 352 B 0755
gsdj500 File 352 B 0755
gsettings File 30.38 KB 0755
gslj File 353 B 0755
gslp File 350 B 0755
gsnd File 277 B 0755
gst-device-monitor-1.0 File 22.4 KB 0755
gst-discoverer-1.0 File 38.48 KB 0755
gst-inspect-1.0 File 66.55 KB 0755
gst-launch-1.0 File 38.48 KB 0755
gst-play-1.0 File 54.48 KB 0755
gst-stats-1.0 File 34.46 KB 0755
gst-tester-1.0 File 18.38 KB 0755
gst-typefind-1.0 File 18.46 KB 0755
gstack File 2.98 KB 0755
gstreamer-codec-install File 22.23 KB 0755
gtbl File 154.55 KB 0755
gted File 654.7 KB 0755
gtf File 18.38 KB 0755
gtk-builder-tool File 34.8 KB 0755
gtk-encode-symbolic-svg File 22.48 KB 0755
gtk-launch File 18.53 KB 0755
gtk-query-settings File 14.38 KB 0755
gtk-update-icon-cache File 42.65 KB 0755
gtk4-broadwayd File 150.46 KB 0755
gtk4-builder-tool File 82.79 KB 0755
gtk4-encode-symbolic-svg File 11.71 MB 0755
gtk4-image-tool File 38.55 KB 0755
gtk4-launch File 18.53 KB 0755
gtk4-path-tool File 50.45 KB 0755
gtk4-query-settings File 14.38 KB 0755
gtk4-rendernode-tool File 46.45 KB 0755
gtk4-update-icon-cache File 42.65 KB 0755
gunzip File 2.28 KB 0755
gzexe File 6.29 KB 0755
gzip File 123.32 KB 0755
h2ph File 28.15 KB 0755
h2xs File 59.51 KB 0755
hardlink File 46.56 KB 0755
hbpldecode File 30.39 KB 0755
hciattach File 60.53 KB 0755
hciconfig File 158.56 KB 0755
hcitool File 166.16 KB 0755
hd File 54.47 KB 0755
head File 46.59 KB 0755
heif-thumbnailer File 34.4 KB 0755
helpztags File 2.46 KB 0755
hex2hcd File 18.45 KB 0755
hexdump File 54.47 KB 0755
hipercdecode File 18.31 KB 0755
host File 118.97 KB 0755
hostid File 34.59 KB 0755
hostname File 22.3 KB 0755
hostnamectl File 34.46 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-plugin-ubuntu File 719 B 0755
hp-probe File 7.98 KB 0755
hp-query File 4.94 KB 0755
hp-scan File 88.25 KB 0755
hp-setup File 37.26 KB 0755
hp-testpage File 5.98 KB 0755
hp-timedate File 3.31 KB 0755
htcacheclean File 38.39 KB 0755
htdbm File 26.38 KB 0755
htdigest File 14.38 KB 0755
htpasswd File 30.38 KB 0755
httpserv File 38.4 KB 0755
hwe-support-status File 11.24 KB 0755
i386 File 26.73 KB 0755
ibd2sdi File 278.98 KB 0755
ibus File 86.45 KB 0755
ibus-daemon File 230.5 KB 0755
ibus-setup File 1.15 KB 0755
ibus-table-createdb File 1.11 KB 0755
iceauth File 42.5 KB 0755
ico File 50.44 KB 0755
iconv File 66.59 KB 0755
id File 42.59 KB 0755
iecset File 26.45 KB 0755
ijs_pxljr File 34.53 KB 0755
im-config File 11.03 KB 0755
im-launch File 2.07 KB 0755
inetutils-telnet File 221.9 KB 0755
info File 245.8 KB 0755
infobrowser File 245.8 KB 0755
infocmp File 70.45 KB 0755
infotocap File 94.49 KB 0755
innochecksum File 179.63 KB 0755
inputattach File 33.75 KB 0755
install File 142.59 KB 0755
install-info File 47.31 KB 0755
instmodsh File 4.27 KB 0755
intel-virtual-output File 66.31 KB 0755
ionice File 18.45 KB 0755
iostat File 58.55 KB 0755
ip File 904.97 KB 0755
ipcmk File 22.52 KB 0755
ipcrm File 18.45 KB 0755
ipcs File 38.45 KB 0755
ipod-read-sysinfo-extended File 22.38 KB 0755
ipod-time-sync File 14.38 KB 0755
ippfind File 46.48 KB 0755
ipptool File 106.38 KB 0755
iptables-xml File 105.02 KB 0755
ischroot File 14.55 KB 0755
isdv4-serial-debugger File 18.31 KB 0755
isdv4-serial-inputattach File 18.31 KB 0755
ispell-wrapper File 7.05 KB 0755
join File 54.63 KB 0755
journalctl File 91.19 KB 0755
jpgicc File 38.47 KB 0755
jq File 34.23 KB 0755
json-patch-jsondiff File 1004 B 0755
json_pp File 4.9 KB 0755
jsondiff File 1004 B 0755
jsonpatch File 3.77 KB 0755
jsonpointer File 1.79 KB 0755
jsonschema File 213 B 0755
kbd_mode File 14.74 KB 0755
kbdinfo File 18.45 KB 0755
kbxutil File 70.91 KB 0755
kernel-install File 54.71 KB 0755
kill File 22.45 KB 0755
killall File 31.42 KB 0755
kmod File 194.31 KB 0755
kmodsign File 18.45 KB 0755
l2ping File 18.38 KB 0755
l2test File 34.72 KB 0755
laptop-detect File 3.74 KB 0755
lavadecode File 22.39 KB 0755
ld File 1.78 MB 0755
ld.bfd File 1.78 MB 0755
ld.so File 245.65 KB 0755
ldapadd File 66.53 KB 0755
ldapcompare File 66.53 KB 0755
ldapdelete File 66.55 KB 0755
ldapexop File 66.53 KB 0755
ldapmodify File 66.53 KB 0755
ldapmodrdn File 62.53 KB 0755
ldappasswd File 66.53 KB 0755
ldapsearch File 102.55 KB 0755
ldapurl File 14.38 KB 0755
ldapwhoami File 62.53 KB 0755
ldd File 5.26 KB 0755
less File 216.21 KB 0755
lessecho File 14.38 KB 0755
lessfile File 8.83 KB 0755
lesskey File 23.79 KB 0755
lesspipe File 8.83 KB 0755
lexgrog File 111.59 KB 0755
libnetcfg File 15.41 KB 0755
libreoffice File 6.5 KB 0755
link File 34.59 KB 0755
linkicc File 26.45 KB 0755
linux-boot-prober File 1.54 KB 0755
linux-check-removal File 4.56 KB 0755
linux-update-symlinks File 6.35 KB 0755
linux-version File 2.63 KB 0755
linux32 File 26.73 KB 0755
linux64 File 26.73 KB 0755
listres File 14.8 KB 0755
ln File 62.59 KB 0755
lnstat File 22.73 KB 0755
loadkeys File 206.98 KB 0755
loadunimap File 34.54 KB 0755
localc File 59 B 0755
locale File 49.71 KB 0755
locale-check File 14.23 KB 0755
localectl File 30.45 KB 0755
localedef File 323.2 KB 0755
localsearch File 133.8 KB 0755
lodraw File 59 B 0755
loffice File 53 B 0755
lofromtemplate File 64 B 0755
logger File 39.05 KB 0755
login File 42.45 KB 0755
loginctl File 66.59 KB 0755
logname File 34.59 KB 0755
logresolve File 14.39 KB 0755
loimpress File 62 B 0755
lomath File 59 B 0755
look File 18.46 KB 0755
loweb File 58 B 0755
lowntfs-3g File 131.05 KB 0755
lowriter File 61 B 0755
lp File 26.38 KB 0755
lpoptions File 22.45 KB 0755
lpq File 22.45 KB 0755
lpr File 22.38 KB 0755
lprm File 14.38 KB 0755
lpstat File 38.7 KB 0755
ls File 155.02 KB 0755
lsattr File 14.38 KB 0755
lsb_release File 2.77 KB 0755
lsblk File 178.46 KB 0755
lscpu File 118.46 KB 0755
lshw File 784.49 KB 0755
lsinitramfs File 735 B 0755
lsipc File 54.45 KB 0755
lslocks File 42.88 KB 0755
lslogins File 50.45 KB 0755
lsmem File 38.45 KB 0755
lsmod File 194.31 KB 0755
lsns File 42.46 KB 0755
lsof File 203.8 KB 0755
lspci File 144.19 KB 0755
lspgpot File 1.06 KB 0755
lspower File 1.2 KB 0755
lsusb File 234.48 KB 0755
lto-dump File 31.61 MB 0755
lto-dump-14 File 31.61 MB 0755
luit File 100.88 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.87 KB 0755
lzcat File 103.02 KB 0755
lzcmp File 7.41 KB 0755
lzdiff File 7.41 KB 0755
lzegrep File 10.17 KB 0755
lzfgrep File 10.17 KB 0755
lzgrep File 10.17 KB 0755
lzless File 2.33 KB 0755
lzma File 103.02 KB 0755
lzmainfo File 14.45 KB 0755
lzmore File 2.18 KB 0755
m17n-db File 3.65 KB 0755
m2300w File 28.83 KB 0755
m2300w-wrapper File 14.24 KB 0755
m2400w File 32.83 KB 0755
make File 344.14 KB 0755
make-first-existing-target File 4.79 KB 0755
makedumpfile File 415.41 KB 0755
makedumpfile-R.pl File 4.83 KB 0755
mako-render File 972 B 0755
man File 129.48 KB 0755
man-recode File 35.48 KB 0755
mandb File 155.74 KB 0755
manpath File 26.86 KB 0755
mapscrn File 34.54 KB 0755
markdown-it File 220 B 0755
mawk File 190.84 KB 0755
mbim-network File 11.08 KB 0755
mbimcli File 216.82 KB 0755
mcookie File 26.52 KB 0755
md5sum File 42.49 KB 0755
md5sum.textutils File 42.49 KB 0755
mdig File 54.48 KB 0755
memhog File 14.42 KB 0755
mesa-overlay-control.py File 5.59 KB 0755
migrate-pubring-from-classic-gpg File 3.02 KB 0755
migratepages File 14.38 KB 0755
migspeed File 14.3 KB 0755
mimeopen File 9.41 KB 0755
mimetype File 12.76 KB 0755
min12xxw File 31.45 KB 0755
mk_modmap File 15.78 KB 0755
mkdir File 70.59 KB 0755
mkfifo File 42.59 KB 0755
mkfontdir File 65 B 0755
mkfontscale File 42.9 KB 0755
mknod File 46.59 KB 0755
mksquashfs File 286.95 KB 0755
mktemp File 38.59 KB 0755
mmcli File 278.02 KB 0755
modutil File 94.49 KB 0755
mokutil File 59.48 KB 0755
monitor-sensor File 18.38 KB 0755
more File 46.46 KB 0755
mount File 50.45 KB 4755
mountpoint File 18.45 KB 0755
mousetweaks File 74.3 KB 0755
mpris-proxy File 94.67 KB 0755
mpstat File 50.55 KB 0755
mscompress File 14.3 KB 0755
msexpand File 14.3 KB 0755
mt File 75.09 KB 0755
mt-gnu File 75.09 KB 0755
mtr File 80.33 KB 0755
mtr-packet File 34.38 KB 0755
mv File 134.6 KB 0755
my_print_defaults File 179.59 KB 0755
myisam_ftdump File 6.36 MB 0755
myisamchk File 6.57 MB 0755
myisamlog File 6.39 MB 0755
myisampack File 6.42 MB 0755
mysql File 6.63 MB 0755
mysql_config_editor File 165.27 KB 0755
mysql_migrate_keyring File 6.53 MB 0755
mysql_secure_installation File 6.45 MB 0755
mysql_tzinfo_to_sql File 79.15 KB 0755
mysqladmin File 6.47 MB 0755
mysqlanalyze File 6.48 MB 0755
mysqlbinlog File 6.86 MB 0755
mysqlcheck File 6.48 MB 0755
mysqld_multi File 26.73 KB 0755
mysqld_safe File 28.45 KB 0755
mysqldump File 6.57 MB 0755
mysqldumpslow File 7.54 KB 0755
mysqlimport File 6.46 MB 0755
mysqloptimize File 6.48 MB 0755
mysqlrepair File 6.48 MB 0755
mysqlshow File 6.46 MB 0755
mysqlslap File 6.47 MB 0755
namei File 22.45 KB 0755
nano File 328.65 KB 0755
nautilus File 1.6 MB 0755
nautilus-autorun-software File 18.38 KB 0755
nautilus-sendto File 22.23 KB 0755
nawk File 190.84 KB 0755
nc File 42.71 KB 0755
nc.openbsd File 42.71 KB 0755
neqn File 913 B 0755
netaddr File 211 B 0755
netcat File 42.71 KB 0755
netstat File 166.68 KB 0755
networkctl File 130.59 KB 0755
networkd-dispatcher File 19.88 KB 0755
newgrp File 18.45 KB 4755
ngettext File 38.46 KB 0755
nhlt-dmic-info File 18.55 KB 0755
nice File 38.59 KB 0755
nisdomainname File 22.3 KB 0755
nl File 42.68 KB 0755
nm File 47.57 KB 0755
nm-connection-editor File 963.06 KB 0755
nm-online File 22.45 KB 0755
nmcli File 1.03 MB 0755
nmtui File 891.73 KB 0755
nmtui-connect File 891.73 KB 0755
nmtui-edit File 891.73 KB 0755
nmtui-hostname File 891.73 KB 0755
nohup File 38.49 KB 0755
notify-send File 26.31 KB 0755
nproc File 38.59 KB 0755
nroff File 5.58 KB 0755
nsenter File 30.71 KB 0755
nslookup File 118.98 KB 0755
nss-addbuiltin File 30.68 KB 0755
nss-dbtest File 22.47 KB 0755
nss-pp File 86.46 KB 0755
nstat File 30.45 KB 0755
nsupdate File 82.62 KB 0755
ntfs-3g File 175.09 KB 4755
ntfs-3g.probe File 14.45 KB 0755
ntfscat File 26.45 KB 0755
ntfscluster File 38.46 KB 0755
ntfscmp File 30.45 KB 0755
ntfsdecrypt File 42.46 KB 0755
ntfsfallocate File 26.46 KB 0755
ntfsfix File 34.46 KB 0755
ntfsinfo File 58.46 KB 0755
ntfsls File 27.53 KB 0755
ntfsmove File 30.46 KB 0755
ntfsrecover File 114.45 KB 0755
ntfssecaudit File 90.94 KB 0755
ntfstruncate File 26.38 KB 0755
ntfsusermap File 18.38 KB 0755
ntfswipe File 46.98 KB 0755
numactl File 35.23 KB 0755
numastat File 35.56 KB 0755
numfmt File 62.6 KB 0755
nvidia-detector File 270 B 0755
oakdecode File 18.33 KB 0755
obexctl File 110.46 KB 0755
objcopy File 166.7 KB 0755
objdump File 397.89 KB 0755
oclock File 23.41 KB 0755
ocspclnt File 70.46 KB 0755
od File 62.59 KB 0755
oem-getlogs File 8.3 KB 0755
on_ac_power File 2.45 KB 0755
oomctl File 18.45 KB 0755
open File 31.53 KB 0755
openssl File 1.08 MB 0755
openvt File 22.8 KB 0755
opldecode File 18.31 KB 0755
orca File 9.52 KB 0755
orca-dm-wrapper File 70 B 0755
os-prober File 4.42 KB 0755
osirrox File 14.15 KB 0755
p11-kit File 214.78 KB 0755
p7content File 22.39 KB 0755
p7env File 18.38 KB 0755
p7sign File 26.39 KB 0755
p7verify File 22.38 KB 0755
pager File 216.21 KB 0755
paper File 22.59 KB 0755
paperconf File 14.38 KB 0755
papers File 6.82 MB 0755
papers-previewer File 46.59 KB 0755
papers-thumbnailer File 18.46 KB 0755
partx File 62.46 KB 0755
passwd File 91.45 KB 4755
paste File 38.49 KB 0755
patch File 182.52 KB 0755
pathchk File 38.59 KB 0755
pcilmr File 50.45 KB 0755
pdb3 File 88.79 KB 0755
pdb3.13 File 88.79 KB 0755
pdf2ps File 909 B 0755
pdfattach File 22.46 KB 0755
pdfdetach File 30.57 KB 0755
pdffonts File 22.6 KB 0755
pdfimages File 42.6 KB 0755
pdfinfo File 74.58 KB 0755
pdfseparate File 22.46 KB 0755
pdfsig File 47.01 KB 0755
pdftocairo File 174.66 KB 0755
pdftohtml File 114.49 KB 0755
pdftoppm File 38.66 KB 0755
pdftops File 34.76 KB 0755
pdftotext File 58.6 KB 0755
pdfunite File 34.46 KB 0755
peekfd File 14.38 KB 0755
perf File 10.59 MB 0755
perl File 3.86 MB 0755
perl5.40-x86_64-linux-gnu File 14.38 KB 0755
perl5.40.1 File 3.86 MB 0755
perlbug File 44.52 KB 0755
perldoc File 125 B 0755
perli11ndoc File 58.17 KB 0755
perlivp File 10.61 KB 0755
perlthanks File 44.52 KB 0755
perror File 1.53 MB 0755
pf2afm File 498 B 0755
pfbtopfa File 516 B 0755
pgrep File 34.55 KB 0755
phar File 14.88 KB 0755
phar.default File 14.88 KB 0755
phar.phar File 14.88 KB 0755
phar.phar.default File 14.88 KB 0755
phar.phar8.4 File 14.88 KB 0755
phar8.4 File 14.88 KB 0755
phar8.4.phar File 14.88 KB 0755
php File 5.79 MB 0755
php.default File 5.79 MB 0755
php8.4 File 5.79 MB 0755
pic File 228.12 KB 0755
pico File 328.65 KB 0755
piconv File 8.16 KB 0755
pidof File 26.3 KB 0755
pidstat File 50.55 KB 0755
pidwait File 34.55 KB 0755
pinentry File 86.73 KB 0755
pinentry-curses File 70.72 KB 0755
pinentry-gnome3 File 86.73 KB 0755
pinentry-x11 File 86.73 KB 0755
ping File 155.74 KB 0755
ping4 File 155.74 KB 0755
ping6 File 155.74 KB 0755
pinky File 42.49 KB 0755
pipewire File 14.45 KB 0755
pipewire-aes67 File 14.45 KB 0755
pipewire-avb File 14.45 KB 0755
pipewire-pulse File 14.45 KB 0755
pk12util File 75.08 KB 0755
pk1sign File 22.52 KB 0755
pkaction File 18.45 KB 0755
pkcheck File 26.38 KB 0755
pkcon File 58.38 KB 0755
pkexec File 30.3 KB 4755
pkill File 34.55 KB 0755
pkmon File 22.38 KB 0755
pkttyagent File 22.45 KB 0755
pl2pm File 4.43 KB 0755
pldd File 22.52 KB 0755
plog File 146 B 0755
plymouth File 54.45 KB 0755
pmap File 38.48 KB 0755
pnm2ppa File 1.57 MB 0755
pod2html File 3.95 KB 0755
pod2man File 18.46 KB 0755
pod2text File 12.8 KB 0755
pod2usage File 4.01 KB 0755
podchecker File 3.64 KB 0755
poff File 2.77 KB 0755
pon File 1.33 KB 0755
powerprofilesctl File 10.49 KB 0755
ppdc File 118.55 KB 0755
ppdhtml File 82.55 KB 0755
ppdi File 106.55 KB 0755
ppdmerge File 18.45 KB 0755
ppdpo File 90.55 KB 0755
pphs File 404 B 0755
pr File 78.64 KB 0755
precat File 5.52 KB 0755
preconv File 62.55 KB 0755
preunzip File 5.52 KB 0755
prezip File 5.52 KB 0755
prezip-bin File 14.38 KB 0755
printafm File 395 B 0755
printenv File 34.46 KB 0755
printer-profile File 5.51 KB 0755
printf File 42.59 KB 0755
prlimit File 26.97 KB 0755
pro File 1003 B 0755
prove File 13.36 KB 0755
prtstat File 22.45 KB 0755
ps File 163.07 KB 0755
ps2ascii File 494 B 0755
ps2epsi File 1.27 KB 0755
ps2pdf File 272 B 0755
ps2pdf12 File 257 B 0755
ps2pdf13 File 257 B 0755
ps2pdf14 File 257 B 0755
ps2pdfwr File 1.05 KB 0755
ps2ps File 647 B 0755
ps2ps2 File 669 B 0755
ps2txt File 494 B 0755
psfaddtable File 26.45 KB 0755
psfgettable File 26.45 KB 0755
psfstriptable File 26.45 KB 0755
psfxtable File 26.45 KB 0755
psicc File 14.39 KB 0755
pslog File 14.38 KB 0755
pstree File 63.4 KB 0755
pstree.x11 File 63.4 KB 0755
ptar File 3.48 KB 0755
ptardiff File 2.58 KB 0755
ptargrep File 4.29 KB 0755
ptx File 58.62 KB 0755
pw-cat File 102.45 KB 0755
pw-cli File 154.56 KB 0755
pw-config File 22.45 KB 0755
pw-container File 22.45 KB 0755
pw-dot File 62.45 KB 0755
pw-dsdplay File 102.45 KB 0755
pw-dump File 114.54 KB 0755
pw-encplay File 102.45 KB 0755
pw-link File 34.45 KB 0755
pw-loopback File 26.45 KB 0755
pw-metadata File 14.45 KB 0755
pw-mididump File 34.45 KB 0755
pw-midiplay File 102.45 KB 0755
pw-midirecord File 102.45 KB 0755
pw-mon File 106.5 KB 0755
pw-play File 102.45 KB 0755
pw-profiler File 26.45 KB 0755
pw-record File 102.45 KB 0755
pw-reserve File 26.45 KB 0755
pw-top File 50.45 KB 0755
pwd File 38.59 KB 0755
pwdecrypt File 22.39 KB 0755
pwdx File 14.45 KB 0755
py3clean File 7.59 KB 0755
py3compile File 12.99 KB 0755
py3versions File 12.52 KB 0755
pybabel File 956 B 0755
pybabel-python3 File 956 B 0755
pydoc3 File 80 B 0755
pydoc3.13 File 80 B 0755
pygettext3 File 23.87 KB 0755
pygettext3.13 File 23.87 KB 0755
pygmentize File 215 B 0755
pyserial-miniterm File 975 B 0755
pyserial-ports File 969 B 0755
python3 File 6.51 MB 0755
python3.13 File 6.51 MB 0755
pzstd File 866.54 KB 0755
qmi-firmware-update File 180.16 KB 0755
qmi-network File 16.04 KB 0755
qmicli File 647.17 KB 0755
qpdldecode File 22.6 KB 0755
quirks-handler File 2.4 KB 0755
ranlib File 54.56 KB 0755
rbash File 1.66 MB 0755
rctest File 42.4 KB 0755
rdma File 126.6 KB 0755
readelf File 790.98 KB 0755
readlink File 42.49 KB 0755
realpath File 42.49 KB 0755
red File 89 B 0755
remmina File 969.16 KB 0755
remmina-file-wrapper File 1.3 KB 0755
remmina-gnome File 530 B 0755
rename.ul File 22.45 KB 0755
rendercheck File 59.78 KB 0755
renice File 14.45 KB 0755
reset File 30.38 KB 0755
resizecons File 30.54 KB 0755
resizepart File 22.45 KB 0755
resolvectl File 178.69 KB 0755
rev File 14.45 KB 0755
rfcomm File 30.81 KB 0755
rgrep File 30 B 0755
rhythmbox File 14.38 KB 0755
rhythmbox-client File 56.29 KB 0755
rm File 62.59 KB 0755
rmdir File 38.49 KB 0755
rnano File 328.65 KB 0755
rotatelogs File 26.46 KB 0755
routel File 1.62 KB 0755
rpcgen File 94.59 KB 0755
rrsync File 12.7 KB 0755
rsaperf File 688.82 KB 0755
rstart File 2.55 KB 0755
rstartd File 1.43 KB 0755
rsync File 594.21 KB 0755
rsync-ssl File 5.01 KB 0755
rtla File 1.58 KB 0755
rtstat File 22.73 KB 0755
run-parts File 30.89 KB 0755
run-with-aspell File 57 B 0755
run0 File 82.9 KB 0755
runcon File 38.59 KB 0755
rview File 2.16 MB 0755
rygel File 50.45 KB 0755
sadf File 396.13 KB 0755
sane-find-scanner File 103.25 KB 0755
sar File 179.1 KB 0755
sar.sysstat File 179.1 KB 0755
savelog File 10.24 KB 0755
sbattach File 22.54 KB 0755
sbkeysync File 34.74 KB 0755
sbsiglist File 14.6 KB 0755
sbsign File 34.7 KB 0755
sbvarsign File 22.73 KB 0755
sbverify File 30.61 KB 0755
scanimage File 79.19 KB 0755
scp File 162.74 KB 0755
scp-dbus-service File 90 B 0755
screendump File 18.37 KB 0755
script File 54.45 KB 0755
scriptlive File 42.45 KB 0755
scriptreplay File 34.45 KB 0755
sdiff File 58.47 KB 0755
sdptool File 148.38 KB 0755
seahorse File 1.18 MB 0755
sed File 110.57 KB 0755
select-default-iwrap File 474 B 0755
select-editor File 2.62 KB 0755
selfserv File 74.42 KB 0755
sensible-browser File 1.06 KB 0755
sensible-editor File 1.51 KB 0755
sensible-pager File 824 B 0755
sensible-terminal File 1.08 KB 0755
seq File 42.59 KB 0755
session-migration File 22.15 KB 0755
sessreg File 14.38 KB 0755
setarch File 26.73 KB 0755
setfacl File 38.38 KB 0755
setfont File 54.91 KB 0755
setkeycodes File 14.45 KB 0755
setleds File 18.51 KB 0755
setlogcons File 14.45 KB 0755
setmetamode File 14.48 KB 0755
setpci File 34.46 KB 0755
setpriv File 46.46 KB 0755
setsid File 14.45 KB 0755
setterm File 38.45 KB 0755
setupcon File 40.01 KB 0755
setxkbmap File 30.78 KB 0755
sftp File 178.73 KB 0755
sg File 18.45 KB 4755
sh File 146.84 KB 0755
sha1sum File 42.49 KB 0755
sha224sum File 42.49 KB 0755
sha256sum File 42.49 KB 0755
sha384sum File 42.49 KB 0755
sha512sum File 42.49 KB 0755
shasum File 9.75 KB 0755
shlibsign File 38.76 KB 0755
shotwell File 5.92 MB 0755
showconsolefont File 18.45 KB 0755
showkey File 18.45 KB 0755
showrgb File 14.38 KB 0755
shred File 62.59 KB 0755
shuf File 50.59 KB 0755
signtool File 122.49 KB 0755
signver File 42.76 KB 0755
simple-scan File 522.44 KB 0755
size File 30.53 KB 0755
skill File 30.49 KB 0755
slabtop File 22.52 KB 0755
sleep File 34.59 KB 0755
slogin File 1.07 MB 0755
slxdecode File 18.31 KB 0755
smproxy File 26.39 KB 0755
snap File 18.41 MB 0755
snapctl File 7.1 MB 0755
snapfuse File 42.3 KB 0755
snapshot File 4.59 MB 0755
snice File 30.49 KB 0755
soelim File 38.55 KB 0755
soffice File 6.5 KB 0755
software-properties-gtk File 4.04 KB 0755
sort File 118.84 KB 0755
spa-acp-tool File 344.34 KB 0755
spa-inspect File 110.55 KB 0755
spa-json-dump File 34.45 KB 0755
spa-monitor File 14.55 KB 0755
spa-resample File 34.8 KB 0755
spd-conf File 1003 B 0755
spd-say File 31.21 KB 0755
spdsend File 14.38 KB 0755
speaker-test File 42.52 KB 0755
speech-dispatcher File 250.48 KB 0755
spice-vdagent File 82.85 KB 0755
splain File 19 KB 0755
split File 59.02 KB 0755
splitfont File 14.37 KB 0755
sqfscat File 147.9 KB 0755
sqfstar File 286.95 KB 0755
ss File 136.93 KB 0755
ssh File 1.07 MB 0755
ssh-add File 350.5 KB 0755
ssh-agent File 366.51 KB 2755
ssh-argv0 File 1.42 KB 0755
ssh-copy-id File 13.84 KB 0755
ssh-import-id File 985 B 0755
ssh-import-id-gh File 785 B 0755
ssh-import-id-lp File 785 B 0755
ssh-keygen File 526.52 KB 0755
ssh-keyscan File 538.52 KB 0755
ssltap File 78.46 KB 0755
sss_ssh_authorizedkeys File 34.38 KB 0755
sss_ssh_knownhosts File 34.38 KB 0755
sss_ssh_knownhostsproxy File 26.38 KB 0755
startx File 5.26 KB 0755
stat File 90.59 KB 0755
static-sh File 2.34 MB 0755
stdbuf File 38.59 KB 0755
strace File 2.13 MB 0755
strace-log-merge File 1.83 KB 0755
streamzip File 7.87 KB 0755
strings File 34.69 KB 0755
strip File 166.73 KB 0755
strsclnt File 46.41 KB 0755
stty File 66.6 KB 0755
su File 54.45 KB 4755
sudo File 287.48 KB 4755
sudoedit File 287.48 KB 4755
sudoreplay File 96.03 KB 0755
sum File 38.49 KB 0755
switcherooctl File 4.77 KB 0755
symkeyutil File 39.29 KB 0755
sync File 34.49 KB 0755
sysprof File 1.2 MB 0755
sysprof-agent File 474.84 KB 0755
sysprof-cat File 322.59 KB 0755
sysprof-cli File 474.84 KB 0755
systemctl File 299 KB 0755
systemd-ac-power File 14.45 KB 0755
systemd-analyze File 218.87 KB 0755
systemd-ask-password File 18.59 KB 0755
systemd-cat File 18.45 KB 0755
systemd-cgls File 22.57 KB 0755
systemd-cgtop File 38.47 KB 0755
systemd-confext File 74.65 KB 0755
systemd-creds File 50.74 KB 0755
systemd-cryptenroll File 83 KB 0755
systemd-cryptsetup File 79.05 KB 0755
systemd-delta File 26.45 KB 0755
systemd-detect-virt File 18.45 KB 0755
systemd-escape File 22.45 KB 0755
systemd-firstboot File 58.88 KB 0755
systemd-hwdb File 14.44 KB 0755
systemd-id128 File 26.45 KB 0755
systemd-inhibit File 22.47 KB 0755
systemd-machine-id-setup File 18.63 KB 0755
systemd-mount File 54.79 KB 0755
systemd-notify File 30.73 KB 0755
systemd-path File 18.45 KB 0755
systemd-run File 82.9 KB 0755
systemd-socket-activate File 30.45 KB 0755
systemd-stdio-bridge File 22.45 KB 0755
systemd-sysext File 74.65 KB 0755
systemd-sysusers File 66.63 KB 0755
systemd-tmpfiles File 126.7 KB 0755
systemd-tty-ask-password-agent File 34.45 KB 0755
systemd-umount File 54.79 KB 0755
systemd-vpick File 26.64 KB 0755
tabs File 18.38 KB 0755
tac File 42.49 KB 0755
tail File 74.61 KB 0755
tapestat File 30.55 KB 0755
tar File 510.04 KB 0755
taskset File 30.45 KB 0755
tbl File 154.55 KB 0755
tclsh File 14.23 KB 0755
tclsh8.6 File 14.23 KB 0755
tcpdump File 1.21 MB 0755
tecla File 66.52 KB 0755
tee File 42.59 KB 0755
telnet File 221.9 KB 0755
tempfile File 14.38 KB 0755
test File 34.51 KB 0755
thunderbird File 2.4 KB 0755
tic File 94.49 KB 0755
tificc File 34.46 KB 0755
time File 26.52 KB 0755
timedatectl File 46.45 KB 0755
timeout File 43.01 KB 0755
tinysparql File 60.69 KB 0755
tload File 22.47 KB 0755
tnftp File 182.9 KB 0755
toe File 22.38 KB 0755
top File 147.77 KB 0755
totem File 22.45 KB 0755
totem-video-thumbnailer File 38.48 KB 0755
touch File 82.59 KB 0755
tput File 26.41 KB 0755
tr File 50.55 KB 0755
trace-cmd File 435.41 KB 0755
tracepath File 18.23 KB 0755
transicc File 38.39 KB 0755
transmission-gtk File 2.78 MB 0755
transset File 22.78 KB 0755
troff File 818.7 KB 0755
true File 34.46 KB 0755
truncate File 38.59 KB 0755
trust File 246.78 KB 0755
tset File 30.38 KB 0755
tsort File 42.59 KB 0755
tstclnt File 106.5 KB 0755
tty File 34.59 KB 0755
turbostat File 1.58 KB 0755
tzselect File 21.39 KB 0755
ua File 1003 B 0755
ubuntu-advantage File 1003 B 0755
ubuntu-bug File 2.27 KB 0755
ubuntu-distro-info File 26.97 KB 0755
ubuntu-drivers File 18.25 KB 0755
ubuntu-report File 7.9 MB 0755
ubuntu-security-status File 22.25 KB 0755
ucf File 35.62 KB 0755
ucfq File 18.46 KB 0755
ucfr File 9.93 KB 0755
uclampset File 30.45 KB 0755
ucs2any File 26.38 KB 0755
udevadm File 618.84 KB 0755
udisksctl File 62.45 KB 0755
ul File 26.46 KB 0755
umax_pp File 191.53 KB 0755
umount File 38.45 KB 4755
uname File 34.59 KB 0755
unattended-upgrade File 116.54 KB 0755
unattended-upgrades File 116.54 KB 0755
uncompress File 2.28 KB 0755
unexpand File 38.61 KB 0755
unicode_start File 2.71 KB 0755
unicode_stop File 528 B 0755
uniq File 46.6 KB 0755
unity-scope-loader File 14.38 KB 0755
unlink File 34.59 KB 0755
unlzma File 103.02 KB 0755
unmkinitramfs File 6.23 KB 0755
unopkg File 52 B 0755
unshare File 46.68 KB 0755
unsquashfs File 147.9 KB 0755
unxz File 103.02 KB 0755
unzip File 190.61 KB 0755
unzipsfx File 94.63 KB 0755
unzstd File 1.22 MB 0755
update-alternatives File 66.46 KB 0755
update-desktop-database File 22.46 KB 0755
update-manager File 4.65 KB 0755
update-mime-database File 90.41 KB 0755
update-notifier File 91.23 KB 0755
upower File 18.38 KB 0755
uptime File 14.45 KB 0755
usb-creator-gtk File 2.87 KB 0755
usb-devices File 4.84 KB 0755
usb_printerid File 14.31 KB 0755
usbhid-dump File 30.46 KB 0755
usbip File 1.58 KB 0755
usbipd File 1.58 KB 0755
usbreset File 14.38 KB 0755
users File 38.59 KB 0755
uuidgen File 22.45 KB 0755
uuidparse File 22.45 KB 0755
varlinkctl File 38.57 KB 0755
vcs-run File 6.75 KB 0755
vdir File 155.02 KB 0755
vfychain File 74.47 KB 0755
vfyserv File 42.47 KB 0755
vi File 2.16 MB 0755
view File 2.16 MB 0755
viewres File 31.3 KB 0755
vim.tiny File 2.16 MB 0755
vmstat File 38.86 KB 0755
vmwarectrl File 14.26 KB 0755
vsftpdwho File 54 B 0755
vstp File 26.24 KB 0755
w File 26.45 KB 0755
wall File 26.45 KB 0755
watch File 34.92 KB 0755
watchgnupg File 22.38 KB 0755
wc File 62.59 KB 0755
wcurl File 10.3 KB 0755
wdctl File 34.48 KB 0755
wget File 579.05 KB 0755
whatis File 47.36 KB 0755
whereis File 30.91 KB 0755
which File 1.05 KB 0755
which.debianutils File 1.05 KB 0755
whiptail File 30.24 KB 0755
who File 46.6 KB 0755
whoami File 34.59 KB 0755
whoopsie File 50.98 KB 0755
whoopsie-preferences File 22.23 KB 0755
wireplumber File 18.64 KB 0755
word-list-compress File 14.38 KB 0755
wpa_passphrase File 14.46 KB 0755
wpctl File 62.51 KB 0755
wpexec File 18.63 KB 0755
wsdd File 72.92 KB 0755
x-session-manager File 958 B 0755
x-terminal-emulator File 6.06 KB 0755
x-www-browser File 2.32 KB 0755
x11perf File 197.46 KB 0755
x11perfcomp File 2.74 KB 0755
x86_64 File 26.73 KB 0755
x86_64-linux-gnu-addr2line File 30.78 KB 0755
x86_64-linux-gnu-ar File 54.56 KB 0755
x86_64-linux-gnu-as File 795.52 KB 0755
x86_64-linux-gnu-c++filt File 26.34 KB 0755
x86_64-linux-gnu-cpp File 1.13 MB 0755
x86_64-linux-gnu-cpp-14 File 1.13 MB 0755
x86_64-linux-gnu-elfedit File 34.79 KB 0755
x86_64-linux-gnu-gcc File 1.13 MB 0755
x86_64-linux-gnu-gcc-14 File 1.13 MB 0755
x86_64-linux-gnu-gcc-ar File 30.66 KB 0755
x86_64-linux-gnu-gcc-ar-14 File 30.66 KB 0755
x86_64-linux-gnu-gcc-nm File 30.66 KB 0755
x86_64-linux-gnu-gcc-nm-14 File 30.66 KB 0755
x86_64-linux-gnu-gcc-ranlib File 30.66 KB 0755
x86_64-linux-gnu-gcc-ranlib-14 File 30.66 KB 0755
x86_64-linux-gnu-gcov File 468.19 KB 0755
x86_64-linux-gnu-gcov-14 File 468.19 KB 0755
x86_64-linux-gnu-gcov-dump File 380.14 KB 0755
x86_64-linux-gnu-gcov-dump-14 File 380.14 KB 0755
x86_64-linux-gnu-gcov-tool File 408.23 KB 0755
x86_64-linux-gnu-gcov-tool-14 File 408.23 KB 0755
x86_64-linux-gnu-gprof File 99.86 KB 0755
x86_64-linux-gnu-ld File 1.78 MB 0755
x86_64-linux-gnu-ld.bfd File 1.78 MB 0755
x86_64-linux-gnu-lto-dump File 31.61 MB 0755
x86_64-linux-gnu-lto-dump-14 File 31.61 MB 0755
x86_64-linux-gnu-nm File 47.57 KB 0755
x86_64-linux-gnu-objcopy File 166.7 KB 0755
x86_64-linux-gnu-objdump File 397.89 KB 0755
x86_64-linux-gnu-ranlib File 54.56 KB 0755
x86_64-linux-gnu-readelf File 790.98 KB 0755
x86_64-linux-gnu-size File 30.53 KB 0755
x86_64-linux-gnu-strings File 34.69 KB 0755
x86_64-linux-gnu-strip File 166.73 KB 0755
x86_energy_perf_policy File 1.58 KB 0755
xargs File 66.49 KB 0755
xauth File 55.03 KB 0755
xbiff File 24.16 KB 0755
xbrlapi File 238.57 KB 0755
xcalc File 51.48 KB 0755
xclipboard File 22.58 KB 0755
xclock File 53.06 KB 0755
xcmsdb File 42.46 KB 0755
xconsole File 23.2 KB 0755
xcursorgen File 22.3 KB 0755
xcutsel File 18.56 KB 0755
xdg-dbus-proxy File 58.3 KB 0755
xdg-desktop-icon File 22.29 KB 0755
xdg-desktop-menu File 43.17 KB 0755
xdg-email File 28.24 KB 0755
xdg-icon-resource File 31.47 KB 0755
xdg-mime File 46.62 KB 0755
xdg-open File 31.53 KB 0755
xdg-screensaver File 38.55 KB 0755
xdg-settings File 43.31 KB 0755
xdg-terminal-exec File 33.69 KB 0755
xdg-user-dir File 234 B 0755
xdg-user-dirs-gtk-update File 22.3 KB 0755
xdg-user-dirs-update File 26.3 KB 0755
xditview File 108.13 KB 0755
xdpyinfo File 39.13 KB 0755
xdriinfo File 14.38 KB 0755
xedit File 705.34 KB 0755
xev File 34.7 KB 0755
xeyes File 32.13 KB 0755
xfd File 40.08 KB 0755
xfontsel File 47.92 KB 0755
xgamma File 14.38 KB 0755
xgc File 70.38 KB 0755
xhost File 22.38 KB 0755
xinit File 22.38 KB 0755
xinput File 58.83 KB 0755
xkbbell File 14.39 KB 0755
xkbcomp File 212.18 KB 0755
xkbevd File 38.46 KB 0755
xkbprint File 94.42 KB 0755
xkbvleds File 23.18 KB 0755
xkbwatch File 23.24 KB 0755
xkeystone File 16.58 KB 0755
xkill File 14.38 KB 0755
xload File 22.92 KB 0755
xlogo File 23.19 KB 0755
xlsatoms File 14.38 KB 0755
xlsclients File 18.38 KB 0755
xlsfonts File 26.48 KB 0755
xmag File 44.31 KB 0755
xman File 77.2 KB 0755
xmessage File 23.27 KB 0755
xmodmap File 46.75 KB 0755
xmore File 14.53 KB 0755
xorrecord File 14.15 KB 0755
xorriso File 14.15 KB 0755
xorrisofs File 14.15 KB 0755
xprop File 48.68 KB 0755
xqxdecode File 18.31 KB 0755
xrandr File 70.48 KB 0755
xrdb File 42.48 KB 0755
xrefresh File 14.46 KB 0755
xset File 34.38 KB 0755
xsetmode File 14.38 KB 0755
xsetpointer File 14.38 KB 0755
xsetroot File 18.38 KB 0755
xsetwacom File 59.84 KB 0755
xsm File 98.71 KB 0755
xstdcmap File 18.96 KB 0755
xsubpp File 5.05 KB 0755
xvidtune File 43.84 KB 0755
xvinfo File 18.38 KB 0755
xwd File 30.31 KB 0755
xwininfo File 50.46 KB 0755
xwud File 30.3 KB 0755
xxd File 22.36 KB 0755
xz File 103.02 KB 0755
xzcat File 103.02 KB 0755
xzcmp File 7.41 KB 0755
xzdiff File 7.41 KB 0755
xzegrep File 10.17 KB 0755
xzfgrep File 10.17 KB 0755
xzgrep File 10.17 KB 0755
xzless File 2.33 KB 0755
xzmore File 2.18 KB 0755
yelp File 58.3 KB 0755
yes File 34.46 KB 0755
ypdomainname File 22.3 KB 0755
zcat File 1.93 KB 0755
zcmp File 1.64 KB 0755
zdiff File 6.3 KB 0755
zdump File 30.36 KB 0755
zegrep File 29 B 0755
zenity File 148.94 KB 0755
zfgrep File 29 B 0755
zforce File 2.03 KB 0755
zgrep File 8.01 KB 0755
zip File 223.08 KB 0755
zipcloak File 74.48 KB 0755
zipdetails File 231.06 KB 0755
zipgrep File 2.89 KB 0755
zipinfo File 190.61 KB 0755
zipnote File 66.48 KB 0755
zipsplit File 62.48 KB 0755
zjsdecode File 26.32 KB 0755
zless File 2.38 KB 0755
zmore File 1.79 KB 0755
znew File 4.46 KB 0755
zstd File 1.22 MB 0755
zstdcat File 1.22 MB 0755
zstdgrep File 3.78 KB 0755
zstdless File 197 B 0755
zstdmt File 1.22 MB 0755
Filemanager