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